Eliminacion de LinearAxisMotor por solo velocity y fricciones normales en los cuerpos
This commit is contained in:
parent
3eee0e3d9b
commit
de0e3ce5f0
|
@ -0,0 +1,10 @@
|
|||
### Aqui se mantiene la memoria de evolucion de las distintas decisiones que fueron tomadas y porque
|
||||
|
||||
* Se usan esferas en vez de cilindros para mejorar la eficiencia. En el Debug3D si se usan cilindros
|
||||
|
||||
* Se usaron LinearAxisMotors originalmente sobre las botellas para crear un movimiento segun las colisicones con los transportes. Luego se busco un sistema que represente mejor la fisica y por cuestiones de eficiencia en vez de crear placas en movimiento real, se usaron cuerpos Box y Triangulos kinematic con velocidad lineal o angular per se interviene en el Integrador para que antes de integrar la velocidad se quite la velocidad y antes del proximo Step se vuelve a aplicar la velocidad. Esto permite que el integrador no mueva los objetos kinetic pero si aplique velocidad por friccion a los objetos dinamic ( simBotella )
|
||||
|
||||
* Originalmente se habia puesto todos los objetos en awaken para poder usar las colisiones constantemente incluso con objetos en modo sleep para que los simBarrera puedan detectar las colisiones. Ahora que se usar RayCast podemos dejar que las simBotellas se duerman
|
||||
|
||||
* La unica clase que se ha terminado de refactorizar respecto a el cambio de coordenadas es simBarrera y ucPhotocell. El concepto es poder separar usando metodos de SimulationManagerBEPU y estructuras como BarreraData la conversion de coordenadas de WPF a coordenadas BEPU. Para esto se usa CoordinateConverter que permite bidireccionalmente convertir las coordenadas pero esto solo se debe usar en SimulationManagerBEPU las clases derivadas de osBase solo deben manejar coordenadas WPF, mientras que las clases dervadas de simBase solo deben almacenar y usar coordenadas BEPU. La z tambien es algo que se debe transferir a SimulationManagerBEPU ya que los objetos simBase deberian recibir tambien sus coordenadas de Z desde SimulationManagerBEPU y ser SimulationManagerBEPU el que gestione las Z.
|
||||
|
|
@ -114,10 +114,11 @@ namespace CtrEditor.Simulacion
|
|||
// ✅ DESCARTES como sensores puros
|
||||
if (descarteA != null || descarteB != null)
|
||||
{
|
||||
var descarte = descarteA ?? descarteB;
|
||||
if (botella != null)
|
||||
{
|
||||
_simulationManager.RegisterDescarteContact(descarte, botella);
|
||||
// ✅ CORREGIDO: Marcar la botella para eliminar directamente, en lugar de registrar el contacto.
|
||||
// Esto es más directo y evita el error de compilación.
|
||||
botella.Descartar = true;
|
||||
}
|
||||
return false; // NO generar contacto físico
|
||||
}
|
||||
|
@ -128,48 +129,41 @@ namespace CtrEditor.Simulacion
|
|||
var curveA = GetCurveFromCollidable(pair.A);
|
||||
var curveB = GetCurveFromCollidable(pair.B);
|
||||
|
||||
// ✅ CONTACTO BOTELLA-TRANSPORTE: Crear o actualizar motor inmediatamente
|
||||
// ✅ CONTACTO BOTELLA-TRANSPORTE: USAR FRICCIÓN Y VELOCIDAD CINEMÁTICA
|
||||
if (botella != null && (transportA != null || transportB != null))
|
||||
{
|
||||
var transport = transportA ?? transportB;
|
||||
// La velocidad del cuerpo cinemático del transporte se establece directamente
|
||||
// en la clase simTransporte. El motor de físicas se encarga del resto.
|
||||
|
||||
// ✅ CREAR O ACTUALIZAR MOTOR DINÁMICO INMEDIATAMENTE
|
||||
transport.UpdateCachedProperties();
|
||||
var direction = transport.DirectionVector;
|
||||
var speed = transport.Speed;
|
||||
botella.CreateOrUpdateMotor(transport, direction, speed);
|
||||
|
||||
//Fricción alta para transportes
|
||||
pairMaterial.FrictionCoefficient = 0.01f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||
// ✅ Fricción ajustada para un arrastre firme pero no excesivo.
|
||||
pairMaterial.FrictionCoefficient = 1.5f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 2.0f;
|
||||
pairMaterial.SpringSettings = new SpringSettings(30, 1);
|
||||
}
|
||||
// ✅ CONTACTO BOTELLA-CURVA: Crear o actualizar motor inmediatamente
|
||||
// ✅ CONTACTO BOTELLA-CURVA: USAR FRICCIÓN Y VELOCIDAD CINEMÁTICA
|
||||
else if (botella != null && (curveA != null || curveB != null))
|
||||
{
|
||||
var curve = curveA ?? curveB;
|
||||
|
||||
// ✅ CREAR O ACTUALIZAR MOTOR DINÁMICO INMEDIATAMENTE
|
||||
var direction = _simulationManager.CalculateCurveDirectionFromBottlePosition(curve, botella);
|
||||
botella.CreateOrUpdateMotor(curve, direction, curve.Speed);
|
||||
|
||||
// Fricción alta para curvas
|
||||
pairMaterial.FrictionCoefficient = 0.01f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||
// El motor de físicas usará la velocidad angular del cuerpo cinemático de la curva
|
||||
// para calcular las fuerzas de fricción y arrastrar la botella.
|
||||
|
||||
// ✅ Fricción ajustada para un arrastre firme pero no excesivo.
|
||||
pairMaterial.FrictionCoefficient = 1.5f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 2.0f;
|
||||
pairMaterial.SpringSettings = new SpringSettings(30, 1);
|
||||
}
|
||||
// ✅ CONTACTO BOTELLA-GUÍA: Configuración específica de fricción
|
||||
else if (botella != null && (GetGuiaFromCollidable(pair.A) != null || GetGuiaFromCollidable(pair.B) != null))
|
||||
{
|
||||
// Configuración específica para guías usando propiedades configurables
|
||||
pairMaterial.FrictionCoefficient = 0.01f;
|
||||
// Fricción baja para las guías, deben deslizar, no arrastrar.
|
||||
pairMaterial.FrictionCoefficient = 0.1f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||
}
|
||||
// Ajustes básicos para otras botellas
|
||||
else if (botella != null)
|
||||
{
|
||||
pairMaterial.FrictionCoefficient = 0.01f;
|
||||
// Fricción moderada para colisiones entre botellas.
|
||||
pairMaterial.FrictionCoefficient = 0.3f;
|
||||
pairMaterial.MaximumRecoveryVelocity = 1f;
|
||||
pairMaterial.SpringSettings = new SpringSettings(80, 6);
|
||||
}
|
||||
|
@ -305,30 +299,54 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
}
|
||||
|
||||
// ✅ REESCRITO COMPLETAMENTE: para corregir errores de compilación y aplicar la lógica de amortiguamiento de forma segura.
|
||||
public void IntegrateVelocity(Vector<int> bodyIndices, Vector3Wide position, QuaternionWide orientation, BodyInertiaWide localInertia, Vector<int> integrationMask, int workerIndex, Vector<float> dt, ref BodyVelocityWide velocity)
|
||||
{
|
||||
// Aplicar gravedad
|
||||
// 1. APLICAR GRAVEDAD Y AMORTIGUAMIENTO ESTÁNDAR (VECTORIZADO)
|
||||
var gravityWide = Vector3Wide.Broadcast(Gravity);
|
||||
velocity.Linear += gravityWide * dt;
|
||||
velocity.Linear.X += gravityWide.X * dt;
|
||||
velocity.Linear.Y += gravityWide.Y * dt;
|
||||
velocity.Linear.Z += gravityWide.Z * dt;
|
||||
|
||||
// ✅ ELIMINADO COMPLETAMENTE - ya no se necesita lógica especial de frenado
|
||||
// El sistema LinearAxisMotor maneja automáticamente todas las fuerzas
|
||||
var linearDampingFactor = new Vector<float>(MathF.Pow(MathHelper.Clamp(1 - LinearDamping, 0, 1), dt[0]));
|
||||
var angularDampingFactor = new Vector<float>(MathF.Pow(MathHelper.Clamp(1 - AngularDamping, 0, 1), dt[0]));
|
||||
|
||||
// Aplicar amortiguamiento lineal y angular para simular resistencia del aire
|
||||
// Esto es crucial para que los cilindros se detengan de forma realista
|
||||
var linearDampingWide = Vector<float>.One * LinearDamping;
|
||||
var angularDampingWide = Vector<float>.One * AngularDamping;
|
||||
velocity.Linear *= linearDampingFactor;
|
||||
velocity.Angular *= angularDampingFactor;
|
||||
|
||||
velocity.Linear *= linearDampingWide;
|
||||
velocity.Angular *= angularDampingWide; // ¡Esto es clave para detener rotaciones infinitas!
|
||||
// 2. LÓGICA PERSONALIZADA PARA BOTELLAS (ESCALAR)
|
||||
const float bottleExtraZAngularDamping = 0.8f;
|
||||
var bottleZAngularDampingFactor = MathF.Pow(MathHelper.Clamp(1 - bottleExtraZAngularDamping, 0, 1), dt[0]);
|
||||
|
||||
// Recorremos cada "carril" del lote de simulación.
|
||||
for (int i = 0; i < Vector<int>.Count; ++i)
|
||||
{
|
||||
if (integrationMask[i] != 0)
|
||||
{
|
||||
var bodyIndex = bodyIndices[i];
|
||||
// Asegurarse de que el índice del cuerpo es válido para el conjunto activo.
|
||||
if (bodyIndex < _simulationManager.simulation.Bodies.ActiveSet.Count)
|
||||
{
|
||||
var handle = _simulationManager.simulation.Bodies.ActiveSet.IndexToHandle[bodyIndex];
|
||||
if (_simulationManager.CollidableData.TryGetValue(handle.Value, out simBase baseObj) && baseObj is simBotella)
|
||||
{
|
||||
// B. DAMPING ANGULAR EXTRA EN EJE Z
|
||||
// Para modificar un solo carril de un Vector<T>, la forma más segura es extraer
|
||||
// los valores a un array, modificar el índice y crear un nuevo Vector<T>.
|
||||
var zVel = new float[Vector<float>.Count];
|
||||
velocity.Angular.Z.CopyTo(zVel, 0);
|
||||
zVel[i] *= bottleZAngularDampingFactor;
|
||||
velocity.Angular.Z = new Vector<float>(zVel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class SimulationManagerBEPU
|
||||
public class SimulationManagerBEPU : IDisposable
|
||||
{
|
||||
public Simulation simulation;
|
||||
public List<simBase> Cuerpos;
|
||||
|
@ -368,6 +386,10 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
private object _contactsLock = new object();
|
||||
|
||||
// ✅ NUEVO: Diccionario para mapear BodyHandle a simBase para acceso O(1).
|
||||
public Dictionary<int, simBase> CollidableData = new Dictionary<int, simBase>();
|
||||
public float GlobalTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene el objeto simBase correspondiente a un BodyHandle
|
||||
/// </summary>
|
||||
|
@ -386,37 +408,8 @@ namespace CtrEditor.Simulacion
|
|||
return simBase as simCurve;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Obtiene todas las botellas que están conectadas a un elemento específico
|
||||
/// </summary>
|
||||
/// <param name="element">Elemento (simTransporte o simCurve) del cual obtener las botellas conectadas</param>
|
||||
/// <returns>Lista de botellas conectadas al elemento</returns>
|
||||
public List<simBotella> GetBottlesConnectedToElement(simBase element)
|
||||
{
|
||||
var connectedBottles = new List<simBotella>();
|
||||
|
||||
try
|
||||
{
|
||||
if (element == null) return connectedBottles;
|
||||
|
||||
// ✅ BUSCAR BOTELLAS QUE TENGAN ESTE ELEMENTO COMO TARGET
|
||||
foreach (var bottle in Cuerpos.OfType<simBotella>())
|
||||
{
|
||||
if (bottle != null && bottle.HasMotor && bottle.CurrentMotorTarget == element)
|
||||
{
|
||||
connectedBottles.Add(bottle);
|
||||
}
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[GetBottlesConnectedToElement] Encontradas {connectedBottles.Count} botellas conectadas a {element.GetType().Name}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[GetBottlesConnectedToElement] ❌ ERROR: {ex.Message}");
|
||||
}
|
||||
|
||||
return connectedBottles;
|
||||
}
|
||||
// ✅ ELIMINADO: GetBottlesConnectedToElement ya no es necesario.
|
||||
// La conexión ahora es implícita a través de la física de contacto.
|
||||
|
||||
// ✅ NUEVOS - gestión automática de clasificación
|
||||
private void RegisterObjectHandle(simBase obj)
|
||||
|
@ -543,7 +536,55 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
simulation = Simulation.Create(bufferPool, narrowPhaseCallbacks, poseIntegratorCallbacks, solveDescription);
|
||||
|
||||
// ✅ NUEVO: Suscribirse a los eventos del solver para controlar la velocidad de los cuerpos cinemáticos.
|
||||
simulation.Solver.SubstepStarted += OnSubstepStarted;
|
||||
simulation.Solver.SubstepEnded += OnSubstepEnded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Se ejecuta al inicio de cada sub-paso de la simulación.
|
||||
/// Restaura las velocidades de los cuerpos cinemáticos para que el solver pueda usarlas para calcular la fricción.
|
||||
/// </summary>
|
||||
private void OnSubstepStarted(int substepIndex)
|
||||
{
|
||||
foreach (var transport in Cuerpos.OfType<simTransporte>())
|
||||
{
|
||||
transport.ActualizarVelocidadCinematica();
|
||||
}
|
||||
foreach (var curve in Cuerpos.OfType<simCurve>())
|
||||
{
|
||||
curve.ActualizarVelocidadCinematica();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Se ejecuta al final de cada sub-paso de la simulación, pero antes de la integración de la pose.
|
||||
/// Pone a cero las velocidades de los cuerpos cinemáticos para evitar que el PoseIntegrator mueva su posición.
|
||||
/// </summary>
|
||||
private void OnSubstepEnded(int substepIndex)
|
||||
{
|
||||
foreach (var transport in Cuerpos.OfType<simTransporte>())
|
||||
{
|
||||
if (transport.BodyHandle.Value >= 0 && simulation.Bodies.BodyExists(transport.BodyHandle))
|
||||
{
|
||||
var body = simulation.Bodies.GetBodyReference(transport.BodyHandle);
|
||||
if (body.Kinematic)
|
||||
{
|
||||
body.Velocity.Linear = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var curve in Cuerpos.OfType<simCurve>())
|
||||
{
|
||||
if (curve.BodyHandle.Value >= 0 && simulation.Bodies.BodyExists(curve.BodyHandle))
|
||||
{
|
||||
var body = simulation.Bodies.GetBodyReference(curve.BodyHandle);
|
||||
if (body.Kinematic)
|
||||
{
|
||||
body.Velocity.Angular = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
|
@ -614,6 +655,10 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
// Limpiar visualización 3D
|
||||
Visualization3DManager?.Clear();
|
||||
|
||||
// ✅ NUEVO: Limpiar también el nuevo diccionario.
|
||||
CollidableData.Clear();
|
||||
GlobalTime = 0f;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -625,7 +670,7 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
try
|
||||
{
|
||||
// ✅ INICIALIZAR PROPIEDADES CACHEADAS
|
||||
// ✅ INICIALIZAR PROPIEDADES CACHEADAS Y VELOCIDADES CINEMÁTICAS
|
||||
foreach (var transport in Cuerpos.OfType<simTransporte>())
|
||||
{
|
||||
transport.UpdateCachedProperties();
|
||||
|
@ -633,8 +678,7 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
foreach (var curve in Cuerpos.OfType<simCurve>())
|
||||
{
|
||||
// ✅ CORREGIDO: Usar método público para actualizar velocidad
|
||||
curve.SetSpeed(curve.Speed); // ✅ SIMPLIFICADO: Reinicializar velocidad
|
||||
curve.SetSpeed(curve.Speed);
|
||||
}
|
||||
|
||||
stopwatch.Start();
|
||||
|
@ -727,13 +771,6 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
}
|
||||
|
||||
// ✅ NUEVO: Detener motores de botellas que no están en contacto con elementos
|
||||
// Esto se ejecuta cada 10 frames para eficiencia
|
||||
if (_frameCount % 10 == 0)
|
||||
{
|
||||
//StopMotorsForBottlesNotInContact();
|
||||
}
|
||||
|
||||
// ✅ CONSERVAR - sistemas que funcionan bien
|
||||
ProcessBarreraContacts();
|
||||
ProcessDescarteContacts();
|
||||
|
@ -960,223 +997,6 @@ namespace CtrEditor.Simulacion
|
|||
return new BarreraData(false, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Detiene motores de botellas que no están en contacto con elementos
|
||||
/// </summary>
|
||||
private void StopMotorsForBottlesNotInContact()
|
||||
{
|
||||
try
|
||||
{
|
||||
var allBottles = Cuerpos.OfType<simBotella>().ToList();
|
||||
|
||||
foreach (var bottle in allBottles)
|
||||
{
|
||||
if (bottle != null && bottle.HasMotor && bottle.IsOnElement)
|
||||
{
|
||||
// ✅ VERIFICAR SI LA BOTELLA ESTÁ REALMENTE EN CONTACTO CON ALGÚN ELEMENTO
|
||||
bool isInContact = false;
|
||||
|
||||
// Verificar contacto con transportes
|
||||
foreach (var transport in Cuerpos.OfType<simTransporte>())
|
||||
{
|
||||
if (IsBottleInContactWithTransport(bottle, transport))
|
||||
{
|
||||
isInContact = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar contacto con curvas
|
||||
if (!isInContact)
|
||||
{
|
||||
foreach (var curve in Cuerpos.OfType<simCurve>())
|
||||
{
|
||||
if (IsBottleInContactWithCurve(bottle, curve))
|
||||
{
|
||||
isInContact = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ DETENER MOTOR SI NO ESTÁ EN CONTACTO
|
||||
if (!isInContact)
|
||||
{
|
||||
bottle.StopMotor();
|
||||
System.Diagnostics.Debug.WriteLine($"[StopMotorsForBottlesNotInContact] ⏹️ Detenido: {bottle.BodyHandle}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[StopMotorsForBottlesNotInContact] ❌ ERROR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Verifica si una botella está en contacto con un transporte
|
||||
/// </summary>
|
||||
private bool IsBottleInContactWithTransport(simBotella bottle, simTransporte transport)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bottlePos = bottle.GetPosition();
|
||||
var transportPos = transport.GetPosition();
|
||||
var distance = Vector2.Distance(
|
||||
new Vector2(bottlePos.X, bottlePos.Y),
|
||||
new Vector2(transportPos.X, transportPos.Y)
|
||||
);
|
||||
|
||||
// Verificar si está dentro del área del transporte + tolerancia
|
||||
var maxDistance = Math.Max(transport.Width, transport.Height) / 2f + bottle.Radius + 0.1f;
|
||||
return distance <= maxDistance;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Verifica si una botella está en contacto con una curva
|
||||
/// </summary>
|
||||
private bool IsBottleInContactWithCurve(simBotella bottle, simCurve curve)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bottlePos = bottle.GetPosition();
|
||||
var curveCenter = curve.CurveCenter; // ✅ NUEVO: Usar centro real
|
||||
var distance = Vector2.Distance(
|
||||
new Vector2(bottlePos.X, bottlePos.Y),
|
||||
new Vector2(curveCenter.X, curveCenter.Y)
|
||||
);
|
||||
|
||||
// Verificar si está dentro del área de la curva + tolerancia
|
||||
var maxDistance = curve.OuterRadius + bottle.Radius + 0.1f;
|
||||
return distance <= maxDistance;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Calcula la dirección tangencial específica basada en la posición de la botella
|
||||
/// </summary>
|
||||
public Vector3 CalculateCurveDirectionFromBottlePosition(simCurve curve, simBotella bottle)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ✅ NUEVO: Usar el centro real almacenado de la curva
|
||||
var curveCenter = curve.CurveCenter;
|
||||
var bottlePosition = bottle.GetPosition();
|
||||
|
||||
// Calcular el vector desde el centro de la curva hasta la botella (en el plano XY)
|
||||
var radiusVector = new Vector3(bottlePosition.X - curveCenter.X, bottlePosition.Y - curveCenter.Y, 0f);
|
||||
var radius = radiusVector.Length();
|
||||
|
||||
if (radius < 0.001f)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CalculateCurveDirectionFromBottlePosition] ⚠️ Botella muy cerca del centro de la curva");
|
||||
return Vector3.UnitX; // Fallback
|
||||
}
|
||||
|
||||
// Normalizar el vector radial
|
||||
var normalizedRadius = radiusVector / radius;
|
||||
|
||||
// Calcular la dirección tangencial (perpendicular al radio)
|
||||
// En coordenadas 2D: si r = (x, y), entonces t = (-y, x) es tangente
|
||||
var tangentDirection = new Vector3(-normalizedRadius.Y, normalizedRadius.X, 0f);
|
||||
|
||||
// Verificar que la dirección tangencial apunte en el sentido correcto según la velocidad de la curva
|
||||
if (curve.Speed < 0)
|
||||
{
|
||||
//tangentDirection = -tangentDirection;
|
||||
}
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine($"[CalculateCurveDirectionFromBottlePosition] 📐 Dirección calculada:");
|
||||
//System.Diagnostics.Debug.WriteLine($" Centro curva: {curveCenter}");
|
||||
//System.Diagnostics.Debug.WriteLine($" Posición botella: {bottlePosition}");
|
||||
//System.Diagnostics.Debug.WriteLine($" Radio: {radius:F3}");
|
||||
//System.Diagnostics.Debug.WriteLine($" Vector radial: {normalizedRadius}");
|
||||
//System.Diagnostics.Debug.WriteLine($" Dirección tangencial: {tangentDirection} (Longitud: {tangentDirection.Length():F3})");
|
||||
|
||||
return -tangentDirection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CalculateCurveDirectionFromBottlePosition] ❌ ERROR: {ex.Message}");
|
||||
return Vector3.UnitX; // Fallback
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Clear();
|
||||
simulation?.Dispose();
|
||||
bufferPool?.Clear();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registra un contacto entre una barrera y una botella para detección de paso
|
||||
/// </summary>
|
||||
/// <param name="barrera">Barrera que detecta el paso</param>
|
||||
/// <param name="botella">Botella que está pasando</param>
|
||||
internal void RegisterBarreraContact(simBarrera barrera, simBotella botella)
|
||||
{
|
||||
if (barrera != null && botella != null)
|
||||
{
|
||||
lock (_contactsLock)
|
||||
{
|
||||
if (!_barreraContacts.ContainsKey(barrera))
|
||||
{
|
||||
_barreraContacts[barrera] = new List<simBotella>();
|
||||
}
|
||||
|
||||
if (!_barreraContacts[barrera].Contains(botella))
|
||||
{
|
||||
_barreraContacts[barrera].Add(botella);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registra un contacto entre un descarte y una botella para marcar eliminación
|
||||
/// </summary>
|
||||
/// <param name="descarte">Descarte que detecta la botella</param>
|
||||
/// <param name="botella">Botella que debe ser eliminada</param>
|
||||
public void RegisterDescarteContact(simDescarte descarte, simBotella botella)
|
||||
{
|
||||
if (descarte != null && botella != null)
|
||||
{
|
||||
lock (_contactsLock)
|
||||
{
|
||||
// Marcar la botella para eliminación
|
||||
_botellasParaEliminar.Add(botella);
|
||||
botella.Descartar = true;
|
||||
|
||||
// También registrar el contacto para la lista del descarte
|
||||
if (!_descarteContacts.ContainsKey(descarte))
|
||||
{
|
||||
_descarteContacts[descarte] = new List<simBotella>();
|
||||
}
|
||||
|
||||
if (!_descarteContacts[descarte].Contains(botella))
|
||||
{
|
||||
_descarteContacts[descarte].Add(botella);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Procesa todas las barreras usando RayCast de BEPU
|
||||
/// Elimina la dependencia de contactos físicos y la necesidad de mantener botellas despiertas
|
||||
|
@ -1258,12 +1078,6 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
// Eliminar botellas marcadas para eliminación (después de procesamiento)
|
||||
RemoveMarkedBottles();
|
||||
|
||||
// Limpiar contactos ya que no los usamos más
|
||||
lock (_contactsLock)
|
||||
{
|
||||
_descarteContacts.Clear();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1361,6 +1175,15 @@ namespace CtrEditor.Simulacion
|
|||
List<simBotella> botellasAEliminar;
|
||||
lock (_contactsLock)
|
||||
{
|
||||
// ✅ NUEVO: Añadir botellas marcadas como "Descartar" desde los callbacks
|
||||
foreach (var botella in Cuerpos.OfType<simBotella>())
|
||||
{
|
||||
if (botella.Descartar)
|
||||
{
|
||||
_botellasParaEliminar.Add(botella);
|
||||
}
|
||||
}
|
||||
|
||||
botellasAEliminar = new List<simBotella>(_botellasParaEliminar);
|
||||
_botellasParaEliminar.Clear();
|
||||
}
|
||||
|
@ -1459,5 +1282,36 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
return lineStart + lineDirection * projectionLength;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Clear();
|
||||
simulation?.Dispose();
|
||||
bufferPool?.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registra un contacto entre una barrera y una botella para detección de paso
|
||||
/// </summary>
|
||||
/// <param name="barrera">Barrera que detecta el paso</param>
|
||||
/// <param name="botella">Botella que está pasando</param>
|
||||
internal void RegisterBarreraContact(simBarrera barrera, simBotella botella)
|
||||
{
|
||||
if (barrera != null && botella != null)
|
||||
{
|
||||
lock (_contactsLock)
|
||||
{
|
||||
if (!_barreraContacts.ContainsKey(barrera))
|
||||
{
|
||||
_barreraContacts[barrera] = new List<simBotella>();
|
||||
}
|
||||
|
||||
if (!_barreraContacts[barrera].Contains(botella))
|
||||
{
|
||||
_barreraContacts[barrera].Add(botella);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ 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
|
||||
{
|
||||
|
@ -29,17 +31,9 @@ namespace CtrEditor.Simulacion
|
|||
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 SISTEMA SIMPLIFICADO: Motor dinámico que se crea solo cuando es necesario
|
||||
public ConstraintHandle CurrentMotor { get; private set; } = default;
|
||||
public ConstraintHandle CurrentDistanceLimit { get; private set; } = default; // ✅ NUEVO: Para curvas
|
||||
public simBase CurrentMotorTarget { get; private set; } = null; // Transporte o curva actual
|
||||
public bool _hasMotor = false; // ✅ BANDERA PÚBLICA para acceso desde callbacks
|
||||
public bool HasMotor => _hasMotor;
|
||||
|
||||
// ✅ NUEVAS PROPIEDADES para el motor dinámico
|
||||
public Vector3 CurrentDirection { get; private set; } = Vector3.UnitX; // Dirección por defecto (1,0,0)
|
||||
public float CurrentSpeed { get; private set; } = 0f; // Velocidad por defecto
|
||||
public bool IsOnElement { get; private set; } = false; // Si está sobre un elemento activo
|
||||
// ✅ NUEVO: Propiedades para la física personalizada
|
||||
public float BaseInverseMass { get; private set; }
|
||||
public Vector3 InitialPosition3D { get; private set; }
|
||||
|
||||
private List<Action> _deferredActions;
|
||||
|
||||
|
@ -56,8 +50,6 @@ namespace CtrEditor.Simulacion
|
|||
// ✅ USAR COORDINATECONVERTER para conversión centralizada
|
||||
var position3D = new Vector3(position.X, CoordinateConverter.WpfYToBepuY(position.Y), Radius + zPos_Transporte + zAltura_Transporte);
|
||||
Create(position3D);
|
||||
|
||||
// ✅ ELIMINADO: No crear motor permanente - se creará cuando sea necesario
|
||||
}
|
||||
|
||||
public float CenterX
|
||||
|
@ -154,303 +146,17 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
BodyHandle = _simulation.Bodies.Add(bodyDescription);
|
||||
_bodyCreated = true; // Marcar que hemos creado un cuerpo
|
||||
|
||||
// ✅ 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Crea o actualiza el motor conectándolo al transporte/curva actual
|
||||
/// </summary>
|
||||
public void CreateOrUpdateMotor(simBase target, Vector3 direction, float speed)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ✅ VALIDAR DIRECCIÓN
|
||||
if (direction.Length() < 0.001f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ VALIDAR QUE EL TARGET Y LA SIMULACIÓN EXISTAN
|
||||
if (target == null || _simulation == null || !_simulation.Bodies.BodyExists(BodyHandle))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ VERIFICAR SI NECESITAMOS CREAR O ACTUALIZAR EL MOTOR
|
||||
bool needsNewMotor = false;
|
||||
|
||||
if (!HasMotor)
|
||||
{
|
||||
// ✅ PRIMERA VEZ: Crear motor nuevo
|
||||
needsNewMotor = true;
|
||||
//System.Diagnostics.Debug.WriteLine($"[CreateOrUpdateMotor] 🆕 Creando motor nuevo para {target?.GetType().Name}");
|
||||
}
|
||||
else if (CurrentMotorTarget != target)
|
||||
{
|
||||
// ✅ CAMBIO DE OBJETO: Eliminar motor anterior y crear uno nuevo
|
||||
RemoveCurrentMotor();
|
||||
needsNewMotor = true;
|
||||
//System.Diagnostics.Debug.WriteLine($"[CreateOrUpdateMotor] 🔄 Cambiando motor de {CurrentMotorTarget?.GetType().Name} a {target?.GetType().Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// ✅ MISMO OBJETO: Solo actualizar velocidad
|
||||
UpdateMotorSpeed(direction, speed / simBase.SpeedConversionFactor);
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ CREAR NUEVO MOTOR SI ES NECESARIO
|
||||
if (needsNewMotor && target != null)
|
||||
{
|
||||
CreateMotorForTarget(target, direction, speed / simBase.SpeedConversionFactor);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CreateOrUpdateMotor] ❌ ERROR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Crea un motor específico para un transporte o curva
|
||||
/// </summary>
|
||||
private void CreateMotorForTarget(simBase target, Vector3 direction, float speed)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_simulation == null || _simulation.Solver == null || !_simulation.Bodies.BodyExists(BodyHandle) || target == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CreateMotorForTarget] ❌ Simulación, solver, body o target no disponible");
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ VERIFICAR QUE EL TARGET TENGA UN BODY VÁLIDO
|
||||
if (!_simulation.Bodies.BodyExists(target.BodyHandle))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CreateMotorForTarget] ❌ Target body no existe: {target.BodyHandle}");
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ VERIFICAR QUE NO TENGA YA UN MOTOR VÁLIDO
|
||||
if (HasMotor && CurrentMotor.Value != 0)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CreateMotorForTarget] ⚠️ Ya existe un motor válido: {CurrentMotor}");
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ NORMALIZAR LA DIRECCIÓN TANGENCIAL
|
||||
var tangentDir = Vector3.Normalize(direction);
|
||||
|
||||
// ✅ CREAR MOTOR CONECTADO AL TARGET
|
||||
var motor = new LinearAxisMotor()
|
||||
{
|
||||
LocalOffsetA = Vector3.Zero, // Target
|
||||
LocalOffsetB = Vector3.Zero, // Botella
|
||||
LocalAxis = tangentDir, // ✅ CORREGIDO: Usar la dirección tangencial calculada
|
||||
TargetVelocity = speed, // ✅ CORREGIDO: Usar la velocidad directamente
|
||||
Settings = new MotorSettings(Math.Max(_mass * 20f, 8f), 0f)
|
||||
};
|
||||
|
||||
// ✅ CONECTAR BOTELLA CON EL TARGET (transporte o curva)
|
||||
CurrentMotor = _simulation.Solver.Add(target.BodyHandle, BodyHandle, motor);
|
||||
|
||||
CurrentMotorTarget = target;
|
||||
_hasMotor = true; // ✅ ESTABLECER BANDERA
|
||||
|
||||
//if (target is simCurve curva) {
|
||||
// // Calcular el vector desde el centro de la curva hasta la botella (en el plano XY)
|
||||
// var curveCenter = curva.CurveCenter;
|
||||
// var bottlePosition = GetPosition();
|
||||
|
||||
// var radiusVector = new Vector3(bottlePosition.X - curveCenter.X, bottlePosition.Y - curveCenter.Y, 0f);
|
||||
// var radius = radiusVector.Length();
|
||||
|
||||
// if (radius > 1e-3f)
|
||||
// {
|
||||
|
||||
// // Calcular offsets locales
|
||||
// var localOffsetA = curveCenter; // Desde el centro de la curva hasta el punto de anclaje
|
||||
// var localOffsetB = Vector3.Zero; // ✅ SIMPLIFICADO: Conectar al centro de la botella
|
||||
|
||||
// var distanceLimit = new DistanceLimit()
|
||||
// {
|
||||
// LocalOffsetA = Vector3.Zero,
|
||||
// LocalOffsetB = Vector3.Zero,
|
||||
// MinimumDistance = radius- 4*Radius, // Distancia mínima = radio actual
|
||||
// MaximumDistance = radius+ 4*Radius, // Distancia máxima = radio actual (mantener distancia fija)
|
||||
// SpringSettings = new SpringSettings(30f, 0f)
|
||||
// };
|
||||
|
||||
// //CurrentDistanceLimit = _simulation.Solver.Add(target.BodyHandle, BodyHandle, distanceLimit);
|
||||
|
||||
// //System.Diagnostics.Debug.WriteLine($"[CreateMotorForTarget-Curve] 📏 DistanceLimit creado:");
|
||||
// //System.Diagnostics.Debug.WriteLine($" Radio actual: {radius:F3}");
|
||||
// //System.Diagnostics.Debug.WriteLine($" Punto de anclaje: {anchorPoint}");
|
||||
// //System.Diagnostics.Debug.WriteLine($" LocalOffsetA (curva): {localOffsetA}");
|
||||
// //System.Diagnostics.Debug.WriteLine($" LocalOffsetB (botella): {localOffsetB} (centro)");
|
||||
// //System.Diagnostics.Debug.WriteLine($" Distancia objetivo: {radius:F3}");
|
||||
// //System.Diagnostics.Debug.WriteLine($" DistanceLimit Handle: {CurrentDistanceLimit}");
|
||||
// }
|
||||
//}
|
||||
// ✅ ACTUALIZAR PROPIEDADES INTERNAS
|
||||
CurrentDirection = direction;
|
||||
CurrentSpeed = speed;
|
||||
IsOnElement = Math.Abs(speed) > 0.001f;
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine($"[CreateMotorForTarget] ✅ Motor creado:");
|
||||
//System.Diagnostics.Debug.WriteLine($" Botella: {BodyHandle}");
|
||||
//System.Diagnostics.Debug.WriteLine($" Target: {target.BodyHandle} ({target.GetType().Name})");
|
||||
//System.Diagnostics.Debug.WriteLine($" Motor Handle: {CurrentMotor} (Value: {CurrentMotor.Value})");
|
||||
//System.Diagnostics.Debug.WriteLine($" Dirección: {direction}");
|
||||
//System.Diagnostics.Debug.WriteLine($" Velocidad efectiva: {effectiveSpeed:F3}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CreateMotorForTarget] ❌ ERROR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Actualiza solo la velocidad del motor existente
|
||||
/// </summary>
|
||||
private void UpdateMotorSpeed(Vector3 direction, float speed)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!HasMotor || _simulation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ NORMALIZAR LA DIRECCIÓN TANGENCIAL
|
||||
var tangentDir = Vector3.Normalize(direction);
|
||||
|
||||
// ✅ OBTENER LA DESCRIPCIÓN ACTUAL DEL MOTOR
|
||||
_simulation.Solver.GetDescription(CurrentMotor, out LinearAxisMotor motor);
|
||||
|
||||
// ✅ ACTUALIZAR DIRECCIÓN Y VELOCIDAD
|
||||
motor.LocalAxis = tangentDir;
|
||||
motor.TargetVelocity = speed;
|
||||
|
||||
// ✅ ACTUALIZAR EL MOTOR EN EL SOLVER
|
||||
_simulation.Solver.ApplyDescription(CurrentMotor, motor);
|
||||
|
||||
// ✅ ACTUALIZAR PROPIEDADES INTERNAS
|
||||
CurrentDirection = direction;
|
||||
CurrentSpeed = speed;
|
||||
IsOnElement = Math.Abs(speed) > 0.001f;
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine($"[UpdateMotorSpeed] 🔄 Velocidad actualizada: {effectiveSpeed:F3}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[UpdateMotorSpeed] ❌ ERROR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Elimina el motor actual
|
||||
/// </summary>
|
||||
public void RemoveCurrentMotor()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (HasMotor && _simulation != null && _simulation.Solver != null)
|
||||
{
|
||||
// ✅ VERIFICAR QUE EL MOTOR EXISTE ANTES DE ELIMINARLO
|
||||
if (CurrentMotor.Value != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
_simulation.Solver.Remove(CurrentMotor);
|
||||
//System.Diagnostics.Debug.WriteLine($"[RemoveCurrentMotor] 🗑️ Motor eliminado: {CurrentMotor}");
|
||||
}
|
||||
catch (Exception removeEx)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[RemoveCurrentMotor] ⚠️ Error eliminando motor {CurrentMotor}: {removeEx.Message}");
|
||||
// Continuar con la limpieza incluso si falla la eliminación
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[RemoveCurrentMotor] ⚠️ Motor ya eliminado o inválido: {CurrentMotor}");
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ NUEVO: Eliminar DistanceLimit si existe
|
||||
if (CurrentDistanceLimit.Value != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
_simulation.Solver.Remove(CurrentDistanceLimit);
|
||||
System.Diagnostics.Debug.WriteLine($"[RemoveCurrentMotor] 🗑️ DistanceLimit eliminado: {CurrentDistanceLimit}");
|
||||
}
|
||||
catch (Exception removeEx)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[RemoveCurrentMotor] ⚠️ Error eliminando DistanceLimit {CurrentDistanceLimit}: {removeEx.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[RemoveCurrentMotor] ❌ ERROR: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// ✅ LIMPIAR REFERENCIAS SIEMPRE
|
||||
CurrentMotor = default;
|
||||
CurrentDistanceLimit = default; // ✅ NUEVO: Limpiar DistanceLimit
|
||||
CurrentMotorTarget = null;
|
||||
_hasMotor = false; // ✅ LIMPIAR BANDERA
|
||||
CurrentDirection = Vector3.UnitX;
|
||||
CurrentSpeed = 0f;
|
||||
IsOnElement = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Detiene el motor (velocidad 0) pero mantiene la conexión
|
||||
/// </summary>
|
||||
public void StopMotor()
|
||||
{
|
||||
UpdateMotorSpeed(CurrentDirection, 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Limpia las restricciones asociadas a un elemento específico
|
||||
/// Solo limpia los datos internos de simBotella, no elimina las restricciones de BEPU
|
||||
/// </summary>
|
||||
/// <param name="target">Elemento (simTransporte o simCurve) del cual limpiar las restricciones</param>
|
||||
public void CleanRestrictions(simBase target)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ✅ VERIFICAR SI EL TARGET COINCIDE CON EL ACTUAL
|
||||
if (CurrentMotorTarget == target)
|
||||
{
|
||||
// ✅ LIMPIAR SOLO LOS DATOS INTERNOS (no eliminar restricciones de BEPU)
|
||||
CurrentMotorTarget = null;
|
||||
_hasMotor = false; // ✅ CRÍTICO: Limpiar el flag del motor
|
||||
CurrentDirection = Vector3.UnitX;
|
||||
CurrentSpeed = 0f;
|
||||
IsOnElement = false;
|
||||
|
||||
// ✅ NO LIMPIAR CurrentMotor ni CurrentDistanceLimit - BEPU los maneja automáticamente
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[CleanRestrictions] ✅ Restricciones limpiadas para {target?.GetType().Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CleanRestrictions] ⚠️ Target no coincide: actual={CurrentMotorTarget?.GetType().Name}, solicitado={target?.GetType().Name}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[CleanRestrictions] ❌ ERROR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void SetDiameter(float diameter)
|
||||
{
|
||||
Radius = diameter / 2f;
|
||||
|
@ -499,8 +205,8 @@ namespace CtrEditor.Simulacion
|
|||
// Convertir a ángulo en Z solamente
|
||||
var rotationZ = GetRotationZ();
|
||||
|
||||
// Crear nueva orientación solo con rotación en Z (botella siempre "de pie")
|
||||
var correctedOrientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, rotationZ);
|
||||
// ✅ 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;
|
||||
|
|
|
@ -32,9 +32,6 @@ namespace CtrEditor.Simulacion
|
|||
// ✅ SIMPLIFICADO: Propiedades esenciales únicamente
|
||||
public List<simBotella> BottlesOnCurve { get; private set; } = new List<simBotella>();
|
||||
|
||||
// ✅ EVENTO para actualización de motores
|
||||
public event Action<simCurve> OnSpeedChanged;
|
||||
|
||||
// ✅ NUEVO: Almacenar triángulos creados para acceso directo
|
||||
private Triangle[] _storedTriangles;
|
||||
|
||||
|
@ -66,11 +63,37 @@ namespace CtrEditor.Simulacion
|
|||
Create(innerRadius, outerRadius, startAngle, endAngle, topLeft, 0);
|
||||
}
|
||||
|
||||
// ✅ SIMPLIFICADO: Configurar velocidad angular para AngularAxisMotor
|
||||
// ✅ MODIFICADO: Actualizar la velocidad y luego la del cuerpo cinemático
|
||||
public void SetSpeed(float speed)
|
||||
{
|
||||
Speed = speed; // Velocidad angular directa (sin inversión)
|
||||
OnSpeedChanged?.Invoke(this);
|
||||
Speed = speed;
|
||||
ActualizarVelocidadCinematica();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Aplica la velocidad angular al cuerpo cinemático de BEPU.
|
||||
/// </summary>
|
||||
public void ActualizarVelocidadCinematica()
|
||||
{
|
||||
if (_simulation != null && this.BodyHandle.Value >= 0 && _simulation.Bodies.BodyExists(this.BodyHandle))
|
||||
{
|
||||
var body = _simulation.Bodies.GetBodyReference(BodyHandle);
|
||||
|
||||
float effectiveRadius = (_innerRadius + _outerRadius) / 2.0f;
|
||||
if (effectiveRadius > 0.001f)
|
||||
{
|
||||
// La velocidad tangencial (Speed) se convierte a velocidad angular (ω = v / r)
|
||||
// ✅ CORREGIDO: Convertir velocidad de m/min a m/s para la simulación
|
||||
float angularSpeed = (Speed / SpeedConversionFactor) / effectiveRadius;
|
||||
|
||||
// La rotación es alrededor del eje Z en el sistema de coordenadas de BEPU
|
||||
body.Velocity.Angular = new Vector3(0, 0, angularSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
body.Velocity.Angular = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -220,19 +243,8 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public new void RemoverBody()
|
||||
{
|
||||
// ✅ NUEVO: Limpiar restricciones de botellas conectadas antes de eliminar el cuerpo
|
||||
if (_simulationManager != null)
|
||||
{
|
||||
var connectedBottles = _simulationManager.GetBottlesConnectedToElement(this);
|
||||
foreach (var bottle in connectedBottles)
|
||||
{
|
||||
bottle.CleanRestrictions(this);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ NUEVO: Limpiar triángulos almacenados
|
||||
_storedTriangles = null;
|
||||
|
||||
|
@ -242,7 +254,7 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
public void Create(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 wpfTopLeft, float unused = 0)
|
||||
{
|
||||
// ✅ CORRIGIDO: Como Aether - startAngle y endAngle definen directamente el sector
|
||||
// ✅ CORREGIDO: Como Aether - startAngle y endAngle definen directamente el sector
|
||||
// No hay rotación separada del objeto
|
||||
|
||||
// Actualizar parámetros internos
|
||||
|
@ -253,53 +265,45 @@ namespace CtrEditor.Simulacion
|
|||
_endAngle = CoordinateConverter.WpfDegreesToBepuRadians(endAngle);
|
||||
|
||||
// ✅ NUEVO: Actualizar el centro real de la curva
|
||||
// Para curvas, el "tamaño" es el diámetro del radio exterior
|
||||
var curveSize = outerRadius * 2f;
|
||||
var zPosition = zPos_Curve;
|
||||
|
||||
// Calcular nueva posición del centro (sin rotación - el sector ya está definido por los ángulos)
|
||||
_curveCenter = CoordinateConverter.CalculateBepuCenterFromWpfTopLeft(wpfTopLeft, curveSize, curveSize, 0f, zPosition);
|
||||
|
||||
Create(_curveCenter); // Sin rotación adicional
|
||||
Create(_curveCenter); // Llamar a la sobrecarga privada
|
||||
}
|
||||
|
||||
private void Create(Vector3 position)
|
||||
{
|
||||
RemoverBody();
|
||||
|
||||
// ✅ SIMPLIFICADO: Crear superficie usando Triangle de BEPU directamente
|
||||
var triangles = CreateSimpleArcTriangles(_innerRadius, _outerRadius, _startAngle, _endAngle);
|
||||
|
||||
// ✅ ALMACENAR triángulos para acceso directo
|
||||
_storedTriangles = triangles;
|
||||
|
||||
// ✅ SIMPLIFICADO: Crear un solo cuerpo con múltiples Triangle shapes usando Mesh
|
||||
if (triangles.Length > 0)
|
||||
if (triangles.Length == 0)
|
||||
{
|
||||
// ✅ CREAR MESH CON LA API CORRECTA DE BEPU
|
||||
var triangleBuffer = new BepuUtilities.Memory.Buffer<Triangle>(triangles.Length, _simulation.BufferPool);
|
||||
for (int i = 0; i < triangles.Length; i++)
|
||||
{
|
||||
triangleBuffer[i] = triangles[i];
|
||||
}
|
||||
var mesh = new BepuPhysics.Collidables.Mesh(triangleBuffer, Vector3.One, _simulation.BufferPool);
|
||||
var shapeIndex = _simulation.Shapes.Add(mesh);
|
||||
|
||||
var bodyDescription = BodyDescription.CreateKinematic(
|
||||
new RigidPose(position),
|
||||
new CollidableDescription(shapeIndex, 0.001f), // ✅ SpeculativeMargin bajo para detección precisa
|
||||
new BodyActivityDescription(0.01f)
|
||||
);
|
||||
|
||||
BodyHandle = _simulation.Bodies.Add(bodyDescription);
|
||||
_bodyCreated = true;
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[simCurve] Creado con {triangles.Length} triángulos reales almacenados");
|
||||
System.Diagnostics.Debug.WriteLine($"[simCurve] No se crearon triángulos, no se creará el cuerpo.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
_storedTriangles = triangles;
|
||||
|
||||
// ✅ MÉTODOS ELIMINADOS: CreateMainBody y CreateTriangleBody ya no son necesarios
|
||||
// La curva ahora se crea como un solo Mesh en el método Create simplificado
|
||||
// ✅ CORREGIDO: Convertir el array de triángulos a un Buffer<Triangle> de BEPU.
|
||||
_simulation.BufferPool.Take(triangles.Length, out BepuUtilities.Memory.Buffer<Triangle> triangleBuffer);
|
||||
for (int i = 0; i < triangles.Length; i++)
|
||||
{
|
||||
triangleBuffer[i] = triangles[i];
|
||||
}
|
||||
var mesh = new Mesh(triangleBuffer, Vector3.One, _simulation.BufferPool);
|
||||
var shapeIndex = _simulation.Shapes.Add(mesh);
|
||||
|
||||
var bodyDescription = BodyDescription.CreateKinematic(
|
||||
new RigidPose(position, Quaternion.Identity), // La rotación se maneja con la velocidad angular, no con la pose inicial
|
||||
new CollidableDescription(shapeIndex, 0.1f),
|
||||
new BodyActivityDescription(-1f) // ✅ CORREGIDO: -1f para que el cuerpo cinemático NUNCA duerma y no se mueva por su cuenta.
|
||||
);
|
||||
BodyHandle = _simulation.Bodies.Add(bodyDescription);
|
||||
_bodyCreated = true;
|
||||
|
||||
// ✅ NUEVO: Establecer la velocidad cinemática inicial al crear el cuerpo.
|
||||
ActualizarVelocidadCinematica();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ SIMPLIFICADO: Crea triángulos usando Triangle de BEPU directamente
|
||||
|
@ -340,11 +344,5 @@ namespace CtrEditor.Simulacion
|
|||
System.Diagnostics.Debug.WriteLine($"[CreateSimpleArcTriangles] Creados {triangles.Count} triángulos en coordenadas locales");
|
||||
return triangles.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ✅ MÉTODOS ELIMINADOS: Los cálculos tangenciales complejos ya no son necesarios
|
||||
// AngularAxisMotor maneja automáticamente la rotación en curvas
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,11 +26,6 @@ namespace CtrEditor.Simulacion
|
|||
public Vector3 DirectionVector { get; private set; }
|
||||
public List<simBotella> BottlesOnTransport { get; private set; } = new List<simBotella>();
|
||||
|
||||
// ✅ NUEVO EVENTO - para actualización de motores
|
||||
public event Action<simTransporte> OnSpeedChanged;
|
||||
|
||||
|
||||
|
||||
public simTransporte(Simulation simulation, List<Action> deferredActions, float width, float height, Vector2 topLeft, float angle = 0, SimulationManagerBEPU simulationManager = null)
|
||||
{
|
||||
_simulation = simulation;
|
||||
|
@ -66,11 +61,8 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
base.SetRotation(wpfAngle);
|
||||
|
||||
// ✅ CRÍTICO: Actualizar propiedades cacheadas después del cambio de rotación
|
||||
// ✅ CRÍTICO: Actualizar propiedades cacheadas y velocidad después del cambio de rotación
|
||||
UpdateCachedProperties();
|
||||
|
||||
// ✅ CRÍTICO: Disparar evento para actualizar motores activos con nueva dirección
|
||||
OnSpeedChanged?.Invoke(this);
|
||||
}
|
||||
|
||||
public new void SetPosition(float x, float y, float z = 0)
|
||||
|
@ -108,14 +100,11 @@ namespace CtrEditor.Simulacion
|
|||
var bepuCenter = CoordinateConverter.CalculateBepuCenterFromWpfTopLeft(wpfTopLeft, Width, Height, wpfAngle, zPosition);
|
||||
CoordinateConverter.UpdateBepuBodyPose(_simulation, BodyHandle, bepuCenter, wpfAngle);
|
||||
|
||||
// ✅ CRÍTICO: Actualizar propiedades cacheadas después del cambio de orientación
|
||||
// ✅ CRÍTICO: Actualizar propiedades cacheadas y velocidad después del cambio de orientación
|
||||
UpdateCachedProperties();
|
||||
|
||||
// ✅ CRÍTICO: Disparar evento para actualizar motores activos con nueva dirección
|
||||
OnSpeedChanged?.Invoke(this);
|
||||
}
|
||||
|
||||
// ✅ NUEVO MÉTODO - actualizar propiedades cacheadas
|
||||
// ✅ NUEVO MÉTODO - actualizar propiedades cacheadas y velocidad cinemática
|
||||
internal void UpdateCachedProperties()
|
||||
{
|
||||
// ✅ CORREGIDO: La dirección siempre es UnitX rotado por el ángulo del transporte
|
||||
|
@ -148,15 +137,29 @@ namespace CtrEditor.Simulacion
|
|||
// 🔍 DEBUG: Agregar información detallada
|
||||
System.Diagnostics.Debug.WriteLine($"[UpdateCached-Fallback] WPF Angle: {wpfAngle}°, WPF Vector: ({wpfX:F3}, {wpfY:F3}), BEPU DirectionVector: {DirectionVector}, Length: {DirectionVector.Length()}");
|
||||
}
|
||||
|
||||
// ✅ NUEVO: Actualizar la velocidad del cuerpo cinemático siempre que cambien las propiedades
|
||||
ActualizarVelocidadCinematica();
|
||||
}
|
||||
|
||||
// ✅ MODIFICAR MÉTODO EXISTENTE - disparar evento
|
||||
// ✅ MODIFICADO: Actualizar la velocidad y luego la del cuerpo cinemático
|
||||
public void SetSpeed(float speed)
|
||||
{
|
||||
Speed = speed;
|
||||
UpdateCachedProperties();
|
||||
// Disparar evento para actualizar motores activos
|
||||
OnSpeedChanged?.Invoke(this);
|
||||
ActualizarVelocidadCinematica();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ NUEVO: Aplica la velocidad al cuerpo cinemático de BEPU.
|
||||
/// </summary>
|
||||
public void ActualizarVelocidadCinematica()
|
||||
{
|
||||
if (_simulation != null && this.BodyHandle.Value >= 0 && _simulation.Bodies.BodyExists(this.BodyHandle))
|
||||
{
|
||||
var body = _simulation.Bodies.GetBodyReference(BodyHandle);
|
||||
// ✅ CORREGIDO: Convertir velocidad de m/min a m/s para la simulación
|
||||
body.Velocity.Linear = this.DirectionVector * (this.Speed / SpeedConversionFactor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -190,9 +193,6 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
// ✅ CRÍTICO: Actualizar propiedades cacheadas después del cambio de dimensiones
|
||||
UpdateCachedProperties();
|
||||
|
||||
// ✅ CRÍTICO: Disparar evento para actualizar motores activos con nueva dirección
|
||||
OnSpeedChanged?.Invoke(this);
|
||||
}
|
||||
|
||||
public void Create(float width, float height, Vector2 wpfTopLeft, float wpfAngle = 0)
|
||||
|
@ -205,20 +205,10 @@ namespace CtrEditor.Simulacion
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// ✅ SIMPLIFICADO: RemoverBody que limpia restricciones y elimina el body
|
||||
/// ✅ SIMPLIFICADO: RemoverBody ya no necesita limpiar restricciones de botellas.
|
||||
/// </summary>
|
||||
public new void RemoverBody()
|
||||
{
|
||||
// ✅ NUEVO: Limpiar restricciones de botellas conectadas antes de eliminar el cuerpo
|
||||
if (_simulationManager != null)
|
||||
{
|
||||
var connectedBottles = _simulationManager.GetBottlesConnectedToElement(this);
|
||||
foreach (var bottle in connectedBottles)
|
||||
{
|
||||
bottle.CleanRestrictions(this);
|
||||
}
|
||||
}
|
||||
|
||||
base.RemoverBody();
|
||||
}
|
||||
|
||||
|
@ -233,7 +223,7 @@ namespace CtrEditor.Simulacion
|
|||
var bodyDescription = BodyDescription.CreateKinematic(
|
||||
new RigidPose(bepuPosition, CoordinateConverter.CreateBepuQuaternionFromWpfAngle(wpfAngle)),
|
||||
new CollidableDescription(shapeIndex, 0.1f),
|
||||
new BodyActivityDescription(0.01f)
|
||||
new BodyActivityDescription(-1f)
|
||||
);
|
||||
|
||||
BodyHandle = _simulation.Bodies.Add(bodyDescription);
|
||||
|
|
Loading…
Reference in New Issue