Implementacion con CV funciona mejor el calculo de overlap para los rectangulos rotados

This commit is contained in:
Miguel 2024-05-08 11:04:46 +02:00
parent 294be7788f
commit 643287583a
4 changed files with 89 additions and 74 deletions

View File

@ -12,6 +12,7 @@
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.77" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="OpenCvSharp4.Windows" Version="4.9.0.20240103" />
</ItemGroup>
</Project>

View File

@ -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

View File

@ -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)
{

View File

@ -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