diff --git a/Q01-Cálculos/Program.cs b/Q01-Cálculos/Program.cs new file mode 100644 index 0000000..1e8e26b --- /dev/null +++ b/Q01-Cálculos/Program.cs @@ -0,0 +1,176 @@ +using System.Numerics; +using System.Runtime.Intrinsics; +using OpenTK; +using OpenTK.Windowing.Desktop; +using Q01_Cálculos; + +// Convenio de signos: +// X: Eje horizontal de la pantalla (izquierda a derecha positivo) +// Z: Eje vertical de la pantalla (abajo a arriba positivo) +// Y: Eje perpendicular a la pantalla (saliente positivo) + + +// Definimos una palanca en punto neutro, utilizando las medidas que +// hemos tomado para definir el volumen, la masa y el centro de gravedad +// de los componentes del ensamblaje que componen la palanca. + +// La masa la estimaremos como el volumen del componente por la densidad +// de su material. + +// Creamos una instancia de la clase Assembly para unir las piezas +// Para simplificar el programa, usamos técnicas de programación orientada a objetos (OOP) +// que nos permiten crear clases que describen los componentes del sistema, y las operaciones +// que se pueden realizar con ellos. Estas son "Assembly" y "Component". +Assembly Palanca = new() { Name = "Palanca de cambio" }; + +// Describimos físicamente los componentes, estimando su masa a partir del +// volumen y la densidad del material, y su centro de masas en base a sólidos +// tridimensionales sencillos: + +// Perimero definimos las densidades de los materiales +const float + DensAcero = 7800f, // kg/m3 + DensAbs = 1110f; // kg/m3 + +// Aproximamos la parte superior del pomo como una esfera +float diametro_esfera = 4.5e-2f; //m +float VolPomo1 = 4 / 3 * MathF.PI * MathF.Pow(diametro_esfera / 2, 3); +Palanca.components.Add(new Component() +{ + Name = "Parte Superior Pomo", + Mass = VolPomo1 * DensAbs, + CenterOfMass = new(0, -diametro_esfera / 2, 30e-2f) +}); +float LongitudPalanca = 30e-2f; //m + +// Aproximamos la parte inferior del pomo como un cilindro hueco por donde +// pasarán la varilla y su cubierta +float diametro_cilindro = 2.5e-2f; // m +float diametro_interno_cilindro = 1.5e-2f; // m +float altura_cilindro = 1.5e-2f; // m +float VolPomo2 = MathF.PI * (MathF.Pow(diametro_cilindro / 2, 2) - MathF.Pow(diametro_interno_cilindro / 2, 2)) * altura_cilindro; +Palanca.components.Add(new Component() +{ + Name = "Base Pomo", + Mass = VolPomo2 * DensAbs, + CenterOfMass = new(0, 0, LongitudPalanca - ((diametro_esfera / 2) - (altura_cilindro / 2))) +}); + +// Aproximamos la cubierta de la varilla como un cilindro hueco +float DiametroExterno2 = 1.5e-2f; //m +float DiametroInterno2 = 0.8e-2f; //m +float VolPalancaExterna = MathF.PI * (MathF.Pow(DiametroExterno2 / 2, 2) - MathF.Pow(DiametroInterno2 / 2, 2)) * LongitudPalanca; +Palanca.components.Add(new Component() +{ + Name = "Cubierta plástica de la varilla", + Mass = VolPalancaExterna * DensAbs, + CenterOfMass = new(0, 0, LongitudPalanca / 2) +}); + +// Aproximamos la varilla como un cilindro macizo +float DiametroExterno1 = 1.5e-2f; //m +float VolPalancaInterna = MathF.PI * MathF.Pow(DiametroExterno1 / 2, 2) * LongitudPalanca; +Palanca.components.Add(new Component() +{ + Name = "Varilla interna de la palanca", + Mass = VolPalancaInterna * DensAcero, + CenterOfMass = new(0, 0, LongitudPalanca / 2) +}); + +// Definimos las posicíones que hemos medido en el plano como tuplas (plano X Y) +(float, float) primera = (-6.5e-2f, -4.5e-2f); +(float, float) tercera = (0, -4.5e-2f); +(float, float) quinta = (4.5e-2f, -4.5e-2f); +(float, float) segunda = (-6.5e-2f, 10e-2f); +(float, float) cuarta = (0, 10e-2f); +(float, float) reversa = (4.5e-2f, 10e-2f); + +Console.WriteLine("================================================"); +Console.WriteLine("== Datos del sistema =="); +Console.WriteLine("================================================"); + +// Mostramos las propiedades físicas de la palanca por pantalla +Console.Write(Palanca.Summary()); + +// La fuerza que hace la palanca en reposo sobre la base es el peso de la palanca: +var fuerza_base = Palanca.Weight; +// Como está en reposo, no se trasalda, de modo que el sumatorio de fuerzas debe ser cero. +// Por ello, la reacción será el vector nulo menos la fuerza sobre la base (el peso de la palanca). +var reaccion_base = Vector3.Zero - fuerza_base; + +// El momento sobre la base será el producto vectorial de la posición del centro de masas +// por el peso de la palanca. +var momento_base = Vector3.Cross(Palanca.CenterOfMass, Palanca.Weight); +// Y como tampoco rota, significa que el sumatorio de momentos se cancela. +// Por ello, el momento de reacción que hace la base sobre la palanca será el vector nulo menos +// el momento que hace la palanca sobre la base. +var momento_reaccion = Vector3.Zero - momento_base; + +Console.WriteLine(); +Console.WriteLine("==============================================================================="); +Console.WriteLine("== Cálculos de fuerzas y momentos : Escalares / =="); +Console.WriteLine("==============================================================================="); +Console.WriteLine($"Fuerza que ejerce la barra sobre la base : {fuerza_base} N"); +Console.WriteLine($"Fuerza resistente que hace la base sobre la palanca : {reaccion_base} N"); +Console.WriteLine($"Momento que hace la barra sobre la base : {momento_base * 100} N·cm"); +Console.WriteLine($"Momento que hace la base sobre la palanca : {momento_reaccion * 100} N·cm"); +Console.WriteLine(); +Console.WriteLine("Momentos que hace la palanca cuando engrana una marcha:"); +Console.WriteLine($"Centro de masa 1a : {CentroDeMasasRotado(Palanca, LongitudPalanca, primera)}"); +Console.WriteLine($"Centro de masa 2a : {CentroDeMasasRotado(Palanca, LongitudPalanca, segunda)}"); +Console.WriteLine($"Centro de masa 3a : {CentroDeMasasRotado(Palanca, LongitudPalanca, tercera)}"); +Console.WriteLine($"Centro de masa 4a : {CentroDeMasasRotado(Palanca, LongitudPalanca, cuarta)}"); +Console.WriteLine($"Centro de masa 5a : {CentroDeMasasRotado(Palanca, LongitudPalanca, quinta)}"); +Console.WriteLine($"Centro de masa Reversa : {CentroDeMasasRotado(Palanca, LongitudPalanca, reversa)}"); +Console.WriteLine(); + +//Simulation simulación = new Simulation(new GameWindowSettings() { UpdateFrequency = 20, RenderFrequency = 20 }, new NativeWindowSettings() { Size = new OpenTK.Mathematics.Vector2i(500, 500) }); +//simulación.Run(); + + +// Obtiene el vector de longitud determinada que corta los planos X e Y por +// los puntos especificados +Vector3 Position(float X, float Y, float vectorLength) +{ + // Calculamos la longitud del segmento de la base de nuestro vector resultado como + // la hipotenusa del triangulo de catetos X,Y (usando Pitágoras): + float _base = MathF.Sqrt(MathF.Pow(X, 2) + MathF.Pow(Y, 2)); + // Calculamos la componente Z como el cateto restante del triangulo cuya hipotenusa + // es el módulo de nuestro vector, con la base calculada en el paso anterior. + float _z = MathF.Sqrt(MathF.Pow(vectorLength, 2) - MathF.Pow(_base, 2)); + // Construimos y devolvemos un vector con el valor Z hallado. + return new Vector3(X, Y, _z); +} + +// Obtiene el vector que representa el centro de masas de la palanca cuando se mueve en el plano XY +// hacia la posición de la marcha. +Vector3 CentroDeMasasRotado(IRigidBody componente, float longitud, (float, float) posicionesMarcha) +{ + Vector3 rcm_rotado; + + // Calculamos el vector de la posición de la palanca con la marcha metida. + var posiciónDestino = Position(posicionesMarcha.Item1, posicionesMarcha.Item2, longitud); + // Calculamos el eje de la rotación que se debe producir para que el vector de posición + // realice el movimiento para engranar la marcha. + // El eje de rotación lo podemos definir como el vector unitario perpendicular al vector rotado + // respecto al vector original. Para ello podemos usar el producto vectorial (producto en cruz) + // normalizado, para convertirlo en vector unitario. + var ejeDeRotación = Vector3.Normalize(Vector3.Cross(componente.CenterOfMass, posiciónDestino)); + + // El siguiente caso es determinar el ángulo que rotó el vector para orientarse a la posición + // final. Para ello usamos una formula derivada de la definición de producto escalar. + // El producti escalar se puede calcular como = a * b * el coseno del angulo que forman, de modo que: + var ángulo = MathF.Acos( + Vector3.Dot(componente.CenterOfMass, posiciónDestino) / + (componente.CenterOfMass.Length() * posiciónDestino.Length())); + + // A continuación, crearemos un quaternión a partir del eje y el ángulo de rotación hallados: + Quaternion rotación = Quaternion.CreateFromAxisAngle(ejeDeRotación, ángulo); + + // Y finalmente aplicamos la transformación vectorial del centro de masas para aplicar la misma + // rotación que ha sufrido el vector que representa la orientación de la palanca de cambio. + rcm_rotado = Vector3.Transform(componente.CenterOfMass, rotación); + + // Devolvemos el centro de masas rotado como resultado + return rcm_rotado; +} \ No newline at end of file