using BepuPhysics.Collidables; using BepuPhysics.Constraints; using BepuPhysics; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; using System.Threading.Tasks; using DocumentFormat.OpenXml.Spreadsheet; using System.Windows.Media.Media3D; using System.Windows.Shapes; namespace CtrEditor.Simulacion { public class simBotella : simBase { public float Radius; public float Height; // Altura para la visualización del cilindro en Helix private float _mass; public bool Descartar = false; public int isOnTransports; public List ListOnTransports; public bool isRestricted; public bool isNoMoreRestricted; public simTransporte ConveyorRestrictedTo; public float OverlapPercentage; public float _neckRadius; public bool isOnBrakeTransport; // Nueva propiedad para marcar si está en un transporte con freno public simTransporte CurrentBrakeTransport { get; set; } // ✅ NUEVA: Referencia al transporte de freno actual // ✅ NUEVO: Propiedades para control de flotación/presión public float? LastTransportCollisionZ { get; set; } public int PressureBuildup { get; set; } = 0; public bool IsTouchingTransport { get; set; } = false; // ✅ NUEVO: Propiedades para la física personalizada public float BaseInverseMass { get; private set; } public Vector3 InitialPosition3D { get; private set; } private List _deferredActions; public simBotella(Simulation simulation, List deferredActions, float diameter, Vector2 position, float mass, float neckRadius = 0) { _simulation = simulation; _deferredActions = deferredActions; Radius = diameter / 2f; Height = diameter; // Altura igual al diámetro para mantener proporciones similares _mass = mass; _neckRadius = neckRadius; ListOnTransports = new List(); // ✅ USAR COORDINATECONVERTER para conversión centralizada var position3D = new Vector3(position.X, CoordinateConverter.WpfYToBepuY(position.Y), Radius + zPos_Transporte + zAltura_Transporte); Create(position3D); } public float CenterX { get { return GetPosition().X; } set { var pos = GetPosition(); SetPosition(value, pos.Y, pos.Z); } } public float CenterY { get { // ✅ USAR COORDINATECONVERTER para conversión centralizada return CoordinateConverter.BepuYToWpfY(GetPosition().Y); } set { var pos = GetPosition(); // ✅ USAR COORDINATECONVERTER para conversión centralizada SetPosition(pos.X, CoordinateConverter.WpfYToBepuY(value), pos.Z); } } public Vector2 Center { get { var pos3D = GetPosition(); // ✅ USAR COORDINATECONVERTER para conversión centralizada return CoordinateConverter.BepuVector3ToWpfVector2(pos3D); } set { // Mantener la Z actual, solo cambiar X, Y var currentPos = GetPosition(); // ✅ USAR COORDINATECONVERTER para conversión centralizada SetPosition(value.X, CoordinateConverter.WpfYToBepuY(value.Y), currentPos.Z); } } public float Mass { get { if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle)) { var bodyReference = _simulation.Bodies.GetBodyReference(BodyHandle); return 1f / bodyReference.LocalInertia.InverseMass; } return _mass; } set { _mass = value; if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle)) { var cylinder = new Cylinder(Radius, Height); var inertia = cylinder.ComputeInertia(_mass); // Modificamos el tensor de inercia para evitar que la botella vuelque. var inverseInertia = inertia.InverseInertiaTensor; inverseInertia.XX = 0f; // Resistencia infinita a la rotación en el eje X (vuelco). inverseInertia.YY = 0f; // Resistencia infinita a la rotación en el eje Y (vuelco). // La resistencia en ZZ (eje Z) se mantiene, permitiendo que la botella gire sobre su base. inertia.InverseInertiaTensor = inverseInertia; _simulation.Bodies.SetLocalInertia(BodyHandle, inertia); } } } private void Create(Vector3 position) { RemoverBody(); var cylinder = new Cylinder(Radius, Height); var shapeIndex = _simulation.Shapes.Add(cylinder); // 1. Creamos el cuerpo con una inercia por defecto. var defaultInertia = cylinder.ComputeInertia(_mass); var activityDescription = new BodyActivityDescription(-1f); var bodyDescription = BodyDescription.CreateDynamic( new RigidPose(position), new BodyVelocity(), defaultInertia, new CollidableDescription(shapeIndex, 0), activityDescription ); BodyHandle = _simulation.Bodies.Add(bodyDescription); _bodyCreated = true; // 2. Inmediatamente después de crearlo, aplicamos la inercia personalizada. // Esto asegura que SetLocalInertia se llame para todos los objetos nuevos. var inertia = cylinder.ComputeInertia(_mass); var inverseInertia = inertia.InverseInertiaTensor; inverseInertia.XX = 0f; inverseInertia.YY = 0f; inertia.InverseInertiaTensor = inverseInertia; _simulation.Bodies.SetLocalInertia(BodyHandle, inertia); // ✅ NUEVO: Almacenar la masa inversa base después de crear el cuerpo // Esto nos sirve como referencia para la lógica de masa dinámica. var bodyReference = _simulation.Bodies.GetBodyReference(BodyHandle); this.BaseInverseMass = bodyReference.LocalInertia.InverseMass; // ✅ CORREGIDO: Usar el diccionario del SimulationManager y la clave .Value del handle. if (_simulationManager != null) _simulationManager.CollidableData[BodyHandle.Value] = this; } public void SetDiameter(float diameter) { Radius = diameter / 2f; Height = diameter; // Mantener altura igual al diámetro para proporciones consistentes // ✅ CORREGIDO: Usar ChangeBodyShape para limpiar la forma anterior if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle)) { var cylinder = new Cylinder(Radius, Height); var shapeIndex = _simulation.Shapes.Add(cylinder); ChangeBodyShape(shapeIndex); } } public void SetHeight(float height) { Height = height; // ✅ CORREGIDO: Usar ChangeBodyShape para limpiar la forma anterior if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle)) { var cylinder = new Cylinder(Radius, Height); var shapeIndex = _simulation.Shapes.Add(cylinder); ChangeBodyShape(shapeIndex); } } public void SetMass(float mass) { Mass = mass; } /// /// Limita la rotación de la botella solo al plano XY (siempre "de pie") /// Esto simplifica enormemente la simulación y es más realista para botellas /// public void LimitRotationToXYPlane() { if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle)) { var bodyReference = _simulation.Bodies.GetBodyReference(BodyHandle); // Extraer solo la rotación en Z (plano XY) y eliminar las rotaciones en X e Y var currentOrientation = bodyReference.Pose.Orientation; // Convertir a ángulo en Z solamente var rotationZ = GetRotationZ(); // ✅ CORREGIDO: Calificar explícitamente para evitar ambigüedad de tipos. var correctedOrientation = System.Numerics.Quaternion.CreateFromAxisAngle(Vector3.UnitZ, rotationZ); // Aplicar la orientación corregida bodyReference.Pose.Orientation = correctedOrientation; // También limitar la velocidad angular a solo rotación en Z var angularVelocity = bodyReference.Velocity.Angular; bodyReference.Velocity.Angular = new Vector3(0, 0, angularVelocity.Z); } } public void ApplyLinearVelocity(Vector3 velocity) { if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle)) { var bodyReference = _simulation.Bodies.GetBodyReference(BodyHandle); bodyReference.Velocity.Linear = velocity; } } public Vector3 GetLinearVelocity() { if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle)) { var bodyReference = _simulation.Bodies.GetBodyReference(BodyHandle); return bodyReference.Velocity.Linear; } return Vector3.Zero; } public void CenterFixtureOnConveyor() { // Implementar lógica de centrado si es necesario } public bool IsOnAnyTransport() { return isOnTransports > 0; } /// /// ✅ SIMPLIFICADO: RemoverBody que confía en BEPU para limpiar constraints /// public new void RemoverBody() { base.RemoverBody(); } } }