Compare commits

..

2 Commits

9 changed files with 737 additions and 127 deletions

View File

@ -21,6 +21,7 @@ using System.Text.Json;
using Newtonsoft.Json;
using System.Windows.Data;
using System.Windows;
using static System.Resources.ResXFileRef;
namespace CtrEditor
{
@ -32,6 +33,8 @@ namespace CtrEditor
public ObservableCollection<TipoSimulable> ListaOsBase { get; } = new ObservableCollection<TipoSimulable>();
private ObservableCollection<osBase> _objetosSimulables = new ObservableCollection<osBase>();
private SimulationManager simulationManager = new SimulationManager();
private readonly DispatcherTimer _timerSimulacion;
public ICommand StartSimulationCommand { get; }
@ -54,7 +57,7 @@ namespace CtrEditor
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
_timerSimulacion = new DispatcherTimer();
_timerSimulacion.Interval = TimeSpan.FromMilliseconds(100); // ajusta el intervalo según sea necesario
_timerSimulacion.Interval = TimeSpan.FromMilliseconds(20); // ajusta el intervalo según sea necesario
_timerSimulacion.Tick += OnTickSimulacion;
StartSimulationCommand = new RelayCommand(StartSimulation);
@ -125,14 +128,18 @@ namespace CtrEditor
private void OnTickSimulacion(object sender, EventArgs e)
{
var args = new TickSimulacionEventArgs();
OnTickSimulacion(args);
simulationManager.Step((float)_timerSimulacion.Interval.TotalMilliseconds);
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.UpdateControl();
}
protected virtual void OnTickSimulacion(TickSimulacionEventArgs e)
{
TickSimulacion?.Invoke(this, e);
}
//protected virtual void OnTickSimulacion(TickSimulacionEventArgs e)
//{
// TickSimulacion?.Invoke(this, e);
//}
public string directorioTrabajo
{
@ -162,6 +169,7 @@ namespace CtrEditor
{
if (_selectedImage != value && value != null)
{
StopSimulation();
SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
_selectedImage = value;
ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); // Dispara el evento con la nueva ruta de imagen
@ -229,7 +237,14 @@ namespace CtrEditor
{
obj.VisualRepresentation = null;
}
var serializedData = JsonConvert.SerializeObject(ObjetosSimulables, settings);
// Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter
var dataToSerialize = new SimulationData
{
ObjetosSimulables = ObjetosSimulables,
UnitConverter = PixelToMeter.Instance.calc
};
var serializedData = JsonConvert.SerializeObject(dataToSerialize, settings);
File.WriteAllText(datosDeTrabajo.ObtenerPathImagenConExtension(_selectedImage, ".json"), serializedData);
}
}
@ -252,11 +267,16 @@ namespace CtrEditor
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
if (jsonString != null)
{
ObjetosSimulables = JsonConvert.DeserializeObject<ObservableCollection<osBase>>(jsonString, settings);
// Ahora recorres la colección de objetos simulables
var simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
if (simulationData != null)
{
ObjetosSimulables = simulationData.ObjetosSimulables;
// Restaura el UnitConverter si es necesario en otra parte de tu código
PixelToMeter.Instance.calc = simulationData.UnitConverter;
// Recorrer la colección de objetos simulables
foreach (var objetoSimulable in ObjetosSimulables)
CrearUsercontrol(objetoSimulable);
}
@ -266,6 +286,7 @@ namespace CtrEditor
catch { /* Consider logging the error or handling it appropriately */ }
}
private bool CrearUsercontrol(osBase osObjeto)
{
Type tipoObjeto = osObjeto.GetType();
@ -276,7 +297,7 @@ namespace CtrEditor
if (userControl != null)
{
// Asignar los datos al UserControl
UserControlFactory.AssignDatos(userControl, osObjeto);
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager);
OnUserControlSelected?.Invoke(userControl);
return true;
@ -297,6 +318,11 @@ namespace CtrEditor
}
public class SimulationData
{
public ObservableCollection<osBase> ObjetosSimulables { get; set; }
public UnitConverter UnitConverter { get; set; }
}
public class TipoSimulable
{

View File

@ -22,6 +22,7 @@ namespace CtrEditor
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///
public partial class MainWindow : Window
{
// Para el Canvas
@ -54,7 +55,7 @@ namespace CtrEditor
if (DataContext is MainViewModel viewModel)
{
viewModel.ImageSelected += ViewModel_ImageSelected;
viewModel.TickSimulacion += MainViewModel_TickSimulacion;
//viewModel.TickSimulacion += MainViewModel_TickSimulacion;
viewModel.OnUserControlSelected += AgregarUserControl;
viewModel?.LoadInitialData(); // Carga la primera imagen por defecto una vez cargada la ventana principal
}
@ -85,19 +86,21 @@ namespace CtrEditor
double centerY = offsetY + (visibleHeight / scaleY) / 2;
// Ajusta la posición del UserControl para que esté centrado en el área visible
double left = centerX - (userControl.ActualWidth / 2);
double top = centerY - (userControl.ActualHeight / 2);
double leftPixels = centerX - (userControl.ActualWidth / 2);
double topPixels = centerY - (userControl.ActualHeight / 2);
// Establece la posición del UserControl
NuevoOS.Left = left;
NuevoOS.Top = top;
NuevoOS.LeftPixels = (float)leftPixels;
NuevoOS.TopPixels = (float)topPixels;
NuevoOS.Inicializado = true;
}
// Establece la posición del UserControl
Canvas.SetLeft(userControl, NuevoOS.Left);
Canvas.SetTop(userControl, NuevoOS.Top);
else
{
// Fuerza a Establecer la posición del UserControl
NuevoOS.LeftPixels = NuevoOS.LeftPixels;
NuevoOS.TopPixels = NuevoOS.TopPixels;
}
// Suscribirse a eventos de mouse para marcar el Control
userControl.MouseEnter += UserControl_MouseEnter;
@ -109,7 +112,8 @@ namespace CtrEditor
userControl.MouseMove += UserControl_MouseMove;
// Añade el UserControl al Canvas
ImagenEnTrabajoCanvas.Children.Add(userControl);
Canvas.SetZIndex(userControl, dataContainer.ZIndex());
ImagenEnTrabajoCanvas.Children.Add(userControl);
}
}
@ -215,7 +219,7 @@ namespace CtrEditor
var newY = Canvas.GetTop(_currentDraggingControl) + dy;
if (_currentDraggingControl is IDataContainer dataContainer)
dataContainer.Move(newX, newY);
dataContainer.Move((float)newX,(float) newY);
_startPointUserControl = currentPosition; // Actualiza el punto inicial para el siguiente movimiento
}
@ -243,7 +247,7 @@ namespace CtrEditor
//rotateTransform.Angle = angle; // - _initialAngleUserControl; // Asegúrate de ajustar esta parte según cómo calcules el ángulo inicial
if (control is IDataContainer dataContainer)
dataContainer.Rotate(angle);
dataContainer.Rotate((float)angle);
// Actualizar el ángulo mostrado
_angleDisplayTextBlock.Text = $"Ángulo: {angle:F2}°";
@ -267,7 +271,7 @@ namespace CtrEditor
control.Width = newWidth; // Asegurar que no sea menor que el mínimo
if (control is IDataContainer dataContainer)
dataContainer.Resize(newWidth, 0);
dataContainer.Resize((float)newWidth, 0);
// Actualizar el punto de inicio para el próximo evento de movimiento
_startPointUserControl = currentPosition;
@ -423,20 +427,6 @@ namespace CtrEditor
}
private void MainViewModel_TickSimulacion(object sender, TickSimulacionEventArgs e)
{
// aquí puedes agregar la lógica para actualizar tus UserControl
// en el ImagenEnTrabajoCanvas
foreach (var child in ImagenEnTrabajoCanvas.Children)
{
if (child is osBase uc)
{
// llama al método Update de cada UserControl
uc.Update();
}
}
}
private void MainWindow_Closed(object sender, EventArgs e)
{
if (DataContext is MainViewModel viewModel)

View File

@ -45,13 +45,14 @@ namespace CtrEditor.ObjetosSim
return instance;
}
public static void AssignDatos(UserControl userControl, osBase datos)
public static void AssignDatos(UserControl userControl, osBase datos, SimulationManager simulationManager)
{
if (userControl is IDataContainer dataContainer)
{
dataContainer.Datos = datos;
userControl.DataContext = datos;
datos.VisualRepresentation = userControl;
datos.ConnectSimManager(simulationManager);
}
}
}

View File

@ -14,68 +14,36 @@ namespace CtrEditor.ObjetosSim
public interface IosBase
{
string Nombre { get; }
void Update();
void ConnectSimManager(SimulationManager simulationManager);
void UpdateControl();
}
public interface IDataContainer
{
osBase? Datos { get; set; }
void Resize(double width, double height);
void Move(double Left, double Top);
void Rotate(double Angle);
void Resize(float width, float height);
void Move(float Left, float Top);
void Rotate(float Angle);
void Highlight(bool State);
int ZIndex();
}
public abstract class osBase : INotifyPropertyChanged, IosBase
{
private string _nombre = "Base";
public double _left;
public double _top;
{
public abstract float LeftPixels { get; set; }
public abstract float TopPixels { get; set; }
public abstract float Left { get; set; }
public abstract float Top { get; set; }
public bool Inicializado = false;
public double Left
{
get => _left;
protected UserControl? _visualRepresentation = null;
set
{
_left = value;
if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, _left);
OnPropertyChanged(nameof(Left));
}
}
public double Top
{
get => _top;
set
{
_top = value;
if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation, _top);
OnPropertyChanged(nameof(Top));
}
}
private UserControl? _visualRepresentation = null;
public string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public abstract void Update();
public abstract string Nombre { get; set; }
public abstract void ConnectSimManager(SimulationManager simulationManager);
public abstract void UpdateControl();
[JsonIgnore]
public UserControl? VisualRepresentation
@ -83,6 +51,7 @@ namespace CtrEditor.ObjetosSim
get => _visualRepresentation;
set => _visualRepresentation = value;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
@ -90,4 +59,62 @@ namespace CtrEditor.ObjetosSim
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class PixelToMeter
{
// Instancia privada estática, parte del patrón Singleton
private static PixelToMeter? _instance;
public UnitConverter calc = new UnitConverter(0.01f);
// Propiedad pública estática para acceder a la instancia
public static PixelToMeter Instance
{
get
{
if (_instance == null)
{
_instance = new PixelToMeter();
}
return _instance;
}
}
}
public class UnitConverter
{
// La escala representa cuántos metros hay en un píxel
public float Scale { get; private set; }
public UnitConverter(float scale)
{
if (scale <= 0)
throw new ArgumentException("Scale must be greater than zero.");
Scale = scale;
}
// Convierte una distancia en metros a píxeles
public float MetersToPixels(float meters)
{
return meters / Scale;
}
// Convierte una distancia en píxeles a metros
public float PixelsToMeters(float pixels)
{
return pixels * Scale;
}
// Configurar o ajustar la escala
public void SetScale(float newScale)
{
if (newScale <= 0)
throw new ArgumentException("Scale must be greater than zero.");
Scale = newScale;
}
}
}

View File

@ -1,6 +1,6 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBotella"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Border Background="Brown" CornerRadius="10" Height="10" Width="10"/>
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Ellipse Height="{Binding DiametroPixels}" Stroke="red" Fill="Gray" Width="{Binding DiametroPixels}"/>
</UserControl>

View File

@ -12,6 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Numerics;
namespace CtrEditor.ObjetosSim
{
@ -22,13 +23,117 @@ namespace CtrEditor.ObjetosSim
public class osBotella : osBase
{
public double diametro { get; set; }
private Circle Data = new Circle();
// Otros datos y métodos relevantes para la simulación
private string _nombre = "Botella";
public override void Update()
public float Diametro {
get => Data.Diameter;
set
{
Data.Diameter = value;
OnPropertyChanged(nameof(Diametro));
OnPropertyChanged(nameof(DiametroPixels));
}
}
public float DiametroPixels
{
// implementation of Update method
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Diameter);
set
{
Data.Diameter = PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(Diametro));
OnPropertyChanged(nameof(DiametroPixels));
}
}
public float Mass {
get => Data.Mass;
set
{
Data.Mass = value;
OnPropertyChanged(nameof(Mass));
}
}
public override float LeftPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Left);
set
{
Data.Left = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, value);
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left));
}
}
public override float TopPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Top);
set
{
Data.Top = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation,value);
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top));
}
}
public override float Left
{
get => Data.Left;
set
{
Data.Left = value;
if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => Data.Top;
set
{
Data.Top = value;
if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top));
}
}
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public osBotella()
{
DiametroPixels = 10;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
simulationManager.circles.Add(Data);
}
public override void UpdateControl()
{
Top = Data.Top;
Left = Data.Left;
}
}
@ -40,17 +145,20 @@ namespace CtrEditor.ObjetosSim
{
InitializeComponent();
}
public void Resize(double width, double height) { }
public void Move(double Left, double Top)
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = Left;
Datos.Top = Top;
Datos.LeftPixels = LeftPixels;
Datos.TopPixels = TopPixels;
}
}
public void Rotate(double Angle) { }
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -6,7 +6,7 @@
xmlns:local="clr-namespace:CtrEditor"
mc:Ignorable="d">
<Canvas>
<Rectangle Width="{Binding Ancho}" Height="{Binding Alto}" Fill="Gray">
<Rectangle Width="{Binding AnchoPixels}" Height="{Binding AltoPixels}" Fill="Gray">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform>

View File

@ -13,6 +13,8 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Numerics;
using System.Windows.Markup;
namespace CtrEditor.ObjetosSim
{
@ -25,47 +27,148 @@ namespace CtrEditor.ObjetosSim
{
private string _nombre = "Transporte TTOP";
private double _Ancho;
private double _Alto;
private double _Angulo;
private float frictionCoefficient;
private float velMax50hz; // en metros por minuto
private float tiempoRampa;
private bool esMarcha;
public double diametro { get; set; }
public double Ancho {
get { return _Ancho; }
private Rectangle Data = new Rectangle();
public override float LeftPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Left);
set
{
_Ancho = value;
Data.Left = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null )
Canvas.SetLeft(_visualRepresentation,value);
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left));
}
}
public override float TopPixels
{
get => PixelToMeter.Instance.calc.MetersToPixels(Data.Top);
set
{
Data.Top = PixelToMeter.Instance.calc.PixelsToMeters(value);
if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation, value);
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top));
}
}
public override float Left
{
get => Data.Left;
set
{
Data.Left = value;
if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(LeftPixels));
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => Data.Top;
set
{
Data.Top = value;
if (_visualRepresentation != null)
Canvas.SetTop(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(value));
OnPropertyChanged(nameof(TopPixels));
OnPropertyChanged(nameof(Top));
}
}
public float Ancho {
get => Data.Length;
set
{
Data.Length = value;
OnPropertyChanged(nameof(Ancho));
}
}
public double Alto {
get { return _Alto; }
public float Alto {
get => Data.Width;
set
{
_Alto = value;
Data.Width = value;
OnPropertyChanged(nameof(Alto));
}
}
public double Angulo
public float AnchoPixels
{
get { return _Angulo; }
get => (float)PixelToMeter.Instance.calc.MetersToPixels(Data.Length);
set
{
_Angulo = value;
OnPropertyChanged(nameof(Angulo));
Data.Length = (float)PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(Ancho));
}
}
public float AltoPixels
{
get => (float)PixelToMeter.Instance.calc.MetersToPixels(Data.Width);
set
{
Data.Width = (float)PixelToMeter.Instance.calc.PixelsToMeters(value);
OnPropertyChanged(nameof(Alto));
}
}
public osTransporteTTop()
public float Angulo
{
Ancho = 100;
Alto = 10;
get => Data.Angle;
set
{
Data.Angle = value;
OnPropertyChanged(nameof(Angulo));
}
}
public float VelocidadActual
{
get => Data.Speed;
set {
Data.Speed = value;
OnPropertyChanged(nameof(VelocidadActual));
}
}
public override void Update()
public override string Nombre
{
// implementation of Update method
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; }
public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; }
public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; }
public bool EsMarcha { get => esMarcha; set => esMarcha = value; }
public osTransporteTTop()
{
AnchoPixels = 100;
AltoPixels = 10;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
simulationManager.rectangles.Add(Data);
}
public override void UpdateControl()
{
}
}
public partial class ucTransporteTTop : UserControl, IDataContainer
@ -76,25 +179,30 @@ namespace CtrEditor.ObjetosSim
{
InitializeComponent();
}
public void Resize(double width, double height)
public void Resize(float width, float height)
{
if (Datos is osTransporteTTop datos)
datos.Ancho = width;
}
public void Move(double Left, double Top)
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = Left;
Datos.Top = Top;
Datos.LeftPixels = LeftPixels;
Datos.TopPixels = TopPixels;
}
}
public void Rotate(double Angle) {
public void Rotate(float Angle) {
if (Datos != null)
if (Datos is osTransporteTTop datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -0,0 +1,350 @@
using CtrEditor.ObjetosSim;
using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Numerics;
using System.Windows.Shapes;
public class Circle
{
private Vector2 position;
public float Left
{
get { return position.X; }
set { position.X = value; }
}
public float Top
{
get { return position.Y; }
set { position.Y = value; }
}
public float Diameter { get; set; }
public float Mass { get; set; }
public float Angle { get; set; } // En grados
public float Speed { get; set; }
public Circle(float left = 0, float top = 0, float diameter = 10, float mass = 1, float angle = 0, float speed = 0)
{
position = new Vector2(left, top);
Diameter = diameter;
Mass = mass;
Angle = angle;
Speed = speed;
}
public void Move(float timeStep_ms, List<Circle> circles, List<Rectangle> rectangles, List<Line> lines)
{
// Convertir timeStep de milisegundos a segundos para la simulación
float timeStepInSeconds = timeStep_ms / 1000.0f;
bool isTracted = false; // Indicador para verificar si el círculo está siendo traccionado
// Aplicar fuerza desde el rectángulo si está sobre uno
foreach (var rectangle in rectangles)
{
float overlap = CalculateOverlapPercentage(this, rectangle);
if (overlap > 0)
{
isTracted = true; // El círculo está siendo traccionado por un rectángulo
// Convertir la velocidad del rectángulo de metros por minuto a metros por segundo
float rectangleSpeedInMetersPerSecond = rectangle.Speed / 60.0f;
if (rectangleSpeedInMetersPerSecond < Speed)
{
// Aplicar una fuerza de frenado si la velocidad del rectángulo es menor que la velocidad del círculo
float brakingForce = (Speed - rectangleSpeedInMetersPerSecond) * (overlap / 100.0f);
Speed -= brakingForce * timeStepInSeconds;
}
else
{
// Alinear gradualmente la velocidad del círculo con la del rectángulo si es mayor
Speed += (rectangleSpeedInMetersPerSecond - Speed) * (overlap / 100.0f) * timeStepInSeconds;
}
Angle = rectangle.Angle;
}
}
// Si el círculo no está siendo traccionado, aplicar desaceleración
if (!isTracted)
{
float deceleration = (1.0f / Mass) * 10.0f; // Coeficiente de desaceleración inversamente proporcional a la masa
Speed -= deceleration * timeStepInSeconds;
if (Speed < 0) Speed = 0; // Evitar que la velocidad sea negativa
}
//// Interacción por impacto con otros círculos
//foreach (var other in circles)
//{
// if (this != other && IsColliding(this, other))
// {
// Vector2 impactDirection = other.position - this.position;
// Angle = (float)Math.Atan2(impactDirection.Y, impactDirection.X) * (180 / (float)Math.PI);
// Speed = other.Speed; // Asumimos que el círculo receptor adopta la velocidad del impacto
// }
//}
// Ajustar por superposición con otros círculos
foreach (var other in circles)
{
if (this != other && IsColliding(this, other))
{
AdjustForOverlap(other);
}
}
// Cambiar dirección al contacto con líneas
foreach (var line in lines)
{
if (IsCollidingWithLine(this, line))
{
float impactAngle = CalculateImpactAngle(this, line);
if (impactAngle < 85)
{
Angle = line.Angle;
}
else if (impactAngle > 95)
{
Angle = line.Angle + 180; // Movimiento contrario
}
else
{
Speed = 0; // Cancelación de movimiento
}
}
}
// Calcular nueva posición
Vector2 direction = new Vector2((float)Math.Cos(Angle * Math.PI / 180), (float)Math.Sin(Angle * Math.PI / 180));
Vector2 velocity = direction * Speed * timeStepInSeconds;
position += velocity;
}
private void AdjustForOverlap(Circle other)
{
if (this == other) return; // No auto-interacción
float distance = Vector2.Distance(this.position, other.position);
float radiusSum = (this.Diameter / 2) + (other.Diameter / 2);
if (distance < radiusSum) // Los círculos están solapando
{
Vector2 directionToOther = Vector2.Normalize(other.position - this.position);
float overlapDistance = radiusSum - distance;
// Decidir qué círculo mover basado en sus velocidades
if (this.Speed == 0 && other.Speed > 0)
{
// Mover este círculo si su velocidad es cero y el otro se está moviendo
this.position -= directionToOther * overlapDistance;
}
else if (other.Speed == 0 && this.Speed > 0)
{
// Mover el otro círculo si su velocidad es cero y este se está moviendo
other.position += directionToOther * overlapDistance;
}
else if (this.Speed == 0 && other.Speed == 0)
{
// Si ambos tienen velocidad cero, mover ambos a la mitad del solapamiento
this.position -= directionToOther * (overlapDistance / 2);
other.position += directionToOther * (overlapDistance / 2);
}
}
}
private bool IsColliding(Circle circle1, Circle circle2)
{
float distance = Vector2.Distance(circle1.position, circle2.position);
float radiusSum = (circle1.Diameter / 2) + (circle2.Diameter / 2);
return distance <= radiusSum;
}
private bool IsCollidingWithLine(Circle circle, Line line)
{
Vector2 nearestPoint = NearestPointOnLine(circle.position, line.start, line.end);
float distanceToLine = Vector2.Distance(circle.position, nearestPoint);
return distanceToLine <= (circle.Diameter / 2);
}
private Vector2 NearestPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 lineVector = lineEnd - lineStart;
Vector2 pointVector = point - lineStart;
float lineLength = lineVector.Length();
float projectedLength = Vector2.Dot(pointVector, lineVector) / lineLength;
projectedLength = Math.Max(0, Math.Min(lineLength, projectedLength)); // Clamping to the line segment
return lineStart + lineVector * (projectedLength / lineLength);
}
private float CalculateImpactAngle(Circle circle, Line line)
{
Vector2 movementDirection = new Vector2((float)Math.Cos(circle.Angle * Math.PI / 180), (float)Math.Sin(circle.Angle * Math.PI / 180));
Vector2 lineDirection = line.end - line.start;
Vector2 lineNormal = new Vector2(-lineDirection.Y, lineDirection.X); // Rotar 90 grados para obtener normal
lineNormal = Vector2.Normalize(lineNormal);
// Calcular ángulo entre el movimiento y la normal de la línea
float dotProduct = Vector2.Dot(movementDirection, lineNormal);
float angle = (float)Math.Acos(dotProduct) * (180 / (float)Math.PI); // Convertir de radianes a grados
// Ajustar para obtener el ángulo relativo correcto
return angle < 90 ? 90 - angle : angle - 90;
}
public float CalculateOverlapPercentage(Circle circle, Rectangle rectangle)
{
// Convertir ángulo del rectángulo de grados a radianes
float angleRadians = (float)(rectangle.Angle * Math.PI / 180);
// Centro del círculo
Vector2 circleCenter = new Vector2(circle.Left + circle.Diameter / 2, circle.Top + circle.Diameter / 2);
float radius = circle.Diameter / 2;
// Pivot del rectángulo es el Top Left
Vector2 rectPivot = new Vector2(rectangle.Left, rectangle.Top);
// Rotar el centro del círculo respecto al pivote del rectángulo
Vector2 rotatedCircleCenter = RotatePoint(circleCenter, rectPivot, -angleRadians);
// Comprobar si el círculo rotado intersecta con el rectángulo alineado
// Rectángulo "alineado" asume que después de rotar el círculo, el rectángulo se comporta como si estuviera alineado con los ejes
if (IsCircleRectangleIntersecting(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width))
{
float overlapArea = EstimateOverlapArea(rotatedCircleCenter, radius, rectPivot, rectangle.Length, rectangle.Width);
float circleArea = (float)(Math.PI * radius * radius);
return (overlapArea / circleArea) * 100;
}
return 0;
}
private bool IsCircleRectangleIntersecting(Vector2 circleCenter, float radius, Vector2 rectTopLeft, float length, float width)
{
float closestX = Math.Max(rectTopLeft.X, Math.Min(circleCenter.X, rectTopLeft.X + length));
float closestY = Math.Max(rectTopLeft.Y, Math.Min(circleCenter.Y, rectTopLeft.Y + width));
float distanceX = circleCenter.X - closestX;
float distanceY = circleCenter.Y - closestY;
return (distanceX * distanceX + distanceY * distanceY) < (radius * radius);
}
private float EstimateOverlapArea(Vector2 circleCenter, float radius, Vector2 rectCenter, float length, float width)
{
// Esto es un placeholder: el cálculo real requiere un algoritmo geométrico complejo
// Puedes retornar una estimación basada en proporciones o usar una librería geométrica
return (float) (radius * radius * Math.PI * 0.25f); // Asumiendo un solapamiento del 25% como placeholder
}
private Vector2 RotatePoint(Vector2 point, Vector2 pivot, float angle)
{
float cosTheta = (float)Math.Cos(angle);
float sinTheta = (float)Math.Sin(angle);
// Ajustar punto por pivot antes de aplicar rotación
Vector2 translatedPoint = new Vector2(point.X - pivot.X, point.Y - pivot.Y);
// Rotar el punto
Vector2 rotatedPoint = new Vector2(
translatedPoint.X * cosTheta - translatedPoint.Y * sinTheta,
translatedPoint.X * sinTheta + translatedPoint.Y * cosTheta
);
// Traducir el punto de vuelta
return new Vector2(rotatedPoint.X + pivot.X, rotatedPoint.Y + pivot.Y);
}
}
public class Rectangle
{
private Vector2 position;
public float Left
{
get { return position.X; }
set { position = new Vector2(value, position.Y); }
}
public float Top
{
get { return position.Y; }
set { position = new Vector2(position.X, value); }
}
public float Length { get; set; }
public float Width { get; set; }
public float Angle { get; set; } // En grados
public float Speed { get; set; } // Velocidad del rectángulo
public Rectangle(float left = 0, float top = 0, float length = 10, float width = 10, float angle = 0, float speed = 0)
{
position = new Vector2(left, top);
Length = length;
Width = width;
Angle = angle;
Speed = speed;
}
}
public class Line
{
public Vector2 start;
public Vector2 end;
public float Left
{
get { return start.X; }
set { start = new Vector2(value, start.Y); UpdateEnd(); }
}
public float Top
{
get { return start.Y; }
set { start = new Vector2(start.X, value); UpdateEnd(); }
}
public float Length { get; set; }
public float Width { get; set; }
public float Angle { get; set; } // En grados
public Line(float left, float top, float length, float width, float angle)
{
start = new Vector2(left, top);
Length = length;
Width = width;
Angle = angle;
UpdateEnd();
}
private void UpdateEnd()
{
// Asumiendo que la línea se extiende en el ángulo desde el punto de inicio
end = new Vector2(start.X + Length * (float)Math.Cos(Angle * Math.PI / 180),
start.Y + Length * (float)Math.Sin(Angle * Math.PI / 180));
}
}
// Clase principal que gestiona la simulación
public class SimulationManager
{
public List<Circle> circles;
public List<Rectangle> rectangles;
public List<Line> lines;
public SimulationManager()
{
circles = new List<Circle>();
rectangles = new List<Rectangle>();
lines = new List<Line>();
}
public void Step(float timeStep)
{
foreach (var circle in circles)
{
circle.Move(timeStep, circles, rectangles, lines);
}
}
}