diff --git a/Simulacion/BEPU.cs b/Simulacion/BEPU.cs index b6e80f0..00a6c1b 100644 --- a/Simulacion/BEPU.cs +++ b/Simulacion/BEPU.cs @@ -859,7 +859,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 - // ✅ NUEVAS PROPIEDADES - gestión de motores + // ✅ NUEVO SISTEMA SIMPLIFICADO: Un solo motor por botella + public enum MotorType { None, Linear, Angular } + public MotorType CurrentMotorType { get; set; } = MotorType.None; public simTransporte CurrentTransport { get; set; } public ConstraintHandle CurrentMotor { get; set; } public bool HasActiveMotor => CurrentMotor.Value != 0; @@ -1066,11 +1068,13 @@ namespace CtrEditor.Simulacion return isOnTransports > 0; } - // ✅ NUEVOS MÉTODOS - gestión de estado - public void AssignToTransport(simTransporte transport, ConstraintHandle motor) + // ✅ SISTEMA SIMPLIFICADO: Métodos unificados de gestión de motores + public void AssignLinearMotor(simTransporte transport, ConstraintHandle motor) { - CurrentTransport = transport; + RemoveCurrentMotor(); // Eliminar motor anterior automáticamente CurrentMotor = motor; + CurrentMotorType = MotorType.Linear; + CurrentTransport = transport; if (transport.isBrake) { isOnBrakeTransport = true; @@ -1078,12 +1082,36 @@ namespace CtrEditor.Simulacion } } - public void RemoveFromTransport() + public void AssignAngularMotor(simCurve curve, ConstraintHandle motor) { - CurrentTransport = null; + RemoveCurrentMotor(); // Eliminar motor anterior automáticamente + CurrentMotor = motor; + CurrentMotorType = MotorType.Angular; + // Las curvas no usan CurrentTransport + } + + public void RemoveCurrentMotor() + { + if (HasActiveMotor) + { + // El MotorManager se encargará de eliminar el motor del solver CurrentMotor = default; + CurrentMotorType = MotorType.None; + CurrentTransport = null; isOnBrakeTransport = false; CurrentBrakeTransport = null; + } + } + + // ✅ LEGACY: Mantener compatibilidad con código existente + public void AssignToTransport(simTransporte transport, ConstraintHandle motor) + { + AssignLinearMotor(transport, motor); + } + + public void RemoveFromTransport() + { + RemoveCurrentMotor(); } } @@ -1104,54 +1132,43 @@ namespace CtrEditor.Simulacion public float Speed { get; set; } // Velocidad para efectos de cinta transportadora (m/s) private List _deferredActions; - internal List _triangleBodyHandles; // Lista de todos los triángulos que componen la curva - private List> _originalTriangles; // ✅ NUEVO: Triángulos originales para visualización debug - // ✅ SIMPLIFICADO: Solo necesitamos velocidad en m/s, no sistema complejo de direcciones - public float SpeedMetersPerSecond { get; private set; } + // ✅ SIMPLIFICADO: Propiedades esenciales únicamente public List BottlesOnCurve { get; private set; } = new List(); - // ✅ NUEVO EVENTO - para actualización de motores + // ✅ EVENTO para actualización de motores public event Action OnSpeedChanged; - // ✅ NUEVA REFERENCIA - para limpiar motors + // ✅ REFERENCIA para limpiar motors private SimulationManagerBEPU _simulationManager; - // ✅ NUEVO: Direcciones tangenciales precalculadas para cada triángulo - internal Dictionary _triangleDirections = new Dictionary(); + // ✅ NUEVO: Almacenar triángulos creados para acceso directo + private Triangle[] _storedTriangles; public float InnerRadius => _innerRadius; public float OuterRadius => _outerRadius; public float StartAngle => CoordinateConverter.BepuRadiansToWpfDegrees(_startAngle); // Convertir de radianes BEPU internos a grados WPF public float EndAngle => CoordinateConverter.BepuRadiansToWpfDegrees(_endAngle); // Convertir de radianes BEPU internos a grados WPF - /// - /// ✅ NUEVO: Expone los triángulos originales para visualización debug - /// - public List> GetOriginalTriangles() => _originalTriangles ?? new List>(); - public simCurve(Simulation simulation, List deferredActions, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 topLeft, float unused = 0, SimulationManagerBEPU simulationManager = null) { _simulation = simulation; _deferredActions = deferredActions; - _simulationManager = simulationManager; // ✅ NUEVA REFERENCIA + _simulationManager = simulationManager; _innerRadius = innerRadius; _outerRadius = outerRadius; - // ✅ CORREGIDO: Usar conversión directa WPF grados → BEPU radianes + // ✅ SIMPLIFICADO: Usar conversión directa WPF grados → BEPU radianes _startAngle = CoordinateConverter.WpfDegreesToBepuRadians(startAngle); _endAngle = CoordinateConverter.WpfDegreesToBepuRadians(endAngle); - _triangleBodyHandles = new List(); - _originalTriangles = new List>(); // ✅ NUEVO: Inicializar lista de triángulos - // Crear la curva con los ángulos que definen el sector + // ✅ SIMPLIFICADO: Crear la curva directamente Create(innerRadius, outerRadius, startAngle, endAngle, topLeft, 0); } - // ✅ MODIFICAR MÉTODO EXISTENTE - disparar evento + // ✅ SIMPLIFICADO: Configurar velocidad angular para AngularAxisMotor public void SetSpeed(float speed) { - Speed = -speed; - SpeedMetersPerSecond = Math.Abs(Speed) / simBase.SPEED_CONVERSION_FACTOR; + Speed = speed; // Velocidad angular directa (sin inversión) OnSpeedChanged?.Invoke(this); } @@ -1210,34 +1227,159 @@ namespace CtrEditor.Simulacion return CoordinateConverter.CalculateWpfTopLeftFromBepuCenter(bepuCenter, curveSize, curveSize, 0f); } - public new void RemoverBody() + /// + /// ✅ NUEVO: Obtiene los triángulos reales creados para la curva + /// Devuelve los triángulos transformados a coordenadas mundiales usando la posición del cuerpo + /// + public Triangle[] GetRealBEPUTriangles() { - // ✅ CRÍTICO: Limpiar todos los motors conectados a esta curva ANTES de eliminar los bodies - RemoveConnectedMotors(); - - // ✅ NUEVO: Limpiar cachés de direcciones - _triangleDirections.Clear(); - - // Remover todos los triángulos de la curva - if (_triangleBodyHandles != null && _simulation != null) + try { - foreach (var triangleHandle in _triangleBodyHandles) + if (_storedTriangles == null || _storedTriangles.Length == 0) { - if (_simulation.Bodies.BodyExists(triangleHandle)) + System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] No hay triángulos almacenados"); + return new Triangle[0]; + } + + // ✅ TRANSFORMAR a coordenadas mundiales + var worldTriangles = TransformTrianglesToWorldCoordinates(_storedTriangles); + + System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] Devolviendo {worldTriangles.Length} triángulos transformados a coordenadas mundiales"); + return worldTriangles; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[GetRealBEPUTriangles] Error: {ex.Message}"); + return new Triangle[0]; + } + } + + /// + /// ✅ NUEVO: Transforma triángulos de coordenadas locales a mundiales + /// + private Triangle[] TransformTrianglesToWorldCoordinates(Triangle[] localTriangles) + { + try + { + if (_simulation == null || !_simulation.Bodies.BodyExists(BodyHandle)) + { + System.Diagnostics.Debug.WriteLine($"[TransformTriangles] Cuerpo no existe, devolviendo triángulos locales"); + return localTriangles; // Fallback: devolver triángulos sin transformar + } + + var body = _simulation.Bodies[BodyHandle]; + var bodyPosition = body.Pose.Position; + var bodyOrientation = body.Pose.Orientation; + + var transformedTriangles = new Triangle[localTriangles.Length]; + + for (int i = 0; i < localTriangles.Length; i++) + { + var localTriangle = localTriangles[i]; + + // Transformar cada vértice del triángulo a coordenadas mundiales + var worldA = bodyPosition + Vector3.Transform(localTriangle.A, bodyOrientation); + var worldB = bodyPosition + Vector3.Transform(localTriangle.B, bodyOrientation); + var worldC = bodyPosition + Vector3.Transform(localTriangle.C, bodyOrientation); + + transformedTriangles[i] = new Triangle(worldA, worldB, worldC); + } + + System.Diagnostics.Debug.WriteLine($"[TransformTriangles] Transformados {transformedTriangles.Length} triángulos. Body pos: {bodyPosition}"); + return transformedTriangles; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[TransformTriangles] Error: {ex.Message}, devolviendo triángulos locales"); + return localTriangles; // Fallback en caso de error + } + } + + /// + /// ✅ NUEVO: Información de debug sobre el Mesh real de BEPU + /// + public string GetBEPUMeshDebugInfo() + { + try + { + if (_simulation == null || !_simulation.Bodies.BodyExists(BodyHandle)) + return "ERROR: Body no existe en simulación"; + + var body = _simulation.Bodies[BodyHandle]; + var collidable = body.Collidable; + var shapeIndex = collidable.Shape; + + var info = new System.Text.StringBuilder(); + info.AppendLine("=== DEBUG INFO MESH REAL DE BEPU ==="); + info.AppendLine($"Position: {body.Pose.Position}"); + info.AppendLine($"Orientation: {body.Pose.Orientation}"); + info.AppendLine($"Shape Index: {shapeIndex.Index}"); + info.AppendLine($"Shape Type: {shapeIndex.Type} (Mesh.Id = {BepuPhysics.Collidables.Mesh.Id})"); + info.AppendLine($"Shape exists: {shapeIndex.Exists}"); + + if (_storedTriangles != null) + { + info.AppendLine($"Triángulos almacenados (locales): {_storedTriangles.Length}"); + + // Mostrar algunos triángulos locales de ejemplo + int triangleCount = Math.Min(3, _storedTriangles.Length); + info.AppendLine($""); + info.AppendLine($"Primeros {triangleCount} triángulos LOCALES:"); + + for (int i = 0; i < triangleCount; i++) { - _simulation.Bodies.Remove(triangleHandle); + var triangle = _storedTriangles[i]; + + info.AppendLine($" Triángulo LOCAL {i}:"); + info.AppendLine($" A: ({triangle.A.X:F3}, {triangle.A.Y:F3}, {triangle.A.Z:F3})"); + info.AppendLine($" B: ({triangle.B.X:F3}, {triangle.B.Y:F3}, {triangle.B.Z:F3})"); + info.AppendLine($" C: ({triangle.C.X:F3}, {triangle.C.Y:F3}, {triangle.C.Z:F3})"); + } + + // ✅ NUEVO: Mostrar triángulos transformados a coordenadas mundiales + try + { + var worldTriangles = TransformTrianglesToWorldCoordinates(_storedTriangles); + info.AppendLine($""); + info.AppendLine($"Primeros {triangleCount} triángulos MUNDIALES (transformados):"); + + for (int i = 0; i < Math.Min(triangleCount, worldTriangles.Length); i++) + { + var triangle = worldTriangles[i]; + + info.AppendLine($" Triángulo MUNDIAL {i}:"); + info.AppendLine($" A: ({triangle.A.X:F3}, {triangle.A.Y:F3}, {triangle.A.Z:F3})"); + info.AppendLine($" B: ({triangle.B.X:F3}, {triangle.B.Y:F3}, {triangle.B.Z:F3})"); + info.AppendLine($" C: ({triangle.C.X:F3}, {triangle.C.Y:F3}, {triangle.C.Z:F3})"); + } + } + catch (Exception ex) + { + info.AppendLine($"ERROR obteniendo triángulos mundiales: {ex.Message}"); } } - _triangleBodyHandles.Clear(); - } + else + { + info.AppendLine($"ERROR: No hay triángulos almacenados"); + } - // ✅ NUEVO: Limpiar también los triángulos originales - if (_originalTriangles != null) + return info.ToString(); + } + catch (Exception ex) { - _originalTriangles.Clear(); + return $"ERROR obteniendo debug info: {ex.Message}"; } + } - // Remover el cuerpo principal (si existe) + public new void RemoverBody() + { + // ✅ SIMPLIFICADO: Limpiar motors conectados + RemoveConnectedMotors(); + + // ✅ NUEVO: Limpiar triángulos almacenados + _storedTriangles = null; + + // ✅ SIMPLIFICADO: Solo remover el cuerpo principal (Mesh único) base.RemoverBody(); } @@ -1251,17 +1393,8 @@ namespace CtrEditor.Simulacion // ✅ USAR REFERENCIA DIRECTA al SimulationManager if (_simulationManager != null) { - // Limpiar motors del cuerpo principal + // ✅ SIMPLIFICADO: Solo limpiar motors del cuerpo principal _simulationManager.RemoveMotorsConnectedToBody(BodyHandle); - - // Limpiar motors de todos los triángulos - if (_triangleBodyHandles != null) - { - foreach (var triangleHandle in _triangleBodyHandles) - { - _simulationManager.RemoveMotorsConnectedToBody(triangleHandle); - } - } } // Limpiar la lista local de botellas @@ -1300,237 +1433,84 @@ namespace CtrEditor.Simulacion { RemoverBody(); - // Crear triángulos para la curva - los ángulos ya definen la forma correcta - var triangles = CreateTriangulatedCurve(_innerRadius, _outerRadius, -_startAngle, -_endAngle); + // ✅ SIMPLIFICADO: Crear superficie usando Triangle de BEPU directamente + var triangles = CreateSimpleArcTriangles(_innerRadius, _outerRadius, _startAngle, _endAngle); - // ✅ NUEVO: Almacenar triángulos originales para visualización debug - _originalTriangles = new List>(triangles); + // ✅ ALMACENAR triángulos para acceso directo + _storedTriangles = triangles; - foreach (var triangle in triangles) + // ✅ SIMPLIFICADO: Crear un solo cuerpo con múltiples Triangle shapes usando Mesh + if (triangles.Length > 0) { - CreateTriangleBody(triangle, position); - } - - // Crear un cuerpo principal invisible para referencia de posición - CreateMainBody(position); - } - - private void CreateMainBody(Vector3 position) - { - // Crear un cuerpo principal muy pequeño e invisible solo para referencia - var smallBox = new Box(0.01f, 0.01f, 0.01f); - var shapeIndex = _simulation.Shapes.Add(smallBox); + // ✅ CREAR MESH CON LA API CORRECTA DE BEPU + var triangleBuffer = new BepuUtilities.Memory.Buffer(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), // Sin rotación - la forma ya está definida por los ángulos - new CollidableDescription(shapeIndex, 0f), // Sin speculative margin + 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; - } - private void CreateTriangleBody(List triangle, Vector3 basePosition) - { - if (triangle.Count != 3) - return; - - try - { - // Calcular centro y dimensiones del triángulo - var center = (triangle[0] + triangle[1] + triangle[2]) / 3f; - var worldCenter = center + basePosition; - - // ✅ NUEVO: Calcular dirección tangencial con compensación centrípeta - // Usamos la posición del centro del triángulo para calcular la tangente - var radiusVector = new Vector2(center.X, center.Y); - if (radiusVector.Length() > 0.001f) - { - // Vector tangente (perpendicular al radio) en el plano XY - var tangent = new Vector3(-radiusVector.Y, radiusVector.X, 0); - tangent = Vector3.Normalize(tangent); - - // ✅ COMPENSACIÓN CENTRÍPETA: Inclinar la dirección hacia el centro - var radiusVector3D = new Vector3(radiusVector.X, radiusVector.Y, 0); - var toCenter = -Vector3.Normalize(radiusVector3D); // Vector hacia el centro - - // Mezclar tangente con componente centrípeta (10% hacia el centro) - const float CENTRIPETAL_FACTOR = 0.1f; // 10% de fuerza hacia el centro - var adjustedDirection = Vector3.Normalize(tangent + toCenter * CENTRIPETAL_FACTOR); - - // Ajustar dirección según la velocidad (signo) - if (Speed < 0) - adjustedDirection = -adjustedDirection; - - // Almacenar dirección ajustada antes de crear el body - var triangleDirection = adjustedDirection; - - // Calcular dimensiones aproximadas del triángulo para crear una caja plana - float minX = Math.Min(Math.Min(triangle[0].X, triangle[1].X), triangle[2].X); - float maxX = Math.Max(Math.Max(triangle[0].X, triangle[1].X), triangle[2].X); - float minY = Math.Min(Math.Min(triangle[0].Y, triangle[1].Y), triangle[2].Y); - float maxY = Math.Max(Math.Max(triangle[0].Y, triangle[1].Y), triangle[2].Y); - - float width = Math.Max(maxX - minX, 0.01f); - float height = Math.Max(maxY - minY, 0.01f); - float thickness = zAltura_Curve; // Altura muy pequeña para triángulos planos - - // Crear una caja plana que aproxime el triángulo - var box = new Box(width, height, thickness); - var shapeIndex = _simulation.Shapes.Add(box); - - // Crear como cuerpo estático sólido (NO sensor) - var activityDescription = new BodyActivityDescription(0.01f); - - var bodyDescription = BodyDescription.CreateKinematic( - new RigidPose(worldCenter), - new CollidableDescription(shapeIndex, 0.001f), // Speculative margin normal - activityDescription - ); - - var triangleHandle = _simulation.Bodies.Add(bodyDescription); - _triangleBodyHandles.Add(triangleHandle); - - // ✅ NUEVO: Almacenar la dirección tangencial para este triángulo - _triangleDirections[triangleHandle] = triangleDirection; - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine($"[simCurve] Error creating triangle: {ex.Message}"); + System.Diagnostics.Debug.WriteLine($"[simCurve] Creado con {triangles.Length} triángulos reales almacenados"); } } - private const float SegmentationFactor = 32f / 3f; - private const int MinSegments = 8; - private const int MaxSegments = 64; + // ✅ 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 /// - /// Crea triángulos para representar una curva/arco en la simulación BEPU. - /// IMPORTANTE: Los parámetros startAngle y endAngle ya están convertidos a radianes BEPU internamente, - /// pero además se invierte la dirección del arco para mantener coherencia visual con WPF. + /// ✅ SIMPLIFICADO: Crea triángulos usando Triangle de BEPU directamente + /// Solo superficie superior, eliminando complejidad innecesaria + /// Los triángulos se crean en coordenadas locales (Z=0) y la posición del cuerpo los ubica correctamente /// /// Radio interno del arco /// Radio externo del arco - /// Ángulo inicial en radianes BEPU (ya convertido desde grados WPF) - /// Ángulo final en radianes BEPU (ya convertido desde grados WPF) - /// Lista de triángulos que forman el arco - private List> CreateTriangulatedCurve(float innerRadius, float outerRadius, float startAngle, float endAngle) + /// Ángulo inicial en radianes BEPU + /// Ángulo final en radianes BEPU + /// Array de triángulos nativos de BEPU en coordenadas locales + private Triangle[] CreateSimpleArcTriangles(float innerRadius, float outerRadius, float startAngle, float endAngle) { - var triangles = new List>(); + var triangles = new List(); - // Calcular número de segmentos basado en el tamaño del arco + // ✅ SIMPLIFICADO: Menos segmentos, menos complejidad float arcLength = Math.Abs(endAngle - startAngle) * ((innerRadius + outerRadius) / 2f); - int segments = (int)(arcLength * SegmentationFactor); - segments = Math.Max(MinSegments, Math.Min(segments, MaxSegments)); + int segments = Math.Max(8, Math.Min((int)(arcLength * 8), 32)); // Menos segmentos + float angleStep = (endAngle - startAngle) / segments; - // ✅ CRÍTICO: Inversión de dirección del arco para conversión WPF ↔ BEPU - // - // Los ángulos individuales ya están convertidos (WPF grados → BEPU radianes con signo invertido), - // pero también necesitamos invertir la DIRECCIÓN del arco: - // - // • WPF: startAngle → endAngle (sentido horario, Y+ hacia abajo) - // • BEPU: -endAngle → -startAngle (sentido antihorario, Y+ hacia arriba) - // - // Ejemplo: WPF arco de 0° a 90° → BEPU arco de -90° a 0° (equivalente visual) - float fromAngle = -endAngle; // Empezar desde el ángulo final negativo - float toAngle = -startAngle; // Terminar en el ángulo inicial negativo - float angleStep = (toAngle - fromAngle) / segments; - - // Generar vértices para los arcos interior y exterior - var innerPoints = new Vector3[segments + 1]; - var outerPoints = new Vector3[segments + 1]; - - for (int i = 0; i <= segments; i++) - { - float angle = fromAngle + i * angleStep; - float cosAngle = (float)Math.Cos(angle); - float sinAngle = (float)Math.Sin(angle); - - // Triángulos planos en el plano XY (Z = 0 relativo) - innerPoints[i] = new Vector3(innerRadius * cosAngle, innerRadius * sinAngle, 0); - outerPoints[i] = new Vector3(outerRadius * cosAngle, outerRadius * sinAngle, 0); - } - - // Crear triángulos + // ✅ SIMPLIFICADO: Sin inversión compleja de ángulos for (int i = 0; i < segments; i++) { - // Primer triángulo del sector - var upperTriangle = new List - { - innerPoints[i], - outerPoints[i], - outerPoints[i + 1] - }; - triangles.Add(upperTriangle); - - // Segundo triángulo del sector - var lowerTriangle = new List - { - innerPoints[i], - outerPoints[i + 1], - innerPoints[i + 1] - }; - triangles.Add(lowerTriangle); + float angle1 = startAngle + i * angleStep; + float angle2 = startAngle + (i + 1) * angleStep; + + // ✅ COORDENADAS LOCALES: Triángulos en Z=0, el cuerpo los posiciona correctamente + var inner1 = new Vector3(innerRadius * (float)Math.Cos(angle1), innerRadius * (float)Math.Sin(angle1), 0); + var outer1 = new Vector3(outerRadius * (float)Math.Cos(angle1), outerRadius * (float)Math.Sin(angle1), 0); + var inner2 = new Vector3(innerRadius * (float)Math.Cos(angle2), innerRadius * (float)Math.Sin(angle2), 0); + var outer2 = new Vector3(outerRadius * (float)Math.Cos(angle2), outerRadius * (float)Math.Sin(angle2), 0); + + // ✅ USAR Triangle NATIVO DE BEPU (solo superficie superior) + triangles.Add(new Triangle(inner1, outer1, outer2)); + triangles.Add(new Triangle(inner1, outer2, inner2)); } - return triangles; + System.Diagnostics.Debug.WriteLine($"[CreateSimpleArcTriangles] Creados {triangles.Count} triángulos en coordenadas locales"); + return triangles.ToArray(); } - /// - /// Calcula la dirección tangencial para aplicar fuerzas de curva. - /// NOTA: Este cálculo es dinámico (basado en posición actual) y no se ve afectado - /// por la inversión de dirección del arco en CreateTriangulatedCurve. - /// - public Vector3 GetTangentialDirection(Vector3 bottlePosition) - { - try - { - var curvePosition = GetPosition(); - var centerToBottle = new Vector2(bottlePosition.X - curvePosition.X, bottlePosition.Y - curvePosition.Y); - - if (centerToBottle.Length() < 0.001f) - return Vector3.Zero; - - // Vector tangente (perpendicular al radio) en el plano XY - var tangent = new Vector3(-centerToBottle.Y, centerToBottle.X, 0); - - // Normalizar - if (tangent.Length() > 0.001f) - { - tangent = Vector3.Normalize(tangent); - } - - // Ajustar dirección según la velocidad (signo) - if (Speed < 0) - tangent = -tangent; - - return tangent; - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine($"[simCurve] Error calculating tangential direction: {ex.Message}"); - return Vector3.Zero; - } - } - - // ✅ MÉTODO MEJORADO - versión optimizada - public Vector3 GetCachedTangentialDirection(Vector3 bottlePosition) - { - var direction = GetTangentialDirection(bottlePosition); - return Speed < 0 ? -direction : direction; - } - - /// - /// ✅ NUEVO - Obtiene la dirección tangencial fija de un triángulo específico - /// - public Vector3 GetTriangleDirection(BodyHandle triangleHandle) - { - return _triangleDirections.GetValueOrDefault(triangleHandle, Vector3.Zero); - } + // ✅ MÉTODOS ELIMINADOS: Los cálculos tangenciales complejos ya no son necesarios + // AngularAxisMotor maneja automáticamente la rotación en curvas } public class simDescarte : simBase @@ -1712,11 +1692,11 @@ namespace CtrEditor.Simulacion return false; // NO generar contacto físico para descartes } - // ✅ PROCESAR CREACIÓN DE MOTORES para transportes y curvas + // ✅ SIMPLIFICADO: Detección directa de transportes y curvas var transportA = GetTransportFromCollidable(pair.A); var transportB = GetTransportFromCollidable(pair.B); - (simCurve curveA, Vector3 directionA) = GetCurveAndDirectionFromCollidable(pair.A); - (simCurve curveB, Vector3 directionB) = GetCurveAndDirectionFromCollidable(pair.B); + var curveA = GetCurveFromCollidable(pair.A); + var curveB = GetCurveFromCollidable(pair.B); // ✅ VERIFICAR SI YA SE CREÓ MOTOR PARA EVITAR DUPLICADOS BodyHandle? bottleHandle = null; @@ -1741,23 +1721,21 @@ namespace CtrEditor.Simulacion pairMaterial.MaximumRecoveryVelocity = 1f; pairMaterial.SpringSettings = new SpringSettings(80, 6); } - // ✅ SIMPLIFICADO: Si hay contacto botella-triángulo de curva, crear motor con dirección específica + // ✅ SIMPLIFICADO: Si hay contacto botella-curva, crear AngularAxisMotor else if (botella != null && (curveA != null || curveB != null)) { var curve = curveA ?? curveB; - var direction = curveA != null ? directionA : directionB; - var triangleHandle = curveA != null ? pair.A.BodyHandle : pair.B.BodyHandle; bottleHandle = botella.BodyHandle; - elementHandle = triangleHandle; + elementHandle = curve.BodyHandle; - // ✅ SIMPLIFICADO: Crear motor usando sistema natural de colisiones de BEPU + // ✅ SIMPLIFICADO: Crear motor angular usando el cuerpo principal de la curva var pairKey = (bottleHandle.Value, elementHandle.Value); if (!_simulationManager._motorsCreated.Contains(pairKey)) { - _simulationManager.TryCreateCurveMotor(botella, curve, direction, triangleHandle); + _simulationManager.TryCreateCurveAngularMotor(botella, curve); _simulationManager._motorsCreated.Add(pairKey); - System.Diagnostics.Debug.WriteLine($"[Manifold] Motor curva: {bottleHandle} → {triangleHandle}"); + System.Diagnostics.Debug.WriteLine($"[Manifold] Motor angular curva: {bottleHandle} → {elementHandle}"); } // Fricción alta para curvas (SIEMPRE aplicar para colisión física) @@ -1842,37 +1820,12 @@ namespace CtrEditor.Simulacion if (collidable.Mobility == CollidableMobility.Kinematic) { var bodyHandle = collidable.BodyHandle; - - // ✅ NUEVO: Buscar específicamente en los triángulos de las curvas - return _simulationManager.GetCurveFromTriangleBodyHandle(bodyHandle); + var simBase = _simulationManager.GetSimBaseFromBodyHandle(bodyHandle); + return simBase as simCurve; } } return null; } - - /// - /// ✅ NUEVO: Obtiene curva y dirección específica del triángulo - /// - private (simCurve curve, Vector3 direction) GetCurveAndDirectionFromCollidable(CollidableReference collidable) - { - if (_simulationManager?.simulation != null) - { - if (collidable.Mobility == CollidableMobility.Kinematic) - { - var bodyHandle = collidable.BodyHandle; - - // Buscar curva que contenga este triángulo - var curve = _simulationManager.GetCurveFromTriangleBodyHandle(bodyHandle); - if (curve != null) - { - // Obtener dirección específica del triángulo - var direction = curve.GetTriangleDirection(bodyHandle); - return (curve, direction); - } - } - } - return (null, Vector3.Zero); - } /// /// ✅ NUEVO MÉTODO: Aplicar fuerzas de frenado directamente en el manifold @@ -2013,6 +1966,15 @@ namespace CtrEditor.Simulacion } public void CreateCurveMotor(simBotella bottle, simCurve curve) + { + // ✅ SIMPLIFICADO: Usar AngularAxisMotor para curvas + CreateCurveAngularMotor(bottle, curve); + } + + /// + /// ✅ NUEVO: Crear motor angular para curvas (simplificado) + /// + public void CreateCurveAngularMotor(simBotella bottle, simCurve curve) { try { @@ -2025,50 +1987,34 @@ namespace CtrEditor.Simulacion !_simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle)) return; - var direction = curve.GetCachedTangentialDirection(bottle.GetPosition()); - - // Validar que la dirección no sea cero - if (direction.Length() < 0.001f) + // Verificar que la curva tenga velocidad + if (Math.Abs(curve.Speed) < 0.001f) return; - // ✅ CONVERTIR DIRECCIÓN MUNDIAL A COORDENADAS LOCALES DE LA CURVA - Vector3 localAxisDirection; - - if (_simulationManager.simulation.Bodies.BodyExists(curve.BodyHandle)) + // ✅ SIMPLIFICADO: AngularAxisMotor para rotación en el plano XY + var angularMotor = new AngularAxisMotor() { - var curveBody = _simulationManager.simulation.Bodies[curve.BodyHandle]; - var inverseCurveOrientation = Quaternion.Conjugate(curveBody.Pose.Orientation); - localAxisDirection = Vector3.Transform(direction, inverseCurveOrientation); - } - else - { - // Fallback: usar dirección mundial si no se puede obtener orientación - localAxisDirection = direction; - } - - var motor = new LinearAxisMotor() - { - LocalOffsetA = Vector3.Zero, - LocalOffsetB = Vector3.Zero, - LocalAxis = localAxisDirection, // ✅ Usar dirección en coordenadas locales - TargetVelocity = curve.SpeedMetersPerSecond, - Settings = new MotorSettings(Math.Max(bottle.Mass * 20f, 8f), 4f) + LocalAxisA = Vector3.UnitZ, // Eje Z para rotación en plano XY + TargetVelocity = curve.Speed, // Velocidad angular directa en rad/s + Settings = new MotorSettings(Math.Max(bottle.Mass * 10f, 5f), 3f) }; var motorHandle = _simulationManager.simulation.Solver.Add( - curve.BodyHandle, // ✅ USAR CUERPO DE LA CURVA + curve.BodyHandle, bottle.BodyHandle, - motor + angularMotor ); _activeMotors[bottle.BodyHandle] = motorHandle; _motorToBottle[motorHandle] = bottle; - bottle.CurrentMotor = motorHandle; + bottle.AssignAngularMotor(curve, motorHandle); curve.BottlesOnCurve.Add(bottle); + + System.Diagnostics.Debug.WriteLine($"[MotorManager] Angular motor created: {bottle.BodyHandle} → {curve.BodyHandle}"); } catch (Exception ex) { - System.Diagnostics.Debug.WriteLine($"[MotorManager] Error creating curve motor: {ex.Message}"); + System.Diagnostics.Debug.WriteLine($"[MotorManager] Error creating angular motor: {ex.Message}"); } } @@ -2232,7 +2178,7 @@ namespace CtrEditor.Simulacion LocalOffsetA = Vector3.Zero, LocalOffsetB = Vector3.Zero, LocalAxis = localAxisDirection, // ✅ Usar dirección en coordenadas locales - TargetVelocity = curve.SpeedMetersPerSecond, + TargetVelocity = curve.Speed, // ✅ SIMPLIFICADO: Usar velocidad directa Settings = new MotorSettings(Math.Max(bottle.Mass * 20f, 8f), 4f) }; @@ -2242,7 +2188,7 @@ namespace CtrEditor.Simulacion motor ); // Logging con coordenadas mundiales y locales para depuración - System.Diagnostics.Debug.WriteLine($"[CreateCurveMotorWithDirection] Bottle: {bottle.BodyHandle} World: ({direction.X:F2}, {direction.Y:F2}) Local: ({localAxisDirection.X:F2}, {localAxisDirection.Y:F2}) Speed: {curve.SpeedMetersPerSecond}"); + System.Diagnostics.Debug.WriteLine($"[CreateCurveMotorWithDirection] Bottle: {bottle.BodyHandle} World: ({direction.X:F2}, {direction.Y:F2}) Local: ({localAxisDirection.X:F2}, {localAxisDirection.Y:F2}) Speed: {curve.Speed}"); _activeMotors[bottle.BodyHandle] = motorHandle; _motorToBottle[motorHandle] = bottle; @@ -2371,16 +2317,10 @@ namespace CtrEditor.Simulacion _activeMotors.Clear(); _motorToBottle.Clear(); - // ✅ NUEVO: Limpiar cachés de direcciones de todas las curvas y pares silenciados + // ✅ SIMPLIFICADO: Solo limpiar pares de motores creados if (_simulationManager?.Cuerpos != null) { - var curves = _simulationManager.Cuerpos.OfType(); - foreach (var curve in curves) - { - curve._triangleDirections?.Clear(); - } - - // También limpiar pares de motores creados + // Limpiar pares de motores creados _simulationManager._motorsCreated?.Clear(); } } @@ -2442,22 +2382,11 @@ namespace CtrEditor.Simulacion } /// - /// ✅ NUEVO: Obtiene una simCurve si el BodyHandle pertenece a uno de sus triángulos + /// ✅ SIMPLIFICADO: Obtiene una simCurve desde su BodyHandle principal /// public simCurve GetCurveFromTriangleBodyHandle(BodyHandle bodyHandle) { - // Buscar en todas las curvas si alguna contiene este BodyHandle en sus triángulos - var curves = Cuerpos.OfType(); - - foreach (var curve in curves) - { - if (curve._triangleBodyHandles != null && curve._triangleBodyHandles.Contains(bodyHandle)) - { - return curve; - } - } - - // Si no se encuentra en triángulos, buscar en cuerpos principales como fallback + // ✅ SIMPLIFICADO: Solo buscar en cuerpos principales (ya no hay triángulos separados) var simBase = GetSimBaseFromBodyHandle(bodyHandle); return simBase as simCurve; } @@ -2479,14 +2408,7 @@ namespace CtrEditor.Simulacion break; case simCurve curve: _curveHandles.Add(curve.BodyHandle); - // También añadir todos sus triángulos - if (curve._triangleBodyHandles != null) - { - foreach (var triangleHandle in curve._triangleBodyHandles) - { - _curveHandles.Add(triangleHandle); - } - } + // ✅ SIMPLIFICADO: Ya no hay triángulos separados, solo el cuerpo principal curve.OnSpeedChanged += _motorManager.UpdateCurveSpeed; break; case simBarrera barrier: @@ -2515,13 +2437,7 @@ namespace CtrEditor.Simulacion break; case simCurve curve: _curveHandles.Remove(curve.BodyHandle); - if (curve._triangleBodyHandles != null) - { - foreach (var triangleHandle in curve._triangleBodyHandles) - { - _curveHandles.Remove(triangleHandle); - } - } + // ✅ SIMPLIFICADO: Ya no hay triángulos separados curve.OnSpeedChanged -= _motorManager.UpdateCurveSpeed; break; case simBarrera barrier: @@ -2689,7 +2605,7 @@ namespace CtrEditor.Simulacion foreach (var curve in Cuerpos.OfType()) { // ✅ CORREGIDO: Usar método público para actualizar velocidad - curve.SetSpeed(curve.Speed); // Esto actualiza automáticamente SpeedMetersPerSecond internamente + curve.SetSpeed(curve.Speed); // ✅ SIMPLIFICADO: Reinicializar velocidad } stopwatch.Start(); @@ -3013,6 +2929,35 @@ namespace CtrEditor.Simulacion } } + /// + /// ✅ NUEVO: Intenta crear un motor angular para curvas si no existe uno activo + /// + public void TryCreateCurveAngularMotor(simBotella bottle, simCurve curve) + { + try + { + // Validaciones básicas + if (bottle == null || curve == null || _motorManager == null) + return; + + // Verificar si ya tiene un motor activo + if (bottle.HasActiveMotor) + return; + + // Asegurar que la curva tenga velocidad + if (Math.Abs(curve.Speed) < 0.001f) + return; // No crear motor para curvas detenidas + + _motorManager.CreateCurveAngularMotor(bottle, curve); + + System.Diagnostics.Debug.WriteLine($"[TryCreateCurveAngularMotor] Motor angular creado: {bottle.BodyHandle} - {curve.BodyHandle}"); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[SimulationManager] Error in TryCreateCurveAngularMotor: {ex.Message}"); + } + } + /// diff --git a/Simulacion/BEPUVisualization3D.cs b/Simulacion/BEPUVisualization3D.cs index 35f8aef..e83ee80 100644 --- a/Simulacion/BEPUVisualization3D.cs +++ b/Simulacion/BEPUVisualization3D.cs @@ -386,153 +386,130 @@ namespace CtrEditor.Simulacion } /// - /// ✅ NUEVO: Crea mesh directamente desde los triángulos de BEPU (debug real) + /// ✅ NUEVO: Crea mesh directamente desde los triángulos reales de BEPU + /// Extrae la geometría exacta que está almacenada en la simulación física /// private void CreateCurveMeshFromBEPUTriangles(MeshBuilder meshBuilder, simCurve curve) { try { - // Obtener los triángulos originales directamente de BEPU - var originalTriangles = curve.GetOriginalTriangles(); - - if (originalTriangles.Count == 0) + // ✅ EXTRAER TRIÁNGULOS REALES DE BEPU + var realTriangles = curve.GetRealBEPUTriangles(); + + if (realTriangles.Length == 0) { - System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No triangles found in BEPU curve"); + System.Diagnostics.Debug.WriteLine($"[3D BEPU] WARNING: No se pudieron extraer triángulos reales, usando fallback"); + CreateCurveMeshFallback(meshBuilder, curve); return; } - // Altura para simular triángulos planos (misma que en BEPU) - const float curveHeight = 0.05f; // simCurve.zAltura_Curve + System.Diagnostics.Debug.WriteLine($"[3D BEPU] Creando mesh desde {realTriangles.Length} triángulos reales de BEPU"); - // Convertir cada triángulo de BEPU a triángulos en Helix - foreach (var triangle in originalTriangles) + // ✅ USAR TRIÁNGULOS EXACTOS DE BEPU + foreach (var triangle in realTriangles) { - if (triangle.Count != 3) - { - System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: Invalid triangle with {triangle.Count} vertices"); - continue; - } - - // Convertir Vector3 de BEPU a Point3D de Helix - // Los triángulos en BEPU están en el plano XY (Z=0), crear superficie 3D - - // Puntos de la superficie inferior (Z = 0) - var p1Bottom = new Point3D(triangle[0].X, triangle[0].Y, 0); - var p2Bottom = new Point3D(triangle[1].X, triangle[1].Y, 0); - var p3Bottom = new Point3D(triangle[2].X, triangle[2].Y, 0); - - // Puntos de la superficie superior (Z = curveHeight) - var p1Top = new Point3D(triangle[0].X, triangle[0].Y, curveHeight); - var p2Top = new Point3D(triangle[1].X, triangle[1].Y, curveHeight); - var p3Top = new Point3D(triangle[2].X, triangle[2].Y, curveHeight); - - // Crear superficie superior del triángulo - meshBuilder.AddTriangle(p1Top, p2Top, p3Top); - - // Crear superficie inferior del triángulo (orden inverso para normales correctas) - meshBuilder.AddTriangle(p1Bottom, p3Bottom, p2Bottom); - - // Crear paredes laterales del triángulo (3 quads) - meshBuilder.AddQuad(p1Bottom, p2Bottom, p2Top, p1Top); // Lado 1-2 - meshBuilder.AddQuad(p2Bottom, p3Bottom, p3Top, p2Top); // Lado 2-3 - meshBuilder.AddQuad(p3Bottom, p1Bottom, p1Top, p3Top); // Lado 3-1 + // Convertir triángulos de BEPU a puntos 3D de Helix + var pointA = new Point3D(triangle.A.X, triangle.A.Y, triangle.A.Z); + var pointB = new Point3D(triangle.B.X, triangle.B.Y, triangle.B.Z); + var pointC = new Point3D(triangle.C.X, triangle.C.Y, triangle.C.Z); + + // Agregar triángulo exacto al mesh + meshBuilder.AddTriangle(pointA, pointB, pointC); } + System.Diagnostics.Debug.WriteLine($"[3D BEPU] ✅ Mesh creado usando triángulos reales de BEPU"); } catch (Exception ex) { - System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR creating mesh from BEPU triangles: {ex.Message}"); - - // Fallback: usar el método anterior si falla la lectura de BEPU - System.Diagnostics.Debug.WriteLine($"[3D Debug] Falling back to recreated geometry"); + System.Diagnostics.Debug.WriteLine($"[3D BEPU] ERROR extrayendo triángulos reales: {ex.Message}"); + System.Diagnostics.Debug.WriteLine($"[3D BEPU] Fallback a geometría recreada"); CreateCurveMeshFallback(meshBuilder, curve); } } /// - /// ✅ NUEVO: Función de debug que muestra triángulos individuales de BEPU - /// Cada triángulo se renderiza de manera separada para poder hacer debug visual + /// ✅ NUEVO: Función de debug que muestra triángulos individuales reales de BEPU + /// Cada triángulo se renderiza de manera separada con offset para poder hacer debug visual /// private void CreateCurveDebugMeshWithIndividualTriangles(MeshBuilder meshBuilder, simCurve curve) { try { - // Obtener los triángulos originales directamente de BEPU - var originalTriangles = curve.GetOriginalTriangles(); - - if (originalTriangles.Count == 0) + // ✅ EXTRAER TRIÁNGULOS REALES DE BEPU PARA DEBUG + var realTriangles = curve.GetRealBEPUTriangles(); + + if (realTriangles.Length == 0) { - System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No triangles found in BEPU curve"); + System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: No hay triángulos reales para debug, usando fallback"); + CreateCurveMeshFallback(meshBuilder, curve); return; } - System.Diagnostics.Debug.WriteLine($"[3D Debug] Creating debug visualization for {originalTriangles.Count} triangles"); + System.Diagnostics.Debug.WriteLine($"[3D Debug] Creando debug de {realTriangles.Length} triángulos reales individuales"); - // Altura muy pequeña para mostrar triángulos planos (como en BEPU) - const float debugHeight = 0.02f; // Más bajo que la superficie normal para diferenciarlo - const float triangleSeparation = 0.01f; // Separación pequeña entre triángulos para distinguirlos - - // Convertir cada triángulo de BEPU a un triángulo 3D individual - for (int i = 0; i < originalTriangles.Count; i++) + // ✅ MOSTRAR CADA TRIÁNGULO SEPARADO CON OFFSET PARA DEBUG + float triangleSeparation = 0.02f; // Separación entre triángulos para debug visual + + for (int i = 0; i < realTriangles.Length; i++) { - var triangle = originalTriangles[i]; - - if (triangle.Count != 3) - { - System.Diagnostics.Debug.WriteLine($"[3D Debug] WARNING: Invalid triangle {i} with {triangle.Count} vertices"); - continue; - } - - // Calcular el centro del triángulo para aplicar separación - var center = new System.Numerics.Vector3( - (triangle[0].X + triangle[1].X + triangle[2].X) / 3f, - (triangle[0].Y + triangle[1].Y + triangle[2].Y) / 3f, - 0 - ); - - // Aplicar una pequeña separación desde el centro para distinguir triángulos - var offset = center * triangleSeparation; - - // Crear triángulo superior (visible desde arriba) - var p1Top = new Point3D(triangle[0].X + offset.X, triangle[0].Y + offset.Y, debugHeight); - var p2Top = new Point3D(triangle[1].X + offset.X, triangle[1].Y + offset.Y, debugHeight); - var p3Top = new Point3D(triangle[2].X + offset.X, triangle[2].Y + offset.Y, debugHeight); - - // Crear triángulo inferior (visible desde abajo) - var p1Bottom = new Point3D(triangle[0].X + offset.X, triangle[0].Y + offset.Y, 0); - var p2Bottom = new Point3D(triangle[1].X + offset.X, triangle[1].Y + offset.Y, 0); - var p3Bottom = new Point3D(triangle[2].X + offset.X, triangle[2].Y + offset.Y, 0); - - // Agregar triángulo superior (normal hacia arriba) - meshBuilder.AddTriangle(p1Top, p2Top, p3Top); - - // Agregar triángulo inferior (normal hacia abajo) - meshBuilder.AddTriangle(p1Bottom, p3Bottom, p2Bottom); - - // ✅ OPCIONAL: Agregar bordes/wireframe para mejor visualización del debug - // Crear líneas delgadas en los bordes del triángulo para verlo mejor - const float edgeThickness = 0.005f; - - // Borde 1-2 - AddDebugEdge(meshBuilder, p1Top, p2Top, p1Bottom, p2Bottom, edgeThickness); - // Borde 2-3 - AddDebugEdge(meshBuilder, p2Top, p3Top, p2Bottom, p3Bottom, edgeThickness); - // Borde 3-1 - AddDebugEdge(meshBuilder, p3Top, p1Top, p3Bottom, p1Bottom, edgeThickness); + var triangle = realTriangles[i]; + + // Calcular centroide del triángulo + var centroid = (triangle.A + triangle.B + triangle.C) / 3f; + + // Calcular normal del triángulo + var edge1 = triangle.B - triangle.A; + var edge2 = triangle.C - triangle.A; + var normal = Vector3.Normalize(Vector3.Cross(edge1, edge2)); + + // Offset hacia arriba para separar triángulos visualmente + var offset = normal * triangleSeparation * (i + 1); + + // Aplicar offset a todos los vértices + var offsetA = triangle.A + offset; + var offsetB = triangle.B + offset; + var offsetC = triangle.C + offset; + + // Convertir a puntos 3D de Helix + var pointA = new Point3D(offsetA.X, offsetA.Y, offsetA.Z); + var pointB = new Point3D(offsetB.X, offsetB.Y, offsetB.Z); + var pointC = new Point3D(offsetC.X, offsetC.Y, offsetC.Z); + + // Agregar triángulo separado + meshBuilder.AddTriangle(pointA, pointB, pointC); + + // ✅ DEBUG: Agregar bordes para visualizar mejor cada triángulo + AddDebugTriangleEdges(meshBuilder, pointA, pointB, pointC, 0.005f); } - System.Diagnostics.Debug.WriteLine($"[3D Debug] Successfully created debug mesh with {originalTriangles.Count} individual triangles"); + System.Diagnostics.Debug.WriteLine($"[3D Debug] ✅ Debug mesh creado con triángulos reales separados"); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR creating debug mesh: {ex.Message}"); - - // Fallback: mostrar mensaje de error en consola y usar geometría básica System.Diagnostics.Debug.WriteLine($"[3D Debug] Falling back to basic debug geometry"); CreateBasicDebugGeometry(meshBuilder, curve); } } + /// + /// ✅ NUEVO: Agrega bordes debug a un triángulo individual + /// + private void AddDebugTriangleEdges(MeshBuilder meshBuilder, Point3D a, Point3D b, Point3D c, double edgeThickness) + { + try + { + // Crear cilindros delgados para los bordes del triángulo + meshBuilder.AddCylinder(a, b, edgeThickness, 4, false, false); + meshBuilder.AddCylinder(b, c, edgeThickness, 4, false, false); + meshBuilder.AddCylinder(c, a, edgeThickness, 4, false, false); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[3D Debug] ERROR adding triangle edges: {ex.Message}"); + } + } + /// /// ✅ NUEVO: Agrega un borde delgado entre dos puntos para debug visual /// @@ -1071,8 +1048,8 @@ namespace CtrEditor.Simulacion } /// - /// ✅ NUEVO: Obtiene información detallada de debug sobre los triángulos de una curva - /// Útil para debug sin cambiar la visualización + /// ✅ NUEVO: Obtiene información detallada de debug sobre los triángulos reales de BEPU + /// Extrae y muestra los triángulos exactos almacenados en la simulación física /// /// Curva para analizar /// Información de debug como string @@ -1083,40 +1060,95 @@ namespace CtrEditor.Simulacion try { - var triangles = curve.GetOriginalTriangles(); var debugInfo = new System.Text.StringBuilder(); - debugInfo.AppendLine($"=== DEBUG INFO PARA CURVA ==="); + debugInfo.AppendLine($"=== DEBUG INFO TRIÁNGULOS REALES DE BEPU ==="); debugInfo.AppendLine($"Parámetros de curva:"); debugInfo.AppendLine($" - Radio Interior: {curve.InnerRadius}"); debugInfo.AppendLine($" - Radio Exterior: {curve.OuterRadius}"); - debugInfo.AppendLine($" - Ángulo Inicio: {curve.StartAngle} rad ({curve.StartAngle * 180 / Math.PI:F1}°)"); - debugInfo.AppendLine($" - Ángulo Fin: {curve.EndAngle} rad ({curve.EndAngle * 180 / Math.PI:F1}°)"); + debugInfo.AppendLine($" - Ángulo Inicio: {curve.StartAngle}° ({curve.StartAngle * Math.PI / 180:F3} rad)"); + debugInfo.AppendLine($" - Ángulo Fin: {curve.EndAngle}° ({curve.EndAngle * Math.PI / 180:F3} rad)"); + debugInfo.AppendLine($" - Velocidad: {curve.Speed} rad/s"); debugInfo.AppendLine($""); - debugInfo.AppendLine($"Triángulos en BEPU: {triangles.Count}"); - for (int i = 0; i < Math.Min(triangles.Count, 10); i++) // Mostrar máximo 10 triángulos + // ✅ OBTENER INFORMACIÓN REAL DEL MESH DE BEPU + var meshInfo = curve.GetBEPUMeshDebugInfo(); + debugInfo.AppendLine("INFORMACIÓN DEL MESH EN BEPU:"); + debugInfo.AppendLine(meshInfo); + debugInfo.AppendLine($""); + + // ✅ EXTRAER Y MOSTRAR TRIÁNGULOS REALES + var realTriangles = curve.GetRealBEPUTriangles(); + debugInfo.AppendLine($"TRIÁNGULOS REALES EXTRAÍDOS: {realTriangles.Length}"); + debugInfo.AppendLine($""); + + // Mostrar los primeros triángulos con detalles completos + int maxToShow = Math.Min(10, realTriangles.Length); + for (int i = 0; i < maxToShow; i++) { - var triangle = triangles[i]; - debugInfo.AppendLine($" Triángulo {i + 1}:"); - debugInfo.AppendLine($" P1: ({triangle[0].X:F3}, {triangle[0].Y:F3}, {triangle[0].Z:F3})"); - debugInfo.AppendLine($" P2: ({triangle[1].X:F3}, {triangle[1].Y:F3}, {triangle[1].Z:F3})"); - debugInfo.AppendLine($" P3: ({triangle[2].X:F3}, {triangle[2].Y:F3}, {triangle[2].Z:F3})"); + var triangle = realTriangles[i]; + debugInfo.AppendLine($"Triángulo {i + 1}:"); + debugInfo.AppendLine($" A: ({triangle.A.X:F4}, {triangle.A.Y:F4}, {triangle.A.Z:F4})"); + debugInfo.AppendLine($" B: ({triangle.B.X:F4}, {triangle.B.Y:F4}, {triangle.B.Z:F4})"); + debugInfo.AppendLine($" C: ({triangle.C.X:F4}, {triangle.C.Y:F4}, {triangle.C.Z:F4})"); + + // Calcular área del triángulo + var edge1 = triangle.B - triangle.A; + var edge2 = triangle.C - triangle.A; + var cross = Vector3.Cross(edge1, edge2); + var area = cross.Length() / 2f; + debugInfo.AppendLine($" Área: {area:F6}"); + debugInfo.AppendLine($""); } - if (triangles.Count > 10) + if (realTriangles.Length > maxToShow) { - debugInfo.AppendLine($" ... y {triangles.Count - 10} triángulos más"); + debugInfo.AppendLine($"... y {realTriangles.Length - maxToShow} triángulos más"); } debugInfo.AppendLine($""); - debugInfo.AppendLine($"Modo debug actual: {(DebugShowIndividualTriangles ? "ACTIVADO" : "DESACTIVADO")}"); + debugInfo.AppendLine($"Modo debug visual: {(DebugShowIndividualTriangles ? "ACTIVADO (triángulos separados)" : "DESACTIVADO (superficie continua)")}"); return debugInfo.ToString(); } catch (Exception ex) { - return $"ERROR obteniendo info de debug: {ex.Message}"; + return $"ERROR obteniendo info de debug real: {ex.Message}"; + } + } + + /// + /// ✅ NUEVO: Activa la visualización de triángulos reales de BEPU + /// Muestra los triángulos exactos extraídos de la simulación física + /// + /// True para mostrar triángulos reales, false para superficie normal + public void SetRealBEPUTrianglesMode(bool enable) + { + SetDebugTrianglesMode(enable); + System.Diagnostics.Debug.WriteLine($"[3D BEPU] Modo triángulos reales: {(enable ? "ACTIVADO" : "DESACTIVADO")}"); + } + + /// + /// ✅ NUEVO: Verifica si una curva tiene triángulos válidos en BEPU + /// + /// Curva a verificar + /// True si tiene triángulos válidos + public bool HasValidBEPUTriangles(simCurve curve) + { + if (curve == null) return false; + + try + { + var triangles = curve.GetRealBEPUTriangles(); + bool hasTriangles = triangles.Length > 0; + + System.Diagnostics.Debug.WriteLine($"[3D BEPU] Curva tiene {triangles.Length} triángulos válidos: {hasTriangles}"); + return hasTriangles; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[3D BEPU] Error verificando triángulos: {ex.Message}"); + return false; } }