From 56a2e994a2be7348c84a11921e5031923ce0156c Mon Sep 17 00:00:00 2001 From: Miguel Date: Tue, 14 May 2024 08:15:54 +0200 Subject: [PATCH] Primera revision. trabajando en la aplicacion de velocidad sobre los transportes. --- CtrEditor.csproj | 16 ++ MainViewModel.cs | 16 +- MainWindow.xaml.cs | 11 +- ObjetosSim/UserControlFactory.cs | 13 +- ObjetosSim/osBase.cs | 101 ++++++- ObjetosSim/ucBotella.xaml.cs | 103 ++++--- ObjetosSim/ucGuia.xaml | 2 +- ObjetosSim/ucGuia.xaml.cs | 60 +++-- ObjetosSim/ucTransporteGuias.xaml.cs | 146 +++++----- ObjetosSim/ucTransporteTTop.xaml | 2 +- ObjetosSim/ucTransporteTTop.xaml.cs | 93 ++++--- ObjetosSim/ucVMmotorSim.xaml.cs | 15 +- Simulacion/FPhysics.cs | 387 +++++++++++++++++++++++++++ 13 files changed, 781 insertions(+), 184 deletions(-) create mode 100644 Simulacion/FPhysics.cs diff --git a/CtrEditor.csproj b/CtrEditor.csproj index 17f0cd1..121c8c3 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -8,11 +8,27 @@ true + + + + + + + + + + + + + + + + diff --git a/MainViewModel.cs b/MainViewModel.cs index edd1f03..b09dfca 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -24,6 +24,7 @@ using System.Windows.Data; using System.Windows; using static System.Resources.ResXFileRef; using CtrEditor.Convertidores; +using CtrEditor.Simulacion; namespace CtrEditor { @@ -36,7 +37,7 @@ namespace CtrEditor private ObservableCollection _objetosSimulables = new ObservableCollection(); public PLCViewModel _plcViewModelData; - private SimulationManager simulationManager = new SimulationManager(); + public SimulationManagerFP simulationManager = new SimulationManagerFP(); private readonly DispatcherTimer _timerSimulacion; @@ -63,7 +64,7 @@ namespace CtrEditor ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick); _timerSimulacion = new DispatcherTimer(); - _timerSimulacion.Interval = TimeSpan.FromMilliseconds(20); // ajusta el intervalo según sea necesario + _timerSimulacion.Interval = TimeSpan.FromMilliseconds(16); // ajusta el intervalo según sea necesario _timerSimulacion.Tick += OnTickSimulacion; StartSimulationCommand = new RelayCommand(StartSimulation); @@ -123,6 +124,11 @@ namespace CtrEditor private void StartSimulation() { + foreach (var objetoSimulable in ObjetosSimulables) + objetoSimulable.UpdateGeometryStart(); + + simulationManager.Debug_DrawInitialBodies(); + _timerSimulacion.Start(); } @@ -138,7 +144,7 @@ namespace CtrEditor { if (_plcViewModelData.IsConnected) objetoSimulable.UpdatePLC(_plcViewModelData.PLCInterface); - objetoSimulable.UpdateGeometry(); + objetoSimulable.UpdateGeometryStep(); } simulationManager.Step((float)_timerSimulacion.Interval.TotalMilliseconds); @@ -259,6 +265,7 @@ namespace CtrEditor foreach (var obj in ObjetosSimulables) { obj.VisualRepresentation = null; + obj.simulationManager = null; } // Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter var dataToSerialize = new SimulationData @@ -328,7 +335,8 @@ namespace CtrEditor // Asignar los datos al UserControl UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager); - OnUserControlSelected?.Invoke(userControl); + OnUserControlSelected?.Invoke(userControl); + return true; } return false; diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 322df05..06d69fe 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -19,6 +19,7 @@ using Label = System.Windows.Controls.Label; using MouseEventArgs = System.Windows.Input.MouseEventArgs; using TextBox = System.Windows.Controls.TextBox; using UserControl = System.Windows.Controls.UserControl; +//using OpenCvSharp; namespace CtrEditor @@ -62,6 +63,7 @@ namespace CtrEditor //viewModel.TickSimulacion += MainViewModel_TickSimulacion; viewModel.OnUserControlSelected += AgregarUserControl; viewModel?.LoadInitialData(); // Carga la primera imagen por defecto una vez cargada la ventana principal + viewModel.simulationManager.DebugCanvas = ImagenEnTrabajoCanvas; } } @@ -72,6 +74,7 @@ namespace CtrEditor var NuevoOS = dataContainer.Datos; if (!NuevoOS.Inicializado) // Aun no fue inicializado { + Random rnd = new Random(); // Obtiene el factor de escala var scaleTransform = ImagenEnTrabajoCanvas.LayoutTransform as ScaleTransform; double scaleX = scaleTransform?.ScaleX ?? 1.0; @@ -94,8 +97,8 @@ namespace CtrEditor double topPixels = centerY - (userControl.ActualHeight / 2); // Establece la posición del UserControl - NuevoOS.Left = PixelToMeter.Instance.calc.PixelsToMeters((float)leftPixels); - NuevoOS.Top = PixelToMeter.Instance.calc.PixelsToMeters((float)topPixels); + NuevoOS.Left = PixelToMeter.Instance.calc.PixelsToMeters((float)leftPixels + (float)(rnd.NextDouble() - 0.5) ); + NuevoOS.Top = PixelToMeter.Instance.calc.PixelsToMeters((float)topPixels + (float)(rnd.NextDouble() - 0.5) ); NuevoOS.Inicializado = true; } @@ -117,7 +120,7 @@ namespace CtrEditor // Añade el UserControl al Canvas Canvas.SetZIndex(userControl, dataContainer.ZIndex()); - ImagenEnTrabajoCanvas.Children.Add(userControl); + ImagenEnTrabajoCanvas.Children.Add(userControl); } } @@ -423,7 +426,7 @@ namespace CtrEditor Source = selectedObject, Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.LostFocus, // Actualizar solo al perder el foco - Converter = (FloatToFormattedStringConverter)Resources["floatFormatter"] // Usar el convertidor + //Converter = (FloatToFormattedStringConverter)Resources["floatFormatter"] // Usar el convertidor }; // Aplicar el convertidor solo a propiedades float diff --git a/ObjetosSim/UserControlFactory.cs b/ObjetosSim/UserControlFactory.cs index bdf0bf0..5b9597c 100644 --- a/ObjetosSim/UserControlFactory.cs +++ b/ObjetosSim/UserControlFactory.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; +using CtrEditor.Simulacion; namespace CtrEditor.ObjetosSim { @@ -20,8 +21,8 @@ namespace CtrEditor.ObjetosSim return new ucGuia(); if (tipoObjeto == typeof(osTransporteGuias)) return new ucTransporteGuias(); - if (tipoObjeto == typeof(osTransporteCurva)) - return new ucTransporteCurva(); + //if (tipoObjeto == typeof(osTransporteCurva)) + // return new ucTransporteCurva(); if (tipoObjeto == typeof(osVMmotorSim )) return new ucVMmotorSim(); @@ -40,8 +41,8 @@ namespace CtrEditor.ObjetosSim return new osGuia(); if (tipoObjeto == typeof(osTransporteGuias)) return new osTransporteGuias(); - if (tipoObjeto == typeof(osTransporteCurva)) - return new osTransporteCurva(); + //if (tipoObjeto == typeof(osTransporteCurva)) + // return new osTransporteCurva(); if (tipoObjeto == typeof(osVMmotorSim)) return new osVMmotorSim(); @@ -62,14 +63,14 @@ namespace CtrEditor.ObjetosSim return instance; } - public static void AssignDatos(UserControl userControl, osBase datos, SimulationManager simulationManager) + public static void AssignDatos(UserControl userControl, osBase datos, SimulationManagerFP simulationManager) { if (userControl is IDataContainer dataContainer) { dataContainer.Datos = datos; userControl.DataContext = datos; datos.VisualRepresentation = userControl; - datos.ConnectSimManager(simulationManager); + datos.simulationManager = simulationManager; } } } diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index 37a3a68..bd35f5c 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -13,6 +13,10 @@ using System.Windows.Data; using static System.Runtime.InteropServices.JavaScript.JSType; using CtrEditor.Convertidores; using CtrEditor.Siemens; +using CtrEditor.Simulacion; +using System.Windows.Media; +using Microsoft.Xna.Framework; +using FarseerPhysics.Dynamics; namespace CtrEditor.ObjetosSim { @@ -23,7 +27,6 @@ namespace CtrEditor.ObjetosSim { string Nombre { get; } - void ConnectSimManager(SimulationManager simulationManager); void UpdateControl(); } @@ -48,10 +51,11 @@ namespace CtrEditor.ObjetosSim public abstract string Nombre { get; set; } - public abstract void ConnectSimManager(SimulationManager simulationManager); public abstract void UpdateControl(); - public abstract void UpdateGeometry(); + public abstract void UpdateGeometryStart(); + public abstract void UpdateGeometryStep(); public abstract void UpdatePLC(PLCModel plc); + public abstract void ucLoaded(); [JsonIgnore] public UserControl? VisualRepresentation @@ -59,6 +63,9 @@ namespace CtrEditor.ObjetosSim get => _visualRepresentation; set => _visualRepresentation = value; } + [JsonIgnore] + public SimulationManagerFP simulationManager; + public void CanvasSetLeftinMeter(float left) { @@ -84,6 +91,91 @@ namespace CtrEditor.ObjetosSim else return 0f; } + public (Vector2 TopLeft, Vector2 BottomRight) GetRectangleCoordinatesInMeter(System.Windows.Shapes.Rectangle rect) + { + if (rect != null) + { + var _canvasLeft = CanvasGetLeftinMeter(); + var _canvasTop = CanvasGetTopinMeter(); + + // Obtiene la transformada del objeto visual + GeneralTransform transform = rect.TransformToAncestor(_visualRepresentation); + + // Obtiene la posición absoluta + Point topLeft = transform.Transform(new Point(0, 0)); + Point bottomRight = transform.Transform(new Point(rect.ActualWidth, rect.ActualHeight)); + + return (new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.Y) + _canvasTop), + new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)bottomRight.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)bottomRight.Y) + _canvasTop)); + } + else return (new Vector2(0, 0), new Vector2(0, 0)); + } + + public (Vector2 Start, Vector2 End) GetCenterLineVectors(System.Windows.Shapes.Rectangle rect) + { + if (rect == null) + return (new Vector2(0, 0), new Vector2(0, 0)); + + var _canvasLeft = CanvasGetLeftinMeter(); + var _canvasTop = CanvasGetTopinMeter(); + + var transform = rect.TransformToAncestor(_visualRepresentation); + + // Puntos en coordenadas locales del rectángulo no rotado + Point startLocal = new Point(0, rect.ActualHeight / 2); + Point endLocal = new Point(rect.ActualWidth, rect.ActualHeight / 2); + + // Transformar estos puntos al sistema de coordenadas del ancestro + Point transformedStart = transform.Transform(startLocal); + Point transformedEnd = transform.Transform(endLocal); + + // Convierte a unidades de Farseer (metros en este caso) + Vector2 start = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.Y) + _canvasTop); + Vector2 end = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.Y) + _canvasTop); + + return (start, end); + } + + + public Vector2 GetRectangleCenter(System.Windows.Shapes.Rectangle wpfRect) + { + var coords = GetRectangleCoordinatesInMeter(wpfRect); + + Vector2 topLeft = coords.TopLeft; + Vector2 bottomRight = coords.BottomRight; + + // Calcular el centro, ancho y alto en metros + return new Vector2((topLeft.X + bottomRight.X) / 2, (topLeft.Y + bottomRight.Y) / 2); + } + + public void UpdateRectangle(simRectangle simRect, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo) + { + if (simRect != null) + simRect.Create(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo); + } + + public simRectangle AddRectangle(SimulationManagerFP simulationManager, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo) + { + return simulationManager.AddRectangle(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo); + } + + public void UpdateOrCreateLine(simLine simGuia, System.Windows.Shapes.Rectangle wpfRect) + { + if (simGuia != null) + { + var coords = GetCenterLineVectors(wpfRect); + + // Crear o actualizar simRectangle + simGuia.Create( coords.Start,coords.End); // asumiendo que el ángulo inicial es 0 + } + } + + public simLine AddLine(SimulationManagerFP simulationManager, System.Windows.Shapes.Rectangle wpfRect) + { + var coords = GetCenterLineVectors(wpfRect); + return simulationManager.AddLine(coords.Start, coords.End); + } + public event PropertyChangedEventHandler PropertyChanged; @@ -93,7 +185,4 @@ namespace CtrEditor.ObjetosSim } } - - - } diff --git a/ObjetosSim/ucBotella.xaml.cs b/ObjetosSim/ucBotella.xaml.cs index 0516460..97a1482 100644 --- a/ObjetosSim/ucBotella.xaml.cs +++ b/ObjetosSim/ucBotella.xaml.cs @@ -12,9 +12,11 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; -using System.Numerics; +using Microsoft.Xna.Framework; using CtrEditor.Convertidores; using CtrEditor.Siemens; +using CtrEditor.Simulacion; +using Newtonsoft.Json.Linq; namespace CtrEditor.ObjetosSim { @@ -25,61 +27,88 @@ namespace CtrEditor.ObjetosSim public class osBotella : osBase { - private Circle Geometria = new Circle(); + private float _diametro; + private float _mass; + private Vector2 _centro = new Vector2(); // Centro + private string _nombre = "Botella"; + private simCircle Simulacion_Botella; // Otros datos y métodos relevantes para la simulación - private string _nombre = "Botella"; public float Diametro { - get => Geometria.Diameter; + get => _diametro; set { - Geometria.Diameter = value; + _diametro = value; + Simulacion_Botella?.SetDiameter(Diametro); OnPropertyChanged(nameof(Diametro)); } } public float Mass { - get => Geometria.Mass; + get => _mass; set { - Geometria.Mass = value; + _mass = value; + Simulacion_Botella?.SetMass(value); OnPropertyChanged(nameof(Mass)); } } - public float Overlap - { - get => Geometria.Overlap; - set - { - Geometria.Overlap = value; - OnPropertyChanged(nameof(Overlap)); - } - } - public override float Left { - get => Geometria.Left; + get => _centro.X-Diametro/2; set { - Geometria.Left = value; + _centro.X = value+Diametro/2; CanvasSetLeftinMeter(value); OnPropertyChanged(nameof(Left)); } } public override float Top { - get => Geometria.Top; + get => _centro.Y - Diametro / 2; set { - Geometria.Top = value; + _centro.Y = value + Diametro / 2; CanvasSetTopinMeter(value); OnPropertyChanged(nameof(Top)); } } + public float CenterX + { + get => _centro.X; + set + { + _centro.X = value; + CanvasSetLeftinMeter(Left); + OnPropertyChanged(nameof(CenterX)); + OnPropertyChanged(nameof(Left)); + } + } + public float CenterY + { + get => _centro.Y; + set + { + _centro.Y = value; + CanvasSetTopinMeter(Top); + OnPropertyChanged(nameof(CenterY)); + OnPropertyChanged(nameof(Top)); + } + } + + private void ActualizarGeometrias() + { + if (Simulacion_Botella != null) + { + Simulacion_Botella.SetDiameter(Diametro); + Simulacion_Botella.SetPosition(CenterX, CenterY); + } + } + public override string Nombre { get => _nombre; @@ -96,25 +125,34 @@ namespace CtrEditor.ObjetosSim public osBotella() { Diametro = 0.10f; + Mass = 1; } - public override void ConnectSimManager(SimulationManager simulationManager) - { - simulationManager.circles.Add(Geometria); - } - public override void UpdateGeometry() + public override void UpdateGeometryStart() { // Se llama antes de la simulacion - + ActualizarGeometrias(); } + public override void UpdateGeometryStep() + { + // Se llama antes de la simulacion + ActualizarGeometrias(); + } + public override void UpdatePLC(PLCModel plc) { } public override void UpdateControl() { - Top = Geometria.Top; - Left = Geometria.Left; - Overlap = Geometria.Overlap; + CenterX = Simulacion_Botella.CenterX; + CenterY = Simulacion_Botella.CenterY; } + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + Simulacion_Botella = simulationManager.AddCircle(Diametro, _centro, Mass); + } + } public partial class ucBotella : UserControl, IDataContainer @@ -124,6 +162,11 @@ namespace CtrEditor.ObjetosSim public ucBotella() { InitializeComponent(); + this.Loaded += OnLoaded; + } + private void OnLoaded(object sender, RoutedEventArgs e) + { + Datos?.ucLoaded(); } public void Resize(float width, float height) { } public void Move(float LeftPixels, float TopPixels) diff --git a/ObjetosSim/ucGuia.xaml b/ObjetosSim/ucGuia.xaml index f839964..b109b2e 100644 --- a/ObjetosSim/ucGuia.xaml +++ b/ObjetosSim/ucGuia.xaml @@ -11,7 +11,7 @@ - + diff --git a/ObjetosSim/ucGuia.xaml.cs b/ObjetosSim/ucGuia.xaml.cs index e2af8ce..347d525 100644 --- a/ObjetosSim/ucGuia.xaml.cs +++ b/ObjetosSim/ucGuia.xaml.cs @@ -14,6 +14,10 @@ using System.Windows.Navigation; using System.Windows.Shapes; using CtrEditor.Convertidores; using CtrEditor.Siemens; +using CtrEditor.Simulacion; +using FarseerPhysics.Dynamics; +using Microsoft.Xna.Framework; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace CtrEditor.ObjetosSim { @@ -22,26 +26,31 @@ namespace CtrEditor.ObjetosSim /// public class osGuia : osBase { + private float _ancho; + private float _altoGuia; + private float _left; + private float _top; + private float _angulo; private string _nombre = "Guia"; - private Line Geometria = new Line(); + private simLine Simulation_Guia; public override float Left { - get => Geometria.Left; + get => _left; set { - Geometria.Left = value; + _left = value; CanvasSetLeftinMeter(value); OnPropertyChanged(nameof(Left)); } } public override float Top { - get => Geometria.Top; + get => _top; set { - Geometria.Top = value; + _top = value; CanvasSetTopinMeter(value); OnPropertyChanged(nameof(Top)); } @@ -49,29 +58,29 @@ namespace CtrEditor.ObjetosSim public float Ancho { - get => Geometria.Length; + get => _ancho; set { - Geometria.Length = value; + _ancho = value; OnPropertyChanged(nameof(Ancho)); } } public float AltoGuia { - get => Geometria.Width; + get => _altoGuia; set { - Geometria.Width = value; + _altoGuia = value; OnPropertyChanged(nameof(AltoGuia)); } } public float Angulo { - get => Geometria.Angle; + get => _angulo; set { - Geometria.Angle = value; + _angulo = value; OnPropertyChanged(nameof(Angulo)); } } @@ -89,25 +98,37 @@ namespace CtrEditor.ObjetosSim } } + private void ActualizarGeometrias() + { + if (_visualRepresentation is ucGuia uc) + UpdateOrCreateLine(Simulation_Guia, uc.Guia); + } + public osGuia() { Ancho = 1; AltoGuia = 0.03f; } - public override void ConnectSimManager(SimulationManager simulationManager) - { - simulationManager.lines.Add(Geometria); - } - public override void UpdateGeometry() + public override void UpdateGeometryStart() { // Se llama antes de la simulacion + ActualizarGeometrias(); } public override void UpdateControl() { } + public override void UpdateGeometryStep() + { + } public override void UpdatePLC(PLCModel plc) { } - + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + if (_visualRepresentation is ucGuia uc) + Simulation_Guia = AddLine(simulationManager, uc.Guia); + } } @@ -119,6 +140,11 @@ namespace CtrEditor.ObjetosSim public ucGuia() { InitializeComponent(); + this.Loaded += OnLoaded; + } + private void OnLoaded(object sender, RoutedEventArgs e) + { + Datos?.ucLoaded(); } public void Resize(float width, float height) { diff --git a/ObjetosSim/ucTransporteGuias.xaml.cs b/ObjetosSim/ucTransporteGuias.xaml.cs index 02e5bb4..c78d139 100644 --- a/ObjetosSim/ucTransporteGuias.xaml.cs +++ b/ObjetosSim/ucTransporteGuias.xaml.cs @@ -14,6 +14,7 @@ using System.Windows.Navigation; using System.Windows.Shapes; using CtrEditor.Convertidores; using CtrEditor.Siemens; +using CtrEditor.Simulacion; namespace CtrEditor.ObjetosSim { @@ -24,36 +25,42 @@ namespace CtrEditor.ObjetosSim { private string _nombre = "Transporte Guias"; + private float _distance; + private float _altoGuia; + private float frictionCoefficient; private float velMax50hz; // en metros por minuto private float tiempoRampa; private bool esMarcha; - private double _distance; - private float altoGuia; - private float left; - private float top; + private float _ancho; + private float _alto; + private float _left; + private float _top; + private float _angulo; + private float _velocidadActual; + + private simRectangle? TransporteCentral; + private simLine? Guia_Superior; + private simLine? Guia_Inferior; - private Rectangle TransporteCentral = new Rectangle(); - private Line Guia_Superior = new Line(); - private Line Guia_Inferior = new Line(); public override float Left { - get => left; + get => _left; set { - left = value; + _left = value; CanvasSetLeftinMeter(value); OnPropertyChanged(nameof(Left)); } } public override float Top { - get => top; + get => _top; set { - top = value; + _top = value; CanvasSetTopinMeter(value); OnPropertyChanged(nameof(Top)); } @@ -61,53 +68,45 @@ namespace CtrEditor.ObjetosSim public float Ancho { - get => TransporteCentral.Length; + get => _ancho; set { - TransporteCentral.Length = value; + _ancho = value; OnPropertyChanged(nameof(Ancho)); } } - public float AltoGuia - { - get => altoGuia; - set - { - altoGuia = value; - OnPropertyChanged(nameof(AltoGuia)); - } - } - public float Alto { - get => TransporteCentral.Width; + get => _alto; set { - TransporteCentral.Width = value; + _alto = value; OnPropertyChanged(nameof(Alto)); } } public float Angulo { - get => TransporteCentral.Angle; + get => _angulo; set { - TransporteCentral.Angle = value; + _angulo = value; OnPropertyChanged(nameof(Angulo)); } } + public float VelocidadActual { - get => TransporteCentral.Speed; + get => _velocidadActual; set { - TransporteCentral.Speed = value; + _velocidadActual = value; + TransporteCentral?.SetSpeed(value); OnPropertyChanged(nameof(VelocidadActual)); } } - public double Distance + public float Distance { get { return _distance; } set @@ -115,11 +114,21 @@ namespace CtrEditor.ObjetosSim if (_distance != value) { _distance = value; - OnPropertyChanged("Distance"); + OnPropertyChanged(nameof(Distance)); } } } + public float AltoGuia + { + get => _altoGuia; + set + { + _altoGuia = value; + OnPropertyChanged(nameof(AltoGuia)); + } + } + public override string Nombre { get => _nombre; @@ -135,50 +144,21 @@ namespace CtrEditor.ObjetosSim private void ActualizarGeometrias() { - ucTransporteGuias ucTG = (ucTransporteGuias)_visualRepresentation; - if (ucTG != null) + if (_visualRepresentation is ucTransporteGuias uc) { - var _canvasLeft = CanvasGetLeftinMeter(); - var _canvasTop = CanvasGetTopinMeter(); + UpdateRectangle(TransporteCentral, uc.Transporte, Alto, Ancho, Angulo); + UpdateOrCreateLine(Guia_Superior, uc.GuiaSuperior); + UpdateOrCreateLine(Guia_Inferior, uc.GuiaInferior) ; - var coordenadas = GetRectangleCoordinatesInMeter(ucTG.Transporte, ucTG); - TransporteCentral.Left = coordenadas.Left + _canvasLeft; - TransporteCentral.Top = coordenadas.Top + _canvasTop; - - coordenadas = GetRectangleCoordinatesInMeter(ucTG.GuiaSuperior, ucTG); - Guia_Superior.Left = coordenadas.Left + _canvasLeft; - Guia_Superior.Top = coordenadas.Top + _canvasTop; ; - - coordenadas = GetRectangleCoordinatesInMeter(ucTG.GuiaInferior, ucTG); - Guia_Inferior.Left = coordenadas.Left + _canvasLeft; - Guia_Inferior.Top = coordenadas.Top + _canvasTop; ; - - TransporteCentral.Angle = Guia_Superior.Angle = Guia_Inferior.Angle = Angulo; - Guia_Superior.Length = Guia_Inferior.Length = Ancho; + TransporteCentral.Speed = VelocidadActual; } } - private (float Left, float Top) GetRectangleCoordinatesInMeter(System.Windows.Shapes.Rectangle rect, ucTransporteGuias ucTG) - { - if (rect != null) - { - // Obtiene la transformada del objeto visual - GeneralTransform transform = rect.TransformToAncestor(ucTG); - - // Obtiene la posición absoluta - Point topLeft = transform.Transform(new Point(0, 0)); - //Point bottomRight = transform.Transform(new Point(rect.ActualWidth, rect.ActualHeight)); - return (PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.X), PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.Y)); - } - else return (0,0); - } - - - 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 osTransporteGuias() { @@ -188,22 +168,35 @@ namespace CtrEditor.ObjetosSim Distance = 0.01f; } - public override void ConnectSimManager(SimulationManager simulationManager) - { - simulationManager.rectangles.Add(TransporteCentral); - simulationManager.lines.Add(Guia_Superior); - simulationManager.lines.Add(Guia_Inferior); - } - public override void UpdateGeometry() + public override void UpdateGeometryStart() { // Se llama antes de la simulacion ActualizarGeometrias(); } - + public override void UpdateGeometryStep() + { + } public override void UpdateControl() { } public override void UpdatePLC(PLCModel plc) { } + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + simulationManager.rectangles.Add(TransporteCentral); + simulationManager.lines.Add(Guia_Superior); + simulationManager.lines.Add(Guia_Inferior); + + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + if (_visualRepresentation is ucTransporteGuias uc) + { + TransporteCentral = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo); + Guia_Superior = AddLine(simulationManager, uc.GuiaSuperior); + Guia_Inferior = AddLine(simulationManager, uc.GuiaInferior); + } + } } @@ -215,6 +208,11 @@ namespace CtrEditor.ObjetosSim public ucTransporteGuias() { InitializeComponent(); + this.Loaded += OnLoaded; + } + private void OnLoaded(object sender, RoutedEventArgs e) + { + Datos?.ucLoaded(); } public void Resize(float width, float height) { diff --git a/ObjetosSim/ucTransporteTTop.xaml b/ObjetosSim/ucTransporteTTop.xaml index f6352bc..f9493e0 100644 --- a/ObjetosSim/ucTransporteTTop.xaml +++ b/ObjetosSim/ucTransporteTTop.xaml @@ -11,7 +11,7 @@ - + diff --git a/ObjetosSim/ucTransporteTTop.xaml.cs b/ObjetosSim/ucTransporteTTop.xaml.cs index 9b94f0c..caf2344 100644 --- a/ObjetosSim/ucTransporteTTop.xaml.cs +++ b/ObjetosSim/ucTransporteTTop.xaml.cs @@ -1,22 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -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; using CtrEditor.Convertidores; using CtrEditor.Siemens; +using CtrEditor.Simulacion; + namespace CtrEditor.ObjetosSim { @@ -34,60 +21,72 @@ namespace CtrEditor.ObjetosSim private float tiempoRampa; private bool esMarcha; - private Rectangle Geometria = new Rectangle(); + private float _ancho; + private float _alto; + private float _left; + private float _top; + private float _angulo; + private float _velocidadActual; + + private simRectangle Simulation_Transporte; + public override float Left { - get => Geometria.Left; + get => _left; set { - Geometria.Left = value; + _left = value; CanvasSetLeftinMeter(value); OnPropertyChanged(nameof(Left)); } } public override float Top { - get => Geometria.Top; + get => _top; set { - Geometria.Top = value; + _top = value; CanvasSetTopinMeter(value); OnPropertyChanged(nameof(Top)); } } - public float Ancho { - get => Geometria.Length; + public float Ancho + { + get => _ancho; set { - Geometria.Length = value; + _ancho = value; OnPropertyChanged(nameof(Ancho)); } } - public float Alto { - get => Geometria.Width; + public float Alto + { + get => _alto; set { - Geometria.Width = value; + _alto = value; OnPropertyChanged(nameof(Alto)); } } public float Angulo { - get => Geometria.Angle; + get => _angulo; set { - Geometria.Angle = value; + _angulo = value; OnPropertyChanged(nameof(Angulo)); } } + public float VelocidadActual { - get => Geometria.Speed; + get => _velocidadActual; set { - Geometria.Speed = value; + _velocidadActual = value; + Simulation_Transporte?.SetSpeed(value); OnPropertyChanged(nameof(VelocidadActual)); } } @@ -105,6 +104,15 @@ namespace CtrEditor.ObjetosSim } } + private void ActualizarGeometrias() + { + if (_visualRepresentation is ucTransporteTTop uc) + { + UpdateRectangle(Simulation_Transporte, uc.Transporte,Alto,Ancho,Angulo); + Simulation_Transporte.Speed = 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; } @@ -116,14 +124,13 @@ namespace CtrEditor.ObjetosSim Alto = 0.10f; } - public override void ConnectSimManager(SimulationManager simulationManager) - { - simulationManager.rectangles.Add(Geometria); - } - public override void UpdateGeometry() + public override void UpdateGeometryStart() { // Se llama antes de la simulacion - + ActualizarGeometrias(); + } + public override void UpdateGeometryStep() + { } public override void UpdatePLC(PLCModel plc) { } @@ -131,6 +138,13 @@ namespace CtrEditor.ObjetosSim public override void UpdateControl() { } + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + if (_visualRepresentation is ucTransporteTTop uc) + Simulation_Transporte = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo); + } } @@ -141,6 +155,11 @@ namespace CtrEditor.ObjetosSim public ucTransporteTTop() { InitializeComponent(); + this.Loaded += OnLoaded; + } + private void OnLoaded(object sender, RoutedEventArgs e) + { + Datos?.ucLoaded(); } public void Resize(float width, float height) { diff --git a/ObjetosSim/ucVMmotorSim.xaml.cs b/ObjetosSim/ucVMmotorSim.xaml.cs index 6bc01f7..56e419b 100644 --- a/ObjetosSim/ucVMmotorSim.xaml.cs +++ b/ObjetosSim/ucVMmotorSim.xaml.cs @@ -14,6 +14,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using CtrEditor.Simulacion; namespace CtrEditor.ObjetosSim { @@ -93,13 +94,13 @@ namespace CtrEditor.ObjetosSim PLC_NumeroMotor = 31; } - public override void ConnectSimManager(SimulationManager simulationManager) - { - } - public override void UpdateGeometry() + public override void UpdateGeometryStart() { // Se llama antes de la simulacion + } + public override void UpdateGeometryStep() + { } public override void UpdatePLC(PLCModel plc) { } @@ -107,6 +108,12 @@ namespace CtrEditor.ObjetosSim { } + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + } + } public partial class ucVMmotorSim : UserControl, IDataContainer diff --git a/Simulacion/FPhysics.cs b/Simulacion/FPhysics.cs new file mode 100644 index 0000000..afaf216 --- /dev/null +++ b/Simulacion/FPhysics.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Forms; +using System.Windows.Media; +using System.Windows.Shapes; +using CtrEditor.Convertidores; +using CtrEditor.ObjetosSim; +using FarseerPhysics.Collision.Shapes; +using FarseerPhysics.Common; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Dynamics.Contacts; +using FarseerPhysics.Factories; +using Microsoft.Xna.Framework; +using OpenCvSharp; +using Siemens.Simatic.Simulation.Runtime; +using static System.Windows.Forms.DataFormats; +using Point = System.Windows.Point; + +namespace CtrEditor.Simulacion +{ + + public class simRectangle + { + public Body Body { get; private set; } + public float Speed { get; set; } // Velocidad para efectos de cinta transportadora + public World _world; + + public simRectangle(World world, float width, float height, Vector2 position, float angle = 0) + { + _world = world; + Create(width, height, position, angle); + } + + public float Angle + { + get { return MathHelper.ToDegrees(Body.Rotation); } + set { Body.Rotation = MathHelper.ToRadians(value); } + } + + public void SetPosition(float x, float y) + { + Body.Position = new Vector2(x, y); + } + + public void SetSpeed(float speed) + { + Speed = speed; + } + + public void SetDimensions(float width, float height) + { + // Primero, elimina el fixture antiguo + Body.DestroyFixture(Body.FixtureList[0]); + + // Crea un nuevo fixture con las nuevas dimensiones + var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f); + Body.CreateFixture(newShape); + } + + public void Create(float width, float height, Vector2 position, float angle = 0) + { + if (Body != null) { + _world.RemoveBody(Body); + } + Body = BodyFactory.CreateRectangle(_world, width, height, 1f, position); + Body.FixtureList[0].IsSensor = true; + Body.BodyType = BodyType.Static; + Body.Rotation = MathHelper.ToRadians(angle); + Body.UserData = this; // Importante para la identificación durante la colisión + } + + + // Otros métodos según sea necesario, como mover, rotar, etc. + } + + + public class simLine + { + public Body Body { get; private set; } + public World _world; + + public simLine(World world, Vector2 start, Vector2 end) + { + _world = world; + Create(start, end); + } + + public void Create(Vector2 start, Vector2 end) + { + if (Body != null) + { + _world.RemoveBody(Body); // Elimina el cuerpo anterior si existe + } + Body = BodyFactory.CreateEdge(_world, start, end); + Body.BodyType = BodyType.Static; + Body.UserData = this; // Importante para la identificación durante la colisión + } + + public void UpdateVertices(Vector2 newStart, Vector2 newEnd) + { + Create(newStart, newEnd); // Recrear la línea con nuevos vértices + } + } + + + + public class simCircle + { + public Body Body { get; private set; } + public World _world; + private float _radius; + private float _mass; + + public simCircle(World world, float diameter, Vector2 position, float mass) + { + _world = world; + _radius = diameter / 2; + _mass = mass; + Create(position); + } + + public float CenterX + { + get { return Body.Position.X; } + set { } + } + + public float CenterY + { + get { return Body.Position.Y; } + set { } + } + + public Vector2 Center + { + get { return Body.Position; } + } + + public float Mass + { + get { + if (_mass <= 0) + _mass = 1; + return _mass; + } + set { _mass = value; } + } + + private void Create(Vector2 position) + { + if (Body != null) + { + _world.RemoveBody(Body); // Remover el cuerpo anterior si existe + } + Body = BodyFactory.CreateCircle(_world, _radius, 10f, position); + Body.BodyType = BodyType.Dynamic; + + // Restablecer manejador de eventos de colisión + Body.OnCollision += HandleCollision; + Body.OnSeparation += HandleOnSeparation; + + Body.UserData = this; // Importante para la identificación durante la colisión + + // Configurar la fricción + Body.Friction = 0.5f; // Ajustar según sea necesario para tu simulación + + // Configurar amortiguamiento + Body.LinearDamping = 0.01f; // Ajustar para controlar la reducción de la velocidad lineal + Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular + Body.Restitution = 0.2f; // Baja restitución para menos rebote + Body.IsBullet = true; + } + + private void HandleOnSeparation(Fixture fixtureA, Fixture fixtureB) + { + Body.LinearDamping = 5f; // Ajustar para controlar la reducción de la velocidad lineal + } + + public void SetPosition(float x, float y) + { + // Usar el ángulo actual, ya que no es relevante para un círculo en este contexto + Body.SetTransform(new Vector2(x, y), Body.Rotation); + } + + public void SetDiameter(float diameter) + { + _radius = diameter / 2; + Create(Body.Position); // Recrear el círculo con el nuevo tamaño + } + + public void SetMass(float mass) + { + Mass = mass; + } + + + private bool HandleCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact) + { + if (fixtureB.Body.UserData is simRectangle) + { + simRectangle conveyor = fixtureB.Body.UserData as simRectangle; + ApplyConveyorEffect(conveyor, fixtureA, contact); + return true; // No aplicar respuestas fisicas + } + return true; // No aplicar respuestas fisicas + } + + private void ApplyConveyorEffect(simRectangle conveyor, Fixture circleFixture, FarseerPhysics.Dynamics.Contacts.Contact contact) + { + // Calcular la velocidad deseada en metros por segundo + float speedMetersPerSecond = conveyor.Speed / 60.0f; + Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond; + + // Calcular la fuerza necesaria para alcanzar esa velocidad + Vector2 velocityChange = desiredVelocity - circleFixture.Body.LinearVelocity; + float timeStep = 1.0f / 60.0f; // Asumiendo 60 Hz de tasa de actualización + Vector2 acceleration = velocityChange / timeStep; + Vector2 force = acceleration / Mass; + + // Aplicar la fuerza al círculo + circleFixture.Body.ApplyForce(force); + } + + + } + + + public class SimulationManagerFP + { + private World world; + private Canvas simulationCanvas; + public List circles; + public List rectangles; + public List lines; + + public Canvas DebugCanvas { get => simulationCanvas; set => simulationCanvas = value; } + + public SimulationManagerFP() + { + world = new World(new Vector2(0,0)); // Vector2.Zero + circles = new List(); + rectangles = new List(); + lines = new List(); + } + public simCircle AddCircle(float diameter, Vector2 position, float mass) + { + simCircle circle = new simCircle(world, diameter, position, mass); + circles.Add(circle); + return circle; + } + + + public simRectangle AddRectangle(float width, float height, Vector2 position, float angle) + { + simRectangle rectangle = new simRectangle(world, width, height, position, angle); + rectangles.Add(rectangle); + return rectangle; + } + + public simLine AddLine(Vector2 start, Vector2 end) + { + simLine line = new simLine(world, start, end); + lines.Add(line); + return line; + } + + // Otros métodos para agregar círculos y ejecutar la simulación + + public void Step(float timeStep) + { + world.Step(timeStep/1000.0f); + // Actualiza y gestiona otras lógicas si es necesario + } + + public void Debug_DrawInitialBodies() + { + ClearSimulationShapes(); + world.Step(0.01f); // Para actualizar la BodyList + foreach (Body body in world.BodyList) + { + foreach (Fixture fixture in body.FixtureList) + { + DrawShape(fixture); + } + } + } + + private void ClearSimulationShapes() + { + var simulationShapes = simulationCanvas.Children.OfType().Where(s => s.Tag as string == "Simulation").ToList(); + foreach (var shape in simulationShapes) + { + simulationCanvas.Children.Remove(shape); + } + } + + private void DrawShape(Fixture fixture) + { + System.Windows.Shapes.Shape shape; + switch (fixture.ShapeType) + { + case ShapeType.Circle: + shape = DrawCircle(fixture); + break; + case ShapeType.Polygon: + shape = DrawPolygon(fixture); + break; + case ShapeType.Edge: + shape = DrawEdge(fixture); + break; + default: + return; + } + shape.Tag = "Simulation"; // Marcar para simulación + Canvas.SetZIndex(shape, 20); + simulationCanvas.Children.Add(shape); + } + + private float p(float x) + { + float c = PixelToMeter.Instance.calc.MetersToPixels(x); + return c; + } + + private System.Windows.Shapes.Shape DrawEdge(Fixture fixture) + { + EdgeShape edge = fixture.Shape as EdgeShape; + Line line = new Line + { + X1 = p(edge.Vertex1.X + fixture.Body.Position.X ), // Aplicar escala y posición + Y1 = p(edge.Vertex1.Y + fixture.Body.Position.Y ), + X2 = p(edge.Vertex2.X + fixture.Body.Position.X ), + Y2 = p(edge.Vertex2.Y + fixture.Body.Position.Y ), + Stroke = Brushes.Black, + StrokeThickness = 2 + }; + return line; + } + + private System.Windows.Shapes.Shape DrawCircle(Fixture fixture) + { + CircleShape circle = fixture.Shape as CircleShape; + Ellipse ellipse = new Ellipse + { + Width = p(circle.Radius * 2), // Escalado para visualización + Height = p(circle.Radius * 2), // Escalado para visualización + Stroke = Brushes.Black, + StrokeThickness = 2 + }; + Canvas.SetLeft(ellipse, p(fixture.Body.Position.X - circle.Radius )); + Canvas.SetTop(ellipse, p(fixture.Body.Position.Y - circle.Radius )); + return ellipse; + } + + private System.Windows.Shapes.Shape DrawPolygon(Fixture fixture) + { + Polygon polygon = new Polygon { Stroke = Brushes.Black, StrokeThickness = 2 }; + PolygonShape polyShape = fixture.Shape as PolygonShape; + + float cos = (float)Math.Cos(fixture.Body.Rotation); + float sin = (float)Math.Sin(fixture.Body.Rotation); + + foreach (Vector2 vertex in polyShape.Vertices) + { + // Aplicar la rotación alrededor del origen y luego trasladar + float rotatedX = vertex.X * cos - vertex.Y * sin + fixture.Body.Position.X; + float rotatedY = vertex.X * sin + vertex.Y * cos + fixture.Body.Position.Y; + + polygon.Points.Add(new Point(p(rotatedX), p(rotatedY))); + } + + return polygon; + } + + + + } + + +}