using System.ComponentModel; using System.Windows; using System.Windows.Controls; using CommunityToolkit.Mvvm.ComponentModel; using LibS7Adv; using CtrEditor.Simulacion; using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; using CtrEditor.FuncionesBase; using System.Text.Json.Serialization; using System.Windows.Media; namespace CtrEditor.ObjetosSim { /// /// Interaction logic for ucTransporteCurvaGuias.xaml /// public partial class osTransporteCurvaGuias : osBase, IosBase { private osBase Motor = null; private simCurve Simulation_TransporteCurvaGuias; // Listas para almacenar los segmentos de las guías curvas private List GuiasSuperiores = new List(); private List GuiasInferiores = new List(); private float _velocidadActual; public static string NombreClase() { return "Transporte Curva Guias"; } private string nombre = "Transporte Curva Guias"; public override string Nombre { get => nombre; set => SetProperty(ref nombre, value); } [ObservableProperty] public float velocidadActual; partial void OnVelocidadActualChanged(float value) { SetSpeed(); } [ObservableProperty] bool invertirDireccion; partial void OnInvertirDireccionChanged(bool value) { SetSpeed(); if (_visualRepresentation is ucTransporteCurvaGuias uc) { CrearAnimacionStoryBoardTrasnporteCircular(uc.Transporte.TransportePath, InvertirDireccion, Angulo); ActualizarAnimacionStoryBoardTransporte(VelocidadActual); } } void SetSpeed() { if (InvertirDireccion) Simulation_TransporteCurvaGuias?.SetSpeed(-VelocidadActual); else Simulation_TransporteCurvaGuias?.SetSpeed(VelocidadActual); ActualizarAnimacionStoryBoardTransporte(VelocidadActual); } [ObservableProperty] private float radioExterno; partial void OnRadioExternoChanged(float value) { // Update ancho and alto based on radioExterno Ancho = value * 2; Alto = value * 2; // Ensure radioInterno maintains proper proportion if needed if (RadioInterno >= value) { RadioInterno = value * 0.75f; // Default proportion } ActualizarGeometrias(); } [ObservableProperty] private float radioInterno; partial void OnRadioInternoChanged(float value) { ActualizarGeometrias(); } [ObservableProperty] [property: Description("Número de segmentos para las guías (máximo 20)")] [property: Category("Guías")] private int numeroSegmentosGuias; partial void OnNumeroSegmentosGuiasChanged(int value) { // Limitar entre 4 y 20 segmentos if (value < 4) NumeroSegmentosGuias = 4; if (value > 20) NumeroSegmentosGuias = 20; ActualizarGeometrias(); } [ObservableProperty] [property: Description("Grosor de las guías")] [property: Category("Guías")] private float grosorGuias; partial void OnGrosorGuiasChanged(float value) { ActualizarGeometrias(); } [ObservableProperty] [property: Description("Distancia de separación de las guías desde el borde")] [property: Category("Guías")] private float distanciaGuias; partial void OnDistanciaGuiasChanged(float value) { ActualizarGeometrias(); } [ObservableProperty] [property: Description("Mostrar guías visuales")] [property: Category("Guías")] private bool mostrarGuias; // Propiedad interna Brush para el binding private Brush _colorGuiasBrush = new SolidColorBrush(Colors.DarkBlue); [JsonIgnore] public Brush ColorGuiasBrush { get => _colorGuiasBrush; set { if (_colorGuiasBrush != value) { _colorGuiasBrush = value; OnPropertyChanged(nameof(ColorGuiasBrush)); // Extraer el color del brush y actualizar la propiedad Color if (value is SolidColorBrush solidBrush) { colorGuias = solidBrush.Color; OnPropertyChanged(nameof(ColorGuias)); } } } } [ObservableProperty] [property: Description("Color de las guías")] [property: Category("Guías")] private Color colorGuias = Colors.DarkBlue; partial void OnColorGuiasChanged(Color value) { // Sincronizar con la propiedad Brush ColorGuiasBrush = new SolidColorBrush(value); } [ObservableProperty] [property: Description("Bit to enable Link to Motor")] [property: Category("PLC link:")] string tag_ReleActivatedMotor; [ObservableProperty] [property: Description("Link to Motor")] [property: Category("PLC link:")] [property: ItemsSource(typeof(osBaseItemsSource))] string id_Motor; [JsonIgnore] private PropertyChangedEventHandler motorPropertyChangedHandler; partial void OnId_MotorChanged(string value) { if (Motor != null && motorPropertyChangedHandler != null) Motor.PropertyChanged -= motorPropertyChangedHandler; if (_mainViewModel != null && !string.IsNullOrEmpty(value)) { Motor = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => s is osVMmotorSim motor && motor.Nombre == value); if (Motor != null) { motorPropertyChangedHandler = (sender, e) => { if (e.PropertyName == nameof(osVMmotorSim.Nombre)) { Id_Motor = ((osVMmotorSim)sender).Nombre; } }; Motor.PropertyChanged += motorPropertyChangedHandler; } } } public override void AnguloChanged(float value) { OnPropertyChanged(nameof(AnguloFinal)); ActualizarGeometrias(); } [ObservableProperty] [NotifyPropertyChangedFor(nameof(AnguloFinal))] private float arco_en_grados; partial void OnArco_en_gradosChanged(float value) { ActualizarGeometrias(); } [Hidden] public float AnguloFinal { get => Angulo + Arco_en_grados; } private void ActualizarGeometrias() { if (_visualRepresentation is ucTransporteCurvaGuias uc) { UpdateCurve(Simulation_TransporteCurvaGuias, RadioInterno, RadioExterno, Angulo, Angulo + Arco_en_grados); ActualizarGuiasCurvas(); SetSpeed(); } } private void ActualizarGuiasCurvas() { // Limpiar guías existentes EliminarGuiasExistentes(); // Crear nuevas guías CrearGuiasCurvas(); } private void EliminarGuiasExistentes() { foreach (var guia in GuiasSuperiores) { simulationManager?.Remove(guia); } foreach (var guia in GuiasInferiores) { simulationManager?.Remove(guia); } GuiasSuperiores.Clear(); GuiasInferiores.Clear(); } private void CrearGuiasCurvas() { if (NumeroSegmentosGuias < 4 || simulationManager == null) return; float radioGuiaSuperior = RadioExterno + DistanciaGuias; float radioGuiaInferior = RadioInterno - DistanciaGuias; // Asegurar que el radio interior de la guía no sea negativo if (radioGuiaInferior < 0.01f) radioGuiaInferior = 0.01f; // Convertir ángulos a radianes float anguloInicioRad = simBase.GradosARadianes(Angulo); float anguloFinalRad = simBase.GradosARadianes(AnguloFinal); float rangoAngular = anguloFinalRad - anguloInicioRad; // Calcular el paso angular entre segmentos float pasoAngular = rangoAngular / NumeroSegmentosGuias; // Obtener el centro una vez para todo el método nkast.Aether.Physics2D.Common.Vector2 centro = GetCurveCenterInMeter(RadioExterno); // Crear segmentos para guía superior (externa) for (int i = 0; i < NumeroSegmentosGuias; i++) { float angulo1 = anguloInicioRad + i * pasoAngular; float angulo2 = anguloInicioRad + (i + 1) * pasoAngular; nkast.Aether.Physics2D.Common.Vector2 punto1 = new nkast.Aether.Physics2D.Common.Vector2( radioGuiaSuperior * (float)Math.Cos(angulo1), radioGuiaSuperior * (float)Math.Sin(angulo1) ); nkast.Aether.Physics2D.Common.Vector2 punto2 = new nkast.Aether.Physics2D.Common.Vector2( radioGuiaSuperior * (float)Math.Cos(angulo2), radioGuiaSuperior * (float)Math.Sin(angulo2) ); // Ajustar por la posición del objeto punto1 += centro; punto2 += centro; simGuia guiaSegmento = simulationManager.AddLine(punto1, punto2); GuiasSuperiores.Add(guiaSegmento); } // Crear segmentos para guía inferior (interna) for (int i = 0; i < NumeroSegmentosGuias; i++) { float angulo1 = anguloInicioRad + i * pasoAngular; float angulo2 = anguloInicioRad + (i + 1) * pasoAngular; nkast.Aether.Physics2D.Common.Vector2 punto1 = new nkast.Aether.Physics2D.Common.Vector2( radioGuiaInferior * (float)Math.Cos(angulo1), radioGuiaInferior * (float)Math.Sin(angulo1) ); nkast.Aether.Physics2D.Common.Vector2 punto2 = new nkast.Aether.Physics2D.Common.Vector2( radioGuiaInferior * (float)Math.Cos(angulo2), radioGuiaInferior * (float)Math.Sin(angulo2) ); // Ajustar por la posición del objeto punto1 += centro; punto2 += centro; simGuia guiaSegmento = simulationManager.AddLine(punto1, punto2); GuiasInferiores.Add(guiaSegmento); } } public override void OnMoveResizeRotate() { ActualizarGeometrias(); } [ObservableProperty] public float frictionCoefficient; [ObservableProperty] public float velMax50hz; [ObservableProperty] public float tiempoRampa; [ObservableProperty] public bool esMarcha; public override void OnResize(float Delta_Width, float Delta_Height) { // Calculate the proportional change factor float widthChangeFactor = (Ancho + Delta_Width) / Ancho; float heightChangeFactor = (Alto + Delta_Height) / Alto; // Use the average or minimum change factor to maintain aspect ratio float changeFactor = Math.Min(widthChangeFactor, heightChangeFactor); // Save the original radiuses for calculating position adjustments float originalRadioExterno = RadioExterno; // Apply the change factor to both radios RadioExterno *= changeFactor; RadioInterno *= changeFactor; // Calculate position adjustment to keep the component centered float radiusDifference = RadioExterno - originalRadioExterno; // Adjust Left and Top to maintain center position // We move by negative half the difference because the component expands outward Left -= radiusDifference; Top -= radiusDifference; // Ensure minimums if (RadioExterno < 0.1f) RadioExterno = 0.1f; if (RadioInterno < 0.05f) RadioInterno = 0.05f; // Ensure radioInterno is always less than radioExterno if (RadioInterno >= RadioExterno) RadioInterno = RadioExterno * 0.75f; } public osTransporteCurvaGuias() { RadioExterno = 1.3f; RadioInterno = 1f; Ancho = RadioExterno * 2; // Set initial width based on external radius Alto = RadioExterno * 2; // Set initial height based on external radius Arco_en_grados = 90; Tag_ReleActivatedMotor = "1"; NumeroSegmentosGuias = 12; // Valor por defecto GrosorGuias = 0.03f; DistanciaGuias = 0.05f; MostrarGuias = true; // Mostrar guías por defecto } public override void UpdateGeometryStart() { // Se llama antes de la simulacion ActualizarGeometrias(); } public override void SimulationStop() { // Se llama al detener la simulacion ActualizarAnimacionStoryBoardTransporte(VelocidadActual); } public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds) { if (Motor != null) { if (Motor is osVMmotorSim motor) if (LeerBitTag(Tag_ReleActivatedMotor)) VelocidadActual = motor.Velocidad; else VelocidadActual = 0; } } public override void ucLoaded() { // El UserControl ya se ha cargado y podemos obtener las coordenadas para // crear el objeto de simulacion base.ucLoaded(); OnId_MotorChanged(Id_Motor); // Link Id_Motor = Motor if (_visualRepresentation is ucTransporteCurvaGuias uc) { Simulation_TransporteCurvaGuias = AddCurve(RadioInterno, RadioExterno, Angulo, Angulo + Arco_en_grados); CrearGuiasCurvas(); // Crear las guías curvas CrearAnimacionStoryBoardTrasnporteCircular(uc.Transporte.TransportePath, InvertirDireccion, Angulo); } } public override void ucUnLoaded() { // El UserControl se esta eliminando // eliminar el objeto de simulacion simulationManager?.Remove(Simulation_TransporteCurvaGuias); EliminarGuiasExistentes(); } } public partial class ucTransporteCurvaGuias : UserControl, IDataContainer { public osBase? Datos { get; set; } public int zIndex_fromFrames { get; set; } public ucTransporteCurvaGuias() { InitializeComponent(); this.Loaded += OnLoaded; this.Unloaded += OnUnloaded; } private void OnLoaded(object sender, RoutedEventArgs e) { Datos?.ucLoaded(); } private void OnUnloaded(object sender, RoutedEventArgs e) { Datos?.ucUnLoaded(); } public void Highlight(bool State) { } public ZIndexEnum ZIndex_Base() { return ZIndexEnum.Estaticos; } } }