diff --git a/CtrEditor.csproj b/CtrEditor.csproj index ee4c224..7b6fce2 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -12,6 +12,7 @@ + diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index 8676782..fb761af 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Globalization; using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Windows.Controls; +using System.Windows.Data; using static System.Runtime.InteropServices.JavaScript.JSType; namespace CtrEditor.ObjetosSim @@ -80,6 +82,21 @@ namespace CtrEditor.ObjetosSim } } + public class MeterToPixelConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + float meters = (float)value; + return PixelToMeter.Instance.calc.MetersToPixels(meters); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + float pixels = (float)value; + return PixelToMeter.Instance.calc.PixelsToMeters(pixels); + } + } + public class UnitConverter { // La escala representa cuántos metros hay en un píxel diff --git a/ObjetosSim/ucTransporteTTop.xaml.cs b/ObjetosSim/ucTransporteTTop.xaml.cs index ac0a356..946e5b3 100644 --- a/ObjetosSim/ucTransporteTTop.xaml.cs +++ b/ObjetosSim/ucTransporteTTop.xaml.cs @@ -89,6 +89,7 @@ namespace CtrEditor.ObjetosSim set { Data.Length = value; + OnPropertyChanged(nameof(AnchoPixels)); OnPropertyChanged(nameof(Ancho)); } } @@ -97,6 +98,7 @@ namespace CtrEditor.ObjetosSim set { Data.Width = value; + OnPropertyChanged(nameof(AltoPixels)); OnPropertyChanged(nameof(Alto)); } } @@ -106,6 +108,7 @@ namespace CtrEditor.ObjetosSim set { Data.Length = (float)PixelToMeter.Instance.calc.PixelsToMeters(value); + OnPropertyChanged(nameof(AnchoPixels)); OnPropertyChanged(nameof(Ancho)); } } @@ -115,6 +118,7 @@ namespace CtrEditor.ObjetosSim set { Data.Width = (float)PixelToMeter.Instance.calc.PixelsToMeters(value); + OnPropertyChanged(nameof(AltoPixels)); OnPropertyChanged(nameof(Alto)); } } @@ -182,7 +186,7 @@ namespace CtrEditor.ObjetosSim public void Resize(float width, float height) { if (Datos is osTransporteTTop datos) - datos.Ancho = width; + datos.AnchoPixels = width; } public void Move(float LeftPixels, float TopPixels) { diff --git a/Simulacion/GeometrySimulator.cs b/Simulacion/GeometrySimulator.cs index fe1853c..533411d 100644 --- a/Simulacion/GeometrySimulator.cs +++ b/Simulacion/GeometrySimulator.cs @@ -1,4 +1,5 @@ using CtrEditor.ObjetosSim; +using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics.Eventing.Reader; @@ -44,7 +45,7 @@ public class Circle foreach (var rectangle in rectangles) { float overlap = CalculateOverlapPercentage(this, rectangle); - if (overlap > 0) + if (overlap > 10) { Overlap += overlap; isTracted = true; // El círculo está siendo traccionado por un rectángulo @@ -196,82 +197,59 @@ public class Circle return angle < 90 ? 90 - angle : angle - 90; } - public float CalculateOverlapPercentage(Circle circle, Rectangle rectangle) + public static float CalculateOverlapPercentage(Circle circle, Rectangle rectangle) { - // Convertir ángulo del rectángulo de grados a radianes - float angleRadians = (float)(rectangle.Angle * Math.PI / 180); - - // Centro del círculo - Vector2 circleCenter = new Vector2(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2); - float radius = circle.Diameter / 2; - - // Pivot del rectángulo es el Top Left - Vector2 rectPivot = new Vector2(rectangle.Left, rectangle.Top); - - // Rotar el centro del círculo respecto al pivote del rectángulo - Vector2 rotatedCircleCenter = RotatePoint(circleCenter, rectPivot, -angleRadians); - - // Comprobar si el círculo rotado intersecta con el rectángulo alineado - // Rectángulo "alineado" asume que después de rotar el círculo, el rectángulo se comporta como si estuviera alineado con los ejes - if (IsCircleRectangleIntersecting(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width)) - { - float overlapArea = EstimateOverlapArea(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width); - float circleArea = (float)(Math.PI * radius * radius); - return (overlapArea / circleArea) * 100; - } - return 0; - } - - - private bool IsCircleRectangleIntersecting(Vector2 circleCenter, float radius, Vector2 rectTopLeft, float length, float width) - { - float closestX = Math.Max(rectTopLeft.X, Math.Min(circleCenter.X, rectTopLeft.X + length)); - float closestY = Math.Max(rectTopLeft.Y, Math.Min(circleCenter.Y, rectTopLeft.Y + width)); - - float distanceX = circleCenter.X - closestX; - float distanceY = circleCenter.Y - closestY; - - return (distanceX * distanceX + distanceY * distanceY) < (radius * radius); - } - - - - private float EstimateOverlapArea(Vector2 circleCenter, float radius, Vector2 rectTopLeft, float length, float width) - { - float rectRight = rectTopLeft.X + length; - float rectBottom = rectTopLeft.Y + width; - float distToLeft = Math.Max(0, rectTopLeft.X - circleCenter.X); - float distToRight = Math.Max(0, circleCenter.X - rectRight); - float distToTop = Math.Max(0, rectTopLeft.Y - circleCenter.Y); - float distToBottom = Math.Max(0, circleCenter.Y - rectBottom); - float distToNearestEdge = Math.Min(Math.Min(distToLeft, distToRight), Math.Min(distToTop, distToBottom)); - - if (distToNearestEdge >= radius) - return 0; // No overlap - - float overlapRadius = radius - distToNearestEdge; - float overlapArea = (float)(Math.PI * overlapRadius * overlapRadius); - - return Math.Min(overlapArea, (float)(Math.PI * radius * radius)); // Cap at circle area - } - - - - private Vector2 RotatePoint(Vector2 point, Vector2 pivot, float angle) - { - float cosTheta = (float)Math.Cos(angle); - float sinTheta = (float)Math.Sin(angle); - // Ajustar punto por pivot antes de aplicar rotación - Vector2 translatedPoint = new Vector2(point.X - pivot.X, point.Y - pivot.Y); - // Rotar el punto - Vector2 rotatedPoint = new Vector2( - translatedPoint.X * cosTheta - translatedPoint.Y * sinTheta, - translatedPoint.X * sinTheta + translatedPoint.Y * cosTheta + // Convertir el círculo en un cuadrado aproximado. + float squareSide = circle.Diameter / (float)Math.Sqrt(Math.PI); + RotatedRect square = new RotatedRect( + new Point2f(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2), + new Size2f(squareSide, squareSide), + 0 // Sin rotación ); - // Traducir el punto de vuelta - return new Vector2(rotatedPoint.X + pivot.X, rotatedPoint.Y + pivot.Y); + + // Ajustamos el rectángulo para que se considere rotado desde el centro, pero calculado desde Top-Left + RotatedRect rotatedRectangle = CreateRotatedRectFromTopLeft(rectangle); + + // Usar OpenCV para encontrar la intersección. + using (var mat = new Mat()) + { + var result = Cv2.RotatedRectangleIntersection(square, rotatedRectangle, mat); + if (result != RectanglesIntersectTypes.None) + { + // Calcular el área de la intersección + float intersectionArea = (float) Cv2.ContourArea(mat); + float circleArea = (float)(Math.PI * Math.Pow(circle.Diameter / 2, 2)); + return (intersectionArea / circleArea) * 100; + } + } + + return 0; // No hay intersección } + public static RotatedRect CreateRotatedRectFromTopLeft(Rectangle rectangle) + { + // El punto de pivote es Top-Left, calculamos el centro sin rotar + float originalCenterX = rectangle.Left + rectangle.Length / 2.0f; + float originalCenterY = rectangle.Top + rectangle.Width / 2.0f; + + // Convertimos el ángulo a radianes para la rotación + float angleRadians = rectangle.Angle * (float)Math.PI / 180; + + // Calcular las nuevas coordenadas del centro después de la rotación + float rotatedCenterX = rectangle.Left + (originalCenterX - rectangle.Left) * (float)Math.Cos(angleRadians) - (originalCenterY - rectangle.Top) * (float)Math.Sin(angleRadians); + float rotatedCenterY = rectangle.Top + (originalCenterX - rectangle.Left) * (float)Math.Sin(angleRadians) + (originalCenterY - rectangle.Top) * (float)Math.Cos(angleRadians); + + // Crear el RotatedRect con el nuevo centro y el tamaño original + RotatedRect rotatedRect = new RotatedRect( + new Point2f(rotatedCenterX, rotatedCenterY), + new Size2f(rectangle.Length, rectangle.Width), + rectangle.Angle + ); + + return rotatedRect; + } + + } @@ -340,6 +318,21 @@ public class Line } } +public class Square +{ + public float Left { get; set; } + public float Top { get; set; } + public float Size { get; set; } // 'Size' es la longitud de un lado del cuadrado + + public Square(float left, float top, float size) + { + Left = left; + Top = top; + Size = size; + } +} + + // Clase principal que gestiona la simulación public class SimulationManager