Funcionando la simulacion pero con problemas con el calculo de overlap

This commit is contained in:
Miguel 2024-05-08 08:00:31 +02:00
parent 3ba10b81c3
commit ef7d3e2618
9 changed files with 532 additions and 188 deletions

View File

@ -21,6 +21,7 @@ using System.Text.Json;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Windows.Data; using System.Windows.Data;
using System.Windows; using System.Windows;
using static System.Resources.ResXFileRef;
namespace CtrEditor namespace CtrEditor
{ {
@ -56,7 +57,7 @@ namespace CtrEditor
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick); ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
_timerSimulacion = new DispatcherTimer(); _timerSimulacion = new DispatcherTimer();
_timerSimulacion.Interval = TimeSpan.FromMilliseconds(100); // ajusta el intervalo según sea necesario _timerSimulacion.Interval = TimeSpan.FromMilliseconds(20); // ajusta el intervalo según sea necesario
_timerSimulacion.Tick += OnTickSimulacion; _timerSimulacion.Tick += OnTickSimulacion;
StartSimulationCommand = new RelayCommand(StartSimulation); StartSimulationCommand = new RelayCommand(StartSimulation);
@ -168,6 +169,7 @@ namespace CtrEditor
{ {
if (_selectedImage != value && value != null) if (_selectedImage != value && value != null)
{ {
StopSimulation();
SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
_selectedImage = value; _selectedImage = value;
ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); // Dispara el evento con la nueva ruta de imagen ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); // Dispara el evento con la nueva ruta de imagen
@ -235,7 +237,14 @@ namespace CtrEditor
{ {
obj.VisualRepresentation = null; obj.VisualRepresentation = null;
} }
var serializedData = JsonConvert.SerializeObject(ObjetosSimulables, settings); // Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter
var dataToSerialize = new SimulationData
{
ObjetosSimulables = ObjetosSimulables,
UnitConverter = PixelToMeter.Instance.calc
};
var serializedData = JsonConvert.SerializeObject(dataToSerialize, settings);
File.WriteAllText(datosDeTrabajo.ObtenerPathImagenConExtension(_selectedImage, ".json"), serializedData); File.WriteAllText(datosDeTrabajo.ObtenerPathImagenConExtension(_selectedImage, ".json"), serializedData);
} }
} }
@ -258,11 +267,16 @@ namespace CtrEditor
PreserveReferencesHandling = PreserveReferencesHandling.Objects, PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
}; };
if (jsonString != null)
{
ObjetosSimulables = JsonConvert.DeserializeObject<ObservableCollection<osBase>>(jsonString, settings);
// Ahora recorres la colección de objetos simulables var simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
if (simulationData != null)
{
ObjetosSimulables = simulationData.ObjetosSimulables;
// Restaura el UnitConverter si es necesario en otra parte de tu código
PixelToMeter.Instance.calc = simulationData.UnitConverter;
// Recorrer la colección de objetos simulables
foreach (var objetoSimulable in ObjetosSimulables) foreach (var objetoSimulable in ObjetosSimulables)
CrearUsercontrol(objetoSimulable); CrearUsercontrol(objetoSimulable);
} }
@ -272,6 +286,7 @@ namespace CtrEditor
catch { /* Consider logging the error or handling it appropriately */ } catch { /* Consider logging the error or handling it appropriately */ }
} }
private bool CrearUsercontrol(osBase osObjeto) private bool CrearUsercontrol(osBase osObjeto)
{ {
Type tipoObjeto = osObjeto.GetType(); Type tipoObjeto = osObjeto.GetType();
@ -303,6 +318,11 @@ namespace CtrEditor
} }
public class SimulationData
{
public ObservableCollection<osBase> ObjetosSimulables { get; set; }
public UnitConverter UnitConverter { get; set; }
}
public class TipoSimulable public class TipoSimulable
{ {

View File

@ -22,6 +22,7 @@ namespace CtrEditor
/// <summary> /// <summary>
/// Interaction logic for MainWindow.xaml /// Interaction logic for MainWindow.xaml
/// </summary> /// </summary>
///
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
// Para el Canvas // Para el Canvas
@ -85,19 +86,21 @@ namespace CtrEditor
double centerY = offsetY + (visibleHeight / scaleY) / 2; double centerY = offsetY + (visibleHeight / scaleY) / 2;
// Ajusta la posición del UserControl para que esté centrado en el área visible // Ajusta la posición del UserControl para que esté centrado en el área visible
double left = centerX - (userControl.ActualWidth / 2); double leftPixels = centerX - (userControl.ActualWidth / 2);
double top = centerY - (userControl.ActualHeight / 2); double topPixels = centerY - (userControl.ActualHeight / 2);
// Establece la posición del UserControl // Establece la posición del UserControl
NuevoOS.Left = (float)left; NuevoOS.LeftPixels = (float)leftPixels;
NuevoOS.Top = (float)top; NuevoOS.TopPixels = (float)topPixels;
NuevoOS.Inicializado = true; NuevoOS.Inicializado = true;
} }
else
// Establece la posición del UserControl {
Canvas.SetLeft(userControl, NuevoOS.Left); // Fuerza a Establecer la posición del UserControl
Canvas.SetTop(userControl, NuevoOS.Top); NuevoOS.LeftPixels = NuevoOS.LeftPixels;
NuevoOS.TopPixels = NuevoOS.TopPixels;
}
// Suscribirse a eventos de mouse para marcar el Control // Suscribirse a eventos de mouse para marcar el Control
userControl.MouseEnter += UserControl_MouseEnter; userControl.MouseEnter += UserControl_MouseEnter;
@ -109,7 +112,8 @@ namespace CtrEditor
userControl.MouseMove += UserControl_MouseMove; userControl.MouseMove += UserControl_MouseMove;
// Añade el UserControl al Canvas // Añade el UserControl al Canvas
ImagenEnTrabajoCanvas.Children.Add(userControl); Canvas.SetZIndex(userControl, dataContainer.ZIndex());
ImagenEnTrabajoCanvas.Children.Add(userControl);
} }
} }

View File

@ -52,7 +52,7 @@ namespace CtrEditor.ObjetosSim
dataContainer.Datos = datos; dataContainer.Datos = datos;
userControl.DataContext = datos; userControl.DataContext = datos;
datos.VisualRepresentation = userControl; datos.VisualRepresentation = userControl;
datos.ConnectSimManager(simulationManager); datos.ConnectSimManager(simulationManager);
} }
} }
} }

View File

@ -26,34 +26,24 @@ namespace CtrEditor.ObjetosSim
void Move(float Left, float Top); void Move(float Left, float Top);
void Rotate(float Angle); void Rotate(float Angle);
void Highlight(bool State); void Highlight(bool State);
int ZIndex();
} }
public abstract class osBase : INotifyPropertyChanged, IosBase public abstract class osBase : INotifyPropertyChanged, IosBase
{ {
private string _nombre = "Base"; public abstract float LeftPixels { get; set; }
public abstract float TopPixels { get; set; }
public abstract float Left { get; set; } public abstract float Left { get; set; }
public abstract float Top { get; set; } public abstract float Top { get; set; }
public bool Inicializado = false; public bool Inicializado = false;
protected UserControl? _visualRepresentation = null; protected UserControl? _visualRepresentation = null;
public string Nombre public abstract string Nombre { get; set; }
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public abstract void ConnectSimManager(SimulationManager simulationManager); public abstract void ConnectSimManager(SimulationManager simulationManager);
public abstract void UpdateControl(); public abstract void UpdateControl();
[JsonIgnore] [JsonIgnore]
public UserControl? VisualRepresentation public UserControl? VisualRepresentation
@ -61,6 +51,7 @@ namespace CtrEditor.ObjetosSim
get => _visualRepresentation; get => _visualRepresentation;
set => _visualRepresentation = value; set => _visualRepresentation = value;
} }
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) protected virtual void OnPropertyChanged(string propertyName)
@ -68,4 +59,62 @@ namespace CtrEditor.ObjetosSim
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
} }
public class PixelToMeter
{
// Instancia privada estática, parte del patrón Singleton
private static PixelToMeter? _instance;
public UnitConverter calc = new UnitConverter(0.01f);
// Propiedad pública estática para acceder a la instancia
public static PixelToMeter Instance
{
get
{
if (_instance == null)
{
_instance = new PixelToMeter();
}
return _instance;
}
}
}
public class UnitConverter
{
// La escala representa cuántos metros hay en un píxel
public float Scale { get; private set; }
public UnitConverter(float scale)
{
if (scale <= 0)
throw new ArgumentException("Scale must be greater than zero.");
Scale = scale;
}
// Convierte una distancia en metros a píxeles
public float MetersToPixels(float meters)
{
return meters / Scale;
}
// Convierte una distancia en píxeles a metros
public float PixelsToMeters(float pixels)
{
return pixels * Scale;
}
// Configurar o ajustar la escala
public void SetScale(float newScale)
{
if (newScale <= 0)
throw new ArgumentException("Scale must be greater than zero.");
Scale = newScale;
}
}
} }

View File

@ -1,6 +1,6 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBotella" <UserControl x:Class="CtrEditor.ObjetosSim.ucBotella"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Ellipse Height="{Binding Diametro}" Stroke="red" Fill="Gray" Width="{Binding Diametro}"/> <Ellipse Height="{Binding DiametroPixels}" Stroke="red" Fill="Gray" Width="{Binding DiametroPixels}"/>
</UserControl> </UserControl>

View File

@ -35,8 +35,20 @@ namespace CtrEditor.ObjetosSim
{ {
Data.Diameter = value; Data.Diameter = value;
OnPropertyChanged(nameof(Diametro)); OnPropertyChanged(nameof(Diametro));
OnPropertyChanged(nameof(DiametroPixels));
} }
} }
public float DiametroPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Diameter);
set
{
Data.Diameter = PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(Diametro));
OnPropertyChanged(nameof(DiametroPixels));
}
}
public float Mass { public float Mass {
get => Data.Mass; get => Data.Mass;
set set
@ -46,39 +58,83 @@ namespace CtrEditor.ObjetosSim
} }
} }
public override float Left public override float LeftPixels
{ {
get => Data.Center.X; get => PixelToMeter.Instance.calc.MetersToPixels(Data.Left);
set set
{ {
Data.Center = new Vector2(value,Top); Data.Left = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null) if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, value); Canvas.SetLeft(_visualRepresentation, value);
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left));
}
}
public override float TopPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Top);
set
{
Data.Top = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation,value);
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top));
}
}
public override float Left
{
get => Data.Left;
set
{
Data.Left = value;
if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left)); OnPropertyChanged(nameof(Left));
} }
} }
public override float Top public override float Top
{ {
get => Data.Center.Y; get => Data.Top;
set set
{ {
Data.Center = new Vector2(Left, value); Data.Top = value;
if (_visualRepresentation != null) if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation, value); Canvas.SetTop(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top)); OnPropertyChanged(nameof(Top));
} }
} }
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public osBotella()
{
DiametroPixels = 10;
}
public override void ConnectSimManager(SimulationManager simulationManager) public override void ConnectSimManager(SimulationManager simulationManager)
{ {
simulationManager.circles.Add(Data); simulationManager.circles.Add(Data);
} }
public override void UpdateControl() public override void UpdateControl()
{ {
Top = Data.Center.Y; Top = Data.Top;
Left = Data.Center.X; Left = Data.Left;
} }
} }
public partial class ucBotella : UserControl, IDataContainer public partial class ucBotella : UserControl, IDataContainer
@ -90,16 +146,19 @@ namespace CtrEditor.ObjetosSim
InitializeComponent(); InitializeComponent();
} }
public void Resize(float width, float height) { } public void Resize(float width, float height) { }
public void Move(float Left, float Top) public void Move(float LeftPixels, float TopPixels)
{ {
if (Datos != null) if (Datos != null)
{ {
Datos.Left = Left; Datos.LeftPixels = LeftPixels;
Datos.Top = Top; Datos.TopPixels = TopPixels;
} }
} }
public void Rotate(float Angle) { } public void Rotate(float Angle) { }
public void Highlight(bool State) { } public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
} }
} }

View File

@ -6,7 +6,7 @@
xmlns:local="clr-namespace:CtrEditor" xmlns:local="clr-namespace:CtrEditor"
mc:Ignorable="d"> mc:Ignorable="d">
<Canvas> <Canvas>
<Rectangle Width="{Binding Ancho}" Height="{Binding Alto}" Fill="Gray"> <Rectangle Width="{Binding AnchoPixels}" Height="{Binding AltoPixels}" Fill="Gray">
<Rectangle.RenderTransform> <Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/> <RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform> </Rectangle.RenderTransform>

View File

@ -34,6 +34,30 @@ namespace CtrEditor.ObjetosSim
private Rectangle Data = new Rectangle(); private Rectangle Data = new Rectangle();
public override float LeftPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Left);
set
{
Data.Left = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null )
Canvas.SetLeft(_visualRepresentation,value);
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left));
}
}
public override float TopPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Top);
set
{
Data.Top = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation, value);
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top));
}
}
public override float Left public override float Left
{ {
get => Data.Left; get => Data.Left;
@ -41,7 +65,8 @@ namespace CtrEditor.ObjetosSim
{ {
Data.Left = value; Data.Left = value;
if (_visualRepresentation != null) if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, value); Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left)); OnPropertyChanged(nameof(Left));
} }
} }
@ -52,27 +77,48 @@ namespace CtrEditor.ObjetosSim
{ {
Data.Top = value; Data.Top = value;
if (_visualRepresentation != null) if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation, value); Canvas.SetTop(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top)); OnPropertyChanged(nameof(Top));
} }
} }
public float Ancho { public float Ancho {
get => Data.Width; get => Data.Length;
set set
{ {
Data.Width = value; Data.Length = value;
OnPropertyChanged(nameof(Ancho)); OnPropertyChanged(nameof(Ancho));
} }
} }
public float Alto { public float Alto {
get => Data.Height; get => Data.Width;
set set
{ {
Data.Height = value; Data.Width = value;
OnPropertyChanged(nameof(Alto)); OnPropertyChanged(nameof(Alto));
} }
} }
public float AnchoPixels
{
get => (float)PixelToMeter.Instance.calc.MetersToPixels(Data.Length);
set
{
Data.Length = (float)PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(Ancho));
}
}
public float AltoPixels
{
get => (float)PixelToMeter.Instance.calc.MetersToPixels(Data.Width);
set
{
Data.Width = (float)PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(Alto));
}
}
public float Angulo public float Angulo
{ {
get => Data.Angle; get => Data.Angle;
@ -91,6 +137,19 @@ namespace CtrEditor.ObjetosSim
} }
} }
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; } public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; }
public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; } public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; }
public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; } public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; }
@ -98,8 +157,8 @@ namespace CtrEditor.ObjetosSim
public osTransporteTTop() public osTransporteTTop()
{ {
Ancho = 100; AnchoPixels = 100;
Alto = 10; AltoPixels = 10;
} }
public override void ConnectSimManager(SimulationManager simulationManager) public override void ConnectSimManager(SimulationManager simulationManager)
@ -125,12 +184,12 @@ namespace CtrEditor.ObjetosSim
if (Datos is osTransporteTTop datos) if (Datos is osTransporteTTop datos)
datos.Ancho = width; datos.Ancho = width;
} }
public void Move(float Left, float Top) public void Move(float LeftPixels, float TopPixels)
{ {
if (Datos != null) if (Datos != null)
{ {
Datos.Left = Left; Datos.LeftPixels = LeftPixels;
Datos.Top = Top; Datos.TopPixels = TopPixels;
} }
} }
public void Rotate(float Angle) { public void Rotate(float Angle) {
@ -139,6 +198,11 @@ namespace CtrEditor.ObjetosSim
datos.Angulo = Angle; datos.Angulo = Angle;
} }
public void Highlight(bool State) { } public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
} }
} }

View File

@ -1,179 +1,328 @@
using Newtonsoft.Json; using CtrEditor.ObjetosSim;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Numerics; using System.Numerics;
using System.Windows.Shapes;
// Definición de la clase Circle public class Circle
public class Circle : ObjetoGeometrico
{ {
public Vector2 Center { get; set; } 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 Diameter { get; set; }
public float Mass { get; set; } public float Mass { get; set; }
[JsonIgnore] public float Angle { get; set; } // En grados
public Vector2 Velocity { get; set; } public float Speed { get; set; }
public Circle(Vector2 center, float diameter, float mass, Vector2 velocity) public Circle(float left = 0, float top = 0, float diameter = 10, float mass = 1, float angle = 0, float speed = 0)
{ {
Center = center; position = new Vector2(left, top);
Diameter = diameter; Diameter = diameter;
Mass = mass; Mass = mass;
Velocity = velocity; Angle = angle;
} Speed = speed;
public Circle() {
Center = new Vector2(0,0);
Center.X = 0;
Diameter = 10;
Mass = 1;
Velocity = new Vector2(0,0);
} }
// Método para calcular la nueva posición del círculo en función del tiempo public void Move(float timeStep_ms, List<Circle> circles, List<Rectangle> rectangles, List<Line> lines)
public void Move(float timeStep, List<Circle> circles, List<Rectangle> rectangles, List<Line> lines)
{ {
Vector2 totalForce = new Vector2(0, 0); // 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
// Aplicar fuerza de los rectángulos // Aplicar fuerza desde el rectángulo si está sobre uno
foreach (var rectangle in rectangles) foreach (var rectangle in rectangles)
{ {
totalForce += rectangle.ApplyForce(this); float overlap = CalculateOverlapPercentage(this, rectangle);
} if (overlap > 0)
// Aplicar fuerza a otros círculos
foreach (var otherCircle in circles)
{
if (otherCircle != this)
{ {
totalForce += ApplyForceToOtherCircle(otherCircle); 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;
}
Angle = rectangle.Angle;
} }
} }
// Aplicar fuerza debido a la inercia (ejemplo simple) // Si el círculo no está siendo traccionado, aplicar desaceleración
Vector2 inertiaForce = -0.1f * Velocity; // coeficiente de inercia if (!isTracted)
totalForce += inertiaForce; {
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 la aceleración //// Interacción por impacto con otros círculos
if (Mass <= 0) //foreach (var other in circles)
Mass = 1; //{
Vector2 acceleration = totalForce / Mass; // if (this != other && IsColliding(this, other))
// {
// Vector2 impactDirection = other.position - this.position;
// Angle = (float)Math.Atan2(impactDirection.Y, impactDirection.X) * (180 / (float)Math.PI);
// Speed = other.Speed; // Asumimos que el círculo receptor adopta la velocidad del impacto
// }
//}
// Actualizar la velocidad y la posición // Ajustar por superposición con otros círculos
Velocity += acceleration * timeStep / 1000; foreach (var other in circles)
Center += Velocity * timeStep; {
if (this != other && IsColliding(this, other))
{
AdjustForOverlap(other);
}
}
// Controlar la colisión con las líneas // Cambiar dirección al contacto con líneas
foreach (var line in lines) foreach (var line in lines)
{ {
line.HandleCollision(this); if (IsCollidingWithLine(this, line))
{
float impactAngle = CalculateImpactAngle(this, line);
if (impactAngle < 85)
{
Angle = line.Angle;
}
else if (impactAngle > 95)
{
Angle = line.Angle + 180; // Movimiento contrario
}
else
{
Speed = 0; // Cancelación de movimiento
}
}
} }
// Calcular nueva posición
Vector2 direction = new Vector2((float)Math.Cos(Angle * Math.PI / 180), (float)Math.Sin(Angle * Math.PI / 180));
Vector2 velocity = direction * Speed * timeStepInSeconds;
position += velocity;
} }
private Vector2 ApplyForceToOtherCircle(Circle other)
{
Vector2 direction = other.Center - this.Center;
float distance = direction.Length();
float overlap = this.Diameter / 2 + other.Diameter / 2 - distance;
if (overlap > 0)
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 forceDirection = Vector2.Normalize(direction); Vector2 directionToOther = Vector2.Normalize(other.position - this.position);
float transferVelocity = 0.5f * this.Velocity.Length(); // Ejemplo de transferencia de parte de la velocidad float overlapDistance = radiusSum - distance;
if (transferVelocity == 0)
transferVelocity = 0.01f; // Decidir qué círculo mover basado en sus velocidades
other.Velocity += forceDirection * transferVelocity; // Asumiendo una simplificación del impacto if (this.Speed == 0 && other.Speed > 0)
return -forceDirection * transferVelocity; // Retorno de fuerza opuesta aplicada a este círculo {
// 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);
}
} }
return Vector2.Zero;
}
}
// Definición de la clase Rectangle
public class Rectangle : ObjetoGeometrico
{
public Vector2 AnchorPoint { get; set; }
public float Left { get; set; }
public float Top { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public float Angle { get; set; }
public float Speed { get; set; }
public Rectangle(Vector2 anchorPoint, float width, float height, float angle)
{
AnchorPoint = anchorPoint;
Width = width;
Height = height;
Angle = angle;
}
public Rectangle()
{
AnchorPoint = new Vector2(0,0);
Width = 1;
Height = 1;
Angle = 0;
} }
public Vector2 ApplyForce(Circle circle) private bool IsColliding(Circle circle1, Circle circle2)
{ {
// Transformar el centro del círculo al sistema de coordenadas del rectángulo float distance = Vector2.Distance(circle1.position, circle2.position);
Vector2 circlePositionRelative = Vector2.Transform(circle.Center - AnchorPoint, Matrix3x2.CreateRotation(-Angle)); float radiusSum = (circle1.Diameter / 2) + (circle2.Diameter / 2);
return distance <= radiusSum;
// Verificar si el círculo está dentro del rectángulo
float halfWidth = Width / 2;
float halfHeight = Height / 2;
if (Math.Abs(circlePositionRelative.X) <= halfWidth && Math.Abs(circlePositionRelative.Y) <= halfHeight)
{
// Calcular fuerza basada en la proximidad al centro del rectángulo
float distanceFromCenter = circlePositionRelative.Length();
float forceMagnitude = Math.Max(0, (Width - distanceFromCenter) / Width); // Simplificación de la magnitud de la fuerza
Vector2 forceDirection = Vector2.Transform(new Vector2(1, 0), Matrix3x2.CreateRotation(Angle)); // Fuerza en la dirección del rectángulo
return forceDirection * forceMagnitude;
}
return Vector2.Zero;
} }
} private bool IsCollidingWithLine(Circle circle, Line line)
// Definición de la clase Line
public class Line : ObjetoGeometrico
{
public Vector2 StartPoint { get; set; }
public Vector2 EndPoint { get; set; }
public Line(Vector2 startPoint, Vector2 endPoint)
{ {
StartPoint = startPoint; Vector2 nearestPoint = NearestPointOnLine(circle.position, line.start, line.end);
EndPoint = endPoint; float distanceToLine = Vector2.Distance(circle.position, nearestPoint);
return distanceToLine <= (circle.Diameter / 2);
} }
public void HandleCollision(Circle circle) private Vector2 NearestPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{ {
Vector2 closestPoint = ClosestPoint(circle.Center); Vector2 lineVector = lineEnd - lineStart;
Vector2 pointVector = point - lineStart;
float lineLength = lineVector.Length();
float projectedLength = Vector2.Dot(pointVector, lineVector) / lineLength;
projectedLength = Math.Max(0, Math.Min(lineLength, projectedLength)); // Clamping to the line segment
return lineStart + lineVector * (projectedLength / lineLength);
}
private float CalculateImpactAngle(Circle circle, Line line)
{
Vector2 movementDirection = new Vector2((float)Math.Cos(circle.Angle * Math.PI / 180), (float)Math.Sin(circle.Angle * Math.PI / 180));
Vector2 lineDirection = line.end - line.start;
Vector2 lineNormal = new Vector2(-lineDirection.Y, lineDirection.X); // Rotar 90 grados para obtener normal
lineNormal = Vector2.Normalize(lineNormal);
// Calcular ángulo entre el movimiento y la normal de la línea
float dotProduct = Vector2.Dot(movementDirection, lineNormal);
float angle = (float)Math.Acos(dotProduct) * (180 / (float)Math.PI); // Convertir de radianes a grados
// Ajustar para obtener el ángulo relativo correcto
return angle < 90 ? 90 - angle : angle - 90;
}
public float CalculateOverlapPercentage(Circle circle, Rectangle rectangle)
{
// Convertir ángulo del rectángulo de grados a radianes
float angleRadians = (float)(rectangle.Angle * Math.PI / 180);
// Centro del círculo
Vector2 circleCenter = new Vector2(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2);
float radius = circle.Diameter / 2; float radius = circle.Diameter / 2;
if (Vector2.Distance(circle.Center, closestPoint) < radius)
// Pivot del rectángulo es el Top Left
Vector2 rectPivot = new Vector2(rectangle.Left, rectangle.Top);
// Rotar el centro del círculo respecto al pivote del rectángulo
Vector2 rotatedCircleCenter = RotatePoint(circleCenter, rectPivot, -angleRadians);
// Comprobar si el círculo rotado intersecta con el rectángulo alineado
// Rectángulo "alineado" asume que después de rotar el círculo, el rectángulo se comporta como si estuviera alineado con los ejes
if (IsCircleRectangleIntersecting(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width))
{ {
Vector2 lineDirection = Vector2.Normalize(EndPoint - StartPoint); float overlapArea = EstimateOverlapArea(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width);
Vector2 normal = new Vector2(-lineDirection.Y, lineDirection.X); // Normal perpendicular a la dirección de la línea float circleArea = (float)(Math.PI * radius * radius);
circle.Velocity = Vector2.Reflect(circle.Velocity, normal); return (overlapArea / circleArea) * 100;
} }
return 0;
} }
private Vector2 ClosestPoint(Vector2 point) private bool IsCircleRectangleIntersecting(Vector2 circleCenter, float radius, Vector2 rectTopLeft, float length, float width)
{ {
Vector2 AP = point - StartPoint; float closestX = Math.Max(rectTopLeft.X, Math.Min(circleCenter.X, rectTopLeft.X + length));
Vector2 AB = EndPoint - StartPoint; float closestY = Math.Max(rectTopLeft.Y, Math.Min(circleCenter.Y, rectTopLeft.Y + width));
float magnitudeAB = AB.LengthSquared();
float ABAPproduct = Vector2.Dot(AP, AB); float distanceX = circleCenter.X - closestX;
float distance = ABAPproduct / magnitudeAB; float distanceY = circleCenter.Y - closestY;
return StartPoint + AB * Math.Clamp(distance, 0, 1);
return (distanceX * distanceX + distanceY * distanceY) < (radius * radius);
} }
private float EstimateOverlapArea(Vector2 circleCenter, float radius, Vector2 rectCenter, float length, float width)
{
// Esto es un placeholder: el cálculo real requiere un algoritmo geométrico complejo
// Puedes retornar una estimación basada en proporciones o usar una librería geométrica
return (float) (radius * radius * Math.PI * 0.25f); // Asumiendo un solapamiento del 25% como placeholder
}
private Vector2 RotatePoint(Vector2 point, Vector2 pivot, float angle)
{
float cosTheta = (float)Math.Cos(angle);
float sinTheta = (float)Math.Sin(angle);
// Ajustar punto por pivot antes de aplicar rotación
Vector2 translatedPoint = new Vector2(point.X - pivot.X, point.Y - pivot.Y);
// Rotar el punto
Vector2 rotatedPoint = new Vector2(
translatedPoint.X * cosTheta - translatedPoint.Y * sinTheta,
translatedPoint.X * sinTheta + translatedPoint.Y * cosTheta
);
// Traducir el punto de vuelta
return new Vector2(rotatedPoint.X + pivot.X, rotatedPoint.Y + pivot.Y);
}
} }
public class ObjetoGeometrico 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
{
public Vector2 start;
public Vector2 end;
public float Left
{
get { return start.X; }
set { start = new Vector2(value, start.Y); UpdateEnd(); }
}
public float Top
{
get { return start.Y; }
set { start = new Vector2(start.X, value); UpdateEnd(); }
}
public float Length { get; set; }
public float Width { get; set; }
public float Angle { get; set; } // En grados
public Line(float left, float top, float length, float width, float angle)
{
start = new Vector2(left, top);
Length = length;
Width = width;
Angle = angle;
UpdateEnd();
}
private void UpdateEnd()
{
// Asumiendo que la línea se extiende en el ángulo desde el punto de inicio
end = new Vector2(start.X + Length * (float)Math.Cos(Angle * Math.PI / 180),
start.Y + Length * (float)Math.Sin(Angle * Math.PI / 180));
}
}
// Clase principal que gestiona la simulación // Clase principal que gestiona la simulación
@ -181,7 +330,8 @@ public class SimulationManager
{ {
public List<Circle> circles; public List<Circle> circles;
public List<Rectangle> rectangles; public List<Rectangle> rectangles;
public List<Line> lines; public List<Line> lines;
public SimulationManager() public SimulationManager()
{ {
@ -197,6 +347,4 @@ public class SimulationManager
circle.Move(timeStep, circles, rectangles, lines); circle.Move(timeStep, circles, rectangles, lines);
} }
} }
} }