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 { /// /// Interaction logic for MainWindow.xaml /// 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 Elements { get; set; } = new List(); private List 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(element); if (element.GetType() == typeof(MotorRod)) DrawElement(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(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 } }