using System.Globalization; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using CtrEditor.ObjetosSim; using CtrEditor.Convertidores; using CtrEditor.Siemens; using static System.Runtime.InteropServices.JavaScript.JSType; using Binding = System.Windows.Data.Binding; 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 CheckBox = System.Windows.Controls.CheckBox; using Orientation = System.Windows.Controls.Orientation; using ListBox = System.Windows.Controls.ListBox; //using OpenCvSharp; namespace CtrEditor { /// /// Interaction logic for MainWindow.xaml /// /// public partial class MainWindow : Window { // Para el Canvas private Point _lastMousePosition; private bool _isDrawingCanvas = false; private bool _isDraggingCanvas = false; private Image imagenDeFondo; // Para los UserControl private Point _startPointUserControl; private UserControl _currentDraggingControl; private bool _isRotatingUserControl = false; private bool _isResizingUserControl = false; private bool _isDraggingUserControl = false; private bool _isMovingUserControl = false; private double _initialAngleUserControl; private TextBlock _angleDisplayTextBlock; public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; ImagenEnTrabajoScrollViewer.PreviewMouseWheel += ImagenEnTrabajoCanvas_MouseWheel; ImagenEnTrabajoCanvas.MouseDown += Canvas_MouseDown_Panning; ImagenEnTrabajoCanvas.MouseMove += Canvas_MouseMove_Panning; ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { if (DataContext is MainViewModel viewModel) { viewModel.ImageSelected += ViewModel_ImageSelected; //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; } } private void AgregarUserControl(UserControl userControl) { if (userControl is IDataContainer dataContainer) { 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; double scaleY = scaleTransform?.ScaleY ?? 1.0; // Obtiene el área visible del ScrollViewer double visibleWidth = ImagenEnTrabajoScrollViewer.ViewportWidth; double visibleHeight = ImagenEnTrabajoScrollViewer.ViewportHeight; // Obtiene la posición actual del desplazamiento ajustada por el zoom double offsetX = ImagenEnTrabajoScrollViewer.HorizontalOffset / scaleX; double offsetY = ImagenEnTrabajoScrollViewer.VerticalOffset / scaleY; // Calcula el centro visible ajustado double centerX = offsetX + (visibleWidth / scaleX) / 2; double centerY = offsetY + (visibleHeight / scaleY) / 2; // Ajusta la posición del UserControl para que esté centrado en el área visible double leftPixels = centerX - (userControl.ActualWidth / 2); double topPixels = centerY - (userControl.ActualHeight / 2); // Establece la posición del UserControl 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; } else { // Fuerza a Establecer la posición del UserControl NuevoOS.Left = NuevoOS.Left; NuevoOS.Top = NuevoOS.Top; } // Suscribirse a eventos de mouse para marcar el Control userControl.MouseEnter += UserControl_MouseEnter; userControl.MouseLeave += UserControl_MouseLeave; // Suscribir a eventos de mouse para panning userControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown; userControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp; userControl.MouseMove += UserControl_MouseMove; // Añade el UserControl al Canvas Canvas.SetZIndex(userControl, dataContainer.ZIndex()); ImagenEnTrabajoCanvas.Children.Add(userControl); } } private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (!_isDrawingCanvas) { var userControl = sender as UserControl; _currentDraggingControl = userControl; userControl.CaptureMouse(); // Importante para recibir eventos de movimiento incluso fuera del control _isMovingUserControl = true; if (sender is UserControl control && control.DataContext is osBase datos) { var viewModel = DataContext as MainViewModel; if (viewModel != null) { viewModel.SelectedItemOsList = datos; // Esto desencadenará ListaOs_SelectionChanged } } // ROTACION if (Keyboard.IsKeyDown(Key.LeftShift)) { // Inicializar la rotación _isRotatingUserControl = true; RotateTransform rotateTransform = userControl.RenderTransform as RotateTransform; if (rotateTransform == null) { rotateTransform = new RotateTransform(); userControl.RenderTransform = rotateTransform; } _initialAngleUserControl = rotateTransform.Angle; // Establecer el punto inicial de referencia para el cálculo de rotación _startPointUserControl = new Point(rotateTransform.CenterX, rotateTransform.CenterY); // Ajusta el punto inicial al espacio del Canvas _startPointUserControl = userControl.TranslatePoint(_startPointUserControl, ImagenEnTrabajoCanvas); // Crear y configurar el TextBlock si no existe if (_angleDisplayTextBlock == null) { _angleDisplayTextBlock = new TextBlock { Foreground = Brushes.Black, Background = Brushes.White, Opacity = 0.8, Padding = new Thickness(5) }; ImagenEnTrabajoCanvas.Children.Add(_angleDisplayTextBlock); } PositionAngleDisplay(userControl); _angleDisplayTextBlock.Visibility = Visibility.Visible; Canvas.SetZIndex(_angleDisplayTextBlock, 15); } // TAMANO else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { // Inicializar el cambio de tamaño _isResizingUserControl = true; } // MOVIMIENTO else { // Inicializar el movimiento/panning _isDraggingUserControl = true; _startPointUserControl = e.GetPosition(ImagenEnTrabajoCanvas); } } } private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (_isMovingUserControl) { var userControl = sender as UserControl; userControl.ReleaseMouseCapture(); _currentDraggingControl = null; _isResizingUserControl = _isRotatingUserControl = _isDraggingUserControl = false ; _isMovingUserControl = false; // Ocultar el TextBlock de ángulo if (_angleDisplayTextBlock != null) { _angleDisplayTextBlock.Visibility = Visibility.Collapsed; } } } private void UserControl_MouseMove(object sender, MouseEventArgs e) { if (_isMovingUserControl && _currentDraggingControl != null) { var currentPosition = e.GetPosition(ImagenEnTrabajoCanvas); if (_isDraggingUserControl) { // Código para mover el control var dx = currentPosition.X - _startPointUserControl.X; var dy = currentPosition.Y - _startPointUserControl.Y; var newX = Canvas.GetLeft(_currentDraggingControl) + dx; var newY = Canvas.GetTop(_currentDraggingControl) + dy; if (_currentDraggingControl is IDataContainer dataContainer) dataContainer.Move((float)newX,(float) newY); _startPointUserControl = currentPosition; // Actualiza el punto inicial para el siguiente movimiento } else if (_isRotatingUserControl) { // Código para rotar el control RotateControl(_currentDraggingControl, currentPosition); } else if (_isResizingUserControl) { // Código para cambiar el tamaño del control ResizeControl(_currentDraggingControl, currentPosition); } } } private void RotateControl(UserControl control, Point currentPosition) { double deltaX = currentPosition.X - _startPointUserControl.X; double deltaY = currentPosition.Y - _startPointUserControl.Y; double angle = Math.Atan2(deltaY, deltaX) * (180 / Math.PI); //RotateTransform rotateTransform = control.RenderTransform as RotateTransform; //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((float)angle); // Actualizar el ángulo mostrado _angleDisplayTextBlock.Text = $"Ángulo: {angle:F2}°"; PositionAngleDisplay(control); } private void PositionAngleDisplay(UserControl control) { // Posicionar el TextBlock sobre el control Canvas.SetLeft(_angleDisplayTextBlock, Canvas.GetLeft(control)); Canvas.SetTop(_angleDisplayTextBlock, Canvas.GetTop(control)); } private void ResizeControl(UserControl control, Point currentPosition) { // Calcular la diferencia en la posición X desde el punto de inicio double widthChange = currentPosition.X - _startPointUserControl.X; // Actualizar el ancho del control double newWidth = Math.Max(control.ActualWidth + widthChange, control.MinWidth); control.Width = newWidth; // Asegurar que no sea menor que el mínimo if (control is IDataContainer dataContainer) dataContainer.Resize((float)newWidth, 0); // Actualizar el punto de inicio para el próximo evento de movimiento _startPointUserControl = currentPosition; } private void UserControl_MouseEnter(object sender, MouseEventArgs e) { if (sender is UserControl userControl) if (userControl is IDataContainer dataContainer) dataContainer.Highlight(true); } private void UserControl_MouseLeave(object sender, MouseEventArgs e) { if (sender is UserControl userControl) if (userControl is IDataContainer dataContainer) dataContainer.Highlight(false); } private void ViewModel_ImageSelected(object sender, string imagePath) { LoadImageToCanvas(imagePath); } private void LoadImageToCanvas(string imagePath) { BitmapImage bitmap = new BitmapImage(new Uri(imagePath, UriKind.Absolute)); if (imagenDeFondo == null) { imagenDeFondo = new Image(); ImagenEnTrabajoCanvas.Children.Add(imagenDeFondo); } imagenDeFondo.Source = bitmap; RenderOptions.SetBitmapScalingMode(imagenDeFondo, BitmapScalingMode.HighQuality); // Elimina solo los ROIs, no la imagen de fondo for (int i = ImagenEnTrabajoCanvas.Children.Count - 1; i >= 0; i--) { if (ImagenEnTrabajoCanvas.Children[i] is not Image) { ImagenEnTrabajoCanvas.Children.RemoveAt(i); } } // Ajusta el tamaño del Canvas a la imagen, si es necesario ImagenEnTrabajoCanvas.Width = bitmap.Width; ImagenEnTrabajoCanvas.Height = bitmap.Height; // Posiciona la imagen correctamente dentro del Canvas Canvas.SetLeft(imagenDeFondo, 0); Canvas.SetTop(imagenDeFondo, 0); } private void Canvas_MouseUp_Panning(object sender, MouseButtonEventArgs e) { if (_isDraggingCanvas) { _isDraggingCanvas = false; ImagenEnTrabajoScrollViewer.ReleaseMouseCapture(); // Finaliza la captura del ratón } } private void Canvas_MouseDown_Panning(object sender, MouseButtonEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas && !_isMovingUserControl) { // Indica que se inicia el panning _isDraggingCanvas = true; // Guarda la posición actual del ratón _lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer); //ImagenEnTrabajoScrollViewer.CaptureMouse(); // Importante para capturar el movimiento } } private void Canvas_MouseMove_Panning(object sender, MouseEventArgs e) { if (_isDraggingCanvas && !_isDrawingCanvas) { // Calcula el nuevo desplazamiento basado en el movimiento del ratón var currentPosition = e.GetPosition(ImagenEnTrabajoScrollViewer); var dx = currentPosition.X - _lastMousePosition.X; var dy = currentPosition.Y - _lastMousePosition.Y; // Ajusta el desplazamiento del ScrollViewer ImagenEnTrabajoScrollViewer.ScrollToHorizontalOffset(ImagenEnTrabajoScrollViewer.HorizontalOffset - dx); ImagenEnTrabajoScrollViewer.ScrollToVerticalOffset(ImagenEnTrabajoScrollViewer.VerticalOffset - dy); // Actualiza la posición del ratón para el próximo movimiento _lastMousePosition = currentPosition; } } private void ImagenEnTrabajoCanvas_MouseWheel(object sender, MouseWheelEventArgs e) { var st = (ScaleTransform)ImagenEnTrabajoCanvas.LayoutTransform; double zoomFactor = e.Delta > 0 ? 1.1 : 0.9; Point cursorPosition = e.GetPosition(ImagenEnTrabajoScrollViewer); // Calcular el punto focal del zoom relativo al contenido del ScrollViewer var absoluteX = ImagenEnTrabajoScrollViewer.HorizontalOffset + cursorPosition.X; var absoluteY = ImagenEnTrabajoScrollViewer.VerticalOffset + cursorPosition.Y; // Aplicar el zoom st.ScaleX *= zoomFactor; st.ScaleY *= zoomFactor; // Calcular el nuevo desplazamiento para que el zoom se centre en la posición del cursor ImagenEnTrabajoScrollViewer.UpdateLayout(); // Asegurarse de que el layout del ScrollViewer esté actualizado var newHorizontalOffset = absoluteX * zoomFactor - cursorPosition.X; var newVerticalOffset = absoluteY * zoomFactor - cursorPosition.Y; // Aplicar el nuevo desplazamiento ImagenEnTrabajoScrollViewer.ScrollToHorizontalOffset(newHorizontalOffset); ImagenEnTrabajoScrollViewer.ScrollToVerticalOffset(newVerticalOffset); e.Handled = true; // Evita el procesamiento adicional del evento } private void ListaOs_SelectionChanged(object sender, SelectionChangedEventArgs e) { PanelEdicion.Children.Clear(); // Limpiar el panel existente if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject) CargarPropiedadesosDatos(selectedObject); } private void CargarPropiedadesosDatos(osBase selectedObject) { PanelEdicion.Children.Clear(); var properties = selectedObject.GetType().GetProperties(); foreach (var property in properties) { if (Attribute.IsDefined(property, typeof(HiddenAttribute))) { continue; } var grid = new Grid(); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); var label = new Label { Content = property.Name + ":", Margin = new Thickness(0, 0, 5, 0), VerticalAlignment = VerticalAlignment.Center }; if (property.PropertyType == typeof(float) || property.PropertyType == typeof(string) || property.PropertyType == typeof(int)) { var textBox = new TextBox { Margin = new Thickness(0), MinWidth = 200, VerticalContentAlignment = VerticalAlignment.Center }; var binding = new Binding(property.Name) { Source = selectedObject, Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.LostFocus }; if (property.PropertyType == typeof(float)) { binding.Converter = (FloatToFormattedStringConverter)Resources["floatFormatter"]; } textBox.SetBinding(TextBox.TextProperty, binding); Grid.SetColumn(label, 0); Grid.SetColumn(textBox, 1); grid.Children.Add(label); grid.Children.Add(textBox); } else if (property.PropertyType == typeof(bool)) { var checkBox = new CheckBox { Margin = new Thickness(5, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center }; var binding = new Binding(property.Name) { Source = selectedObject, Mode = BindingMode.TwoWay }; checkBox.SetBinding(CheckBox.IsCheckedProperty, binding); Grid.SetColumn(label, 0); Grid.SetColumn(checkBox, 1); grid.Children.Add(label); grid.Children.Add(checkBox); } else if (property.PropertyType == typeof(Brush)) { var listBox = new ListBox { ItemsSource = new List { "Rojo", "Azul", "Negro", "Verde", "Gris" }, Margin = new Thickness(0), MinWidth = 200 }; listBox.SelectionChanged += (sender, e) => { if (listBox.SelectedItem != null) { switch (listBox.SelectedItem.ToString()) { case "Rojo": property.SetValue(selectedObject, Brushes.Red); break; case "Azul": property.SetValue(selectedObject, Brushes.Blue); break; case "Negro": property.SetValue(selectedObject, Brushes.Black); break; case "Verde": property.SetValue(selectedObject, Brushes.Green); break; case "Gris": property.SetValue(selectedObject, Brushes.Gray); break; } } }; var binding = new Binding(property.Name) { Source = selectedObject, Mode = BindingMode.TwoWay, Converter = new BrushToColorNameConverter() }; listBox.SetBinding(ListBox.SelectedItemProperty, binding); Grid.SetColumn(label, 0); Grid.SetColumn(listBox, 1); grid.Children.Add(label); grid.Children.Add(listBox); } PanelEdicion.Children.Add(grid); } } private void MainWindow_Closed(object sender, EventArgs e) { if (DataContext is MainViewModel viewModel) { viewModel.ImageSelected -= ViewModel_ImageSelected; } } } public class FloatValidationRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { // Comprobamos si el valor es nulo o está vacío if (string.IsNullOrEmpty(value?.ToString())) return new ValidationResult(false, "El campo no puede estar vacío."); // Intentamos convertir el valor a un tipo float if (float.TryParse(value.ToString(), NumberStyles.Float, cultureInfo, out float result)) return ValidationResult.ValidResult; // La validación es exitosa else return new ValidationResult(false, "Ingrese un número válido."); } } }