From f85c707cfc27b964b5878a144c575a0aabbb849e Mon Sep 17 00:00:00 2001 From: Miguel Date: Sat, 14 Jun 2025 22:48:00 +0200 Subject: [PATCH] =?UTF-8?q?Se=20a=C3=B1adieron=20mejoras=20en=20la=20gesti?= =?UTF-8?q?=C3=B3n=20de=20la=20manipulaci=C3=B3n=20de=20objetos=20rotados,?= =?UTF-8?q?=20incluyendo=20m=C3=A9todos=20para=20adaptar=20los=20cursores?= =?UTF-8?q?=20y=20el=20comportamiento=20de=20los=20handles=20seg=C3=BAn=20?= =?UTF-8?q?la=20rotaci=C3=B3n=20del=20objeto.=20Se=20implement=C3=B3=20la?= =?UTF-8?q?=20transformaci=C3=B3n=20de=20cambios=20del=20mouse=20en=20coor?= =?UTF-8?q?denadas=20locales=20para=20un=20redimensionamiento=20m=C3=A1s?= =?UTF-8?q?=20intuitivo.=20Adem=C3=A1s,=20se=20documentaron=20las=20nuevas?= =?UTF-8?q?=20funcionalidades=20en=20la=20clase=20ObjectManipulationManage?= =?UTF-8?q?r.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ObjectManipulationManager.cs | 156 +++++++++++++++++++++++++++++++++-- 1 file changed, 151 insertions(+), 5 deletions(-) diff --git a/ObjectManipulationManager.cs b/ObjectManipulationManager.cs index a63f41b..6501644 100644 --- a/ObjectManipulationManager.cs +++ b/ObjectManipulationManager.cs @@ -79,6 +79,19 @@ namespace CtrEditor JoinVertically } + /// + /// Gestor de manipulación de objetos que maneja la selección, movimiento, rotación y redimensionamiento de objetos. + /// + /// MEJORAS PARA OBJETOS ROTADOS: + /// - HandleResizeForObject: Aplica transformaciones matemáticas para convertir los cambios del mouse + /// desde coordenadas del canvas a coordenadas locales del objeto rotado. + /// - DetermineHandleMode: Adapta el comportamiento de los handles según la rotación del objeto. + /// - GetAdaptiveCursors: Cambia los cursores de los handles para reflejar la orientación actual del objeto. + /// + /// Estas mejoras hacen que el redimensionamiento sea más intuitivo cuando los objetos están rotados, + /// ya que los handles responden de acuerdo a la orientación del objeto en lugar de las coordenadas + /// absolutas del canvas. + /// public class ObjectManipulationManager { private enum ResizeMode @@ -371,19 +384,22 @@ namespace CtrEditor // Calcular el offset para posicionar los manejadores fuera del rectángulo double offset = rectSize / 2; + // Determinar cursores adaptativos según la rotación del objeto + var cursors = GetAdaptiveCursors(); + var positions = new List<(Point Position, string Tag, Cursor Cursor, Brush Stroke)> { // Esquinas - mover hacia afuera del rectángulo (new Point(rectBox.Left - offset, rectBox.Top - offset), "TopLeft", Cursors.Arrow, Brushes.Gray), (new Point(rectBox.Right + offset, rectBox.Top - offset), "TopRight", rotationCursorRx, Brushes.Red), (new Point(rectBox.Left - offset, rectBox.Bottom + offset), "BottomLeft", rotationCursorSx, Brushes.DarkRed), - (new Point(rectBox.Right + offset, rectBox.Bottom + offset), "BottomRight", Cursors.SizeNWSE, Brushes.Blue), + (new Point(rectBox.Right + offset, rectBox.Bottom + offset), "BottomRight", cursors.ResizeBoth, Brushes.Blue), // Centros de los bordes - mover hacia afuera del rectángulo (new Point(rectBox.Left + rectBox.Width / 2, rectBox.Top - offset), "TopCenter", Cursors.Arrow, Brushes.Gray), - (new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom + offset), "BottomCenter", Cursors.SizeNS, Brushes.Blue), + (new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom + offset), "BottomCenter", cursors.ResizeVertical, Brushes.Blue), (new Point(rectBox.Left - offset, rectBox.Top + rectBox.Height / 2), "CenterLeft", rotationCursorRx, Brushes.Red), - (new Point(rectBox.Right + offset, rectBox.Top + rectBox.Height / 2), "CenterRight", Cursors.SizeWE, Brushes.Blue) + (new Point(rectBox.Right + offset, rectBox.Top + rectBox.Height / 2), "CenterRight", cursors.ResizeHorizontal, Brushes.Blue) }; foreach (var (Position, Tag, Cursor, Stroke) in positions) @@ -395,6 +411,43 @@ namespace CtrEditor } } + private (Cursor ResizeHorizontal, Cursor ResizeVertical, Cursor ResizeBoth) GetAdaptiveCursors() + { + // Para un solo objeto o múltiples objetos con la misma rotación + var firstObject = _selectedObjects.FirstOrDefault(); + if (firstObject == null) + 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) || + (angle > 165 && angle < 195)) + { + return (Cursors.SizeWE, Cursors.SizeNS, Cursors.SizeNWSE); + } + else if ((angle > 75 && angle < 105) || (angle > 255 && angle < 285)) + { + // Objeto rotado 90 grados - intercambiar cursores + return (Cursors.SizeNS, Cursors.SizeWE, Cursors.SizeNESW); + } + else if ((angle > 30 && angle < 60) || (angle > 210 && angle < 240)) + { + // Objeto rotado aproximadamente 45 grados + return (Cursors.SizeNESW, Cursors.SizeNWSE, Cursors.SizeAll); + } + else if ((angle > 120 && angle < 150) || (angle > 300 && angle < 330)) + { + // Objeto rotado aproximadamente 135 grados + return (Cursors.SizeNWSE, Cursors.SizeNESW, Cursors.SizeAll); + } + else + { + // Para otros ángulos, usar cursores generales + return (Cursors.SizeAll, Cursors.SizeAll, Cursors.SizeAll); + } + } + private Rectangle CreateResizeHandle(double rectSize, string tag, Cursor cursor, Brush stroke) { var handle = new Rectangle @@ -921,6 +974,39 @@ namespace CtrEditor } private HandleMode DetermineHandleMode(string resizeDirection) + { + // Si tenemos múltiples objetos seleccionados con diferentes rotaciones, + // usar el comportamiento por defecto + if (_selectedObjects.Count > 1) + { + var angles = _selectedObjects.Select(obj => Math.Abs(obj.Angulo % 360)).Distinct().ToList(); + if (angles.Count > 1) // Diferentes ángulos + { + return DetermineHandleModeDefault(resizeDirection); + } + } + + // Para un solo objeto o múltiples objetos con la misma rotación + var firstObject = _selectedObjects.FirstOrDefault(); + if (firstObject == null) + 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) || + (angle > 255 && angle < 285)) + { + return DetermineHandleModeDefault(resizeDirection); + } + + // Para objetos rotados significativamente, adaptar los handles + return DetermineHandleModeRotated(resizeDirection, angle); + } + + private HandleMode DetermineHandleModeDefault(string resizeDirection) { return resizeDirection switch { @@ -934,6 +1020,21 @@ namespace CtrEditor }; } + private HandleMode DetermineHandleModeRotated(string resizeDirection, double angle) + { + // Para objetos rotados, hacer que todos los handles de redimensionamiento funcionen como los corners + // Esto es mucho más intuitivo ya que el usuario puede redimensionar en cualquier dirección + // sin preocuparse por la orientación específica del objeto + + return resizeDirection switch + { + "TopLeft" => HandleMode.None, + "TopRight" or "BottomLeft" or "CenterLeft" => HandleMode.Rotate, + "BottomRight" or "BottomCenter" or "CenterRight" or "TopCenter" => HandleMode.ResizeBoth, + _ => HandleMode.None + }; + } + private void HandleResize(Point currentPosition, HandleMode mode) { PurgeDeletedObjects(); @@ -960,8 +1061,53 @@ namespace CtrEditor private void HandleResizeForObject(osBase obj, Point currentPosition, bool activateSizeWidth, bool activateSizeHeight) { - double widthChange = activateSizeWidth ? currentPosition.X - _startPointUserControl.X : 0; - double heightChange = activateSizeHeight ? currentPosition.Y - _startPointUserControl.Y : 0; + // Calcular el cambio del mouse en coordenadas del canvas + double deltaX = currentPosition.X - _startPointUserControl.X; + double deltaY = currentPosition.Y - _startPointUserControl.Y; + + // Inicializar variables de cambio + double widthChange = 0; + double heightChange = 0; + + // Si el objeto no está rotado, usar el método original + if (Math.Abs(obj.Angulo) < 0.1) // Umbral pequeño para considerar "sin rotación" + { + widthChange = activateSizeWidth ? deltaX : 0; + heightChange = activateSizeHeight ? deltaY : 0; + } + else + { + // 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; + + // Determinar qué componente aplicar según el tipo de redimensionamiento + if (activateSizeWidth && activateSizeHeight) + { + // Redimensionamiento proporcional - usar la componente más grande transformada + widthChange = localDeltaX; + heightChange = localDeltaY; + } + else if (activateSizeWidth) + { + // Solo redimensionar ancho - usar componente X transformada + widthChange = localDeltaX; + } + else if (activateSizeHeight) + { + // Solo redimensionar alto - usar componente Y transformada + heightChange = localDeltaY; + } + } obj.Resize(widthChange, heightChange); }