using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Numerics;
using System.Drawing;
namespace JansenLegSimulator
{
public class Rod
{
#region Constructores
// Constructor de barra libre
public Rod() { }
// Constructor de barra con un extremo fijado a un punto
public Rod(Vector2 begining)
{
_startPosition = begining;
}
#endregion
#region Conexiones
///
/// Barra a la que se une por el extremo inicial
///
private Rod JointBegining { get; set; } = null;
///
/// Barras a las cuales se une por el extremo final
///
private List JointsEnding { get; set; } = new();
///
/// Conecta el inicio de una barra a otra barra
///
/// La barra a la cual se unirá esta
public void ConnectBegin(ref Rod linkage)
{
if (linkage == null) throw new NullReferenceException("The rod cannot be null");
if (JointBegining != null) throw new Exception("The beginning is already assigned");
JointBegining = linkage;
}
///
/// Conecta el final de una barra a otra barra
///
/// La barra a la cual se unirá esta
public void ConnectEnd(ref Rod linkage)
{
if (linkage == null) throw new NullReferenceException("The rod cannot be null");
if (!JointsEnding.Contains(linkage))
JointsEnding.Add(linkage);
}
///
/// Desconecta (si está conectada) la barra especificada
///
/// Barra a ser desconectada
public void Disconnect(ref Rod linkage)
{
if (JointBegining == linkage) JointBegining = null;
if (JointsEnding.Contains(linkage)) JointsEnding.Remove(linkage);
}
#endregion
#region Propiedades de la barra
public string RodName { get; set; } = "Unnamed";
///
/// Longitud de la barra
///
public float Length { get; set; } = 20;
private float? _alpha = null;
///
/// Ángulo de rotación de la barra. Si no se establece será 0 radianes.
///
public float Alpha
{
get => (_alpha != null) ? (float)_alpha : 0;
set => _alpha = value;
}
private Vector2? _startPosition = null;
///
/// Posición de incio (eje de esta barra).
/// Si se ha unido el inicio a otra barra, se calculará a partir de la posición final de la barra sobre la cual está
///
public virtual Vector2 StartPosition
{
get => (_startPosition != null) ? (Vector2)_startPosition :
(JointBegining != null) ? JointBegining.EndPosition : new Vector2(0, 0);
set => _startPosition = value;
}
///
/// Posición final.
/// Se calcula a partir de la posición inicial, el ángulo y la longitud.
///
public virtual Vector2 EndPosition
{
get => new Vector2(
StartPosition.X + Length * MathF.Cos(Alpha),
StartPosition.Y + Length * MathF.Sin(Alpha)
);
}
///
/// Posición central.
/// Se calcula a partir de la posición inicial, el ángulo y la longitud.
///
public virtual Vector2 Center
{
get => new Vector2(
StartPosition.X + Length * MathF.Cos(Alpha) / 2,
StartPosition.Y + Length * MathF.Sin(Alpha) / 2
);
}
#endregion
///
/// Calcula el punto de intersección entre dos barras de longitud y eje de rotación determinados.
///
/// Primera barra
/// Segunda barra
/// En caso de que existan dos intersecciones, número de la intersección a elegir
/// El vector que representa el punto en el que se unen las dos barras.
/// Si las barras no intersecan en ningún punto, lanzará una excepción.
/// Si las barras intersecan pero no se encuentra la intersección especificada, lanzará una excepción.
public static Vector2 GetIntersection(Rod A, Rod B, int solution = -1)
{
Vector2[] intersects = CosasQueNoSeExplicanEnMatesDePrimero.CalculateCircleIntersections(
A.StartPosition,
B.StartPosition,
A.Length,
B.Length);
return (intersects.Length == 0) ? throw new RodsDoNotIntersectException("No existe conexión posible entre estas dos barras. Pruebe a darles la vuelta.") :
(intersects.Length == 1 || solution < 0) ? intersects[0] :
(intersects.Length > solution) ? intersects[solution] :
throw new IntersectionOutOfRangeException("La intersección especificada no existe. Pruebe con las soluciones 0 y 1");
}
///
/// Calcula la diferencia entre dos vectores
///
/// Vector final
/// Vector inicial
/// Vector final - Vector inicial
public static Vector2 Delta(Vector2 A, Vector2 B) => A - B;
///
/// Calcula para cada elemento el ángulo de la intersección de dos vectores a su posición respecto el 0 en el primer cuadrante.
///
/// Vector 1
/// Vector 2
/// Devuelve los angulos de cada eje a la intersección
///
public static (float, float) GetIntersectionAlphas(Rod A, Rod B, int solution = -1)
{
(float, float) result = new();
// Calcular las intersecciones y los vectores de
// incremento entre el eje de la barra y su unión.
Vector2
I = GetIntersection(A, B, solution),
DeltaA = Delta(I, A.StartPosition),
DeltaB = Delta(I, B.StartPosition);
// Obtener los ángulos mediante la inversa del coseno
result.Item1 = MathF.Acos(DeltaA.X / DeltaA.Length());
result.Item2 = MathF.Acos(DeltaB.X / DeltaB.Length());
// Voltear verticalmente según la dirección vertical
// del vector incremento
if (DeltaA.Y < 0) result.Item1 *= -1.0f;
if (DeltaB.Y < 0) result.Item2 *= -1.0f;
return result;
}
[Serializable]
public class RodsDoNotIntersectException : Exception
{
public RodsDoNotIntersectException() { }
public RodsDoNotIntersectException(string message) : base(message) { }
public RodsDoNotIntersectException(string message, Exception inner) : base(message, inner) { }
protected RodsDoNotIntersectException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
[Serializable]
public class IntersectionOutOfRangeException : Exception
{
public IntersectionOutOfRangeException() { }
public IntersectionOutOfRangeException(string message) : base(message) { }
public IntersectionOutOfRangeException(string message, Exception inner) : base(message, inner) { }
protected IntersectionOutOfRangeException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}
}