Compare commits

..

No commits in common. "643287583ab52eeaedf653c4240a8ecc48ebb3b2" and "ef7d3e26187769587271b969bdf3a969fcd476c8" have entirely different histories.

5 changed files with 59 additions and 102 deletions

View File

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

View File

@ -1,13 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using static System.Runtime.InteropServices.JavaScript.JSType; using static System.Runtime.InteropServices.JavaScript.JSType;
namespace CtrEditor.ObjetosSim namespace CtrEditor.ObjetosSim
@ -82,21 +80,6 @@ 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 public class UnitConverter
{ {
// La escala representa cuántos metros hay en un píxel // La escala representa cuántos metros hay en un píxel

View File

@ -58,17 +58,6 @@ namespace CtrEditor.ObjetosSim
} }
} }
public float Overlap
{
get => Data.Overlap;
set
{
Data.Overlap = value;
OnPropertyChanged(nameof(Overlap));
}
}
public override float LeftPixels public override float LeftPixels
{ {
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Left); get => PixelToMeter.Instance.calc.MetersToPixels(Data.Left);
@ -145,7 +134,6 @@ namespace CtrEditor.ObjetosSim
{ {
Top = Data.Top; Top = Data.Top;
Left = Data.Left; Left = Data.Left;
Overlap = Data.Overlap;
} }
} }

View File

@ -89,7 +89,6 @@ namespace CtrEditor.ObjetosSim
set set
{ {
Data.Length = value; Data.Length = value;
OnPropertyChanged(nameof(AnchoPixels));
OnPropertyChanged(nameof(Ancho)); OnPropertyChanged(nameof(Ancho));
} }
} }
@ -98,7 +97,6 @@ namespace CtrEditor.ObjetosSim
set set
{ {
Data.Width = value; Data.Width = value;
OnPropertyChanged(nameof(AltoPixels));
OnPropertyChanged(nameof(Alto)); OnPropertyChanged(nameof(Alto));
} }
} }
@ -108,7 +106,6 @@ namespace CtrEditor.ObjetosSim
set set
{ {
Data.Length = (float)PixelToMeter.Instance.calc.PixelsToMeters(value); Data.Length = (float)PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(AnchoPixels));
OnPropertyChanged(nameof(Ancho)); OnPropertyChanged(nameof(Ancho));
} }
} }
@ -118,7 +115,6 @@ namespace CtrEditor.ObjetosSim
set set
{ {
Data.Width = (float)PixelToMeter.Instance.calc.PixelsToMeters(value); Data.Width = (float)PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(AltoPixels));
OnPropertyChanged(nameof(Alto)); OnPropertyChanged(nameof(Alto));
} }
} }
@ -186,7 +182,7 @@ namespace CtrEditor.ObjetosSim
public void Resize(float width, float height) public void Resize(float width, float height)
{ {
if (Datos is osTransporteTTop datos) if (Datos is osTransporteTTop datos)
datos.AnchoPixels = width; datos.Ancho = width;
} }
public void Move(float LeftPixels, float TopPixels) public void Move(float LeftPixels, float TopPixels)
{ {

View File

@ -1,5 +1,4 @@
using CtrEditor.ObjetosSim; using CtrEditor.ObjetosSim;
using OpenCvSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader; using System.Diagnostics.Eventing.Reader;
@ -21,16 +20,15 @@ public class Circle
} }
public float Diameter { get; set; } public float Diameter { get; set; }
public float Mass { get; set; } public float Mass { get; set; }
public float AngleofMovement { get; set; } // En grados public float Angle { get; set; } // En grados
public float Speed { get; set; } public float Speed { get; set; }
public float Overlap { get; set; }
public Circle(float left = 0, float top = 0, float diameter = 10, float mass = 1, float angle = 0, float speed = 0) public Circle(float left = 0, float top = 0, float diameter = 10, float mass = 1, float angle = 0, float speed = 0)
{ {
position = new Vector2(left, top); position = new Vector2(left, top);
Diameter = diameter; Diameter = diameter;
Mass = mass; Mass = mass;
AngleofMovement = angle; Angle = angle;
Speed = speed; Speed = speed;
} }
@ -39,15 +37,13 @@ public class Circle
// Convertir timeStep de milisegundos a segundos para la simulación // Convertir timeStep de milisegundos a segundos para la simulación
float timeStepInSeconds = timeStep_ms / 1000.0f; float timeStepInSeconds = timeStep_ms / 1000.0f;
bool isTracted = false; // Indicador para verificar si el círculo está siendo traccionado bool isTracted = false; // Indicador para verificar si el círculo está siendo traccionado
Overlap = 0;
// Aplicar fuerza desde el rectángulo si está sobre uno // Aplicar fuerza desde el rectángulo si está sobre uno
foreach (var rectangle in rectangles) foreach (var rectangle in rectangles)
{ {
float overlap = CalculateOverlapPercentage(this, rectangle); float overlap = CalculateOverlapPercentage(this, rectangle);
if (overlap > 10) if (overlap > 0)
{ {
Overlap += overlap;
isTracted = true; // El círculo está siendo traccionado por un rectángulo isTracted = true; // El círculo está siendo traccionado por un rectángulo
// Convertir la velocidad del rectángulo de metros por minuto a metros por segundo // Convertir la velocidad del rectángulo de metros por minuto a metros por segundo
float rectangleSpeedInMetersPerSecond = rectangle.Speed / 60.0f; float rectangleSpeedInMetersPerSecond = rectangle.Speed / 60.0f;
@ -64,7 +60,7 @@ public class Circle
Speed += (rectangleSpeedInMetersPerSecond - Speed) * (overlap / 100.0f) * timeStepInSeconds; Speed += (rectangleSpeedInMetersPerSecond - Speed) * (overlap / 100.0f) * timeStepInSeconds;
} }
AngleofMovement = rectangle.Angle; Angle = rectangle.Angle;
} }
} }
@ -104,11 +100,11 @@ public class Circle
float impactAngle = CalculateImpactAngle(this, line); float impactAngle = CalculateImpactAngle(this, line);
if (impactAngle < 85) if (impactAngle < 85)
{ {
AngleofMovement = line.Angle; Angle = line.Angle;
} }
else if (impactAngle > 95) else if (impactAngle > 95)
{ {
AngleofMovement = line.Angle + 180; // Movimiento contrario Angle = line.Angle + 180; // Movimiento contrario
} }
else else
{ {
@ -118,7 +114,7 @@ public class Circle
} }
// Calcular nueva posición // Calcular nueva posición
Vector2 direction = new Vector2((float)Math.Cos(AngleofMovement * Math.PI / 180), (float)Math.Sin(AngleofMovement * Math.PI / 180)); Vector2 direction = new Vector2((float)Math.Cos(Angle * Math.PI / 180), (float)Math.Sin(Angle * Math.PI / 180));
Vector2 velocity = direction * Speed * timeStepInSeconds; Vector2 velocity = direction * Speed * timeStepInSeconds;
position += velocity; position += velocity;
} }
@ -184,7 +180,7 @@ public class Circle
private float CalculateImpactAngle(Circle circle, Line line) private float CalculateImpactAngle(Circle circle, Line line)
{ {
Vector2 movementDirection = new Vector2((float)Math.Cos(circle.AngleofMovement * Math.PI / 180), (float)Math.Sin(circle.AngleofMovement * Math.PI / 180)); Vector2 movementDirection = new Vector2((float)Math.Cos(circle.Angle * Math.PI / 180), (float)Math.Sin(circle.Angle * Math.PI / 180));
Vector2 lineDirection = line.end - line.start; Vector2 lineDirection = line.end - line.start;
Vector2 lineNormal = new Vector2(-lineDirection.Y, lineDirection.X); // Rotar 90 grados para obtener normal Vector2 lineNormal = new Vector2(-lineDirection.Y, lineDirection.X); // Rotar 90 grados para obtener normal
lineNormal = Vector2.Normalize(lineNormal); lineNormal = Vector2.Normalize(lineNormal);
@ -197,60 +193,70 @@ public class Circle
return angle < 90 ? 90 - angle : angle - 90; return angle < 90 ? 90 - angle : angle - 90;
} }
public static float CalculateOverlapPercentage(Circle circle, Rectangle rectangle) public float CalculateOverlapPercentage(Circle circle, Rectangle rectangle)
{ {
// Convertir el círculo en un cuadrado aproximado. // Convertir ángulo del rectángulo de grados a radianes
float squareSide = circle.Diameter / (float)Math.Sqrt(Math.PI); float angleRadians = (float)(rectangle.Angle * Math.PI / 180);
RotatedRect square = new RotatedRect(
new Point2f(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2),
new Size2f(squareSide, squareSide),
0 // Sin rotación
);
// Ajustamos el rectángulo para que se considere rotado desde el centro, pero calculado desde Top-Left // Centro del círculo
RotatedRect rotatedRectangle = CreateRotatedRectFromTopLeft(rectangle); Vector2 circleCenter = new Vector2(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2);
float radius = circle.Diameter / 2;
// Usar OpenCV para encontrar la intersección. // Pivot del rectángulo es el Top Left
using (var mat = new Mat()) 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))
{ {
var result = Cv2.RotatedRectangleIntersection(square, rotatedRectangle, mat); float overlapArea = EstimateOverlapArea(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width);
if (result != RectanglesIntersectTypes.None) float circleArea = (float)(Math.PI * radius * radius);
{ return (overlapArea / circleArea) * 100;
// 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;
return 0; // No hay intersección
} }
public static RotatedRect CreateRotatedRectFromTopLeft(Rectangle rectangle)
private bool IsCircleRectangleIntersecting(Vector2 circleCenter, float radius, Vector2 rectTopLeft, float length, float width)
{ {
// El punto de pivote es Top-Left, calculamos el centro sin rotar float closestX = Math.Max(rectTopLeft.X, Math.Min(circleCenter.X, rectTopLeft.X + length));
float originalCenterX = rectangle.Left + rectangle.Length / 2.0f; float closestY = Math.Max(rectTopLeft.Y, Math.Min(circleCenter.Y, rectTopLeft.Y + width));
float originalCenterY = rectangle.Top + rectangle.Width / 2.0f;
// Convertimos el ángulo a radianes para la rotación float distanceX = circleCenter.X - closestX;
float angleRadians = rectangle.Angle * (float)Math.PI / 180; float distanceY = circleCenter.Y - closestY;
// Calcular las nuevas coordenadas del centro después de la rotación return (distanceX * distanceX + distanceY * distanceY) < (radius * radius);
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;
} }
private float EstimateOverlapArea(Vector2 circleCenter, float radius, Vector2 rectCenter, float length, float width)
{
// Esto es un placeholder: el cálculo real requiere un algoritmo geométrico complejo
// Puedes retornar una estimación basada en proporciones o usar una librería geométrica
return (float) (radius * radius * Math.PI * 0.25f); // Asumiendo un solapamiento del 25% como placeholder
}
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
);
// Traducir el punto de vuelta
return new Vector2(rotatedPoint.X + pivot.X, rotatedPoint.Y + pivot.Y);
}
} }
public class Rectangle public class Rectangle
@ -318,21 +324,6 @@ 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 // Clase principal que gestiona la simulación
public class SimulationManager public class SimulationManager