From 2cb90ec2dcf0228b2d684bf969019cadb280d97e Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 25 Jun 2025 13:59:07 +0200 Subject: [PATCH] =?UTF-8?q?Se=20a=C3=B1adieron=20configuraciones=20para=20?= =?UTF-8?q?Hot=20Reload=20en=20CtrEditor.csproj=20y=20se=20ajustaron=20las?= =?UTF-8?q?=20propiedades=20de=20depuraci=C3=B3n=20en=20los=20grupos=20de?= =?UTF-8?q?=20configuraci=C3=B3n.=20En=20MainViewModel.cs,=20se=20corrigi?= =?UTF-8?q?=C3=B3=20un=20error=20en=20el=20tiempo=20de=20simulaci=C3=B3n.?= =?UTF-8?q?=20En=20Aether.cs,=20se=20implementaron=20nuevas=20propiedades?= =?UTF-8?q?=20y=20m=C3=A9todos=20para=20gestionar=20la=20presi=C3=B3n=20en?= =?UTF-8?q?tre=20botellas,=20mejorando=20la=20l=C3=B3gica=20de=20colisione?= =?UTF-8?q?s=20y=20evitando=20superposiciones.=20Estas=20mejoras=20optimiz?= =?UTF-8?q?an=20la=20simulaci=C3=B3n=20y=20la=20interacci=C3=B3n=20entre?= =?UTF-8?q?=20objetos.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CtrEditor.csproj | 12 +- MainViewModel.cs | 2 +- Simulacion/Aether.cs | 257 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 262 insertions(+), 9 deletions(-) diff --git a/CtrEditor.csproj b/CtrEditor.csproj index ffa7a96..bbfe580 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -7,14 +7,24 @@ enable true x64 + + true + true + true - True + False + DEBUG;TRACE + portable + true True + TRACE + pdbonly + true diff --git a/MainViewModel.cs b/MainViewModel.cs index bad6e71..65442ba 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -947,7 +947,7 @@ namespace CtrEditor simSampleCount++; // Eliminar el diseño de Debug luego de 2 segundos - if (TiempoDesdeStartSimulacion > 12000) + if (TiempoDesdeStartSimulacion > 1200) simulationManager.Debug_ClearSimulationShapes(); else TiempoDesdeStartSimulacion += (float)elapsedMilliseconds; diff --git a/Simulacion/Aether.cs b/Simulacion/Aether.cs index 3010485..79d9c0e 100644 --- a/Simulacion/Aether.cs +++ b/Simulacion/Aether.cs @@ -138,12 +138,12 @@ namespace CtrEditor.Simulacion Vector2 centerToBottle = bottle.Body.Position - Body.Position; // Calculate angle of bottle relative to curve center (in radians) float bottleAngle = (float)Math.Atan2(centerToBottle.Y, centerToBottle.X); - + // Normalize all angles to the same range [0, 2π] float normalizedBottleAngle = bottleAngle < 0 ? bottleAngle + 2 * (float)Math.PI : bottleAngle; float normalizedStartAngle = _startAngle < 0 ? _startAngle + 2 * (float)Math.PI : _startAngle; float normalizedEndAngle = _endAngle < 0 ? _endAngle + 2 * (float)Math.PI : _endAngle; - + // Handle case where the arc crosses the 0/2π boundary if (normalizedStartAngle > normalizedEndAngle) { @@ -164,7 +164,7 @@ namespace CtrEditor.Simulacion return 0; // Calculate how far the bottle is from the edges using normalized angles float angleFromStart, angleToEnd, totalAngle; - + if (normalizedStartAngle > normalizedEndAngle) { // Arc crosses 0/2π boundary @@ -187,7 +187,7 @@ namespace CtrEditor.Simulacion angleToEnd = normalizedEndAngle - normalizedBottleAngle; totalAngle = normalizedEndAngle - normalizedStartAngle; } - + // Use the minimum distance to either edge to calculate overlap float minAngleDistance = Math.Min(angleFromStart, angleToEnd); // Calculate overlap percentage based on angle proximity to edges @@ -514,6 +514,9 @@ namespace CtrEditor.Simulacion public float OverlapPercentage; public float _neckRadius; PrismaticJoint _activeJoint; + public bool isBeingPressedByOtherBottles; + public int bottleContactCount; + public float debugPenetrationLevel; // Para debug: nivel de penetración actual private List _deferredActions; public simBotella(World world, List deferredActions, float diameter, Vector2 position, float mass, float neckRadius = 0) { @@ -583,6 +586,8 @@ namespace CtrEditor.Simulacion RemoverBody(); isOnTransports = 0; _activeJoint = null; + isBeingPressedByOtherBottles = false; + bottleContactCount = 0; Body = _world.CreateCircle(Radius, 1f, position); Body.BodyType = BodyType.Dynamic; // Restablecer manejador de eventos de colisión @@ -648,6 +653,11 @@ namespace CtrEditor.Simulacion return true; // No aplicar respuestas físicas } + else if (fixtureB.Body.Tag is simBotella) + { + bottleContactCount++; + return true; // Permitir respuestas físicas entre botellas + } return true; // No aplicar respuestas físicas } @@ -702,6 +712,11 @@ namespace CtrEditor.Simulacion Sensor.LuzCortada -= 1; Sensor.ListSimBotellaContact.Remove(this); } + + if (fixtureB.Body.Tag is simBotella) + { + bottleContactCount = Math.Max(0, bottleContactCount - 1); + } } /// @@ -753,8 +768,17 @@ namespace CtrEditor.Simulacion return true; } - // Aplicar la fuerza de fricción en función del porcentaje de superficie sobrepuesta - Body.LinearVelocity += frictionCoefficient * desiredVelocity; + // Si la botella está siendo presionada por múltiples botellas, reducir la aplicación de fuerza + float pressureReductionFactor = 1.0f; + if (isBeingPressedByOtherBottles) + { + // Reducir el factor basándose en la cantidad de contactos + pressureReductionFactor = Math.Max(0.1f, 1.0f / (1.0f + bottleContactCount * 0.5f)); + } + + // Aplicar la fuerza de fricción en función del porcentaje de superficie sobrepuesta y la presión + Vector2 finalVelocityChange = frictionCoefficient * desiredVelocity * pressureReductionFactor; + Body.LinearVelocity += finalVelocityChange; //Body.ApplyForce(frictionForce * overlapPercentage); return false; } @@ -842,6 +866,9 @@ namespace CtrEditor.Simulacion _deferredActions = new List(); stopwatch = new Stopwatch(); stopwatch.Start(); + + // Configurar el callback PreSolve para manejar contactos entre botellas + world.ContactManager.PreSolve += OnPreSolve; } public void Clear() @@ -862,6 +889,15 @@ namespace CtrEditor.Simulacion public void Step() { + // Resetear las flags de presión al inicio del frame + ResetBottlePressureFlags(); + + // Verificar y corregir el espaciado entre botellas antes del step + PreventBottleOverlapping(); + + // Actualizar el estado de presión basado en contactos reales + UpdateBottlePressureStatus(); + // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos float elapsedMilliseconds = (float)(stopwatch.Elapsed.TotalMilliseconds - stopwatch_last); stopwatch_last = stopwatch.Elapsed.TotalMilliseconds; @@ -876,7 +912,7 @@ namespace CtrEditor.Simulacion { botella.CenterFixtureOnConveyor(); botella.Body.Inertia = 0; - botella.Body.Mass = 100; + botella.Body.Mass = botella.OriginalMass * 100; } else if (botella.isNoMoreRestricted) { @@ -1048,5 +1084,212 @@ namespace CtrEditor.Simulacion return polygon; } + + private void OnPreSolve(Contact contact, ref nkast.Aether.Physics2D.Collision.Manifold oldManifold) + { + var fixtureA = contact.FixtureA; + var fixtureB = contact.FixtureB; + + // Verificar si el contacto es entre dos botellas + if (fixtureA.Body.Tag is simBotella botellaA && fixtureB.Body.Tag is simBotella botellaB) + { + // Calcular la distancia entre centros de las botellas + Vector2 centerA = botellaA.Body.Position; + Vector2 centerB = botellaB.Body.Position; + Vector2 direction = centerB - centerA; + float distance = direction.Length(); + + // Distancia mínima permitida (suma de radios + margen de seguridad) + float minDistance = botellaA.Radius + botellaB.Radius + 0.01f; // 1cm de margen + + // Calcular la velocidad relativa + Vector2 relativeVelocity = botellaB.Body.LinearVelocity - botellaA.Body.LinearVelocity; + float relativeSpeed = relativeVelocity.Length(); + + // Solo marcar como presionadas si hay verdadera interpenetración O múltiples contactos + if (distance < minDistance * 0.95f) // Solo si están muy cerca (interpenetración significativa) + { + botellaA.isBeingPressedByOtherBottles = true; + botellaB.isBeingPressedByOtherBottles = true; + + // Ajustar propiedades del contacto para reducir fuerzas explosivas + float penetrationRatio = Math.Max(0, (minDistance - distance) / minDistance); + + // Reducir restitución progresivamente según la penetración + contact.Restitution = Math.Min(contact.Restitution, 0.1f * (1.0f - penetrationRatio)); + + // Aumentar fricción para disipar energía + contact.Friction = Math.Max(contact.Friction, 0.7f + penetrationRatio * 0.2f); + } + + // Si la velocidad relativa es muy alta, también ajustar contacto + if (relativeSpeed > 2.0f) + { + contact.Restitution = 0.0f; // Sin rebote + contact.Friction = Math.Max(contact.Friction, 0.9f); + } + } + } + + public void ResetBottlePressureFlags() + { + foreach (var cuerpo in Cuerpos) + { + if (cuerpo is simBotella botella) + { + botella.isBeingPressedByOtherBottles = false; + } + } + } + + private void UpdateBottlePressureStatus() + { + var botellas = new List(); + + // Recopilar todas las botellas + foreach (var cuerpo in Cuerpos) + { + if (cuerpo is simBotella botella) + { + botellas.Add(botella); + } + } + + // Verificar el estado de presión de cada botella basado en sus contactos reales + foreach (var botella in botellas) + { + int proximityCount = 0; + float totalPenetration = 0; + + foreach (var otherBottle in botellas) + { + if (botella == otherBottle) continue; + + Vector2 direction = otherBottle.Body.Position - botella.Body.Position; + float distance = direction.Length(); + float minDistance = botella.Radius + otherBottle.Radius + 0.01f; // 1cm margen + + if (distance < minDistance) + { + proximityCount++; + totalPenetration += (minDistance - distance); + } + } + + // Actualizar información de debug + botella.debugPenetrationLevel = totalPenetration; + + // Solo marcar como presionada si tiene múltiples contactos cercanos O penetración significativa + // Y además la botella está en un transportador (para evitar marcar botellas libres) + if ((proximityCount >= 2 || totalPenetration > 0.01f) && botella.IsOnAnyTransport()) + { + botella.isBeingPressedByOtherBottles = true; + } + } + } + + private void PreventBottleOverlapping() + { + var botellas = new List(); + + // Recopilar todas las botellas + foreach (var cuerpo in Cuerpos) + { + if (cuerpo is simBotella botella) + { + botellas.Add(botella); + } + } + + // Verificar cada par de botellas múltiples veces para resolver interpenetraciones complejas + int iterations = 3; // Múltiples iteraciones para resolver conflictos + + for (int iter = 0; iter < iterations; iter++) + { + bool anyCorrection = false; + + for (int i = 0; i < botellas.Count; i++) + { + for (int j = i + 1; j < botellas.Count; j++) + { + var botellaA = botellas[i]; + var botellaB = botellas[j]; + + Vector2 centerA = botellaA.Body.Position; + Vector2 centerB = botellaB.Body.Position; + Vector2 direction = centerB - centerA; + float distance = direction.Length(); + + // Distancia mínima permitida (suma de radios + margen de seguridad) + float minDistance = botellaA.Radius + botellaB.Radius + 0.005f; // 5mm de margen + + // Si están demasiado cerca + if (distance < minDistance && distance > 0.001f) + { + anyCorrection = true; + + // Normalizar la dirección + direction.Normalize(); + + // Calcular la corrección necesaria + float penetration = minDistance - distance; + float correction = penetration * 0.5f; + + // Factor de corrección que decrece con las iteraciones + float correctionFactor = 0.3f / (iter + 1); // Más agresivo en la primera iteración + Vector2 correctionVector = direction * correction * correctionFactor; + + // Aplicar corrección considerando masas (botellas más pesadas se mueven menos) + float massA = botellaA.Body.Mass; + float massB = botellaB.Body.Mass; + float totalMass = massA + massB; + + float ratioA = massB / totalMass; // Botella más liviana se mueve más + float ratioB = massA / totalMass; + + // Solo mover si las botellas no están restringidas por transportadores + if (!botellaA.isRestricted) + { + Vector2 newPosA = centerA - correctionVector * ratioA; + if (!float.IsNaN(newPosA.X) && !float.IsNaN(newPosA.Y)) + { + botellaA.Body.Position = newPosA; + } + } + + if (!botellaB.isRestricted) + { + Vector2 newPosB = centerB + correctionVector * ratioB; + if (!float.IsNaN(newPosB.X) && !float.IsNaN(newPosB.Y)) + { + botellaB.Body.Position = newPosB; + } + } + + // Solo marcar como presionadas si hay interpenetración significativa + if (penetration > 0.005f) // Solo si penetran más de 5mm + { + botellaA.isBeingPressedByOtherBottles = true; + botellaB.isBeingPressedByOtherBottles = true; + } + + // Reducir velocidades para evitar acumulación de energía + float velocityDamping = 0.9f; + if (penetration > 0.01f) // Si la penetración es significativa (> 1cm) + { + velocityDamping = 0.7f; // Reducción más agresiva + } + + botellaA.Body.LinearVelocity *= velocityDamping; + botellaB.Body.LinearVelocity *= velocityDamping; + } + } + } + + // Si no hubo correcciones en esta iteración, podemos parar + if (!anyCorrection) + break; + } + } } } \ No newline at end of file