Compare commits
No commits in common. "5f680b3a7a34bc2a2581a32f7c657dca2a2e399a" and "061007158d53d5711f557546d9e4c89ab0d2521c" have entirely different histories.
5f680b3a7a
...
061007158d
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": "."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../Libraries/LibS7Adv"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {}
|
|
||||||
}
|
|
|
@ -746,7 +746,7 @@ namespace CtrEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch.Stop(); // Stop measuring time
|
stopwatch.Stop(); // Stop measuring time
|
||||||
// Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
|
Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenWorkDirectory()
|
private void OpenWorkDirectory()
|
||||||
|
|
|
@ -6,7 +6,6 @@ using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using System.Windows.Shapes;
|
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
|
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
|
||||||
using UserControl = System.Windows.Controls.UserControl;
|
using UserControl = System.Windows.Controls.UserControl;
|
||||||
|
@ -62,9 +61,6 @@ namespace CtrEditor
|
||||||
|
|
||||||
// Importante: Agregar el evento para el menú contextual
|
// Importante: Agregar el evento para el menú contextual
|
||||||
ImagenEnTrabajoCanvas.MouseRightButtonDown += Canvas_MouseRightButtonDown;
|
ImagenEnTrabajoCanvas.MouseRightButtonDown += Canvas_MouseRightButtonDown;
|
||||||
|
|
||||||
// Asegurarse de que estos eventos estén conectados
|
|
||||||
ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
@ -151,36 +147,20 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas)
|
if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas)
|
||||||
{
|
{
|
||||||
// Verificar si el clic fue en el canvas, imagen de fondo o en el rectángulo de selección
|
// Solo permitir el panning si el clic fue en el canvas o en la imagen de fondo
|
||||||
if (e.Source == ImagenEnTrabajoCanvas ||
|
// y no en otros controles
|
||||||
e.Source == imagenDeFondo ||
|
if (e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo)
|
||||||
(e.Source is Rectangle rect && rect == _objectManager._selectionRectangle))
|
|
||||||
{
|
{
|
||||||
ImagenEnTrabajoCanvas.Focus();
|
ImagenEnTrabajoCanvas.Focus(); // Asegurar que el canvas tiene el foco
|
||||||
|
_isDraggingCanvas = true;
|
||||||
|
_lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
|
||||||
|
ImagenEnTrabajoCanvas.CaptureMouse();
|
||||||
|
Mouse.OverrideCursor = Cursors.Hand;
|
||||||
|
|
||||||
if (_objectManager.IsRectangleSelectionActive)
|
if (DataContext is MainViewModel viewModel)
|
||||||
{
|
{
|
||||||
if (!_objectManager.HasActiveSelectionRectangle)
|
viewModel.SelectedItemOsList = null;
|
||||||
{
|
_objectManager.RemoveResizeRectangles();
|
||||||
_objectManager.StartRectangleSelection(e.GetPosition(ImagenEnTrabajoCanvas));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_objectManager.FinishRectangleSelection(e.GetPosition(ImagenEnTrabajoCanvas));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_isDraggingCanvas = true;
|
|
||||||
_lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
|
|
||||||
ImagenEnTrabajoCanvas.CaptureMouse();
|
|
||||||
Mouse.OverrideCursor = Cursors.Hand;
|
|
||||||
|
|
||||||
if (DataContext is MainViewModel viewModel)
|
|
||||||
{
|
|
||||||
viewModel.SelectedItemOsList = null;
|
|
||||||
_objectManager.RemoveResizeRectangles();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
@ -189,12 +169,7 @@ namespace CtrEditor
|
||||||
|
|
||||||
private void Canvas_MouseMove_Panning(object sender, MouseEventArgs e)
|
private void Canvas_MouseMove_Panning(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
if (_objectManager.IsRectangleSelectionActive && _objectManager.HasActiveSelectionRectangle)
|
if (_isDraggingCanvas && e.LeftButton == MouseButtonState.Pressed)
|
||||||
{
|
|
||||||
var currentPoint = e.GetPosition(ImagenEnTrabajoCanvas);
|
|
||||||
_objectManager.UpdateRectangleSelection(currentPoint);
|
|
||||||
}
|
|
||||||
else if (_isDraggingCanvas && e.LeftButton == MouseButtonState.Pressed)
|
|
||||||
{
|
{
|
||||||
var currentPosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
|
var currentPosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
|
||||||
var dx = currentPosition.X - _lastMousePosition.X;
|
var dx = currentPosition.X - _lastMousePosition.X;
|
||||||
|
@ -461,116 +436,59 @@ namespace CtrEditor
|
||||||
private void ShowContextMenu(Point position)
|
private void ShowContextMenu(Point position)
|
||||||
{
|
{
|
||||||
var contextMenu = new ContextMenu();
|
var contextMenu = new ContextMenu();
|
||||||
|
var multiSelectMenuItem = new MenuItem
|
||||||
|
{
|
||||||
|
Header = "Modo Multi-Selección",
|
||||||
|
IsCheckable = true,
|
||||||
|
StaysOpenOnClick = false
|
||||||
|
};
|
||||||
|
|
||||||
if (DataContext is MainViewModel viewModel)
|
if (DataContext is MainViewModel viewModel)
|
||||||
{
|
{
|
||||||
// Multi-selection checkbox
|
multiSelectMenuItem.IsChecked = viewModel.IsMultiSelectionActive;
|
||||||
var multiSelectMenuItem = new MenuItem
|
|
||||||
{
|
|
||||||
Header = "Modo Multi-Selección",
|
|
||||||
IsCheckable = true,
|
|
||||||
StaysOpenOnClick = false,
|
|
||||||
IsChecked = viewModel.IsMultiSelectionActive
|
|
||||||
};
|
|
||||||
|
|
||||||
multiSelectMenuItem.Click += (s, e) =>
|
multiSelectMenuItem.Click += (s, e) =>
|
||||||
{
|
{
|
||||||
viewModel.IsMultiSelectionActive = multiSelectMenuItem.IsChecked;
|
viewModel.IsMultiSelectionActive = multiSelectMenuItem.IsChecked;
|
||||||
|
|
||||||
// Si se desactiva la multi-selección, desactivar también la selección por rectángulo
|
|
||||||
if (!multiSelectMenuItem.IsChecked)
|
|
||||||
_objectManager.IsRectangleSelectionActive = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
contextMenu.Items.Add(multiSelectMenuItem);
|
// Solo mostrar opciones de alineación si hay objetos seleccionados
|
||||||
|
|
||||||
// Mostrar opción de selección por rectángulo solo cuando multi-selección está activa
|
|
||||||
if (viewModel.IsMultiSelectionActive)
|
|
||||||
{
|
|
||||||
var rectangleSelectMenuItem = new MenuItem
|
|
||||||
{
|
|
||||||
Header = "Selección por Rectángulo",
|
|
||||||
IsCheckable = true,
|
|
||||||
IsChecked = _objectManager.IsRectangleSelectionActive,
|
|
||||||
StaysOpenOnClick = false
|
|
||||||
};
|
|
||||||
|
|
||||||
rectangleSelectMenuItem.Click += (s, e) =>
|
|
||||||
{
|
|
||||||
_objectManager.IsRectangleSelectionActive = rectangleSelectMenuItem.IsChecked;
|
|
||||||
_objectManager.IsDraggingCanvas = false; // Desactivar panning cuando se activa la selección por rectángulo
|
|
||||||
};
|
|
||||||
|
|
||||||
contextMenu.Items.Add(rectangleSelectMenuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si hay objetos seleccionados, agregar opciones de alineación
|
|
||||||
if (_objectManager.SelectedObjects.Count > 1)
|
if (_objectManager.SelectedObjects.Count > 1)
|
||||||
{
|
{
|
||||||
|
var alignmentMenu = new MenuItem { Header = "Alinear" };
|
||||||
|
var sizeMenu = new MenuItem { Header = "Igualar Tamaño" };
|
||||||
|
var joinMenu = new MenuItem { Header = "Unir" };
|
||||||
|
|
||||||
|
// Opciones de alineación
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Alinear a la Izquierda", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.Left)) });
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Alinear a la Derecha", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.Right)) });
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Alinear Arriba", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.Top)) });
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Alinear Abajo", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.Bottom)) });
|
||||||
|
alignmentMenu.Items.Add(new Separator());
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Centrar Horizontalmente", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.CenterHorizontally)) });
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Centrar Verticalmente", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.CenterVertically)) });
|
||||||
|
alignmentMenu.Items.Add(new Separator());
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Distribuir Horizontalmente", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.DistributeHorizontally)) });
|
||||||
|
alignmentMenu.Items.Add(new MenuItem { Header = "Distribuir Verticalmente", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.DistributeVertically)) });
|
||||||
|
|
||||||
|
// Opciones de igualar tamaño
|
||||||
|
sizeMenu.Items.Add(new MenuItem { Header = "Igualar Ancho", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.EqualWidth)) });
|
||||||
|
sizeMenu.Items.Add(new MenuItem { Header = "Igualar Alto", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.EqualHeight)) });
|
||||||
|
sizeMenu.Items.Add(new MenuItem { Header = "Igualar Ángulo", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.EqualAngle)) });
|
||||||
|
|
||||||
|
// Opciones de unir
|
||||||
|
joinMenu.Items.Add(new MenuItem { Header = "Unir Horizontalmente", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.JoinHorizontally)) });
|
||||||
|
joinMenu.Items.Add(new MenuItem { Header = "Unir Verticalmente", Command = new RelayCommand(() => _objectManager.AlignObjects(AlignmentType.JoinVertically)) });
|
||||||
|
|
||||||
|
contextMenu.Items.Add(alignmentMenu);
|
||||||
|
contextMenu.Items.Add(sizeMenu);
|
||||||
|
contextMenu.Items.Add(joinMenu);
|
||||||
contextMenu.Items.Add(new Separator());
|
contextMenu.Items.Add(new Separator());
|
||||||
|
|
||||||
var alignSubmenu = new MenuItem { Header = "Alinear" };
|
|
||||||
|
|
||||||
// Alineación horizontal
|
|
||||||
var alignLeftItem = new MenuItem { Header = "Izquierda" };
|
|
||||||
alignLeftItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Left);
|
|
||||||
alignSubmenu.Items.Add(alignLeftItem);
|
|
||||||
|
|
||||||
var alignRightItem = new MenuItem { Header = "Derecha" };
|
|
||||||
alignRightItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Right);
|
|
||||||
alignSubmenu.Items.Add(alignRightItem);
|
|
||||||
|
|
||||||
var alignCenterHItem = new MenuItem { Header = "Centro Horizontal" };
|
|
||||||
alignCenterHItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.CenterHorizontally);
|
|
||||||
alignSubmenu.Items.Add(alignCenterHItem);
|
|
||||||
|
|
||||||
// Alineación vertical
|
|
||||||
alignSubmenu.Items.Add(new Separator());
|
|
||||||
|
|
||||||
var alignTopItem = new MenuItem { Header = "Arriba" };
|
|
||||||
alignTopItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Top);
|
|
||||||
alignSubmenu.Items.Add(alignTopItem);
|
|
||||||
|
|
||||||
var alignBottomItem = new MenuItem { Header = "Abajo" };
|
|
||||||
alignBottomItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Bottom);
|
|
||||||
alignSubmenu.Items.Add(alignBottomItem);
|
|
||||||
|
|
||||||
var alignCenterVItem = new MenuItem { Header = "Centro Vertical" };
|
|
||||||
alignCenterVItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.CenterVertically);
|
|
||||||
alignSubmenu.Items.Add(alignCenterVItem);
|
|
||||||
|
|
||||||
// Distribución
|
|
||||||
alignSubmenu.Items.Add(new Separator());
|
|
||||||
|
|
||||||
var distributeHItem = new MenuItem { Header = "Distribuir Horizontalmente" };
|
|
||||||
distributeHItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.DistributeHorizontally);
|
|
||||||
alignSubmenu.Items.Add(distributeHItem);
|
|
||||||
|
|
||||||
var distributeVItem = new MenuItem { Header = "Distribuir Verticalmente" };
|
|
||||||
distributeVItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.DistributeVertically);
|
|
||||||
alignSubmenu.Items.Add(distributeVItem);
|
|
||||||
|
|
||||||
// Igualar tamaños
|
|
||||||
alignSubmenu.Items.Add(new Separator());
|
|
||||||
|
|
||||||
var equalWidthItem = new MenuItem { Header = "Igualar Ancho" };
|
|
||||||
equalWidthItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualWidth);
|
|
||||||
alignSubmenu.Items.Add(equalWidthItem);
|
|
||||||
|
|
||||||
var equalHeightItem = new MenuItem { Header = "Igualar Alto" };
|
|
||||||
equalHeightItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualHeight);
|
|
||||||
alignSubmenu.Items.Add(equalHeightItem);
|
|
||||||
|
|
||||||
var equalAngleItem = new MenuItem { Header = "Igualar Ángulo" };
|
|
||||||
equalAngleItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualAngle);
|
|
||||||
alignSubmenu.Items.Add(equalAngleItem);
|
|
||||||
|
|
||||||
contextMenu.Items.Add(alignSubmenu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contextMenu.IsOpen = true;
|
contextMenu.Items.Add(multiSelectMenuItem);
|
||||||
contextMenu.PlacementTarget = ImagenEnTrabajoCanvas;
|
contextMenu.PlacementTarget = ImagenEnTrabajoCanvas;
|
||||||
contextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint;
|
contextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint;
|
||||||
|
contextMenu.IsOpen = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,8 +553,6 @@ namespace CtrEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Image ImagenDeFondo => imagenDeFondo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FloatValidationRule : ValidationRule
|
public class FloatValidationRule : ValidationRule
|
||||||
|
|
|
@ -68,35 +68,11 @@ namespace CtrEditor
|
||||||
private ObservableCollection<osBase> _selectedObjects = new ObservableCollection<osBase>();
|
private ObservableCollection<osBase> _selectedObjects = new ObservableCollection<osBase>();
|
||||||
private List<Rectangle> _selectionHighlightRectangles = new List<Rectangle>(); // Add this line
|
private List<Rectangle> _selectionHighlightRectangles = new List<Rectangle>(); // Add this line
|
||||||
private List<(UserControl Control, Rectangle Highlight)> _selectionHighlightPairs = new List<(UserControl, Rectangle)>();
|
private List<(UserControl Control, Rectangle Highlight)> _selectionHighlightPairs = new List<(UserControl, Rectangle)>();
|
||||||
private Point _rectangleStart;
|
|
||||||
public Rectangle _selectionRectangle; // Cambiado a private
|
|
||||||
private ScrollViewer _scrollViewer; // Add this line
|
|
||||||
private Image _backgroundImage; // Add this line
|
|
||||||
internal bool IsDraggingCanvas { get; set; }
|
|
||||||
private bool _isRectangleSelectionActive;
|
|
||||||
public bool IsRectangleSelectionActive
|
|
||||||
{
|
|
||||||
get => _isRectangleSelectionActive;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_isRectangleSelectionActive = value;
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
// Al activar la selección por rectángulo, desactivar el panning
|
|
||||||
IsDraggingCanvas = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Propiedad pública para verificar si hay un rectángulo de selección activo
|
|
||||||
public bool HasActiveSelectionRectangle => _selectionRectangle != null;
|
|
||||||
|
|
||||||
public ObjectManipulationManager(MainWindow mainWindow, Canvas canvas)
|
public ObjectManipulationManager(MainWindow mainWindow, Canvas canvas)
|
||||||
{
|
{
|
||||||
_mainWindow = mainWindow;
|
_mainWindow = mainWindow;
|
||||||
_canvas = canvas;
|
_canvas = canvas;
|
||||||
_scrollViewer = mainWindow.ImagenEnTrabajoScrollViewer; // Add this line
|
|
||||||
_backgroundImage = mainWindow.ImagenDeFondo; // Add this line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PurgeDeletedObjects()
|
private void PurgeDeletedObjects()
|
||||||
|
@ -867,81 +843,5 @@ namespace CtrEditor
|
||||||
viewModel.HasUnsavedChanges = true;
|
viewModel.HasUnsavedChanges = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartRectangleSelection(Point startPoint)
|
|
||||||
{
|
|
||||||
_rectangleStart = startPoint;
|
|
||||||
_selectionRectangle = new Rectangle
|
|
||||||
{
|
|
||||||
Stroke = Brushes.Blue,
|
|
||||||
StrokeThickness = 1,
|
|
||||||
Fill = new SolidColorBrush(Color.FromArgb(50, 0, 0, 255))
|
|
||||||
};
|
|
||||||
|
|
||||||
Canvas.SetLeft(_selectionRectangle, startPoint.X);
|
|
||||||
Canvas.SetTop(_selectionRectangle, startPoint.Y);
|
|
||||||
Canvas.SetZIndex(_selectionRectangle, (int)ZIndexEnum.RectangulosPropiead);
|
|
||||||
|
|
||||||
_canvas.Children.Add(_selectionRectangle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSelectionRectangle(Point currentPoint)
|
|
||||||
{
|
|
||||||
double left = Math.Min(_rectangleStart.X, currentPoint.X);
|
|
||||||
double top = Math.Min(_rectangleStart.Y, currentPoint.Y);
|
|
||||||
double width = Math.Abs(currentPoint.X - _rectangleStart.X);
|
|
||||||
double height = Math.Abs(currentPoint.Y - _rectangleStart.Y);
|
|
||||||
|
|
||||||
_selectionRectangle.Width = width;
|
|
||||||
_selectionRectangle.Height = height;
|
|
||||||
Canvas.SetLeft(_selectionRectangle, left);
|
|
||||||
Canvas.SetTop(_selectionRectangle, top);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FinishRectangleSelection(Point currentPoint)
|
|
||||||
{
|
|
||||||
if (_selectionRectangle != null)
|
|
||||||
{
|
|
||||||
var left = Canvas.GetLeft(_selectionRectangle);
|
|
||||||
var top = Canvas.GetTop(_selectionRectangle);
|
|
||||||
var right = left + _selectionRectangle.Width;
|
|
||||||
var bottom = top + _selectionRectangle.Height;
|
|
||||||
var selectionBounds = new Rect(new Point(left, top), new Point(right, bottom));
|
|
||||||
|
|
||||||
var itemsToProcess = _canvas.Children.OfType<UserControl>().Where(child => child is IDataContainer).ToList();
|
|
||||||
|
|
||||||
foreach (var child in itemsToProcess)
|
|
||||||
{
|
|
||||||
var childBounds = GetElementBounds(child);
|
|
||||||
if (selectionBounds.Contains(childBounds) || selectionBounds.IntersectsWith(childBounds))
|
|
||||||
{
|
|
||||||
if (child.DataContext is osBase osObject)
|
|
||||||
{
|
|
||||||
SelectObject(osObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_canvas.Children.Remove(_selectionRectangle);
|
|
||||||
_selectionRectangle = null;
|
|
||||||
|
|
||||||
UpdateSelectionVisuals();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Rect GetElementBounds(FrameworkElement element)
|
|
||||||
{
|
|
||||||
var bounds = VisualTreeHelper.GetDescendantBounds(element);
|
|
||||||
var transform = element.TransformToAncestor(_canvas);
|
|
||||||
return transform.TransformBounds(bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateRectangleSelection(Point currentPoint)
|
|
||||||
{
|
|
||||||
if (_selectionRectangle != null)
|
|
||||||
{
|
|
||||||
UpdateSelectionRectangle(currentPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,36 +14,46 @@ namespace CtrEditor.ObjetosSim
|
||||||
public partial class osBottGenerator : osBase, IosBase
|
public partial class osBottGenerator : osBase, IosBase
|
||||||
{
|
{
|
||||||
TimerTON_TOFF _TON_TOFF = new TimerTON_TOFF();
|
TimerTON_TOFF _TON_TOFF = new TimerTON_TOFF();
|
||||||
|
// Otros datos y métodos relevantes para la simulación
|
||||||
|
|
||||||
private float TiempoRestante;
|
private float TiempoRestante;
|
||||||
|
private osBotella UltimaBotella;
|
||||||
|
|
||||||
public static string NombreClase()
|
public static string NombreClase()
|
||||||
{
|
{
|
||||||
return "BottGenerator";
|
return "BottGenerator";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string nombre = NombreClase();
|
private string nombre = NombreClase();
|
||||||
public override string Nombre { get => nombre; set => SetProperty(ref nombre, value); }
|
public override string Nombre
|
||||||
|
{
|
||||||
|
get => nombre;
|
||||||
|
set => SetProperty(ref nombre, value);
|
||||||
|
}
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private float offsetLeftSalida;
|
private float offsetLeftSalida;
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private float offsetTopSalida;
|
private float offsetTopSalida;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
[property: Description("The bottle will be destroyed if fall outside transport.")]
|
[property: Description("The bottle will be destroyed if fall outside transport.")]
|
||||||
[property: Category("Enable to Run:")]
|
[property: Category("Enable to Run:")]
|
||||||
private bool preserve_Outside_Transport;
|
private bool preserve_Outside_Transport;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
[property: Description("PLC tag for consense to run. 1 => always")]
|
[property: Description("PLC tag for consense to run. 1 => always")]
|
||||||
[property: Category("Enable to Run:")]
|
[property: Category("Enable to Run:")]
|
||||||
private string tag_consenso;
|
private string tag_consenso;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
[property: Description("Consense to run.")]
|
[property: Description("Consense to run.")]
|
||||||
[property: Category("Enable to Run:")]
|
[property: Category("Enable to Run:")]
|
||||||
private bool consenso;
|
private bool consenso;
|
||||||
|
|
||||||
partial void OnConsensoChanged(bool value)
|
partial void OnConsensoChanged(bool value)
|
||||||
{
|
{
|
||||||
_TON_TOFF.Senal = value;
|
_TON_TOFF.Senal = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
[property: Description("Consense is Normally close.")]
|
[property: Description("Consense is Normally close.")]
|
||||||
[property: Category("Enable to Run:")]
|
[property: Category("Enable to Run:")]
|
||||||
|
@ -64,8 +74,10 @@ namespace CtrEditor.ObjetosSim
|
||||||
[property: Description("Filter OUT signal.")]
|
[property: Description("Filter OUT signal.")]
|
||||||
[property: Category("Enable to Run:")]
|
[property: Category("Enable to Run:")]
|
||||||
bool filter_Output;
|
bool filter_Output;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private float botellas_hora;
|
private float botellas_hora;
|
||||||
|
|
||||||
partial void OnBotellas_horaChanged(float value)
|
partial void OnBotellas_horaChanged(float value)
|
||||||
{
|
{
|
||||||
Botellas_segundo = value / 3600;
|
Botellas_segundo = value / 3600;
|
||||||
|
@ -73,6 +85,7 @@ namespace CtrEditor.ObjetosSim
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private float botellas_segundo;
|
private float botellas_segundo;
|
||||||
|
|
||||||
partial void OnBotellas_segundoChanged(float value)
|
partial void OnBotellas_segundoChanged(float value)
|
||||||
{
|
{
|
||||||
Botellas_hora = value * 3600;
|
Botellas_hora = value * 3600;
|
||||||
|
@ -82,6 +95,21 @@ namespace CtrEditor.ObjetosSim
|
||||||
private float velocidad_actual_percentual;
|
private float velocidad_actual_percentual;
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private float diametro_botella;
|
private float diametro_botella;
|
||||||
|
|
||||||
|
|
||||||
|
public osBottGenerator()
|
||||||
|
{
|
||||||
|
Ancho = 0.30f;
|
||||||
|
Alto = 0.30f;
|
||||||
|
Angulo = 0;
|
||||||
|
Velocidad_actual_percentual = 100;
|
||||||
|
Diametro_botella = 0.1f;
|
||||||
|
Botellas_hora = 10000;
|
||||||
|
Filtro_consenso_ON_s = 1;
|
||||||
|
Filtro_consenso_OFF_s = 0.1f;
|
||||||
|
Preserve_Outside_Transport = false;
|
||||||
|
}
|
||||||
|
|
||||||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
||||||
{
|
{
|
||||||
if (Consenso_NC)
|
if (Consenso_NC)
|
||||||
|
@ -90,68 +118,67 @@ namespace CtrEditor.ObjetosSim
|
||||||
Consenso = LeerBitTag(Tag_consenso);
|
Consenso = LeerBitTag(Tag_consenso);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HayEspacioParaNuevaBotella(float X, float Y)
|
|
||||||
{
|
|
||||||
float radioMinimo = Diametro_botella / 4; // Distancia mínima entre centros
|
|
||||||
float radioMinimoCuadrado = radioMinimo * radioMinimo;
|
|
||||||
|
|
||||||
// Buscar todas las botellas cercanas
|
|
||||||
foreach (var obj in _mainViewModel.ObjetosSimulables)
|
|
||||||
{
|
|
||||||
if (obj is osBotella)
|
|
||||||
{
|
|
||||||
float distanciaCuadrada = (float)(Math.Pow(obj.Left - X, 2) + Math.Pow(obj.Top - Y, 2));
|
|
||||||
if (distanciaCuadrada < radioMinimoCuadrado)
|
|
||||||
{
|
|
||||||
return false; // Hay una botella demasiado cerca
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true; // No hay botellas cercanas
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateControl(int elapsedMilliseconds)
|
public override void UpdateControl(int elapsedMilliseconds)
|
||||||
{
|
{
|
||||||
bool habilitado;
|
bool habilitado;
|
||||||
|
|
||||||
_TON_TOFF.Tiempo_ON_s = Filtro_consenso_ON_s;
|
_TON_TOFF.Tiempo_ON_s = Filtro_consenso_ON_s;
|
||||||
_TON_TOFF.Tiempo_OFF_s = Filtro_consenso_OFF_s;
|
_TON_TOFF.Tiempo_OFF_s = Filtro_consenso_OFF_s;
|
||||||
|
|
||||||
if (Consenso_Filtrado)
|
if (Consenso_Filtrado)
|
||||||
habilitado = _TON_TOFF.SenalFiltrada();
|
habilitado = _TON_TOFF.SenalFiltrada();
|
||||||
else
|
else
|
||||||
habilitado = Consenso;
|
habilitado = Consenso;
|
||||||
|
|
||||||
Filter_Output = habilitado;
|
Filter_Output = habilitado;
|
||||||
|
|
||||||
if (habilitado && Velocidad_actual_percentual > 0)
|
if (habilitado && Velocidad_actual_percentual > 0)
|
||||||
{
|
{
|
||||||
if (PrimeraActualizacion)
|
|
||||||
{
|
|
||||||
TiempoEntreBotellas = 3600 / (Botellas_hora * (Velocidad_actual_percentual / 100.0f));
|
|
||||||
TiempoRestante = TiempoEntreBotellas;
|
|
||||||
PrimeraActualizacion = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TiempoRestante -= elapsedMilliseconds / 1000.0f;
|
TiempoRestante -= elapsedMilliseconds / 1000.0f;
|
||||||
if (TiempoRestante <= 0)
|
if (TiempoRestante <= 0)
|
||||||
{
|
{
|
||||||
|
bool BotellaCreada = false;
|
||||||
var X = Left + OffsetLeftSalida;
|
var X = Left + OffsetLeftSalida;
|
||||||
var Y = Top + OffsetTopSalida;
|
var Y = Top + OffsetTopSalida;
|
||||||
|
|
||||||
if (HayEspacioParaNuevaBotella(X, Y))
|
if (UltimaBotella != null && UltimaBotella.RemoverDesdeSimulacion)
|
||||||
|
UltimaBotella = null;
|
||||||
|
|
||||||
|
if (UltimaBotella == null)
|
||||||
{
|
{
|
||||||
|
// No hay botellas, se puede crear una nueva directamente
|
||||||
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
|
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
|
||||||
((osBotella)nuevaBotella).Diametro = Diametro_botella;
|
((osBotella)nuevaBotella).Diametro = Diametro_botella;
|
||||||
((osBotella)nuevaBotella).Preserve_Outside_Transport = Preserve_Outside_Transport;
|
((osBotella)nuevaBotella).Preserve_Outside_Transport = Preserve_Outside_Transport;
|
||||||
|
((osBotella)nuevaBotella).AutoCreated = true;
|
||||||
nuevaBotella.AutoCreated = true;
|
nuevaBotella.AutoCreated = true;
|
||||||
|
UltimaBotella = (osBotella)nuevaBotella;
|
||||||
// Recalcular el tiempo entre botellas por si cambió la velocidad
|
BotellaCreada = true;
|
||||||
TiempoEntreBotellas = 3600 / (Botellas_hora * (Velocidad_actual_percentual / 100.0f));
|
|
||||||
TiempoRestante = TiempoEntreBotellas;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Calcular la distancia entre el centro de la última botella y la nueva posición
|
||||||
|
float distancia = (float)Math.Sqrt(Math.Pow(UltimaBotella.Left - X, 2) + Math.Pow(UltimaBotella.Top - Y, 2));
|
||||||
|
float distanciaMinima = Diametro_botella / 4; // Asumiendo que el diámetro de la nueva botella es similar
|
||||||
|
|
||||||
|
if (distancia > distanciaMinima)
|
||||||
|
{
|
||||||
|
osBotella nuevaBotella = (osBotella)_mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
|
||||||
|
nuevaBotella.Diametro = Diametro_botella;
|
||||||
|
((osBotella)nuevaBotella).Preserve_Outside_Transport = Preserve_Outside_Transport;
|
||||||
|
nuevaBotella.AutoCreated = true;
|
||||||
|
UltimaBotella = nuevaBotella;
|
||||||
|
BotellaCreada = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (BotellaCreada)
|
||||||
|
TiempoRestante += 3600 / (Botellas_hora * (Velocidad_actual_percentual / 100.0f));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PrimeraActualizacion = true;
|
TiempoRestante = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,13 +189,6 @@ namespace CtrEditor.ObjetosSim
|
||||||
base.ucLoaded();
|
base.ucLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
private float TiempoEntreBotellas; // Nuevo: almacena el intervalo calculado
|
|
||||||
private bool PrimeraActualizacion = true; // Nuevo: flag para la primera actualización
|
|
||||||
public override void SimulationStop()
|
|
||||||
{
|
|
||||||
PrimeraActualizacion = true; // Reset del flag al detener
|
|
||||||
base.SimulationStop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class ucBottGenerator : UserControl, IDataContainer
|
public partial class ucBottGenerator : UserControl, IDataContainer
|
||||||
|
@ -182,24 +202,19 @@ namespace CtrEditor.ObjetosSim
|
||||||
this.Loaded += OnLoaded;
|
this.Loaded += OnLoaded;
|
||||||
this.Unloaded += OnUnloaded;
|
this.Unloaded += OnUnloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Datos?.ucLoaded();
|
Datos?.ucLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Datos?.ucUnLoaded();
|
Datos?.ucUnLoaded();
|
||||||
}
|
}
|
||||||
|
public void Highlight(bool State) { }
|
||||||
public void Highlight(bool State)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ZIndexEnum ZIndex_Base()
|
public ZIndexEnum ZIndex_Base()
|
||||||
{
|
{
|
||||||
return ZIndexEnum.Generadores;
|
return ZIndexEnum.Generadores;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -202,15 +202,10 @@ namespace CtrEditor.ObjetosSim
|
||||||
if (DB_Motor == 0)
|
if (DB_Motor == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Add timestamp to trace when the read occurs
|
// Read ControlWord in one operation
|
||||||
var timestamp = DateTime.Now;
|
int controlWord = plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ControlWord") ?? 0;
|
||||||
|
|
||||||
// Read ControlWord and track the raw response
|
|
||||||
var rawResponse = plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ControlWord");
|
|
||||||
int controlWord = rawResponse ?? 0;
|
|
||||||
var control = VMMotorBitPacker.UnpackControlWord(controlWord);
|
var control = VMMotorBitPacker.UnpackControlWord(controlWord);
|
||||||
|
|
||||||
|
|
||||||
// Update local state from ControlWord
|
// Update local state from ControlWord
|
||||||
OUT_Run = control.run;
|
OUT_Run = control.run;
|
||||||
OUT_Stop = control.stop;
|
OUT_Stop = control.stop;
|
||||||
|
@ -238,7 +233,6 @@ namespace CtrEditor.ObjetosSim
|
||||||
if (Data.VFD_Trip_NC)
|
if (Data.VFD_Trip_NC)
|
||||||
STATUS_VFD_Trip = !STATUS_VFD_Trip;
|
STATUS_VFD_Trip = !STATUS_VFD_Trip;
|
||||||
|
|
||||||
|
|
||||||
// Pack all status bits and speed into StatusWord
|
// Pack all status bits and speed into StatusWord
|
||||||
int statusWord = VMMotorBitPacker.PackStatusWord(
|
int statusWord = VMMotorBitPacker.PackStatusWord(
|
||||||
_STATUS_VFD_Ready,
|
_STATUS_VFD_Ready,
|
||||||
|
|
|
@ -128,43 +128,6 @@ namespace CtrEditor.Simulacion
|
||||||
return verticesList;
|
return verticesList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float CalculateAngleOverlap(Fixture bottle)
|
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyCurveEffect(Fixture bottle)
|
public void ApplyCurveEffect(Fixture bottle)
|
||||||
{
|
{
|
||||||
Vector2 centerToBottle = bottle.Body.Position - Body.Position;
|
Vector2 centerToBottle = bottle.Body.Position - Body.Position;
|
||||||
|
@ -172,46 +135,17 @@ namespace CtrEditor.Simulacion
|
||||||
|
|
||||||
if (distanceToCenter >= _innerRadius && distanceToCenter <= _outerRadius)
|
if (distanceToCenter >= _innerRadius && distanceToCenter <= _outerRadius)
|
||||||
{
|
{
|
||||||
// Calculate overlap percentage
|
// Calcular la velocidad tangencial
|
||||||
float overlapPercentage = CalculateAngleOverlap(bottle);
|
float speedMetersPerSecond = Speed / 60.0f;
|
||||||
|
float angularVelocity = speedMetersPerSecond / distanceToCenter;
|
||||||
|
|
||||||
// Only apply effect if there's overlap
|
// Vector tangente (perpendicular al radio)
|
||||||
if (overlapPercentage > 0)
|
Vector2 tangent = new Vector2(-centerToBottle.Y, centerToBottle.X);
|
||||||
{
|
tangent.Normalize();
|
||||||
// Calculate the tangential velocity
|
|
||||||
float speedMetersPerSecond = Speed / 60.0f;
|
|
||||||
|
|
||||||
// Vector tangent (perpendicular to radius)
|
// Velocidad deseada
|
||||||
Vector2 tangent = new Vector2(-centerToBottle.Y, centerToBottle.X);
|
Vector2 desiredVelocity = tangent * angularVelocity * distanceToCenter;
|
||||||
tangent.Normalize();
|
bottle.Body.LinearVelocity = desiredVelocity;
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -640,9 +574,8 @@ namespace CtrEditor.Simulacion
|
||||||
foreach (var transporte in ListOnTransports)
|
foreach (var transporte in ListOnTransports)
|
||||||
{
|
{
|
||||||
if (transporte is simTransporte conveyorRect)
|
if (transporte is simTransporte conveyorRect)
|
||||||
ApplyConveyorEffect(deltaTime_s, conveyorRect);
|
if (ApplyConveyorEffect(deltaTime_s, conveyorRect))
|
||||||
// if (ApplyConveyorEffect(deltaTime_s, conveyorRect))
|
break;
|
||||||
// break;
|
|
||||||
if (transporte is simCurve conveyorCurve)
|
if (transporte is simCurve conveyorCurve)
|
||||||
conveyorCurve.ApplyCurveEffect(Body.FixtureList[0]);
|
conveyorCurve.ApplyCurveEffect(Body.FixtureList[0]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue