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