Agregar archivos de proyecto.
This commit is contained in:
9
JansenLegViewer/App.xaml
Normal file
9
JansenLegViewer/App.xaml
Normal file
@ -0,0 +1,9 @@
|
||||
<Application x:Class="JansenLegViewer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:JansenLegViewer"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
17
JansenLegViewer/App.xaml.cs
Normal file
17
JansenLegViewer/App.xaml.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace JansenLegViewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
10
JansenLegViewer/AssemblyInfo.cs
Normal file
10
JansenLegViewer/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
75
JansenLegViewer/CosasQueNoSeExplicanEnMatesDePrimero.cs
Normal file
75
JansenLegViewer/CosasQueNoSeExplicanEnMatesDePrimero.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using static System.Math;
|
||||
using System.Numerics;
|
||||
|
||||
/*
|
||||
Función que permite obtener las posiciones de las
|
||||
posibles intersecciones entre dos circulos.
|
||||
|
||||
PD: le debeo un café a alguien random de internet
|
||||
*/
|
||||
|
||||
namespace JansenLegSimulator
|
||||
{
|
||||
public class CosasQueNoSeExplicanEnMatesDePrimero
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the intersections of two circles
|
||||
/// </summary>
|
||||
/// <param name="center1">The first circle's center</param>
|
||||
/// <param name="center2">The second circle's center</param>
|
||||
/// <param name="radius1">The first circle's radius</param>
|
||||
/// <param name="radius2">The second circle's radius. If omitted, assumed to equal the first circle's radius</param>
|
||||
/// <returns>An array of intersection points. May have zero, one, or two values</returns>
|
||||
/// <remarks>Adapted from http://csharphelper.com/blog/2014/09/determine-where-two-circles-intersect-in-c/</remarks>
|
||||
public static Vector2[] CalculateCircleIntersections(Vector2 center1, Vector2 center2, double radius1, double? radius2 = null)
|
||||
{
|
||||
// Definir radios. Si sólo se ha dado R1,
|
||||
// asignar valor de R1 a los dos radios
|
||||
var (r1, r2) = (radius1, radius2 ?? radius1);
|
||||
|
||||
// Definir centros de los circulos.
|
||||
(double x1, double y1, double x2, double y2) = (center1.X, center1.Y, center2.X, center2.Y);
|
||||
|
||||
// Determinar la distancia entre los dos centros.
|
||||
// Es el módulo del vector que une los dos centros.
|
||||
double d = Sqrt(Pow(x1 - x2, 2) + Pow(y1 - y2, 2));
|
||||
|
||||
// Si la suma de radios es menor a la distancia que
|
||||
// une los centros, devolver un array vacío.
|
||||
// No existe intersección.
|
||||
if (!(Abs(r1 - r2) <= d && d <= r1 + r2)) { return new Vector2[0]; }
|
||||
|
||||
// En este momento, se dan las condiciones para que
|
||||
// por lo menos exista una intersección
|
||||
var dsq = d * d; // Caucular el cuadrado de la distancia (d^2)
|
||||
var (r1sq, r2sq) = (r1 * r1, r2 * r2); // Calcular los cuadrados de los radios (r^2)
|
||||
var r1sq_r2sq = r1sq - r2sq; // Calcular la diferencia de los r^2 (incR^2)
|
||||
var a = r1sq_r2sq / (2 * dsq); // Calcular la división (incR^2)/(2*d^2)
|
||||
|
||||
var c = Sqrt(2 * (r1sq + r2sq) / dsq - (r1sq_r2sq * r1sq_r2sq) / (dsq * dsq) - 1);
|
||||
|
||||
// Calcular las componentes de los resultados
|
||||
var fx = (x1 + x2) / 2 + a * (x2 - x1);
|
||||
var gx = c * (y2 - y1) / 2;
|
||||
|
||||
var fy = (y1 + y2) / 2 + a * (y2 - y1);
|
||||
var gy = c * (x1 - x2) / 2;
|
||||
|
||||
// Generar los vectores de las intersecciones resultado
|
||||
var i1 = new Vector2((float)(fx + gx), (float)(fy + gy));
|
||||
var i2 = new Vector2((float)(fx - gx), (float)(fy - gy));
|
||||
|
||||
// Si son la misma solucción deveolver sólo i1.
|
||||
// Si son ditintas, devolver ambas soluciones.
|
||||
return i1 == i2 ?
|
||||
new Vector2[] { i1 } :
|
||||
new Vector2[] { i1, i2 };
|
||||
}
|
||||
}
|
||||
}
|
||||
73
JansenLegViewer/Cálculos.cs
Normal file
73
JansenLegViewer/Cálculos.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static JansenLegSimulator.Rod;
|
||||
|
||||
namespace JansenLegSimulator
|
||||
{
|
||||
public partial class Cálculos
|
||||
{
|
||||
private void DefineSystem()
|
||||
{
|
||||
// Definir las barra motriz
|
||||
M = new Rod(new Vector2(a, l)) { Length = m, RodName = "M" };
|
||||
|
||||
// Definir las barras semi-independientes
|
||||
// Aquellas que tienen un extremo fijado a una coordenada
|
||||
B = new Rod(new Vector2(0, 0)) { Length = b, RodName = "B" };
|
||||
C = new Rod(new Vector2(0, 0)) { Length = c, RodName = "C" };
|
||||
D = new Rod(new Vector2(0, 0)) { Length = d, RodName = "D" };
|
||||
|
||||
// Definir y conectar los inicios de las barras flotantes
|
||||
// Aquellas cuyos dos extremos están conectados a otra barra
|
||||
J = new Rod() { Length = j, RodName = "J" }; J.ConnectBegin(ref M);
|
||||
K = new Rod() { Length = k, RodName = "K" }; K.ConnectBegin(ref M);
|
||||
E = new Rod() { Length = e, RodName = "E" }; E.ConnectBegin(ref B);
|
||||
|
||||
G = new Rod() { Length = g, RodName = "G" }; G.ConnectBegin(ref C);
|
||||
F = new Rod() { Length = f, RodName = "F" }; F.ConnectBegin(ref D);
|
||||
|
||||
H = new Rod() { Length = h, RodName = "H" }; H.ConnectBegin(ref G);
|
||||
I = new Rod() { Length = i, RodName = "I" }; I.ConnectBegin(ref C);
|
||||
|
||||
// Añadir las barras definidas a la lista
|
||||
// de elementos móviles.
|
||||
Elements.Add(M);
|
||||
Elements.Add(J);
|
||||
Elements.Add(B);
|
||||
|
||||
Elements.Add(C);
|
||||
Elements.Add(K);
|
||||
|
||||
Elements.Add(E);
|
||||
Elements.Add(D);
|
||||
|
||||
Elements.Add(G);
|
||||
Elements.Add(F);
|
||||
|
||||
Elements.Add(H);
|
||||
Elements.Add(I);
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
public void CalculateSystem()
|
||||
{
|
||||
// Para cada par de barras calcular y asignar los
|
||||
// ángulos de rotación necesarios para unirlas
|
||||
|
||||
// Articulación JB
|
||||
(J.Alpha, B.Alpha) = GetIntersectionAlphas(J, B);
|
||||
// Articulación CK
|
||||
(C.Alpha, K.Alpha) = GetIntersectionAlphas(C, K);
|
||||
// Articulación ED
|
||||
(E.Alpha, D.Alpha) = GetIntersectionAlphas(E, D);
|
||||
// Articulación FG
|
||||
(F.Alpha, G.Alpha) = GetIntersectionAlphas(F, G);
|
||||
// Articulación HI
|
||||
(H.Alpha, I.Alpha) = GetIntersectionAlphas(H, I);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
JansenLegViewer/JansenLegSimulator.csproj
Normal file
9
JansenLegViewer/JansenLegSimulator.csproj
Normal file
@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
BIN
JansenLegViewer/JansenLegSimulator.zip
Normal file
BIN
JansenLegViewer/JansenLegSimulator.zip
Normal file
Binary file not shown.
56
JansenLegViewer/MainWindow.xaml
Normal file
56
JansenLegViewer/MainWindow.xaml
Normal file
@ -0,0 +1,56 @@
|
||||
<Window x:Class="JansenLegSimulator.Cálculos"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:JansenLegViewer"
|
||||
Name="thisWindow"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="800">
|
||||
<Grid Margin="1,0,-1,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80*" />
|
||||
<ColumnDefinition Width="20*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="26"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Canvas Name="plot" Grid.Column="0"/>
|
||||
<GridSplitter BorderBrush="LightGray" BorderThickness="3"/>
|
||||
|
||||
<StackPanel Grid.Column="1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Name="btnPlay" Content=">" Click="btnPlay_Click"/>
|
||||
<Button Name="btnPause" Content="| |" Click="btnPause_Click"/>
|
||||
<Button Name="btnStop" Content="#" Click="btnStop_Click"/>
|
||||
</StackPanel>
|
||||
<Label Content="ω rotor (rad/s)"/>
|
||||
<TextBox Text="{Binding ElementName=thisWindow, Path=Omega, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="Número de puntos"/>
|
||||
<Label Content="{Binding ElementName=sbPoints, Path=Value}"/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollBar Name="sbPoints" Orientation="Horizontal" Value="{Binding ElementName=thisWindow, Path=MaxPoints, UpdateSourceTrigger=PropertyChanged}" Minimum="0" Maximum="500"/>
|
||||
|
||||
<ListView ItemsSource="{Binding ElementName=thisWindow, Path=Elements}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate DataType="local:Rod">
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<TextBox Text="{Binding RodName, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
|
||||
<TextBox Text="{Binding Length, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
<StatusBar Grid.Row="1" Grid.ColumnSpan="2">
|
||||
<StatusBarItem Name="SimulationStatus"/>
|
||||
<StatusBarItem Name="Time"/>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</Window>
|
||||
201
JansenLegViewer/MainWindow.xaml.cs
Normal file
201
JansenLegViewer/MainWindow.xaml.cs
Normal file
@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using System.Numerics;
|
||||
using System.Drawing;
|
||||
using System.Windows.Controls;
|
||||
using static JansenLegSimulator.Rod;
|
||||
|
||||
namespace JansenLegSimulator
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class Cálculos : Window
|
||||
{
|
||||
// Origen de cordenadas
|
||||
Vector2 Center;
|
||||
// Dimensiones de los segmentos (iniciales, se pueden modificar des de la interfaz)
|
||||
public float SCALE { get; set; } = 2.0f;
|
||||
public float a => 38.0f * SCALE;
|
||||
public float b => 41.5f * SCALE;
|
||||
public float c => 39.3f * SCALE;
|
||||
public float d => 40.1f * SCALE;
|
||||
public float e => 55.8f * SCALE;
|
||||
public float f => 39.4f * SCALE;
|
||||
public float g => 36.7f * SCALE;
|
||||
public float h => 65.7f * SCALE;
|
||||
public float i => 49.0f * SCALE;
|
||||
public float j => 50.0f * SCALE;
|
||||
public float k => 61.9f * SCALE;
|
||||
public float l => 7.8f * SCALE;
|
||||
public float m => 15.0f * SCALE;
|
||||
|
||||
private Rod B, C, D, E, F, G, H, I, J, K, M;
|
||||
|
||||
public float Omega { get; set; } = (2 * MathF.PI);
|
||||
|
||||
System.Timers.Timer t = new(15);
|
||||
private System.Diagnostics.Stopwatch Chrono = new();
|
||||
|
||||
public List<Rod> Elements { get; set; } = new List<Rod>();
|
||||
private List<Vector2> Points = new();
|
||||
public int MaxPoints { get; set; } = 25;
|
||||
|
||||
#region Constructor y handlers de la ventana
|
||||
public Cálculos()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.Loaded += MainWindow_Loaded;
|
||||
this.Closing += MainWindow_Closing;
|
||||
}
|
||||
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DefineSystem();
|
||||
DrawSystem();
|
||||
t.Elapsed += Tick;
|
||||
}
|
||||
|
||||
// Receptores de eventos de click de los botones de la interfaz
|
||||
private void btnPlay_Click(object sender, RoutedEventArgs e) => StartSimulation();
|
||||
private void btnPause_Click(object sender, RoutedEventArgs e) => PauseSimulation();
|
||||
private void btnStop_Click(object sender, RoutedEventArgs e) => StopSimulation();
|
||||
#endregion
|
||||
|
||||
// Receptor de evento de ciclo del reloj
|
||||
private void Tick(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Actualizar el ángulo de la barra motriz
|
||||
// con ω * t radianes
|
||||
(Elements[0] as Rod).Alpha = (float)(Omega * Chrono.Elapsed.TotalSeconds) % 2 * MathF.PI;
|
||||
|
||||
lock (Points)
|
||||
{
|
||||
// Eliminar puntos antiguos si se ha superado el límite de puntos especificado
|
||||
if (Points.Count > MaxPoints)
|
||||
Points.RemoveAt(0);
|
||||
}
|
||||
|
||||
// Ejecutar cálculos del sistema
|
||||
CalculateSystem();
|
||||
// Ejecutar renderizado del sistema en el hilo de principal
|
||||
this.Dispatcher.Invoke(() => DrawSystem());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Si la geometría es inválida, se lanzará una excepción
|
||||
// Detener simulación y mostrar el estado de error
|
||||
this.Dispatcher.Invoke(() =>
|
||||
{
|
||||
StopSimulation();
|
||||
SimulationStatus.Content = "Error";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#region Control de la simulación
|
||||
// Inicia la simulación
|
||||
public void StartSimulation()
|
||||
{
|
||||
SimulationStatus.Content = "Simulando...";
|
||||
t.Start();
|
||||
Chrono.Start();
|
||||
}
|
||||
// Pausa la simulación
|
||||
public void PauseSimulation()
|
||||
{
|
||||
SimulationStatus.Content = "En pausa";
|
||||
t.Stop();
|
||||
Chrono.Stop();
|
||||
}
|
||||
// Detiene la simulación y reinicia las variables
|
||||
// de tiempo
|
||||
public void StopSimulation()
|
||||
{
|
||||
SimulationStatus.Content = "Detenido";
|
||||
t.Stop();
|
||||
Chrono.Stop();
|
||||
Chrono.Reset();
|
||||
}
|
||||
#endregion
|
||||
|
||||
[STAThread]
|
||||
public void DrawSystem()
|
||||
{
|
||||
plot.Children.Clear(); // Borrar el contenido anterior del lienzo
|
||||
Center = new Vector2( // Calcular el centro del lienzo
|
||||
(float)plot.ActualWidth / 2,
|
||||
(float)plot.ActualHeight / 2);
|
||||
|
||||
foreach (var element in Elements) // Dibujar los elementos en el lienzo
|
||||
{
|
||||
if (element.GetType() == typeof(Rod))
|
||||
DrawElement<Rod>(element);
|
||||
if (element.GetType() == typeof(MotorRod))
|
||||
DrawElement<MotorRod>(element);
|
||||
}
|
||||
|
||||
// Registrar el punto que actualmente se ubica
|
||||
// en el extremo final de la barra I
|
||||
Points.Add(I.EndPosition);
|
||||
|
||||
lock (Points)
|
||||
{
|
||||
// Dibujar los puntos en el lienzo
|
||||
foreach (Vector2 point in Points)
|
||||
DrawPoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
#region Inserción de objetos
|
||||
public void DrawElement<Type>(object r) where Type : Rod
|
||||
{
|
||||
// Declarar una línea de color azul con 5px de grosos
|
||||
// segmentada entre los dos extremos de la barra.
|
||||
Line l = new Line()
|
||||
{
|
||||
Stroke = Brushes.Blue,
|
||||
StrokeThickness = 5,
|
||||
X1 = Center.X + (r as Type).StartPosition.X,
|
||||
X2 = Center.X + (r as Type).EndPosition.X,
|
||||
Y1 = Center.Y - (r as Type).StartPosition.Y,
|
||||
Y2 = Center.Y - (r as Type).EndPosition.Y,
|
||||
};
|
||||
// Añadir la barra al lienzo
|
||||
plot.Children.Add(l);
|
||||
}
|
||||
public void DrawPoint(Vector2 Location, double diameter = 5)
|
||||
{
|
||||
double r = diameter / 2; // Calcular radio del punto
|
||||
|
||||
// Declarar una elipse de color rojo con
|
||||
// el mismo ancho que altura (diámetro).
|
||||
Ellipse e = new Ellipse()
|
||||
{
|
||||
Fill = Brushes.Red,
|
||||
Width = diameter,
|
||||
Height = diameter
|
||||
};
|
||||
|
||||
// Añadir la elipse al lienzo
|
||||
plot.Children.Add(e);
|
||||
|
||||
// Situar la elipse dentro del lienzo.
|
||||
// Los ejes de coordenadas del lienzo tienen origen en
|
||||
// la esquina superior izquierda. El eje vertical se debe
|
||||
// invertir.
|
||||
Canvas.SetLeft(e, Center.X + Location.X - r);
|
||||
Canvas.SetTop(e, plot.ActualHeight - (Center.Y + Location.Y - r));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
15
JansenLegViewer/Motor.cs
Normal file
15
JansenLegViewer/Motor.cs
Normal file
@ -0,0 +1,15 @@
|
||||
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 MotorRod : Rod
|
||||
{
|
||||
public override Vector2 StartPosition { get; set; }
|
||||
}
|
||||
}
|
||||
199
JansenLegViewer/Rod.cs
Normal file
199
JansenLegViewer/Rod.cs
Normal file
@ -0,0 +1,199 @@
|
||||
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
|
||||
/// <summary>
|
||||
/// Barra a la que se une por el extremo inicial
|
||||
/// </summary>
|
||||
private Rod JointBegining { get; set; } = null;
|
||||
/// <summary>
|
||||
/// Barras a las cuales se une por el extremo final
|
||||
/// </summary>
|
||||
private List<Rod> JointsEnding { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Conecta el inicio de una barra a otra barra
|
||||
/// </summary>
|
||||
/// <param name="linkage">La barra a la cual se unirá esta</param>
|
||||
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;
|
||||
}
|
||||
/// <summary>
|
||||
/// Conecta el final de una barra a otra barra
|
||||
/// </summary>
|
||||
/// <param name="linkage">La barra a la cual se unirá esta</param>
|
||||
public void ConnectEnd(ref Rod linkage)
|
||||
{
|
||||
if (linkage == null) throw new NullReferenceException("The rod cannot be null");
|
||||
if (!JointsEnding.Contains(linkage))
|
||||
JointsEnding.Add(linkage);
|
||||
}
|
||||
/// <summary>
|
||||
/// Desconecta (si está conectada) la barra especificada
|
||||
/// </summary>
|
||||
/// <param name="linkage">Barra a ser desconectada</param>
|
||||
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";
|
||||
/// <summary>
|
||||
/// Longitud de la barra
|
||||
/// </summary>
|
||||
public float Length { get; set; } = 20;
|
||||
|
||||
private float? _alpha = null;
|
||||
/// <summary>
|
||||
/// Ángulo de rotación de la barra. Si no se establece será 0 radianes.
|
||||
/// </summary>
|
||||
public float Alpha
|
||||
{
|
||||
get => (_alpha != null) ? (float)_alpha : 0;
|
||||
set => _alpha = value;
|
||||
}
|
||||
|
||||
private Vector2? _startPosition = null;
|
||||
/// <summary>
|
||||
/// 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á
|
||||
/// </summary>
|
||||
public virtual Vector2 StartPosition
|
||||
{
|
||||
get => (_startPosition != null) ? (Vector2)_startPosition :
|
||||
(JointBegining != null) ? JointBegining.EndPosition : new Vector2(0, 0);
|
||||
set => _startPosition = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// Posición final.
|
||||
/// Se calcula a partir de la posición inicial, el ángulo y la longitud.
|
||||
/// </summary>
|
||||
public virtual Vector2 EndPosition
|
||||
{
|
||||
get => new Vector2(
|
||||
StartPosition.X + Length * MathF.Cos(Alpha),
|
||||
StartPosition.Y + Length * MathF.Sin(Alpha)
|
||||
);
|
||||
}
|
||||
/// <summary>
|
||||
/// Posición central.
|
||||
/// Se calcula a partir de la posición inicial, el ángulo y la longitud.
|
||||
/// </summary>
|
||||
public virtual Vector2 Center
|
||||
{
|
||||
get => new Vector2(
|
||||
StartPosition.X + Length * MathF.Cos(Alpha) / 2,
|
||||
StartPosition.Y + Length * MathF.Sin(Alpha) / 2
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Calcula el punto de intersección entre dos barras de longitud y eje de rotación determinados.
|
||||
/// </summary>
|
||||
/// <param name="A">Primera barra</param>
|
||||
/// <param name="B">Segunda barra</param>
|
||||
/// <param name="solution">En caso de que existan dos intersecciones, número de la intersección a elegir</param>
|
||||
/// <returns>El vector que representa el punto en el que se unen las dos barras.</returns>
|
||||
/// <exception cref="RodsDoNotIntersectException">Si las barras no intersecan en ningún punto, lanzará una excepción.</exception>
|
||||
/// <exception cref="IntersectionOutOfRangeException">Si las barras intersecan pero no se encuentra la intersección especificada, lanzará una excepción.</exception>
|
||||
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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calcula la diferencia entre dos vectores
|
||||
/// </summary>
|
||||
/// <param name="A">Vector final</param>
|
||||
/// <param name="B">Vector inicial</param>
|
||||
/// <returns>Vector final - Vector inicial</returns>
|
||||
public static Vector2 Delta(Vector2 A, Vector2 B) => A - B;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calcula para cada elemento el ángulo de la intersección de dos vectores a su posición respecto el 0 en el primer cuadrante.
|
||||
/// </summary>
|
||||
/// <param name="A">Vector 1</param>
|
||||
/// <param name="B">Vector 2</param>
|
||||
/// <param name="solution">Devuelve los angulos de cada eje a la intersección</param>
|
||||
/// <returns></returns>
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user