Inicio de Migracion de 2D a 3D
This commit is contained in:
parent
d259f53081
commit
121e586d53
|
@ -27,12 +27,16 @@
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="ObjetosSim\Fluids\**" />
|
||||||
|
<EmbeddedResource Remove="ObjetosSim\Fluids\**" />
|
||||||
|
<None Remove="ObjetosSim\Fluids\**" />
|
||||||
|
<Page Remove="ObjetosSim\Fluids\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Documentation\PlantillaEstandarizacion.cs" />
|
<Compile Remove="Documentation\PlantillaEstandarizacion.cs" />
|
||||||
<Compile Remove="ObjetosSim\ucBasicExample.xaml.cs" />
|
|
||||||
<Compile Remove="ObjetosSim\ucTransporteCurva.xaml.cs" />
|
<Compile Remove="ObjetosSim\ucTransporteCurva.xaml.cs" />
|
||||||
<Compile Remove="Simulacion\FPhysics.cs" />
|
|
||||||
<Compile Remove="Simulacion\GeometrySimulator.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -76,26 +80,23 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Remove="ObjetosSim\ucBasicExample.xaml" />
|
|
||||||
<Page Remove="ObjetosSim\ucTransporteCurva.xaml" />
|
<Page Remove="ObjetosSim\ucTransporteCurva.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Documentation\PlantillaEstandarizacion.cs" />
|
<None Include="Documentation\PlantillaEstandarizacion.cs" />
|
||||||
<None Include="ObjetosSim\ucBasicExample.xaml" />
|
|
||||||
<None Include="ObjetosSim\ucBasicExample.xaml.cs" />
|
|
||||||
<None Include="Simulacion\FPhysics.cs" />
|
|
||||||
<None Include="Simulacion\GeometrySimulator.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Aether.Physics2D" Version="2.2.0" />
|
<PackageReference Include="BepuPhysics" Version="2.5.0-beta.26" />
|
||||||
|
<PackageReference Include="BepuUtilities" Version="2.5.0-beta.26" />
|
||||||
<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.9.0.5494" />
|
<PackageReference Include="Emgu.CV" Version="4.9.0.5494" />
|
||||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
|
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
|
||||||
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
|
<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="HelixToolkit.Wpf" Version="2.27.0" />
|
||||||
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
||||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc3.3" />
|
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc3.3" />
|
||||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
||||||
|
@ -103,8 +104,6 @@
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||||
<PackageReference Include="PaddleOCRSharp" Version="4.5.0.1" />
|
<PackageReference Include="PaddleOCRSharp" Version="4.5.0.1" />
|
||||||
<PackageReference Include="Tesseract" Version="5.2.0" />
|
|
||||||
<PackageReference Include="Tesseract.Drawing" Version="5.2.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -187,7 +186,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="ObjetosSim\Fluids\" />
|
|
||||||
<Folder Include="paddleocr\cls\inference\" />
|
<Folder Include="paddleocr\cls\inference\" />
|
||||||
<Folder Include="paddleocr\det\inference\" />
|
<Folder Include="paddleocr\det\inference\" />
|
||||||
<Folder Include="paddleocr\keys\" />
|
<Folder Include="paddleocr\keys\" />
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace CtrEditor
|
||||||
private float TiempoDesdeStartSimulacion;
|
private float TiempoDesdeStartSimulacion;
|
||||||
private bool Debug_SimulacionCreado = false;
|
private bool Debug_SimulacionCreado = false;
|
||||||
|
|
||||||
public SimulationManagerFP simulationManager = new SimulationManagerFP();
|
public SimulationManagerBEPU simulationManager = new SimulationManagerBEPU();
|
||||||
|
|
||||||
private readonly DispatcherTimer _timerSimulacion;
|
private readonly DispatcherTimer _timerSimulacion;
|
||||||
private readonly DispatcherTimer _timerPLCUpdate;
|
private readonly DispatcherTimer _timerPLCUpdate;
|
||||||
|
@ -52,6 +52,9 @@ namespace CtrEditor
|
||||||
|
|
||||||
public Canvas MainCanvas;
|
public Canvas MainCanvas;
|
||||||
|
|
||||||
|
// Manager para la visualización 3D
|
||||||
|
public BEPUVisualization3DManager Visualization3DManager { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool isConnected;
|
private bool isConnected;
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,9 @@
|
||||||
<Grid Grid.Column="1">
|
<Grid Grid.Column="1">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="3*" />
|
||||||
|
<RowDefinition Height="5" />
|
||||||
|
<RowDefinition Height="2*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<ToolBarTray Grid.Row="0">
|
<ToolBarTray Grid.Row="0">
|
||||||
|
@ -223,6 +225,35 @@
|
||||||
</Canvas.RenderTransform>
|
</Canvas.RenderTransform>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- Separador entre vistas -->
|
||||||
|
<GridSplitter Grid.Row="2" Height="5" HorizontalAlignment="Stretch" Background="DarkGray"
|
||||||
|
ResizeDirection="Rows" VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
<!-- Vista 3D (Inferior) -->
|
||||||
|
<Border Grid.Row="3" BorderBrush="Gray" BorderThickness="1" Margin="2">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Header del panel 3D -->
|
||||||
|
<TextBlock Grid.Row="0" Text="Debug 3D - Mundo BEPU" Background="DarkGray" Foreground="White"
|
||||||
|
Padding="5" FontWeight="Bold" />
|
||||||
|
|
||||||
|
<!-- HelixViewport3D -->
|
||||||
|
<helix:HelixViewport3D x:Name="Debug3DViewport" Grid.Row="1" ShowCoordinateSystem="True"
|
||||||
|
ShowFrameRate="True" CoordinateSystemLabelZ="Z - Altura" EnableCurrentPosition="True"
|
||||||
|
ShowFieldOfView="True" CalculateCursorPosition="True" IsManipulationEnabled="True"
|
||||||
|
Orthographic="True">
|
||||||
|
<helix:DefaultLights />
|
||||||
|
<helix:GridLinesVisual3D Width="100" Length="100" MinorDistance="1" Thickness="0.01"
|
||||||
|
MajorDistance="7" />
|
||||||
|
<!-- Contenido del viewport se configurará desde code-behind -->
|
||||||
|
</helix:HelixViewport3D>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- GridSplitter -->
|
<!-- GridSplitter -->
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
//using using Microsoft.Xna.Framework;
|
|
||||||
|
|
||||||
using LibS7Adv;
|
using LibS7Adv;
|
||||||
using CtrEditor.Simulacion;
|
using CtrEditor.Simulacion;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using nkast.Aether.Physics2D.Common;
|
using System.Numerics;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using CtrEditor.FuncionesBase;
|
using CtrEditor.FuncionesBase;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.Windows.Controls;
|
||||||
using LibS7Adv;
|
using LibS7Adv;
|
||||||
using CtrEditor.Simulacion;
|
using CtrEditor.Simulacion;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using nkast.Aether.Physics2D.Common;
|
using System.Numerics;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using CtrEditor.FuncionesBase;
|
using CtrEditor.FuncionesBase;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
|
@ -3,7 +3,7 @@ using LibS7Adv;
|
||||||
using CtrEditor.Simulacion;
|
using CtrEditor.Simulacion;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using nkast.Aether.Physics2D.Common;
|
using System.Numerics;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CtrEditor.FuncionesBase;
|
using CtrEditor.FuncionesBase;
|
||||||
|
|
|
@ -4,7 +4,7 @@ using CtrEditor.Serialization;
|
||||||
using CtrEditor.Services;
|
using CtrEditor.Services;
|
||||||
using CtrEditor.Simulacion;
|
using CtrEditor.Simulacion;
|
||||||
using LibS7Adv;
|
using LibS7Adv;
|
||||||
using nkast.Aether.Physics2D.Common;
|
using System.Numerics;
|
||||||
using PaddleOCRSharp;
|
using PaddleOCRSharp;
|
||||||
using Siemens.Simatic.Simulation.Runtime;
|
using Siemens.Simatic.Simulation.Runtime;
|
||||||
using System.ComponentModel; // Para poder usar [property: Category ...
|
using System.ComponentModel; // Para poder usar [property: Category ...
|
||||||
|
|
|
@ -1,325 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CtrEditor.FuncionesBase;
|
|
||||||
using CtrEditor.Simulacion.Fluids;
|
|
||||||
using CtrEditor.Simulacion.Fluids.Components;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
using Color = System.Windows.Media.Color;
|
|
||||||
using Siemens.Simatic.Simulation.Runtime;
|
|
||||||
using LibS7Adv;
|
|
||||||
|
|
||||||
namespace CtrEditor.ObjetosSim
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// ViewModel para el sistema de fluidos
|
|
||||||
/// </summary>
|
|
||||||
public partial class osSistemaFluidos : osBase, IosBase
|
|
||||||
{
|
|
||||||
// Referencia a la simulación de fluidos
|
|
||||||
public SimulacionFluidos _simFluidos;
|
|
||||||
|
|
||||||
// Tamaño del área de simulación
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Ancho del área de simulación en metros")]
|
|
||||||
[property: Name("Ancho Simulación")]
|
|
||||||
private float anchoSimulacion = 10.0f;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Alto del área de simulación en metros")]
|
|
||||||
[property: Name("Alto Simulación")]
|
|
||||||
private float altoSimulacion = 10.0f;
|
|
||||||
|
|
||||||
// Propiedades del fluido
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Tamaño visual de las partículas")]
|
|
||||||
[property: Name("Tamaño Partícula")]
|
|
||||||
private float tamañoParticula = 0.01f;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Color del fluido")]
|
|
||||||
[property: Name("Color Fluido")]
|
|
||||||
private Color colorFluido = Colors.CornflowerBlue;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Opacidad de las partículas")]
|
|
||||||
[property: Name("Opacidad Partículas")]
|
|
||||||
private double opacidadParticulas = 0.7;
|
|
||||||
|
|
||||||
// Propiedades de gravedad
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Simulación")]
|
|
||||||
[property: Description("Gravedad en X (m/s²)")]
|
|
||||||
[property: Name("Gravedad X")]
|
|
||||||
private float gravedadX = 0.0f;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Simulación")]
|
|
||||||
[property: Description("Gravedad en Y (m/s²)")]
|
|
||||||
[property: Name("Gravedad Y")]
|
|
||||||
private float gravedadY = 9.8f;
|
|
||||||
|
|
||||||
partial void OnGravedadXChanged(float value)
|
|
||||||
{
|
|
||||||
ActualizarGravedad();
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnGravedadYChanged(float value)
|
|
||||||
{
|
|
||||||
ActualizarGravedad();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Estadísticas de la simulación
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Información")]
|
|
||||||
[property: Description("Número de partículas")]
|
|
||||||
[property: Name("Número Partículas")]
|
|
||||||
private int numeroParticulas;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Información")]
|
|
||||||
[property: Description("Rendimiento en FPS")]
|
|
||||||
[property: Name("FPS")]
|
|
||||||
private double fps;
|
|
||||||
|
|
||||||
// Referencia a componentes (solo para la función Add)
|
|
||||||
private List<IContenedorFluido> _contenedores = new List<IContenedorFluido>();
|
|
||||||
|
|
||||||
// Nombre de la clase para identificación
|
|
||||||
public static string NombreClase()
|
|
||||||
{
|
|
||||||
return "Sistema de Fluidos";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string nombre = NombreClase();
|
|
||||||
|
|
||||||
[property: Category("Identificación")]
|
|
||||||
[property: Description("Nombre identificativo del objeto")]
|
|
||||||
[property: Name("Nombre")]
|
|
||||||
public override string Nombre
|
|
||||||
{
|
|
||||||
get => nombre;
|
|
||||||
set => SetProperty(ref nombre, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Métodos para interactuar con la simulación
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Agrega partículas en un punto específico
|
|
||||||
/// </summary>
|
|
||||||
public void AgregarParticula(Vector2 posicion)
|
|
||||||
{
|
|
||||||
_simFluidos?.AgregarParticula(posicion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Agrega múltiples partículas en un área
|
|
||||||
/// </summary>
|
|
||||||
public void AgregarParticulasEnArea(Vector2 centro, float ancho, float alto, int cantidad)
|
|
||||||
{
|
|
||||||
_simFluidos?.AgregarParticulasEnArea(centro, ancho, alto, cantidad);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Crea un nuevo tanque y lo agrega a la simulación
|
|
||||||
/// </summary>
|
|
||||||
public Tanque CrearTanque(Vector2 posicion, float ancho, float alto)
|
|
||||||
{
|
|
||||||
if (_simFluidos == null) return null;
|
|
||||||
|
|
||||||
Tanque tanque = new Tanque(posicion, ancho, alto, (int)(AnchoSimulacion * 100));
|
|
||||||
_simFluidos.AgregarContenedor(tanque);
|
|
||||||
_contenedores.Add(tanque);
|
|
||||||
return tanque;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Crea una nueva tubería
|
|
||||||
/// </summary>
|
|
||||||
public Tuberia CrearTuberia(float diametro)
|
|
||||||
{
|
|
||||||
if (_simFluidos == null) return null;
|
|
||||||
|
|
||||||
Tuberia tuberia = new Tuberia(diametro, (int)(AnchoSimulacion * 100));
|
|
||||||
_simFluidos.AgregarContenedor(tuberia);
|
|
||||||
_contenedores.Add(tuberia);
|
|
||||||
return tuberia;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Crea una nueva válvula
|
|
||||||
/// </summary>
|
|
||||||
public Valvula CrearValvula(Vector2 posicion, float diametro, float apertura = 1.0f)
|
|
||||||
{
|
|
||||||
if (_simFluidos == null) return null;
|
|
||||||
|
|
||||||
Valvula valvula = new Valvula(posicion, diametro, apertura, (int)(AnchoSimulacion * 100));
|
|
||||||
_simFluidos.AgregarContenedor(valvula);
|
|
||||||
_contenedores.Add(valvula);
|
|
||||||
return valvula;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Elimina un componente de la simulación
|
|
||||||
/// </summary>
|
|
||||||
public void EliminarComponente(IContenedorFluido componente)
|
|
||||||
{
|
|
||||||
if (_simFluidos == null || componente == null) return;
|
|
||||||
|
|
||||||
_simFluidos.RemoverContenedor(componente);
|
|
||||||
_contenedores.Remove(componente);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Limpia todas las partículas de la simulación
|
|
||||||
/// </summary>
|
|
||||||
public void LimpiarParticulas()
|
|
||||||
{
|
|
||||||
_simFluidos?.LimpiarParticulas();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actualiza el vector de gravedad según las propiedades
|
|
||||||
/// </summary>
|
|
||||||
private void ActualizarGravedad()
|
|
||||||
{
|
|
||||||
if (_simFluidos != null)
|
|
||||||
{
|
|
||||||
_simFluidos.AjustarGravedad(new Vector2(GravedadX, GravedadY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor de la clase
|
|
||||||
/// </summary>
|
|
||||||
public osSistemaFluidos()
|
|
||||||
{
|
|
||||||
// Inicializar propiedades básicas
|
|
||||||
Ancho = 1.0f;
|
|
||||||
Alto = 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Métodos sobrescritos de osBase
|
|
||||||
|
|
||||||
public override void UpdateGeometryStart()
|
|
||||||
{
|
|
||||||
// Crear la simulación de fluidos si es necesario
|
|
||||||
if (_simFluidos == null)
|
|
||||||
{
|
|
||||||
_simFluidos = new SimulacionFluidos(
|
|
||||||
AnchoSimulacion,
|
|
||||||
AltoSimulacion,
|
|
||||||
10000, // Máximo de partículas
|
|
||||||
new Vector2(GravedadX, GravedadY)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateGeometryStep()
|
|
||||||
{
|
|
||||||
// No es necesario actualizar en cada paso
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateControl(int elapsedMilliseconds)
|
|
||||||
{
|
|
||||||
// Actualizar estadísticas
|
|
||||||
if (_simFluidos != null)
|
|
||||||
{
|
|
||||||
NumeroParticulas = _simFluidos.ParticlesCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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()
|
|
||||||
{
|
|
||||||
// Limpiar recursos si es necesario cuando se detiene la simulación
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ucLoaded()
|
|
||||||
{
|
|
||||||
base.ucLoaded();
|
|
||||||
|
|
||||||
// Inicializar la simulación de fluidos si es necesario
|
|
||||||
UpdateGeometryStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ucUnLoaded()
|
|
||||||
{
|
|
||||||
// Limpiar recursos
|
|
||||||
_simFluidos = null;
|
|
||||||
_contenedores.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementación para las conexiones con PLC
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Description("Tag de lectura/escritura del nivel del Tanque 1")]
|
|
||||||
[property: Category("PLC:")]
|
|
||||||
private string tagNivelTanque1;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Description("Tag de lectura/escritura de la apertura de la Válvula 1")]
|
|
||||||
[property: Category("PLC:")]
|
|
||||||
private string tagAperturaValvula1;
|
|
||||||
|
|
||||||
// Referencia a componentes típicos para integración con PLC
|
|
||||||
private Tanque _tanque1;
|
|
||||||
private Valvula _valvula1;
|
|
||||||
|
|
||||||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
|
||||||
{
|
|
||||||
// Ejemplo de integración con PLC para la válvula
|
|
||||||
if (_valvula1 != null && !string.IsNullOrEmpty(TagAperturaValvula1))
|
|
||||||
{
|
|
||||||
float aperturaValvula = LeerWordTagScaled(TagAperturaValvula1) / 100.0f;
|
|
||||||
_valvula1.Apertura = Math.Clamp(aperturaValvula, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejemplo de escritura del nivel del tanque al PLC
|
|
||||||
if (_tanque1 != null && !string.IsNullOrEmpty(TagNivelTanque1))
|
|
||||||
{
|
|
||||||
float nivelTanque = 0; // Implementar cálculo real del nivel
|
|
||||||
EscribirWordTagScaled(TagNivelTanque1, nivelTanque * 100, 0, 100, 0, 27648);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
<UserControl x:Class="CtrEditor.ObjetosSim.ucBasicExample"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
|
||||||
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
|
|
||||||
<UserControl.DataContext>
|
|
||||||
<vm:osBasicExample Ancho="2"/>
|
|
||||||
</UserControl.DataContext>
|
|
||||||
|
|
||||||
<Canvas>
|
|
||||||
<Rectangle x:Name="Transporte"
|
|
||||||
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Fill="{StaticResource BeltBrush}">
|
|
||||||
<Rectangle.RenderTransform>
|
|
||||||
<RotateTransform Angle="{Binding Angulo}"/>
|
|
||||||
</Rectangle.RenderTransform>
|
|
||||||
</Rectangle>
|
|
||||||
</Canvas>
|
|
||||||
</UserControl>
|
|
|
@ -1,245 +0,0 @@
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Media.Animation;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
|
|
||||||
using LibS7Adv;
|
|
||||||
using CtrEditor.Simulacion;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
|
|
||||||
namespace CtrEditor.ObjetosSim
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for ucBasicExample.xaml
|
|
||||||
/// </summary>
|
|
||||||
///
|
|
||||||
|
|
||||||
public partial class osBasicExample : osBase, IosBase
|
|
||||||
{
|
|
||||||
private osBase _osMotor = null;
|
|
||||||
|
|
||||||
private simTransporte SimGeometria;
|
|
||||||
|
|
||||||
public static string NombreClase()
|
|
||||||
{
|
|
||||||
return "Ejemplo Básico";
|
|
||||||
}
|
|
||||||
private string nombre = NombreClase();
|
|
||||||
|
|
||||||
[property: Category("Identificación")]
|
|
||||||
[property: Description("Nombre identificativo del objeto")]
|
|
||||||
[property: Name("Nombre")]
|
|
||||||
public override string Nombre
|
|
||||||
{
|
|
||||||
get => nombre;
|
|
||||||
set => SetProperty(ref nombre, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Simulación")]
|
|
||||||
[property: Description("Velocidad actual del transporte")]
|
|
||||||
[property: Name("Velocidad Actual")]
|
|
||||||
public float velocidadActual;
|
|
||||||
|
|
||||||
partial void OnVelocidadActualChanged(float value)
|
|
||||||
{
|
|
||||||
SetSpeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Invierte el sentido de movimiento")]
|
|
||||||
[property: Name("Invertir Dirección")]
|
|
||||||
bool invertirDireccion;
|
|
||||||
|
|
||||||
partial void OnInvertirDireccionChanged(bool value)
|
|
||||||
{
|
|
||||||
SetSpeed();
|
|
||||||
if (_visualRepresentation is ucBasicExample uc)
|
|
||||||
{
|
|
||||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
|
||||||
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSpeed()
|
|
||||||
{
|
|
||||||
if (InvertirDireccion)
|
|
||||||
SimGeometria?.SetSpeed(-VelocidadActual);
|
|
||||||
else
|
|
||||||
SimGeometria?.SetSpeed(VelocidadActual);
|
|
||||||
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Enlace PLC")]
|
|
||||||
[property: Description("Motor enlazado al transporte")]
|
|
||||||
[property: Name("Motor")]
|
|
||||||
public string motor;
|
|
||||||
|
|
||||||
partial void OnMotorChanged(string value)
|
|
||||||
{
|
|
||||||
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Ancho del transporte")]
|
|
||||||
[property: Name("Ancho")]
|
|
||||||
public float ancho;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Alto del transporte")]
|
|
||||||
[property: Name("Alto")]
|
|
||||||
public float alto;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Ángulo de rotación")]
|
|
||||||
[property: Name("Ángulo")]
|
|
||||||
public float angulo;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Coeficiente de fricción")]
|
|
||||||
[property: Name("Coeficiente Fricción")]
|
|
||||||
public float frictionCoefficient;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Velocidad máxima a 50Hz")]
|
|
||||||
[property: Name("Velocidad Max 50Hz")]
|
|
||||||
public float velMax50hz;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Tiempo de rampa")]
|
|
||||||
[property: Name("Tiempo Rampa")]
|
|
||||||
public float tiempoRampa;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Información")]
|
|
||||||
[property: Description("Estado de marcha")]
|
|
||||||
[property: Name("En Marcha")]
|
|
||||||
public bool esMarcha;
|
|
||||||
|
|
||||||
|
|
||||||
private void ActualizarGeometrias()
|
|
||||||
{
|
|
||||||
if (_visualRepresentation is ucBasicExample uc)
|
|
||||||
{
|
|
||||||
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
|
|
||||||
SetSpeed();
|
|
||||||
}
|
|
||||||
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
|
|
||||||
}
|
|
||||||
|
|
||||||
public osBasicExample()
|
|
||||||
{
|
|
||||||
Ancho = 1;
|
|
||||||
Alto = 0.10f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SimulationStop()
|
|
||||||
{
|
|
||||||
// Se llama al detener la simulacion
|
|
||||||
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
|
|
||||||
}
|
|
||||||
public override void UpdateGeometryStart()
|
|
||||||
{
|
|
||||||
// Se llama antes de la simulacion
|
|
||||||
ActualizarGeometrias();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
|
||||||
{
|
|
||||||
if (_osMotor != null)
|
|
||||||
{
|
|
||||||
if (_osMotor is osVMmotorSim motor)
|
|
||||||
VelocidadActual = motor.Velocidad;
|
|
||||||
}
|
|
||||||
else if (Motor.Length > 0)
|
|
||||||
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ucLoaded()
|
|
||||||
{
|
|
||||||
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
|
|
||||||
// crear el objeto de simulacion
|
|
||||||
base.ucLoaded();
|
|
||||||
|
|
||||||
if (_visualRepresentation is ucBasicExample uc)
|
|
||||||
{
|
|
||||||
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
|
|
||||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public override void ucUnLoaded()
|
|
||||||
{
|
|
||||||
// El UserControl se esta eliminando
|
|
||||||
// eliminar el objeto de simulacion
|
|
||||||
simulationManager.Remove(SimGeometria);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class ucBasicExample : UserControl, IDataContainer
|
|
||||||
{
|
|
||||||
public osBase? Datos { get; set; }
|
|
||||||
|
|
||||||
public ucBasicExample()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
this.Loaded += OnLoaded;
|
|
||||||
this.Unloaded += OnUnloaded;
|
|
||||||
}
|
|
||||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Datos?.ucLoaded();
|
|
||||||
}
|
|
||||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Datos?.ucUnLoaded();
|
|
||||||
}
|
|
||||||
public void Resize(float width, float height)
|
|
||||||
{
|
|
||||||
if (Datos is osBasicExample datos)
|
|
||||||
datos.Ancho += PixelToMeter.Instance.calc.PixelsToMeters(width);
|
|
||||||
}
|
|
||||||
public void Move(float LeftPixels, float TopPixels)
|
|
||||||
{
|
|
||||||
if (Datos != null)
|
|
||||||
{
|
|
||||||
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
|
|
||||||
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float Angle()
|
|
||||||
{
|
|
||||||
if (Datos != null)
|
|
||||||
if (Datos is osBasicExample datos)
|
|
||||||
return datos.Angulo;
|
|
||||||
return 0f;
|
|
||||||
}
|
|
||||||
public void Rotate(float Angle)
|
|
||||||
{
|
|
||||||
if (Datos != null)
|
|
||||||
if (Datos is osBasicExample datos)
|
|
||||||
datos.Angulo += Angle;
|
|
||||||
}
|
|
||||||
public void Highlight(bool State) { }
|
|
||||||
public int ZIndex()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<UserControl x:Class="CtrEditor.ObjetosSim.ucSistemaFluidos"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
|
|
||||||
|
|
||||||
<UserControl.DataContext>
|
|
||||||
<vm:osSistemaFluidos/>
|
|
||||||
</UserControl.DataContext>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid x:Name="ContenedorVisual" Background="Transparent">
|
|
||||||
<!-- El DrawingVisual renderizará las partículas aquí -->
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Panel informativo opcional que se puede ocultar -->
|
|
||||||
<Border Padding="5"
|
|
||||||
Background="#60000000"
|
|
||||||
CornerRadius="5"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Margin="5">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="{Binding Nombre}"
|
|
||||||
Foreground="White"
|
|
||||||
FontWeight="Bold"/>
|
|
||||||
<TextBlock Text="{Binding NumeroParticulas, StringFormat='Partículas: {0}'}"
|
|
||||||
Foreground="White"/>
|
|
||||||
<TextBlock Text="{Binding Fps, StringFormat='FPS: {0:F1}'}"
|
|
||||||
Foreground="White"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
|
@ -1,188 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<UserControl x:Class="CtrEditor.ObjetosSim.ucTuberiaFluido"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
|
|
||||||
xmlns:ctr="clr-namespace:CtrEditor">
|
|
||||||
|
|
||||||
<UserControl.DataContext>
|
|
||||||
<local:osTuberiaFluido/>
|
|
||||||
</UserControl.DataContext>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Path x:Name="TuberiaPath"
|
|
||||||
Data="{Binding PathData}"
|
|
||||||
StrokeEndLineCap="Round"
|
|
||||||
StrokeStartLineCap="Round"
|
|
||||||
StrokeLineJoin="Round">
|
|
||||||
<Path.Effect>
|
|
||||||
<BlurEffect Radius="0.5"/>
|
|
||||||
</Path.Effect>
|
|
||||||
</Path>
|
|
||||||
|
|
||||||
<!-- Visualización opcional de la densidad del fluido dentro de la tubería -->
|
|
||||||
<Path x:Name="FluidoPath"
|
|
||||||
Data="{Binding PathData}"
|
|
||||||
Opacity="{Binding DensidadFluido}"
|
|
||||||
StrokeEndLineCap="Round"
|
|
||||||
StrokeStartLineCap="Round"
|
|
||||||
StrokeLineJoin="Round">
|
|
||||||
</Path>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
|
@ -1,268 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using CtrEditor.FuncionesBase;
|
|
||||||
using CtrEditor.Simulacion.Fluids.Components;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace CtrEditor.ObjetosSim
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Lógica para ucTuberiaFluido.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class ucTuberiaFluido : UserControl, IDataContainer
|
|
||||||
{
|
|
||||||
public osBase? Datos { get; set; }
|
|
||||||
public int zIndex_fromFrames { get; set; }
|
|
||||||
|
|
||||||
public ucTuberiaFluido()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
this.Loaded += OnLoaded;
|
|
||||||
this.Unloaded += OnUnloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Datos?.ucLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Datos?.ucUnLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Highlight(bool State) { }
|
|
||||||
|
|
||||||
public ZIndexEnum ZIndex_Base()
|
|
||||||
{
|
|
||||||
return ZIndexEnum.Estaticos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ViewModel para la tubería de fluidos
|
|
||||||
/// </summary>
|
|
||||||
public partial class osTuberiaFluido : osBase, IosBase
|
|
||||||
{
|
|
||||||
// Tubería en la simulación de fluidos
|
|
||||||
private Tuberia _tuberia;
|
|
||||||
|
|
||||||
// Referencia al sistema de fluidos
|
|
||||||
private osSistemaFluidos _sistemaFluidos;
|
|
||||||
|
|
||||||
// Propiedades visuales
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Diámetro de la tubería en metros")]
|
|
||||||
[property: Name("Diámetro")]
|
|
||||||
private float diametro = 0.05f;
|
|
||||||
|
|
||||||
partial void OnDiametroChanged(float value)
|
|
||||||
{
|
|
||||||
// Actualizar geometría si la tubería ya existe
|
|
||||||
if (_tuberia != null)
|
|
||||||
{
|
|
||||||
// No es posible cambiar el diámetro directamente, recrear la tubería
|
|
||||||
ReconstruirTuberia();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Diámetro interno para visualización del fluido")]
|
|
||||||
[property: Name("Diámetro Interno")]
|
|
||||||
private float diametroInterno;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Color de la tubería")]
|
|
||||||
[property: Name("Color Tubería")]
|
|
||||||
private System.Windows.Media.Color color = System.Windows.Media.Colors.Gray;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Color del fluido")]
|
|
||||||
[property: Name("Color Fluido")]
|
|
||||||
private System.Windows.Media.Color colorFluido = System.Windows.Media.Colors.CornflowerBlue;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Información")]
|
|
||||||
[property: Description("Datos del path para dibujar la tubería")]
|
|
||||||
[property: Name("Path Data")]
|
|
||||||
private string pathData;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Simulación")]
|
|
||||||
[property: Description("Densidad del fluido (0-1)")]
|
|
||||||
[property: Name("Densidad Fluido")]
|
|
||||||
private double densidadFluido = 0.7;
|
|
||||||
|
|
||||||
// Lista de puntos que forman la tubería
|
|
||||||
private List<Vector2> _puntos = new List<Vector2>();
|
|
||||||
|
|
||||||
// Nombre de la clase para identificación
|
|
||||||
public static string NombreClase()
|
|
||||||
{
|
|
||||||
return "Tubería de Fluido";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string nombre = NombreClase();
|
|
||||||
|
|
||||||
[property: Category("Identificación")]
|
|
||||||
[property: Description("Nombre identificativo del objeto")]
|
|
||||||
[property: Name("Nombre")]
|
|
||||||
public override string Nombre
|
|
||||||
{
|
|
||||||
get => nombre;
|
|
||||||
set => SetProperty(ref nombre, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Agrega un punto a la tubería
|
|
||||||
/// </summary>
|
|
||||||
public void AgregarPunto(float x, float y)
|
|
||||||
{
|
|
||||||
_puntos.Add(new Vector2(x, y));
|
|
||||||
ActualizarPathData();
|
|
||||||
|
|
||||||
// Si la tubería ya está creada, agregar el punto a la simulación
|
|
||||||
if (_tuberia != null)
|
|
||||||
{
|
|
||||||
_tuberia.AgregarPunto(new Vector2(x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actualiza la representación visual de la tubería
|
|
||||||
/// </summary>
|
|
||||||
private void ActualizarPathData()
|
|
||||||
{
|
|
||||||
if (_puntos.Count < 2) return;
|
|
||||||
|
|
||||||
var converter = new MeterToPixelConverter();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
// Iniciar el path
|
|
||||||
sb.Append($"M {converter.Convert(_puntos[0].X, null, null, null)} {converter.Convert(_puntos[0].Y, null, null, null)} ");
|
|
||||||
|
|
||||||
// Añadir los demás puntos
|
|
||||||
for (int i = 1; i < _puntos.Count; i++)
|
|
||||||
{
|
|
||||||
sb.Append($"L {converter.Convert(_puntos[i].X, null, null, null)} {converter.Convert(_puntos[i].Y, null, null, null)} ");
|
|
||||||
}
|
|
||||||
|
|
||||||
PathData = sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reconstruye la tubería en la simulación
|
|
||||||
/// </summary>
|
|
||||||
private void ReconstruirTuberia()
|
|
||||||
{
|
|
||||||
if (_sistemaFluidos != null)
|
|
||||||
{
|
|
||||||
// Eliminar la tubería existente
|
|
||||||
if (_tuberia != null)
|
|
||||||
{
|
|
||||||
_sistemaFluidos.EliminarComponente(_tuberia);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crear nueva tubería
|
|
||||||
_tuberia = _sistemaFluidos.CrearTuberia(Diametro);
|
|
||||||
|
|
||||||
// Agregar todos los puntos existentes
|
|
||||||
foreach (var punto in _puntos)
|
|
||||||
{
|
|
||||||
_tuberia.AgregarPunto(punto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
public osTuberiaFluido()
|
|
||||||
{
|
|
||||||
DiametroInterno = Diametro * 0.8f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMove(float LeftPixels, float TopPixels)
|
|
||||||
{
|
|
||||||
// Mover todos los puntos de la tubería
|
|
||||||
if (_puntos.Count > 0)
|
|
||||||
{
|
|
||||||
Vector2 delta = new Vector2(Left - CanvasGetLeftinMeter(), Top - CanvasGetTopinMeter());
|
|
||||||
|
|
||||||
for (int i = 0; i < _puntos.Count; i++)
|
|
||||||
{
|
|
||||||
_puntos[i] += delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
ActualizarPathData();
|
|
||||||
ReconstruirTuberia();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ucLoaded()
|
|
||||||
{
|
|
||||||
base.ucLoaded();
|
|
||||||
|
|
||||||
// Buscar el sistema de fluidos en la simulación
|
|
||||||
if (_mainViewModel?.ObjetosSimulables != null)
|
|
||||||
{
|
|
||||||
foreach (var obj in _mainViewModel.ObjetosSimulables)
|
|
||||||
{
|
|
||||||
if (obj is osSistemaFluidos sistemaFluidos)
|
|
||||||
{
|
|
||||||
_sistemaFluidos = sistemaFluidos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si no hay puntos, agregar dos puntos iniciales
|
|
||||||
if (_puntos.Count == 0)
|
|
||||||
{
|
|
||||||
_puntos.Add(new Vector2(Left, Top));
|
|
||||||
_puntos.Add(new Vector2(Left + Ancho, Top));
|
|
||||||
ActualizarPathData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crear la tubería en la simulación
|
|
||||||
ReconstruirTuberia();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ucUnLoaded()
|
|
||||||
{
|
|
||||||
// Eliminar la tubería de la simulación
|
|
||||||
if (_sistemaFluidos != null && _tuberia != null)
|
|
||||||
{
|
|
||||||
_sistemaFluidos.EliminarComponente(_tuberia);
|
|
||||||
_tuberia = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateControl(int elapsedMilliseconds)
|
|
||||||
{
|
|
||||||
// Actualizar la densidad del fluido basada en la simulación
|
|
||||||
if (_sistemaFluidos != null && _sistemaFluidos._simFluidos != null && _puntos.Count > 0)
|
|
||||||
{
|
|
||||||
// Calcular el centro aproximado de la tubería
|
|
||||||
Vector2 centro = Vector2.Zero;
|
|
||||||
foreach (var punto in _puntos)
|
|
||||||
{
|
|
||||||
centro += punto;
|
|
||||||
}
|
|
||||||
centro /= _puntos.Count;
|
|
||||||
|
|
||||||
// Obtener densidad en esa posición
|
|
||||||
DensidadFluido = _sistemaFluidos._simFluidos.ObtenerDensidadEnPosicion(centro);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
<UserControl x:Class="CtrEditor.ObjetosSim.ucValvulaFluido"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:local="clr-namespace:CtrEditor.ObjetosSim">
|
|
||||||
|
|
||||||
<UserControl.DataContext>
|
|
||||||
<local:osValvulaFluido/>
|
|
||||||
</UserControl.DataContext>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Canvas RenderTransformOrigin="0.5,0.5">
|
|
||||||
<Canvas.RenderTransform>
|
|
||||||
<TransformGroup>
|
|
||||||
<RotateTransform Angle="{Binding Angulo}"/>
|
|
||||||
</TransformGroup>
|
|
||||||
</Canvas.RenderTransform>
|
|
||||||
|
|
||||||
<!-- Cuerpo de la válvula -->
|
|
||||||
<Rectangle Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"
|
|
||||||
RadiusX="5" RadiusY="5"
|
|
||||||
Canvas.Left="{Binding OffsetXRectangulo, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Canvas.Top="{Binding OffsetYRectangulo, Converter={StaticResource MeterToPixelConverter}}"/>
|
|
||||||
|
|
||||||
<!-- Indicador de apertura -->
|
|
||||||
<Rectangle Width="{Binding AperturaVisual, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Height="{Binding GrosorIndicador, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Fill="{Binding ColorIndicador, Converter={StaticResource ColorToBrushConverter}}"
|
|
||||||
Canvas.Left="{Binding OffsetXIndicador, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Canvas.Top="{Binding OffsetYIndicador, Converter={StaticResource MeterToPixelConverter}}"/>
|
|
||||||
|
|
||||||
<!-- Texto con el valor numérico de apertura -->
|
|
||||||
<TextBlock Text="{Binding ValorApertura, StringFormat='{}{0:P0}'}"
|
|
||||||
Canvas.Left="{Binding OffsetXTexto, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
Canvas.Top="{Binding OffsetYTexto, Converter={StaticResource MeterToPixelConverter}}"
|
|
||||||
FontWeight="Bold"
|
|
||||||
Foreground="White"
|
|
||||||
FontSize="12"/>
|
|
||||||
</Canvas>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
|
@ -1,217 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using CtrEditor.FuncionesBase;
|
|
||||||
using CtrEditor.Simulacion.Fluids.Components;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using Color = System.Windows.Media.Color;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using LibS7Adv;
|
|
||||||
|
|
||||||
namespace CtrEditor.ObjetosSim
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Lógica para ucValvulaFluido.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class ucValvulaFluido : UserControl, IDataContainer
|
|
||||||
{
|
|
||||||
public osBase? Datos { get; set; }
|
|
||||||
public int zIndex_fromFrames { get; set; }
|
|
||||||
|
|
||||||
public ucValvulaFluido()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
this.Loaded += OnLoaded;
|
|
||||||
this.Unloaded += OnUnloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Datos?.ucLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
Datos?.ucUnLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Highlight(bool State) { }
|
|
||||||
|
|
||||||
public ZIndexEnum ZIndex_Base()
|
|
||||||
{
|
|
||||||
return ZIndexEnum.Estaticos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ViewModel para la válvula de fluidos
|
|
||||||
/// </summary>
|
|
||||||
public partial class osValvulaFluido : osBase, IosBase
|
|
||||||
{
|
|
||||||
// Válvula en la simulación de fluidos
|
|
||||||
private Valvula _valvula;
|
|
||||||
|
|
||||||
// Referencia al sistema de fluidos
|
|
||||||
private osSistemaFluidos _sistemaFluidos;
|
|
||||||
|
|
||||||
// Propiedades dimensionales
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Ancho de la válvula en metros")]
|
|
||||||
[property: Name("Ancho")]
|
|
||||||
private float ancho = 0.1f;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Alto de la válvula en metros")]
|
|
||||||
[property: Name("Alto")]
|
|
||||||
private float alto = 0.06f;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Configuración")]
|
|
||||||
[property: Description("Diámetro de la tubería conectada")]
|
|
||||||
[property: Name("Diámetro Tubería")]
|
|
||||||
private float diametroTuberia = 0.05f;
|
|
||||||
|
|
||||||
// Propiedades visuales
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Color de la válvula")]
|
|
||||||
[property: Name("Color")]
|
|
||||||
private Color color = Colors.Silver;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Apariencia")]
|
|
||||||
[property: Description("Color del indicador de apertura")]
|
|
||||||
[property: Name("Color Indicador")]
|
|
||||||
private Color colorIndicador = Colors.CornflowerBlue;
|
|
||||||
|
|
||||||
// Propiedades de funcionamiento
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Simulación")]
|
|
||||||
[property: Description("Apertura de la válvula (0-1)")]
|
|
||||||
[property: Name("Apertura")]
|
|
||||||
private float apertura = 1.0f;
|
|
||||||
|
|
||||||
partial void OnAperturaChanged(float value)
|
|
||||||
{
|
|
||||||
Apertura = Math.Clamp(value, 0, 1);
|
|
||||||
|
|
||||||
// Actualizar la válvula en la simulación
|
|
||||||
if (_valvula != null)
|
|
||||||
{
|
|
||||||
_valvula.Apertura = Apertura;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tag PLC para la válvula
|
|
||||||
[ObservableProperty]
|
|
||||||
[property: Category("Enlace PLC")]
|
|
||||||
[property: Description("Tag PLC para lectura/escritura de apertura (0-100%)")]
|
|
||||||
[property: Name("Tag Apertura")]
|
|
||||||
private string tagApertura;
|
|
||||||
|
|
||||||
// Propiedades calculadas para visualización
|
|
||||||
public float AperturaVisual => Ancho * 0.8f * Apertura;
|
|
||||||
public float GrosorIndicador => Alto * 0.2f;
|
|
||||||
public float ValorApertura => Apertura;
|
|
||||||
|
|
||||||
// Propiedades para posicionamiento de elementos
|
|
||||||
public float OffsetXRectangulo => -Ancho / 2;
|
|
||||||
public float OffsetYRectangulo => -Alto / 2;
|
|
||||||
public float OffsetXIndicador => -Ancho * 0.4f;
|
|
||||||
public float OffsetYIndicador => -GrosorIndicador / 2;
|
|
||||||
public float OffsetXTexto => -Ancho * 0.15f;
|
|
||||||
public float OffsetYTexto => Alto * 0.1f;
|
|
||||||
|
|
||||||
// Nombre de la clase para identificación
|
|
||||||
public static string NombreClase()
|
|
||||||
{
|
|
||||||
return "Válvula de Fluido";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string nombre = NombreClase();
|
|
||||||
|
|
||||||
[property: Category("Identificación")]
|
|
||||||
[property: Description("Nombre identificativo del objeto")]
|
|
||||||
[property: Name("Nombre")]
|
|
||||||
public override string Nombre
|
|
||||||
{
|
|
||||||
get => nombre;
|
|
||||||
set => SetProperty(ref nombre, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
public osValvulaFluido()
|
|
||||||
{
|
|
||||||
// Constructor
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMove(float LeftPixels, float TopPixels)
|
|
||||||
{
|
|
||||||
if (_valvula != null)
|
|
||||||
{
|
|
||||||
// Actualizar posición de la válvula en la simulación
|
|
||||||
_valvula.ActualizarPosicion(new Vector2(Left, Top));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnRotate(float Delta_Angle)
|
|
||||||
{
|
|
||||||
// La rotación visual ya está manejada por el XAML
|
|
||||||
// No necesita actualizaciones adicionales en la simulación
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ucLoaded()
|
|
||||||
{
|
|
||||||
base.ucLoaded();
|
|
||||||
|
|
||||||
// Buscar el sistema de fluidos en la simulación
|
|
||||||
if (_mainViewModel?.ObjetosSimulables != null)
|
|
||||||
{
|
|
||||||
foreach (var obj in _mainViewModel.ObjetosSimulables)
|
|
||||||
{
|
|
||||||
if (obj is osSistemaFluidos sistemaFluidos)
|
|
||||||
{
|
|
||||||
_sistemaFluidos = sistemaFluidos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crear la válvula en la simulación
|
|
||||||
if (_sistemaFluidos != null)
|
|
||||||
{
|
|
||||||
_valvula = _sistemaFluidos.CrearValvula(
|
|
||||||
new Vector2(Left, Top),
|
|
||||||
DiametroTuberia,
|
|
||||||
Apertura
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ucUnLoaded()
|
|
||||||
{
|
|
||||||
// Eliminar la válvula de la simulación
|
|
||||||
if (_sistemaFluidos != null && _valvula != null)
|
|
||||||
{
|
|
||||||
_sistemaFluidos.EliminarComponente(_valvula);
|
|
||||||
_valvula = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
|
||||||
{
|
|
||||||
// Manejar la comunicación con PLC
|
|
||||||
if (!string.IsNullOrEmpty(TagApertura))
|
|
||||||
{
|
|
||||||
float aperturaPlc = LeerWordTagScaled(TagApertura) / 100.0f;
|
|
||||||
Apertura = Math.Clamp(aperturaPlc, 0, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1054
Simulacion/Aether.cs
1054
Simulacion/Aether.cs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,641 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Shapes;
|
|
||||||
using FarseerPhysics.Dynamics;
|
|
||||||
using FarseerPhysics.Factories;
|
|
||||||
using FarseerPhysics.Collision.Shapes;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
using FarseerPhysics.Common;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using FarseerPhysics.Dynamics.Joints;
|
|
||||||
using CtrEditor.ObjetosSim;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
|
|
||||||
namespace CtrEditor.Simulacion
|
|
||||||
{
|
|
||||||
public class simBase
|
|
||||||
{
|
|
||||||
public Body Body { get; protected set; }
|
|
||||||
public World _world;
|
|
||||||
|
|
||||||
public void RemoverBody()
|
|
||||||
{
|
|
||||||
if (Body != null)
|
|
||||||
{
|
|
||||||
_world.RemoveBody(Body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void SetPosition(float x, float y)
|
|
||||||
{
|
|
||||||
Body.SetTransform(new Vector2(x, y), Body.Rotation);
|
|
||||||
}
|
|
||||||
public void SetPosition(Vector2 centro)
|
|
||||||
{
|
|
||||||
Body.SetTransform(centro, Body.Rotation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class simCurve : simBase
|
|
||||||
{
|
|
||||||
private float _innerRadius;
|
|
||||||
private float _outerRadius;
|
|
||||||
private float _startAngle;
|
|
||||||
private float _endAngle;
|
|
||||||
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora
|
|
||||||
|
|
||||||
public simCurve(World world, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
|
|
||||||
{
|
|
||||||
_world = world;
|
|
||||||
_innerRadius = innerRadius;
|
|
||||||
_outerRadius = outerRadius;
|
|
||||||
_startAngle = MathHelper.ToRadians(startAngle);
|
|
||||||
_endAngle = MathHelper.ToRadians(endAngle);
|
|
||||||
Create(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Create(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
|
|
||||||
{
|
|
||||||
if (_world == null) return;
|
|
||||||
_innerRadius = innerRadius;
|
|
||||||
_outerRadius = outerRadius;
|
|
||||||
_startAngle = MathHelper.ToRadians(startAngle);
|
|
||||||
_endAngle = MathHelper.ToRadians(endAngle);
|
|
||||||
Create(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Create(Vector2 position)
|
|
||||||
{
|
|
||||||
RemoverBody();
|
|
||||||
|
|
||||||
// Crear la geometría del sensor de curva
|
|
||||||
List<Vertices> segments = CreateCurveVertices(_innerRadius, _outerRadius, _startAngle, _endAngle);
|
|
||||||
Body = new Body(_world);
|
|
||||||
foreach (var segment in segments)
|
|
||||||
{
|
|
||||||
var shape = new PolygonShape(segment, 1f);
|
|
||||||
var fixture = Body.CreateFixture(shape);
|
|
||||||
fixture.IsSensor = true;
|
|
||||||
}
|
|
||||||
Body.Position = position;
|
|
||||||
Body.BodyType = BodyType.Static;
|
|
||||||
Body.UserData = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSpeed(float speed)
|
|
||||||
{
|
|
||||||
Speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Vertices> CreateCurveVertices(float innerRadius, float outerRadius, float startAngle, float endAngle)
|
|
||||||
{
|
|
||||||
List<Vertices> verticesList = new List<Vertices>();
|
|
||||||
int segments = 32;
|
|
||||||
float angleStep = (endAngle - startAngle) / segments;
|
|
||||||
|
|
||||||
Vertices innerVertices = new Vertices();
|
|
||||||
Vertices outerVertices = new Vertices();
|
|
||||||
|
|
||||||
for (int i = 0; i <= segments; i++)
|
|
||||||
{
|
|
||||||
float angle = startAngle + i * angleStep;
|
|
||||||
innerVertices.Add(new Vector2(innerRadius * (float)Math.Cos(angle), innerRadius * (float)Math.Sin(angle)));
|
|
||||||
outerVertices.Add(new Vector2(outerRadius * (float)Math.Cos(angle), outerRadius * (float)Math.Sin(angle)));
|
|
||||||
}
|
|
||||||
|
|
||||||
outerVertices.Reverse();
|
|
||||||
innerVertices.AddRange(outerVertices);
|
|
||||||
verticesList.Add(innerVertices);
|
|
||||||
|
|
||||||
return verticesList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyCurveEffect(Fixture bottle)
|
|
||||||
{
|
|
||||||
Vector2 centerToBottle = bottle.Body.Position - Body.Position;
|
|
||||||
float distanceToCenter = centerToBottle.Length();
|
|
||||||
|
|
||||||
if (distanceToCenter >= _innerRadius && distanceToCenter <= _outerRadius)
|
|
||||||
{
|
|
||||||
// Calcular la velocidad tangencial
|
|
||||||
float speedMetersPerSecond = Speed / 60.0f;
|
|
||||||
float angularVelocity = speedMetersPerSecond / distanceToCenter;
|
|
||||||
|
|
||||||
// Vector tangente (perpendicular al radio)
|
|
||||||
Vector2 tangent = new Vector2(-centerToBottle.Y, centerToBottle.X);
|
|
||||||
tangent.Normalize();
|
|
||||||
|
|
||||||
// Velocidad deseada
|
|
||||||
Vector2 desiredVelocity = tangent * angularVelocity * distanceToCenter;
|
|
||||||
bottle.Body.LinearVelocity = desiredVelocity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class simDescarte : simBase
|
|
||||||
{
|
|
||||||
private float _radius;
|
|
||||||
|
|
||||||
public simDescarte(World world, float diameter, Vector2 position)
|
|
||||||
{
|
|
||||||
_world = world;
|
|
||||||
_radius = diameter / 2;
|
|
||||||
Create(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDiameter(float diameter)
|
|
||||||
{
|
|
||||||
_radius = diameter / 2;
|
|
||||||
Create(Body.Position); // Recrear el círculo con el nuevo tamaño
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Create(Vector2 position)
|
|
||||||
{
|
|
||||||
RemoverBody();
|
|
||||||
Body = BodyFactory.CreateCircle(_world, _radius, 1f, position);
|
|
||||||
|
|
||||||
Body.FixtureList[0].IsSensor = true;
|
|
||||||
Body.BodyType = BodyType.Static;
|
|
||||||
Body.UserData = this; // Importante para la identificación durante la colisión
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class simTransporte : simBase
|
|
||||||
{
|
|
||||||
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora
|
|
||||||
public float DistanceGuide2Guide { get; set; }
|
|
||||||
public bool TransportWithGuides = false;
|
|
||||||
|
|
||||||
public simTransporte(World world, float width, float height, Vector2 position, float angle = 0)
|
|
||||||
{
|
|
||||||
_world = world;
|
|
||||||
Create(width, height, position, angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float Angle
|
|
||||||
{
|
|
||||||
get { return MathHelper.ToDegrees(Body.Rotation); }
|
|
||||||
set { Body.Rotation = MathHelper.ToRadians(value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public new void SetPosition(float x, float y)
|
|
||||||
{
|
|
||||||
Body.Position = new Vector2(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSpeed(float speed)
|
|
||||||
{
|
|
||||||
Speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDimensions(float width, float height)
|
|
||||||
{
|
|
||||||
Body.DestroyFixture(Body.FixtureList[0]);
|
|
||||||
|
|
||||||
var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f);
|
|
||||||
Body.CreateFixture(newShape);
|
|
||||||
}
|
|
||||||
public void Create(float width, float height, Vector2 position, float angle = 0)
|
|
||||||
{
|
|
||||||
RemoverBody();
|
|
||||||
Body = BodyFactory.CreateRectangle(_world, width, height, 1f, position);
|
|
||||||
Body.FixtureList[0].IsSensor = true;
|
|
||||||
Body.BodyType = BodyType.Static;
|
|
||||||
Body.Rotation = MathHelper.ToRadians(angle);
|
|
||||||
Body.UserData = this; // Importante para la identificación durante la colisión
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class simBarrera : simBase
|
|
||||||
{
|
|
||||||
public bool LuzCortada = false;
|
|
||||||
|
|
||||||
public simBarrera(World world, float width, float height, Vector2 position, float angle = 0)
|
|
||||||
{
|
|
||||||
_world = world;
|
|
||||||
Create(width, height, position, angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float Angle
|
|
||||||
{
|
|
||||||
get { return MathHelper.ToDegrees(Body.Rotation); }
|
|
||||||
set { Body.Rotation = MathHelper.ToRadians(value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public new void SetPosition(float x, float y)
|
|
||||||
{
|
|
||||||
Body.Position = new Vector2(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDimensions(float width, float height)
|
|
||||||
{
|
|
||||||
Body.DestroyFixture(Body.FixtureList[0]);
|
|
||||||
|
|
||||||
var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f);
|
|
||||||
Body.CreateFixture(newShape);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Create(float width, float height, Vector2 position, float angle = 0)
|
|
||||||
{
|
|
||||||
RemoverBody();
|
|
||||||
Body = BodyFactory.CreateRectangle(_world, width, height, 1f, position);
|
|
||||||
Body.FixtureList[0].IsSensor = true;
|
|
||||||
Body.BodyType = BodyType.Static;
|
|
||||||
Body.Rotation = MathHelper.ToRadians(angle);
|
|
||||||
Body.UserData = this; // Importante para la identificación durante la colisión
|
|
||||||
LuzCortada = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class simGuia : simBase
|
|
||||||
{
|
|
||||||
public simGuia(World world, Vector2 start, Vector2 end)
|
|
||||||
{
|
|
||||||
_world = world;
|
|
||||||
Create(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Create(Vector2 start, Vector2 end)
|
|
||||||
{
|
|
||||||
RemoverBody();
|
|
||||||
Body = BodyFactory.CreateEdge(_world, start, end);
|
|
||||||
Body.BodyType = BodyType.Static;
|
|
||||||
Body.UserData = this; // Importante para la identificación durante la colisión
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateVertices(Vector2 newStart, Vector2 newEnd)
|
|
||||||
{
|
|
||||||
Create(newStart, newEnd); // Recrear la línea con nuevos vértices
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class simBotella : simBase
|
|
||||||
{
|
|
||||||
private float _radius;
|
|
||||||
private float _mass;
|
|
||||||
public bool Descartar = false;
|
|
||||||
|
|
||||||
public simBotella(World world, float diameter, Vector2 position, float mass)
|
|
||||||
{
|
|
||||||
_world = world;
|
|
||||||
_radius = diameter / 2;
|
|
||||||
_mass = mass;
|
|
||||||
Create(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float CenterX
|
|
||||||
{
|
|
||||||
get { return Body.Position.X; }
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public float CenterY
|
|
||||||
{
|
|
||||||
get { return Body.Position.Y; }
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2 Center
|
|
||||||
{
|
|
||||||
get { return Body.Position; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public float Mass
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_mass <= 0)
|
|
||||||
_mass = 1;
|
|
||||||
return _mass;
|
|
||||||
}
|
|
||||||
set { _mass = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Create(Vector2 position)
|
|
||||||
{
|
|
||||||
RemoverBody();
|
|
||||||
Body = BodyFactory.CreateCircle(_world, _radius, 0.2f, position);
|
|
||||||
Body.BodyType = BodyType.Dynamic;
|
|
||||||
|
|
||||||
// Restablecer manejador de eventos de colisión
|
|
||||||
Body.OnCollision += HandleCollision;
|
|
||||||
//Body.OnSeparation += HandleOnSeparation;
|
|
||||||
|
|
||||||
Body.UserData = this; // Importante para la identificación durante la colisión
|
|
||||||
|
|
||||||
// Configurar la fricción
|
|
||||||
Body.Friction = 0.3f; // Ajustar según sea necesario para tu simulación
|
|
||||||
|
|
||||||
// Configurar amortiguamiento
|
|
||||||
Body.LinearDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad lineal
|
|
||||||
Body.AngularDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad angular
|
|
||||||
Body.Restitution = 0.2f; // Baja restitución para menos rebote
|
|
||||||
// Body.IsBullet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDiameter(float diameter)
|
|
||||||
{
|
|
||||||
_radius = diameter / 2;
|
|
||||||
Create(Body.Position); // Recrear el círculo con el nuevo tamaño
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetMass(float mass)
|
|
||||||
{
|
|
||||||
Mass = mass;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool HandleCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact)
|
|
||||||
{
|
|
||||||
if (fixtureB.Body.UserData is simBarrera Sensor)
|
|
||||||
{
|
|
||||||
Sensor.LuzCortada = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (fixtureB.Body.UserData is simCurve curve)
|
|
||||||
{
|
|
||||||
curve.ApplyCurveEffect(fixtureA);
|
|
||||||
return true; // No aplicar respuestas físicas
|
|
||||||
}
|
|
||||||
else if (fixtureB.Body.UserData is simDescarte)
|
|
||||||
{
|
|
||||||
Descartar = true;
|
|
||||||
return true;
|
|
||||||
} else if (fixtureB.Body.UserData is simTransporte)
|
|
||||||
{
|
|
||||||
simTransporte conveyor = fixtureB.Body.UserData as simTransporte;
|
|
||||||
|
|
||||||
if ( conveyor.Speed != 0 ) {
|
|
||||||
|
|
||||||
CircleShape circleShape = fixtureA.Shape as CircleShape;
|
|
||||||
PolygonShape polygonShape = fixtureB.Shape as PolygonShape;
|
|
||||||
|
|
||||||
// Obtener centro y radio del círculo
|
|
||||||
Vector2 centroCirculo = fixtureA.Body.Position;
|
|
||||||
float radio = circleShape.Radius;
|
|
||||||
|
|
||||||
// Obtener los vértices del polígono (rectángulo)
|
|
||||||
Vector2[] vertices = new Vector2[polygonShape.Vertices.Count];
|
|
||||||
float cos = (float)Math.Cos(fixtureB.Body.Rotation);
|
|
||||||
float sin = (float)Math.Sin(fixtureB.Body.Rotation);
|
|
||||||
|
|
||||||
for (int i = 0; i < polygonShape.Vertices.Count; i++)
|
|
||||||
{
|
|
||||||
Vector2 vertex = polygonShape.Vertices[i];
|
|
||||||
float rotatedX = vertex.X * cos - vertex.Y * sin + fixtureB.Body.Position.X;
|
|
||||||
float rotatedY = vertex.X * sin + vertex.Y * cos + fixtureB.Body.Position.Y;
|
|
||||||
vertices[i] = new Vector2(rotatedX, rotatedY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calcular el porcentaje de la superficie compartida
|
|
||||||
float porcentajeCompartido = InterseccionCirculoRectangulo.CalcularSuperficieCompartida(vertices, centroCirculo, radio);
|
|
||||||
|
|
||||||
// Aplicar el efecto del transportador usando el porcentaje calculado
|
|
||||||
if (conveyor.TransportWithGuides)
|
|
||||||
if (conveyor.DistanceGuide2Guide <= radio * 2)
|
|
||||||
CenterFixtureOnConveyor(fixtureA, conveyor);
|
|
||||||
ApplyConveyorEffect(conveyor, fixtureA, porcentajeCompartido);
|
|
||||||
|
|
||||||
}
|
|
||||||
return true; // No aplicar respuestas físicas
|
|
||||||
}
|
|
||||||
return true; // No aplicar respuestas físicas
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyConveyorEffect(simTransporte conveyor, Fixture circleFixture, float porcentajeCompartido)
|
|
||||||
{
|
|
||||||
float speedMetersPerSecond = conveyor.Speed / 60.0f;
|
|
||||||
Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond;
|
|
||||||
circleFixture.Body.LinearVelocity += desiredVelocity * porcentajeCompartido;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CenterFixtureOnConveyor(Fixture fixtureA, simTransporte conveyor)
|
|
||||||
{
|
|
||||||
// Obtener el centro del conveyor
|
|
||||||
Vector2 conveyorCenter = conveyor.Body.Position;
|
|
||||||
|
|
||||||
// Calcular el vector de la línea horizontal centrada de conveyor
|
|
||||||
float halfDistance = conveyor.DistanceGuide2Guide / 2;
|
|
||||||
float cos = (float)Math.Cos(conveyor.Body.Rotation);
|
|
||||||
float sin = (float)Math.Sin(conveyor.Body.Rotation);
|
|
||||||
|
|
||||||
Vector2 offset = new Vector2(halfDistance * cos, halfDistance * sin);
|
|
||||||
|
|
||||||
// Línea horizontal centrada de conveyor en el espacio del mundo
|
|
||||||
Vector2 lineStart = conveyorCenter - offset;
|
|
||||||
Vector2 lineEnd = conveyorCenter + offset;
|
|
||||||
|
|
||||||
// Proyectar el centro de fixtureA sobre la línea horizontal
|
|
||||||
Vector2 fixtureCenter = fixtureA.Body.Position;
|
|
||||||
Vector2 closestPoint = ProjectPointOntoLine(fixtureCenter, lineStart, lineEnd);
|
|
||||||
|
|
||||||
// Mover fixtureA al punto más cercano en la línea horizontal
|
|
||||||
fixtureA.Body.Position = closestPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector2 ProjectPointOntoLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
|
|
||||||
{
|
|
||||||
Vector2 lineDirection = lineEnd - lineStart;
|
|
||||||
lineDirection.Normalize();
|
|
||||||
|
|
||||||
Vector2 pointToLineStart = point - lineStart;
|
|
||||||
float projectionLength = Vector2.Dot(pointToLineStart, lineDirection);
|
|
||||||
|
|
||||||
return lineStart + projectionLength * lineDirection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SimulationManagerFP
|
|
||||||
{
|
|
||||||
private World world;
|
|
||||||
private Canvas simulationCanvas;
|
|
||||||
public List<simBase> Cuerpos;
|
|
||||||
|
|
||||||
private Stopwatch stopwatch;
|
|
||||||
private double stopwatch_last;
|
|
||||||
|
|
||||||
public Canvas DebugCanvas { get => simulationCanvas; set => simulationCanvas = value; }
|
|
||||||
|
|
||||||
public SimulationManagerFP()
|
|
||||||
{
|
|
||||||
world = new World(new Vector2(0, 0)); // Vector2.Zero
|
|
||||||
Cuerpos = new List<simBase>();
|
|
||||||
stopwatch = new Stopwatch();
|
|
||||||
stopwatch.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
if (world.BodyList.Count > 0)
|
|
||||||
world.Clear();
|
|
||||||
if (Cuerpos.Count > 0)
|
|
||||||
Cuerpos.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Step()
|
|
||||||
{
|
|
||||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
|
||||||
float elapsedMilliseconds = (float) (stopwatch.Elapsed.TotalMilliseconds - stopwatch_last);
|
|
||||||
stopwatch_last = stopwatch.Elapsed.TotalMilliseconds;
|
|
||||||
|
|
||||||
// Pasar el tiempo transcurrido al método Step
|
|
||||||
world.Step(elapsedMilliseconds / 1000.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(simBase Objeto)
|
|
||||||
{
|
|
||||||
if (Objeto != null)
|
|
||||||
{
|
|
||||||
Objeto.RemoverBody();
|
|
||||||
Cuerpos.Remove(Objeto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public simCurve AddCurve(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
|
|
||||||
{
|
|
||||||
simCurve curva = new simCurve( world, innerRadius, outerRadius, startAngle, endAngle, position);
|
|
||||||
Cuerpos.Add(curva);
|
|
||||||
return curva;
|
|
||||||
}
|
|
||||||
|
|
||||||
public simBotella AddCircle(float diameter, Vector2 position, float mass)
|
|
||||||
{
|
|
||||||
simBotella circle = new simBotella(world, diameter, position, mass);
|
|
||||||
Cuerpos.Add(circle);
|
|
||||||
return circle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public simTransporte AddRectangle(float width, float height, Vector2 position, float angle)
|
|
||||||
{
|
|
||||||
simTransporte rectangle = new simTransporte(world, width, height, position, angle);
|
|
||||||
Cuerpos.Add(rectangle);
|
|
||||||
return rectangle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public simBarrera AddBarrera(float width, float height, Vector2 position, float angle)
|
|
||||||
{
|
|
||||||
simBarrera rectangle = new simBarrera(world, width, height, position, angle);
|
|
||||||
Cuerpos.Add(rectangle);
|
|
||||||
return rectangle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public simGuia AddLine(Vector2 start, Vector2 end)
|
|
||||||
{
|
|
||||||
simGuia line = new simGuia(world, start, end);
|
|
||||||
Cuerpos.Add(line);
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
public simDescarte AddDescarte(float diameter, Vector2 position)
|
|
||||||
{
|
|
||||||
simDescarte descarte = new simDescarte(world, diameter, position);
|
|
||||||
Cuerpos.Add(descarte);
|
|
||||||
return descarte;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Debug_DrawInitialBodies()
|
|
||||||
{
|
|
||||||
Debug_ClearSimulationShapes();
|
|
||||||
world.Step(0.01f); // Para actualizar la BodyList
|
|
||||||
foreach (Body body in world.BodyList)
|
|
||||||
{
|
|
||||||
foreach (Fixture fixture in body.FixtureList)
|
|
||||||
{
|
|
||||||
DrawShape(fixture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Debug_ClearSimulationShapes()
|
|
||||||
{
|
|
||||||
var simulationShapes = simulationCanvas.Children.OfType<System.Windows.Shapes.Shape>().Where(s => s.Tag as string == "Simulation").ToList();
|
|
||||||
foreach (var shape in simulationShapes)
|
|
||||||
{
|
|
||||||
simulationCanvas.Children.Remove(shape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawShape(Fixture fixture)
|
|
||||||
{
|
|
||||||
System.Windows.Shapes.Shape shape;
|
|
||||||
switch (fixture.ShapeType)
|
|
||||||
{
|
|
||||||
case ShapeType.Circle:
|
|
||||||
shape = DrawCircle(fixture);
|
|
||||||
break;
|
|
||||||
case ShapeType.Polygon:
|
|
||||||
shape = DrawPolygon(fixture);
|
|
||||||
break;
|
|
||||||
case ShapeType.Edge:
|
|
||||||
shape = DrawEdge(fixture);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
shape.Tag = "Simulation"; // Marcar para simulación
|
|
||||||
Canvas.SetZIndex(shape, 20);
|
|
||||||
simulationCanvas.Children.Add(shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
private float p(float x)
|
|
||||||
{
|
|
||||||
float c = PixelToMeter.Instance.calc.MetersToPixels(x);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
private System.Windows.Shapes.Shape DrawEdge(Fixture fixture)
|
|
||||||
{
|
|
||||||
EdgeShape edge = fixture.Shape as EdgeShape;
|
|
||||||
Line line = new Line
|
|
||||||
{
|
|
||||||
X1 = p(edge.Vertex1.X + fixture.Body.Position.X), // Aplicar escala y posición
|
|
||||||
Y1 = p(edge.Vertex1.Y + fixture.Body.Position.Y),
|
|
||||||
X2 = p(edge.Vertex2.X + fixture.Body.Position.X),
|
|
||||||
Y2 = p(edge.Vertex2.Y + fixture.Body.Position.Y),
|
|
||||||
Stroke = Brushes.Black,
|
|
||||||
StrokeThickness = 2
|
|
||||||
};
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
private System.Windows.Shapes.Shape DrawCircle(Fixture fixture)
|
|
||||||
{
|
|
||||||
CircleShape circle = fixture.Shape as CircleShape;
|
|
||||||
Ellipse ellipse = new Ellipse
|
|
||||||
{
|
|
||||||
Width = p(circle.Radius * 2), // Escalado para visualización
|
|
||||||
Height = p(circle.Radius * 2), // Escalado para visualización
|
|
||||||
Stroke = Brushes.Black,
|
|
||||||
StrokeThickness = 2
|
|
||||||
};
|
|
||||||
Canvas.SetLeft(ellipse, p(fixture.Body.Position.X - circle.Radius));
|
|
||||||
Canvas.SetTop(ellipse, p(fixture.Body.Position.Y - circle.Radius));
|
|
||||||
return ellipse;
|
|
||||||
}
|
|
||||||
|
|
||||||
private System.Windows.Shapes.Shape DrawPolygon(Fixture fixture)
|
|
||||||
{
|
|
||||||
Polygon polygon = new Polygon { Stroke = Brushes.Black, StrokeThickness = 2 };
|
|
||||||
PolygonShape polyShape = fixture.Shape as PolygonShape;
|
|
||||||
|
|
||||||
float cos = (float)Math.Cos(fixture.Body.Rotation);
|
|
||||||
float sin = (float)Math.Sin(fixture.Body.Rotation);
|
|
||||||
|
|
||||||
foreach (Vector2 vertex in polyShape.Vertices)
|
|
||||||
{
|
|
||||||
float rotatedX = vertex.X * cos - vertex.Y * sin + fixture.Body.Position.X;
|
|
||||||
float rotatedY = vertex.X * sin + vertex.Y * cos + fixture.Body.Position.Y;
|
|
||||||
|
|
||||||
polygon.Points.Add(new Point(p(rotatedX), p(rotatedY)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return polygon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Fluid parameters, see pvfs.pdf for a detailed explanation
|
|
||||||
/// </summary>
|
|
||||||
public struct FluidDefinition
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Distance of influence between the particles
|
|
||||||
/// </summary>
|
|
||||||
public float InfluenceRadius;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Density of the fluid
|
|
||||||
/// </summary>
|
|
||||||
public float DensityRest;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stiffness of the fluid (when particles are far)
|
|
||||||
/// </summary>
|
|
||||||
public float Stiffness;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stiffness of the fluid (when particles are near)
|
|
||||||
/// Set by Check()
|
|
||||||
/// </summary>
|
|
||||||
public float StiffnessNear;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toggles viscosity forces
|
|
||||||
/// </summary>
|
|
||||||
public bool UseViscosity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// See pvfs.pdf for more information
|
|
||||||
/// </summary>
|
|
||||||
public float ViscositySigma;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// See pvfs.pdf for more information
|
|
||||||
/// </summary>
|
|
||||||
public float ViscosityBeta;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toggles plasticity computation (springs etc.)
|
|
||||||
/// </summary>
|
|
||||||
public bool UsePlasticity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Plasticity, amount of memory of the shape
|
|
||||||
/// See pvfs.pdf for more information
|
|
||||||
/// </summary>
|
|
||||||
public float Plasticity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// K of the springs used for plasticity
|
|
||||||
/// </summary>
|
|
||||||
public float KSpring;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Amount of change of the rest length of the springs (when compressed)
|
|
||||||
/// </summary>
|
|
||||||
public float YieldRatioCompress;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Amount of change of the rest length of the springs (when stretched)
|
|
||||||
/// </summary>
|
|
||||||
public float YieldRatioStretch;
|
|
||||||
|
|
||||||
public static FluidDefinition Default
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
FluidDefinition def = new FluidDefinition
|
|
||||||
{
|
|
||||||
InfluenceRadius = 1.0f,
|
|
||||||
DensityRest = 10.0f,
|
|
||||||
Stiffness = 10.0f,
|
|
||||||
StiffnessNear = 0.0f, // Set by Check()
|
|
||||||
|
|
||||||
UseViscosity = false,
|
|
||||||
ViscositySigma = 10.0f,
|
|
||||||
ViscosityBeta = 0.0f,
|
|
||||||
|
|
||||||
UsePlasticity = false,
|
|
||||||
Plasticity = 0.3f,
|
|
||||||
KSpring = 2.0f,
|
|
||||||
YieldRatioCompress = 0.1f,
|
|
||||||
YieldRatioStretch = 0.1f
|
|
||||||
};
|
|
||||||
|
|
||||||
def.Check();
|
|
||||||
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Check()
|
|
||||||
{
|
|
||||||
InfluenceRadius = MathUtils.Clamp(InfluenceRadius, 0.1f, 10.0f);
|
|
||||||
DensityRest = MathUtils.Clamp(DensityRest, 1.0f, 100.0f);
|
|
||||||
Stiffness = MathUtils.Clamp(Stiffness, 0.1f, 10.0f);
|
|
||||||
StiffnessNear = Stiffness * 100.0f; // See pvfs.pdf
|
|
||||||
|
|
||||||
ViscositySigma = Math.Max(ViscositySigma, 0.0f);
|
|
||||||
ViscosityBeta = Math.Max(ViscosityBeta, 0.0f);
|
|
||||||
|
|
||||||
Plasticity = Math.Max(Plasticity, 0.0f);
|
|
||||||
KSpring = Math.Max(KSpring, 0.0f);
|
|
||||||
YieldRatioCompress = Math.Max(YieldRatioCompress, 0.0f);
|
|
||||||
YieldRatioStretch = Math.Max(YieldRatioStretch, 0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
public class FluidParticle
|
|
||||||
{
|
|
||||||
public Vector2 Position;
|
|
||||||
public Vector2 PreviousPosition;
|
|
||||||
|
|
||||||
public Vector2 Velocity;
|
|
||||||
public Vector2 Acceleration;
|
|
||||||
|
|
||||||
internal FluidParticle(Vector2 position)
|
|
||||||
{
|
|
||||||
Neighbours = new List<FluidParticle>();
|
|
||||||
IsActive = true;
|
|
||||||
MoveTo(position);
|
|
||||||
|
|
||||||
Damping = 0.0f;
|
|
||||||
Mass = 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsActive { get; set; }
|
|
||||||
|
|
||||||
public List<FluidParticle> Neighbours { get; private set; }
|
|
||||||
|
|
||||||
// For gameplay purposes
|
|
||||||
public float Density { get; internal set; }
|
|
||||||
public float Pressure { get; internal set; }
|
|
||||||
|
|
||||||
// Other properties
|
|
||||||
public int Index { get; internal set; }
|
|
||||||
|
|
||||||
// Physics properties
|
|
||||||
public float Damping { get; set; }
|
|
||||||
public float Mass { get; set; }
|
|
||||||
|
|
||||||
public void MoveTo(Vector2 p)
|
|
||||||
{
|
|
||||||
Position = p;
|
|
||||||
PreviousPosition = p;
|
|
||||||
|
|
||||||
Velocity = Vector2.Zero;
|
|
||||||
Acceleration = Vector2.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyForce(ref Vector2 force)
|
|
||||||
{
|
|
||||||
Acceleration += force * Mass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyImpulse(ref Vector2 impulse)
|
|
||||||
{
|
|
||||||
Velocity += impulse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(float deltaTime)
|
|
||||||
{
|
|
||||||
Velocity += Acceleration * deltaTime;
|
|
||||||
|
|
||||||
Vector2 delta = (1.0f - Damping) * Velocity * deltaTime;
|
|
||||||
|
|
||||||
PreviousPosition = Position;
|
|
||||||
Position += delta;
|
|
||||||
|
|
||||||
Acceleration = Vector2.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateVelocity(float deltaTime)
|
|
||||||
{
|
|
||||||
Velocity = (Position - PreviousPosition) / deltaTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,413 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
public class FluidSystem1
|
|
||||||
{
|
|
||||||
private float _influenceRadiusSquared;
|
|
||||||
private HashGrid _hashGrid = new HashGrid();
|
|
||||||
private Dictionary<SpringHash, Spring> _springs = new Dictionary<SpringHash, Spring>();
|
|
||||||
private List<SpringHash> _springsToRemove = new List<SpringHash>();
|
|
||||||
private Vector2 _totalForce;
|
|
||||||
|
|
||||||
public FluidSystem1(Vector2 gravity)
|
|
||||||
{
|
|
||||||
Gravity = gravity;
|
|
||||||
Particles = new List<FluidParticle>();
|
|
||||||
DefaultDefinition();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FluidDefinition Definition { get; private set; }
|
|
||||||
public List<FluidParticle> Particles { get; private set; }
|
|
||||||
public int ParticlesCount { get { return Particles.Count; } }
|
|
||||||
public Vector2 Gravity { get; set; }
|
|
||||||
|
|
||||||
public void DefaultDefinition()
|
|
||||||
{
|
|
||||||
SetDefinition(FluidDefinition.Default);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDefinition(FluidDefinition def)
|
|
||||||
{
|
|
||||||
Definition = def;
|
|
||||||
Definition.Check();
|
|
||||||
_influenceRadiusSquared = Definition.InfluenceRadius * Definition.InfluenceRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FluidParticle AddParticle(Vector2 position)
|
|
||||||
{
|
|
||||||
FluidParticle particle = new FluidParticle(position) { Index = Particles.Count };
|
|
||||||
Particles.Add(particle);
|
|
||||||
return particle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyForce(Vector2 f)
|
|
||||||
{
|
|
||||||
_totalForce += f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyForces()
|
|
||||||
{
|
|
||||||
Vector2 f = Gravity + _totalForce;
|
|
||||||
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
Particles[i].ApplyForce(ref f);
|
|
||||||
}
|
|
||||||
|
|
||||||
_totalForce = Vector2.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyViscosity(FluidParticle p, float timeStep)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < p.Neighbours.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle neighbour = p.Neighbours[i];
|
|
||||||
|
|
||||||
if (p.Index >= neighbour.Index)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float q;
|
|
||||||
Vector2.DistanceSquared(ref p.Position, ref neighbour.Position, out q);
|
|
||||||
|
|
||||||
if (q > _influenceRadiusSquared)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 direction;
|
|
||||||
Vector2.Subtract(ref neighbour.Position, ref p.Position, out direction);
|
|
||||||
|
|
||||||
if (direction.LengthSquared() < float.Epsilon)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
direction.Normalize();
|
|
||||||
|
|
||||||
Vector2 deltaVelocity;
|
|
||||||
Vector2.Subtract(ref p.Velocity, ref neighbour.Velocity, out deltaVelocity);
|
|
||||||
|
|
||||||
float u;
|
|
||||||
Vector2.Dot(ref deltaVelocity, ref direction, out u);
|
|
||||||
|
|
||||||
if (u > 0.0f)
|
|
||||||
{
|
|
||||||
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
|
|
||||||
|
|
||||||
float impulseFactor = 0.5f * timeStep * q * (u * (Definition.ViscositySigma + Definition.ViscosityBeta * u));
|
|
||||||
|
|
||||||
Vector2 impulse;
|
|
||||||
|
|
||||||
Vector2.Multiply(ref direction, -impulseFactor, out impulse);
|
|
||||||
p.ApplyImpulse(ref impulse);
|
|
||||||
|
|
||||||
Vector2.Multiply(ref direction, impulseFactor, out impulse);
|
|
||||||
neighbour.ApplyImpulse(ref impulse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int MaxNeighbors = 25;
|
|
||||||
//private int _len2;
|
|
||||||
//private int _j;
|
|
||||||
//private float _q;
|
|
||||||
//private float _qq;
|
|
||||||
|
|
||||||
//private Vector2 _rij;
|
|
||||||
//private float _d;
|
|
||||||
//private Vector2 _dx;
|
|
||||||
private float _density;
|
|
||||||
private float _densityNear;
|
|
||||||
private float _pressure;
|
|
||||||
private float _pressureNear;
|
|
||||||
private float[] _distanceCache = new float[MaxNeighbors];
|
|
||||||
|
|
||||||
//private void DoubleDensityRelaxation1(FluidParticle p, float timeStep)
|
|
||||||
//{
|
|
||||||
// _density = 0;
|
|
||||||
// _densityNear = 0;
|
|
||||||
|
|
||||||
// _len2 = p.Neighbours.Count;
|
|
||||||
// if (_len2 > MaxNeighbors)
|
|
||||||
// _len2 = MaxNeighbors;
|
|
||||||
|
|
||||||
// for (_j = 0; _j < _len2; _j++)
|
|
||||||
// {
|
|
||||||
// _q = Vector2.DistanceSquared(p.Position, p.Neighbours[_j].Position);
|
|
||||||
// _distanceCache[_j] = _q;
|
|
||||||
// if (_q < _influenceRadiusSquared && _q != 0)
|
|
||||||
// {
|
|
||||||
// _q = (float)Math.Sqrt(_q);
|
|
||||||
// _q /= Definition.InfluenceRadius;
|
|
||||||
// _qq = ((1 - _q) * (1 - _q));
|
|
||||||
// _density += _qq;
|
|
||||||
// _densityNear += _qq * (1 - _q);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// _pressure = Definition.Stiffness * (_density - Definition.DensityRest);
|
|
||||||
// _pressureNear = Definition.StiffnessNear * _densityNear;
|
|
||||||
|
|
||||||
// _dx = Vector2.Zero;
|
|
||||||
|
|
||||||
// for (_j = 0; _j < _len2; _j++)
|
|
||||||
// {
|
|
||||||
// _q = _distanceCache[_j];
|
|
||||||
// if (_q < _influenceRadiusSquared && _q != 0)
|
|
||||||
// {
|
|
||||||
// _q = (float)Math.Sqrt(_q);
|
|
||||||
// _rij = p.Neighbours[_j].Position;
|
|
||||||
// _rij -= p.Position;
|
|
||||||
// _rij *= 1 / _q;
|
|
||||||
// _q /= _influenceRadiusSquared;
|
|
||||||
|
|
||||||
// _d = ((timeStep * timeStep) * (_pressure * (1 - _q) + _pressureNear * (1 - _q) * (1 - _q)));
|
|
||||||
// _rij *= _d * 0.5f;
|
|
||||||
// p.Neighbours[_j].Position += _rij;
|
|
||||||
// _dx -= _rij;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// p.Position += _dx;
|
|
||||||
//}
|
|
||||||
|
|
||||||
private void DoubleDensityRelaxation(FluidParticle particle, float deltaTime2)
|
|
||||||
{
|
|
||||||
_density = 0.0f;
|
|
||||||
_densityNear = 0.0f;
|
|
||||||
|
|
||||||
int neightborCount = particle.Neighbours.Count;
|
|
||||||
|
|
||||||
if (neightborCount > MaxNeighbors)
|
|
||||||
neightborCount = MaxNeighbors;
|
|
||||||
|
|
||||||
for (int i = 0; i < neightborCount; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle neighbour = particle.Neighbours[i];
|
|
||||||
|
|
||||||
if (particle.Index == neighbour.Index)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float q;
|
|
||||||
Vector2.DistanceSquared(ref particle.Position, ref neighbour.Position, out q);
|
|
||||||
_distanceCache[i] = q;
|
|
||||||
|
|
||||||
if (q > _influenceRadiusSquared)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
|
|
||||||
|
|
||||||
float densityDelta = q * q;
|
|
||||||
_density += densityDelta;
|
|
||||||
_densityNear += densityDelta * q;
|
|
||||||
}
|
|
||||||
|
|
||||||
_pressure = Definition.Stiffness * (_density - Definition.DensityRest);
|
|
||||||
_pressureNear = Definition.StiffnessNear * _densityNear;
|
|
||||||
|
|
||||||
// For gameplay purposes
|
|
||||||
particle.Density = _density + _densityNear;
|
|
||||||
particle.Pressure = _pressure + _pressureNear;
|
|
||||||
|
|
||||||
Vector2 delta = Vector2.Zero;
|
|
||||||
|
|
||||||
for (int i = 0; i < neightborCount; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle neighbour = particle.Neighbours[i];
|
|
||||||
|
|
||||||
if (particle.Index == neighbour.Index)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float q = _distanceCache[i];
|
|
||||||
|
|
||||||
if (q > _influenceRadiusSquared)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
|
|
||||||
|
|
||||||
float dispFactor = deltaTime2 * (q * (_pressure + _pressureNear * q));
|
|
||||||
|
|
||||||
Vector2 direction;
|
|
||||||
Vector2.Subtract(ref neighbour.Position, ref particle.Position, out direction);
|
|
||||||
|
|
||||||
if (direction.LengthSquared() < float.Epsilon)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
direction.Normalize();
|
|
||||||
|
|
||||||
Vector2 disp;
|
|
||||||
|
|
||||||
Vector2.Multiply(ref direction, dispFactor, out disp);
|
|
||||||
Vector2.Add(ref neighbour.Position, ref disp, out neighbour.Position);
|
|
||||||
|
|
||||||
Vector2.Multiply(ref direction, -dispFactor, out disp);
|
|
||||||
Vector2.Add(ref delta, ref disp, out delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2.Add(ref particle.Position, ref delta, out particle.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateSprings(FluidParticle p)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < p.Neighbours.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle neighbour = p.Neighbours[i];
|
|
||||||
|
|
||||||
if (p.Index >= neighbour.Index)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
float q;
|
|
||||||
Vector2.DistanceSquared(ref p.Position, ref neighbour.Position, out q);
|
|
||||||
|
|
||||||
if (q > _influenceRadiusSquared)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
SpringHash hash = new SpringHash { P0 = p, P1 = neighbour };
|
|
||||||
|
|
||||||
if (!_springs.ContainsKey(hash))
|
|
||||||
{
|
|
||||||
//TODO: Use pool?
|
|
||||||
Spring spring = new Spring(p, neighbour) { RestLength = (float)Math.Sqrt(q) };
|
|
||||||
_springs.Add(hash, spring);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AdjustSprings(float timeStep)
|
|
||||||
{
|
|
||||||
foreach (var pair in _springs)
|
|
||||||
{
|
|
||||||
Spring spring = pair.Value;
|
|
||||||
|
|
||||||
spring.Update(timeStep, Definition.KSpring, Definition.InfluenceRadius);
|
|
||||||
|
|
||||||
if (spring.Active)
|
|
||||||
{
|
|
||||||
float L = spring.RestLength;
|
|
||||||
float distance;
|
|
||||||
Vector2.Distance(ref spring.P0.Position, ref spring.P1.Position, out distance);
|
|
||||||
|
|
||||||
if (distance > (L + (Definition.YieldRatioStretch * L)))
|
|
||||||
{
|
|
||||||
spring.RestLength += timeStep * Definition.Plasticity * (distance - L - (Definition.YieldRatioStretch * L));
|
|
||||||
}
|
|
||||||
else if (distance < (L - (Definition.YieldRatioCompress * L)))
|
|
||||||
{
|
|
||||||
spring.RestLength -= timeStep * Definition.Plasticity * (L - (Definition.YieldRatioCompress * L) - distance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_springsToRemove.Add(pair.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < _springsToRemove.Count; ++i)
|
|
||||||
{
|
|
||||||
_springs.Remove(_springsToRemove[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ComputeNeighbours()
|
|
||||||
{
|
|
||||||
_hashGrid.GridSize = Definition.InfluenceRadius;
|
|
||||||
_hashGrid.Clear();
|
|
||||||
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle p = Particles[i];
|
|
||||||
|
|
||||||
if (p.IsActive)
|
|
||||||
{
|
|
||||||
_hashGrid.Add(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle p = Particles[i];
|
|
||||||
p.Neighbours.Clear();
|
|
||||||
_hashGrid.Find(ref p.Position, p.Neighbours);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(float deltaTime)
|
|
||||||
{
|
|
||||||
if (deltaTime == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
float deltaTime2 = 0.5f * deltaTime * deltaTime;
|
|
||||||
|
|
||||||
ComputeNeighbours();
|
|
||||||
ApplyForces();
|
|
||||||
|
|
||||||
if (Definition.UseViscosity)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle p = Particles[i];
|
|
||||||
if (p.IsActive)
|
|
||||||
{
|
|
||||||
ApplyViscosity(p, deltaTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle p = Particles[i];
|
|
||||||
if (p.IsActive)
|
|
||||||
{
|
|
||||||
p.Update(deltaTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle p = Particles[i];
|
|
||||||
if (p.IsActive)
|
|
||||||
{
|
|
||||||
DoubleDensityRelaxation(p, deltaTime2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Definition.UsePlasticity)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
FluidParticle p = Particles[i];
|
|
||||||
if (p.IsActive)
|
|
||||||
{
|
|
||||||
CreateSprings(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AdjustSprings(deltaTime);
|
|
||||||
|
|
||||||
UpdateVelocities(deltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateVelocities(float timeStep)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Particles.Count; ++i)
|
|
||||||
{
|
|
||||||
Particles[i].UpdateVelocity(timeStep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Grid used by particle system to keep track of neightbor particles.
|
|
||||||
/// </summary>
|
|
||||||
public class HashGrid
|
|
||||||
{
|
|
||||||
private Dictionary<ulong, List<FluidParticle>> _hash = new Dictionary<ulong, List<FluidParticle>>();
|
|
||||||
private Stack<List<FluidParticle>> _bucketPool = new Stack<List<FluidParticle>>();
|
|
||||||
|
|
||||||
public HashGrid()
|
|
||||||
{
|
|
||||||
GridSize = 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GridSize { get; set; }
|
|
||||||
|
|
||||||
private static ulong HashKey(int x, int y)
|
|
||||||
{
|
|
||||||
return ((ulong)x * 2185031351ul) ^ ((ulong)y * 4232417593ul);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong HashKey(Vector2 position)
|
|
||||||
{
|
|
||||||
return HashKey(
|
|
||||||
(int)Math.Floor(position.X / GridSize),
|
|
||||||
(int)Math.Floor(position.Y / GridSize)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<ulong, List<FluidParticle>> pair in _hash)
|
|
||||||
{
|
|
||||||
pair.Value.Clear();
|
|
||||||
_bucketPool.Push(pair.Value);
|
|
||||||
}
|
|
||||||
_hash.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(FluidParticle particle)
|
|
||||||
{
|
|
||||||
ulong key = HashKey(particle.Position);
|
|
||||||
List<FluidParticle> bucket;
|
|
||||||
if (!_hash.TryGetValue(key, out bucket))
|
|
||||||
{
|
|
||||||
if (_bucketPool.Count > 0)
|
|
||||||
{
|
|
||||||
bucket = _bucketPool.Pop();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bucket = new List<FluidParticle>();
|
|
||||||
}
|
|
||||||
_hash.Add(key, bucket);
|
|
||||||
}
|
|
||||||
bucket.Add(particle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Find(ref Vector2 position, List<FluidParticle> neighbours)
|
|
||||||
{
|
|
||||||
int ix = (int)Math.Floor(position.X / GridSize);
|
|
||||||
int iy = (int)Math.Floor(position.Y / GridSize);
|
|
||||||
|
|
||||||
// Check all 9 neighbouring cells
|
|
||||||
for (int x = ix - 1; x <= ix + 1; ++x)
|
|
||||||
{
|
|
||||||
for (int y = iy - 1; y <= iy + 1; ++y)
|
|
||||||
{
|
|
||||||
ulong key = HashKey(x, y);
|
|
||||||
List<FluidParticle> bucket;
|
|
||||||
if (_hash.TryGetValue(key, out bucket))
|
|
||||||
{
|
|
||||||
for (int i = 0; i < bucket.Count; ++i)
|
|
||||||
{
|
|
||||||
if (bucket[i] != null)
|
|
||||||
{
|
|
||||||
neighbours.Add(bucket[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
//TODO: Could be struct?
|
|
||||||
|
|
||||||
public class Spring
|
|
||||||
{
|
|
||||||
public FluidParticle P0;
|
|
||||||
public FluidParticle P1;
|
|
||||||
|
|
||||||
public Spring(FluidParticle p0, FluidParticle p1)
|
|
||||||
{
|
|
||||||
Active = true;
|
|
||||||
P0 = p0;
|
|
||||||
P1 = p1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Active { get; set; }
|
|
||||||
public float RestLength { get; set; }
|
|
||||||
|
|
||||||
public void Update(float timeStep, float kSpring, float influenceRadius)
|
|
||||||
{
|
|
||||||
if (!Active)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Vector2 dir = P1.Position - P0.Position;
|
|
||||||
float distance = dir.Length();
|
|
||||||
dir.Normalize();
|
|
||||||
|
|
||||||
// This is to avoid imploding simulation with really springy fluids
|
|
||||||
if (distance < 0.5f * influenceRadius)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (RestLength > influenceRadius)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Algorithm 3
|
|
||||||
float displacement = timeStep * timeStep * kSpring * (1.0f - RestLength / influenceRadius) * (RestLength - distance) * 0.5f;
|
|
||||||
|
|
||||||
dir *= displacement;
|
|
||||||
|
|
||||||
P0.Position -= dir;
|
|
||||||
P1.Position += dir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
public class SpringHash : IEqualityComparer<SpringHash>
|
|
||||||
{
|
|
||||||
public FluidParticle P0;
|
|
||||||
public FluidParticle P1;
|
|
||||||
|
|
||||||
public bool Equals(SpringHash lhs, SpringHash rhs)
|
|
||||||
{
|
|
||||||
return (lhs.P0.Index == rhs.P0.Index && lhs.P1.Index == rhs.P1.Index)
|
|
||||||
|| (lhs.P0.Index == rhs.P1.Index && lhs.P1.Index == rhs.P0.Index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(SpringHash s)
|
|
||||||
{
|
|
||||||
return (s.P0.Index * 73856093) ^ (s.P1.Index * 19349663) ^ (s.P0.Index * 19349663) ^ (s.P1.Index * 73856093);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,445 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
public class FluidSystem2
|
|
||||||
{
|
|
||||||
public const int MaxNeighbors = 25;
|
|
||||||
public const int CellSize = 1;
|
|
||||||
|
|
||||||
// Most of these can be tuned at runtime with F1-F9 and keys 1-9 (no numpad)
|
|
||||||
public const float InfluenceRadius = 20.0f;
|
|
||||||
public const float InfluenceRadiusSquared = InfluenceRadius * InfluenceRadius;
|
|
||||||
public const float Stiffness = 0.504f;
|
|
||||||
public const float StiffnessFarNearRatio = 10.0f;
|
|
||||||
public const float StiffnessNear = Stiffness * StiffnessFarNearRatio;
|
|
||||||
public const float ViscositySigma = 0.0f;
|
|
||||||
public const float ViscosityBeta = 0.3f;
|
|
||||||
public const float DensityRest = 10.0f;
|
|
||||||
public const float KSpring = 0.3f;
|
|
||||||
public const float RestLength = 5.0f;
|
|
||||||
public const float RestLengthSquared = RestLength * RestLength;
|
|
||||||
public const float YieldRatioStretch = 0.5f;
|
|
||||||
public const float YieldRatioCompress = 0.5f;
|
|
||||||
public const float Plasticity = 0.5f;
|
|
||||||
public const int VelocityCap = 150;
|
|
||||||
public const float DeformationFactor = 0f;
|
|
||||||
public const float CollisionForce = 0.3f;
|
|
||||||
|
|
||||||
private bool _isElasticityInitialized;
|
|
||||||
private bool _elasticityEnabled;
|
|
||||||
private bool _isPlasticityInitialized;
|
|
||||||
private bool _plasticityEnabled;
|
|
||||||
|
|
||||||
private float _deltaTime2;
|
|
||||||
private Vector2 _dx = new Vector2(0.0f, 0.0f);
|
|
||||||
private const int Wpadding = 20;
|
|
||||||
private const int Hpadding = 20;
|
|
||||||
|
|
||||||
public SpatialTable Particles;
|
|
||||||
|
|
||||||
// Temp variables
|
|
||||||
private Vector2 _rij = new Vector2(0.0f, 0.0f);
|
|
||||||
private Vector2 _tempVect = new Vector2(0.0f, 0.0f);
|
|
||||||
|
|
||||||
private Dictionary<int, List<int>> _springPresenceTable;
|
|
||||||
private List<Spring2> _springs;
|
|
||||||
private List<Particle> _tempParticles;
|
|
||||||
|
|
||||||
private int _worldWidth;
|
|
||||||
private int _worldHeight;
|
|
||||||
|
|
||||||
public int ParticlesCount { get { return Particles.Count; } }
|
|
||||||
|
|
||||||
public FluidSystem2(Vector2 gravity, int maxParticleLimit, int worldWidth, int worldHeight)
|
|
||||||
{
|
|
||||||
_worldHeight = worldHeight;
|
|
||||||
_worldWidth = worldWidth;
|
|
||||||
Particles = new SpatialTable(worldWidth, worldHeight, CellSize);
|
|
||||||
MaxParticleLimit = maxParticleLimit;
|
|
||||||
Gravity = gravity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2 Gravity { get; set; }
|
|
||||||
public int MaxParticleLimit { get; private set; }
|
|
||||||
|
|
||||||
public bool ElasticityEnabled
|
|
||||||
{
|
|
||||||
get { return _elasticityEnabled; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (!_isElasticityInitialized)
|
|
||||||
InitializeElasticity();
|
|
||||||
|
|
||||||
_elasticityEnabled = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool PlasticityEnabled
|
|
||||||
{
|
|
||||||
get { return _plasticityEnabled; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (!_isPlasticityInitialized)
|
|
||||||
InitializePlasticity();
|
|
||||||
|
|
||||||
_plasticityEnabled = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateParticleVelocity(float deltaTime)
|
|
||||||
{
|
|
||||||
for(int i = 0; i < Particles.Count; i++)
|
|
||||||
{
|
|
||||||
Particle particle = Particles[i];
|
|
||||||
particle.PreviousPosition = particle.Position;
|
|
||||||
particle.Position = new Vector2(particle.Position.X + (deltaTime * particle.Velocity.X), particle.Position.Y + (deltaTime * particle.Velocity.Y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WallCollision(Particle pi)
|
|
||||||
{
|
|
||||||
float x = 0;
|
|
||||||
float y = 0;
|
|
||||||
|
|
||||||
if (pi.Position.X > (_worldWidth / 2 - Wpadding))
|
|
||||||
x -= (pi.Position.X - (_worldWidth / 2 - Wpadding)) / CollisionForce;
|
|
||||||
else if (pi.Position.X < (-_worldWidth / 2 + Wpadding))
|
|
||||||
x += ((-_worldWidth / 2 + Wpadding) - pi.Position.X) / CollisionForce;
|
|
||||||
|
|
||||||
if (pi.Position.Y > (_worldHeight - Hpadding))
|
|
||||||
y -= (pi.Position.Y - (_worldHeight - Hpadding)) / CollisionForce;
|
|
||||||
else if (pi.Position.Y < Hpadding)
|
|
||||||
y += (Hpadding - pi.Position.Y) / CollisionForce;
|
|
||||||
|
|
||||||
pi.Velocity.X += x;
|
|
||||||
pi.Velocity.Y += y;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CapVelocity(Vector2 v)
|
|
||||||
{
|
|
||||||
if (v.X > VelocityCap)
|
|
||||||
v.X = VelocityCap;
|
|
||||||
else if (v.X < -VelocityCap)
|
|
||||||
v.X = -VelocityCap;
|
|
||||||
|
|
||||||
if (v.Y > VelocityCap)
|
|
||||||
v.Y = VelocityCap;
|
|
||||||
else if (v.Y < -VelocityCap)
|
|
||||||
v.Y = -VelocityCap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializePlasticity()
|
|
||||||
{
|
|
||||||
_isPlasticityInitialized = true;
|
|
||||||
|
|
||||||
_springs.Clear();
|
|
||||||
float q;
|
|
||||||
foreach (Particle pa in Particles)
|
|
||||||
{
|
|
||||||
foreach (Particle pb in Particles)
|
|
||||||
{
|
|
||||||
if (pa.GetHashCode() == pb.GetHashCode())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vector2.Distance(ref pa.Position, ref pb.Position, out q);
|
|
||||||
Vector2.Subtract(ref pb.Position, ref pa.Position, out _rij);
|
|
||||||
_rij /= q;
|
|
||||||
|
|
||||||
if (q < RestLength)
|
|
||||||
{
|
|
||||||
_springs.Add(new Spring2(pa, pb, q));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pa.Velocity = Vector2.Zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculatePlasticity(float deltaTime)
|
|
||||||
{
|
|
||||||
foreach (Spring2 spring in _springs)
|
|
||||||
{
|
|
||||||
spring.Update();
|
|
||||||
|
|
||||||
if (spring.CurrentDistance == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vector2.Subtract(ref spring.PB.Position, ref spring.PA.Position, out _rij);
|
|
||||||
_rij /= spring.CurrentDistance;
|
|
||||||
float D = deltaTime * KSpring * (spring.RestLength - spring.CurrentDistance);
|
|
||||||
_rij *= (D * 0.5f);
|
|
||||||
spring.PA.Position = new Vector2(spring.PA.Position.X - _rij.X, spring.PA.Position.Y - _rij.Y);
|
|
||||||
spring.PB.Position = new Vector2(spring.PB.Position.X + _rij.X, spring.PB.Position.Y + _rij.Y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeElasticity()
|
|
||||||
{
|
|
||||||
_isElasticityInitialized = true;
|
|
||||||
|
|
||||||
foreach (Particle particle in Particles)
|
|
||||||
{
|
|
||||||
_springPresenceTable.Add(particle.GetHashCode(), new List<int>(MaxParticleLimit));
|
|
||||||
particle.Velocity = Vector2.Zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateElasticity(float deltaTime)
|
|
||||||
{
|
|
||||||
float sqDist;
|
|
||||||
for (int i = 0; i < Particles.Count; i++)
|
|
||||||
{
|
|
||||||
Particle pa = Particles[i];
|
|
||||||
|
|
||||||
if (Particles.CountNearBy(pa) <= 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_tempParticles = Particles.GetNearby(pa);
|
|
||||||
int len2 = _tempParticles.Count;
|
|
||||||
|
|
||||||
if (len2 > MaxNeighbors)
|
|
||||||
len2 = MaxNeighbors;
|
|
||||||
|
|
||||||
for (int j = 0; j < len2; j++)
|
|
||||||
{
|
|
||||||
Particle pb = Particles[j];
|
|
||||||
Vector2.DistanceSquared(ref pa.Position, ref pb.Position, out sqDist);
|
|
||||||
if (sqDist > RestLengthSquared)
|
|
||||||
continue;
|
|
||||||
if (pa.GetHashCode() == pb.GetHashCode())
|
|
||||||
continue;
|
|
||||||
if (!_springPresenceTable[pa.GetHashCode()].Contains(pb.GetHashCode()))
|
|
||||||
{
|
|
||||||
_springs.Add(new Spring2(pa, pb, RestLength));
|
|
||||||
_springPresenceTable[pa.GetHashCode()].Add(pb.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = _springs.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
Spring2 spring = _springs[i];
|
|
||||||
spring.Update();
|
|
||||||
|
|
||||||
// Stretch
|
|
||||||
if (spring.CurrentDistance > (spring.RestLength + DeformationFactor))
|
|
||||||
{
|
|
||||||
spring.RestLength += deltaTime * Plasticity * (spring.CurrentDistance - spring.RestLength - (YieldRatioStretch * spring.RestLength));
|
|
||||||
}
|
|
||||||
// Compress
|
|
||||||
else if (spring.CurrentDistance < (spring.RestLength - DeformationFactor))
|
|
||||||
{
|
|
||||||
spring.RestLength -= deltaTime * Plasticity * (spring.RestLength - (YieldRatioCompress * spring.RestLength) - spring.CurrentDistance);
|
|
||||||
}
|
|
||||||
// Remove springs with restLength longer than REST_LENGTH
|
|
||||||
if (spring.RestLength > RestLength)
|
|
||||||
{
|
|
||||||
_springs.RemoveAt(i);
|
|
||||||
_springPresenceTable[spring.PA.GetHashCode()].Remove(spring.PB.GetHashCode());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (spring.CurrentDistance == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vector2.Subtract(ref spring.PB.Position, ref spring.PA.Position, out _rij);
|
|
||||||
_rij /= spring.CurrentDistance;
|
|
||||||
float D = deltaTime * KSpring * (spring.RestLength - spring.CurrentDistance);
|
|
||||||
_rij *= (D * 0.5f);
|
|
||||||
spring.PA.Position = new Vector2(spring.PA.Position.X - _rij.X, spring.PA.Position.Y - _rij.Y);
|
|
||||||
spring.PB.Position = new Vector2(spring.PB.Position.X + _rij.X, spring.PB.Position.Y + _rij.Y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyGravity(Particle particle)
|
|
||||||
{
|
|
||||||
particle.Velocity = new Vector2(particle.Velocity.X + Gravity.X, particle.Velocity.Y + Gravity.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyViscosity(float deltaTime)
|
|
||||||
{
|
|
||||||
float u, q;
|
|
||||||
for (int i = 0; i < Particles.Count; i++)
|
|
||||||
{
|
|
||||||
Particle particle = Particles[i];
|
|
||||||
|
|
||||||
_tempParticles = Particles.GetNearby(particle);
|
|
||||||
|
|
||||||
int len2 = _tempParticles.Count;
|
|
||||||
if (len2 > MaxNeighbors)
|
|
||||||
len2 = MaxNeighbors;
|
|
||||||
|
|
||||||
for (int j = 0; j < len2; j++)
|
|
||||||
{
|
|
||||||
Particle tempParticle = _tempParticles[j];
|
|
||||||
|
|
||||||
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
|
|
||||||
if ((q < InfluenceRadiusSquared) && (q != 0))
|
|
||||||
{
|
|
||||||
q = (float)Math.Sqrt(q);
|
|
||||||
Vector2.Subtract(ref tempParticle.Position, ref particle.Position, out _rij);
|
|
||||||
Vector2.Divide(ref _rij, q, out _rij);
|
|
||||||
|
|
||||||
Vector2.Subtract(ref particle.Velocity, ref tempParticle.Velocity, out _tempVect);
|
|
||||||
Vector2.Dot(ref _tempVect, ref _rij, out u);
|
|
||||||
if (u <= 0.0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
q /= InfluenceRadius;
|
|
||||||
|
|
||||||
float I = (deltaTime * (1 - q) * (ViscositySigma * u + ViscosityBeta * u * u));
|
|
||||||
Vector2.Multiply(ref _rij, (I * 0.5f), out _rij);
|
|
||||||
Vector2.Subtract(ref particle.Velocity, ref _rij, out _tempVect);
|
|
||||||
particle.Velocity = _tempVect;
|
|
||||||
_tempVect = tempParticle.Velocity;
|
|
||||||
_tempVect += _rij;
|
|
||||||
tempParticle.Velocity = _tempVect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DoubleDensityRelaxation()
|
|
||||||
{
|
|
||||||
float q;
|
|
||||||
for (int i = 0; i < Particles.Count; i++)
|
|
||||||
{
|
|
||||||
Particle particle = Particles[i];
|
|
||||||
particle.Density = 0;
|
|
||||||
particle.NearDensity = 0;
|
|
||||||
|
|
||||||
_tempParticles = Particles.GetNearby(particle);
|
|
||||||
|
|
||||||
int len2 = _tempParticles.Count;
|
|
||||||
if (len2 > MaxNeighbors)
|
|
||||||
len2 = MaxNeighbors;
|
|
||||||
|
|
||||||
for (int j = 0; j < len2; j++)
|
|
||||||
{
|
|
||||||
Particle tempParticle = _tempParticles[j];
|
|
||||||
|
|
||||||
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
|
|
||||||
if (q < InfluenceRadiusSquared && q != 0)
|
|
||||||
{
|
|
||||||
q = (float)Math.Sqrt(q);
|
|
||||||
q /= InfluenceRadius;
|
|
||||||
float qq = ((1 - q) * (1 - q));
|
|
||||||
particle.Density += qq;
|
|
||||||
particle.NearDensity += qq * (1 - q);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
particle.Pressure = (Stiffness * (particle.Density - DensityRest));
|
|
||||||
particle.NearPressure = (StiffnessNear * particle.NearDensity);
|
|
||||||
_dx = Vector2.Zero;
|
|
||||||
|
|
||||||
for (int j = 0; j < len2; j++)
|
|
||||||
{
|
|
||||||
Particle tempParticle = _tempParticles[j];
|
|
||||||
|
|
||||||
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
|
|
||||||
if ((q < InfluenceRadiusSquared) && (q != 0))
|
|
||||||
{
|
|
||||||
q = (float)Math.Sqrt(q);
|
|
||||||
Vector2.Subtract(ref tempParticle.Position, ref particle.Position, out _rij);
|
|
||||||
Vector2.Divide(ref _rij, q, out _rij);
|
|
||||||
q /= InfluenceRadius;
|
|
||||||
|
|
||||||
float D = (_deltaTime2 * (particle.Pressure * (1 - q) + particle.NearPressure * (1 - q) * (1 - q)));
|
|
||||||
Vector2.Multiply(ref _rij, (D * 0.5f), out _rij);
|
|
||||||
tempParticle.Position = new Vector2(tempParticle.Position.X + _rij.X, tempParticle.Position.Y + _rij.Y);
|
|
||||||
Vector2.Subtract(ref _dx, ref _rij, out _dx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
particle.Position = particle.Position + _dx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(float deltaTime)
|
|
||||||
{
|
|
||||||
if (deltaTime == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_deltaTime2 = deltaTime * deltaTime;
|
|
||||||
|
|
||||||
ApplyViscosity(deltaTime);
|
|
||||||
|
|
||||||
//Update velocity
|
|
||||||
UpdateParticleVelocity(deltaTime);
|
|
||||||
|
|
||||||
Particles.Rehash();
|
|
||||||
|
|
||||||
if (_elasticityEnabled)
|
|
||||||
CalculateElasticity(deltaTime);
|
|
||||||
|
|
||||||
if (_plasticityEnabled)
|
|
||||||
CalculatePlasticity(deltaTime);
|
|
||||||
|
|
||||||
DoubleDensityRelaxation();
|
|
||||||
|
|
||||||
for(int i = 0; i < Particles.Count; i++)
|
|
||||||
{
|
|
||||||
Particle particle = Particles[i];
|
|
||||||
particle.Velocity = new Vector2((particle.Position.X - particle.PreviousPosition.X) / deltaTime, (particle.Position.Y - particle.PreviousPosition.Y) / deltaTime);
|
|
||||||
ApplyGravity(particle);
|
|
||||||
WallCollision(particle);
|
|
||||||
CapVelocity(particle.Velocity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddParticle(Vector2 position)
|
|
||||||
{
|
|
||||||
Particles.Add(new Particle(position.X, position.Y));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Particle
|
|
||||||
{
|
|
||||||
public float Density;
|
|
||||||
public float NearDensity;
|
|
||||||
public float NearPressure;
|
|
||||||
public Vector2 Position = new Vector2(0, 0);
|
|
||||||
public float Pressure;
|
|
||||||
public Vector2 PreviousPosition = new Vector2(0, 0);
|
|
||||||
public Vector2 Velocity = new Vector2(0, 0);
|
|
||||||
|
|
||||||
public Particle(float posX, float posY)
|
|
||||||
{
|
|
||||||
Position = new Vector2(posX, posY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Spring2
|
|
||||||
{
|
|
||||||
public float CurrentDistance;
|
|
||||||
public Particle PA;
|
|
||||||
public Particle PB;
|
|
||||||
public float RestLength;
|
|
||||||
|
|
||||||
public Spring2(Particle pa, Particle pb, float restLength)
|
|
||||||
{
|
|
||||||
PA = pa;
|
|
||||||
PB = pb;
|
|
||||||
RestLength = restLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
Vector2.Distance(ref PA.Position, ref PB.Position, out CurrentDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(Particle p)
|
|
||||||
{
|
|
||||||
return (PA.GetHashCode() == p.GetHashCode() || PB.GetHashCode() == p.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
/* Original source Farseer Physics Engine:
|
|
||||||
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
|
|
||||||
* Microsoft Permissive License (Ms-PL) v1.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace tainicom.Aether.Physics2D.Fluids
|
|
||||||
{
|
|
||||||
public class SpatialTable : IEnumerable<Particle>
|
|
||||||
{
|
|
||||||
// default nearby table size
|
|
||||||
private const int DefaultNearbySize = 50;
|
|
||||||
private List<Particle> _table;
|
|
||||||
private List<Particle> _voidList = new List<Particle>(1);
|
|
||||||
private List<Particle>[][] _nearby;
|
|
||||||
bool _initialized;
|
|
||||||
|
|
||||||
private int _row;
|
|
||||||
private int _column;
|
|
||||||
private int _cellSize;
|
|
||||||
|
|
||||||
public SpatialTable(int column, int row, int cellSize)
|
|
||||||
{
|
|
||||||
_row = row;
|
|
||||||
_cellSize = cellSize;
|
|
||||||
_column = column;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
_table = new List<Particle>((_row * _column) / 2);
|
|
||||||
_nearby = new List<Particle>[_column][];
|
|
||||||
|
|
||||||
for (int i = 0; i < _column; ++i)
|
|
||||||
{
|
|
||||||
_nearby[i] = new List<Particle>[_row];
|
|
||||||
|
|
||||||
for (int j = 0; j < _row; ++j)
|
|
||||||
{
|
|
||||||
_nearby[i][j] = new List<Particle>(DefaultNearbySize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Append value to the table and identify its position in the space.
|
|
||||||
/// Don't need to rehash table after append operation.</summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
public void Add(Particle value)
|
|
||||||
{
|
|
||||||
if (!_initialized)
|
|
||||||
Initialize();
|
|
||||||
|
|
||||||
AddInterRadius(value);
|
|
||||||
_table.Add(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Particle this[int i]
|
|
||||||
{
|
|
||||||
get { return _table[i]; }
|
|
||||||
set { _table[i] = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(Particle value)
|
|
||||||
{
|
|
||||||
_table.Remove(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _column; ++i)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < _row; ++j)
|
|
||||||
{
|
|
||||||
_nearby[i][j].Clear();
|
|
||||||
_nearby[i][j] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_table.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get { return (_table == null)? 0 : _table.Count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Particle> GetNearby(Particle value)
|
|
||||||
{
|
|
||||||
int x = posX(value);
|
|
||||||
int y = posY(value);
|
|
||||||
|
|
||||||
if (!InRange(x, y))
|
|
||||||
return _voidList;
|
|
||||||
|
|
||||||
return _nearby[x][y];
|
|
||||||
}
|
|
||||||
|
|
||||||
private int posX(Particle value)
|
|
||||||
{
|
|
||||||
return (int)((value.Position.X + (_column / 2) + 0.3f) / _cellSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int posY(Particle value)
|
|
||||||
{
|
|
||||||
return (int)((value.Position.Y + 0.3f) / _cellSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int CountNearBy(Particle value)
|
|
||||||
{
|
|
||||||
return GetNearby(value).Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the spatial relationships of objects. Rehash function
|
|
||||||
/// needed if elements change their position in the space.
|
|
||||||
/// </summary>
|
|
||||||
public void Rehash()
|
|
||||||
{
|
|
||||||
if (_table == null || _table.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < _column; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < _row; j++)
|
|
||||||
{
|
|
||||||
if (_nearby[i][j] != null)
|
|
||||||
_nearby[i][j].Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Particle particle in _table)
|
|
||||||
{
|
|
||||||
AddInterRadius(particle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add element to its position and neighbor cells.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
private void AddInterRadius(Particle value)
|
|
||||||
{
|
|
||||||
for (int i = -1; i < 2; ++i)
|
|
||||||
{
|
|
||||||
for (int j = -1; j < 2; ++j)
|
|
||||||
{
|
|
||||||
int x = posX(value) + i;
|
|
||||||
int y = posY(value) + j;
|
|
||||||
if (InRange(x, y))
|
|
||||||
_nearby[x][y].Add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if a position is out of the spatial range
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="x"></param>
|
|
||||||
/// <param name="y"></param>
|
|
||||||
/// <returns>true if position is in range.</returns>
|
|
||||||
private bool InRange(float x, float y)
|
|
||||||
{
|
|
||||||
return (x > 0 && x < _column && y > 0 && y < _row);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<Particle> GetEnumerator()
|
|
||||||
{
|
|
||||||
return _table.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,412 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
using tainicom.Aether.Physics2D.Fluids;
|
|
||||||
using CtrEditor.Simulacion.Fluids.Components;
|
|
||||||
|
|
||||||
namespace CtrEditor.Simulacion.Fluids.Components
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Clase base para todos los componentes de fluidos
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ComponenteFluido : IContenedorFluido
|
|
||||||
{
|
|
||||||
public Vector2 Posicion { get; set; }
|
|
||||||
public bool EsActivo { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convierte una posición de metros/WPF a sistema de coordenadas de FluidSystem2
|
|
||||||
/// </summary>
|
|
||||||
protected Vector2 ConvertirPosicion(Vector2 posicion, int worldWidth)
|
|
||||||
{
|
|
||||||
return new Vector2(
|
|
||||||
posicion.X * 100 - worldWidth/2,
|
|
||||||
posicion.Y * 100
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implementación en clases derivadas para restricciones específicas
|
|
||||||
/// </summary>
|
|
||||||
public abstract void RestringirParticula(Particle particula);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tanque para contener fluidos
|
|
||||||
/// </summary>
|
|
||||||
public class Tanque : ComponenteFluido
|
|
||||||
{
|
|
||||||
public float Ancho { get; set; }
|
|
||||||
public float Alto { get; set; }
|
|
||||||
private int _worldWidth;
|
|
||||||
|
|
||||||
// Coordenadas ajustadas al sistema de FluidSystem2
|
|
||||||
private float _xMin, _xMax, _yMin, _yMax;
|
|
||||||
private float _coefRebote = 0.3f; // Factor de rebote en colisiones
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor para un tanque
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="posicion">Posición del centro del tanque</param>
|
|
||||||
/// <param name="ancho">Ancho del tanque</param>
|
|
||||||
/// <param name="alto">Alto del tanque</param>
|
|
||||||
/// <param name="worldWidth">Ancho del mundo en cm (para conversión de coordenadas)</param>
|
|
||||||
public Tanque(Vector2 posicion, float ancho, float alto, int worldWidth)
|
|
||||||
{
|
|
||||||
Posicion = posicion;
|
|
||||||
Ancho = ancho;
|
|
||||||
Alto = alto;
|
|
||||||
_worldWidth = worldWidth;
|
|
||||||
|
|
||||||
ActualizarLimites();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actualiza los límites internos del tanque después de cambiar la posición o tamaño
|
|
||||||
/// </summary>
|
|
||||||
public void ActualizarLimites()
|
|
||||||
{
|
|
||||||
// Convertir a coordenadas internas
|
|
||||||
_xMin = (Posicion.X - Ancho/2) * 100 - _worldWidth/2;
|
|
||||||
_xMax = (Posicion.X + Ancho/2) * 100 - _worldWidth/2;
|
|
||||||
_yMin = Posicion.Y * 100;
|
|
||||||
_yMax = (Posicion.Y + Alto) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Restringe las partículas para que permanezcan dentro del tanque
|
|
||||||
/// </summary>
|
|
||||||
public override void RestringirParticula(Particle particula)
|
|
||||||
{
|
|
||||||
if (!EsActivo) return;
|
|
||||||
|
|
||||||
// Comprobar si la partícula está dentro del área del tanque
|
|
||||||
bool dentroX = particula.Position.X >= _xMin && particula.Position.X <= _xMax;
|
|
||||||
bool dentroY = particula.Position.Y >= _yMin && particula.Position.Y <= _yMax;
|
|
||||||
|
|
||||||
if (dentroX && dentroY)
|
|
||||||
{
|
|
||||||
// La partícula está dentro del tanque, comprobar si está cerca de los bordes
|
|
||||||
if (particula.Position.X < _xMin + 5)
|
|
||||||
{
|
|
||||||
particula.Position.X = _xMin + 5;
|
|
||||||
particula.Velocity.X *= -_coefRebote;
|
|
||||||
}
|
|
||||||
else if (particula.Position.X > _xMax - 5)
|
|
||||||
{
|
|
||||||
particula.Position.X = _xMax - 5;
|
|
||||||
particula.Velocity.X *= -_coefRebote;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (particula.Position.Y < _yMin + 5)
|
|
||||||
{
|
|
||||||
particula.Position.Y = _yMin + 5;
|
|
||||||
particula.Velocity.Y *= -_coefRebote;
|
|
||||||
}
|
|
||||||
else if (particula.Position.Y > _yMax - 5)
|
|
||||||
{
|
|
||||||
particula.Position.Y = _yMax - 5;
|
|
||||||
particula.Velocity.Y *= -_coefRebote;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obtiene el nivel de llenado del tanque en porcentaje
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="simulacion">Instancia activa de la simulación</param>
|
|
||||||
/// <returns>Porcentaje de llenado (0-1)</returns>
|
|
||||||
public float ObtenerNivelLlenado()
|
|
||||||
{
|
|
||||||
// Sería necesario acceder a las partículas para calcular esto
|
|
||||||
// Implementación en clases derivadas específicas
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Representa un segmento de tubo para fluidos
|
|
||||||
/// </summary>
|
|
||||||
public class SegmentoTubo
|
|
||||||
{
|
|
||||||
public Vector2 Inicio { get; }
|
|
||||||
public Vector2 Fin { get; }
|
|
||||||
public float Radio { get; }
|
|
||||||
|
|
||||||
public SegmentoTubo(Vector2 inicio, Vector2 fin, float radio)
|
|
||||||
{
|
|
||||||
Inicio = inicio;
|
|
||||||
Fin = fin;
|
|
||||||
Radio = radio;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Contiene y restringe el movimiento de una partícula dentro del tubo
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Verdadero si se aplicó alguna restricción</returns>
|
|
||||||
public bool ContenerParticula(Particle particula)
|
|
||||||
{
|
|
||||||
// Calcular distancia de la partícula al segmento de línea
|
|
||||||
Vector2 pos = new Vector2(particula.Position.X, particula.Position.Y);
|
|
||||||
float distancia = DistanciaPuntoALinea(pos, Inicio, Fin);
|
|
||||||
|
|
||||||
// Si la distancia es mayor que el radio, aplicar restricción
|
|
||||||
if (distancia > Radio)
|
|
||||||
{
|
|
||||||
// Calcular punto más cercano en el segmento
|
|
||||||
Vector2 puntoMasCercano = PuntoMasCercanoEnLinea(pos, Inicio, Fin);
|
|
||||||
|
|
||||||
// Dirección desde partícula hacia punto más cercano en el centro del tubo
|
|
||||||
Vector2 direccion = VectorUtils.NormalizeVector(puntoMasCercano - pos);
|
|
||||||
|
|
||||||
// Mover la partícula al interior del tubo
|
|
||||||
particula.Position.X = puntoMasCercano.X - direccion.X * Radio * 0.9f;
|
|
||||||
particula.Position.Y = puntoMasCercano.Y - direccion.Y * Radio * 0.9f;
|
|
||||||
|
|
||||||
// Ajustar velocidad para simular rebote
|
|
||||||
float velocidadNormal = VectorUtils.DotProduct(
|
|
||||||
new Vector2(particula.Velocity.X, particula.Velocity.Y),
|
|
||||||
direccion);
|
|
||||||
|
|
||||||
particula.Velocity.X -= direccion.X * velocidadNormal * 1.8f;
|
|
||||||
particula.Velocity.Y -= direccion.Y * velocidadNormal * 1.8f;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false; // La partícula está dentro del tubo
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calcula la distancia de un punto a una línea
|
|
||||||
/// </summary>
|
|
||||||
private float DistanciaPuntoALinea(Vector2 punto, Vector2 lineaInicio, Vector2 lineaFin)
|
|
||||||
{
|
|
||||||
// Vector que representa la dirección de la línea
|
|
||||||
Vector2 linea = lineaFin - lineaInicio;
|
|
||||||
float longitud = linea.Length();
|
|
||||||
|
|
||||||
if (longitud < 0.0001f)
|
|
||||||
return VectorUtils.DistanceBetween(punto, lineaInicio); // Es un punto, no una línea
|
|
||||||
|
|
||||||
// Normalizar el vector de la línea
|
|
||||||
Vector2 lineaNormalizada = linea / longitud;
|
|
||||||
|
|
||||||
// Vector desde el inicio de la línea hasta el punto
|
|
||||||
Vector2 puntoDesdeInicio = punto - lineaInicio;
|
|
||||||
|
|
||||||
// Proyectar puntoDesdeInicio sobre la línea
|
|
||||||
float proyeccion = VectorUtils.DotProduct(puntoDesdeInicio, lineaNormalizada);
|
|
||||||
|
|
||||||
// Limitar la proyección al segmento de línea
|
|
||||||
proyeccion = Math.Max(0, Math.Min(longitud, proyeccion));
|
|
||||||
|
|
||||||
// Punto más cercano en la línea
|
|
||||||
Vector2 puntoMasCercano = lineaInicio + lineaNormalizada * proyeccion;
|
|
||||||
|
|
||||||
// Distancia desde el punto hasta el punto más cercano
|
|
||||||
return VectorUtils.DistanceBetween(punto, puntoMasCercano);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calcula el punto más cercano en una línea desde un punto dado
|
|
||||||
/// </summary>
|
|
||||||
private Vector2 PuntoMasCercanoEnLinea(Vector2 punto, Vector2 lineaInicio, Vector2 lineaFin)
|
|
||||||
{
|
|
||||||
// Vector que representa la dirección de la línea
|
|
||||||
Vector2 linea = lineaFin - lineaInicio;
|
|
||||||
float longitud = linea.Length();
|
|
||||||
|
|
||||||
if (longitud < 0.0001f)
|
|
||||||
return lineaInicio; // Es un punto, no una línea
|
|
||||||
|
|
||||||
// Normalizar el vector de la línea
|
|
||||||
Vector2 lineaNormalizada = linea / longitud;
|
|
||||||
|
|
||||||
// Vector desde el inicio de la línea hasta el punto
|
|
||||||
Vector2 puntoDesdeInicio = punto - lineaInicio;
|
|
||||||
|
|
||||||
// Proyectar puntoDesdeInicio sobre la línea
|
|
||||||
float proyeccion;
|
|
||||||
Vector2.Dot(ref puntoDesdeInicio, ref lineaNormalizada, out proyeccion);
|
|
||||||
|
|
||||||
// Limitar la proyección al segmento de línea
|
|
||||||
proyeccion = Math.Max(0, Math.Min(longitud, proyeccion));
|
|
||||||
|
|
||||||
// Punto más cercano en la línea
|
|
||||||
return lineaInicio + lineaNormalizada * proyeccion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tubería para conducir fluidos entre puntos
|
|
||||||
/// </summary>
|
|
||||||
public class Tuberia : ComponenteFluido
|
|
||||||
{
|
|
||||||
private List<SegmentoTubo> _segmentos = new List<SegmentoTubo>();
|
|
||||||
private List<Vector2> _puntos = new List<Vector2>();
|
|
||||||
public float Diametro { get; private set; }
|
|
||||||
private int _worldWidth;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor para una tubería
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="diametro">Diámetro de la tubería</param>
|
|
||||||
/// <param name="worldWidth">Ancho del mundo en cm (para conversión de coordenadas)</param>
|
|
||||||
public Tuberia(float diametro, int worldWidth)
|
|
||||||
{
|
|
||||||
Diametro = diametro;
|
|
||||||
_worldWidth = worldWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Agrega un punto a la tubería, creando segmentos automáticamente
|
|
||||||
/// </summary>
|
|
||||||
public void AgregarPunto(Vector2 punto)
|
|
||||||
{
|
|
||||||
// Convertir punto a coordenadas internas
|
|
||||||
Vector2 puntoAjustado = ConvertirPosicion(punto, _worldWidth);
|
|
||||||
|
|
||||||
// Si ya hay puntos, crear un segmento con el punto anterior
|
|
||||||
if (_puntos.Count > 0)
|
|
||||||
{
|
|
||||||
_segmentos.Add(new SegmentoTubo(
|
|
||||||
_puntos[_puntos.Count - 1],
|
|
||||||
puntoAjustado,
|
|
||||||
Diametro * 100 / 2 // Radio en unidades internas
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
_puntos.Add(puntoAjustado);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Restringe las partículas para que permanezcan dentro de la tubería
|
|
||||||
/// </summary>
|
|
||||||
public override void RestringirParticula(Particle particula)
|
|
||||||
{
|
|
||||||
if (!EsActivo) return;
|
|
||||||
|
|
||||||
// Comprobar si la partícula está cerca de la tubería
|
|
||||||
Vector2 posParticula = new Vector2(particula.Position.X, particula.Position.Y);
|
|
||||||
float distanciaMinima = float.MaxValue;
|
|
||||||
SegmentoTubo segmentoMasCercano = null;
|
|
||||||
|
|
||||||
// Encontrar el segmento más cercano
|
|
||||||
foreach (var segmento in _segmentos)
|
|
||||||
{
|
|
||||||
float distancia = DistanciaPuntoASegmento(posParticula, segmento.Inicio, segmento.Fin);
|
|
||||||
if (distancia < distanciaMinima)
|
|
||||||
{
|
|
||||||
distanciaMinima = distancia;
|
|
||||||
segmentoMasCercano = segmento;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aplicar restricciones si hay un segmento cercano
|
|
||||||
if (segmentoMasCercano != null)
|
|
||||||
{
|
|
||||||
segmentoMasCercano.ContenerParticula(particula);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calcula la distancia desde un punto a un segmento de línea
|
|
||||||
/// </summary>
|
|
||||||
private float DistanciaPuntoASegmento(Vector2 punto, Vector2 segmentoInicio, Vector2 segmentoFin)
|
|
||||||
{
|
|
||||||
// Vector dirección del segmento
|
|
||||||
Vector2 segmento = segmentoFin - segmentoInicio;
|
|
||||||
float longitudCuadrada = segmento.LengthSquared();
|
|
||||||
|
|
||||||
if (longitudCuadrada < 0.0001f)
|
|
||||||
return VectorUtils.DistanceBetween(punto, segmentoInicio); // Es un punto, no un segmento
|
|
||||||
|
|
||||||
// Calcular proyección del punto sobre el segmento
|
|
||||||
float t = VectorUtils.DotProduct(punto - segmentoInicio, segmento) / longitudCuadrada;
|
|
||||||
t = Math.Max(0, Math.Min(1, t));
|
|
||||||
|
|
||||||
// Punto más cercano en el segmento
|
|
||||||
Vector2 proyeccion = segmentoInicio + t * segmento;
|
|
||||||
|
|
||||||
// Distancia desde el punto hasta la proyección
|
|
||||||
return VectorUtils.DistanceBetween(punto, proyeccion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Válvula para controlar el flujo de fluidos
|
|
||||||
/// </summary>
|
|
||||||
public class Valvula : ComponenteFluido
|
|
||||||
{
|
|
||||||
public float Apertura { get; set; } // 0.0 = cerrada, 1.0 = abierta
|
|
||||||
public float Diametro { get; set; }
|
|
||||||
private int _worldWidth;
|
|
||||||
private Vector2 _posicionAjustada;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor para una válvula
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="posicion">Posición de la válvula</param>
|
|
||||||
/// <param name="diametro">Diámetro del conducto de la válvula</param>
|
|
||||||
/// <param name="apertura">Apertura inicial (0-1)</param>
|
|
||||||
/// <param name="worldWidth">Ancho del mundo en cm (para conversión de coordenadas)</param>
|
|
||||||
public Valvula(Vector2 posicion, float diametro, float apertura, int worldWidth)
|
|
||||||
{
|
|
||||||
Posicion = posicion;
|
|
||||||
Diametro = diametro;
|
|
||||||
Apertura = Math.Clamp(apertura, 0, 1);
|
|
||||||
_worldWidth = worldWidth;
|
|
||||||
|
|
||||||
_posicionAjustada = ConvertirPosicion(posicion, worldWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actualiza la posición ajustada después de cambiar la posición
|
|
||||||
/// </summary>
|
|
||||||
public void ActualizarPosicion(Vector2 nuevaPosicion)
|
|
||||||
{
|
|
||||||
Posicion = nuevaPosicion;
|
|
||||||
_posicionAjustada = ConvertirPosicion(nuevaPosicion, _worldWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Restringe y modula el flujo de partículas a través de la válvula
|
|
||||||
/// </summary>
|
|
||||||
public override void RestringirParticula(Particle particula)
|
|
||||||
{
|
|
||||||
if (!EsActivo) return;
|
|
||||||
|
|
||||||
// Calcular distancia al centro de la válvula
|
|
||||||
Vector2 posParticula = new Vector2(particula.Position.X, particula.Position.Y);
|
|
||||||
float distancia = VectorUtils.DistanceBetween(_posicionAjustada, posParticula);
|
|
||||||
float radioValvula = Diametro * 100 / 2; // Radio en unidades internas
|
|
||||||
|
|
||||||
// Verificar si la partícula está dentro del radio de acción de la válvula
|
|
||||||
if (distancia < radioValvula * 1.2)
|
|
||||||
{
|
|
||||||
// Si la válvula está cerrada o casi cerrada, bloquear paso
|
|
||||||
if (Apertura < 0.05f)
|
|
||||||
{
|
|
||||||
// Rechazar partícula
|
|
||||||
Vector2 direccion = VectorUtils.NormalizeVector(posParticula - _posicionAjustada);
|
|
||||||
particula.Position.X = _posicionAjustada.X + direccion.X * (radioValvula * 1.2f);
|
|
||||||
particula.Position.Y = _posicionAjustada.Y + direccion.Y * (radioValvula * 1.2f);
|
|
||||||
|
|
||||||
// Reducir velocidad significativamente
|
|
||||||
particula.Velocity.X *= 0.1f;
|
|
||||||
particula.Velocity.Y *= 0.1f;
|
|
||||||
}
|
|
||||||
// Si está parcialmente abierta, reducir velocidad proporcionalmente
|
|
||||||
else if (Apertura < 0.9f)
|
|
||||||
{
|
|
||||||
// Calcular factor de reducción basado en la apertura
|
|
||||||
float factorReduccion = Apertura * Apertura; // Relación no lineal
|
|
||||||
|
|
||||||
// Aplicar resistencia proporcional a la apertura
|
|
||||||
particula.Velocity.X *= factorReduccion;
|
|
||||||
particula.Velocity.Y *= factorReduccion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
using System;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace CtrEditor.Simulacion.Fluids.Components
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Utilidades para operaciones con Vector2 de nkast.Aether.Physics2D.Common
|
|
||||||
/// </summary>
|
|
||||||
public static class VectorUtils
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Calcula la distancia entre dos vectores
|
|
||||||
/// </summary>
|
|
||||||
public static float DistanceBetween(Vector2 v1, Vector2 v2)
|
|
||||||
{
|
|
||||||
float result;
|
|
||||||
Vector2 v1Ref = v1;
|
|
||||||
Vector2 v2Ref = v2;
|
|
||||||
Vector2.Distance(ref v1Ref, ref v2Ref, out result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calcula el producto punto entre dos vectores
|
|
||||||
/// </summary>
|
|
||||||
public static float DotProduct(Vector2 v1, Vector2 v2)
|
|
||||||
{
|
|
||||||
float result;
|
|
||||||
Vector2 v1Ref = v1;
|
|
||||||
Vector2 v2Ref = v2;
|
|
||||||
Vector2.Dot(ref v1Ref, ref v2Ref, out result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Normaliza un vector y retorna el resultado
|
|
||||||
/// </summary>
|
|
||||||
public static Vector2 NormalizeVector(Vector2 v)
|
|
||||||
{
|
|
||||||
Vector2 result;
|
|
||||||
float length = v.Length();
|
|
||||||
|
|
||||||
if (length < 1e-6f)
|
|
||||||
return new Vector2(0, 0);
|
|
||||||
|
|
||||||
Vector2.Divide(ref v, length, out result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calcula la reflexión de un vector respecto a una normal
|
|
||||||
/// </summary>
|
|
||||||
public static Vector2 Reflect(Vector2 vector, Vector2 normal)
|
|
||||||
{
|
|
||||||
Vector2 normalized = NormalizeVector(normal);
|
|
||||||
float dot;
|
|
||||||
Vector2 vectorRef = vector;
|
|
||||||
Vector2 normalizedRef = normalized;
|
|
||||||
Vector2.Dot(ref vectorRef, ref normalizedRef, out dot);
|
|
||||||
|
|
||||||
Vector2 result;
|
|
||||||
Vector2.Multiply(ref normalizedRef, 2f * dot, out result);
|
|
||||||
Vector2 vectorRef2 = vector;
|
|
||||||
Vector2 resultRef = result;
|
|
||||||
Vector2.Subtract(ref vectorRef2, ref resultRef, out result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
using tainicom.Aether.Physics2D.Fluids;
|
|
||||||
using CtrEditor.Simulacion.Fluids.Components;
|
|
||||||
|
|
||||||
namespace CtrEditor.Simulacion.Fluids
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Clase principal que gestiona la simulación de fluidos basada en FluidSystem2
|
|
||||||
/// </summary>
|
|
||||||
public class SimulacionFluidos
|
|
||||||
{
|
|
||||||
public FluidSystem2 SistemaFluido { get; private set; }
|
|
||||||
private List<IContenedorFluido> _contenedores = new List<IContenedorFluido>();
|
|
||||||
private int _worldWidth;
|
|
||||||
private int _worldHeight;
|
|
||||||
|
|
||||||
public int ParticlesCount => SistemaFluido?.ParticlesCount ?? 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor para el sistema de simulación de fluidos
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ancho">Ancho del área de simulación en metros</param>
|
|
||||||
/// <param name="alto">Alto del área de simulación en metros</param>
|
|
||||||
/// <param name="maxParticulas">Número máximo de partículas</param>
|
|
||||||
/// <param name="gravedad">Vector de gravedad</param>
|
|
||||||
public SimulacionFluidos(float ancho, float alto, int maxParticulas, Vector2 gravedad)
|
|
||||||
{
|
|
||||||
// Convertir parámetros a unidades internas de FluidSystem2
|
|
||||||
_worldWidth = (int)(ancho * 100); // Convertir a cm
|
|
||||||
_worldHeight = (int)(alto * 100); // Convertir a cm
|
|
||||||
|
|
||||||
// Inicializar el sistema
|
|
||||||
SistemaFluido = new FluidSystem2(
|
|
||||||
gravedad,
|
|
||||||
maxParticulas,
|
|
||||||
_worldWidth,
|
|
||||||
_worldHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
// Configurar comportamiento del fluido
|
|
||||||
SistemaFluido.ElasticityEnabled = true;
|
|
||||||
SistemaFluido.PlasticityEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Método para agregar una partícula al sistema
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="posicion">Posición en metros</param>
|
|
||||||
public void AgregarParticula(Vector2 posicion)
|
|
||||||
{
|
|
||||||
// Convertir a sistema de coordenadas de FluidSystem2
|
|
||||||
float xAjustado = posicion.X * 100 - _worldWidth/2;
|
|
||||||
float yAjustado = posicion.Y * 100;
|
|
||||||
|
|
||||||
SistemaFluido.AddParticle(new Vector2(xAjustado, yAjustado));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Agrega múltiples partículas en un área rectangular
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="centro">Centro del área</param>
|
|
||||||
/// <param name="ancho">Ancho del área</param>
|
|
||||||
/// <param name="alto">Alto del área</param>
|
|
||||||
/// <param name="cantidad">Cantidad de partículas a agregar</param>
|
|
||||||
public void AgregarParticulasEnArea(Vector2 centro, float ancho, float alto, int cantidad)
|
|
||||||
{
|
|
||||||
Random rnd = new Random();
|
|
||||||
for (int i = 0; i < cantidad; i++)
|
|
||||||
{
|
|
||||||
float x = centro.X - ancho/2 + (float)rnd.NextDouble() * ancho;
|
|
||||||
float y = centro.Y - alto/2 + (float)rnd.NextDouble() * alto;
|
|
||||||
AgregarParticula(new Vector2(x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Agrega un contenedor al sistema de fluidos
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="contenedor">Implementación de IContenedorFluido</param>
|
|
||||||
public void AgregarContenedor(IContenedorFluido contenedor)
|
|
||||||
{
|
|
||||||
_contenedores.Add(contenedor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Elimina un contenedor del sistema de fluidos
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="contenedor">Contenedor a eliminar</param>
|
|
||||||
public void RemoverContenedor(IContenedorFluido contenedor)
|
|
||||||
{
|
|
||||||
_contenedores.Remove(contenedor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actualiza la simulación avanzando un paso de tiempo
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="deltaTime">Tiempo transcurrido en segundos</param>
|
|
||||||
public void Actualizar(float deltaTime)
|
|
||||||
{
|
|
||||||
// Aplicar restricciones de contenedores personalizados
|
|
||||||
foreach (var contenedor in _contenedores)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < SistemaFluido.ParticlesCount; i++)
|
|
||||||
{
|
|
||||||
var particula = SistemaFluido.Particles[i];
|
|
||||||
contenedor.RestringirParticula(particula);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actualizar la física del sistema de fluidos
|
|
||||||
SistemaFluido.Update(deltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Obtiene la densidad del fluido en una posición específica
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="posicion">Posición en metros</param>
|
|
||||||
/// <returns>Densidad relativa de fluido (0-1)</returns>
|
|
||||||
public float ObtenerDensidadEnPosicion(Vector2 posicion)
|
|
||||||
{
|
|
||||||
// Convertir a sistema de coordenadas de FluidSystem2
|
|
||||||
float xAjustado = posicion.X * 100 - _worldWidth/2;
|
|
||||||
float yAjustado = posicion.Y * 100;
|
|
||||||
Vector2 posAjustada = new Vector2(xAjustado, yAjustado);
|
|
||||||
|
|
||||||
float densidadTotal = 0;
|
|
||||||
int particulasCercanas = 0;
|
|
||||||
|
|
||||||
// Buscar partículas cercanas y sumar sus densidades
|
|
||||||
foreach (var p in SistemaFluido.Particles)
|
|
||||||
{
|
|
||||||
float distancia;
|
|
||||||
Vector2 posAjustadaRef = posAjustada;
|
|
||||||
Vector2 positionRef = p.Position;
|
|
||||||
Vector2.Distance(ref posAjustadaRef, ref positionRef, out distancia);
|
|
||||||
if (distancia < FluidSystem2.InfluenceRadius)
|
|
||||||
{
|
|
||||||
densidadTotal += p.Density;
|
|
||||||
particulasCercanas++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calcular densidad promedio, normalizada
|
|
||||||
if (particulasCercanas > 0)
|
|
||||||
return Math.Min(1.0f, densidadTotal / (particulasCercanas * FluidSystem2.DensityRest * 1.5f));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ajusta la gravedad del sistema
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="gravedad">Nuevo vector de gravedad</param>
|
|
||||||
public void AjustarGravedad(Vector2 gravedad)
|
|
||||||
{
|
|
||||||
SistemaFluido.Gravity = gravedad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Limpia todas las partículas del sistema
|
|
||||||
/// </summary>
|
|
||||||
public void LimpiarParticulas()
|
|
||||||
{
|
|
||||||
// No hay método clear() directo en FluidSystem2, recreamos el sistema
|
|
||||||
SistemaFluido = new FluidSystem2(
|
|
||||||
SistemaFluido.Gravity,
|
|
||||||
SistemaFluido.MaxParticleLimit,
|
|
||||||
_worldWidth,
|
|
||||||
_worldHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
// Restaurar configuración
|
|
||||||
SistemaFluido.ElasticityEnabled = true;
|
|
||||||
SistemaFluido.PlasticityEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interfaz para contenedores de fluido personalizados como tubos, tanques, etc.
|
|
||||||
/// </summary>
|
|
||||||
public interface IContenedorFluido
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Aplica restricciones a una partícula para mantenerla dentro o fuera del contenedor
|
|
||||||
/// </summary>
|
|
||||||
void RestringirParticula(Particle particula);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,375 +0,0 @@
|
||||||
using CtrEditor.ObjetosSim;
|
|
||||||
using OpenCvSharp;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.Eventing.Reader;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Windows.Shapes;
|
|
||||||
|
|
||||||
public class Circle
|
|
||||||
{
|
|
||||||
private Vector2 position;
|
|
||||||
public float Left
|
|
||||||
{
|
|
||||||
get { return position.X; }
|
|
||||||
set { position.X = value; }
|
|
||||||
}
|
|
||||||
public float Top
|
|
||||||
{
|
|
||||||
get { return position.Y; }
|
|
||||||
set { position.Y = value; }
|
|
||||||
}
|
|
||||||
public float Diameter { get; set; }
|
|
||||||
public float Mass { get; set; }
|
|
||||||
public float AngleofMovement { get; set; } // En grados
|
|
||||||
public float Speed { get; set; }
|
|
||||||
public float Overlap { get; set; }
|
|
||||||
|
|
||||||
public Circle(float left = 0, float top = 0, float diameter = 10, float mass = 1, float angle = 0, float speed = 0)
|
|
||||||
{
|
|
||||||
position = new Vector2(left, top);
|
|
||||||
Diameter = diameter;
|
|
||||||
Mass = mass;
|
|
||||||
AngleofMovement = angle;
|
|
||||||
Speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Move(float timeStep_ms, List<Circle> circles, List<Rectangle> rectangles, List<Line> lines)
|
|
||||||
{
|
|
||||||
// Convertir timeStep de milisegundos a segundos para la simulación
|
|
||||||
float timeStepInSeconds = timeStep_ms / 1000.0f;
|
|
||||||
bool isTracted = false; // Indicador para verificar si el círculo está siendo traccionado
|
|
||||||
Overlap = 0;
|
|
||||||
|
|
||||||
// Aplicar fuerza desde el rectángulo si está sobre uno
|
|
||||||
foreach (var rectangle in rectangles)
|
|
||||||
{
|
|
||||||
float overlap = CalculateOverlapPercentage(this, rectangle);
|
|
||||||
if (overlap > 10)
|
|
||||||
{
|
|
||||||
Overlap += overlap;
|
|
||||||
isTracted = true; // El círculo está siendo traccionado por un rectángulo
|
|
||||||
// Convertir la velocidad del rectángulo de metros por minuto a metros por segundo
|
|
||||||
float rectangleSpeedInMetersPerSecond = rectangle.Speed / 60.0f;
|
|
||||||
|
|
||||||
if (rectangleSpeedInMetersPerSecond < Speed)
|
|
||||||
{
|
|
||||||
// Aplicar una fuerza de frenado si la velocidad del rectángulo es menor que la velocidad del círculo
|
|
||||||
float brakingForce = (Speed - rectangleSpeedInMetersPerSecond) * (overlap / 100.0f);
|
|
||||||
Speed -= brakingForce * timeStepInSeconds;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Alinear gradualmente la velocidad del círculo con la del rectángulo si es mayor
|
|
||||||
Speed += (rectangleSpeedInMetersPerSecond - Speed) * (overlap / 100.0f) * timeStepInSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
AngleofMovement = rectangle.Angle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si el círculo no está siendo traccionado, aplicar desaceleración
|
|
||||||
if (!isTracted)
|
|
||||||
{
|
|
||||||
float deceleration = (1.0f / Mass) * 10.0f; // Coeficiente de desaceleración inversamente proporcional a la masa
|
|
||||||
Speed -= deceleration * timeStepInSeconds;
|
|
||||||
if (Speed < 0) Speed = 0; // Evitar que la velocidad sea negativa
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calcular nueva posición
|
|
||||||
Vector2 direction = new Vector2((float)Math.Cos(AngleofMovement * Math.PI / 180), (float)Math.Sin(AngleofMovement * Math.PI / 180));
|
|
||||||
Vector2 velocity = direction * Speed * timeStepInSeconds;
|
|
||||||
position += velocity;
|
|
||||||
|
|
||||||
// Ajustar por colisiones con líneas
|
|
||||||
foreach (var line in lines)
|
|
||||||
{
|
|
||||||
Vector2 movementVector = Vector2FromPolar(Speed, AngleofMovement);
|
|
||||||
Vector2 circleCenter = GetCircleCenter(this);
|
|
||||||
|
|
||||||
// Calcular la nueva posición tentativa del centro del círculo
|
|
||||||
Vector2 newPosition = circleCenter + movementVector * timeStepInSeconds;
|
|
||||||
|
|
||||||
if (LineCircleCollision(newPosition, Diameter / 2, line, out Vector2 collisionPoint))
|
|
||||||
{
|
|
||||||
// Ajustar la posición del centro del círculo y el vector de movimiento
|
|
||||||
AdjustCircleAfterCollision(ref newPosition, ref movementVector, line, collisionPoint, Diameter / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actualizar la posición del círculo basada en el nuevo centro
|
|
||||||
Left = newPosition.X - Diameter / 2;
|
|
||||||
Top = newPosition.Y - Diameter / 2;
|
|
||||||
Speed = movementVector.Length();
|
|
||||||
AngleofMovement = PolarAngleFromVector(movementVector);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ajustar por superposición con otros círculos
|
|
||||||
foreach (var other in circles)
|
|
||||||
{
|
|
||||||
if (this != other && IsColliding(this, other))
|
|
||||||
{
|
|
||||||
AdjustForOverlap(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 GetCircleCenter(Circle circle)
|
|
||||||
{
|
|
||||||
return new Vector2(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private bool LineCircleCollision(Vector2 circleCenter, float radius, Line line, out Vector2 collisionPoint)
|
|
||||||
{
|
|
||||||
// Transformar la línea a un vector con el ángulo rotado
|
|
||||||
float angleRadians = line.Angle * (float)Math.PI / 180;
|
|
||||||
Vector2 lineDirection = new Vector2(
|
|
||||||
(float)Math.Cos(angleRadians),
|
|
||||||
(float)Math.Sin(angleRadians)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Calcular los puntos de inicio y fin de la línea
|
|
||||||
Vector2 lineStart = new Vector2(line.Left, line.Top);
|
|
||||||
Vector2 lineEnd = lineStart + lineDirection * line.Length;
|
|
||||||
|
|
||||||
// Encontrar el punto más cercano del centro del círculo a la línea
|
|
||||||
Vector2 lineToCircle = circleCenter - lineStart;
|
|
||||||
float projection = Vector2.Dot(lineToCircle, lineDirection);
|
|
||||||
projection = Math.Clamp(projection, 0, line.Length);
|
|
||||||
Vector2 closestPointOnLine = lineStart + projection * lineDirection;
|
|
||||||
|
|
||||||
// Calcular la distancia entre el círculo y el punto más cercano
|
|
||||||
float distance = Vector2.Distance(circleCenter, closestPointOnLine);
|
|
||||||
collisionPoint = closestPointOnLine;
|
|
||||||
|
|
||||||
// Verificar si hay colisión
|
|
||||||
return distance <= radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AdjustCircleAfterCollision(ref Vector2 circlePosition, ref Vector2 movementVector, Line line, Vector2 collisionPoint, float radius)
|
|
||||||
{
|
|
||||||
// Calcular el vector normal de la línea para reflejar la dirección de movimiento del círculo
|
|
||||||
float angleRadians = line.Angle * (float)Math.PI / 180;
|
|
||||||
Vector2 lineNormal = new Vector2(-(float)Math.Sin(angleRadians), (float)Math.Cos(angleRadians));
|
|
||||||
|
|
||||||
// Asegurar que la normal está correctamente orientada hacia fuera de la línea
|
|
||||||
if (Vector2.Dot(lineNormal, circlePosition - collisionPoint) < 0)
|
|
||||||
lineNormal = -lineNormal;
|
|
||||||
|
|
||||||
// Calcular el desplazamiento necesario para separar el círculo de la línea
|
|
||||||
Vector2 displacement = lineNormal * (radius - Vector2.Distance(circlePosition, collisionPoint));
|
|
||||||
circlePosition += displacement;
|
|
||||||
|
|
||||||
// Opcionalmente, ajustar la velocidad si el círculo debe "rebotar" de la línea
|
|
||||||
movementVector = Vector2.Reflect(movementVector, lineNormal);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Vector2 Vector2FromPolar(float speed, float angleDegrees)
|
|
||||||
{
|
|
||||||
float angleRadians = angleDegrees * (float)Math.PI / 180;
|
|
||||||
return new Vector2(
|
|
||||||
speed * (float)Math.Cos(angleRadians),
|
|
||||||
speed * (float)Math.Sin(angleRadians)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private float PolarAngleFromVector(Vector2 vector)
|
|
||||||
{
|
|
||||||
return (float)Math.Atan2(vector.Y, vector.X) * 180 / (float)Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void AdjustForOverlap(Circle other)
|
|
||||||
{
|
|
||||||
if (this == other) return; // No auto-interacción
|
|
||||||
|
|
||||||
float distance = Vector2.Distance(this.position, other.position);
|
|
||||||
float radiusSum = (this.Diameter / 2) + (other.Diameter / 2);
|
|
||||||
|
|
||||||
if (distance < radiusSum) // Los círculos están solapando
|
|
||||||
{
|
|
||||||
Vector2 directionToOther = Vector2.Normalize(other.position - this.position);
|
|
||||||
float overlapDistance = radiusSum - distance;
|
|
||||||
|
|
||||||
// Decidir qué círculo mover basado en sus velocidades
|
|
||||||
if (this.Speed == 0 && other.Speed > 0)
|
|
||||||
{
|
|
||||||
// Mover este círculo si su velocidad es cero y el otro se está moviendo
|
|
||||||
this.position -= directionToOther * overlapDistance;
|
|
||||||
}
|
|
||||||
else if (other.Speed == 0 && this.Speed > 0)
|
|
||||||
{
|
|
||||||
// Mover el otro círculo si su velocidad es cero y este se está moviendo
|
|
||||||
other.position += directionToOther * overlapDistance;
|
|
||||||
}
|
|
||||||
else if (this.Speed == 0 && other.Speed == 0)
|
|
||||||
{
|
|
||||||
// Si ambos tienen velocidad cero, mover ambos a la mitad del solapamiento
|
|
||||||
this.position -= directionToOther * (overlapDistance / 2);
|
|
||||||
other.position += directionToOther * (overlapDistance / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private bool IsColliding(Circle circle1, Circle circle2)
|
|
||||||
{
|
|
||||||
float distance = Vector2.Distance(circle1.position, circle2.position);
|
|
||||||
float radiusSum = (circle1.Diameter / 2) + (circle2.Diameter / 2);
|
|
||||||
return distance <= radiusSum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Circulos sobre Rectangulos
|
|
||||||
public static float CalculateOverlapPercentage(Circle circle, Rectangle rectangle)
|
|
||||||
{
|
|
||||||
// Convertir el círculo en un cuadrado aproximado.
|
|
||||||
float squareSide = circle.Diameter / (float)Math.Sqrt(Math.PI);
|
|
||||||
RotatedRect square = new RotatedRect(
|
|
||||||
new Point2f(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2),
|
|
||||||
new Size2f(squareSide, squareSide),
|
|
||||||
0 // Sin rotación
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ajustamos el rectángulo para que se considere rotado desde el centro, pero calculado desde Top-Left
|
|
||||||
RotatedRect rotatedRectangle = CreateRotatedRectFromTopLeft(rectangle);
|
|
||||||
|
|
||||||
// Usar OpenCV para encontrar la intersección.
|
|
||||||
using (var mat = new Mat())
|
|
||||||
{
|
|
||||||
var result = Cv2.RotatedRectangleIntersection(square, rotatedRectangle, mat);
|
|
||||||
if (result != RectanglesIntersectTypes.None)
|
|
||||||
{
|
|
||||||
// Calcular el área de la intersección
|
|
||||||
float intersectionArea = (float) Cv2.ContourArea(mat);
|
|
||||||
float circleArea = (float)(Math.PI * Math.Pow(circle.Diameter / 2, 2));
|
|
||||||
return (intersectionArea / circleArea) * 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; // No hay intersección
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RotatedRect CreateRotatedRectFromTopLeft(Rectangle rectangle)
|
|
||||||
{
|
|
||||||
// El punto de pivote es Top-Left, calculamos el centro sin rotar
|
|
||||||
float originalCenterX = rectangle.Left + rectangle.Length / 2.0f;
|
|
||||||
float originalCenterY = rectangle.Top + rectangle.Width / 2.0f;
|
|
||||||
|
|
||||||
// Convertimos el ángulo a radianes para la rotación
|
|
||||||
float angleRadians = rectangle.Angle * (float)Math.PI / 180;
|
|
||||||
|
|
||||||
// Calcular las nuevas coordenadas del centro después de la rotación
|
|
||||||
float rotatedCenterX = rectangle.Left + (originalCenterX - rectangle.Left) * (float)Math.Cos(angleRadians) - (originalCenterY - rectangle.Top) * (float)Math.Sin(angleRadians);
|
|
||||||
float rotatedCenterY = rectangle.Top + (originalCenterX - rectangle.Left) * (float)Math.Sin(angleRadians) + (originalCenterY - rectangle.Top) * (float)Math.Cos(angleRadians);
|
|
||||||
|
|
||||||
// Crear el RotatedRect con el nuevo centro y el tamaño original
|
|
||||||
RotatedRect rotatedRect = new RotatedRect(
|
|
||||||
new Point2f(rotatedCenterX, rotatedCenterY),
|
|
||||||
new Size2f(rectangle.Length, rectangle.Width),
|
|
||||||
rectangle.Angle
|
|
||||||
);
|
|
||||||
|
|
||||||
return rotatedRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Rectangle
|
|
||||||
{
|
|
||||||
private Vector2 position;
|
|
||||||
public float Left
|
|
||||||
{
|
|
||||||
get { return position.X; }
|
|
||||||
set { position = new Vector2(value, position.Y); }
|
|
||||||
}
|
|
||||||
public float Top
|
|
||||||
{
|
|
||||||
get { return position.Y; }
|
|
||||||
set { position = new Vector2(position.X, value); }
|
|
||||||
}
|
|
||||||
public float Length { get; set; }
|
|
||||||
public float Width { get; set; }
|
|
||||||
public float Angle { get; set; } // En grados
|
|
||||||
public float Speed { get; set; } // Velocidad del rectángulo
|
|
||||||
|
|
||||||
public Rectangle(float left = 0, float top = 0, float length = 10, float width = 10, float angle = 0, float speed = 0)
|
|
||||||
{
|
|
||||||
position = new Vector2(left, top);
|
|
||||||
Length = length;
|
|
||||||
Width = width;
|
|
||||||
Angle = angle;
|
|
||||||
Speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Line
|
|
||||||
{
|
|
||||||
private Vector2 position;
|
|
||||||
public float Left
|
|
||||||
{
|
|
||||||
get { return position.X; }
|
|
||||||
set { position = new Vector2(value, position.Y); }
|
|
||||||
}
|
|
||||||
public float Top
|
|
||||||
{
|
|
||||||
get { return position.Y; }
|
|
||||||
set { position = new Vector2(position.X, value); }
|
|
||||||
}
|
|
||||||
public float Length { get; set; }
|
|
||||||
public float Width { get; set; }
|
|
||||||
public float Angle { get; set; } // En grados
|
|
||||||
public float Grip { get; set; } // Friccion por contacto
|
|
||||||
|
|
||||||
public Line(float left = 0, float top = 0, float length = 10, float angle = 0, float grip = 0)
|
|
||||||
{
|
|
||||||
position = new Vector2(left, top);
|
|
||||||
Length = length;
|
|
||||||
Angle = angle;
|
|
||||||
Grip = grip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Square
|
|
||||||
{
|
|
||||||
public float Left { get; set; }
|
|
||||||
public float Top { get; set; }
|
|
||||||
public float Size { get; set; } // 'Size' es la longitud de un lado del cuadrado
|
|
||||||
|
|
||||||
public Square(float left, float top, float size)
|
|
||||||
{
|
|
||||||
Left = left;
|
|
||||||
Top = top;
|
|
||||||
Size = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Clase principal que gestiona la simulación
|
|
||||||
public class SimulationManager
|
|
||||||
{
|
|
||||||
public List<Circle> circles;
|
|
||||||
public List<Rectangle> rectangles;
|
|
||||||
public List<Line> lines;
|
|
||||||
|
|
||||||
|
|
||||||
public SimulationManager()
|
|
||||||
{
|
|
||||||
circles = new List<Circle>();
|
|
||||||
rectangles = new List<Rectangle>();
|
|
||||||
lines = new List<Line>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Step(float timeStep)
|
|
||||||
{
|
|
||||||
foreach (var circle in circles)
|
|
||||||
{
|
|
||||||
circle.Move(timeStep, circles, rectangles, lines);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.VisualBasic.Devices;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
|
|
||||||
namespace CtrEditor.Simulacion
|
|
||||||
{
|
|
||||||
internal class InterseccionCirculoRectangulo
|
|
||||||
{
|
|
||||||
// Definición de la función CalcularSuperficieCompartida
|
|
||||||
public static float CalcularSuperficieCompartida(Vector2[] vertices, Vector2 center, float radio)
|
|
||||||
{
|
|
||||||
float totalCircleArea = (float) (Math.PI * radio * radio);
|
|
||||||
|
|
||||||
// Distancia a líneas ajustado
|
|
||||||
float[] distances = new float[4];
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
distances[i] = DistanceFromLine(center, vertices[i], vertices[(i + 1) % 4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
float minDistance = float.MaxValue;
|
|
||||||
foreach (var dist in distances)
|
|
||||||
{
|
|
||||||
if (Math.Abs(dist) < Math.Abs(minDistance))
|
|
||||||
minDistance = dist;
|
|
||||||
}
|
|
||||||
float d = Math.Abs(minDistance);
|
|
||||||
|
|
||||||
float sharedArea = 0;
|
|
||||||
if (Array.TrueForAll(distances, dist => Math.Abs(dist) > radio))
|
|
||||||
{
|
|
||||||
sharedArea = totalCircleArea;
|
|
||||||
}
|
|
||||||
else if (d < radio)
|
|
||||||
{
|
|
||||||
float cosTheta = Math.Min(1, d / radio);
|
|
||||||
float sinTheta = (float)Math.Sqrt(Math.Max(0, radio * radio - d * d));
|
|
||||||
if (minDistance < 0) // El centro está dentro del rectángulo
|
|
||||||
{
|
|
||||||
float areaOutside = radio * radio * (float)Math.Acos(cosTheta) - d * sinTheta;
|
|
||||||
sharedArea = totalCircleArea - areaOutside;
|
|
||||||
}
|
|
||||||
else // El centro está fuera del rectángulo
|
|
||||||
{
|
|
||||||
sharedArea = radio * radio * (float)Math.Acos(cosTheta) - d * sinTheta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sharedArea = 0;
|
|
||||||
}
|
|
||||||
var area = (sharedArea / totalCircleArea) * 1.1f;
|
|
||||||
return area > 1 ? 1 : area;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float DistanceFromLine(Vector2 point, Vector2 start, Vector2 end)
|
|
||||||
{
|
|
||||||
float A = end.Y - start.Y;
|
|
||||||
float B = start.X - end.X;
|
|
||||||
float C = end.X * start.Y - start.X * end.Y;
|
|
||||||
float distance = (A * point.X + B * point.Y + C) / (float)Math.Sqrt(A * A + B * B);
|
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,244 +0,0 @@
|
||||||
using nkast.Aether.Physics2D.Collision.Shapes;
|
|
||||||
using nkast.Aether.Physics2D.Common;
|
|
||||||
using nkast.Aether.Physics2D.Dynamics;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using CtrEditor.Simulacion;
|
|
||||||
|
|
||||||
namespace CtrEditor.Simulacion
|
|
||||||
{
|
|
||||||
|
|
||||||
class OverlapedArea
|
|
||||||
{
|
|
||||||
public static float CalculateOverlapedArea(Body bodyA, Body bodyB)
|
|
||||||
{
|
|
||||||
List<Vector2> aVertices = GetRotatedVertices(bodyA);
|
|
||||||
List<Vector2> bVertices = GetRotatedVertices(bodyB);
|
|
||||||
|
|
||||||
List<Vector2> intersectionPolygon = SutherlandHodgmanClip(aVertices, bVertices);
|
|
||||||
|
|
||||||
//Debug.WriteLine("");
|
|
||||||
//Debug.WriteLine("");
|
|
||||||
//Debug.WriteLine("subjectPolygon :");
|
|
||||||
//foreach (var vertex in aVertices)
|
|
||||||
//{
|
|
||||||
// Debug.WriteLine(vertex);
|
|
||||||
//}
|
|
||||||
//Debug.WriteLine("clipPolygon :");
|
|
||||||
//foreach (var vertex in bVertices)
|
|
||||||
//{
|
|
||||||
// Debug.WriteLine(vertex);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//Debug.WriteLine("intersectionPolygon:");
|
|
||||||
//foreach (var vertex in intersectionPolygon)
|
|
||||||
//{
|
|
||||||
// Debug.WriteLine(vertex);
|
|
||||||
//}
|
|
||||||
return ComputePolygonArea(intersectionPolygon);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float ComputePolygonArea(List<Vector2> polygon)
|
|
||||||
{
|
|
||||||
float area = 0;
|
|
||||||
int count = polygon.Count;
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
Vector2 current = polygon[i];
|
|
||||||
Vector2 next = polygon[(i + 1) % count];
|
|
||||||
area += current.X * next.Y - next.X * current.Y;
|
|
||||||
}
|
|
||||||
return Math.Abs(area) / 2.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Vector2> SutherlandHodgmanClip(List<Vector2> subjectPolygon, List<Vector2> clipPolygon)
|
|
||||||
{
|
|
||||||
List<Vector2> outputList = new List<Vector2>(subjectPolygon);
|
|
||||||
|
|
||||||
for (int i = 0; i < clipPolygon.Count; i++)
|
|
||||||
{
|
|
||||||
Vector2 clipEdgeStart = clipPolygon[i];
|
|
||||||
Vector2 clipEdgeEnd = clipPolygon[(i + 1) % clipPolygon.Count];
|
|
||||||
List<Vector2> inputList = outputList;
|
|
||||||
outputList = new List<Vector2>();
|
|
||||||
|
|
||||||
for (int j = 0; j < inputList.Count; j++)
|
|
||||||
{
|
|
||||||
Vector2 currentVertex = inputList[j];
|
|
||||||
Vector2 previousVertex = inputList[(j + inputList.Count - 1) % inputList.Count];
|
|
||||||
|
|
||||||
if (IsInside(clipEdgeStart, clipEdgeEnd, currentVertex))
|
|
||||||
{
|
|
||||||
if (!IsInside(clipEdgeStart, clipEdgeEnd, previousVertex))
|
|
||||||
{
|
|
||||||
outputList.Add(ComputeIntersection(clipEdgeStart, clipEdgeEnd, previousVertex, currentVertex));
|
|
||||||
}
|
|
||||||
outputList.Add(currentVertex);
|
|
||||||
}
|
|
||||||
else if (IsInside(clipEdgeStart, clipEdgeEnd, previousVertex))
|
|
||||||
{
|
|
||||||
outputList.Add(ComputeIntersection(clipEdgeStart, clipEdgeEnd, previousVertex, currentVertex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsInside(Vector2 edgeStart, Vector2 edgeEnd, Vector2 point)
|
|
||||||
{
|
|
||||||
return (edgeEnd.X - edgeStart.X) * (point.Y - edgeStart.Y) > (edgeEnd.Y - edgeStart.Y) * (point.X - edgeStart.X);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Vector2 ComputeIntersection(Vector2 edgeStart, Vector2 edgeEnd, Vector2 point1, Vector2 point2)
|
|
||||||
{
|
|
||||||
Vector2 edge = edgeEnd - edgeStart;
|
|
||||||
Vector2 segment = point2 - point1;
|
|
||||||
float edgeCrossSegment = Cross(edge, segment);
|
|
||||||
|
|
||||||
if (Math.Abs(edgeCrossSegment) < float.Epsilon)
|
|
||||||
{
|
|
||||||
return point1; // Return any point on the segment since they are nearly parallel.
|
|
||||||
}
|
|
||||||
|
|
||||||
float t = Cross(point1 - edgeStart, edge) / edgeCrossSegment;
|
|
||||||
return point1 + t * segment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float Cross(Vector2 v1, Vector2 v2)
|
|
||||||
{
|
|
||||||
return v1.X * v2.Y - v1.Y * v2.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Vector2> GetRotatedRectangleVertices(Vector2 center, float width, float height, float rotation)
|
|
||||||
{
|
|
||||||
float halfWidth = width / 2.0f;
|
|
||||||
float halfHeight = height / 2.0f;
|
|
||||||
float cos = (float)Math.Cos(rotation);
|
|
||||||
float sin = (float)Math.Sin(rotation);
|
|
||||||
|
|
||||||
return new List<Vector2>
|
|
||||||
{
|
|
||||||
new Vector2(center.X + cos * (-halfWidth) - sin * (-halfHeight), center.Y + sin * (-halfWidth) + cos * (-halfHeight)),
|
|
||||||
new Vector2(center.X + cos * (halfWidth) - sin * (-halfHeight), center.Y + sin * (halfWidth) + cos * (-halfHeight)),
|
|
||||||
new Vector2(center.X + cos * (halfWidth) - sin * (halfHeight), center.Y + sin * (halfWidth) + cos * (halfHeight)),
|
|
||||||
new Vector2(center.X + cos * (-halfWidth) - sin * (halfHeight), center.Y + sin * (-halfWidth) + cos * (halfHeight))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Vector2> GetRotatedVertices(Body body)
|
|
||||||
{
|
|
||||||
List<Vector2> vertices = new List<Vector2>();
|
|
||||||
|
|
||||||
// Verificar el tipo de Shape del Body
|
|
||||||
foreach (var fixture in body.FixtureList)
|
|
||||||
{
|
|
||||||
if (fixture.Shape is PolygonShape polygonShape)
|
|
||||||
{
|
|
||||||
// Es un rectángulo o un polígono
|
|
||||||
foreach (var vertex in polygonShape.Vertices)
|
|
||||||
{
|
|
||||||
vertices.Add(RotatePoint(vertex, body.Position, body.Rotation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (fixture.Shape is CircleShape circleShape)
|
|
||||||
{
|
|
||||||
// Es un círculo
|
|
||||||
float radius = circleShape.Radius;
|
|
||||||
float halfSide = radius; // El lado del cuadrado inscrito es igual al radio
|
|
||||||
|
|
||||||
Vector2[] squareVertices = new Vector2[]
|
|
||||||
{
|
|
||||||
new Vector2(-halfSide, -halfSide),
|
|
||||||
new Vector2(halfSide, -halfSide),
|
|
||||||
new Vector2(halfSide, halfSide),
|
|
||||||
new Vector2(-halfSide, halfSide)
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var vertex in squareVertices)
|
|
||||||
{
|
|
||||||
vertices.Add(vertex + body.Position); // Trasladar el cuadrado a la posición del cuerpo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static Vector2 RotatePoint(Vector2 point, Vector2 origin, float rotation)
|
|
||||||
{
|
|
||||||
float cos = (float)Math.Cos(rotation);
|
|
||||||
float sin = (float)Math.Sin(rotation);
|
|
||||||
float x = point.X * cos - point.Y * sin;
|
|
||||||
float y = point.X * sin + point.Y * cos;
|
|
||||||
return new Vector2(x + origin.X, y + origin.Y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class OverlapedAreaFast
|
|
||||||
{
|
|
||||||
|
|
||||||
public static float CalculateOverlapedArea(simBotella botella, simTransporte conveyor)
|
|
||||||
{
|
|
||||||
var porcentajeSuperpuesto = (float) (1.1 * CalculateOverlapPercentage(botella.Body.Position, botella.Radius, conveyor.Body.Position, conveyor.Width, conveyor.Height, conveyor.Body.Rotation));
|
|
||||||
return porcentajeSuperpuesto > 1 ? 1 : porcentajeSuperpuesto; // 1.1 Porque las botellas apollan sobre un circulo inscripto 10 % menor.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double[] RotatePoint(double px, double py, double ox, double oy, double angle)
|
|
||||||
{
|
|
||||||
double cosAngle = Math.Cos(angle);
|
|
||||||
double sinAngle = Math.Sin(angle);
|
|
||||||
|
|
||||||
double qx = ox + cosAngle * (px - ox) - sinAngle * (py - oy);
|
|
||||||
double qy = oy + sinAngle * (px - ox) + cosAngle * (py - oy);
|
|
||||||
|
|
||||||
return new double[] { qx, qy };
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double CalculateOverlapPercentage(
|
|
||||||
Vector2 circleCenter, double circleRadius, Vector2 rectCenter, double rectWidth, double rectHeight, double rectAngle)
|
|
||||||
{
|
|
||||||
// Transform the center of the circle to the coordinate system of the rotated rectangle
|
|
||||||
double[] newCircleCenter = RotatePoint(circleCenter.X, circleCenter.Y, rectCenter.X, rectCenter.Y, -rectAngle);
|
|
||||||
|
|
||||||
// Create a square with the same rotation as the rectangle
|
|
||||||
double squareSide = 2 * circleRadius;
|
|
||||||
double[] squareCenter = newCircleCenter;
|
|
||||||
|
|
||||||
// Coordinates of the square (non-rotated)
|
|
||||||
double x3 = squareCenter[0] - circleRadius;
|
|
||||||
double y3 = squareCenter[1] - circleRadius;
|
|
||||||
double x4 = squareCenter[0] + circleRadius;
|
|
||||||
double y4 = squareCenter[1] + circleRadius;
|
|
||||||
|
|
||||||
// Coordinates of the rectangle (non-rotated)
|
|
||||||
double x1 = rectCenter.X - rectWidth / 2;
|
|
||||||
double y1 = rectCenter.Y - rectHeight / 2;
|
|
||||||
double x2 = rectCenter.X + rectWidth / 2;
|
|
||||||
double y2 = rectCenter.Y + rectHeight / 2;
|
|
||||||
|
|
||||||
// Limits of the intersection
|
|
||||||
double xLeft = Math.Max(x1, x3);
|
|
||||||
double xRight = Math.Min(x2, x4);
|
|
||||||
double yBottom = Math.Max(y1, y3);
|
|
||||||
double yTop = Math.Min(y2, y4);
|
|
||||||
|
|
||||||
// Width and height of the intersection
|
|
||||||
double intersectWidth = Math.Max(0, xRight - xLeft);
|
|
||||||
double intersectHeight = Math.Max(0, yTop - yBottom);
|
|
||||||
|
|
||||||
// Area of the intersection
|
|
||||||
double intersectionArea = intersectWidth * intersectHeight;
|
|
||||||
|
|
||||||
// Area of the square
|
|
||||||
double squareArea = squareSide * squareSide;
|
|
||||||
|
|
||||||
// Overlap percentage relative to the total area of the square
|
|
||||||
double squareOverlapPercentage = (squareArea > 0) ? (intersectionArea / squareArea) : 0;
|
|
||||||
|
|
||||||
return squareOverlapPercentage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ using System.Diagnostics;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CtrEditor.Simulacion.Fluids;
|
using CtrEditor.Simulacion.Fluids;
|
||||||
using nkast.Aether.Physics2D.Common;
|
using System.Numerics;
|
||||||
using CtrEditor.ObjetosSim;
|
using CtrEditor.ObjetosSim;
|
||||||
|
|
||||||
namespace CtrEditor
|
namespace CtrEditor
|
||||||
|
|
Loading…
Reference in New Issue