Se añadieron mejoras en la gestión de la manipulación de objetos rotados, incluyendo métodos para adaptar los cursores y el comportamiento de los handles según la rotación del objeto. Se implementó la transformación de cambios del mouse en coordenadas locales para un redimensionamiento más intuitivo. Además, se documentaron las nuevas funcionalidades en la clase ObjectManipulationManager.

This commit is contained in:
Miguel 2025-06-14 22:48:00 +02:00
parent 88e6de77cb
commit f85c707cfc
1 changed files with 151 additions and 5 deletions

View File

@ -79,6 +79,19 @@ namespace CtrEditor
JoinVertically JoinVertically
} }
/// <summary>
/// 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.
/// </summary>
public class ObjectManipulationManager public class ObjectManipulationManager
{ {
private enum ResizeMode private enum ResizeMode
@ -371,19 +384,22 @@ namespace CtrEditor
// Calcular el offset para posicionar los manejadores fuera del rectángulo // Calcular el offset para posicionar los manejadores fuera del rectángulo
double offset = rectSize / 2; 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)> var positions = new List<(Point Position, string Tag, Cursor Cursor, Brush Stroke)>
{ {
// Esquinas - mover hacia afuera del rectángulo // Esquinas - mover hacia afuera del rectángulo
(new Point(rectBox.Left - offset, rectBox.Top - offset), "TopLeft", Cursors.Arrow, Brushes.Gray), (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.Right + offset, rectBox.Top - offset), "TopRight", rotationCursorRx, Brushes.Red),
(new Point(rectBox.Left - offset, rectBox.Bottom + offset), "BottomLeft", rotationCursorSx, Brushes.DarkRed), (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 // 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.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.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) 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) private Rectangle CreateResizeHandle(double rectSize, string tag, Cursor cursor, Brush stroke)
{ {
var handle = new Rectangle var handle = new Rectangle
@ -921,6 +974,39 @@ namespace CtrEditor
} }
private HandleMode DetermineHandleMode(string resizeDirection) 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 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) private void HandleResize(Point currentPosition, HandleMode mode)
{ {
PurgeDeletedObjects(); PurgeDeletedObjects();
@ -960,8 +1061,53 @@ namespace CtrEditor
private void HandleResizeForObject(osBase obj, Point currentPosition, bool activateSizeWidth, bool activateSizeHeight) private void HandleResizeForObject(osBase obj, Point currentPosition, bool activateSizeWidth, bool activateSizeHeight)
{ {
double widthChange = activateSizeWidth ? currentPosition.X - _startPointUserControl.X : 0; // Calcular el cambio del mouse en coordenadas del canvas
double heightChange = activateSizeHeight ? currentPosition.Y - _startPointUserControl.Y : 0; 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); obj.Resize(widthChange, heightChange);
} }