diff --git a/MainViewModel.cs b/MainViewModel.cs index 9c37cb6..02dd284 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -945,6 +945,33 @@ namespace CtrEditor plcSampleCount = 0; } } + + // Reemplazar la propiedad _multiPropertyEditorWindow por un diccionario + private Dictionary _propertyEditorWindows = new(); + + public void ShowMultiPropertyEditor(IEnumerable selectedObjects) + { + var objectsList = selectedObjects.ToList(); + // Crear una clave única basada en los IDs de los objetos seleccionados + string key = string.Join("_", objectsList.Select(o => o.Id.Value).OrderBy(id => id)); + + // Verificar si ya existe una ventana para esta selección + if (_propertyEditorWindows.TryGetValue(key, out var existingWindow) && + existingWindow.IsVisible) + { + existingWindow.Activate(); + return; + } + + // Crear nueva ventana + var window = new Windows.MultiPropertyEditorWindow(objectsList, MainWindow); + window.Closed += (s, e) => _propertyEditorWindows.Remove(key); + _propertyEditorWindows[key] = window; + + window.Show(); + HasUnsavedChanges = true; + _objectManager.UpdateSelectionVisuals(); + } } public class SimulationData { diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 34445d7..c1b70f5 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -610,6 +610,16 @@ namespace CtrEditor contextMenu.Items.Add(alignSubmenu); } + // Add Edit Properties option when objects are selected + if (_objectManager.SelectedObjects.Count > 0) + { + var editPropertiesItem = new MenuItem { Header = "Edit Properties" }; + editPropertiesItem.Click += (s, e) => viewModel.ShowMultiPropertyEditor(_objectManager.SelectedObjects); + contextMenu.Items.Add(editPropertiesItem); + + contextMenu.Items.Add(new Separator()); + } + contextMenu.IsOpen = true; contextMenu.PlacementTarget = ImagenEnTrabajoCanvas; contextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint; diff --git a/Simulacion/Aether.cs b/Simulacion/Aether.cs index 70ec2c8..8a08e6f 100644 --- a/Simulacion/Aether.cs +++ b/Simulacion/Aether.cs @@ -1,7 +1,6 @@ using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; - using System.Windows; using System.Diagnostics; using nkast.Aether.Physics2D.Dynamics; @@ -9,22 +8,24 @@ using nkast.Aether.Physics2D.Common; using nkast.Aether.Physics2D.Collision.Shapes; using nkast.Aether.Physics2D.Dynamics.Contacts; using nkast.Aether.Physics2D.Dynamics.Joints; - +using System; +using System.Collections.Generic; namespace CtrEditor.Simulacion { public class simBase { public Body Body { get; protected set; } - public World _world; + public World _world; public void RemoverBody() { - if (Body != null && _world.BodyList.Count>0 && _world.BodyList.Contains(Body)) + if (Body != null && _world.BodyList.Count > 0 && _world.BodyList.Contains(Body)) { _world.Remove(Body); } } + public static float Min(float Value, float Min = 0.01f) { return Value < Min ? Min : Value; @@ -34,21 +35,26 @@ namespace CtrEditor.Simulacion { return (float)(grados * (Math.PI / 180)); } + public static float RadianesAGrados(float radianes) { return (float)(radianes * (180 / Math.PI)); } + public void SetPosition(float x, float y) { Body.SetTransform(new Vector2(x, y), Body.Rotation); } + public void SetPosition(Vector2 centro) { try { Body.SetTransform(centro, Body.Rotation); } - catch { } + catch + { + } } } @@ -59,8 +65,8 @@ namespace CtrEditor.Simulacion private float _startAngle; private float _endAngle; public float Speed { get; set; } // Velocidad para efectos de cinta transportadora - private List _deferredActions; + private List _deferredActions; public simCurve(World world, List deferredActions, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position) { _world = world; @@ -74,7 +80,8 @@ namespace CtrEditor.Simulacion public void Create(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position) { - if (_world == null) return; + if (_world == null) + return; _innerRadius = innerRadius; _outerRadius = outerRadius; _startAngle = GradosARadianes(startAngle); @@ -85,16 +92,16 @@ namespace CtrEditor.Simulacion public void Create(Vector2 position) { RemoverBody(); - - // Crear la geometría del sensor de curva - List segments = CreateCurveVertices(_innerRadius, _outerRadius, _startAngle, _endAngle); + // Crear triangulación de la curva + List triangles = CreateTriangulatedCurve(_innerRadius, _outerRadius, _startAngle, _endAngle); Body = _world.CreateBody(); - foreach (var segment in segments) + foreach (var triangle in triangles) { - var shape = new PolygonShape(segment, 1f); + var shape = new PolygonShape(triangle, 1f); var fixture = Body.CreateFixture(shape); fixture.IsSensor = true; - } + } + Body.Position = position; Body.BodyType = BodyType.Static; Body.Tag = this; @@ -110,10 +117,8 @@ namespace CtrEditor.Simulacion List verticesList = new List(); int segments = 32; float angleStep = (endAngle - startAngle) / segments; - Vertices innerVertices = new Vertices(); Vertices outerVertices = new Vertices(); - for (int i = 0; i <= segments; i++) { float angle = startAngle + i * angleStep; @@ -124,7 +129,6 @@ namespace CtrEditor.Simulacion outerVertices.Reverse(); innerVertices.AddRange(outerVertices); verticesList.Add(innerVertices); - return verticesList; } @@ -132,36 +136,27 @@ namespace CtrEditor.Simulacion { // Get bottle position relative to curve center 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 angle to be positive if (bottleAngle < 0) bottleAngle += 2 * (float)Math.PI; - // If bottle is outside the angle range, return 0 if (bottleAngle < _startAngle || bottleAngle > _endAngle) return 0; - // Calculate distance from center float distanceToCenter = centerToBottle.Length(); - // If bottle is outside radius range, return 0 if (distanceToCenter < _innerRadius || distanceToCenter > _outerRadius) return 0; - // Calculate how far the bottle is from the edges float angleFromStart = bottleAngle - _startAngle; float angleToEnd = _endAngle - bottleAngle; - // Use the minimum distance to either edge to calculate overlap float minAngleDistance = Math.Min(angleFromStart, angleToEnd); float totalAngle = _endAngle - _startAngle; - // Calculate overlap percentage based on angle proximity to edges float overlapPercentage = Math.Min(minAngleDistance / (totalAngle * 0.1f), 1.0f); - return overlapPercentage; } @@ -169,59 +164,91 @@ namespace CtrEditor.Simulacion { Vector2 centerToBottle = bottle.Body.Position - Body.Position; float distanceToCenter = centerToBottle.Length(); - if (distanceToCenter >= _innerRadius && distanceToCenter <= _outerRadius) { // Calculate overlap percentage float overlapPercentage = CalculateAngleOverlap(bottle); - // Only apply effect if there's overlap if (overlapPercentage > 0) { // Calculate the tangential velocity float speedMetersPerSecond = Speed / 60.0f; - // Vector tangent (perpendicular to radius) Vector2 tangent = new Vector2(-centerToBottle.Y, centerToBottle.X); tangent.Normalize(); - // Adjust tangent direction based on speed sign if (speedMetersPerSecond < 0) tangent = -tangent; - // Current velocity magnitude float currentSpeed = bottle.Body.LinearVelocity.Length(); - // Desired conveyor speed float conveyorSpeed = Math.Abs(speedMetersPerSecond); - // Use the larger of the two speeds as base speed float targetSpeed = Math.Max(currentSpeed, conveyorSpeed); - // Lerp between current direction and curve direction Vector2 currentDir = bottle.Body.LinearVelocity; if (currentDir.LengthSquared() > 0) currentDir.Normalize(); else currentDir = tangent; - // Interpolate between current direction and curve direction Vector2 newDirection = currentDir * (1 - overlapPercentage) + tangent * overlapPercentage; newDirection.Normalize(); - // Apply new velocity with combined speed bottle.Body.LinearVelocity = newDirection * targetSpeed; } } } - } + private const float SegmentationFactor = 32f / 3f; + private const int MinSegments = 8; + private const int MaxSegments = 64; + private List CreateTriangulatedCurve(float innerRadius, float outerRadius, float startAngle, float endAngle) + { + List triangles = new List(); + float arcLength = (endAngle - startAngle) * ((innerRadius + outerRadius) / 2f); + // Calcular número de segmentos basado en el tamaño del arco + int segments = (int)(arcLength * SegmentationFactor); + // Aplicar límites para asegurar calidad y eficiencia + segments = Math.Max(MinSegments, Math.Min(segments, MaxSegments)); + float angleStep = (endAngle - startAngle) / segments; + // Generar vértices para los arcos interior y exterior + Vector2[] innerPoints = new Vector2[segments + 1]; + Vector2[] outerPoints = new Vector2[segments + 1]; + for (int i = 0; i <= segments; i++) + { + float angle = startAngle + i * angleStep; + float cosAngle = (float)Math.Cos(angle); + float sinAngle = (float)Math.Sin(angle); + innerPoints[i] = new Vector2(innerRadius * cosAngle, innerRadius * sinAngle); + outerPoints[i] = new Vector2(outerRadius * cosAngle, outerRadius * sinAngle); + } + + // Crear triángulos + for (int i = 0; i < segments; i++) + { + // Primer triángulo (superior) del sector + Vertices upperTriangle = new Vertices(3); + upperTriangle.Add(innerPoints[i]); + upperTriangle.Add(outerPoints[i]); + upperTriangle.Add(outerPoints[i + 1]); + triangles.Add(upperTriangle); + // Segundo triángulo (inferior) del sector + Vertices lowerTriangle = new Vertices(3); + lowerTriangle.Add(innerPoints[i]); + lowerTriangle.Add(outerPoints[i + 1]); + lowerTriangle.Add(innerPoints[i + 1]); + triangles.Add(lowerTriangle); + } + + return triangles; + } + } public class simDescarte : simBase { private float _radius; private List _deferredActions; - public simDescarte(World world, List deferredActions, float diameter, Vector2 position) { _world = world; @@ -235,27 +262,26 @@ namespace CtrEditor.Simulacion if (diameter <= 0) diameter = 0.01f; _radius = diameter / 2; - Create(Body.Position); // Recrear el círculo con el nuevo tamaño + Create(Body.Position); // Recrear el círculo con el nuevo tamaño } public void Create(Vector2 position) { RemoverBody(); Body = _world.CreateCircle(_radius, 1f, position); - Body.FixtureList[0].IsSensor = true; Body.BodyType = BodyType.Static; Body.Tag = this; // Importante para la identificación durante la colisión } } - public class simTransporte : simBase { public float Speed { get; set; } // Velocidad para efectos de cinta transportadora public float Friction { get; set; } // Friccion para efectos de cinta transportadora public float DistanceGuide2Guide { get; set; } public bool isBrake { get; set; } + public bool TransportWithGuides = false; private List _deferredActions; public float Width { get; set; } @@ -270,8 +296,15 @@ namespace CtrEditor.Simulacion public float Angle { - get { return RadianesAGrados(Body.Rotation); } - set { Body.Rotation = GradosARadianes(value); } + get + { + return RadianesAGrados(Body.Rotation); + } + + set + { + Body.Rotation = GradosARadianes(value); + } } public new void SetPosition(float x, float y) @@ -287,19 +320,19 @@ namespace CtrEditor.Simulacion public void SetDimensions(float width, float height) { Body.Remove(Body.FixtureList[0]); - var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f); Body.CreateFixture(newShape); Width = width; Height = height; } + public void Create(float width, float height, Vector2 position, float angle = 0) { RemoverBody(); Width = width; Height = height; Friction = 0.1f; - Body = _world.CreateRectangle( width, height, 1f, position); + Body = _world.CreateRectangle(width, height, 1f, position); Body.FixtureList[0].IsSensor = true; Body.BodyType = BodyType.Static; Body.Rotation = GradosARadianes(angle); @@ -315,9 +348,7 @@ namespace CtrEditor.Simulacion public bool DetectNeck; public List ListSimBotellaContact; float _height; - private List _deferredActions; - public simBarrera(World world, List deferredActions, float width, float height, Vector2 position, float angle = 0, bool detectectNeck = false) { _world = world; @@ -330,8 +361,15 @@ namespace CtrEditor.Simulacion public float Angle { - get { return RadianesAGrados(Body.Rotation); } - set { Body.Rotation = GradosARadianes(value); } + get + { + return RadianesAGrados(Body.Rotation); + } + + set + { + Body.Rotation = GradosARadianes(value); + } } public new void SetPosition(float x, float y) @@ -342,7 +380,6 @@ namespace CtrEditor.Simulacion public void SetDimensions(float width, float height) { Body.Remove(Body.FixtureList[0]); - var newShape = new PolygonShape(PolygonTools.CreateRectangle(Min(width) / 2, Min(height) / 2), 1f); Body.CreateFixture(newShape); } @@ -352,7 +389,7 @@ namespace CtrEditor.Simulacion RemoverBody(); _height = Min(height); DetectNeck = detectectNeck; - Body = _world.CreateRectangle( Min(width), _height, 1f, position); + Body = _world.CreateRectangle(Min(width), _height, 1f, position); Body.FixtureList[0].IsSensor = true; Body.BodyType = BodyType.Static; Body.Rotation = GradosARadianes(angle); @@ -362,29 +399,25 @@ namespace CtrEditor.Simulacion public void CheckIfNecksIsTouching() { - if (LuzCortada == 0) return; - + if (LuzCortada == 0) + return; foreach (var botella in ListSimBotellaContact) { // Obtener el centro de la barrera Vector2 sensorCenter = Body.Position; - // Calcular el vector de la línea horizontal centrada de la barrera float halfHeight = _height / 2; float cos = (float)Math.Cos(Body.Rotation); float sin = (float)Math.Sin(Body.Rotation); - // Calcular los puntos inicial y final de la línea horizontal centrada y rotada Vector2 lineStart = sensorCenter + new Vector2(-halfHeight * cos, halfHeight * sin); Vector2 lineEnd = sensorCenter + new Vector2(halfHeight * cos, -halfHeight * sin); - // Proyectar el centro de la botella sobre la línea horizontal Vector2 fixtureCenter = botella.Body.Position; Vector2 closestPoint = ProjectPointOntoLine(fixtureCenter, lineStart, lineEnd); - // Calcular la distancia entre el punto más cercano y el centro del cuello de la botella float distance; - Vector2.Distance(ref closestPoint,ref fixtureCenter, out distance); + Vector2.Distance(ref closestPoint, ref fixtureCenter, out distance); Distancia = distance; if (distance <= botella._neckRadius) { @@ -392,6 +425,7 @@ namespace CtrEditor.Simulacion return; } } + LuzCortadaNeck = false; } @@ -399,16 +433,11 @@ namespace CtrEditor.Simulacion { Vector2 lineDirection = lineEnd - lineStart; lineDirection.Normalize(); - Vector2 pointToLineStart = point - lineStart; float projectionLength; Vector2.Dot(ref pointToLineStart, ref lineDirection, out projectionLength); - return lineStart + projectionLength * lineDirection; } - - - } public class simGuia : simBase @@ -424,7 +453,7 @@ namespace CtrEditor.Simulacion public void Create(Vector2 start, Vector2 end) { RemoverBody(); - Body = _world.CreateEdge( start, end); + Body = _world.CreateEdge(start, end); Body.BodyType = BodyType.Static; Body.Tag = this; // Importante para la identificación durante la colisión } @@ -440,7 +469,6 @@ namespace CtrEditor.Simulacion public float Radius; private float _mass; public bool Descartar = false; - public int isOnTransports; public List ListOnTransports; public bool isRestricted; @@ -448,15 +476,11 @@ namespace CtrEditor.Simulacion public float OriginalMass; public simTransporte ConveyorRestrictedTo; public Fixture axisRestrictedBy; - public float OverlapPercentage; - public float _neckRadius; - PrismaticJoint _activeJoint; private List _deferredActions; - - public simBotella(World world, List deferredActions, float diameter, Vector2 position, float mass,float neckRadius = 0) + public simBotella(World world, List deferredActions, float diameter, Vector2 position, float mass, float neckRadius = 0) { _world = world; ListOnTransports = new List(); @@ -464,30 +488,42 @@ namespace CtrEditor.Simulacion Radius = diameter / 2; _mass = mass; _activeJoint = null; - - if (neckRadius<=0) + if (neckRadius <= 0) neckRadius = diameter / 4; - _neckRadius = neckRadius; - Create(position); } public float CenterX { - get { return Body.Position.X; } - set { } + get + { + return Body.Position.X; + } + + set + { + } } public float CenterY { - get { return Body.Position.Y; } - set { } + get + { + return Body.Position.Y; + } + + set + { + } } public Vector2 Center { - get { return Body.Position; } + get + { + return Body.Position; + } } public float Mass @@ -498,7 +534,11 @@ namespace CtrEditor.Simulacion _mass = 1; return _mass; } - set { _mass = value; } + + set + { + _mass = value; + } } private void Create(Vector2 position) @@ -506,24 +546,18 @@ namespace CtrEditor.Simulacion RemoverBody(); isOnTransports = 0; _activeJoint = null; - - Body = _world.CreateCircle( Radius, 1f, position); + Body = _world.CreateCircle(Radius, 1f, position); Body.BodyType = BodyType.Dynamic; - // Restablecer manejador de eventos de colisión Body.OnCollision += HandleCollision; Body.OnSeparation += HandleOnSeparation; - Body.Tag = this; // Importante para la identificación durante la colisión - // Configurar la fricción Body.SetFriction(0.2f); - // Configurar amortiguamiento - Body.LinearDamping = 3f; // Ajustar para controlar la reducción de la velocidad lineal - Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular + Body.LinearDamping = 3f; // 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(0f); // Baja restitución para menos rebote - Body.SleepingAllowed = false; Body.IsBullet = true; } @@ -531,7 +565,7 @@ namespace CtrEditor.Simulacion public void SetDiameter(float diameter) { Radius = diameter / 2; - Create(Body.Position); // Recrear el círculo con el nuevo tamaño + Create(Body.Position); // Recrear el círculo con el nuevo tamaño } public void SetMass(float mass) @@ -561,10 +595,8 @@ namespace CtrEditor.Simulacion else if (fixtureB.Body.Tag is simTransporte) { simTransporte conveyor = fixtureB.Body.Tag as simTransporte; - isOnTransports += 1; ListOnTransports.Add(conveyor); - // Aplicar el efecto del transportador usando el porcentaje calculado if (conveyor.TransportWithGuides && conveyor.isBrake) { @@ -574,8 +606,10 @@ namespace CtrEditor.Simulacion isRestricted = true; isNoMoreRestricted = false; } + return true; // No aplicar respuestas físicas } + return true; // No aplicar respuestas físicas } @@ -583,16 +617,13 @@ namespace CtrEditor.Simulacion { CircleShape circleShape = Body.FixtureList[0].Shape as CircleShape; PolygonShape polygonShape = conveyor.Body.FixtureList[0].Shape as PolygonShape; - // Obtener centro y radio del círculo Vector2 centroCirculo = 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(conveyor.Body.Rotation); float sin = (float)Math.Sin(conveyor.Body.Rotation); - for (int i = 0; i < polygonShape.Vertices.Count; i++) { Vector2 vertex = polygonShape.Vertices[i]; @@ -613,36 +644,40 @@ namespace CtrEditor.Simulacion ListOnTransports.Remove(transport); isOnTransports -= 1; } + if (isOnTransports > 0 && fixtureB.Body.Tag is simCurve) { if (fixtureB.Body.Tag is simCurve transport) ListOnTransports.Remove(transport); isOnTransports -= 1; } + if (isRestricted && fixtureB == axisRestrictedBy) { isRestricted = false; isNoMoreRestricted = true; } + if (fixtureB.Body.Tag is simBarrera Sensor) { Sensor.LuzCortada -= 1; Sensor.ListSimBotellaContact.Remove(this); } } + /// /// Aplica la fuerza de traccion a la botellas segun los /// transportes sobre los que se encuentra /// - /// + /// public void ApplyConveyorSpeed(float deltaTime_s) { foreach (var transporte in ListOnTransports) { if (transporte is simTransporte conveyorRect) ApplyConveyorEffect(deltaTime_s, conveyorRect); - // if (ApplyConveyorEffect(deltaTime_s, conveyorRect)) - // break; + // if (ApplyConveyorEffect(deltaTime_s, conveyorRect)) + // break; if (transporte is simCurve conveyorCurve) conveyorCurve.ApplyCurveEffect(Body.FixtureList[0]); } @@ -651,39 +686,34 @@ namespace CtrEditor.Simulacion private bool ApplyConveyorEffect(float deltaTime_s, simTransporte conveyor) { // Calcular el porcentaje de superficie sobrepuesta - float overlapPercentage = CalculateOverlapedArea(this, conveyor); // CalculateOverlapPercentage(conveyor); + float overlapPercentage = CalculateOverlapedArea(this, conveyor); // CalculateOverlapPercentage(conveyor); OverlapPercentage = overlapPercentage; - // Calcular la velocidad deseada del transporte float speedMetersPerSecond = conveyor.Speed / 60.0f; Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond; - // Obtener la velocidad actual de la botella Vector2 currentVelocity = Body.LinearVelocity; - // Calcular la diferencia de velocidad deseada Vector2 velocityDifference = desiredVelocity - currentVelocity; - // Calcular la fuerza de fricción necesaria - 0 : ya esta en velocidad - 1 : esta detenido - float proporcionalVelocityNeeded = 1-CalculateProportion(currentVelocity, desiredVelocity); + float proporcionalVelocityNeeded = 1 - CalculateProportion(currentVelocity, desiredVelocity); float frictionCoefficient; - - switch (proporcionalVelocityNeeded) { + switch (proporcionalVelocityNeeded) + { case > 0.3f: frictionCoefficient = conveyor.Friction * overlapPercentage; break; - default: frictionCoefficient = proporcionalVelocityNeeded * overlapPercentage; break; } - if (isRestricted && conveyor == ConveyorRestrictedTo && overlapPercentage > 0.5f) { Body.LinearVelocity = desiredVelocity; return true; } + // Aplicar la fuerza de fricción en función del porcentaje de superficie sobrepuesta Body.LinearVelocity += frictionCoefficient * desiredVelocity; //Body.ApplyForce(frictionForce * overlapPercentage); @@ -705,7 +735,6 @@ namespace CtrEditor.Simulacion float dotProduct; Vector2.Dot(ref desiredVelocity, ref currentVelocity, out dotProduct); float magnitudeV1Squared = desiredVelocity.LengthSquared(); - // Si la magnitud de v1 es 0, la proporción no está definida if (magnitudeV1Squared == 0) { @@ -721,25 +750,19 @@ namespace CtrEditor.Simulacion { if (!isRestricted || ConveyorRestrictedTo == null) return; - // Obtener el centro del conveyor Vector2 conveyorCenter = ConveyorRestrictedTo.Body.Position; - // Calcular el vector de la línea horizontal centrada de conveyor 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); - // Línea horizontal centrada de conveyor en el espacio del mundo Vector2 lineStart = conveyorCenter - offset; Vector2 lineEnd = conveyorCenter + offset; - // Proyectar el centro de fixtureA sobre la línea horizontal Vector2 fixtureCenter = Body.Position; Vector2 closestPoint = ProjectPointOntoLine(fixtureCenter, lineStart, lineEnd); - // Mover fixtureA al punto más cercano en la línea horizontal Body.Position = closestPoint; } @@ -748,10 +771,8 @@ namespace CtrEditor.Simulacion { Vector2 lineDirection = lineEnd - lineStart; lineDirection.Normalize(); - Vector2 pointToLineStart = point - lineStart; - Vector2.Dot(ref pointToLineStart,ref lineDirection, out float projectionLength); - + Vector2.Dot(ref pointToLineStart, ref lineDirection, out float projectionLength); return lineStart + projectionLength * lineDirection; } @@ -768,20 +789,16 @@ namespace CtrEditor.Simulacion public List Cuerpos; public List _deferredActions; SolverIterations Iterations; - private Stopwatch stopwatch; private double stopwatch_last; - public Canvas DebugCanvas { get => simulationCanvas; set => simulationCanvas = value; } public SimulationManagerFP() { - world = new World(new Vector2(0, 0)); // Vector2.Zero - + world = new World(new Vector2(0, 0)); // Vector2.Zero SolverIterations Iterations = new SolverIterations(); Iterations.PositionIterations = 1; Iterations.VelocityIterations = 1; - Cuerpos = new List(); _deferredActions = new List(); stopwatch = new Stopwatch(); @@ -795,9 +812,9 @@ namespace CtrEditor.Simulacion if (Cuerpos.Count > 0) Cuerpos.Clear(); } - // ****************************************************************************************************************************************** - // ****************************************************************************************************************************************** + // ****************************************************************************************************************************************** + // ****************************************************************************************************************************************** public void Start() { stopwatch.Start(); @@ -809,16 +826,13 @@ namespace CtrEditor.Simulacion // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos float elapsedMilliseconds = (float)(stopwatch.Elapsed.TotalMilliseconds - stopwatch_last); stopwatch_last = stopwatch.Elapsed.TotalMilliseconds; - // Pasar el tiempo transcurrido al método Step world.Step(elapsedMilliseconds / 1000.0f); - foreach (var cuerpo in Cuerpos) { if (cuerpo is simBotella botella) { - botella.ApplyConveyorSpeed(elapsedMilliseconds/1000); - + botella.ApplyConveyorSpeed(elapsedMilliseconds / 1000); if (botella.isRestricted) { botella.CenterFixtureOnConveyor(); @@ -840,6 +854,7 @@ namespace CtrEditor.Simulacion { action(); } + _deferredActions.Clear(); } @@ -897,7 +912,7 @@ namespace CtrEditor.Simulacion public void Debug_DrawInitialBodies() { Debug_ClearSimulationShapes(); - world.Step(0.01f); // Para actualizar la BodyList + world.Step(0.01f); // Para actualizar la BodyList foreach (Body body in world.BodyList) { foreach (Fixture fixture in body.FixtureList) @@ -933,6 +948,7 @@ namespace CtrEditor.Simulacion default: return; } + shape.Tag = "Simulation"; // Marcar para simulación Canvas.SetZIndex(shape, 20); simulationCanvas.Children.Add(shape); @@ -949,7 +965,7 @@ namespace CtrEditor.Simulacion EdgeShape edge = fixture.Shape as EdgeShape; Line line = new Line { - X1 = p(edge.Vertex1.X + fixture.Body.Position.X), // Aplicar escala y posición + X1 = p(edge.Vertex1.X + fixture.Body.Position.X), // Aplicar escala y posición Y1 = p(edge.Vertex1.Y + fixture.Body.Position.Y), X2 = p(edge.Vertex2.X + fixture.Body.Position.X), Y2 = p(edge.Vertex2.Y + fixture.Body.Position.Y), @@ -976,21 +992,22 @@ namespace CtrEditor.Simulacion private System.Windows.Shapes.Shape DrawPolygon(Fixture fixture) { - Polygon polygon = new Polygon { Stroke = Brushes.Black, StrokeThickness = 2 }; + Polygon polygon = new Polygon + { + Stroke = Brushes.Black, + StrokeThickness = 2 + }; PolygonShape polyShape = fixture.Shape as PolygonShape; - float cos = (float)Math.Cos(fixture.Body.Rotation); float sin = (float)Math.Sin(fixture.Body.Rotation); - foreach (Vector2 vertex in polyShape.Vertices) { float rotatedX = vertex.X * cos - vertex.Y * sin + fixture.Body.Position.X; float rotatedY = vertex.X * sin + vertex.Y * cos + fixture.Body.Position.Y; - polygon.Points.Add(new Point(p(rotatedX), p(rotatedY))); } return polygon; } } -} +} \ No newline at end of file diff --git a/Windows/MultiPropertyEditorWindow.xaml b/Windows/MultiPropertyEditorWindow.xaml new file mode 100644 index 0000000..477a0e9 --- /dev/null +++ b/Windows/MultiPropertyEditorWindow.xaml @@ -0,0 +1,23 @@ + + + +