From ef7d3e26187769587271b969bdf3a969fcd476c8 Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 8 May 2024 08:00:31 +0200 Subject: [PATCH] Funcionando la simulacion pero con problemas con el calculo de overlap --- MainViewModel.cs | 32 ++- MainWindow.xaml.cs | 22 +- ObjetosSim/UserControlFactory.cs | 2 +- ObjetosSim/osBase.cs | 83 ++++-- ObjetosSim/ucBotella.xaml | 2 +- ObjetosSim/ucBotella.xaml.cs | 87 +++++- ObjetosSim/ucTransporteTTop.xaml | 2 +- ObjetosSim/ucTransporteTTop.xaml.cs | 86 +++++- Simulacion/GeometrySimulator.cs | 404 +++++++++++++++++++--------- 9 files changed, 532 insertions(+), 188 deletions(-) diff --git a/MainViewModel.cs b/MainViewModel.cs index fa45b4b..04b6e78 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -21,6 +21,7 @@ using System.Text.Json; using Newtonsoft.Json; using System.Windows.Data; using System.Windows; +using static System.Resources.ResXFileRef; namespace CtrEditor { @@ -56,7 +57,7 @@ namespace CtrEditor ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick); _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; StartSimulationCommand = new RelayCommand(StartSimulation); @@ -168,6 +169,7 @@ namespace CtrEditor { if (_selectedImage != value && value != null) { + StopSimulation(); SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen _selectedImage = value; ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); // Dispara el evento con la nueva ruta de imagen @@ -235,7 +237,14 @@ namespace CtrEditor { 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); } } @@ -258,11 +267,16 @@ namespace CtrEditor PreserveReferencesHandling = PreserveReferencesHandling.Objects, ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor }; - if (jsonString != null) - { - ObjetosSimulables = JsonConvert.DeserializeObject>(jsonString, settings); - // Ahora recorres la colección de objetos simulables + var simulationData = JsonConvert.DeserializeObject(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) CrearUsercontrol(objetoSimulable); } @@ -272,6 +286,7 @@ namespace CtrEditor catch { /* Consider logging the error or handling it appropriately */ } } + private bool CrearUsercontrol(osBase osObjeto) { Type tipoObjeto = osObjeto.GetType(); @@ -303,6 +318,11 @@ namespace CtrEditor } + public class SimulationData + { + public ObservableCollection ObjetosSimulables { get; set; } + public UnitConverter UnitConverter { get; set; } + } public class TipoSimulable { diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 3e55816..05fabc7 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -22,6 +22,7 @@ namespace CtrEditor /// /// Interaction logic for MainWindow.xaml /// + /// public partial class MainWindow : Window { // Para el Canvas @@ -85,19 +86,21 @@ namespace CtrEditor double centerY = offsetY + (visibleHeight / scaleY) / 2; // Ajusta la posición del UserControl para que esté centrado en el área visible - double left = centerX - (userControl.ActualWidth / 2); - double top = centerY - (userControl.ActualHeight / 2); + double leftPixels = centerX - (userControl.ActualWidth / 2); + double topPixels = centerY - (userControl.ActualHeight / 2); // Establece la posición del UserControl - NuevoOS.Left = (float)left; - NuevoOS.Top = (float)top; + NuevoOS.LeftPixels = (float)leftPixels; + NuevoOS.TopPixels = (float)topPixels; NuevoOS.Inicializado = true; } - - // Establece la posición del UserControl - Canvas.SetLeft(userControl, NuevoOS.Left); - Canvas.SetTop(userControl, NuevoOS.Top); + else + { + // Fuerza a Establecer la posición del UserControl + NuevoOS.LeftPixels = NuevoOS.LeftPixels; + NuevoOS.TopPixels = NuevoOS.TopPixels; + } // Suscribirse a eventos de mouse para marcar el Control userControl.MouseEnter += UserControl_MouseEnter; @@ -109,7 +112,8 @@ namespace CtrEditor userControl.MouseMove += UserControl_MouseMove; // Añade el UserControl al Canvas - ImagenEnTrabajoCanvas.Children.Add(userControl); + Canvas.SetZIndex(userControl, dataContainer.ZIndex()); + ImagenEnTrabajoCanvas.Children.Add(userControl); } } diff --git a/ObjetosSim/UserControlFactory.cs b/ObjetosSim/UserControlFactory.cs index 3087298..073dbcc 100644 --- a/ObjetosSim/UserControlFactory.cs +++ b/ObjetosSim/UserControlFactory.cs @@ -52,7 +52,7 @@ namespace CtrEditor.ObjetosSim dataContainer.Datos = datos; userControl.DataContext = datos; datos.VisualRepresentation = userControl; - datos.ConnectSimManager(simulationManager); + datos.ConnectSimManager(simulationManager); } } } diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index acdb5b4..8676782 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -26,34 +26,24 @@ namespace CtrEditor.ObjetosSim void Move(float Left, float Top); void Rotate(float Angle); void Highlight(bool State); + int ZIndex(); } 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 Top { get; set; } public bool Inicializado = false; - + protected UserControl? _visualRepresentation = null; - public string Nombre - { - get => _nombre; - set - { - if (_nombre != value) - { - _nombre = value; - OnPropertyChanged(nameof(Nombre)); - } - } - } + public abstract string Nombre { get; set; } public abstract void ConnectSimManager(SimulationManager simulationManager); - public abstract void UpdateControl(); - + public abstract void UpdateControl(); [JsonIgnore] public UserControl? VisualRepresentation @@ -61,6 +51,7 @@ namespace CtrEditor.ObjetosSim get => _visualRepresentation; set => _visualRepresentation = value; } + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) @@ -68,4 +59,62 @@ namespace CtrEditor.ObjetosSim 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; + } + } + + } diff --git a/ObjetosSim/ucBotella.xaml b/ObjetosSim/ucBotella.xaml index 89e0253..8894b38 100644 --- a/ObjetosSim/ucBotella.xaml +++ b/ObjetosSim/ucBotella.xaml @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/ObjetosSim/ucBotella.xaml.cs b/ObjetosSim/ucBotella.xaml.cs index 6ef5f54..19a4383 100644 --- a/ObjetosSim/ucBotella.xaml.cs +++ b/ObjetosSim/ucBotella.xaml.cs @@ -35,8 +35,20 @@ namespace CtrEditor.ObjetosSim { Data.Diameter = value; 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 { get => Data.Mass; 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 { - Data.Center = new Vector2(value,Top); + 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 + { + get => Data.Left; + set + { + Data.Left = value; + if (_visualRepresentation != null) + Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value)); + OnPropertyChanged(nameof(LeftPixels)); OnPropertyChanged(nameof(Left)); } } public override float Top { - get => Data.Center.Y; + get => Data.Top; set { - Data.Center = new Vector2(Left, value); + Data.Top = value; if (_visualRepresentation != null) - Canvas.SetTop(_visualRepresentation, value); + Canvas.SetTop(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value)); + OnPropertyChanged(nameof(TopPixels)); 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) { - simulationManager.circles.Add(Data); + simulationManager.circles.Add(Data); } public override void UpdateControl() { - Top = Data.Center.Y; - Left = Data.Center.X; + Top = Data.Top; + Left = Data.Left; } - } public partial class ucBotella : UserControl, IDataContainer @@ -90,16 +146,19 @@ namespace CtrEditor.ObjetosSim InitializeComponent(); } public void Resize(float width, float height) { } - public void Move(float Left, float Top) + public void Move(float LeftPixels, float TopPixels) { if (Datos != null) { - Datos.Left = Left; - Datos.Top = Top; + Datos.LeftPixels = LeftPixels; + Datos.TopPixels = TopPixels; } } public void Rotate(float Angle) { } public void Highlight(bool State) { } - + public int ZIndex() + { + return 10; + } } } diff --git a/ObjetosSim/ucTransporteTTop.xaml b/ObjetosSim/ucTransporteTTop.xaml index 773720f..5bb63eb 100644 --- a/ObjetosSim/ucTransporteTTop.xaml +++ b/ObjetosSim/ucTransporteTTop.xaml @@ -6,7 +6,7 @@ xmlns:local="clr-namespace:CtrEditor" mc:Ignorable="d"> - + diff --git a/ObjetosSim/ucTransporteTTop.xaml.cs b/ObjetosSim/ucTransporteTTop.xaml.cs index bc81b26..ac0a356 100644 --- a/ObjetosSim/ucTransporteTTop.xaml.cs +++ b/ObjetosSim/ucTransporteTTop.xaml.cs @@ -34,6 +34,30 @@ namespace CtrEditor.ObjetosSim 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 { get => Data.Left; @@ -41,7 +65,8 @@ namespace CtrEditor.ObjetosSim { Data.Left = value; if (_visualRepresentation != null) - Canvas.SetLeft(_visualRepresentation, value); + Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value)); + OnPropertyChanged(nameof(LeftPixels)); OnPropertyChanged(nameof(Left)); } } @@ -52,27 +77,48 @@ namespace CtrEditor.ObjetosSim { Data.Top = value; if (_visualRepresentation != null) - Canvas.SetTop(_visualRepresentation, value); + Canvas.SetTop(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value)); + OnPropertyChanged(nameof(TopPixels)); OnPropertyChanged(nameof(Top)); } } + public float Ancho { - get => Data.Width; + get => Data.Length; set { - Data.Width = value; + Data.Length = value; OnPropertyChanged(nameof(Ancho)); } } public float Alto { - get => Data.Height; + get => Data.Width; set { - Data.Height = value; + Data.Width = value; 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 { 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 VelMax50hz { get => velMax50hz; set => velMax50hz = value; } public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; } @@ -98,8 +157,8 @@ namespace CtrEditor.ObjetosSim public osTransporteTTop() { - Ancho = 100; - Alto = 10; + AnchoPixels = 100; + AltoPixels = 10; } public override void ConnectSimManager(SimulationManager simulationManager) @@ -125,12 +184,12 @@ namespace CtrEditor.ObjetosSim if (Datos is osTransporteTTop datos) datos.Ancho = width; } - public void Move(float Left, float Top) + public void Move(float LeftPixels, float TopPixels) { if (Datos != null) { - Datos.Left = Left; - Datos.Top = Top; + Datos.LeftPixels = LeftPixels; + Datos.TopPixels = TopPixels; } } public void Rotate(float Angle) { @@ -139,6 +198,11 @@ namespace CtrEditor.ObjetosSim datos.Angulo = Angle; } public void Highlight(bool State) { } + public int ZIndex() + { + return 1; + } + } } diff --git a/Simulacion/GeometrySimulator.cs b/Simulacion/GeometrySimulator.cs index ace49c3..0c7d866 100644 --- a/Simulacion/GeometrySimulator.cs +++ b/Simulacion/GeometrySimulator.cs @@ -1,179 +1,328 @@ -using Newtonsoft.Json; +using CtrEditor.ObjetosSim; using System; using System.Collections.Generic; +using System.Diagnostics.Eventing.Reader; using System.Numerics; +using System.Windows.Shapes; -// Definición de la clase Circle -public class Circle : ObjetoGeometrico +public class Circle { - 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 Mass { get; set; } - [JsonIgnore] - public Vector2 Velocity { get; set; } + public float Angle { get; set; } // En grados + 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; Mass = mass; - Velocity = velocity; - } - public Circle() { - Center = new Vector2(0,0); - Center.X = 0; - Diameter = 10; - Mass = 1; - Velocity = new Vector2(0,0); + Angle = angle; + Speed = speed; } - // Método para calcular la nueva posición del círculo en función del tiempo - public void Move(float timeStep, List circles, List rectangles, List lines) + public void Move(float timeStep_ms, List circles, List rectangles, List 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) { - totalForce += rectangle.ApplyForce(this); - } - - // Aplicar fuerza a otros círculos - foreach (var otherCircle in circles) - { - if (otherCircle != this) + float overlap = CalculateOverlapPercentage(this, rectangle); + if (overlap > 0) { - 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) - Vector2 inertiaForce = -0.1f * Velocity; // coeficiente de inercia - totalForce += inertiaForce; + // 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 la aceleración - if (Mass <= 0) - Mass = 1; - Vector2 acceleration = totalForce / Mass; + //// Interacción por impacto con otros círculos + //foreach (var other in circles) + //{ + // 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 - Velocity += acceleration * timeStep / 1000; - Center += Velocity * timeStep; + // Ajustar por superposición con otros círculos + foreach (var other in circles) + { + 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) { - 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); - float transferVelocity = 0.5f * this.Velocity.Length(); // Ejemplo de transferencia de parte de la velocidad - if (transferVelocity == 0) - transferVelocity = 0.01f; - other.Velocity += forceDirection * transferVelocity; // Asumiendo una simplificación del impacto - return -forceDirection * transferVelocity; // Retorno de fuerza opuesta aplicada a este círculo + 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); + } } - 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 - Vector2 circlePositionRelative = Vector2.Transform(circle.Center - AnchorPoint, Matrix3x2.CreateRotation(-Angle)); - - // 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; + float distance = Vector2.Distance(circle1.position, circle2.position); + float radiusSum = (circle1.Diameter / 2) + (circle2.Diameter / 2); + return distance <= radiusSum; } -} - -// 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) + private bool IsCollidingWithLine(Circle circle, Line line) { - StartPoint = startPoint; - EndPoint = endPoint; + Vector2 nearestPoint = NearestPointOnLine(circle.position, line.start, line.end); + 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; - 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); - Vector2 normal = new Vector2(-lineDirection.Y, lineDirection.X); // Normal perpendicular a la dirección de la línea - circle.Velocity = Vector2.Reflect(circle.Velocity, normal); + float overlapArea = EstimateOverlapArea(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width); + float circleArea = (float)(Math.PI * radius * radius); + 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; - Vector2 AB = EndPoint - StartPoint; - float magnitudeAB = AB.LengthSquared(); - float ABAPproduct = Vector2.Dot(AP, AB); - float distance = ABAPproduct / magnitudeAB; - return StartPoint + AB * Math.Clamp(distance, 0, 1); + float closestX = Math.Max(rectTopLeft.X, Math.Min(circleCenter.X, rectTopLeft.X + length)); + float closestY = Math.Max(rectTopLeft.Y, Math.Min(circleCenter.Y, rectTopLeft.Y + width)); + + float distanceX = circleCenter.X - closestX; + float distanceY = circleCenter.Y - closestY; + + 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 @@ -181,7 +330,8 @@ public class SimulationManager { public List circles; public List rectangles; - public List lines; + public List lines; + public SimulationManager() { @@ -197,6 +347,4 @@ public class SimulationManager circle.Move(timeStep, circles, rectangles, lines); } } -} - - +} \ No newline at end of file