CtrEditor/Simulacion/OverlapedArea.cs

245 lines
9.6 KiB
C#
Raw Permalink Normal View History

2024-05-30 13:48:37 -03:00
using nkast.Aether.Physics2D.Collision.Shapes;
using nkast.Aether.Physics2D.Common;
using nkast.Aether.Physics2D.Dynamics;
using System.Diagnostics;
using CtrEditor.Simulacion;
namespace CtrEditor.Simulacion
{
class OverlapedArea
{
public static float CalculateOverlapedArea(Body bodyA, Body bodyB)
{
List<Vector2> aVertices = GetRotatedVertices(bodyA);
List<Vector2> bVertices = GetRotatedVertices(bodyB);
List<Vector2> intersectionPolygon = SutherlandHodgmanClip(aVertices, bVertices);
//Debug.WriteLine("");
//Debug.WriteLine("");
//Debug.WriteLine("subjectPolygon :");
//foreach (var vertex in aVertices)
//{
// Debug.WriteLine(vertex);
//}
//Debug.WriteLine("clipPolygon :");
//foreach (var vertex in bVertices)
//{
// Debug.WriteLine(vertex);
//}
//Debug.WriteLine("intersectionPolygon:");
//foreach (var vertex in intersectionPolygon)
//{
// Debug.WriteLine(vertex);
//}
return ComputePolygonArea(intersectionPolygon);
}
public static float ComputePolygonArea(List<Vector2> polygon)
{
float area = 0;
int count = polygon.Count;
for (int i = 0; i < count; i++)
{
Vector2 current = polygon[i];
Vector2 next = polygon[(i + 1) % count];
area += current.X * next.Y - next.X * current.Y;
}
return Math.Abs(area) / 2.0f;
}
public static List<Vector2> SutherlandHodgmanClip(List<Vector2> subjectPolygon, List<Vector2> clipPolygon)
{
List<Vector2> outputList = new List<Vector2>(subjectPolygon);
for (int i = 0; i < clipPolygon.Count; i++)
{
Vector2 clipEdgeStart = clipPolygon[i];
Vector2 clipEdgeEnd = clipPolygon[(i + 1) % clipPolygon.Count];
List<Vector2> inputList = outputList;
outputList = new List<Vector2>();
for (int j = 0; j < inputList.Count; j++)
{
Vector2 currentVertex = inputList[j];
Vector2 previousVertex = inputList[(j + inputList.Count - 1) % inputList.Count];
if (IsInside(clipEdgeStart, clipEdgeEnd, currentVertex))
{
if (!IsInside(clipEdgeStart, clipEdgeEnd, previousVertex))
{
outputList.Add(ComputeIntersection(clipEdgeStart, clipEdgeEnd, previousVertex, currentVertex));
}
outputList.Add(currentVertex);
}
else if (IsInside(clipEdgeStart, clipEdgeEnd, previousVertex))
{
outputList.Add(ComputeIntersection(clipEdgeStart, clipEdgeEnd, previousVertex, currentVertex));
}
}
}
return outputList;
}
private static bool IsInside(Vector2 edgeStart, Vector2 edgeEnd, Vector2 point)
{
return (edgeEnd.X - edgeStart.X) * (point.Y - edgeStart.Y) > (edgeEnd.Y - edgeStart.Y) * (point.X - edgeStart.X);
}
private static Vector2 ComputeIntersection(Vector2 edgeStart, Vector2 edgeEnd, Vector2 point1, Vector2 point2)
{
Vector2 edge = edgeEnd - edgeStart;
Vector2 segment = point2 - point1;
float edgeCrossSegment = Cross(edge, segment);
if (Math.Abs(edgeCrossSegment) < float.Epsilon)
{
return point1; // Return any point on the segment since they are nearly parallel.
}
float t = Cross(point1 - edgeStart, edge) / edgeCrossSegment;
return point1 + t * segment;
}
public static float Cross(Vector2 v1, Vector2 v2)
{
return v1.X * v2.Y - v1.Y * v2.X;
}
public static List<Vector2> GetRotatedRectangleVertices(Vector2 center, float width, float height, float rotation)
{
float halfWidth = width / 2.0f;
float halfHeight = height / 2.0f;
float cos = (float)Math.Cos(rotation);
float sin = (float)Math.Sin(rotation);
return new List<Vector2>
{
new Vector2(center.X + cos * (-halfWidth) - sin * (-halfHeight), center.Y + sin * (-halfWidth) + cos * (-halfHeight)),
new Vector2(center.X + cos * (halfWidth) - sin * (-halfHeight), center.Y + sin * (halfWidth) + cos * (-halfHeight)),
new Vector2(center.X + cos * (halfWidth) - sin * (halfHeight), center.Y + sin * (halfWidth) + cos * (halfHeight)),
new Vector2(center.X + cos * (-halfWidth) - sin * (halfHeight), center.Y + sin * (-halfWidth) + cos * (halfHeight))
};
}
public static List<Vector2> GetRotatedVertices(Body body)
{
List<Vector2> vertices = new List<Vector2>();
// Verificar el tipo de Shape del Body
foreach (var fixture in body.FixtureList)
{
if (fixture.Shape is PolygonShape polygonShape)
{
// Es un rectángulo o un polígono
foreach (var vertex in polygonShape.Vertices)
{
vertices.Add(RotatePoint(vertex, body.Position, body.Rotation));
}
}
else if (fixture.Shape is CircleShape circleShape)
{
// Es un círculo
float radius = circleShape.Radius;
float halfSide = radius; // El lado del cuadrado inscrito es igual al radio
2024-05-31 10:06:49 -03:00
Vector2[] squareVertices = new Vector2[]
{
new Vector2(-halfSide, -halfSide),
new Vector2(halfSide, -halfSide),
new Vector2(halfSide, halfSide),
new Vector2(-halfSide, halfSide)
};
2024-05-30 13:48:37 -03:00
foreach (var vertex in squareVertices)
{
vertices.Add(vertex + body.Position); // Trasladar el cuadrado a la posición del cuerpo
}
}
}
return vertices;
}
2024-05-31 10:06:49 -03:00
2024-05-30 13:48:37 -03:00
private static Vector2 RotatePoint(Vector2 point, Vector2 origin, float rotation)
{
float cos = (float)Math.Cos(rotation);
float sin = (float)Math.Sin(rotation);
float x = point.X * cos - point.Y * sin;
float y = point.X * sin + point.Y * cos;
return new Vector2(x + origin.X, y + origin.Y);
}
}
public class OverlapedAreaFast
{
public static float CalculateOverlapedArea(simBotella botella, simTransporte conveyor)
{
var porcentajeSuperpuesto = (float) (1.1 * CalculateOverlapPercentage(botella.Body.Position, botella.Radius, conveyor.Body.Position, conveyor.Width, conveyor.Height, conveyor.Body.Rotation));
return porcentajeSuperpuesto > 1 ? 1 : porcentajeSuperpuesto; // 1.1 Porque las botellas apollan sobre un circulo inscripto 10 % menor.
}
public static double[] RotatePoint(double px, double py, double ox, double oy, double angle)
{
double cosAngle = Math.Cos(angle);
double sinAngle = Math.Sin(angle);
double qx = ox + cosAngle * (px - ox) - sinAngle * (py - oy);
double qy = oy + sinAngle * (px - ox) + cosAngle * (py - oy);
return new double[] { qx, qy };
}
public static double CalculateOverlapPercentage(
Vector2 circleCenter, double circleRadius, Vector2 rectCenter, double rectWidth, double rectHeight, double rectAngle)
{
// Transform the center of the circle to the coordinate system of the rotated rectangle
double[] newCircleCenter = RotatePoint(circleCenter.X, circleCenter.Y, rectCenter.X, rectCenter.Y, -rectAngle);
// Create a square with the same rotation as the rectangle
double squareSide = 2 * circleRadius;
double[] squareCenter = newCircleCenter;
// Coordinates of the square (non-rotated)
double x3 = squareCenter[0] - circleRadius;
double y3 = squareCenter[1] - circleRadius;
double x4 = squareCenter[0] + circleRadius;
double y4 = squareCenter[1] + circleRadius;
// Coordinates of the rectangle (non-rotated)
double x1 = rectCenter.X - rectWidth / 2;
double y1 = rectCenter.Y - rectHeight / 2;
double x2 = rectCenter.X + rectWidth / 2;
double y2 = rectCenter.Y + rectHeight / 2;
// Limits of the intersection
double xLeft = Math.Max(x1, x3);
double xRight = Math.Min(x2, x4);
double yBottom = Math.Max(y1, y3);
double yTop = Math.Min(y2, y4);
// Width and height of the intersection
double intersectWidth = Math.Max(0, xRight - xLeft);
double intersectHeight = Math.Max(0, yTop - yBottom);
// Area of the intersection
double intersectionArea = intersectWidth * intersectHeight;
// Area of the square
double squareArea = squareSide * squareSide;
// Overlap percentage relative to the total area of the square
double squareOverlapPercentage = (squareArea > 0) ? (intersectionArea / squareArea) : 0;
return squareOverlapPercentage;
}
}
}