using CtrEditor.ObjetosSim; using System; using System.Collections.Generic; using System.Diagnostics.Eventing.Reader; using System.Numerics; using System.Windows.Shapes; public class Circle { 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; } } public float Diameter { get; set; } public float Mass { get; set; } public float AngleofMovement { get; set; } // En grados 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) { position = new Vector2(left, top); Diameter = diameter; Mass = mass; AngleofMovement = angle; Speed = speed; } public void Move(float timeStep_ms, List circles, List rectangles, List lines) { // 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 Overlap = 0; // Aplicar fuerza desde el rectángulo si está sobre uno foreach (var rectangle in rectangles) { float overlap = CalculateOverlapPercentage(this, rectangle); if (overlap > 0) { Overlap += overlap; 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; } AngleofMovement = rectangle.Angle; } } // 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 } //// 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 // } //} // Ajustar por superposición con otros círculos foreach (var other in circles) { if (this != other && IsColliding(this, other)) { AdjustForOverlap(other); } } // Cambiar dirección al contacto con líneas foreach (var line in lines) { if (IsCollidingWithLine(this, line)) { float impactAngle = CalculateImpactAngle(this, line); if (impactAngle < 85) { AngleofMovement = line.Angle; } else if (impactAngle > 95) { AngleofMovement = line.Angle + 180; // Movimiento contrario } else { Speed = 0; // Cancelación de movimiento } } } // Calcular nueva posición Vector2 direction = new Vector2((float)Math.Cos(AngleofMovement * Math.PI / 180), (float)Math.Sin(AngleofMovement * Math.PI / 180)); Vector2 velocity = direction * Speed * timeStepInSeconds; position += velocity; } private void AdjustForOverlap(Circle other) { if (this == other) return; // No auto-interacción 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 { 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); } } } 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; } private bool IsCollidingWithLine(Circle circle, Line line) { Vector2 nearestPoint = NearestPointOnLine(circle.position, line.start, line.end); float distanceToLine = Vector2.Distance(circle.position, nearestPoint); return distanceToLine <= (circle.Diameter / 2); } private Vector2 NearestPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd) { 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); } 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 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 // Ajustar para obtener el ángulo relativo correcto return angle < 90 ? 90 - angle : angle - 90; } public 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 ); // Traducir el punto de vuelta return new Vector2(rotatedPoint.X + pivot.X, rotatedPoint.Y + pivot.Y); } } public class Rectangle { 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 public Rectangle(float left = 0, float top = 0, float length = 10, float width = 10, float angle = 0, float speed = 0) { position = new Vector2(left, top); Length = length; Width = width; Angle = angle; Speed = speed; } } public class Line { public Vector2 start; public Vector2 end; public float Left { 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(); } } public float Length { get; set; } public float Width { get; set; } public float Angle { get; set; } // En grados public Line(float left, float top, float length, float width, float angle) { start = new Vector2(left, top); Length = length; Width = width; Angle = angle; UpdateEnd(); } private void UpdateEnd() { // 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)); } } // Clase principal que gestiona la simulación public class SimulationManager { public List circles; public List rectangles; public List lines; public SimulationManager() { circles = new List(); rectangles = new List(); lines = new List(); } public void Step(float timeStep) { foreach (var circle in circles) { circle.Move(timeStep, circles, rectangles, lines); } } }