Compare commits

..

2 Commits

Author SHA1 Message Date
Miguel 0410c87e93 Agregado del TextPlate 2024-06-04 17:33:00 +02:00
Miguel 1ce0371d18 Trabajando con los decimales 2024-06-02 19:14:35 +02:00
24 changed files with 784 additions and 218 deletions

View File

@ -10,7 +10,11 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>True</Optimize>
<Optimize>False</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<Optimize>False</Optimize>
</PropertyGroup>
<ItemGroup>
@ -40,6 +44,7 @@
<None Remove="Icons\start.png" />
<None Remove="Icons\stop.png" />
<None Remove="imagenes\filler.png" />
<None Remove="imagenes\gear.png" />
<None Remove="imagenes\motorNegro.png" />
<None Remove="imagenes\motorVerde.png" />
<None Remove="motor2.png" />
@ -92,6 +97,7 @@
<Resource Include="Icons\start.png" />
<Resource Include="Icons\stop.png" />
<Resource Include="imagenes\filler.png" />
<Resource Include="imagenes\gear.png" />
<Resource Include="imagenes\motorNegro.png" />
<Resource Include="imagenes\motorVerde.png" />
<Resource Include="imagenes\tank.png" />

View File

@ -15,6 +15,7 @@ using System.Diagnostics;
using System.Reflection;
using CommunityToolkit.Mvvm.ComponentModel;
using Xceed.Wpf.Toolkit.PropertyGrid;
using System.Text.RegularExpressions;
namespace CtrEditor
@ -282,7 +283,28 @@ namespace CtrEditor
var NuevoObjetoDuplicado = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
if (NuevoObjetoDuplicado != null)
{
NuevoObjetoDuplicado.Nombre += "_Duplicado";
string nombre = NuevoObjetoDuplicado.Nombre;
// Expresión regular para identificar un nombre que termina con _número
Regex regex = new Regex(@"_(\d+)$");
if (regex.IsMatch(nombre))
{
// Extraer el número actual y sumarle 1
var match = regex.Match(nombre);
int numeroActual = int.Parse(match.Groups[1].Value);
int nuevoNumero = numeroActual + 1;
// Reemplazar el número en el nombre
nombre = regex.Replace(nombre, $"_{nuevoNumero}");
}
else
{
// Si no termina con _número, añadir _1
nombre += "_1";
}
NuevoObjetoDuplicado.Nombre = nombre;
NuevoObjetoDuplicado.Left += 0.5f;
NuevoObjetoDuplicado.Top += 0.5f;
ObjetosSimulables.Add(NuevoObjetoDuplicado);

View File

@ -166,20 +166,55 @@
<!-- PanelEdicion -->
<xctk:PropertyGrid Grid.Row="2" Margin="5" x:Name="PanelEdicion" AutoGenerateProperties="False" SelectedObject="{Binding}">
<xctk:PropertyGrid.EditorDefinitions>
<!-- String -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type sys:String}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<TextBox Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Text="{Binding Value}" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- Velocidad -->
<xctk:EditorTemplateDefinition TargetProperties="VelocidadActual">
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<xctk:SingleUpDown Increment="0.01" Background="Beige" Text="{Binding Value, Converter={StaticResource floatFormatter}}" FontWeight="Bold"/>
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- Float -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type sys:Single}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, Converter={StaticResource floatFormatter}}"/>
<xctk:SingleUpDown Increment="0.01" Text="{Binding Value, Converter={StaticResource floatFormatter}}" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- Double -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type sys:Double}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, Converter={StaticResource doubleFormatter}}"/>
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
</xctk:PropertyGrid.EditorDefinitions>
</xctk:PropertyGrid.EditorDefinitions>
</xctk:PropertyGrid>

View File

@ -0,0 +1,29 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTextPlate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osTextPlate Color_Titulo="Black" Alto_Titulo="0.1"/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Height="{Binding Alto_Titulo, Converter={StaticResource MeterToPixelConverter}}" >
<Label Content="{Binding Titulo}"
VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold"
Foreground="{Binding Color_Titulo, Converter={StaticResource ColorToBrushConverter}}"/>
</Viewbox>
<Rectangle Grid.Row="1" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}" />
</Grid>
</UserControl>

View File

@ -0,0 +1,153 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
using System.Windows.Input;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTextPlate.xaml
/// </summary>
///
public partial class osTextPlate : osBase, IosBase
{
public static string NombreClase()
{
return "Plate";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
Color color;
[ObservableProperty]
Color color_Titulo;
[ObservableProperty]
string titulo;
[ObservableProperty]
public float alto_Titulo;
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
public override void TopChanging(float oldValue, float newValue) {
UpdateChildsTop(newValue - oldValue);
}
public override void LeftChanging(float oldValue, float newValue) {
UpdateChildsLeft(newValue - oldValue);
}
protected void UpdateChildsTop(float offsetY)
{
if (!string.IsNullOrEmpty(Nombre) && _mainViewModel != null)
{
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable != this && objetoSimulable.Group_Panel == Nombre)
{
objetoSimulable.Top += offsetY;
}
}
}
}
protected void UpdateChildsLeft(float offsetX)
{
if (!string.IsNullOrEmpty(Nombre) && _mainViewModel != null)
{
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable != this && objetoSimulable.Group_Panel == Nombre)
{
objetoSimulable.Left += offsetX;
}
}
}
}
public osTextPlate()
{
Ancho = 0.5f;
Alto = 0.5f;
Alto_Titulo = 0.2f;
Color = Colors.Gray;
Titulo = "Titulo";
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
}
}
public partial class ucTextPlate : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTextPlate()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osTextPlate datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(height);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osTextPlate datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -0,0 +1,24 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBottGenerator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.DataContext>
<vm:osFiller/>
</UserControl.DataContext>
<Grid>
<Image Source="/imagenes/gear.png"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
<Image.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" />
</Image.RenderTransform>
</Image>
</Grid>
</UserControl>

View File

@ -0,0 +1,208 @@
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBottGenerator.xaml
/// </summary>
public partial class osBottGenerator : osBase, IosBase
{
TimerTON_TOFF _TON_TOFF = new TimerTON_TOFF();
// Otros datos y métodos relevantes para la simulación
private float TiempoRestante;
private osBotella UltimaBotella;
public static string NombreClase()
{
return "BottGenerator";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private float offsetLeftSalida;
[ObservableProperty]
private float offsetTopSalida;
[ObservableProperty]
private string tag_consenso;
[ObservableProperty]
private bool consenso;
partial void OnConsensoChanged(bool value)
{
_TON_TOFF.Senal = value;
}
[ObservableProperty]
private bool consenso_NC;
[ObservableProperty]
private bool consenso_Filtrado;
[ObservableProperty]
float filtro_consenso_s;
[ObservableProperty]
private float botellas_hora;
partial void OnBotellas_horaChanged(float value)
{
Botellas_segundo = value / 3600;
}
[ObservableProperty]
private float botellas_segundo;
partial void OnBotellas_segundoChanged(float value)
{
Botellas_hora = value * 3600;
}
[ObservableProperty]
private float velocidad_actual_percentual;
[ObservableProperty]
private float diametro_botella;
[ObservableProperty]
private float ancho;
[ObservableProperty]
private float alto;
[ObservableProperty]
private float angulo;
public osBottGenerator()
{
Ancho = 0.30f;
Alto = 0.30f;
Angulo = 0;
Velocidad_actual_percentual = 0;
Diametro_botella = 0.1f;
Botellas_hora = 10000;
Filtro_consenso_s = 1;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
if (Consenso_NC)
Consenso = !LeerBitTag(Tag_consenso);
else
Consenso = LeerBitTag(Tag_consenso);
}
public override void UpdateControl(int elapsedMilliseconds)
{
bool habilitado;
_TON_TOFF.Tiempo_ON_s = _TON_TOFF.Tiempo_OFF_s = Filtro_consenso_s;
if (Consenso_Filtrado)
habilitado = _TON_TOFF.SenalFiltrada();
else
habilitado = Consenso;
if (habilitado && Velocidad_actual_percentual > 0)
{
TiempoRestante -= elapsedMilliseconds / 1000.0f;
if (TiempoRestante <= 0)
{
TiempoRestante += 3600 / (Botellas_hora * (Velocidad_actual_percentual / 100.0f));
var X = Left + OffsetLeftSalida;
var Y = Top + OffsetTopSalida;
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);
((osBotella)nuevaBotella).Diametro = Diametro_botella;
nuevaBotella.AutoCreated = true;
UltimaBotella = (osBotella)nuevaBotella;
}
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 / 2; // Asumiendo que el diámetro de la nueva botella es similar
if (distancia > distanciaMinima)
{
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
((osBotella)nuevaBotella).Diametro = Diametro_botella;
nuevaBotella.AutoCreated = true;
UltimaBotella = (osBotella)nuevaBotella;
}
}
}
}
else
{
TiempoRestante = 0;
}
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucBottGenerator : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBottGenerator()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osBottGenerator datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osBottGenerator datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -28,18 +28,37 @@
</UserControl.DataContext>
<Grid>
<Canvas>
<StackPanel x:Name="RectanglesContainer">
<StackPanel.RenderTransform>
<Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="{Binding Angulo}"/>
</StackPanel.RenderTransform>
<Rectangle x:Name="GuiaSuperior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"/>
<Rectangle x:Name="Transporte" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"
Fill="{StaticResource BeltBrush}"/>
<Rectangle x:Name="GuiaInferior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue"/>
<TranslateTransform/>
</TransformGroup>
</Canvas.RenderTransform>
<StackPanel x:Name="RectanglesContainer">
<Rectangle x:Name="GuiaSuperior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"/>
<Rectangle x:Name="Transporte" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"
Fill="{StaticResource BeltBrush}"/>
<Rectangle x:Name="GuiaInferior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"/>
</StackPanel>
<Viewbox Canvas.Top="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
<Label Content="{Binding Nombre}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" FontSize="18" Opacity="0.5"/>
</Viewbox>
</Canvas>
</Grid>
</UserControl>

View File

@ -1,5 +1,6 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
@ -60,6 +61,9 @@ namespace CtrEditor.ObjetosSim
}
[ObservableProperty]
Color color = Colors.Blue;
[ObservableProperty]
public string motor;
@ -110,10 +114,9 @@ namespace CtrEditor.ObjetosSim
UpdateOrCreateLine(Guia_Superior, uc.GuiaSuperior);
UpdateOrCreateLine(Guia_Inferior, uc.GuiaInferior) ;
SimGeometria.DistanceGuide2Guide = Alto;
SimGeometria.Speed = VelocidadActual;
SimGeometria.DistanceGuide2Guide = Alto;
SimGeometria.isBrake = esFreno;
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
SetSpeed();
}
}
@ -123,7 +126,7 @@ namespace CtrEditor.ObjetosSim
Ancho = 1;
Alto = 0.10f;
AltoGuia = 0.03f;
Distance = 0.01f;
Distance = 0.01f;
}
public override void UpdateGeometryStart()

View File

@ -39,7 +39,7 @@
</UserControl.Resources>
<UserControl.DataContext>
<vm:osTransporteGuiasUnion/>
<vm:osTransporteGuiasUnion Color="Red"/>
</UserControl.DataContext>
<Grid>
@ -67,14 +67,13 @@
AnchoCentro="{Binding AnchoCentral, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Altura="{Binding Alto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
AltoGuia="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Canvas.Top="{Binding Distance, Converter={StaticResource MeterToPixelConverter},ConverterParameter=-1}"
Color="Blue"/>
Canvas.Top="{Binding Distance, Converter={StaticResource MeterToPixelConverter},ConverterParameter=-1}" Color="{Binding Color}"/>
<uc:ThreeLinesControl x:Name="GuiaInferior" AnchoRecto="{Binding AnchoRecto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
AnchoCentro="{Binding AnchoCentral, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Altura="{Binding Alto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
AltoGuia="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Color="Blue">
Color="{Binding Color}">
<Canvas.Top>
<MultiBinding Converter="{StaticResource SumConverter}">

View File

@ -36,7 +36,7 @@ namespace CtrEditor.ObjetosSim
}
[ObservableProperty]
string color;
Color color;
[ObservableProperty]
public string motorA;

View File

@ -9,25 +9,35 @@
<UserControl.DataContext>
<vm:osVMmotorSim ImageSource_oculta="/imagenes/motorNegro.png" />
<vm:osVMmotorSim ImageSource_oculta="/imagenes/motorNegro.png"/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid RenderTransformOrigin="0,0">
<Grid.RenderTransform>
<TransformGroup>
<RotateTransform Angle="{Binding Angulo}"/>
</TransformGroup>
</Grid.RenderTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Content="{Binding Nombre}"
<Image Grid.Column="0" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
<Viewbox Grid.Column="1" Stretch="Uniform">
<Label Content="{Binding Nombre}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
Foreground="Black"/>
<Image Grid.Row="1" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
Foreground="Black"
Opacity="0.5"/>
</Viewbox>
</Grid>
</UserControl>

View File

@ -35,6 +35,9 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty]
public ImageSource imageSource_oculta;
[ObservableProperty]
public float angulo;
[ObservableProperty]
public float tamano;
[ObservableProperty]
@ -105,6 +108,7 @@ namespace CtrEditor.ObjetosSim
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
OnVelocidadChanged(Velocidad);
}
}
@ -136,7 +140,11 @@ namespace CtrEditor.ObjetosSim
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Rotate(float Angle) {
if (Datos != null)
if (Datos is osVMmotorSim datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{

View File

@ -12,7 +12,15 @@
<vm:osBoton Color="#FFADE6C0" ColorButton_oculto="#FFC72323"/>
</UserControl.DataContext>
<Grid>
<Canvas RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="{Binding Angulo}"/>
<TranslateTransform/>
</TransformGroup>
</Canvas.RenderTransform>
<Border x:Name="BackgroundRectangle"
BorderBrush="Black"
BorderThickness="2"
@ -27,10 +35,18 @@
StrokeThickness="2"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Canvas.Top="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"
MouseLeftButtonUp="Ellipse_MouseLeftButtonUp">
</Ellipse>
</Grid>
<Viewbox Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}">
<Label Content="{Binding Button_Name}"
VerticalAlignment="Top" HorizontalAlignment="Center"
Foreground="{Binding Color_Titulo, Converter={StaticResource ColorToBrushConverter}}"/>
</Viewbox>
</Canvas>
</UserControl>

View File

@ -26,9 +26,15 @@ namespace CtrEditor.ObjetosSim
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
string button_Name;
[ObservableProperty]
public bool tipo_NC;
[ObservableProperty]
Color color_Titulo;
[ObservableProperty]
Color color;
@ -41,6 +47,10 @@ namespace CtrEditor.ObjetosSim
private Color colorButton_oculto;
[ObservableProperty]
public float tamano;
[ObservableProperty]
public float angulo;
[ObservableProperty]
public bool estado;
@ -75,6 +85,8 @@ namespace CtrEditor.ObjetosSim
Tag = "M50.0";
// Set initial color
Color = Colors.LightBlue;
color_Titulo = Colors.Black;
button_Name = "TAG";
}
public override void UpdateGeometryStart()
@ -163,7 +175,10 @@ namespace CtrEditor.ObjetosSim
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Rotate(float Angle) {
if (Datos is osBoton osBotonData)
osBotonData.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{

View File

@ -16,27 +16,29 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<!-- Columna para el Label -->
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<!-- Columna para la Image -->
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<!-- Columna para el Rectangle -->
</Grid.ColumnDefinitions>
<Grid.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" CenterX="0" CenterY="0"/>
</Grid.RenderTransform>
<!-- Label en la primera columna -->
<Label Content="{Binding Nombre}" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0" Foreground="{Binding Color}" >
</Label>
<Viewbox Grid.Column="0" Stretch="UniformToFill">
<Label Content="{Binding Nombre}"
VerticalAlignment="Center" HorizontalAlignment="Center" Opacity="0.8" FontWeight="Bold" Foreground="{Binding Color}"/>
</Viewbox>
<Image Source="/Icons/fotocelula.png"
Width="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=3}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=3}"
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,0,0" Grid.Column="1"/>
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.1}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=2}"
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,0,0" Grid.Column="1"/>
<Rectangle x:Name="Photocell" Grid.Column="2" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}">
<Rectangle x:Name="Photocell" Grid.Column="2" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.9}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}">
<Rectangle.Fill>
<VisualBrush x:Name="MovingPattern" TileMode="Tile" Viewport="0,0,3,3" ViewportUnits="Absolute" Viewbox="0,0,3,3" ViewboxUnits="Absolute">
<VisualBrush.Visual>
@ -46,8 +48,8 @@
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
<!-- No se aplica la transformación aquí -->
</Rectangle>
</Grid>
</UserControl>

View File

@ -31,8 +31,7 @@ namespace CtrEditor.ObjetosSim
public float min_OUT_Scaled;
[ObservableProperty]
public float max_OUT_Scaled;
[TypeConverter(typeof(FloatToStringConverter))]
[ObservableProperty]
public float value;

View File

@ -92,39 +92,8 @@ namespace CtrEditor.ObjetosSim
// Add properties dynamically based on attributes
var properties = TypeDescriptor.GetProperties(selectedObject)
.Cast<PropertyDescriptor>()
.Where(prop => !prop.Attributes.OfType<HiddenAttribute>().Any());
//// Create the DataTemplate for float and double editors
//var doubleFactory = new FrameworkElementFactory(typeof(DoubleUpDown));
//doubleFactory.SetValue(DoubleUpDown.FormatStringProperty, "F3");
//doubleFactory.SetValue(DoubleUpDown.IncrementProperty, 0.001);
//doubleFactory.SetValue(DoubleUpDown.MaximumProperty, 200000.599);
//doubleFactory.SetBinding(DoubleUpDown.ValueProperty, new Binding("Value")
//{
// Mode = BindingMode.TwoWay,
// UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
//});
//var doubleTemplate = new DataTemplate { VisualTree = doubleFactory };
//// Add editor definitions for float and double
//var floatEditorDefinition = new EditorDefinition
//{
// EditorTemplate = doubleTemplate,
// TargetType = typeof(float)
//};
//var doubleEditorDefinition = new EditorDefinition
//{
// EditorTemplate = doubleTemplate,
// TargetType = typeof(double)
//};
//propertyGrid.EditorDefinitions.Add(floatEditorDefinition);
//propertyGrid.EditorDefinitions.Add(doubleEditorDefinition);
.Where(prop => !prop.Attributes.OfType<HiddenAttribute>().Any()).OrderBy(prop => prop.Name);
foreach (var property in properties)
{
var displayNameAttr = property.Attributes.OfType<DisplayNameAttribute>().FirstOrDefault();
@ -143,8 +112,11 @@ namespace CtrEditor.ObjetosSim
{
propertyDefinition.DisplayName = property.Name.Replace("_", " ");
}
propertyGrid.PropertyDefinitions.Add(propertyDefinition);
if (property.PropertyType == typeof(double) || property.PropertyType == typeof(float) || property.PropertyType == typeof(string) || property.PropertyType == typeof(int) ||
property.PropertyType == typeof(bool) || property.PropertyType == typeof(Color))
{
propertyGrid.PropertyDefinitions.Add(propertyDefinition);
}
}
}

View File

@ -23,7 +23,7 @@ namespace CtrEditor.ObjetosSim.UserControls
{
InitializeComponent();
this.Loaded += ThreeLinesControl_Loaded;
Color = Brushes.Gray;
//Color = Colors.Gray;
}
private void ThreeLinesControl_Loaded(object sender, RoutedEventArgs e)
@ -31,8 +31,14 @@ namespace CtrEditor.ObjetosSim.UserControls
CreateOrUpdateRectangles();
}
[ObservableProperty]
private Brush color;
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(Color), typeof(ThreeLinesControl), new PropertyMetadata(Colors.Red, OnDimensionsChanged));
public double AnchoRecto
{
@ -85,7 +91,7 @@ namespace CtrEditor.ObjetosSim.UserControls
{
Width = AnchoRecto,
Height = AltoGuia,
Fill = Color
Fill = new SolidColorBrush(Color)
};
Canvas.SetLeft(_liz, 0);
Canvas.SetTop(_liz, -AltoGuia / 2);
@ -96,7 +102,7 @@ namespace CtrEditor.ObjetosSim.UserControls
{
Width = CalculateLcWidth(),
Height = AltoGuia,
Fill = Color,
Fill = new SolidColorBrush(Color),
RenderTransformOrigin = new Point(0, 0.5)
};
_lc.RenderTransform = new RotateTransform(GetRotationAngle(AnchoCentro, Altura));
@ -109,7 +115,7 @@ namespace CtrEditor.ObjetosSim.UserControls
{
Width = AnchoRecto,
Height = AltoGuia,
Fill = Color
Fill = new SolidColorBrush(Color)
};
Canvas.SetLeft(_lde, AnchoRecto + AnchoCentro);
Canvas.SetTop(_lde, Altura - AltoGuia / 2);
@ -120,12 +126,14 @@ namespace CtrEditor.ObjetosSim.UserControls
// Actualizar Liz
_liz.Width = AnchoRecto;
_liz.Height = AltoGuia;
_liz.Fill = new SolidColorBrush(Color);
Canvas.SetLeft(_liz, 0);
Canvas.SetTop(_liz, -AltoGuia / 2);
// Actualizar Lc
_lc.Width = CalculateLcWidth();
_lc.Height = AltoGuia;
_lc.Fill = new SolidColorBrush(Color);
_lc.RenderTransform = new RotateTransform(GetRotationAngle(AnchoCentro, Altura));
Canvas.SetLeft(_lc, AnchoRecto);
Canvas.SetTop(_lc, -AltoGuia / 2);
@ -133,6 +141,7 @@ namespace CtrEditor.ObjetosSim.UserControls
// Actualizar Lde
_lde.Width = AnchoRecto;
_lde.Height = AltoGuia;
_lde.Fill = new SolidColorBrush(Color);
Canvas.SetLeft(_lde, AnchoRecto + AnchoCentro);
Canvas.SetTop(_lde, Altura - AltoGuia / 2);
}

View File

@ -54,7 +54,6 @@ namespace CtrEditor.ObjetosSim
}
}
[TypeConverter(typeof(DoubleToStringConverter))]
public abstract partial class osBase : ObservableObject
{
public virtual string Nombre { get; set; } = "osBase";
@ -69,18 +68,33 @@ namespace CtrEditor.ObjetosSim
CanvasSetLeftinMeter(value);
LeftChanged(value);
}
partial void OnLeftChanging(float oldValue, float newValue)
{
LeftChanging(oldValue, newValue);
}
public virtual void LeftChanged(float value) { }
public virtual void LeftChanging(float oldValue, float newValue) { }
[ObservableProperty]
private float top;
[ObservableProperty]
private string group_Panel;
partial void OnTopChanged(float value)
{
CanvasSetTopinMeter(value);
TopChanged(value);
}
partial void OnTopChanging(float oldValue, float newValue)
{
TopChanging(oldValue, newValue);
}
public virtual void TopChanged(float value) { }
public virtual void TopChanging(float oldValue, float newValue) { }
/// <summary>
/// Usado para saber cuando un objeto fue creado manualmente o por algun generador
@ -177,7 +191,6 @@ namespace CtrEditor.ObjetosSim
[JsonIgnore]
public SimulationManagerFP simulationManager;
/// <summary>
/// Prepara la clase para ser serializable poniendo a null los objetos que tienen referencias circulares
/// Luego se debe llamar a RestaurarDatosNoSerializables para restaurar el estado original

View File

@ -1,37 +1,41 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBasicExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
>
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<!-- Define the VisualBrush for the conveyor belt pattern -->
<VisualBrush x:Key="BeltBrush" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10" ViewboxUnits="Absolute">
<VisualBrush.Transform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="Gray" Width="10" Height="10"/>
<Rectangle Fill="DarkGray" Width="10" Height="10" Canvas.Left="10"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osBasicExample />
<vm:osBasicExample Ancho="2"/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding Nombre}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
Foreground="Black"/>
<Image Grid.Row="1" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
</Grid>
</UserControl>
<Canvas>
<Rectangle x:Name="Transporte"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{StaticResource BeltBrush}">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</UserControl>

View File

@ -1,11 +1,14 @@

using CtrEditor.Siemens;
using System.Windows;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media;
using Newtonsoft.Json;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
using System.Windows.Input;
namespace CtrEditor.ObjetosSim
{
/// <summary>
@ -15,80 +18,112 @@ namespace CtrEditor.ObjetosSim
public partial class osBasicExample : osBase, IosBase
{
private osBase _osMotor = null;
// Otros datos y métodos relevantes para la simulación
private simTransporte SimGeometria;
public static string NombreClase()
{
return "Ejemplo";
return "Example";
}
private string nombre = NombreClase();
private string nombre = "Transporte TTOP";
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[JsonIgnore]
[ObservableProperty]
public ImageSource imageSource_oculta;
public float velocidadActual;
[ObservableProperty]
public float tamano;
[ObservableProperty]
public float maxRatedHz;
[ObservableProperty]
public float tiempoRampa;
partial void OnTiempoRampaChanged(float value)
partial void OnVelocidadActualChanged(float value)
{
if (value < 0.1f)
value = 0.1f;
tiempoRampa = value;
SetSpeed();
}
[ObservableProperty]
public bool encendido;
[ObservableProperty]
public int pLC_NumeroMotor;
[ObservableProperty]
public float ratio;
bool invertirDireccion;
[ObservableProperty]
public float velocidad;
partial void OnVelocidadChanged(float value)
partial void OnInvertirDireccionChanged(bool value)
{
if (value > 0)
ImageSource_oculta = ImageFromPath("/imagenes/motorVerde.png");
SetSpeed();
if (_visualRepresentation is ucBasicExample uc)
{
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
}
void SetSpeed()
{
if (InvertirDireccion)
SimGeometria?.SetSpeed(-VelocidadActual);
else
ImageSource_oculta = ImageFromPath("/imagenes/motorNegro.png");
SimGeometria?.SetSpeed(VelocidadActual);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
[ObservableProperty]
public string motor;
partial void OnMotorChanged(string value)
{
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
[ObservableProperty]
public float frictionCoefficient;
[ObservableProperty]
public float velMax50hz;
[ObservableProperty]
public float tiempoRampa;
[ObservableProperty]
public bool esMarcha;
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucBasicExample uc)
{
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
SetSpeed();
}
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public osBasicExample()
{
Tamano = 0.30f;
PLC_NumeroMotor = 31;
MaxRatedHz = 100;
TiempoRampa = 3;
ImageSource_oculta = ImageFromPath("/imagenes/motor2.png");
Ancho = 1;
Alto = 0.10f;
}
public override void SimulationStop()
{
// Se llama al detener la simulacion
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Calculamos la velocidad
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
}
else if (Motor.Length > 0)
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public override void ucLoaded()
@ -97,7 +132,19 @@ namespace CtrEditor.ObjetosSim
// crear el objeto de simulacion
ActualizarLeftTop();
if (_visualRepresentation is ucBasicExample uc)
{
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
}
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
}
}
public partial class ucBasicExample : UserControl, IDataContainer
@ -118,7 +165,11 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Resize(float width, float height)
{
if (Datos is osBasicExample datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
@ -127,11 +178,20 @@ namespace CtrEditor.ObjetosSim
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osBasicExample datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
return 1;
}
}
}

View File

@ -101,47 +101,6 @@ namespace CtrEditor
DisplayName = displayName;
}
}
public class DoubleToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double doubleValue)
{
return doubleValue.ToString("0.00", culture);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue && double.TryParse(stringValue, NumberStyles.Float, culture, out double result))
{
return result;
}
return value;
}
}
public class FloatToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is float doubleValue)
{
return doubleValue.ToString("0.00", culture);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue && float.TryParse(stringValue, NumberStyles.Float, culture, out float result))
{
return result;
}
return value;
}
}
public class StringToBrushConverter : IValueConverter
{
@ -404,14 +363,15 @@ namespace CtrEditor
{
if (value is float floatValue)
{
return floatValue.ToString("0.00", culture); // Formatear a dos decimales
var r = floatValue.ToString("0.00", culture).Replace('.', ','); // Formatear a dos decimales
return r;
}
return value; // Devolver el valor original si no es un float
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue && float.TryParse(stringValue, NumberStyles.Float, culture, out float result))
if (value is string stringValue && float.TryParse(stringValue.Replace(',', '.'), NumberStyles.Float, culture, out float result))
{
return result;
}
@ -425,14 +385,14 @@ namespace CtrEditor
{
if (value is double floatValue)
{
return floatValue.ToString("0.00", culture); // Formatear a dos decimales
return floatValue.ToString("0.00", culture).Replace('.', ','); // Formatear a dos decimales
}
return value; // Devolver el valor original si no es un float
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue && double.TryParse(stringValue, NumberStyles.Float, culture, out double result))
if (value is string stringValue && double.TryParse(stringValue.Replace(',', '.'), NumberStyles.Float, culture, out double result))
{
return result;
}

BIN
imagenes/gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB