diff --git a/ObjetosSim/Dinamicos/ucBotella.xaml b/ObjetosSim/Dinamicos/ucBotella.xaml index 945e485..c8a25b1 100644 --- a/ObjetosSim/Dinamicos/ucBotella.xaml +++ b/ObjetosSim/Dinamicos/ucBotella.xaml @@ -1,12 +1,19 @@  + + + + + + + Stroke="{Binding ColorButton_oculto}" Fill="Gray" + Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}" StrokeThickness="0.5"/> \ No newline at end of file diff --git a/ObjetosSim/Dinamicos/ucBotella.xaml.cs b/ObjetosSim/Dinamicos/ucBotella.xaml.cs index abd3182..2720ca4 100644 --- a/ObjetosSim/Dinamicos/ucBotella.xaml.cs +++ b/ObjetosSim/Dinamicos/ucBotella.xaml.cs @@ -6,6 +6,7 @@ using CtrEditor.Siemens; using CtrEditor.Simulacion; using CommunityToolkit.Mvvm.ComponentModel; using nkast.Aether.Physics2D.Common; +using System.Windows.Media; namespace CtrEditor.ObjetosSim { @@ -31,6 +32,9 @@ namespace CtrEditor.ObjetosSim set => SetProperty(ref nombre, value); } + [ObservableProperty] + private Brush colorButton_oculto; + [ObservableProperty] private float diametro; @@ -64,7 +68,7 @@ namespace CtrEditor.ObjetosSim { if (SimGeometria != null) { - SimGeometria.SetDiameter(Diametro); + SimGeometria.SetPosition(GetCentro()); } } @@ -73,30 +77,38 @@ namespace CtrEditor.ObjetosSim { Diametro = 0.10f; Mass = 1; + ColorButton_oculto = Brushes.Gray; + } + + public void UpdateAfterMove() + { + ActualizarGeometrias(); + SimGeometria?.SetDiameter(Diametro); } public override void UpdateGeometryStart() { // Se llama antes de la simulacion ActualizarGeometrias(); + SimGeometria?.SetDiameter(Diametro); } - public override void SimulationStop() - { - // Se llama al detener la simulacion. Util para detener Storyboards - } - public override void UpdateGeometryStep() { // Se llama antes de la simulacion ActualizarGeometrias(); } - - public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { } - public override void UpdateControl(int elapsedMilliseconds) { SetCentro(SimGeometria.Center); - + if (SimGeometria.isRestricted) + ColorButton_oculto = Brushes.Yellow; + else + { + if (SimGeometria.isOnTransports > 0) + ColorButton_oculto = Brushes.Red; + else + ColorButton_oculto = Brushes.Gray; + } if (SimGeometria.Descartar) // Ha sido marcada para remover RemoverDesdeSimulacion = true; } @@ -143,6 +155,8 @@ namespace CtrEditor.ObjetosSim { Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels); Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels); + if (Datos is osBotella botella) + botella.UpdateAfterMove(); } } public void Rotate(float Angle) { } diff --git a/ObjetosSim/Estaticos/ucTransporteGuias.xaml.cs b/ObjetosSim/Estaticos/ucTransporteGuias.xaml.cs index 8541018..3906139 100644 --- a/ObjetosSim/Estaticos/ucTransporteGuias.xaml.cs +++ b/ObjetosSim/Estaticos/ucTransporteGuias.xaml.cs @@ -57,6 +57,15 @@ namespace CtrEditor.ObjetosSim ActualizarGeometrias(); } + [ObservableProperty] + public bool esFreno; + + partial void OnEsFrenoChanged(bool value) + { + if (SimGeometria != null) + SimGeometria.isBrake = value; + } + [ObservableProperty] public float angulo; [ObservableProperty] @@ -82,6 +91,7 @@ namespace CtrEditor.ObjetosSim SimGeometria.DistanceGuide2Guide = Alto; SimGeometria.Speed = VelocidadActual; + SimGeometria.isBrake = esFreno; ActualizarAnimacionStoryBoardTransporte(VelocidadActual); } } diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index 7a20424..698e9f5 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -107,25 +107,40 @@ namespace CtrEditor.ObjetosSim [JsonIgnore] protected PLCModel? _plc = null; + /// + /// + /// + /// public virtual void UpdateControl(int elapsedMilliseconds) { } - public virtual void UpdateGeometryStart() - { - // Se llama antes de la simulacion - } + /// + /// Se llama antes de la simulacion + /// + public virtual void UpdateGeometryStart() { } public virtual void SimulationStop() { } public virtual void UpdateGeometryStep() { } + + /// + /// Es llamada en cada Tick del reloj establecido del PLC + /// cuando la conexion con el PLC esta establecida + /// + /// + /// public virtual void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { } + + /// + /// El UserControl ya se ha cargado y podemos obtener las coordenadas para + /// crear el objeto de simulacion + /// public virtual void ucLoaded() { - // El UserControl ya se ha cargado y podemos obtener las coordenadas para - // crear el objeto de simulacion ActualizarLeftTop(); } - public virtual void ucUnLoaded() - { - // El UserControl se esta eliminando - // eliminar el objeto de simulacion - } + + /// + /// El UserControl se esta eliminando + /// eliminar el objeto de simulacion + /// + public virtual void ucUnLoaded() { } [JsonIgnore] public MainViewModel _mainViewModel; diff --git a/Simulacion/Aether.cs b/Simulacion/Aether.cs index f90b8d7..8ad34e0 100644 --- a/Simulacion/Aether.cs +++ b/Simulacion/Aether.cs @@ -13,6 +13,10 @@ using nkast.Aether.Physics2D; using nkast.Aether.Physics2D.Dynamics; using nkast.Aether.Physics2D.Common; using nkast.Aether.Physics2D.Collision.Shapes; +using nkast.Aether.Physics2D.Dynamics.Contacts; +using nkast.Aether.Physics2D.Collision; +using Transform = nkast.Aether.Physics2D.Common.Transform; +using nkast.Aether.Physics2D.Dynamics.Joints; namespace CtrEditor.Simulacion { @@ -23,7 +27,7 @@ namespace CtrEditor.Simulacion public void RemoverBody() { - if (Body != null) + if (Body != null && _world.BodyList.Count>0) { _world.Remove(Body); } @@ -45,10 +49,12 @@ namespace CtrEditor.Simulacion private float _startAngle; private float _endAngle; public float Speed { get; set; } // Velocidad para efectos de cinta transportadora + private List _deferredActions; - public simCurve(World world, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position) + public simCurve(World world, List deferredActions, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position) { _world = world; + _deferredActions = deferredActions; _innerRadius = innerRadius; _outerRadius = outerRadius; _startAngle = Microsoft.Xna.Framework.MathHelper.ToRadians(startAngle); @@ -138,10 +144,12 @@ namespace CtrEditor.Simulacion public class simDescarte : simBase { private float _radius; + private List _deferredActions; - public simDescarte(World world, float diameter, Vector2 position) + public simDescarte(World world, List deferredActions, float diameter, Vector2 position) { _world = world; + _deferredActions = deferredActions; _radius = diameter / 2; Create(position); } @@ -168,11 +176,14 @@ namespace CtrEditor.Simulacion { public float Speed { get; set; } // Velocidad para efectos de cinta transportadora public float DistanceGuide2Guide { get; set; } + public bool isBrake { get; set; } public bool TransportWithGuides = false; + private List _deferredActions; - public simTransporte(World world, float width, float height, Vector2 position, float angle = 0) + public simTransporte(World world, List deferredActions, float width, float height, Vector2 position, float angle = 0) { _world = world; + _deferredActions = deferredActions; Create(width, height, position, angle); } @@ -213,10 +224,12 @@ namespace CtrEditor.Simulacion public class simBarrera : simBase { public bool LuzCortada = false; + private List _deferredActions; - public simBarrera(World world, float width, float height, Vector2 position, float angle = 0) + public simBarrera(World world, List deferredActions, float width, float height, Vector2 position, float angle = 0) { _world = world; + _deferredActions = deferredActions; Create(width, height, position, angle); } @@ -253,9 +266,11 @@ namespace CtrEditor.Simulacion public class simGuia : simBase { - public simGuia(World world, Vector2 start, Vector2 end) + private List _deferredActions; + public simGuia(World world, List deferredActions, Vector2 start, Vector2 end) { _world = world; + _deferredActions = deferredActions; Create(start, end); } @@ -279,11 +294,25 @@ namespace CtrEditor.Simulacion private float _mass; public bool Descartar = false; - public simBotella(World world, float diameter, Vector2 position, float mass) + public int isOnTransports; + public bool isRestricted; + public bool isNoMoreRestricted; + public float OriginalMass; + public simTransporte ConveyorRestrictedTo; + public Fixture axisRestrictedBy; + + public Vector2 Speed; + + PrismaticJoint _activeJoint; + private List _deferredActions; + + public simBotella(World world, List deferredActions, float diameter, Vector2 position, float mass) { _world = world; + _deferredActions = deferredActions; _radius = diameter / 2; _mass = mass; + _activeJoint = null; Create(position); } @@ -318,23 +347,26 @@ namespace CtrEditor.Simulacion private void Create(Vector2 position) { RemoverBody(); - Body = _world.CreateCircle( _radius, 0.2f, position); + isOnTransports = 0; + _activeJoint = null; + Body = _world.CreateCircle( _radius, 1f, position); Body.BodyType = BodyType.Dynamic; // Restablecer manejador de eventos de colisión Body.OnCollision += HandleCollision; - //Body.OnSeparation += HandleOnSeparation; + Body.OnSeparation += HandleOnSeparation; Body.Tag = this; // Importante para la identificación durante la colisión // Configurar la fricción - Body.SetFriction(0.3f); + Body.SetFriction(0.5f); // Configurar amortiguamiento - Body.LinearDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad lineal - Body.AngularDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad angular - Body.SetRestitution(0.2f); // Baja restitución para menos rebote + Body.LinearDamping = 4f; // Ajustar para controlar la reducción de la velocidad lineal + Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular + Body.SetRestitution(0.3f); // Baja restitución para menos rebote + Body.SleepingAllowed = false; Body.IsBullet = true; } @@ -349,7 +381,7 @@ namespace CtrEditor.Simulacion Mass = mass; } - private bool HandleCollision(Fixture fixtureA, Fixture fixtureB, nkast.Aether.Physics2D.Dynamics.Contacts.Contact contact) + private bool HandleCollision(Fixture fixtureA, Fixture fixtureB, Contact contact) { if (fixtureB.Body.Tag is simBarrera Sensor) { @@ -370,60 +402,76 @@ namespace CtrEditor.Simulacion { simTransporte conveyor = fixtureB.Body.Tag as simTransporte; + isOnTransports += 1; + if (conveyor.Speed != 0) { - - CircleShape circleShape = fixtureA.Shape as CircleShape; - PolygonShape polygonShape = fixtureB.Shape as PolygonShape; - - // Obtener centro y radio del círculo - Vector2 centroCirculo = fixtureA.Body.Position; - float radio = circleShape.Radius; - - // Obtener los vértices del polígono (rectángulo) - Vector2[] vertices = new Vector2[polygonShape.Vertices.Count]; - float cos = (float)Math.Cos(fixtureB.Body.Rotation); - float sin = (float)Math.Sin(fixtureB.Body.Rotation); - - for (int i = 0; i < polygonShape.Vertices.Count; i++) + // Aplicar el efecto del transportador usando el porcentaje calculado + if (conveyor.TransportWithGuides && conveyor.isBrake) { - Vector2 vertex = polygonShape.Vertices[i]; - float rotatedX = vertex.X * cos - vertex.Y * sin + fixtureB.Body.Position.X; - float rotatedY = vertex.X * sin + vertex.Y * cos + fixtureB.Body.Position.Y; - vertices[i] = new Vector2(rotatedX, rotatedY); + ConveyorRestrictedTo = conveyor; + axisRestrictedBy = fixtureB; + OriginalMass = Body.Mass; + isRestricted = true; + isNoMoreRestricted = false; } - // Calcular el porcentaje de la superficie compartida - float porcentajeCompartido = InterseccionCirculoRectanguloAether.CalcularSuperficieCompartida(vertices, centroCirculo, radio); - - // Aplicar el efecto del transportador usando el porcentaje calculado - //if (conveyor.TransportWithGuides) - // if (conveyor.DistanceGuide2Guide <= radio * 2) - // CenterFixtureOnConveyor(fixtureA, conveyor); - ApplyConveyorEffect(conveyor, fixtureA, porcentajeCompartido); - + ApplyConveyorEffect(conveyor, fixtureA, 1); } return true; // No aplicar respuestas físicas } return true; // No aplicar respuestas físicas } + public static bool IntersectAABBs(ref AABB aabbA, ref AABB aabbB, out AABB overlap) + { + overlap = new AABB(); + + float minX = Math.Max(aabbA.LowerBound.X, aabbB.LowerBound.X); + float minY = Math.Max(aabbA.LowerBound.Y, aabbB.LowerBound.Y); + float maxX = Math.Min(aabbA.UpperBound.X, aabbB.UpperBound.X); + float maxY = Math.Min(aabbA.UpperBound.Y, aabbB.UpperBound.Y); + + if (minX < maxX && minY < maxY) + { + overlap.LowerBound = new Vector2(minX, minY); + overlap.UpperBound = new Vector2(maxX, maxY); + return true; + } + return false; + } + + private void HandleOnSeparation(Fixture sender, Fixture fixtureB, Contact contact) + { + if (isOnTransports>0 && fixtureB.Body.Tag is simTransporte) + isOnTransports -= 1; + if (isRestricted && fixtureB == axisRestrictedBy) + { + isRestricted = false; + isNoMoreRestricted = true; + } + } + private void ApplyConveyorEffect(simTransporte conveyor, Fixture circleFixture, float porcentajeCompartido) { float speedMetersPerSecond = conveyor.Speed / 60.0f; Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond; - circleFixture.Body.LinearVelocity += desiredVelocity * porcentajeCompartido; + // circleFixture.Body.ApplyForce(desiredVelocity * porcentajeCompartido); + Speed = desiredVelocity; } - private void CenterFixtureOnConveyor(Fixture fixtureA, simTransporte conveyor) + public void CenterFixtureOnConveyor() { + if (!isRestricted || ConveyorRestrictedTo == null) + return; + // Obtener el centro del conveyor - Vector2 conveyorCenter = conveyor.Body.Position; + Vector2 conveyorCenter = ConveyorRestrictedTo.Body.Position; // Calcular el vector de la línea horizontal centrada de conveyor - float halfDistance = conveyor.DistanceGuide2Guide / 2; - float cos = (float)Math.Cos(conveyor.Body.Rotation); - float sin = (float)Math.Sin(conveyor.Body.Rotation); + float halfDistance = ConveyorRestrictedTo.DistanceGuide2Guide / 2; + float cos = (float)Math.Cos(ConveyorRestrictedTo.Body.Rotation); + float sin = (float)Math.Sin(ConveyorRestrictedTo.Body.Rotation); Vector2 offset = new Vector2(halfDistance * cos, halfDistance * sin); @@ -432,11 +480,11 @@ namespace CtrEditor.Simulacion Vector2 lineEnd = conveyorCenter + offset; // Proyectar el centro de fixtureA sobre la línea horizontal - Vector2 fixtureCenter = fixtureA.Body.Position; + Vector2 fixtureCenter = Body.Position; Vector2 closestPoint = ProjectPointOntoLine(fixtureCenter, lineStart, lineEnd); // Mover fixtureA al punto más cercano en la línea horizontal - fixtureA.Body.Position = closestPoint; + Body.Position = closestPoint; } private Vector2 ProjectPointOntoLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd) @@ -456,6 +504,7 @@ namespace CtrEditor.Simulacion private World world; private Canvas simulationCanvas; public List Cuerpos; + public List _deferredActions; private Stopwatch stopwatch; private double stopwatch_last; @@ -466,6 +515,7 @@ namespace CtrEditor.Simulacion { world = new World(new Vector2(0, 0)); // Vector2.Zero Cuerpos = new List(); + _deferredActions = new List(); stopwatch = new Stopwatch(); stopwatch.Start(); } @@ -477,6 +527,8 @@ namespace CtrEditor.Simulacion if (Cuerpos.Count > 0) Cuerpos.Clear(); } + // ****************************************************************************************************************************************** + // ****************************************************************************************************************************************** public void Step() { @@ -486,6 +538,33 @@ namespace CtrEditor.Simulacion // Pasar el tiempo transcurrido al método Step world.Step(elapsedMilliseconds / 1000.0f); + + + foreach (var cuerpo in Cuerpos) + { + if (cuerpo is simBotella botella) + { + if (botella.isOnTransports > 0) + botella.Body.LinearVelocity = botella.Speed; + if (botella.isRestricted) + { + botella.CenterFixtureOnConveyor(); + botella.Body.Inertia = 0; + botella.Body.Mass = 20; + } else if (botella.isNoMoreRestricted) + { + botella.isNoMoreRestricted = false; + botella.Body.Mass = botella.OriginalMass; + } + } + } + + // Procesa las acciones diferidas + foreach (var action in _deferredActions) + { + action(); + } + _deferredActions.Clear(); } public void Remove(simBase Objeto) @@ -499,42 +578,42 @@ namespace CtrEditor.Simulacion public simCurve AddCurve(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position) { - simCurve curva = new simCurve(world, innerRadius, outerRadius, startAngle, endAngle, position); + simCurve curva = new simCurve(world, _deferredActions, innerRadius, outerRadius, startAngle, endAngle, position); Cuerpos.Add(curva); return curva; } public simBotella AddCircle(float diameter, Vector2 position, float mass) { - simBotella circle = new simBotella(world, diameter, position, mass); + simBotella circle = new simBotella(world, _deferredActions, diameter, position, mass); Cuerpos.Add(circle); return circle; } public simTransporte AddRectangle(float width, float height, Vector2 position, float angle) { - simTransporte rectangle = new simTransporte(world, width, height, position, angle); + simTransporte rectangle = new simTransporte(world, _deferredActions, width, height, position, angle); Cuerpos.Add(rectangle); return rectangle; } public simBarrera AddBarrera(float width, float height, Vector2 position, float angle) { - simBarrera rectangle = new simBarrera(world, width, height, position, angle); + simBarrera rectangle = new simBarrera(world, _deferredActions, width, height, position, angle); Cuerpos.Add(rectangle); return rectangle; } public simGuia AddLine(Vector2 start, Vector2 end) { - simGuia line = new simGuia(world, start, end); + simGuia line = new simGuia(world, _deferredActions, start, end); Cuerpos.Add(line); return line; } public simDescarte AddDescarte(float diameter, Vector2 position) { - simDescarte descarte = new simDescarte(world, diameter, position); + simDescarte descarte = new simDescarte(world, _deferredActions, diameter, position); Cuerpos.Add(descarte); return descarte; } diff --git a/Simulacion/InterseccionCirculoRectangulo.cs b/Simulacion/InterseccionCirculoRectangulo.cs index 2925ff6..f8c3667 100644 --- a/Simulacion/InterseccionCirculoRectangulo.cs +++ b/Simulacion/InterseccionCirculoRectangulo.cs @@ -68,67 +68,6 @@ namespace CtrEditor.Simulacion } } - - internal class InterseccionCirculoRectanguloAether - { - // Definición de la función CalcularSuperficieCompartida - public static float CalcularSuperficieCompartida(nkast.Aether.Physics2D.Common.Vector2[] vertices, nkast.Aether.Physics2D.Common.Vector2 center, float r) - { - float totalCircleArea = (float)Math.PI * r * r; - - // Distancia a líneas ajustado - float[] distances = new float[4]; - for (int i = 0; i < 4; i++) - { - distances[i] = DistanceFromLine(center, vertices[i], vertices[(i + 1) % 4]); - } - - float minDistance = float.MaxValue; - foreach (var dist in distances) - { - if (Math.Abs(dist) < Math.Abs(minDistance)) - minDistance = dist; - } - float d = Math.Abs(minDistance); - - float sharedArea = 0; - if (Array.TrueForAll(distances, dist => Math.Abs(dist) > r)) - { - sharedArea = totalCircleArea; - } - else if (d < r) - { - float cosTheta = Math.Min(1, d / r); - float sinTheta = (float)Math.Sqrt(Math.Max(0, r * r - d * d)); - if (minDistance < 0) // El centro está dentro del rectángulo - { - float areaOutside = r * r * (float)Math.Acos(cosTheta) - d * sinTheta; - sharedArea = totalCircleArea - areaOutside; - } - else // El centro está fuera del rectángulo - { - sharedArea = r * r * (float)Math.Acos(cosTheta) - d * sinTheta; - } - } - else - { - sharedArea = 0; - } - - return sharedArea / totalCircleArea; - } - - public static float DistanceFromLine(nkast.Aether.Physics2D.Common.Vector2 point, nkast.Aether.Physics2D.Common.Vector2 start, nkast.Aether.Physics2D.Common.Vector2 end) - { - float A = end.Y - start.Y; - float B = start.X - end.X; - float C = end.X * start.Y - start.X * end.Y; - float distance = (A * point.X + B * point.Y + C) / (float)Math.Sqrt(A * A + B * B); - return distance; - } - - } - }