diff --git a/ObjectManipulationManager.cs b/ObjectManipulationManager.cs index 97022ce..b2605d9 100644 --- a/ObjectManipulationManager.cs +++ b/ObjectManipulationManager.cs @@ -173,8 +173,8 @@ namespace CtrEditor private void PurgeDeletedObjects() { - var deletedObjects = _selectedObjects.Where(obj => - obj.VisualRepresentation == null || + var deletedObjects = _selectedObjects.Where(obj => + obj.VisualRepresentation == null || !_canvas.Children.Contains(obj.VisualRepresentation)).ToList(); foreach (var obj in deletedObjects) @@ -202,7 +202,7 @@ namespace CtrEditor PurgeDeletedObjects(); // Asegurarse de que el canvas haya actualizado su layout _canvas.UpdateLayout(); - + RemoveResizeRectangles(); if (_selectedObjects.Any()) { @@ -253,14 +253,15 @@ namespace CtrEditor public void AddResizeRectangles(IEnumerable selectedObjects) { double rectHighlightSize = 1; - RemoveResizeRectangles(); + RemoveResizeRectangles(); // Verificar si hay objetos bloqueados bool hasLockedObjects = selectedObjects.Any(obj => obj.Lock_movement); // Calcular el bounding box que contenga todos los objetos seleccionados Rect boundingBox = CalculateTotalBoundingBox(selectedObjects); - if (_selectedObjectsAreVisible) { + if (_selectedObjectsAreVisible) + { FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(boundingBox); rectBox.Left -= (float)rectHighlightSize; @@ -303,7 +304,7 @@ namespace CtrEditor foreach (var obj in selectedObjects) { - if (obj.VisualRepresentation != null && obj.VisualRepresentation.Visibility!=Visibility.Collapsed) + if (obj.VisualRepresentation != null && obj.VisualRepresentation.Visibility != Visibility.Collapsed) { // Obtener el bounding box del objeto actual Rect objectBounds = VisualTreeHelper.GetDescendantBounds(obj.VisualRepresentation); @@ -336,7 +337,7 @@ namespace CtrEditor // Cambiar el estilo visual dependiendo si hay objetos bloqueados Brush strokeBrush; DoubleCollection dashArray; - + if (hasLockedObjects) { // Estilo para objetos bloqueados (rojo/naranja con líneas más separadas) @@ -377,10 +378,10 @@ namespace CtrEditor // Calcular el tamaño apropiado para los manejadores basado en el tamaño del objeto double minObjectDimension = Math.Min(rectBox.Width, rectBox.Height); double rectSize = Math.Min(defaultRectSize, minObjectDimension / 3); - + // Asegurar un tamaño mínimo para los manejadores rectSize = Math.Max(rectSize, 6); - + // Calcular el offset para posicionar los manejadores fuera del rectángulo double offset = rectSize / 2; @@ -419,9 +420,9 @@ namespace CtrEditor return (Cursors.SizeWE, Cursors.SizeNS, Cursors.SizeNWSE); double angle = Math.Abs(firstObject.Angulo % 360); - + // Si el objeto no está significativamente rotado, usar cursores estándar - if (angle < 15 || (angle > 345 && angle < 360) || + if (angle < 15 || (angle > 345 && angle < 360) || (angle > 165 && angle < 195)) { return (Cursors.SizeWE, Cursors.SizeNS, Cursors.SizeNWSE); @@ -569,7 +570,7 @@ namespace CtrEditor } UpdateSelectionVisuals(); - + // Update the view model's selection state vm.NotifySelectionChanged(); } @@ -583,7 +584,7 @@ namespace CtrEditor _selectedObjects.Remove(obj); obj.IsSelected = false; RemoveSelectionHighlight(obj.VisualRepresentation); - + // Update the view model's selection state if (_mainWindow.DataContext is MainViewModel vm) { @@ -652,7 +653,7 @@ namespace CtrEditor Width = transformedBoundingBox.Width, Height = transformedBoundingBox.Height, Fill = Brushes.Transparent, - Stroke = new SolidColorBrush(isReference ? + Stroke = new SolidColorBrush(isReference ? Color.FromArgb(180, 128, 0, 128) : // Purple for reference Color.FromArgb(180, 255, 0, 0)), // Red for others StrokeThickness = isReference ? 3 : 2, // Más grueso para el de referencia @@ -715,7 +716,7 @@ namespace CtrEditor { // Verificar si hay objetos bloqueados en la selección actual bool hasLockedObjects = _selectedObjects.Any(obj => obj.Lock_movement); - + if (_resizeRectangles != null && _resizeRectangles.Contains(sender)) { // Si hay objetos bloqueados, no permitir redimensionamiento @@ -724,10 +725,10 @@ namespace CtrEditor e.Handled = true; return; } - + // Capturar estado antes de iniciar redimensionamiento CaptureUndoState(); - + _currentDraggingRectangle = sender as Rectangle; _lastMousePosition = e.GetPosition(_canvas); _currentDraggingRectangle.CaptureMouse(); @@ -741,10 +742,10 @@ namespace CtrEditor if (userControl != null && userControl.DataContext is osBase datos) { bool isControlPressed = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl); - + // Determinar qué objetos serán afectados por el movimiento List objectsToMove = new List(); - + if (!isControlPressed) { // Si no está Ctrl presionado y vamos a hacer dragging @@ -758,28 +759,28 @@ namespace CtrEditor // Si no está seleccionado, solo vamos a mover este objeto objectsToMove.Add(datos); } - + // Verificar si alguno de los objetos a mover está bloqueado bool willMoveLockedObjects = objectsToMove.Any(obj => obj.Lock_movement); - + if (!willMoveLockedObjects) { // Capturar estado antes de iniciar movimiento CaptureUndoStateForObjects(objectsToMove); - + userControl.CaptureMouse(); _currentDraggingControl = userControl; _isMovingUserControl = true; InitializeDrag(e); } } - + // Manejar la selección después de capturar el estado HandleObjectSelection(userControl, datos); - + // Actualizar el estado de objetos bloqueados después de la selección hasLockedObjects = _selectedObjects.Any(obj => obj.Lock_movement); - + if (hasLockedObjects && !isControlPressed) { // Si hay objetos bloqueados, no permitir el inicio del arrastre @@ -831,14 +832,14 @@ namespace CtrEditor private void HandleDrag(Point currentPosition) { PurgeDeletedObjects(); - + // Verificar si hay objetos bloqueados antes de mover bool hasLockedObjects = _selectedObjects.Any(obj => obj.Lock_movement); if (hasLockedObjects) { return; // No mover si hay objetos bloqueados } - + var dx = currentPosition.X - _startPointUserControl.X; var dy = currentPosition.Y - _startPointUserControl.Y; @@ -992,11 +993,11 @@ namespace CtrEditor return HandleMode.None; double angle = Math.Abs(firstObject.Angulo % 360); - + // Si el objeto no está significativamente rotado, usar el comportamiento por defecto - if (angle < 15 || (angle > 345 && angle < 360) || - (angle > 75 && angle < 105) || - (angle > 165 && angle < 195) || + if (angle < 15 || (angle > 345 && angle < 360) || + (angle > 75 && angle < 105) || + (angle > 165 && angle < 195) || (angle > 255 && angle < 285)) { return DetermineHandleModeDefault(resizeDirection); @@ -1038,14 +1039,14 @@ namespace CtrEditor private void HandleResize(Point currentPosition, HandleMode mode) { PurgeDeletedObjects(); - + // Verificar si hay objetos bloqueados antes de redimensionar bool hasLockedObjects = _selectedObjects.Any(obj => obj.Lock_movement); if (hasLockedObjects) { return; // No redimensionar si hay objetos bloqueados } - + RemoveAllSelectionHighlights(); // Remover antes de redimensionar foreach (var selectedObject in _selectedObjects) @@ -1080,13 +1081,13 @@ namespace CtrEditor // Para objetos rotados, transformar los cambios del mouse según la orientación del objeto // Convertir ángulo de grados a radianes double angleRad = obj.Angulo * Math.PI / 180.0; - + // Calcular los componentes de cambio transformados según la rotación // Rotar el vector de cambio por el ángulo negativo del objeto para obtener // los cambios en el sistema de coordenadas local del objeto double cos = Math.Cos(-angleRad); double sin = Math.Sin(-angleRad); - + double localDeltaX = deltaX * cos - deltaY * sin; double localDeltaY = deltaX * sin + deltaY * cos; @@ -1115,16 +1116,19 @@ namespace CtrEditor private void HandleRotation(Point currentPosition) { PurgeDeletedObjects(); - + // Verificar si hay objetos bloqueados antes de rotar bool hasLockedObjects = _selectedObjects.Any(obj => obj.Lock_movement); if (hasLockedObjects) { return; // No rotar si hay objetos bloqueados } - + RemoveAllSelectionHighlights(); // Remover antes de rotar + // Verificar si la tecla Shift está presionada para rotación en incrementos de 45 grados + bool isShiftPressed = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); + // Calcular el ángulo respecto al centro del bounding box que contiene todos los objetos seleccionados double deltaX = currentPosition.X - _transformedBoundingBoxCenter.X; double deltaY = currentPosition.Y - _transformedBoundingBoxCenter.Y; @@ -1137,11 +1141,48 @@ namespace CtrEditor else { double deltaAngle = angle - _lastAngle; - foreach (var selectedObject in _selectedObjects) + + if (isShiftPressed) { - selectedObject.Rotate(deltaAngle); + // Rotación en incrementos de 45 grados + // Calcular el ángulo total acumulado desde el inicio de la rotación + double totalAngleChange = angle - _lastAngle; + + // Determinar a qué incremento de 45 grados corresponde + double snapAngle = Math.Round(totalAngleChange / 45.0) * 45.0; + + // Solo aplicar la rotación si hay un cambio significativo (al menos 22.5 grados de movimiento) + if (Math.Abs(totalAngleChange) > 22.5) + { + // Calcular el cambio real que necesitamos aplicar + double actualRotationChange = snapAngle; + + foreach (var selectedObject in _selectedObjects) + { + // Obtener el ángulo actual del objeto + double currentObjectAngle = selectedObject.Angulo; + + // Calcular el nuevo ángulo alineado a incrementos de 45 grados + double targetAngle = Math.Round((currentObjectAngle + actualRotationChange) / 45.0) * 45.0; + + // Aplicar la rotación necesaria para llegar al ángulo objetivo + double rotationNeeded = targetAngle - currentObjectAngle; + selectedObject.Rotate(rotationNeeded); + } + + // Actualizar el ángulo de referencia para evitar aplicar la misma rotación múltiples veces + _lastAngle = (float)angle; + } + } + else + { + // Rotación libre (comportamiento original) + foreach (var selectedObject in _selectedObjects) + { + selectedObject.Rotate(deltaAngle); + } + _lastAngle = (float)angle; } - _lastAngle = (float)angle; } UpdateAllSelectionHighlights(); @@ -1237,10 +1278,10 @@ namespace CtrEditor ClearSelection(); RemoveResizeRectangles(); RemoveAllSelectionHighlights(); - + // Ensure the property panel is cleared by explicitly setting SelectedItemOsList to null viewModel.SelectedItemOsList = null; - + // Actualizar el estado de cambios sin guardar if (viewModel != null) { @@ -1339,7 +1380,7 @@ namespace CtrEditor { // Capturar solo el estado de los objetos seleccionados var undoState = new UndoState(_selectedObjects); - + // Mantener solo los últimos MaxUndoSteps estados if (_undoHistory.Count >= MaxUndoSteps) { @@ -1353,7 +1394,7 @@ namespace CtrEditor _undoHistory.Push(state); } } - + _undoHistory.Push(undoState); Console.WriteLine($"Estado capturado para undo de {_selectedObjects.Count} objetos. Historial: {_undoHistory.Count} estados"); } @@ -1374,7 +1415,7 @@ namespace CtrEditor { // Capturar solo el estado de los objetos especificados var undoState = new UndoState(objectsList); - + // Mantener solo los últimos MaxUndoSteps estados if (_undoHistory.Count >= MaxUndoSteps) { @@ -1388,7 +1429,7 @@ namespace CtrEditor _undoHistory.Push(state); } } - + _undoHistory.Push(undoState); Console.WriteLine($"Estado capturado para undo de {objectsList.Count} objetos específicos. Historial: {_undoHistory.Count} estados"); } @@ -1417,7 +1458,7 @@ namespace CtrEditor if (_mainWindow.DataContext is MainViewModel viewModel) { _isApplyingUndo = true; - + try { var undoState = _undoHistory.Pop(); @@ -1443,10 +1484,10 @@ namespace CtrEditor // Actualizar los visuales de selección UpdateSelectionVisuals(); - + // Marcar como cambios sin guardar viewModel.HasUnsavedChanges = true; - + Console.WriteLine("Undo aplicado exitosamente"); } finally