Adaptacion con simCurve

This commit is contained in:
Miguel 2024-05-30 18:48:37 +02:00
parent 56f8630a65
commit 09980689fb
13 changed files with 925 additions and 109 deletions

View File

@ -48,6 +48,16 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty] [ObservableProperty]
private float inercia_desde_simulacion; private float inercia_desde_simulacion;
[ObservableProperty]
bool test;
partial void OnTestChanged(bool value)
{
SimGeometria.Body.LinearVelocity = new Vector2(1,1);
}
[ObservableProperty]
private float porcentaje_Traccion;
[ObservableProperty] [ObservableProperty]
private float mass; private float mass;
@ -119,6 +129,7 @@ namespace CtrEditor.ObjetosSim
RemoverDesdeSimulacion = true; RemoverDesdeSimulacion = true;
Velocidad_desde_simulacion = SimGeometria.Body.LinearVelocity.ToString(); Velocidad_desde_simulacion = SimGeometria.Body.LinearVelocity.ToString();
Inercia_desde_simulacion = SimGeometria.Body.Inertia; Inercia_desde_simulacion = SimGeometria.Body.Inertia;
Porcentaje_Traccion = SimGeometria.OverlapPercentage;
} }
public override void ucLoaded() public override void ucLoaded()

View File

@ -0,0 +1,19 @@
<UserControl x:Class="CtrEditor.ObjetosSim.Dinamicos.ucBotellaCuello"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osBotella/>
</UserControl.DataContext>
<Ellipse Height="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Stroke="{Binding ColorButton_oculto}" Fill="Gray"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}" StrokeThickness="0.5"/>
</UserControl>

View File

@ -0,0 +1,175 @@
using System.Windows;
using System.Windows.Controls;
//using using Microsoft.Xna.Framework;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
using CommunityToolkit.Mvvm.ComponentModel;
using nkast.Aether.Physics2D.Common;
using System.Windows.Media;
namespace CtrEditor.ObjetosSim.Dinamicos
{
/// <summary>
/// Interaction logic for ucBotellaCuelloCuello.xaml
/// </summary>
public partial class osBotellaCuello : osBase, IosBase
{
private simBotella SimGeometria;
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Botella con Cuello";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private Brush colorButton_oculto;
[ObservableProperty]
private float diametro;
partial void OnDiametroChanged(float value)
{
SimGeometria?.SetDiameter(Diametro);
}
[ObservableProperty]
private string velocidad_desde_simulacion;
[ObservableProperty]
private float inercia_desde_simulacion;
[ObservableProperty]
private float mass;
partial void OnMassChanged(float value)
{
SimGeometria?.SetMass(value);
}
public Vector2 GetCentro()
{
return new Vector2(Left + Diametro / 2, Top + Diametro / 2);
}
public void SetCentro(float x, float y)
{ Left = x; Top = y; }
public void SetCentro(Vector2 centro)
{
Left = centro.X - Diametro / 2;
Top = centro.Y - Diametro / 2;
}
private void ActualizarGeometrias()
{
if (SimGeometria != null)
{
SimGeometria.SetPosition(GetCentro());
}
}
public osBotellaCuello()
{
Diametro = 0.10f;
Mass = 1;
ColorButton_oculto = Brushes.Gray;
}
public void UpdateAfterMove()
{
ActualizarGeometrias();
SimGeometria?.SetDiameter(Diametro);
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
SimGeometria?.SetDiameter(Diametro);
}
public override void UpdateGeometryStep()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdateControl(int elapsedMilliseconds)
{
SetCentro(SimGeometria.Center);
if (SimGeometria.isRestricted)
ColorButton_oculto = Brushes.Yellow;
else
{
if (SimGeometria.isOnTransports > 0)
ColorButton_oculto = Brushes.Red;
else
ColorButton_oculto = Brushes.Gray;
}
if (SimGeometria.Descartar) // Ha sido marcada para remover
RemoverDesdeSimulacion = true;
Velocidad_desde_simulacion = SimGeometria.Body.LinearVelocity.ToString();
Inercia_desde_simulacion = SimGeometria.Body.Inertia;
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
SimGeometria = simulationManager.AddCircle(Diametro, GetCentro(), Mass);
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
}
}
public partial class ucBotellaCuello : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBotellaCuello()
{
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) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
if (Datos is osBotellaCuello BotellaCuello)
BotellaCuello.UpdateAfterMove();
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -3,6 +3,7 @@ using CtrEditor.Siemens;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
namespace CtrEditor.ObjetosSim namespace CtrEditor.ObjetosSim
{ {
@ -11,6 +12,7 @@ namespace CtrEditor.ObjetosSim
/// </summary> /// </summary>
public partial class osFiller : osBase, IosBase public partial class osFiller : osBase, IosBase
{ {
TimerTON_TOFF _TON_TOFF = new TimerTON_TOFF();
// Otros datos y métodos relevantes para la simulación // Otros datos y métodos relevantes para la simulación
private float TiempoRestante; private float TiempoRestante;
@ -31,8 +33,23 @@ namespace CtrEditor.ObjetosSim
private float offsetLeftSalida; private float offsetLeftSalida;
[ObservableProperty] [ObservableProperty]
private float offsetTopSalida; private float offsetTopSalida;
[ObservableProperty]
private string tag_consenso;
[ObservableProperty] [ObservableProperty]
private bool consenso; private bool consenso;
partial void OnConsensoChanged(bool value)
{
_TON_TOFF.Senal = value;
}
[ObservableProperty]
private bool consenso_NC;
[ObservableProperty]
private bool consenso_Filtrado;
[ObservableProperty]
float filtro_consenso_s;
[ObservableProperty] [ObservableProperty]
private float botellas_hora; private float botellas_hora;
@ -54,8 +71,6 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty] [ObservableProperty]
private float diametro_botella; private float diametro_botella;
[ObservableProperty] [ObservableProperty]
private string tag_consenso;
[ObservableProperty]
private float ancho; private float ancho;
[ObservableProperty] [ObservableProperty]
private float alto; private float alto;
@ -70,16 +85,29 @@ namespace CtrEditor.ObjetosSim
Velocidad_actual_percentual = 0; Velocidad_actual_percentual = 0;
Diametro_botella = 0.1f; Diametro_botella = 0.1f;
Botellas_hora = 10000; Botellas_hora = 10000;
Filtro_consenso_s = 1;
} }
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{ {
Consenso = LeerBitTag(Tag_consenso); if (Consenso_NC)
Consenso = !LeerBitTag(Tag_consenso);
else
Consenso = LeerBitTag(Tag_consenso);
} }
public override void UpdateControl(int elapsedMilliseconds) public override void UpdateControl(int elapsedMilliseconds)
{ {
if (Consenso && Velocidad_actual_percentual > 0) bool habilitado;
_TON_TOFF.Tiempo_ON_s = _TON_TOFF.Tiempo_OFF_s = Filtro_consenso_s;
if (Consenso_Filtrado)
habilitado = _TON_TOFF.SenalFiltrada();
else
habilitado = Consenso;
if (habilitado && Velocidad_actual_percentual > 0)
{ {
TiempoRestante -= elapsedMilliseconds / 1000.0f; TiempoRestante -= elapsedMilliseconds / 1000.0f;
if (TiempoRestante <= 0) if (TiempoRestante <= 0)

View File

@ -117,7 +117,7 @@ namespace CtrEditor.ObjetosSim
public void Highlight(bool State) { } public void Highlight(bool State) { }
public int ZIndex() public int ZIndex()
{ {
return 1; return 3;
} }
} }

View File

@ -91,6 +91,8 @@ namespace CtrEditor.ObjetosSim
{ {
// Se llama antes de la simulacion // Se llama antes de la simulacion
ActualizarGeometrias(); ActualizarGeometrias();
} }
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{ {

View File

@ -32,9 +32,9 @@ namespace CtrEditor.ObjetosSim
} }
[ObservableProperty] [ObservableProperty]
public Brush color; Brush color;
[ObservableProperty] [ObservableProperty]
public bool luzCortada; bool luzCortada;
partial void OnLuzCortadaChanged(bool value) partial void OnLuzCortadaChanged(bool value)
{ {
@ -65,6 +65,20 @@ namespace CtrEditor.ObjetosSim
} }
[ObservableProperty]
float distancia_cuello;
[ObservableProperty]
bool detectarCuello;
partial void OnDetectarCuelloChanged(bool value)
{
if (Simulation_Photocell == null) return;
Simulation_Photocell.DetectNeck = value;
}
[ObservableProperty] [ObservableProperty]
float filter_Frecuency; float filter_Frecuency;
@ -149,7 +163,11 @@ namespace CtrEditor.ObjetosSim
} }
public override void UpdateControl(int elapsedMilliseconds) public override void UpdateControl(int elapsedMilliseconds)
{ {
LuzCortada = Simulation_Photocell.LuzCortada > 0; Distancia_cuello = Simulation_Photocell.Distancia;
if (DetectarCuello)
LuzCortada = Simulation_Photocell.LuzCortadaNeck;
else
LuzCortada = Simulation_Photocell.LuzCortada > 0;
} }
public override void UpdatePLCPrimerCiclo() public override void UpdatePLCPrimerCiclo()
{ {
@ -164,10 +182,10 @@ namespace CtrEditor.ObjetosSim
{ {
// El UserControl ya se ha cargado y podemos obtener las coordenadas para // El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion // crear el objeto de simulacion
ActualizarLeftTop(); base.ucLoaded();
if (_visualRepresentation is ucPhotocell uc) if (_visualRepresentation is ucPhotocell uc)
Simulation_Photocell = AddBarrera(simulationManager, uc.Photocell, Alto, Ancho, Angulo); Simulation_Photocell = AddBarrera(simulationManager, uc.Photocell, Alto, Ancho, Angulo, DetectarCuello);
} }
public override void ucUnLoaded() public override void ucUnLoaded()
{ {

View File

@ -18,7 +18,8 @@
</UserControl.DataContext> </UserControl.DataContext>
<Grid Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"> <Grid Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<!-- Title --> <!-- Title -->
@ -28,14 +29,14 @@
<!-- Descriptions --> <!-- Descriptions -->
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Titulo}" FontSize="16" HorizontalAlignment="Center" Margin="10"/> <TextBlock Grid.Row="0" Text="{Binding Titulo}" FontSize="10" HorizontalAlignment="Center"/>
<Canvas Grid.Row="1" x:Name="ChartCanvas" Background="White" Margin="10"/> <Canvas Grid.Row="1" x:Name="ChartCanvas" Background="White" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" />
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10"> <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" >
<TextBlock Text="{Binding Descripcion_Serie_1}" Margin="10" Foreground="{Binding Color_Serie_1}"/> <TextBlock Text="{Binding Descripcion_Serie_1}" Margin="1" FontSize="8" Foreground="{Binding Color_Serie_1}"/>
<TextBlock Text="{Binding Descripcion_Serie_2}" Margin="10" Foreground="{Binding Color_Serie_2}"/> <TextBlock Text="{Binding Descripcion_Serie_2}" Margin="1" FontSize="8" Foreground="{Binding Color_Serie_2}"/>
<TextBlock Text="{Binding Descripcion_Serie_3}" Margin="10" Foreground="{Binding Color_Serie_3}"/> <TextBlock Text="{Binding Descripcion_Serie_3}" Margin="1" FontSize="8" Foreground="{Binding Color_Serie_3}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -43,11 +43,17 @@ namespace CtrEditor.ObjetosSim.Traces
[ObservableProperty] [ObservableProperty]
float ancho; float ancho;
[ObservableProperty]
bool serie1_Tipo_Bool;
[ObservableProperty] [ObservableProperty]
string tag_Serie1; string tag_Serie1;
[ObservableProperty] [ObservableProperty]
bool serie2_Tipo_Bool;
[ObservableProperty]
string tag_Serie2; string tag_Serie2;
[ObservableProperty] [ObservableProperty]
bool serie3_Tipo_Bool;
[ObservableProperty]
string tag_Serie3; string tag_Serie3;
[ObservableProperty] [ObservableProperty]
@ -96,6 +102,45 @@ namespace CtrEditor.ObjetosSim.Traces
Max_Serie_1 = 2; Max_Serie_1 = 2;
Max_Serie_2 = 2; Max_Serie_2 = 2;
Max_Serie_3 = 2; Max_Serie_3 = 2;
Serie1_Tipo_Bool = true;
Serie2_Tipo_Bool = true;
Serie3_Tipo_Bool = true;
}
public override void UpdatePLCPrimerCiclo()
{
// Escribimos el valor actual al iniciar la conexion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
float v1, v2, v3;
base.UpdatePLC(plc, elapsedMilliseconds);
if (Serie1_Tipo_Bool)
v1 = LeerBitTag(Tag_Serie1) ? 0 : 1;
else
v1 = LeerWordTag(Tag_Serie1);
if (Serie2_Tipo_Bool)
v2 = LeerBitTag(Tag_Serie2) ? 0 : 1;
else
v2 = LeerWordTag(Tag_Serie2);
if (Serie3_Tipo_Bool)
v3 = LeerBitTag(Tag_Serie3) ? 0 : 1;
else
v3 = LeerWordTag(Tag_Serie3);
AddData(v1, v2, v3);
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
} }
public void AddData(float series1Value, float series2Value, float series3Value) public void AddData(float series1Value, float series2Value, float series3Value)
@ -121,13 +166,14 @@ namespace CtrEditor.ObjetosSim.Traces
{ {
uc.ChartCanvas.Children.Clear(); uc.ChartCanvas.Children.Clear();
DrawSeries(uc.ChartCanvas,_series1, Brushes.Red,Min_Serie_1, Max_Serie_1, Y_offset_1); DrawSeries(uc.ChartCanvas, _series1, Brushes.Red, Min_Serie_1, Max_Serie_1, Y_offset_1);
DrawSeries(uc.ChartCanvas,_series2, Brushes.Green, Min_Serie_2, Max_Serie_2, Y_offset_2); DrawSeries(uc.ChartCanvas, _series2, Brushes.Green, Min_Serie_2, Max_Serie_2, Y_offset_2);
DrawSeries(uc.ChartCanvas,_series3, Brushes.Blue, Min_Serie_3, Max_Serie_3, Y_offset_3); DrawSeries(uc.ChartCanvas, _series3, Brushes.Blue, Min_Serie_3, Max_Serie_3, Y_offset_3);
} }
} }
private void DrawSeries(Canvas ChartCanvas, List<float> series, Brush color, float minY, float maxY, float yoffset) private void DrawSeries(Canvas ChartCanvas, List<float> series, Brush color, float minY, float maxY, float yoffset)
{ {
if (series.Count < 2) return; if (series.Count < 2) return;
@ -139,53 +185,78 @@ namespace CtrEditor.ObjetosSim.Traces
} }
if (minY == maxY) return; if (minY == maxY) return;
float canvasHeight = (float) ChartCanvas.ActualHeight; float canvasHeight = (float)ChartCanvas.ActualHeight;
float canvasWidth = (float) ChartCanvas.ActualWidth; float canvasWidth = (float)ChartCanvas.ActualWidth;
float scaleX = canvasWidth / Max_Cantidad_Elementos; float scaleX = canvasWidth / Max_Cantidad_Elementos;
float scaleY = canvasHeight / (maxY - minY + yoffset); float scaleY = canvasHeight / (maxY - minY + yoffset);
for (int i = 1; i < series.Count; i++) int startIndex = 0;
{ float lastX = 0;
float x1 = (i - 1) * scaleX; float lastY = canvasHeight - (series[startIndex] - minY + yoffset) * scaleY;
float y1 = canvasHeight - (series[i - 1] - minY + yoffset) * scaleY;
float x2 = i * scaleX;
float y2 = canvasHeight - (series[i] - minY + yoffset) * scaleY;
var line = new Line while (startIndex < series.Count - 1)
{
// Buscar el próximo cambio en la serie
int endIndex = startIndex + 1;
while (endIndex < series.Count && series[endIndex] == series[startIndex])
{
endIndex++;
}
// Dibujar la línea horizontal desde startIndex hasta endIndex - 1
float x1 = lastX;
float y1 = lastY;
float x2 = (endIndex - 1) * scaleX;
float y2 = y1;
var horizontalLine = new Line
{ {
Stroke = color, Stroke = color,
StrokeThickness = 2, StrokeThickness = 0.4,
X1 = x1, X1 = x1,
Y1 = y1, Y1 = y1,
X2 = x2, X2 = x2,
Y2 = y2 Y2 = y2
}; };
ChartCanvas.Children.Add(line); ChartCanvas.Children.Add(horizontalLine);
// Dibujar la línea vertical en endIndex - 1 si no es el último punto
if (endIndex < series.Count)
{
float xVertical = x2;
float yVerticalStart = y2;
float yVerticalEnd = canvasHeight - (series[endIndex] - minY + yoffset) * scaleY;
var verticalLine = new Line
{
Stroke = color,
StrokeThickness = 0.4,
X1 = xVertical,
Y1 = yVerticalStart,
X2 = xVertical,
Y2 = yVerticalEnd
};
ChartCanvas.Children.Add(verticalLine);
// Actualizar la última posición dibujada
lastX = xVertical;
lastY = yVerticalEnd;
}
else
{
// Actualizar la última posición dibujada para la última línea horizontal
lastX = x2;
lastY = y2;
}
// Actualizar startIndex
startIndex = endIndex;
} }
} }
public override void UpdatePLCPrimerCiclo()
{
// Escribimos el valor actual al iniciar la conexion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
base.UpdatePLC(plc, elapsedMilliseconds);
float v1 = LeerBitTag(Tag_Serie1) ? 0 : 1;
float v2 = LeerBitTag(Tag_Serie2) ? 0 : 1;
float v3 = LeerBitTag(Tag_Serie3) ? 0 : 1;
AddData(v1, v2, v3);
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
}
} }
public partial class ucTrace3 : UserControl, IDataContainer public partial class ucTrace3 : UserControl, IDataContainer

View File

@ -22,6 +22,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Input; using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
using System.Diagnostics;
namespace CtrEditor.ObjetosSim namespace CtrEditor.ObjetosSim
{ {
@ -272,7 +273,7 @@ namespace CtrEditor.ObjetosSim
if (!_mainViewModel.IsSimulationRunning) if (!_mainViewModel.IsSimulationRunning)
_storyboard.SetSpeedRatio(0); _storyboard.SetSpeedRatio(0);
else else
_storyboard.SetSpeedRatio(velocidadActual); _storyboard.SetSpeedRatio(Math.Abs(velocidadActual));
} }
/// <summary> /// <summary>
@ -321,7 +322,7 @@ namespace CtrEditor.ObjetosSim
} }
} }
public float LeerWordTagScaled(PLCModel _plc, string Tag, float IN_scale_Min, float IN_scale_Max, float OUT_scale_Min, float OUT_scale_Max) public float LeerWordTagScaled(string Tag, float IN_scale_Min = 0, float IN_scale_Max = 27648, float OUT_scale_Min = 0, float OUT_scale_Max = 100)
{ {
if (_plc == null) return 0; if (_plc == null) return 0;
if (!string.IsNullOrEmpty(Tag)) if (!string.IsNullOrEmpty(Tag))
@ -332,7 +333,23 @@ namespace CtrEditor.ObjetosSim
{ {
SDataValue plcData = _plc.LeerTag(Tag); SDataValue plcData = _plc.LeerTag(Tag);
float Value = plcData.UInt16; // WORD float Value = plcData.UInt16; // WORD
return (Value - OUT_scale_Min) / (OUT_scale_Max - OUT_scale_Min) * (IN_scale_Max - IN_scale_Min) + IN_scale_Min; return (Value - IN_scale_Min) / (IN_scale_Max - IN_scale_Min) * (OUT_scale_Max - OUT_scale_Min) + OUT_scale_Min;
}
}
return 0;
}
public float LeerWordTag(string Tag)
{
if (_plc == null) return 0;
if (!string.IsNullOrEmpty(Tag))
{
if (float.TryParse(Tag, out float v))
return v;
if (_plc != null)
{
SDataValue plcData = _plc.LeerTag(Tag);
float Value = plcData.UInt16; // WORD
return Value;
} }
} }
return 0; return 0;
@ -460,9 +477,9 @@ namespace CtrEditor.ObjetosSim
return simulationManager.AddRectangle(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo); return simulationManager.AddRectangle(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo);
} }
public simBarrera AddBarrera(SimulationManagerFP simulationManager, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo) public simBarrera AddBarrera(SimulationManagerFP simulationManager, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo, bool detectarCuello)
{ {
return simulationManager.AddBarrera(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo); return simulationManager.AddBarrera(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo, detectarCuello);
} }
public void UpdateOrCreateLine(simGuia simGuia, System.Windows.Shapes.Rectangle wpfRect) public void UpdateOrCreateLine(simGuia simGuia, System.Windows.Shapes.Rectangle wpfRect)
@ -505,6 +522,60 @@ namespace CtrEditor.ObjetosSim
} }
public class TimerTON_TOFF
{
Stopwatch _stopwatch_ON = new Stopwatch();
Stopwatch _stopwatch_OFF = new Stopwatch();
private bool _senalFiltrada;
public float Tiempo_ON_s { get; set; }
public float Tiempo_OFF_s { get; set; }
private bool senal;
public bool Senal
{
get => senal;
set
{
senal = value;
if (value)
{
ReiniciarTimer(_stopwatch_ON);
_stopwatch_OFF.Reset();
}
else
{
_stopwatch_ON.Reset();
ReiniciarTimer(_stopwatch_OFF);
}
}
}
public TimerTON_TOFF()
{
Senal = false;
Tiempo_ON_s = 1;
Tiempo_OFF_s = 1;
}
public bool SenalFiltrada()
{
if (_stopwatch_ON.ElapsedMilliseconds > (Tiempo_ON_s * 1000))
_senalFiltrada = true;
if (_stopwatch_OFF.ElapsedMilliseconds > (Tiempo_OFF_s * 1000))
_senalFiltrada = false;
return _senalFiltrada;
}
void ReiniciarTimer(Stopwatch timer)
{
timer.Reset();
timer.Start();
}
}
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class HiddenAttribute : Attribute public class HiddenAttribute : Attribute
{ {

View File

@ -17,6 +17,7 @@ using nkast.Aether.Physics2D.Dynamics.Contacts;
using nkast.Aether.Physics2D.Collision; using nkast.Aether.Physics2D.Collision;
using Transform = nkast.Aether.Physics2D.Common.Transform; using Transform = nkast.Aether.Physics2D.Common.Transform;
using nkast.Aether.Physics2D.Dynamics.Joints; using nkast.Aether.Physics2D.Dynamics.Joints;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace CtrEditor.Simulacion namespace CtrEditor.Simulacion
{ {
@ -27,7 +28,7 @@ namespace CtrEditor.Simulacion
public void RemoverBody() public void RemoverBody()
{ {
if (Body != null && _world.BodyList.Count>0) if (Body != null && _world.BodyList.Count>0 && _world.BodyList.Contains(Body))
{ {
_world.Remove(Body); _world.Remove(Body);
} }
@ -38,7 +39,11 @@ namespace CtrEditor.Simulacion
} }
public void SetPosition(Vector2 centro) public void SetPosition(Vector2 centro)
{ {
Body.SetTransform(centro, Body.Rotation); try
{
Body.SetTransform(centro, Body.Rotation);
}
catch { }
} }
} }
@ -78,13 +83,13 @@ namespace CtrEditor.Simulacion
// Crear la geometría del sensor de curva // Crear la geometría del sensor de curva
List<Vertices> segments = CreateCurveVertices(_innerRadius, _outerRadius, _startAngle, _endAngle); List<Vertices> segments = CreateCurveVertices(_innerRadius, _outerRadius, _startAngle, _endAngle);
Body = new Body(); Body = _world.CreateBody();
foreach (var segment in segments) foreach (var segment in segments)
{ {
var shape = new PolygonShape(segment, 1f); var shape = new PolygonShape(segment, 1f);
var fixture = Body.CreateFixture(shape); var fixture = Body.CreateFixture(shape);
fixture.IsSensor = true; fixture.IsSensor = true;
} }
Body.Position = position; Body.Position = position;
Body.BodyType = BodyType.Static; Body.BodyType = BodyType.Static;
Body.Tag = this; Body.Tag = this;
@ -175,10 +180,13 @@ namespace CtrEditor.Simulacion
public class simTransporte : simBase public class simTransporte : simBase
{ {
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora public float Speed { get; set; } // Velocidad para efectos de cinta transportadora
public float Friction { get; set; } // Friccion para efectos de cinta transportadora
public float DistanceGuide2Guide { get; set; } public float DistanceGuide2Guide { get; set; }
public bool isBrake { get; set; } public bool isBrake { get; set; }
public bool TransportWithGuides = false; public bool TransportWithGuides = false;
private List<Action> _deferredActions; private List<Action> _deferredActions;
public float Width { get; set; }
public float Height { get; set; }
public simTransporte(World world, List<Action> deferredActions, float width, float height, Vector2 position, float angle = 0) public simTransporte(World world, List<Action> deferredActions, float width, float height, Vector2 position, float angle = 0)
{ {
@ -209,10 +217,15 @@ namespace CtrEditor.Simulacion
var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f); var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f);
Body.CreateFixture(newShape); Body.CreateFixture(newShape);
Width = width;
Height = height;
} }
public void Create(float width, float height, Vector2 position, float angle = 0) public void Create(float width, float height, Vector2 position, float angle = 0)
{ {
RemoverBody(); RemoverBody();
Width = width;
Height = height;
Friction = 0.1f;
Body = _world.CreateRectangle( width, height, 1f, position); Body = _world.CreateRectangle( width, height, 1f, position);
Body.FixtureList[0].IsSensor = true; Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static; Body.BodyType = BodyType.Static;
@ -223,13 +236,22 @@ namespace CtrEditor.Simulacion
public class simBarrera : simBase public class simBarrera : simBase
{ {
public float Distancia;
public int LuzCortada; public int LuzCortada;
public bool LuzCortadaNeck;
public bool DetectNeck;
public List<simBotella> ListSimBotellaContact;
float _height;
private List<Action> _deferredActions; private List<Action> _deferredActions;
public simBarrera(World world, List<Action> deferredActions, float width, float height, Vector2 position, float angle = 0) public simBarrera(World world, List<Action> deferredActions, float width, float height, Vector2 position, float angle = 0, bool detectectNeck = false)
{ {
_world = world; _world = world;
_height = height;
DetectNeck = detectectNeck;
_deferredActions = deferredActions; _deferredActions = deferredActions;
ListSimBotellaContact = new List<simBotella>();
Create(width, height, position, angle); Create(width, height, position, angle);
} }
@ -252,9 +274,11 @@ namespace CtrEditor.Simulacion
Body.CreateFixture(newShape); Body.CreateFixture(newShape);
} }
public void Create(float width, float height, Vector2 position, float angle = 0) public void Create(float width, float height, Vector2 position, float angle = 0, bool detectectNeck = false)
{ {
RemoverBody(); RemoverBody();
_height = height;
DetectNeck = detectectNeck;
Body = _world.CreateRectangle( width, height, 1f, position); Body = _world.CreateRectangle( width, height, 1f, position);
Body.FixtureList[0].IsSensor = true; Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static; Body.BodyType = BodyType.Static;
@ -262,6 +286,56 @@ namespace CtrEditor.Simulacion
Body.Tag = this; // Importante para la identificación durante la colisión Body.Tag = this; // Importante para la identificación durante la colisión
LuzCortada = 0; LuzCortada = 0;
} }
public void CheckIfNecksIsTouching()
{
if (LuzCortada == 0) return;
foreach (var botella in ListSimBotellaContact)
{
// Obtener el centro de la barrera
Vector2 sensorCenter = Body.Position;
// Calcular el vector de la línea horizontal centrada de la barrera
float halfHeight = _height / 2;
float cos = (float)Math.Cos(Body.Rotation);
float sin = (float)Math.Sin(Body.Rotation);
// Calcular los puntos inicial y final de la línea horizontal centrada y rotada
Vector2 lineStart = sensorCenter + new Vector2(-halfHeight * cos, halfHeight * sin);
Vector2 lineEnd = sensorCenter + new Vector2(halfHeight * cos, -halfHeight * sin);
// Proyectar el centro de la botella sobre la línea horizontal
Vector2 fixtureCenter = botella.Body.Position;
Vector2 closestPoint = ProjectPointOntoLine(fixtureCenter, lineStart, lineEnd);
// Calcular la distancia entre el punto más cercano y el centro del cuello de la botella
float distance;
Vector2.Distance(ref closestPoint,ref fixtureCenter, out distance);
Distancia = distance;
if (distance <= botella._neckRadius)
{
LuzCortadaNeck = true;
return;
}
}
LuzCortadaNeck = false;
}
private Vector2 ProjectPointOntoLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 lineDirection = lineEnd - lineStart;
lineDirection.Normalize();
Vector2 pointToLineStart = point - lineStart;
float projectionLength;
Vector2.Dot(ref pointToLineStart, ref lineDirection, out projectionLength);
return lineStart + projectionLength * lineDirection;
}
} }
public class simGuia : simBase public class simGuia : simBase
@ -290,29 +364,39 @@ namespace CtrEditor.Simulacion
public class simBotella : simBase public class simBotella : simBase
{ {
private float _radius; public float Radius;
private float _mass; private float _mass;
public bool Descartar = false; public bool Descartar = false;
public int isOnTransports; public int isOnTransports;
public List<simTransporte> ListOnTransports; public List<simBase> ListOnTransports;
public bool isRestricted; public bool isRestricted;
public bool isNoMoreRestricted; public bool isNoMoreRestricted;
public float OriginalMass; public float OriginalMass;
public simTransporte ConveyorRestrictedTo; public simTransporte ConveyorRestrictedTo;
public Fixture axisRestrictedBy; public Fixture axisRestrictedBy;
public float OverlapPercentage;
public float _neckRadius;
PrismaticJoint _activeJoint; PrismaticJoint _activeJoint;
private List<Action> _deferredActions; private List<Action> _deferredActions;
public simBotella(World world, List<Action> deferredActions, float diameter, Vector2 position, float mass) public simBotella(World world, List<Action> deferredActions, float diameter, Vector2 position, float mass,float neckRadius = 0)
{ {
_world = world; _world = world;
ListOnTransports = new List<simTransporte>(); ListOnTransports = new List<simBase>();
_deferredActions = deferredActions; _deferredActions = deferredActions;
_radius = diameter / 2; Radius = diameter / 2;
_mass = mass; _mass = mass;
_activeJoint = null; _activeJoint = null;
if (neckRadius<=0)
neckRadius = diameter / 4;
_neckRadius = neckRadius;
Create(position); Create(position);
} }
@ -349,7 +433,8 @@ namespace CtrEditor.Simulacion
RemoverBody(); RemoverBody();
isOnTransports = 0; isOnTransports = 0;
_activeJoint = null; _activeJoint = null;
Body = _world.CreateCircle( _radius, 1f, position);
Body = _world.CreateCircle( Radius, 1f, position);
Body.BodyType = BodyType.Dynamic; Body.BodyType = BodyType.Dynamic;
// Restablecer manejador de eventos de colisión // Restablecer manejador de eventos de colisión
@ -359,12 +444,12 @@ namespace CtrEditor.Simulacion
Body.Tag = this; // Importante para la identificación durante la colisión Body.Tag = this; // Importante para la identificación durante la colisión
// Configurar la fricción // Configurar la fricción
Body.SetFriction(0.5f); Body.SetFriction(0.2f);
// Configurar amortiguamiento // Configurar amortiguamiento
Body.LinearDamping = 1f; // Ajustar para controlar la reducción de la velocidad lineal Body.LinearDamping = 1f; // Ajustar para controlar la reducción de la velocidad lineal
Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular
Body.SetRestitution(0.1f); // Baja restitución para menos rebote Body.SetRestitution(0f); // Baja restitución para menos rebote
Body.SleepingAllowed = false; Body.SleepingAllowed = false;
Body.IsBullet = true; Body.IsBullet = true;
@ -372,7 +457,7 @@ namespace CtrEditor.Simulacion
public void SetDiameter(float diameter) public void SetDiameter(float diameter)
{ {
_radius = diameter / 2; Radius = diameter / 2;
Create(Body.Position); // Recrear el círculo con el nuevo tamaño Create(Body.Position); // Recrear el círculo con el nuevo tamaño
} }
@ -386,11 +471,13 @@ namespace CtrEditor.Simulacion
if (fixtureB.Body.Tag is simBarrera Sensor) if (fixtureB.Body.Tag is simBarrera Sensor)
{ {
Sensor.LuzCortada += 1; Sensor.LuzCortada += 1;
Sensor.ListSimBotellaContact.Add(this);
return true; return true;
} }
else if (fixtureB.Body.Tag is simCurve curve) else if (fixtureB.Body.Tag is simCurve curve)
{ {
curve.ApplyCurveEffect(fixtureA); isOnTransports += 1;
ListOnTransports.Add(curve);
return true; // No aplicar respuestas físicas return true; // No aplicar respuestas físicas
} }
else if (fixtureB.Body.Tag is simDescarte) else if (fixtureB.Body.Tag is simDescarte)
@ -419,22 +506,30 @@ namespace CtrEditor.Simulacion
return true; // No aplicar respuestas físicas return true; // No aplicar respuestas físicas
} }
public static bool IntersectAABBs(ref AABB aabbA, ref AABB aabbB, out AABB overlap) private float CalculateOverlapPercentage(simTransporte conveyor)
{ {
overlap = new AABB(); CircleShape circleShape = Body.FixtureList[0].Shape as CircleShape;
PolygonShape polygonShape = conveyor.Body.FixtureList[0].Shape as PolygonShape;
float minX = Math.Max(aabbA.LowerBound.X, aabbB.LowerBound.X); // Obtener centro y radio del círculo
float minY = Math.Max(aabbA.LowerBound.Y, aabbB.LowerBound.Y); Vector2 centroCirculo = Body.Position;
float maxX = Math.Min(aabbA.UpperBound.X, aabbB.UpperBound.X); float radio = circleShape.Radius;
float maxY = Math.Min(aabbA.UpperBound.Y, aabbB.UpperBound.Y);
if (minX < maxX && minY < maxY) // Obtener los vértices del polígono (rectángulo)
Vector2[] vertices = new Vector2[polygonShape.Vertices.Count];
float cos = (float)Math.Cos(conveyor.Body.Rotation);
float sin = (float)Math.Sin(conveyor.Body.Rotation);
for (int i = 0; i < polygonShape.Vertices.Count; i++)
{ {
overlap.LowerBound = new Vector2(minX, minY); Vector2 vertex = polygonShape.Vertices[i];
overlap.UpperBound = new Vector2(maxX, maxY); float rotatedX = vertex.X * cos - vertex.Y * sin + conveyor.Body.Position.X;
return true; float rotatedY = vertex.X * sin + vertex.Y * cos + conveyor.Body.Position.Y;
vertices[i] = new Vector2(rotatedX, rotatedY);
} }
return false;
// Calcular el porcentaje de la superficie compartida
return InterseccionCirculoRectangulo.CalcularSuperficieCompartida(vertices, centroCirculo, radio);
} }
private void HandleOnSeparation(Fixture sender, Fixture fixtureB, Contact contact) private void HandleOnSeparation(Fixture sender, Fixture fixtureB, Contact contact)
@ -445,29 +540,107 @@ namespace CtrEditor.Simulacion
ListOnTransports.Remove(transport); ListOnTransports.Remove(transport);
isOnTransports -= 1; isOnTransports -= 1;
} }
if (isOnTransports > 0 && fixtureB.Body.Tag is simCurve)
{
if (fixtureB.Body.Tag is simCurve transport)
ListOnTransports.Remove(transport);
isOnTransports -= 1;
}
if (isRestricted && fixtureB == axisRestrictedBy) if (isRestricted && fixtureB == axisRestrictedBy)
{ {
isRestricted = false; isRestricted = false;
isNoMoreRestricted = true; isNoMoreRestricted = true;
} }
if (fixtureB.Body.Tag is simBarrera Sensor) if (fixtureB.Body.Tag is simBarrera Sensor)
Sensor.LuzCortada -= 1;
}
public void ApplyConveyorSpeed()
{
Body.LinearVelocity = new Vector2(0.0f, 0.0f);
foreach (var conveyor in ListOnTransports)
{ {
ApplyConveyorEffect(conveyor); Sensor.LuzCortada -= 1;
Sensor.ListSimBotellaContact.Remove(this);
}
}
/// <summary>
/// Aplica la fuerza de traccion a la botellas segun los
/// transportes sobre los que se encuentra
/// </summary>
/// <param name="deltaTime_s"></param>
public void ApplyConveyorSpeed(float deltaTime_s)
{
foreach (var transporte in ListOnTransports)
{
if (transporte is simTransporte conveyorRect)
if (ApplyConveyorEffect(deltaTime_s, conveyorRect))
break;
if (transporte is simCurve conveyorCurve)
conveyorCurve.ApplyCurveEffect(Body.FixtureList[0]);
} }
} }
private void ApplyConveyorEffect(simTransporte conveyor) private bool ApplyConveyorEffect(float deltaTime_s, simTransporte conveyor)
{ {
// Calcular el porcentaje de superficie sobrepuesta
float overlapPercentage = CalculateOverlapedArea(this, conveyor); // CalculateOverlapPercentage(conveyor);
OverlapPercentage = overlapPercentage;
// Calcular la velocidad deseada del transporte
float speedMetersPerSecond = conveyor.Speed / 60.0f; float speedMetersPerSecond = conveyor.Speed / 60.0f;
Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond; Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond;
Body.LinearVelocity += desiredVelocity;
// Obtener la velocidad actual de la botella
Vector2 currentVelocity = Body.LinearVelocity;
// Calcular la diferencia de velocidad deseada
Vector2 velocityDifference = desiredVelocity - currentVelocity;
// Calcular la fuerza de fricción necesaria - 0 : ya esta en velocidad - 1 : esta detenido
float proporcionalVelocityNeeded = 1-CalculateProportion(currentVelocity, desiredVelocity);
float frictionCoefficient;
switch (proporcionalVelocityNeeded) {
case > 0.3f:
frictionCoefficient = conveyor.Friction * overlapPercentage;
break;
default:
frictionCoefficient = proporcionalVelocityNeeded * overlapPercentage;
break;
}
if (isRestricted && conveyor == ConveyorRestrictedTo && overlapPercentage > 0.5f)
{
Body.LinearVelocity = desiredVelocity;
return true;
}
// Aplicar la fuerza de fricción en función del porcentaje de superficie sobrepuesta
Body.LinearVelocity += frictionCoefficient * desiredVelocity;
//Body.ApplyForce(frictionForce * overlapPercentage);
return false;
}
public float CalculateOverlapedArea(simBotella botella, simTransporte conveyor)
{
//float areaBotella = (float) ((botella.Radius * botella.Radius * Math.PI) / (Math.PI / 4)); // Math.PI/4 porque es un cuadrado en vez de un circulo
//float area = OverlapedArea.CalculateOverlapedArea(botella.Body, conveyor.Body);
//if (areaBotella == 0) return 0;
//return area/areaBotella;
return OverlapedAreaFast.CalculateOverlapedArea(botella, conveyor);
}
public static float CalculateProportion(Vector2 currentVelocity, Vector2 desiredVelocity)
{
// Calcular la proyección escalar de v2 sobre v1
float dotProduct;
Vector2.Dot(ref desiredVelocity, ref currentVelocity, out dotProduct);
float magnitudeV1Squared = desiredVelocity.LengthSquared();
// Si la magnitud de v1 es 0, la proporción no está definida
if (magnitudeV1Squared == 0)
{
return 0;
}
// Calcular la proporción
float proportion = dotProduct / magnitudeV1Squared;
return proportion;
} }
public void CenterFixtureOnConveyor() public void CenterFixtureOnConveyor()
@ -559,19 +732,22 @@ namespace CtrEditor.Simulacion
{ {
if (cuerpo is simBotella botella) if (cuerpo is simBotella botella)
{ {
botella.ApplyConveyorSpeed(); botella.ApplyConveyorSpeed(elapsedMilliseconds/1000);
if (botella.isRestricted) if (botella.isRestricted)
{ {
botella.CenterFixtureOnConveyor(); botella.CenterFixtureOnConveyor();
botella.Body.Inertia = 0; botella.Body.Inertia = 0;
botella.Body.Mass = 20; botella.Body.Mass = 100;
} else if (botella.isNoMoreRestricted) }
else if (botella.isNoMoreRestricted)
{ {
botella.isNoMoreRestricted = false; botella.isNoMoreRestricted = false;
botella.Body.Mass = botella.OriginalMass; botella.Body.Mass = botella.OriginalMass;
} }
} }
else if (cuerpo is simBarrera barrera)
barrera.CheckIfNecksIsTouching();
} }
// Procesa las acciones diferidas // Procesa las acciones diferidas
@ -612,9 +788,9 @@ namespace CtrEditor.Simulacion
return rectangle; return rectangle;
} }
public simBarrera AddBarrera(float width, float height, Vector2 position, float angle) public simBarrera AddBarrera(float width, float height, Vector2 position, float angle, bool detectarCuello)
{ {
simBarrera rectangle = new simBarrera(world, _deferredActions, width, height, position, angle); simBarrera rectangle = new simBarrera(world, _deferredActions, width, height, position, angle, detectarCuello);
Cuerpos.Add(rectangle); Cuerpos.Add(rectangle);
return rectangle; return rectangle;
} }

View File

@ -12,9 +12,9 @@ namespace CtrEditor.Simulacion
internal class InterseccionCirculoRectangulo internal class InterseccionCirculoRectangulo
{ {
// Definición de la función CalcularSuperficieCompartida // Definición de la función CalcularSuperficieCompartida
public static float CalcularSuperficieCompartida(Vector2[] vertices, Vector2 center, float r) public static float CalcularSuperficieCompartida(Vector2[] vertices, Vector2 center, float radio)
{ {
float totalCircleArea = (float)Math.PI * r * r; float totalCircleArea = (float) (Math.PI * radio * radio);
// Distancia a líneas ajustado // Distancia a líneas ajustado
float[] distances = new float[4]; float[] distances = new float[4];
@ -32,30 +32,30 @@ namespace CtrEditor.Simulacion
float d = Math.Abs(minDistance); float d = Math.Abs(minDistance);
float sharedArea = 0; float sharedArea = 0;
if (Array.TrueForAll(distances, dist => Math.Abs(dist) > r)) if (Array.TrueForAll(distances, dist => Math.Abs(dist) > radio))
{ {
sharedArea = totalCircleArea; sharedArea = totalCircleArea;
} }
else if (d < r) else if (d < radio)
{ {
float cosTheta = Math.Min(1, d / r); float cosTheta = Math.Min(1, d / radio);
float sinTheta = (float)Math.Sqrt(Math.Max(0, r * r - d * d)); float sinTheta = (float)Math.Sqrt(Math.Max(0, radio * radio - d * d));
if (minDistance < 0) // El centro está dentro del rectángulo if (minDistance < 0) // El centro está dentro del rectángulo
{ {
float areaOutside = r * r * (float)Math.Acos(cosTheta) - d * sinTheta; float areaOutside = radio * radio * (float)Math.Acos(cosTheta) - d * sinTheta;
sharedArea = totalCircleArea - areaOutside; sharedArea = totalCircleArea - areaOutside;
} }
else // El centro está fuera del rectángulo else // El centro está fuera del rectángulo
{ {
sharedArea = r * r * (float)Math.Acos(cosTheta) - d * sinTheta; sharedArea = radio * radio * (float)Math.Acos(cosTheta) - d * sinTheta;
} }
} }
else else
{ {
sharedArea = 0; sharedArea = 0;
} }
var area = (sharedArea / totalCircleArea) * 1.1f;
return sharedArea / totalCircleArea; return area > 1 ? 1 : area;
} }
public static float DistanceFromLine(Vector2 point, Vector2 start, Vector2 end) public static float DistanceFromLine(Vector2 point, Vector2 start, Vector2 end)
@ -68,6 +68,7 @@ namespace CtrEditor.Simulacion
} }
} }
} }

243
Simulacion/OverlapedArea.cs Normal file
View File

@ -0,0 +1,243 @@
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(-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;
}
}
}