275 lines
10 KiB
C#
275 lines
10 KiB
C#
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<simBase> 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<Action> _deferredActions;
|
|
|
|
public simBotella(Simulation simulation, List<Action> 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<simBase>();
|
|
|
|
// ✅ 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// ✅ SIMPLIFICADO: RemoverBody que confía en BEPU para limpiar constraints
|
|
/// </summary>
|
|
public new void RemoverBody()
|
|
{
|
|
base.RemoverBody();
|
|
}
|
|
|
|
}
|
|
|
|
}
|