From 3ba10b81c308223d196db1cf7326193ea8dda7bb Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 6 May 2024 17:31:45 +0200 Subject: [PATCH] Primera logica de simulacion --- MainViewModel.cs | 20 ++- MainWindow.xaml.cs | 26 +--- ObjetosSim/UserControlFactory.cs | 3 +- ObjetosSim/osBase.cs | 44 ++---- ObjetosSim/ucBotella.xaml | 6 +- ObjetosSim/ucBotella.xaml.cs | 63 ++++++++- ObjetosSim/ucTransporteTTop.xaml.cs | 80 ++++++++--- Simulacion/GeometrySimulator.cs | 202 ++++++++++++++++++++++++++++ 8 files changed, 355 insertions(+), 89 deletions(-) create mode 100644 Simulacion/GeometrySimulator.cs diff --git a/MainViewModel.cs b/MainViewModel.cs index aaa6b64..fa45b4b 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -32,6 +32,8 @@ namespace CtrEditor public ObservableCollection ListaOsBase { get; } = new ObservableCollection(); private ObservableCollection _objetosSimulables = new ObservableCollection(); + private SimulationManager simulationManager = new SimulationManager(); + private readonly DispatcherTimer _timerSimulacion; public ICommand StartSimulationCommand { get; } @@ -125,14 +127,18 @@ namespace CtrEditor private void OnTickSimulacion(object sender, EventArgs e) { - var args = new TickSimulacionEventArgs(); - OnTickSimulacion(args); + + simulationManager.Step((float)_timerSimulacion.Interval.TotalMilliseconds); + + foreach (var objetoSimulable in ObjetosSimulables) + objetoSimulable.UpdateControl(); + } - protected virtual void OnTickSimulacion(TickSimulacionEventArgs e) - { - TickSimulacion?.Invoke(this, e); - } + //protected virtual void OnTickSimulacion(TickSimulacionEventArgs e) + //{ + // TickSimulacion?.Invoke(this, e); + //} public string directorioTrabajo { @@ -276,7 +282,7 @@ namespace CtrEditor if (userControl != null) { // Asignar los datos al UserControl - UserControlFactory.AssignDatos(userControl, osObjeto); + UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager); OnUserControlSelected?.Invoke(userControl); return true; diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index b102b1b..3e55816 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -54,7 +54,7 @@ namespace CtrEditor if (DataContext is MainViewModel viewModel) { viewModel.ImageSelected += ViewModel_ImageSelected; - viewModel.TickSimulacion += MainViewModel_TickSimulacion; + //viewModel.TickSimulacion += MainViewModel_TickSimulacion; viewModel.OnUserControlSelected += AgregarUserControl; viewModel?.LoadInitialData(); // Carga la primera imagen por defecto una vez cargada la ventana principal } @@ -89,8 +89,8 @@ namespace CtrEditor double top = centerY - (userControl.ActualHeight / 2); // Establece la posición del UserControl - NuevoOS.Left = left; - NuevoOS.Top = top; + NuevoOS.Left = (float)left; + NuevoOS.Top = (float)top; NuevoOS.Inicializado = true; } @@ -215,7 +215,7 @@ namespace CtrEditor var newY = Canvas.GetTop(_currentDraggingControl) + dy; if (_currentDraggingControl is IDataContainer dataContainer) - dataContainer.Move(newX, newY); + dataContainer.Move((float)newX,(float) newY); _startPointUserControl = currentPosition; // Actualiza el punto inicial para el siguiente movimiento } @@ -243,7 +243,7 @@ namespace CtrEditor //rotateTransform.Angle = angle; // - _initialAngleUserControl; // Asegúrate de ajustar esta parte según cómo calcules el ángulo inicial if (control is IDataContainer dataContainer) - dataContainer.Rotate(angle); + dataContainer.Rotate((float)angle); // Actualizar el ángulo mostrado _angleDisplayTextBlock.Text = $"Ángulo: {angle:F2}°"; @@ -267,7 +267,7 @@ namespace CtrEditor control.Width = newWidth; // Asegurar que no sea menor que el mínimo if (control is IDataContainer dataContainer) - dataContainer.Resize(newWidth, 0); + dataContainer.Resize((float)newWidth, 0); // Actualizar el punto de inicio para el próximo evento de movimiento _startPointUserControl = currentPosition; @@ -423,20 +423,6 @@ namespace CtrEditor } - private void MainViewModel_TickSimulacion(object sender, TickSimulacionEventArgs e) - { - // aquí puedes agregar la lógica para actualizar tus UserControl - // en el ImagenEnTrabajoCanvas - foreach (var child in ImagenEnTrabajoCanvas.Children) - { - if (child is osBase uc) - { - // llama al método Update de cada UserControl - uc.Update(); - } - } - } - private void MainWindow_Closed(object sender, EventArgs e) { if (DataContext is MainViewModel viewModel) diff --git a/ObjetosSim/UserControlFactory.cs b/ObjetosSim/UserControlFactory.cs index 0241e50..3087298 100644 --- a/ObjetosSim/UserControlFactory.cs +++ b/ObjetosSim/UserControlFactory.cs @@ -45,13 +45,14 @@ namespace CtrEditor.ObjetosSim return instance; } - public static void AssignDatos(UserControl userControl, osBase datos) + public static void AssignDatos(UserControl userControl, osBase datos, SimulationManager simulationManager) { if (userControl is IDataContainer dataContainer) { dataContainer.Datos = datos; userControl.DataContext = datos; datos.VisualRepresentation = userControl; + datos.ConnectSimManager(simulationManager); } } } diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index 2d087be..acdb5b4 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -14,52 +14,29 @@ namespace CtrEditor.ObjetosSim public interface IosBase { string Nombre { get; } - void Update(); + + void ConnectSimManager(SimulationManager simulationManager); + void UpdateControl(); } public interface IDataContainer { osBase? Datos { get; set; } - void Resize(double width, double height); - void Move(double Left, double Top); - void Rotate(double Angle); + void Resize(float width, float height); + void Move(float Left, float Top); + void Rotate(float Angle); void Highlight(bool State); } public abstract class osBase : INotifyPropertyChanged, IosBase { private string _nombre = "Base"; - public double _left; - public double _top; + public abstract float Left { get; set; } + public abstract float Top { get; set; } public bool Inicializado = false; - public double Left - { - get => _left; - set - { - _left = value; - if (_visualRepresentation != null) - Canvas.SetLeft(_visualRepresentation, _left); - OnPropertyChanged(nameof(Left)); - } - } - public double Top - { - get => _top; - - set - { - _top = value; - if (_visualRepresentation != null) - Canvas.SetTop(_visualRepresentation, _top); - - OnPropertyChanged(nameof(Top)); - } - } - - private UserControl? _visualRepresentation = null; + protected UserControl? _visualRepresentation = null; public string Nombre { @@ -74,7 +51,8 @@ namespace CtrEditor.ObjetosSim } } - public abstract void Update(); + public abstract void ConnectSimManager(SimulationManager simulationManager); + public abstract void UpdateControl(); [JsonIgnore] diff --git a/ObjetosSim/ucBotella.xaml b/ObjetosSim/ucBotella.xaml index 3d043f8..89e0253 100644 --- a/ObjetosSim/ucBotella.xaml +++ b/ObjetosSim/ucBotella.xaml @@ -1,6 +1,6 @@  - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + \ No newline at end of file diff --git a/ObjetosSim/ucBotella.xaml.cs b/ObjetosSim/ucBotella.xaml.cs index fcd191d..6ef5f54 100644 --- a/ObjetosSim/ucBotella.xaml.cs +++ b/ObjetosSim/ucBotella.xaml.cs @@ -12,6 +12,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using System.Numerics; namespace CtrEditor.ObjetosSim { @@ -22,14 +23,62 @@ namespace CtrEditor.ObjetosSim public class osBotella : osBase { - public double diametro { get; set; } + private Circle Data = new Circle(); + // Otros datos y métodos relevantes para la simulación private string _nombre = "Botella"; - public override void Update() - { - // implementation of Update method + + public float Diametro { + get => Data.Diameter; + set + { + Data.Diameter = value; + OnPropertyChanged(nameof(Diametro)); + } } + public float Mass { + get => Data.Mass; + set + { + Data.Mass = value; + OnPropertyChanged(nameof(Mass)); + } + } + + public override float Left + { + get => Data.Center.X; + set + { + Data.Center = new Vector2(value,Top); + if (_visualRepresentation != null) + Canvas.SetLeft(_visualRepresentation, value); + OnPropertyChanged(nameof(Left)); + } + } + public override float Top + { + get => Data.Center.Y; + set + { + Data.Center = new Vector2(Left, value); + if (_visualRepresentation != null) + Canvas.SetTop(_visualRepresentation, value); + OnPropertyChanged(nameof(Top)); + } + } + public override void ConnectSimManager(SimulationManager simulationManager) + { + simulationManager.circles.Add(Data); + } + + public override void UpdateControl() + { + Top = Data.Center.Y; + Left = Data.Center.X; + } + } public partial class ucBotella : UserControl, IDataContainer @@ -40,8 +89,8 @@ namespace CtrEditor.ObjetosSim { InitializeComponent(); } - public void Resize(double width, double height) { } - public void Move(double Left, double Top) + public void Resize(float width, float height) { } + public void Move(float Left, float Top) { if (Datos != null) { @@ -49,7 +98,7 @@ namespace CtrEditor.ObjetosSim Datos.Top = Top; } } - public void Rotate(double Angle) { } + public void Rotate(float Angle) { } public void Highlight(bool State) { } } diff --git a/ObjetosSim/ucTransporteTTop.xaml.cs b/ObjetosSim/ucTransporteTTop.xaml.cs index 86dfd4e..bc81b26 100644 --- a/ObjetosSim/ucTransporteTTop.xaml.cs +++ b/ObjetosSim/ucTransporteTTop.xaml.cs @@ -13,6 +13,8 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using static System.Runtime.InteropServices.JavaScript.JSType; +using System.Numerics; +using System.Windows.Markup; namespace CtrEditor.ObjetosSim { @@ -25,36 +27,74 @@ namespace CtrEditor.ObjetosSim { private string _nombre = "Transporte TTOP"; - private double _Ancho; - private double _Alto; - private double _Angulo; + private float frictionCoefficient; + private float velMax50hz; // en metros por minuto + private float tiempoRampa; + private bool esMarcha; - public double diametro { get; set; } - public double Ancho { - get { return _Ancho; } + private Rectangle Data = new Rectangle(); + + public override float Left + { + get => Data.Left; set { - _Ancho = value; + Data.Left = value; + if (_visualRepresentation != null) + Canvas.SetLeft(_visualRepresentation, value); + OnPropertyChanged(nameof(Left)); + } + } + public override float Top + { + get => Data.Top; + set + { + Data.Top = value; + if (_visualRepresentation != null) + Canvas.SetTop(_visualRepresentation, value); + OnPropertyChanged(nameof(Top)); + } + } + + public float Ancho { + get => Data.Width; + set + { + Data.Width = value; OnPropertyChanged(nameof(Ancho)); } } - public double Alto { - get { return _Alto; } + public float Alto { + get => Data.Height; set { - _Alto = value; + Data.Height = value; OnPropertyChanged(nameof(Alto)); } } - public double Angulo + public float Angulo { - get { return _Angulo; } + get => Data.Angle; set { - _Angulo = value; + Data.Angle = value; OnPropertyChanged(nameof(Angulo)); } } + public float VelocidadActual + { + get => Data.Speed; + set { + Data.Speed = value; + OnPropertyChanged(nameof(VelocidadActual)); + } + } + + public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; } + public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; } + public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; } + public bool EsMarcha { get => esMarcha; set => esMarcha = value; } public osTransporteTTop() { @@ -62,10 +102,14 @@ namespace CtrEditor.ObjetosSim Alto = 10; } - public override void Update() + public override void ConnectSimManager(SimulationManager simulationManager) { - // implementation of Update method + simulationManager.rectangles.Add(Data); } + public override void UpdateControl() + { + } + } public partial class ucTransporteTTop : UserControl, IDataContainer @@ -76,12 +120,12 @@ namespace CtrEditor.ObjetosSim { InitializeComponent(); } - public void Resize(double width, double height) + public void Resize(float width, float height) { if (Datos is osTransporteTTop datos) datos.Ancho = width; } - public void Move(double Left, double Top) + public void Move(float Left, float Top) { if (Datos != null) { @@ -89,7 +133,7 @@ namespace CtrEditor.ObjetosSim Datos.Top = Top; } } - public void Rotate(double Angle) { + public void Rotate(float Angle) { if (Datos != null) if (Datos is osTransporteTTop datos) datos.Angulo = Angle; diff --git a/Simulacion/GeometrySimulator.cs b/Simulacion/GeometrySimulator.cs new file mode 100644 index 0000000..ace49c3 --- /dev/null +++ b/Simulacion/GeometrySimulator.cs @@ -0,0 +1,202 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Numerics; + +// Definición de la clase Circle +public class Circle : ObjetoGeometrico +{ + public Vector2 Center { get; set; } + public float Diameter { get; set; } + public float Mass { get; set; } + [JsonIgnore] + public Vector2 Velocity { get; set; } + + public Circle(Vector2 center, float diameter, float mass, Vector2 velocity) + { + Center = center; + 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); + } + + // 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) + { + Vector2 totalForce = new Vector2(0, 0); + + // Aplicar fuerza de los rectángulos + foreach (var rectangle in rectangles) + { + totalForce += rectangle.ApplyForce(this); + } + + // Aplicar fuerza a otros círculos + foreach (var otherCircle in circles) + { + if (otherCircle != this) + { + totalForce += ApplyForceToOtherCircle(otherCircle); + } + } + + // Aplicar fuerza debido a la inercia (ejemplo simple) + Vector2 inertiaForce = -0.1f * Velocity; // coeficiente de inercia + totalForce += inertiaForce; + + // Calcular la aceleración + if (Mass <= 0) + Mass = 1; + Vector2 acceleration = totalForce / Mass; + + // Actualizar la velocidad y la posición + Velocity += acceleration * timeStep / 1000; + Center += Velocity * timeStep; + + // Controlar la colisión con las líneas + foreach (var line in lines) + { + line.HandleCollision(this); + } + } + + 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) + { + 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 + } + 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) + { + // 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; + } + +} + +// 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; + EndPoint = endPoint; + } + + public void HandleCollision(Circle circle) + { + Vector2 closestPoint = ClosestPoint(circle.Center); + float radius = circle.Diameter / 2; + if (Vector2.Distance(circle.Center, closestPoint) < radius) + { + 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); + } + } + + + private Vector2 ClosestPoint(Vector2 point) + { + 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); + } +} + +public class ObjetoGeometrico +{ } + + +// Clase principal que gestiona la simulación +public class SimulationManager +{ + public List circles; + public List rectangles; + public List lines; + + public SimulationManager() + { + circles = new List(); + rectangles = new List(); + lines = new List(); + } + + public void Step(float timeStep) + { + foreach (var circle in circles) + { + circle.Move(timeStep, circles, rectangles, lines); + } + } +} + +