2024-05-08 03:00:31 -03:00
|
|
|
|
using CtrEditor.ObjetosSim;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2024-05-08 03:00:31 -03:00
|
|
|
|
using System.Diagnostics.Eventing.Reader;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
using System.Numerics;
|
2024-05-08 03:00:31 -03:00
|
|
|
|
using System.Windows.Shapes;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public class Circle
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
private Vector2 position;
|
|
|
|
|
public float Left
|
|
|
|
|
{
|
|
|
|
|
get { return position.X; }
|
|
|
|
|
set { position.X = value; }
|
|
|
|
|
}
|
|
|
|
|
public float Top
|
|
|
|
|
{
|
|
|
|
|
get { return position.Y; }
|
|
|
|
|
set { position.Y = value; }
|
|
|
|
|
}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
public float Diameter { get; set; }
|
|
|
|
|
public float Mass { get; set; }
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public float Angle { get; set; } // En grados
|
|
|
|
|
public float Speed { get; set; }
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public Circle(float left = 0, float top = 0, float diameter = 10, float mass = 1, float angle = 0, float speed = 0)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
position = new Vector2(left, top);
|
2024-05-06 12:31:45 -03:00
|
|
|
|
Diameter = diameter;
|
|
|
|
|
Mass = mass;
|
2024-05-08 03:00:31 -03:00
|
|
|
|
Angle = angle;
|
|
|
|
|
Speed = speed;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public void Move(float timeStep_ms, List<Circle> circles, List<Rectangle> rectangles, List<Line> lines)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Convertir timeStep de milisegundos a segundos para la simulación
|
|
|
|
|
float timeStepInSeconds = timeStep_ms / 1000.0f;
|
|
|
|
|
bool isTracted = false; // Indicador para verificar si el círculo está siendo traccionado
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Aplicar fuerza desde el rectángulo si está sobre uno
|
2024-05-06 12:31:45 -03:00
|
|
|
|
foreach (var rectangle in rectangles)
|
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
float overlap = CalculateOverlapPercentage(this, rectangle);
|
|
|
|
|
if (overlap > 0)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
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
|
|
|
|
|
float rectangleSpeedInMetersPerSecond = rectangle.Speed / 60.0f;
|
|
|
|
|
|
|
|
|
|
if (rectangleSpeedInMetersPerSecond < Speed)
|
|
|
|
|
{
|
|
|
|
|
// Aplicar una fuerza de frenado si la velocidad del rectángulo es menor que la velocidad del círculo
|
|
|
|
|
float brakingForce = (Speed - rectangleSpeedInMetersPerSecond) * (overlap / 100.0f);
|
|
|
|
|
Speed -= brakingForce * timeStepInSeconds;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Alinear gradualmente la velocidad del círculo con la del rectángulo si es mayor
|
|
|
|
|
Speed += (rectangleSpeedInMetersPerSecond - Speed) * (overlap / 100.0f) * timeStepInSeconds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Angle = rectangle.Angle;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Si el círculo no está siendo traccionado, aplicar desaceleración
|
|
|
|
|
if (!isTracted)
|
|
|
|
|
{
|
|
|
|
|
float deceleration = (1.0f / Mass) * 10.0f; // Coeficiente de desaceleración inversamente proporcional a la masa
|
|
|
|
|
Speed -= deceleration * timeStepInSeconds;
|
|
|
|
|
if (Speed < 0) Speed = 0; // Evitar que la velocidad sea negativa
|
|
|
|
|
}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
//// Interacción por impacto con otros círculos
|
|
|
|
|
//foreach (var other in circles)
|
|
|
|
|
//{
|
|
|
|
|
// if (this != other && IsColliding(this, other))
|
|
|
|
|
// {
|
|
|
|
|
// Vector2 impactDirection = other.position - this.position;
|
|
|
|
|
// Angle = (float)Math.Atan2(impactDirection.Y, impactDirection.X) * (180 / (float)Math.PI);
|
|
|
|
|
// Speed = other.Speed; // Asumimos que el círculo receptor adopta la velocidad del impacto
|
|
|
|
|
// }
|
|
|
|
|
//}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Ajustar por superposición con otros círculos
|
|
|
|
|
foreach (var other in circles)
|
|
|
|
|
{
|
|
|
|
|
if (this != other && IsColliding(this, other))
|
|
|
|
|
{
|
|
|
|
|
AdjustForOverlap(other);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Cambiar dirección al contacto con líneas
|
2024-05-06 12:31:45 -03:00
|
|
|
|
foreach (var line in lines)
|
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
if (IsCollidingWithLine(this, line))
|
|
|
|
|
{
|
|
|
|
|
float impactAngle = CalculateImpactAngle(this, line);
|
|
|
|
|
if (impactAngle < 85)
|
|
|
|
|
{
|
|
|
|
|
Angle = line.Angle;
|
|
|
|
|
}
|
|
|
|
|
else if (impactAngle > 95)
|
|
|
|
|
{
|
|
|
|
|
Angle = line.Angle + 180; // Movimiento contrario
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Speed = 0; // Cancelación de movimiento
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
2024-05-08 03:00:31 -03:00
|
|
|
|
|
|
|
|
|
// Calcular nueva posición
|
|
|
|
|
Vector2 direction = new Vector2((float)Math.Cos(Angle * Math.PI / 180), (float)Math.Sin(Angle * Math.PI / 180));
|
|
|
|
|
Vector2 velocity = direction * Speed * timeStepInSeconds;
|
|
|
|
|
position += velocity;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void AdjustForOverlap(Circle other)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
if (this == other) return; // No auto-interacción
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
float distance = Vector2.Distance(this.position, other.position);
|
|
|
|
|
float radiusSum = (this.Diameter / 2) + (other.Diameter / 2);
|
|
|
|
|
|
|
|
|
|
if (distance < radiusSum) // Los círculos están solapando
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
Vector2 directionToOther = Vector2.Normalize(other.position - this.position);
|
|
|
|
|
float overlapDistance = radiusSum - distance;
|
|
|
|
|
|
|
|
|
|
// Decidir qué círculo mover basado en sus velocidades
|
|
|
|
|
if (this.Speed == 0 && other.Speed > 0)
|
|
|
|
|
{
|
|
|
|
|
// Mover este círculo si su velocidad es cero y el otro se está moviendo
|
|
|
|
|
this.position -= directionToOther * overlapDistance;
|
|
|
|
|
}
|
|
|
|
|
else if (other.Speed == 0 && this.Speed > 0)
|
|
|
|
|
{
|
|
|
|
|
// Mover el otro círculo si su velocidad es cero y este se está moviendo
|
|
|
|
|
other.position += directionToOther * overlapDistance;
|
|
|
|
|
}
|
|
|
|
|
else if (this.Speed == 0 && other.Speed == 0)
|
|
|
|
|
{
|
|
|
|
|
// Si ambos tienen velocidad cero, mover ambos a la mitad del solapamiento
|
|
|
|
|
this.position -= directionToOther * (overlapDistance / 2);
|
|
|
|
|
other.position += directionToOther * (overlapDistance / 2);
|
|
|
|
|
}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
private bool IsColliding(Circle circle1, Circle circle2)
|
|
|
|
|
{
|
|
|
|
|
float distance = Vector2.Distance(circle1.position, circle2.position);
|
|
|
|
|
float radiusSum = (circle1.Diameter / 2) + (circle2.Diameter / 2);
|
|
|
|
|
return distance <= radiusSum;
|
|
|
|
|
}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
private bool IsCollidingWithLine(Circle circle, Line line)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
Vector2 nearestPoint = NearestPointOnLine(circle.position, line.start, line.end);
|
|
|
|
|
float distanceToLine = Vector2.Distance(circle.position, nearestPoint);
|
|
|
|
|
return distanceToLine <= (circle.Diameter / 2);
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
2024-05-08 03:00:31 -03:00
|
|
|
|
|
|
|
|
|
private Vector2 NearestPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
Vector2 lineVector = lineEnd - lineStart;
|
|
|
|
|
Vector2 pointVector = point - lineStart;
|
|
|
|
|
float lineLength = lineVector.Length();
|
|
|
|
|
float projectedLength = Vector2.Dot(pointVector, lineVector) / lineLength;
|
|
|
|
|
projectedLength = Math.Max(0, Math.Min(lineLength, projectedLength)); // Clamping to the line segment
|
|
|
|
|
return lineStart + lineVector * (projectedLength / lineLength);
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
private float CalculateImpactAngle(Circle circle, Line line)
|
|
|
|
|
{
|
|
|
|
|
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 lineNormal = new Vector2(-lineDirection.Y, lineDirection.X); // Rotar 90 grados para obtener normal
|
|
|
|
|
lineNormal = Vector2.Normalize(lineNormal);
|
|
|
|
|
|
|
|
|
|
// Calcular ángulo entre el movimiento y la normal de la línea
|
|
|
|
|
float dotProduct = Vector2.Dot(movementDirection, lineNormal);
|
|
|
|
|
float angle = (float)Math.Acos(dotProduct) * (180 / (float)Math.PI); // Convertir de radianes a grados
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Ajustar para obtener el ángulo relativo correcto
|
|
|
|
|
return angle < 90 ? 90 - angle : angle - 90;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float CalculateOverlapPercentage(Circle circle, Rectangle rectangle)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Convertir ángulo del rectángulo de grados a radianes
|
|
|
|
|
float angleRadians = (float)(rectangle.Angle * Math.PI / 180);
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// 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))
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
float overlapArea = EstimateOverlapArea(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width);
|
|
|
|
|
float circleArea = (float)(Math.PI * radius * radius);
|
|
|
|
|
return (overlapArea / circleArea) * 100;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
2024-05-08 03:00:31 -03:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
|
|
|
|
|
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);
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public class Rectangle
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
private Vector2 position;
|
|
|
|
|
public float Left
|
|
|
|
|
{
|
|
|
|
|
get { return position.X; }
|
|
|
|
|
set { position = new Vector2(value, position.Y); }
|
|
|
|
|
}
|
|
|
|
|
public float Top
|
|
|
|
|
{
|
|
|
|
|
get { return position.Y; }
|
|
|
|
|
set { position = new Vector2(position.X, value); }
|
|
|
|
|
}
|
|
|
|
|
public float Length { get; set; }
|
|
|
|
|
public float Width { get; set; }
|
|
|
|
|
public float Angle { get; set; } // En grados
|
|
|
|
|
public float Speed { get; set; } // Velocidad del rectángulo
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public Rectangle(float left = 0, float top = 0, float length = 10, float width = 10, float angle = 0, float speed = 0)
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
position = new Vector2(left, top);
|
|
|
|
|
Length = length;
|
|
|
|
|
Width = width;
|
|
|
|
|
Angle = angle;
|
|
|
|
|
Speed = speed;
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class Line
|
|
|
|
|
{
|
|
|
|
|
public Vector2 start;
|
|
|
|
|
public Vector2 end;
|
|
|
|
|
|
|
|
|
|
public float Left
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
get { return start.X; }
|
|
|
|
|
set { start = new Vector2(value, start.Y); UpdateEnd(); }
|
|
|
|
|
}
|
|
|
|
|
public float Top
|
|
|
|
|
{
|
|
|
|
|
get { return start.Y; }
|
|
|
|
|
set { start = new Vector2(start.X, value); UpdateEnd(); }
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public float Length { get; set; }
|
|
|
|
|
public float Width { get; set; }
|
|
|
|
|
public float Angle { get; set; } // En grados
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public Line(float left, float top, float length, float width, float angle)
|
|
|
|
|
{
|
|
|
|
|
start = new Vector2(left, top);
|
|
|
|
|
Length = length;
|
|
|
|
|
Width = width;
|
|
|
|
|
Angle = angle;
|
|
|
|
|
UpdateEnd();
|
|
|
|
|
}
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
2024-05-08 03:00:31 -03:00
|
|
|
|
private void UpdateEnd()
|
2024-05-06 12:31:45 -03:00
|
|
|
|
{
|
2024-05-08 03:00:31 -03:00
|
|
|
|
// Asumiendo que la línea se extiende en el ángulo desde el punto de inicio
|
|
|
|
|
end = new Vector2(start.X + Length * (float)Math.Cos(Angle * Math.PI / 180),
|
|
|
|
|
start.Y + Length * (float)Math.Sin(Angle * Math.PI / 180));
|
2024-05-06 12:31:45 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Clase principal que gestiona la simulación
|
|
|
|
|
public class SimulationManager
|
|
|
|
|
{
|
|
|
|
|
public List<Circle> circles;
|
|
|
|
|
public List<Rectangle> rectangles;
|
2024-05-08 03:00:31 -03:00
|
|
|
|
public List<Line> lines;
|
|
|
|
|
|
2024-05-06 12:31:45 -03:00
|
|
|
|
|
|
|
|
|
public SimulationManager()
|
|
|
|
|
{
|
|
|
|
|
circles = new List<Circle>();
|
|
|
|
|
rectangles = new List<Rectangle>();
|
|
|
|
|
lines = new List<Line>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Step(float timeStep)
|
|
|
|
|
{
|
|
|
|
|
foreach (var circle in circles)
|
|
|
|
|
{
|
|
|
|
|
circle.Move(timeStep, circles, rectangles, lines);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-08 03:00:31 -03:00
|
|
|
|
}
|