Compare commits

..

No commits in common. "d1ec3332435718a5772e8a57e4c995a959968d22" and "304bdb06d4693678096b5096fd0562c886fc0943" have entirely different histories.

51 changed files with 97 additions and 4997 deletions

View File

@ -18,12 +18,6 @@ namespace CtrEditor.Controls
UserControlFactory.CargarPropiedadesosDatos(selectedObject, PropertyGridControl); UserControlFactory.CargarPropiedadesosDatos(selectedObject, PropertyGridControl);
} }
// Add a new method to clear properties
public void ClearProperties()
{
UserControlFactory.LimpiarPropiedadesosDatos(PropertyGridControl);
}
public bool IsKeyboardFocusWithin => PropertyGridControl.IsKeyboardFocusWithin; public bool IsKeyboardFocusWithin => PropertyGridControl.IsKeyboardFocusWithin;
private void ImagePathButton_Click(object sender, RoutedEventArgs e) private void ImagePathButton_Click(object sender, RoutedEventArgs e)

View File

@ -77,19 +77,18 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aether.Physics2D" Version="2.2.0" /> <PackageReference Include="Aether.Physics2D" Version="2.1.0" />
<PackageReference Include="ClosedXML" Version="0.105.0-rc" /> <PackageReference Include="ClosedXML" Version="0.104.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Emgu.CV" Version="4.10.0.5680" /> <PackageReference Include="Emgu.CV" Version="4.9.0.5494" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.10.0.5680" /> <PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
<PackageReference Include="Emgu.CV.UI" Version="4.10.0.5680" /> <PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.7.25104.5739" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
<PackageReference Include="LanguageDetection" Version="1.2.0" /> <PackageReference Include="LanguageDetection" Version="1.2.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" /> <PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" /> <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" /> <PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="PaddleOCRSharp" Version="4.5.0.1" />
<PackageReference Include="Tesseract" Version="5.2.0" /> <PackageReference Include="Tesseract" Version="5.2.0" />
<PackageReference Include="Tesseract.Drawing" Version="5.2.0" /> <PackageReference Include="Tesseract.Drawing" Version="5.2.0" />
</ItemGroup> </ItemGroup>
@ -173,11 +172,4 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="paddleocr\cls\inference\" />
<Folder Include="paddleocr\det\inference\" />
<Folder Include="paddleocr\keys\" />
<Folder Include="paddleocr\rec\inference\" />
</ItemGroup>
</Project> </Project>

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,623 +0,0 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>PaddleOCRSharp</name>
</assembly>
<members>
<member name="T:PaddleOCRSharp.EngineBase">
<summary>
Base class for engine objects
</summary>
</member>
<member name="P:PaddleOCRSharp.EngineBase.PaddleOCRdllPath">
<summary>
Custom loading path for PaddleOCR.dll, default is empty. If specified, it needs to be assigned before engine instantiation.
</summary>
</member>
<member name="M:PaddleOCRSharp.EngineBase.#ctor">
<summary>
Initialization
</summary>
</member>
<member name="M:PaddleOCRSharp.EngineBase.GetDllDirectory">
<summary>
Get the current path of the program
</summary>
<returns></returns>
</member>
<member name="M:PaddleOCRSharp.EngineBase.GetRootDirectory">
<summary>
Get the current path of the program
</summary>
<returns></returns>
</member>
<member name="M:PaddleOCRSharp.EngineBase.ImageToBytes(System.Drawing.Image)">
<summary>
Convert Image to Byte[]
</summary>
<param name="image"></param>
<returns></returns>
</member>
<member name="M:PaddleOCRSharp.EngineBase.Dispose">
<summary>
Release memory
</summary>
</member>
<member name="M:PaddleOCRSharp.EngineBase.GetLastError">
<summary>
Get underlying error information
</summary>
<returns></returns>
</member>
<member name="T:PaddleOCRSharp.JsonHelper">
<summary>
Json helper class
</summary>
</member>
<member name="M:PaddleOCRSharp.JsonHelper.DeserializeObject``1(System.String)">
<summary>
Json deserialization
</summary>
<typeparam name="T"></typeparam>
<param name="json"></param>
<returns></returns>
</member>
<member name="T:PaddleOCRSharp.OCRModelConfig">
<summary>
Model configuration object
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRModelConfig.det_infer">
<summary>
det_infer model path
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRModelConfig.cls_infer">
<summary>
cls_infer model path
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRModelConfig.rec_infer">
<summary>
rec_infer model path
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRModelConfig.keys">
<summary>
Full path of ppocr_keys.txt file
</summary>
</member>
<member name="T:PaddleOCRSharp.StructureModelConfig">
<summary>
Table model configuration object
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureModelConfig.table_model_dir">
<summary>
table_model_dir model path
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureModelConfig.table_char_dict_path">
<summary>
Table recognition dictionary
</summary>
</member>
<member name="T:PaddleOCRSharp.OCRParameter">
<summary>
OCR recognition parameters
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.use_gpu">
<summary>
Whether to use GPU; default false
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.gpu_id">
<summary>
GPU id, effective when using GPU; default 0
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.gpu_mem">
<summary>
Requested GPU memory; default 4000
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.cpu_math_library_num_threads">
<summary>
Number of threads for CPU prediction. When the machine has sufficient cores, the higher this value, the faster the prediction; default 10
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.enable_mkldnn">
<summary>
Whether to use mkldnn library; default true
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.det">
<summary>
Whether to perform text detection; default true
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.rec">
<summary>
Whether to perform text recognition; default true
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.cls">
<summary>
Whether to perform text orientation classification; default false
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.max_side_len">
<summary>
When the input image length and width are greater than 960, the image is scaled proportionally so that the longest side is 960; default 960
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.det_db_thresh">
<summary>
Used to filter the binary image predicted by DB. Setting to 0.-0.3 has no significant effect on results; default 0.3
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.det_db_box_thresh">
<summary>
DB post-processing threshold for filtering boxes. If detection has missing boxes, this can be reduced accordingly; default 0.5
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.det_db_unclip_ratio">
<summary>
Represents the tightness of the text box. Smaller values mean the text box is closer to the text; default 1.6
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.use_dilation">
<summary>
Whether to use dilation on the output map, default false
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.det_db_score_mode">
<summary>
true: use polygon box to calculate bbox; false: use rectangle box. Rectangle calculation is faster, polygon boxes are more accurate for curved text areas.
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.visualize">
<summary>
Whether to visualize the results. If true, the prediction results will be saved as an ocr_vis.png file in the current directory. Default false
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.use_angle_cls">
<summary>
Whether to use direction classifier, default false
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.cls_thresh">
<summary>
Score threshold for direction classifier, default 0.9
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.cls_batch_num">
<summary>
Direction classifier batchsize, default 1
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.rec_batch_num">
<summary>
Recognition model batchsize, default 6
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.rec_img_h">
<summary>
Recognition model input image height, default 48
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.rec_img_w">
<summary>
Recognition model input image width, default 320
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.show_img_vis">
<summary>
Whether to display prediction results, default false
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRParameter.use_tensorrt">
<summary>
When using GPU prediction, whether to enable tensorrt, default false
</summary>
</member>
<member name="T:PaddleOCRSharp.ModifyParameter">
<summary>
OCR modifiable parameters
</summary>
</member>
<member name="P:PaddleOCRSharp.ModifyParameter.m_det">
<summary>
Dynamically modify whether to detect. When OCRParameter.det=true, m_det can dynamically turn off the det parameter
</summary>
</member>
<member name="P:PaddleOCRSharp.ModifyParameter.m_rec">
<summary>
Dynamically modify whether to recognize. When OCRParameter.rec=true, m_rec can dynamically turn off the rec parameter
</summary>
</member>
<member name="P:PaddleOCRSharp.ModifyParameter.m_max_side_len">
<summary>
When the input image length and width are greater than 960, the image is scaled proportionally so that the longest side is 960; default 960
</summary>
</member>
<member name="P:PaddleOCRSharp.ModifyParameter.m_det_db_thresh">
<summary>
Used to filter the binary image predicted by DB. Setting to 0.-0.3 has no significant effect on results; default 0.3. Effective when m_det=true
</summary>
</member>
<member name="P:PaddleOCRSharp.ModifyParameter.m_det_db_box_thresh">
<summary>
DB post-processing threshold for filtering boxes. If detection has missing boxes, this can be reduced accordingly; default 0.5. Effective when m_det=true
</summary>
</member>
<member name="P:PaddleOCRSharp.ModifyParameter.m_det_db_unclip_ratio">
<summary>
Represents the tightness of the text box. Smaller values mean the text box is closer to the text; default 1.6. Effective when m_det=true
</summary>
</member>
<member name="T:PaddleOCRSharp.OCRResult">
<summary>
OCR recognition result
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRResult.TextBlocks">
<summary>
List of text blocks
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRResult.Text">
<summary>
Recognition result text
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRResult.JsonText">
<summary>
Recognition result text in Json format
</summary>
</member>
<member name="M:PaddleOCRSharp.OCRResult.ToString">
<summary>
Return string format
</summary>
</member>
<member name="T:PaddleOCRSharp.TextBlock">
<summary>
Recognized text block
</summary>
</member>
<member name="P:PaddleOCRSharp.TextBlock.BoxPoints">
<summary>
List of coordinate vertices around the text block
</summary>
</member>
<member name="P:PaddleOCRSharp.TextBlock.Text">
<summary>
Text block content
</summary>
</member>
<member name="P:PaddleOCRSharp.TextBlock.Score">
<summary>
Text recognition confidence
</summary>
</member>
<member name="P:PaddleOCRSharp.TextBlock.cls_score">
<summary>
Angle classification confidence
</summary>
</member>
<member name="P:PaddleOCRSharp.TextBlock.cls_label">
<summary>
Angle classification label
</summary>
</member>
<member name="M:PaddleOCRSharp.TextBlock.ToString">
<summary>
Return string format
</summary>
</member>
<member name="T:PaddleOCRSharp.OCRPoint">
<summary>
Point object
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRPoint.X">
<summary>
X coordinate, in pixels
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRPoint.Y">
<summary>
Y coordinate, in pixels
</summary>
</member>
<member name="M:PaddleOCRSharp.OCRPoint.#ctor">
<summary>
Default constructor
</summary>
</member>
<member name="M:PaddleOCRSharp.OCRPoint.#ctor(System.Int32,System.Int32)">
<summary>
Constructor
</summary>
<param name="x"></param>
<param name="y"></param>
</member>
<member name="M:PaddleOCRSharp.OCRPoint.ToString">
<summary>
Return string format
</summary>
</member>
<member name="T:PaddleOCRSharp.OCRStructureResult">
<summary>
OCR structured recognition result
</summary>
</member>
<member name="M:PaddleOCRSharp.OCRStructureResult.#ctor">
<summary>
Table recognition result
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRStructureResult.RowCount">
<summary>
Number of rows
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRStructureResult.ColCount">
<summary>
Number of columns
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRStructureResult.Cells">
<summary>
List of cells
</summary>
</member>
<member name="P:PaddleOCRSharp.OCRStructureResult.TextBlocks">
<summary>
List of text blocks
</summary>
</member>
<member name="T:PaddleOCRSharp.StructureCells">
<summary>
Cell
</summary>
</member>
<member name="M:PaddleOCRSharp.StructureCells.#ctor">
<summary>
Cell constructor
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureCells.Row">
<summary>
Row number
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureCells.Col">
<summary>
Column number
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureCells.TextBlocks">
<summary>
Text blocks
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureCells.Text">
<summary>
Recognized text
</summary>
</member>
<member name="T:PaddleOCRSharp.PaddleOCREngine">
<summary>
PaddleOCR text recognition engine object
</summary>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor">
<summary>
Initialize OCR engine object with default parameters
</summary>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor(PaddleOCRSharp.OCRModelConfig)">
<summary>
Initialize OCR engine object with default parameters
</summary>
<param name="config">Model configuration object, if null then default values are used</param>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor(PaddleOCRSharp.OCRModelConfig,PaddleOCRSharp.OCRParameter)">
<summary>
PaddleOCR recognition engine object initialization
</summary>
<param name="config">Model configuration object, if null then default values are used</param>
<param name="parameter">Recognition parameters, if null then default values are used</param>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor(PaddleOCRSharp.OCRModelConfig,System.String)">
<summary>
PaddleOCR recognition engine object initialization
</summary>
<param name="config">Model configuration object, if null then default values are used</param>
<param name="parameterjson">Recognition parameters in json string format</param>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.GetDefaultConfig(System.String)">
<summary>
Get default configuration
</summary>
<param name="rootpath">Root directory</param>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.String)">
<summary>
Perform text recognition on image file
</summary>
<param name="imagefile">Image file</param>
<returns>OCR recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.Drawing.Bitmap)">
<summary>
Perform text recognition on image object
</summary>
<param name="image">Image</param>
<returns>OCR recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.Byte[])">
<summary>
Text recognition
</summary>
<param name="imagebyte">Image memory stream</param>
<returns>OCR recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectTextBase64(System.String)">
<summary>
Text recognition
</summary>
<param name="imagebase64">Image base64</param>
<returns>OCR recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.IntPtr,System.Int32,System.Int32,System.Int32)">
<summary>
Text recognition
</summary>
<param name="imgPtr">Image memory address</param>
<param name="nWidth">Image width</param>
<param name="nHeight">Image height</param>
<param name="nChannel">Image channel, usually 3 or 1</param>
<returns>OCR recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.ConvertResult(System.IntPtr)">
<summary>
Result parsing
</summary>
<param name="ptrResult"></param>
<returns></returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectStructure(System.Drawing.Bitmap)">
<summary>
Structured text recognition
</summary>
<param name="image">Image</param>
<returns>Table recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.getzeroindexs(System.Int32[],System.Int32)">
<summary>
Calculate table splitting
</summary>
<param name="pixellist"></param>
<param name="thresholdtozero"></param>
<returns></returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.ModifyParameter(PaddleOCRSharp.ModifyParameter)">
<summary>
Dynamically modify parameters after initialization
</summary>
<param name="parameter">Modifiable parameter object</param>
<returns>Whether successful, calling before initialization will result in failure</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.EnableDetUseRect(System.Boolean)">
<summary>
Whether to enable rectangular processing of detection results. Single characters are easily detected as diamond shapes, processing them as rectangles can improve recognition accuracy. Only applicable for horizontal text.
</summary>
<param name="enable"></param>
</member>
<member name="M:PaddleOCRSharp.PaddleOCREngine.Dispose">
<summary>
Release object
</summary>
</member>
<member name="T:PaddleOCRSharp.PaddleStructureEngine">
<summary>
PaddleOCR table recognition engine object
</summary>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor">
<summary>
PaddleStructureEngine recognition engine object initialization
</summary>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor(PaddleOCRSharp.StructureModelConfig)">
<summary>
PaddleStructureEngine recognition engine object initialization
</summary>
<param name="config">Model configuration object, if null then default values are used</param>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor(PaddleOCRSharp.StructureModelConfig,PaddleOCRSharp.StructureParameter)">
<summary>
PaddleStructureEngine recognition engine object initialization
</summary>
<param name="config">Model configuration object, if null then default values are used</param>
<param name="parameter">Recognition parameters, if null then default values are used</param>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor(PaddleOCRSharp.StructureModelConfig,System.String)">
<summary>
PaddleStructureEngine recognition engine object initialization
</summary>
<param name="config">Model configuration object, if null then default values are used</param>
<param name="parameterjson">Recognition parameters in Json format, if null then default values are used</param>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.GetDefaultConfig(System.String)">
<summary>
Get default configuration
</summary>
<param name="rootpath">Root directory</param>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetectFile(System.String)">
<summary>
Perform table text recognition on image file
</summary>
<param name="imagefile">Image file</param>
<returns>Table recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetect(System.Drawing.Image)">
<summary>
Perform table text recognition on image object
</summary>
<param name="image">Image</param>
<returns>Table recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetect(System.Byte[])">
<summary>
Perform table text recognition on image Byte array
</summary>
<param name="imagebyte">Image byte array</param>
<returns>Table recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetectBase64(System.String)">
<summary>
Perform table text recognition on image Base64
</summary>
<param name="imagebase64">Image Base64</param>
<returns>Table recognition result</returns>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.ConvertResult(System.IntPtr)">
<summary>
Result parsing
</summary>
<param name="ptrResult"></param>
<returns></returns>
</member>
<member name="M:PaddleOCRSharp.PaddleStructureEngine.Dispose">
<summary>
Release object
</summary>
</member>
<member name="T:PaddleOCRSharp.StructureParameter">
<summary>
OCR recognition parameters
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureParameter.table_max_len">
<summary>
When the input image length and width are greater than 488, the image is scaled proportionally, default 488
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureParameter.merge_no_span_structure">
<summary>
Whether to merge empty cells
</summary>
</member>
<member name="P:PaddleOCRSharp.StructureParameter.table_batch_num">
<summary>
Batch recognition quantity
</summary>
</member>
</members>
</doc>

View File

@ -1,30 +0,0 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace CtrEditor.FuncionesBase
{
public class BooleanToDoubleConverter : IValueConverter
{
public double TrueValue { get; set; } = -1;
public double FalseValue { get; set; } = 1;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return boolValue ? TrueValue : FalseValue;
}
return FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double doubleValue)
{
return Math.Abs(doubleValue - TrueValue) < double.Epsilon;
}
return false;
}
}
}

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
namespace CtrEditor.FuncionesBase namespace CtrEditor.FuncionesBase
@ -16,10 +15,8 @@ namespace CtrEditor.FuncionesBase
{ "DESCRIPCION", "All uppercase text" }, { "DESCRIPCION", "All uppercase text" },
{ "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" }, { "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" },
{ "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" }, { "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" },
{ "LETRASNUMEROS", "Format: ABC...123... (letters and numbers only, other chars become _)" }, { "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" },
{ "LETRASNUMEROS/*-+", "Format: ABC...123... (letters, numbers, and /*-+ chars only, other chars become _)" }, { "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces allowed)" },
{ "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces only, other chars become _)" },
{ "LETRASNUMEROSESPACIOS/*-+", "Format: ABC...123... (letters, numbers, spaces, and /*-+ chars only, other chars become _)" },
{ "Numero", "Numeric value" } { "Numero", "Numeric value" }
}; };
@ -39,9 +36,7 @@ namespace CtrEditor.FuncionesBase
"Siemens IO Input" => ApplySiemensPattern(text, "E"), "Siemens IO Input" => ApplySiemensPattern(text, "E"),
"Siemens IO Output" => ApplySiemensPattern(text, "A"), "Siemens IO Output" => ApplySiemensPattern(text, "A"),
"LETRASNUMEROS" => ApplyLetrasNumerosPattern(text), "LETRASNUMEROS" => ApplyLetrasNumerosPattern(text),
"LETRASNUMEROS/*-+" => ApplyLetrasNumerosSpecialPattern(text),
"LETRASNUMEROSESPACIOS" => ApplyLetrasNumerosEspaciosPattern(text), "LETRASNUMEROSESPACIOS" => ApplyLetrasNumerosEspaciosPattern(text),
"LETRASNUMEROSESPACIOS/*-+" => ApplyLetrasNumerosEspaciosSpecialPattern(text),
"Numero" => ApplyNumberPattern(text), "Numero" => ApplyNumberPattern(text),
_ => text _ => text
}; };
@ -59,7 +54,7 @@ namespace CtrEditor.FuncionesBase
if (match.Success) if (match.Success)
{ {
int address = Math.Min(int.Parse(match.Groups[1].Value), 65535); int address = Math.Min(int.Parse(match.Groups[1].Value), 65535);
int bit = match.Groups[2].Success ? int bit = match.Groups[2].Success ?
Math.Min(int.Parse(match.Groups[2].Value), 7) : 0; Math.Min(int.Parse(match.Groups[2].Value), 7) : 0;
return $"{prefix}{address}.{bit}"; return $"{prefix}{address}.{bit}";
} }
@ -68,67 +63,29 @@ namespace CtrEditor.FuncionesBase
private static string ApplyLetrasNumerosPattern(string text) private static string ApplyLetrasNumerosPattern(string text)
{ {
// Replace any character that is not a letter or digit with "_" // Extract letters and numbers from the text
char[] result = new char[text.Length]; var letters = new string(text.Where(c => char.IsLetter(c)).Take(10).ToArray());
var numbers = new string(text.Where(c => char.IsDigit(c)).ToArray());
for (int i = 0; i < text.Length; i++) // If no letters found, return "A" as default
{ if (string.IsNullOrEmpty(letters))
if (char.IsLetter(text[i]) || char.IsDigit(text[i])) letters = "A";
result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
else
result[i] = '_';
}
return new string(result); // If no numbers found, return "0" as default
if (string.IsNullOrEmpty(numbers))
numbers = "0";
// Combine letters (uppercase) and numbers
return $"{letters.ToUpper()}{numbers}";
} }
private static string ApplyLetrasNumerosEspaciosPattern(string text) private static string ApplyLetrasNumerosEspaciosPattern(string text)
{ {
// Replace any character that is not a letter, digit, or space with "_" // Keep only letters, numbers and spaces
char[] result = new char[text.Length]; var cleanedText = new string(text.Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)).ToArray());
for (int i = 0; i < text.Length; i++) // Convert to uppercase
{ return cleanedText.ToUpper();
if (char.IsLetter(text[i]) || char.IsDigit(text[i]) || char.IsWhiteSpace(text[i]))
result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
else
result[i] = '_';
}
return new string(result);
}
private static string ApplyLetrasNumerosSpecialPattern(string text)
{
// Replace any character that is not a letter, digit, or one of /*-+ with "_"
char[] result = new char[text.Length];
for (int i = 0; i < text.Length; i++)
{
if (char.IsLetter(text[i]) || char.IsDigit(text[i]) || text[i] == '/' || text[i] == '*' || text[i] == '-' || text[i] == '+')
result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
else
result[i] = '_';
}
return new string(result);
}
private static string ApplyLetrasNumerosEspaciosSpecialPattern(string text)
{
// Replace any character that is not a letter, digit, space, or one of /*-+ with "_"
char[] result = new char[text.Length];
for (int i = 0; i < text.Length; i++)
{
if (char.IsLetter(text[i]) || char.IsDigit(text[i]) || char.IsWhiteSpace(text[i]) ||
text[i] == '/' || text[i] == '*' || text[i] == '-' || text[i] == '+')
result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
else
result[i] = '_';
}
return new string(result);
} }
private static string ApplyNumberPattern(string text) private static string ApplyNumberPattern(string text)
@ -150,4 +107,4 @@ namespace CtrEditor.FuncionesBase
return items; return items;
} }
} }
} }

View File

@ -273,9 +273,10 @@ namespace CtrEditor
partial void OnSelectedItemOsListChanged(osBase value) partial void OnSelectedItemOsListChanged(osBase value)
{ {
// Enable delete and duplicate commands when either an individual item is selected if (value != null)
// or when there are multiple objects selected habilitarEliminarUserControl = true;
habilitarEliminarUserControl = _objectManager.SelectedObjects.Count > 0; else
habilitarEliminarUserControl = false;
} }
@ -518,66 +519,8 @@ namespace CtrEditor
private void DuplicarUserControl() private void DuplicarUserControl()
{ {
if (_objectManager.SelectedObjects.Count > 0) if (SelectedItemOsList is osBase objDuplicar)
{ DuplicarObjeto(objDuplicar, 0.5f, 0.5f);
// Create a copy of the selected objects to avoid issues during iteration
var objectsToDuplicate = _objectManager.SelectedObjects.ToList();
// Clear current selection before duplicating
_objectManager.ClearSelection();
// Track all newly created objects
var newObjects = new List<osBase>();
// Duplicate each object with a small offset
float offsetX = 0.5f;
float offsetY = 0.5f;
foreach (var objToDuplicate in objectsToDuplicate)
{
var newObj = DuplicarObjeto(objToDuplicate, offsetX, offsetY);
if (newObj != null)
{
newObjects.Add(newObj);
}
}
// Force a complete layout update to ensure all controls are positioned
MainWindow.ImagenEnTrabajoCanvas.UpdateLayout();
// Use a dispatcher to delay the selection until the UI has had time to fully render
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
{
// Select all newly created objects
foreach (var newObj in newObjects)
{
if (newObj.VisualRepresentation != null)
{
double left = Canvas.GetLeft(newObj.VisualRepresentation);
double top = Canvas.GetTop(newObj.VisualRepresentation);
// Only add to selection if the object has valid coordinates
if (!double.IsNaN(left) && !double.IsNaN(top) && !double.IsInfinity(left) && !double.IsInfinity(top))
{
_objectManager.SelectObject(newObj);
}
}
}
// Force another layout update before updating selection visuals
MainWindow.ImagenEnTrabajoCanvas.UpdateLayout();
// Update SelectedItemOsList if there are newly created objects
if (newObjects.Count > 0)
{
// Set to the last duplicated object so it's visible in the property panel
SelectedItemOsList = newObjects.LastOrDefault();
}
// Now update selection visuals
_objectManager.UpdateSelectionVisuals();
}));
}
} }
public osBase DuplicarObjeto(osBase objDuplicar, float OffsetX, float OffsetY) public osBase DuplicarObjeto(osBase objDuplicar, float OffsetX, float OffsetY)
@ -632,7 +575,6 @@ namespace CtrEditor
private void EliminarUserControl() private void EliminarUserControl()
{ {
_objectManager.EliminarObjetosSeleccionados(); _objectManager.EliminarObjetosSeleccionados();
SelectedItemOsList = null;
} }
@ -945,15 +887,7 @@ namespace CtrEditor
// Se cargan los datos de cada UserControl en el StackPanel // Se cargan los datos de cada UserControl en el StackPanel
public void CargarPropiedadesosDatos(osBase selectedObject, Controls.PanelEdicionControl PanelEdicion, ResourceDictionary Resources) public void CargarPropiedadesosDatos(osBase selectedObject, Controls.PanelEdicionControl PanelEdicion, ResourceDictionary Resources)
{ {
if (selectedObject == null) PanelEdicion.CargarPropiedades(selectedObject);
{
// Clear the property panel when no object is selected
PanelEdicion.ClearProperties();
}
else
{
PanelEdicion.CargarPropiedades(selectedObject);
}
} }
private RelayCommand saveCommand; private RelayCommand saveCommand;
@ -1076,22 +1010,12 @@ namespace CtrEditor
HasUnsavedChanges = true; HasUnsavedChanges = true;
_objectManager.UpdateSelectionVisuals(); _objectManager.UpdateSelectionVisuals();
} }
// Add this method to MainViewModel class
public void NotifySelectionChanged()
{
OnPropertyChanged(nameof(SelectedItemOsList));
}
} }
public class SimulationData public class SimulationData
{ {
public ObservableCollection<osBase>? ObjetosSimulables { get; set; } public ObservableCollection<osBase>? ObjetosSimulables { get; set; }
public UnitConverter? UnitConverter { get; set; } public UnitConverter? UnitConverter { get; set; }
public PLCViewModel? PLC_ConnectionData { get; set; } public PLCViewModel? PLC_ConnectionData { get; set; }
// Nueva propiedad para almacenar los datos locales de objetos globales
public ObservableCollection<DatosLocales>? DatosLocalesObjetos { get; set; }
} }
public class TipoSimulable public class TipoSimulable

View File

@ -319,6 +319,7 @@ namespace CtrEditor
private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{ {
if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject) if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject)
{ {
// Siempre trabajar con selección única para las propiedades // Siempre trabajar con selección única para las propiedades
@ -326,10 +327,8 @@ namespace CtrEditor
_objectManager.SelectObject(selectedObject); _objectManager.SelectObject(selectedObject);
} }
else if (e.RemovedItems.Count > 0 && e.AddedItems.Count == 0) else
{ {
// This handles the case when an item is deselected and no new item is selected
CargarPropiedadesosDatos(null);
_objectManager.RemoveResizeRectangles(); _objectManager.RemoveResizeRectangles();
} }
} }
@ -368,7 +367,6 @@ namespace CtrEditor
{ {
if (e.Key == Key.Delete) if (e.Key == Key.Delete)
{ {
// This will delete all selected objects
_objectManager.EliminarObjetosSeleccionados(); _objectManager.EliminarObjetosSeleccionados();
e.Handled = true; e.Handled = true;
} }
@ -445,15 +443,7 @@ namespace CtrEditor
private void CargarPropiedadesosDatos(osBase selectedObject) private void CargarPropiedadesosDatos(osBase selectedObject)
{ {
if (DataContext is MainViewModel viewModel) if (DataContext is MainViewModel viewModel)
{
viewModel.CargarPropiedadesosDatos(selectedObject, PanelEdicion, Resources); viewModel.CargarPropiedadesosDatos(selectedObject, PanelEdicion, Resources);
// If no object is selected, make sure to clear the properties panel
if (selectedObject == null)
{
PanelEdicion.ClearProperties();
}
}
} }
public (float X, float Y) ObtenerCentroCanvasMeters() public (float X, float Y) ObtenerCentroCanvasMeters()

View File

@ -74,8 +74,6 @@ namespace CtrEditor
private Image _backgroundImage; // Add this line private Image _backgroundImage; // Add this line
internal bool IsDraggingCanvas { get; set; } internal bool IsDraggingCanvas { get; set; }
private bool _isRectangleSelectionActive; private bool _isRectangleSelectionActive;
private bool _selectedObjectsAreVisible;
public bool IsRectangleSelectionActive public bool IsRectangleSelectionActive
{ {
get => _isRectangleSelectionActive; get => _isRectangleSelectionActive;
@ -183,37 +181,35 @@ namespace CtrEditor
public void AddResizeRectangles(IEnumerable<osBase> selectedObjects) public void AddResizeRectangles(IEnumerable<osBase> selectedObjects)
{ {
double rectHighlightSize = 1; double rectHighlightSize = 1;
RemoveResizeRectangles(); RemoveResizeRectangles();
// Calcular el bounding box que contenga todos los objetos seleccionados // Calcular el bounding box que contenga todos los objetos seleccionados
Rect boundingBox = CalculateTotalBoundingBox(selectedObjects); Rect boundingBox = CalculateTotalBoundingBox(selectedObjects);
if (_selectedObjectsAreVisible) {
FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(boundingBox); FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(boundingBox);
rectBox.Left -= (float)rectHighlightSize; rectBox.Left -= (float)rectHighlightSize;
rectBox.Right += (float)rectHighlightSize; rectBox.Right += (float)rectHighlightSize;
rectBox.Top -= (float)rectHighlightSize; rectBox.Top -= (float)rectHighlightSize;
rectBox.Bottom += (float)rectHighlightSize; rectBox.Bottom += (float)rectHighlightSize;
_transformedBoundingBoxCenter = new Point( _transformedBoundingBoxCenter = new Point(
boundingBox.Left + boundingBox.Width / 2, boundingBox.Left + boundingBox.Width / 2,
boundingBox.Top + boundingBox.Height / 2 boundingBox.Top + boundingBox.Height / 2
); );
// Selection rectangle // Selection rectangle
Rectangle selectionRect = CreateSelectionRectangle(rectBox, rectHighlightSize); Rectangle selectionRect = CreateSelectionRectangle(rectBox, rectHighlightSize);
_resizeRectangles.Add(selectionRect); _resizeRectangles.Add(selectionRect);
_canvas.Children.Add(selectionRect); _canvas.Children.Add(selectionRect);
// Load rotation cursors // Load rotation cursors
Cursor rotationCursorRx = new Cursor(Application.GetResourceStream( Cursor rotationCursorRx = new Cursor(Application.GetResourceStream(
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationRx.cur")).Stream); new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationRx.cur")).Stream);
Cursor rotationCursorSx = new Cursor(Application.GetResourceStream( Cursor rotationCursorSx = new Cursor(Application.GetResourceStream(
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationSx.cur")).Stream); new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationSx.cur")).Stream);
// Add resize/rotation handles // Add resize/rotation handles
AddResizeHandles(rectBox, 10, rotationCursorRx, rotationCursorSx); AddResizeHandles(rectBox, 10, rotationCursorRx, rotationCursorSx);
}
} }
private Rect CalculateTotalBoundingBox(IEnumerable<osBase> selectedObjects) private Rect CalculateTotalBoundingBox(IEnumerable<osBase> selectedObjects)
@ -222,11 +218,10 @@ namespace CtrEditor
double top = double.MaxValue; double top = double.MaxValue;
double right = double.MinValue; double right = double.MinValue;
double bottom = double.MinValue; double bottom = double.MinValue;
_selectedObjectsAreVisible = false;
foreach (var obj in selectedObjects) foreach (var obj in selectedObjects)
{ {
if (obj.VisualRepresentation != null && obj.VisualRepresentation.Visibility!=Visibility.Collapsed) if (obj.VisualRepresentation != null)
{ {
// Obtener el bounding box del objeto actual // Obtener el bounding box del objeto actual
Rect objectBounds = VisualTreeHelper.GetDescendantBounds(obj.VisualRepresentation); Rect objectBounds = VisualTreeHelper.GetDescendantBounds(obj.VisualRepresentation);
@ -238,7 +233,6 @@ namespace CtrEditor
top = Math.Min(top, transformedBounds.Top); top = Math.Min(top, transformedBounds.Top);
right = Math.Max(right, transformedBounds.Right); right = Math.Max(right, transformedBounds.Right);
bottom = Math.Max(bottom, transformedBounds.Bottom); bottom = Math.Max(bottom, transformedBounds.Bottom);
_selectedObjectsAreVisible = true;
} }
} }
@ -435,9 +429,6 @@ namespace CtrEditor
} }
UpdateSelectionVisuals(); UpdateSelectionVisuals();
// Update the view model's selection state
vm.NotifySelectionChanged();
} }
} }
} }
@ -449,12 +440,6 @@ namespace CtrEditor
_selectedObjects.Remove(obj); _selectedObjects.Remove(obj);
obj.IsSelected = false; obj.IsSelected = false;
RemoveSelectionHighlight(obj.VisualRepresentation); RemoveSelectionHighlight(obj.VisualRepresentation);
// Update the view model's selection state
if (_mainWindow.DataContext is MainViewModel vm)
{
vm.NotifySelectionChanged();
}
} }
} }
@ -914,9 +899,6 @@ namespace CtrEditor
RemoveResizeRectangles(); RemoveResizeRectangles();
RemoveAllSelectionHighlights(); RemoveAllSelectionHighlights();
// Ensure the property panel is cleared by explicitly setting SelectedItemOsList to null
viewModel.SelectedItemOsList = null;
// Actualizar el estado de cambios sin guardar // Actualizar el estado de cambios sin guardar
if (viewModel != null) if (viewModel != null)
{ {

View File

@ -1,22 +1,14 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucCustomImage" <UserControl x:Class="CtrEditor.ObjetosSim.ucCustomImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
xmlns:funcionesbase="clr-namespace:CtrEditor.FuncionesBase">
<UserControl.DataContext> <UserControl.DataContext>
<vm:osCustomImage /> <vm:osCustomImage />
</UserControl.DataContext> </UserControl.DataContext>
<UserControl.Resources>
<funcionesbase:BooleanToDoubleConverter x:Key="FlipConverter" TrueValue="-1" FalseValue="1"/>
</UserControl.Resources>
<Grid RenderTransformOrigin="0.5,0.5"> <Grid RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform> <Grid.RenderTransform>
<TransformGroup> <TransformGroup>
<ScaleTransform
ScaleX="{Binding Horizontal_Flip, Converter={StaticResource FlipConverter}}"
ScaleY="{Binding Vertical_Flip, Converter={StaticResource FlipConverter}}"/>
<RotateTransform Angle="{Binding Angulo}"/> <RotateTransform Angle="{Binding Angulo}"/>
</TransformGroup> </TransformGroup>
</Grid.RenderTransform> </Grid.RenderTransform>

View File

@ -33,16 +33,6 @@ namespace CtrEditor.ObjetosSim
[property: Category("Image:")] [property: Category("Image:")]
private string imagePath; private string imagePath;
[ObservableProperty]
[property: Description("Flip the image horizontally")]
[property: Category("Image:")]
private bool horizontal_Flip;
[ObservableProperty]
[property: Description("Flip the image vertically")]
[property: Category("Image:")]
private bool vertical_Flip;
[JsonIgnore] [JsonIgnore]
[ObservableProperty] [ObservableProperty]
@ -61,16 +51,6 @@ namespace CtrEditor.ObjetosSim
} }
} }
partial void OnHorizontal_FlipChanged(bool value)
{
// Property changed handler will trigger UI update through binding
}
partial void OnVertical_FlipChanged(bool value)
{
// Property changed handler will trigger UI update through binding
}
public osCustomImage() public osCustomImage()
{ {
Ancho = 0.30f; Ancho = 0.30f;

View File

@ -68,13 +68,6 @@ namespace CtrEditor.ObjetosSim
[property: Category("Encoders:")] [property: Category("Encoders:")]
public float k_encoder_X; public float k_encoder_X;
[ObservableProperty]
[property: ReadOnly(true)]
[property: Description("Actual Position X")]
[property: Category("Encoders:")]
public float encoder_X_Position;
[ObservableProperty] [ObservableProperty]
[property: Description("X in meter offset Left. Position when the encoder is 0")] [property: Description("X in meter offset Left. Position when the encoder is 0")]
[property: Category("Encoders:")] [property: Category("Encoders:")]
@ -92,12 +85,6 @@ namespace CtrEditor.ObjetosSim
[property: Category("Encoders:")] [property: Category("Encoders:")]
public float k_encoder_Y; public float k_encoder_Y;
[ObservableProperty]
[property: ReadOnly(true)]
[property: Description("Actual Position Y")]
[property: Category("Encoders:")]
public float encoder_Y_Position;
[ObservableProperty] [ObservableProperty]
[property: Description("Y in meter offset Top. Position when the encoder is 0")] [property: Description("Y in meter offset Top. Position when the encoder is 0")]
[property: Category("Encoders:")] [property: Category("Encoders:")]
@ -199,16 +186,11 @@ namespace CtrEditor.ObjetosSim
{ {
// Update X position if encoder is available // Update X position if encoder is available
if (EncoderX != null && K_encoder_X != 0) if (EncoderX != null && K_encoder_X != 0)
{ Left = (EncoderX.Valor_Actual / k_encoder_X) + offset_encoder_X;
Encoder_Y_Position = EncoderY.Valor_Actual;
Left = (Encoder_X_Position / k_encoder_X) + offset_encoder_X;
}
// Update Y position if encoder is available // Update Y position if encoder is available
if (EncoderY != null && K_encoder_Y != 0) if (EncoderY != null && K_encoder_Y != 0)
{ Top = (EncoderY.Valor_Actual / k_encoder_Y) + offset_encoder_Y;
Encoder_Y_Position = EncoderY.Valor_Actual;
Top = (Encoder_Y_Position / k_encoder_Y) + offset_encoder_Y;
}
} }
public override void TopChanging(float oldValue, float newValue) public override void TopChanging(float oldValue, float newValue)

View File

@ -13,7 +13,7 @@ using System.Text.Json.Serialization;
namespace CtrEditor.ObjetosSim namespace CtrEditor.ObjetosSim
{ {
/// <summary> /// <summary>
/// Interaction logic for ucTransporteTTopDualInverter.xaml /// Interaction logic for ucTransporteTTop.xaml
/// </summary> /// </summary>
/// ///
@ -53,7 +53,7 @@ namespace CtrEditor.ObjetosSim
partial void OnInvertirDireccionChanged(bool value) partial void OnInvertirDireccionChanged(bool value)
{ {
SetSpeed(); SetSpeed();
if (_visualRepresentation is ucTransporteTTopDualInverter uc) if (_visualRepresentation is ucTransporteTTop uc)
{ {
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion); CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual); ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
@ -156,7 +156,7 @@ namespace CtrEditor.ObjetosSim
private void ActualizarGeometrias() private void ActualizarGeometrias()
{ {
if (_visualRepresentation is ucTransporteTTopDualInverter uc) if (_visualRepresentation is ucTransporteTTop uc)
{ {
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo); UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
SetSpeed(); SetSpeed();
@ -206,8 +206,6 @@ namespace CtrEditor.ObjetosSim
else else
VelocidadActual = 0; VelocidadActual = 0;
} }
else
VelocidadActual = 0;
} }
public override void ucLoaded() public override void ucLoaded()
@ -218,7 +216,7 @@ namespace CtrEditor.ObjetosSim
OnId_Motor_AChanged(Id_Motor_A); // Link Id_Motor = Motor OnId_Motor_AChanged(Id_Motor_A); // Link Id_Motor = Motor
OnId_Motor_BChanged(Id_Motor_B); // Link Id_Motor = Motor OnId_Motor_BChanged(Id_Motor_B); // Link Id_Motor = Motor
if (_visualRepresentation is ucTransporteTTopDualInverter uc) if (_visualRepresentation is ucTransporteTTop uc)
{ {
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo); SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion); CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);

View File

@ -213,7 +213,7 @@ namespace CtrEditor.ObjetosSim
// 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;
OUT_Reversal = control.reversal; OUT_Reversal = control.reversal;
OUT_OUT_VFD_REQ_Speed_Hz = control.reqSpeedHz; OUT_OUT_VFD_REQ_Speed_Hz = control.reqSpeedHz;

View File

@ -165,7 +165,7 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
public void CaptureImageAreaAndDoOCR() public void CaptureImageAreaAndDoOCR()
{ {
string extractedText = CaptureImageAreaAndDoOCRPPaddle(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window); string extractedText = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
// Clean up the extracted text if eliminar_enters is true // Clean up the extracted text if eliminar_enters is true
if (Eliminar_enters && !string.IsNullOrEmpty(extractedText)) if (Eliminar_enters && !string.IsNullOrEmpty(extractedText))

View File

@ -56,12 +56,6 @@ namespace CtrEditor.ObjetosSim
[property: Category("PLC link:")] [property: Category("PLC link:")]
string tag_Valor; string tag_Valor;
[ObservableProperty]
[property: Description("Tag para leer el valor del encoder. Este tag tiene prioridad sobre el Motor. Si se usa solo se lee el tag para actualizar la posicion.")]
[property: Category("PLC link:")]
string tag_ReadValor;
partial void OnId_MotorChanged(string value) partial void OnId_MotorChanged(string value)
{ {
if (Motor != null) if (Motor != null)
@ -91,16 +85,7 @@ namespace CtrEditor.ObjetosSim
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds) public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{ {
if (Tag_ReadValor != null && Tag_ReadValor.Length > 0) if (Motor != null && Motor is osVMmotorSim motor)
{
// Bypass motor and read directly from tag
var value = LeerDINTTag(tag_ReadValor);
if (value != null)
{
Valor_Actual = (int)value;
return;
}
} else if (Motor != null && Motor is osVMmotorSim motor)
{ {
VelocidadActual = motor.Velocidad; VelocidadActual = motor.Velocidad;

View File

@ -1,17 +1,15 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase; using CtrEditor.FuncionesBase;
using CtrEditor.Serialization;
using CtrEditor.Services;
using CtrEditor.Simulacion; using CtrEditor.Simulacion;
using LibS7Adv; using LibS7Adv;
using nkast.Aether.Physics2D.Common; using nkast.Aether.Physics2D.Common;
using PaddleOCRSharp;
using Siemens.Simatic.Simulation.Runtime; using Siemens.Simatic.Simulation.Runtime;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Animation; using System.Windows.Media.Animation;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
@ -382,10 +380,7 @@ namespace CtrEditor.ObjetosSim
if (Angulo != 0) if (Angulo != 0)
{ {
// TransformedBitmap only accepts rotations in 90-degree increments RotateTransform rotateTransform = new RotateTransform(-Angulo);
// Round to nearest 90 degrees: 0, 90, 180, or 270
double normalizedAngle = Math.Round(Angulo / 90.0) * 90.0;
RotateTransform rotateTransform = new RotateTransform(-normalizedAngle);
transformedBitmap.Transform = rotateTransform; transformedBitmap.Transform = rotateTransform;
} }
@ -418,7 +413,7 @@ namespace CtrEditor.ObjetosSim
using (var engine = new TesseractEngine(tesseractPath, "eng", EngineMode.Default)) using (var engine = new TesseractEngine(tesseractPath, "eng", EngineMode.Default))
{ {
// Configuraciones para mejorar el OCR de una sola letra // Configuraciones para mejorar el OCR de una sola letra
engine.SetVariable("tessedit_char_whitelist", " ABCDEFGHIJKLMNÑOPQRSTUVWXYZabcdefghijklmnñopqrstuvwxyz0123456789-./"); // Lista blanca de caracteres engine.SetVariable("tessedit_char_whitelist", " ABCDEFGHIJKLMNÑOPQRSTUVWXYZabcdefghijklmnñopqrstuvwxyz0123456789-."); // Lista blanca de caracteres
var result = engine.Process(img); var result = engine.Process(img);
return result.GetText(); return result.GetText();
} }
@ -431,101 +426,6 @@ namespace CtrEditor.ObjetosSim
return ""; return "";
} }
// Reemplaza el método existente CaptureImageAreaAndDoOCR con esta implementación
public string CaptureImageAreaAndDoOCRPPaddle(float Left, float Top, float Ancho, float Alto, float Angulo = 0, bool ShowPreview = false)
{
if (_mainViewModel?.MainCanvas.Children[0] is System.Windows.Controls.Image imagenDeFondo)
{
if (imagenDeFondo.Source is BitmapSource bitmapSource)
{
float originalDpiX = (float)bitmapSource.DpiX;
float originalDpiY = (float)bitmapSource.DpiY;
float canvasDpiX = 96;
float canvasDpiY = 96;
float scaleFactorX = originalDpiX / canvasDpiX;
float scaleFactorY = originalDpiY / canvasDpiY;
int x = (int)MeterToPixels(Left * scaleFactorX);
int y = (int)MeterToPixels(Top * scaleFactorY);
int width = (int)MeterToPixels(Ancho * scaleFactorX);
int height = (int)MeterToPixels(Alto * scaleFactorY);
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + width > bitmapSource.PixelWidth) width = bitmapSource.PixelWidth - x;
if (y + height > bitmapSource.PixelHeight) height = bitmapSource.PixelHeight - y;
CroppedBitmap croppedBitmap = new CroppedBitmap(bitmapSource, new Int32Rect(x, y, width, height));
TransformedBitmap transformedBitmap = new TransformedBitmap();
transformedBitmap.BeginInit();
transformedBitmap.Source = croppedBitmap;
if (Angulo != 0)
{
// TransformedBitmap only accepts rotations in 90-degree increments
// Round to nearest 90 degrees: 0, 90, 180, or 270
double normalizedAngle = Math.Round(Angulo / 90.0) * 90.0;
RotateTransform rotateTransform = new RotateTransform(-normalizedAngle);
transformedBitmap.Transform = rotateTransform;
}
transformedBitmap.EndInit();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(transformedBitmap));
using (MemoryStream memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
if (ShowPreview) ShowPreviewWindow(memoryStream);
try
{
// Primero convertimos a un array de bytes
byte[] imageBytes = memoryStream.ToArray();
// Obtener el motor PaddleOCR
var ocrEngine = PaddleOCRManager.GetEngine();
// Usar la sobrecarga que acepta un array de bytes en lugar de Bitmap
OCRResult result = ocrEngine.DetectText(imageBytes);
if (result != null && result.TextBlocks != null && result.TextBlocks.Count > 0)
{
// Lista de caracteres permitidos (similar a la whitelist de Tesseract)
string allowedChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-./";
// Filtrar texto por lista de caracteres permitidos
var filteredBlocks = result.TextBlocks
.Where(block => block.Score > 0.5) // Solo bloques con confianza > 50%
.Select(block => new {
Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()),
Score = block.Score
})
.OrderByDescending(block => block.Score);
string recognizedText = string.Join(" ", filteredBlocks.Select(b => b.Text));
return recognizedText.Trim();
}
return "";
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error en OCR: {ex.Message}");
return "";
}
}
}
}
return "";
}
// Método para obtener un color más claro y saturado // Método para obtener un color más claro y saturado
public static Color ObtenerColorMasClaroYSaturado(Color colorOriginal, double incrementoL, double incrementoS) public static Color ObtenerColorMasClaroYSaturado(Color colorOriginal, double incrementoL, double incrementoS)
{ {
@ -549,151 +449,10 @@ namespace CtrEditor.ObjetosSim
[property: Category("Layout:")] [property: Category("Layout:")]
private bool enable_On_All_Pages; private bool enable_On_All_Pages;
partial void OnEnable_On_All_PagesChanged(bool value) partial void OnEnable_On_All_PagesChanged(bool value)
{ {
// Si se está desactivando el modo global
if (!value) if (!value)
{
// Limpiar los datos locales
_datosLocales = null;
_snapshotGlobal = null;
// Desactivar datos locales
Enable_Local_Data = false;
Show_On_This_Page = true; Show_On_This_Page = true;
}
}
// Local Data for Global Objects
[NotifyPropertyChangedFor(nameof(Show_On_This_Page))]
[ObservableProperty]
[property: Description("Enable local data of this global Object on all pages.")]
[property: Category("Layout:")]
private bool enable_Local_Data_for_All;
partial void OnEnable_Local_Data_for_AllChanged(bool value)
{
if (value)
{
// Activar datos locales
Enable_Local_Data = true;
}
}
// Local Data for Global Objects
[NotifyPropertyChangedFor(nameof(Show_On_This_Page))]
[ObservableProperty]
[property: Description("Enable local data of this global Object on this page.")]
[property: Category("Layout:")]
[property: JsonIgnore]
private bool enable_Local_Data;
// Añadir estas propiedades y métodos a la clase osBase
[JsonIgnore]
private DatosLocales _snapshotGlobal;
[JsonIgnore]
private DatosLocales _datosLocales;
/// <summary>
/// Crea un snapshot de los datos globales actuales
/// </summary>
public void CrearSnapshotGlobal()
{
if (Enable_On_All_Pages) // No verificamos Enable_Local_Data aquí
{
_snapshotGlobal = new DatosLocales(Id);
_snapshotGlobal.CopiarDesdeObjeto(this);
}
}
/// <summary>
/// Restaura los datos globales desde el snapshot
/// </summary>
public void RestaurarDesdeSnapshotGlobal()
{
if (Enable_On_All_Pages && Enable_Local_Data && _snapshotGlobal != null)
{
// Guarda los datos locales actuales antes de restaurar
if (_datosLocales == null)
{
_datosLocales = new DatosLocales(Id);
}
_datosLocales.CopiarDesdeObjeto(this);
// Restaura desde el snapshot
_snapshotGlobal.AplicarAObjeto(this);
}
}
/// <summary>
/// Restaura los datos locales después de guardar
/// </summary>
public void RestaurarDatosLocales()
{
if (Enable_On_All_Pages && Enable_Local_Data && _datosLocales != null)
{
_datosLocales.AplicarAObjeto(this);
}
}
/// <summary>
/// Obtiene los datos locales del objeto
/// </summary>
public DatosLocales ObtenerDatosLocales()
{
if (!Enable_On_All_Pages)
return null;
DatosLocales datos = new DatosLocales(Id);
datos.CopiarDesdeObjeto(this);
return datos;
}
/// <summary>
/// Aplica datos locales al objeto
/// </summary>
public void AplicarDatosLocales(DatosLocales datos)
{
if (Enable_On_All_Pages && datos != null && datos.Id_GlobalObject.Value == Id.Value)
{
Enable_Local_Data = true; // Activar datos locales si se encuentran
datos.AplicarAObjeto(this);
// Actualiza la visualización
ActualizarLeftTop();
OnAnchoChanged(Ancho);
OnAltoChanged(Alto);
}
}
partial void OnEnable_Local_DataChanged(bool value)
{
// Si se está desactivando los datos locales y teníamos un snapshot
if (!value && _snapshotGlobal != null && Enable_On_All_Pages)
{
// Restaurar los valores globales
_snapshotGlobal.AplicarAObjeto(this);
// Limpiar los datos locales
_datosLocales = null;
_snapshotGlobal = null;
// Actualizar la visualización
ActualizarLeftTop();
OnAnchoChanged(Ancho);
OnAltoChanged(Alto);
}
// Si se está activando, crear el snapshot
else if (value && Enable_On_All_Pages)
{
CrearSnapshotGlobal();
}
} }
[ObservableProperty] [ObservableProperty]
@ -1023,16 +782,6 @@ namespace CtrEditor.ObjetosSim
} }
} }
public int? LeerDINTTag(string Tag)
{
if (_plc == null) return null;
if (!string.IsNullOrEmpty(Tag))
{
return _plc.LeerTagDInt(Tag);
}
return null;
}
public void EscribirWordTagScaled(string Tag, float Value, float IN_scale_Min, float IN_scale_Max, float OUT_scale_Min, float OUT_scale_Max) public void EscribirWordTagScaled(string Tag, float Value, float IN_scale_Min, float IN_scale_Max, float OUT_scale_Min, float OUT_scale_Max)
{ {
if (_plc == null) return; if (_plc == null) return;

View File

@ -1,91 +0,0 @@
using CtrEditor.ObjetosSim;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace CtrEditor.Serialization
{
/// <summary>
/// Almacena datos locales para un objeto global
/// </summary>
public class DatosLocales
{
/// <summary>
/// ID del objeto global al que pertenecen estos datos locales
/// </summary>
public UniqueId Id_GlobalObject { get; set; }
/// <summary>
/// Posición vertical local
/// </summary>
public float? Top { get; set; }
/// <summary>
/// Posición horizontal local
/// </summary>
public float? Left { get; set; }
/// <summary>
/// Ancho local
/// </summary>
public float? Ancho { get; set; }
/// <summary>
/// Alto local
/// </summary>
public float? Alto { get; set; }
/// <summary>
/// Diccionario para almacenar propiedades dinámicas adicionales
/// </summary>
public Dictionary<string, object> PropiedadesAdicionales { get; set; }
/// <summary>
/// Constructor por defecto necesario para deserialización
/// </summary>
public DatosLocales()
{
PropiedadesAdicionales = new Dictionary<string, object>();
}
/// <summary>
/// Constructor con ID del objeto global
/// </summary>
public DatosLocales(UniqueId idGlobalObject)
{
Id_GlobalObject = idGlobalObject;
PropiedadesAdicionales = new Dictionary<string, object>();
}
/// <summary>
/// Copia los datos relevantes desde un objeto global
/// </summary>
public void CopiarDesdeObjeto(osBase objeto)
{
if (objeto == null) return;
Id_GlobalObject = objeto.Id;
Top = objeto.Top;
Left = objeto.Left;
Ancho = objeto.Ancho;
Alto = objeto.Alto;
// Aquí puedes añadir más propiedades si es necesario
}
/// <summary>
/// Aplica los datos locales al objeto global
/// </summary>
public void AplicarAObjeto(osBase objeto)
{
if (objeto == null || objeto.Id.Value != Id_GlobalObject.Value) return;
if (Left.HasValue) objeto.Left = Left.Value;
if (Top.HasValue) objeto.Top = Top.Value;
if (Ancho.HasValue) objeto.Ancho = Ancho.Value;
if (Alto.HasValue) objeto.Alto = Alto.Value;
// Aquí puedes aplicar más propiedades si es necesario
}
}
}

View File

@ -1,128 +0,0 @@
using CtrEditor.ObjetosSim;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
namespace CtrEditor.Serialization
{
/// <summary>
/// Clase para gestionar qué propiedades deben ser locales en objetos globales
/// </summary>
public static class PropiedadesLocalesManager
{
// Definición de propiedades por defecto que se guardan localmente para todos los objetos
private static readonly List<string> PropiedadesPorDefecto = new List<string>
{
"Left", "Top", "Ancho", "Alto"
};
// Diccionario que contiene conjuntos de propiedades locales por tipo de objeto
private static readonly Dictionary<Type, List<string>> PropiedadesPorTipo = new Dictionary<Type, List<string>>();
/// <summary>
/// Registra un conjunto de propiedades locales para un tipo específico de objeto
/// </summary>
/// <typeparam name="T">Tipo de objeto (debe heredar de osBase)</typeparam>
/// <param name="propiedades">Lista de nombres de propiedades a registrar como locales</param>
public static void RegistrarPropiedadesParaTipo<T>(List<string> propiedades) where T : osBase
{
PropiedadesPorTipo[typeof(T)] = propiedades;
}
/// <summary>
/// Obtiene la lista de propiedades que deben ser locales para un objeto concreto
/// </summary>
/// <param name="objeto">Objeto del que obtener las propiedades locales</param>
/// <returns>Lista de nombres de propiedades</returns>
public static List<string> ObtenerPropiedadesLocales(osBase objeto)
{
var resultado = new List<string>(PropiedadesPorDefecto);
if (objeto != null && PropiedadesPorTipo.TryGetValue(objeto.GetType(), out var propiedadesExtra))
{
foreach (var prop in propiedadesExtra)
{
if (!resultado.Contains(prop))
resultado.Add(prop);
}
}
return resultado;
}
/// <summary>
/// Crea un objeto dinámico que permite acceder a las propiedades locales de un objeto
/// </summary>
/// <param name="objeto">Objeto del que crear una vista dinámica</param>
/// <returns>Objeto dinámico con propiedades locales</returns>
public static dynamic CrearAccesoDinamico(osBase objeto)
{
return new PropiedadesLocalesDinamicas(objeto);
}
/// <summary>
/// Clase interna que implementa un acceso dinámico a propiedades
/// </summary>
private class PropiedadesLocalesDinamicas : DynamicObject
{
private readonly osBase _objeto;
private readonly List<string> _propiedadesLocales;
public PropiedadesLocalesDinamicas(osBase objeto)
{
_objeto = objeto;
_propiedadesLocales = ObtenerPropiedadesLocales(objeto);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var nombrePropiedad = binder.Name;
if (_propiedadesLocales.Contains(nombrePropiedad))
{
var propInfo = _objeto.GetType().GetProperty(nombrePropiedad);
if (propInfo != null && propInfo.CanRead)
{
result = propInfo.GetValue(_objeto);
return true;
}
}
result = null;
return false;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var nombrePropiedad = binder.Name;
if (_propiedadesLocales.Contains(nombrePropiedad))
{
var propInfo = _objeto.GetType().GetProperty(nombrePropiedad);
if (propInfo != null && propInfo.CanWrite)
{
try
{
var valorConvertido = Convert.ChangeType(value, propInfo.PropertyType);
propInfo.SetValue(_objeto, valorConvertido);
return true;
}
catch (Exception)
{
// Silenciar errores de conversión
}
}
}
return false;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _propiedadesLocales;
}
}
}
}

View File

@ -5,7 +5,6 @@ using System.Collections.ObjectModel;
using System.Windows; using System.Windows;
using CtrEditor.Simulacion; using CtrEditor.Simulacion;
using System.IO; using System.IO;
using System.Linq;
namespace CtrEditor.Serialization namespace CtrEditor.Serialization
{ {
@ -31,68 +30,36 @@ namespace CtrEditor.Serialization
var objetosSimulables = new ObservableCollection<osBase>(); var objetosSimulables = new ObservableCollection<osBase>();
var objetosSimulablesAllPages = new ObservableCollection<osBase>(); var objetosSimulablesAllPages = new ObservableCollection<osBase>();
var datosLocalesObjetos = new ObservableCollection<DatosLocales>();
// Paso 1: Preparar objetos globales y locales
foreach (var obj in _mainViewModel.ObjetosSimulables) foreach (var obj in _mainViewModel.ObjetosSimulables)
{ {
// Para objetos globales con datos locales
if (obj.Enable_On_All_Pages && obj.Enable_Local_Data)
{
// Guarda los datos locales independientemente de Show_On_This_Page
var datosLocales = obj.ObtenerDatosLocales();
if (datosLocales != null)
{
datosLocalesObjetos.Add(datosLocales);
}
// Restaura datos globales para guardar
obj.RestaurarDesdeSnapshotGlobal();
}
// Prepara para serialización
obj.SalvarDatosNoSerializables(); obj.SalvarDatosNoSerializables();
// Separa objetos globales y locales
if (!obj.Enable_On_All_Pages) if (!obj.Enable_On_All_Pages)
{
objetosSimulables.Add(obj); objetosSimulables.Add(obj);
}
else else
{
objetosSimulablesAllPages.Add(obj); objetosSimulablesAllPages.Add(obj);
}
} }
// Paso 2: Guardar datos de la página actual // Save current page objects
var dataToSerialize = new SimulationData var dataToSerialize = new SimulationData
{ {
ObjetosSimulables = objetosSimulables.Count > 0 ? objetosSimulables : null, ObjetosSimulables = objetosSimulables,
UnitConverter = PixelToMeter.Instance.calc, UnitConverter = PixelToMeter.Instance.calc,
PLC_ConnectionData = _mainViewModel.PLCViewModel, PLC_ConnectionData = _mainViewModel.PLCViewModel
DatosLocalesObjetos = datosLocalesObjetos.Count > 0 ? datosLocalesObjetos : null
}; };
var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json"); var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
if (path != null) if (path != null)
SerializeAndSave(dataToSerialize, path); SerializeAndSave(dataToSerialize, path);
// Paso 3: Guardar objetos globales // Save all pages objects
path = _datosDeTrabajo.ObtenerPathAllPages(".json"); path = _datosDeTrabajo.ObtenerPathAllPages(".json");
if (path != null) if (path != null)
SerializeAndSave(objetosSimulablesAllPages, path); SerializeAndSave(objetosSimulablesAllPages, path);
// Paso 4: Restaurar estado para seguir trabajando // Restore original properties
foreach (var obj in _mainViewModel.ObjetosSimulables) foreach (var obj in _mainViewModel.ObjetosSimulables)
{
obj.RestaurarDatosNoSerializables(); obj.RestaurarDatosNoSerializables();
// Para objetos globales, restaura los datos locales
if (obj.Enable_On_All_Pages && obj.Enable_Local_Data)
{
obj.RestaurarDatosLocales();
}
}
} }
} }
@ -108,47 +75,10 @@ namespace CtrEditor.Serialization
if (selectedImage != null) if (selectedImage != null)
{ {
var settings = GetJsonSerializerSettings(); var settings = GetJsonSerializerSettings();
LoadCurrentPageState(selectedImage, settings);
// Paso 1: Cargar objetos globales
LoadAllPagesState(settings); LoadAllPagesState(settings);
// Paso 2: Cargar datos de la página actual // Create UserControls for all loaded objects
var simulationData = LoadCurrentPageState(selectedImage, settings);
// Paso 3: Crear snapshots de los objetos globales
// Nota: No filtramos por Enable_Local_Data aquí ya que se establecerá después
foreach (var obj in _mainViewModel.ObjetosSimulables)
{
if (obj.Enable_On_All_Pages)
{
obj.CrearSnapshotGlobal();
}
}
// Paso 4: Aplicar datos locales a objetos globales
if (simulationData?.DatosLocalesObjetos != null)
{
// Primero, desactivar Enable_Local_Data en todos los objetos globales
foreach (var obj in _mainViewModel.ObjetosSimulables.Where(o => o.Enable_On_All_Pages))
{
obj.Enable_Local_Data = false;
}
// Luego aplicar los datos locales, esto activará Enable_Local_Data automáticamente
foreach (var datosLocales in simulationData.DatosLocalesObjetos)
{
var objetoGlobal = _mainViewModel.ObjetosSimulables.FirstOrDefault(
o => o.Enable_On_All_Pages &&
o.Id.Value == datosLocales.Id_GlobalObject.Value);
if (objetoGlobal != null)
{
objetoGlobal.AplicarDatosLocales(datosLocales);
}
}
}
// Paso 5: Crear controles visuales
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables) foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{ {
if (objetoSimulable != null) if (objetoSimulable != null)
@ -165,21 +95,17 @@ namespace CtrEditor.Serialization
} }
} }
private SimulationData LoadCurrentPageState(string selectedImage, JsonSerializerSettings settings) private void LoadCurrentPageState(string selectedImage, JsonSerializerSettings settings)
{ {
SimulationData simulationData = null;
string jsonPath = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json"); string jsonPath = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
if (File.Exists(jsonPath)) if (File.Exists(jsonPath))
{ {
string jsonString = File.ReadAllText(jsonPath); string jsonString = File.ReadAllText(jsonPath);
simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings); var simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
if (simulationData != null) if (simulationData != null)
{ {
if (simulationData.ObjetosSimulables is not null) if (simulationData.ObjetosSimulables is not null)
{ _mainViewModel.ObjetosSimulables = simulationData.ObjetosSimulables;
foreach (var obj in simulationData.ObjetosSimulables)
_mainViewModel.ObjetosSimulables.Add(obj);
}
if (simulationData.PLC_ConnectionData is not null) if (simulationData.PLC_ConnectionData is not null)
_mainViewModel.PLCViewModel = simulationData.PLC_ConnectionData; _mainViewModel.PLCViewModel = simulationData.PLC_ConnectionData;
@ -189,7 +115,6 @@ namespace CtrEditor.Serialization
PixelToMeter.Instance.calc = simulationData.UnitConverter; PixelToMeter.Instance.calc = simulationData.UnitConverter;
} }
} }
return simulationData;
} }
private void LoadAllPagesState(JsonSerializerSettings settings) private void LoadAllPagesState(JsonSerializerSettings settings)
@ -233,4 +158,5 @@ namespace CtrEditor.Serialization
}; };
} }
} }
} }

View File

@ -1,88 +0,0 @@
using System;
using System.IO;
using System.Drawing;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Linq;
using PaddleOCRSharp;
namespace CtrEditor.Services
{
// Clase auxiliar para gestionar PaddleOCR
public static class PaddleOCRManager
{
private static PaddleOCREngine _engine;
private static bool _initialized = false;
private static readonly object _lockObj = new object();
public static PaddleOCREngine GetEngine()
{
if (!_initialized)
{
lock (_lockObj)
{
if (!_initialized)
{
InitializeEngine();
_initialized = true;
}
}
}
return _engine;
}
private static void InitializeEngine()
{
try
{
string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "paddleocr");
OCRModelConfig config = new OCRModelConfig
{
// Rutas a modelos de inferencia
det_infer = Path.Combine(baseDir, "det", "inference"),
cls_infer = Path.Combine(baseDir, "cls", "inference"),
rec_infer = Path.Combine(baseDir, "rec", "inference"),
keys = Path.Combine(baseDir, "keys", "ppocr_keys.txt")
};
OCRParameter parameter = new OCRParameter
{
// Configurar parámetros de OCR
use_angle_cls = true,
cls_thresh = 0.9f,
det_db_thresh = 0.3f,
det_db_box_thresh = 0.6f,
rec_batch_num = 6,
rec_img_h = 48,
enable_mkldnn = true,
use_gpu = false,
cpu_math_library_num_threads = Environment.ProcessorCount
};
_engine = new PaddleOCREngine(config, parameter);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error al inicializar PaddleOCR: {ex.Message}");
// Fallback - inicializar con configuración predeterminada
_engine = new PaddleOCREngine();
}
}
public static void Cleanup()
{
lock (_lockObj)
{
if (_initialized && _engine != null)
{
_engine.Dispose();
_engine = null;
_initialized = false;
}
}
}
}
}

View File

@ -553,11 +553,11 @@ namespace CtrEditor.Simulacion
Body.OnSeparation += HandleOnSeparation; Body.OnSeparation += HandleOnSeparation;
Body.Tag = this; // Importante para la identificación durante la colisión Body.Tag = this; // Importante para la identificación durante la colisión
// Configurar la fricción // Configurar la fricción
//Body.SetFriction(0.2f); Body.SetFriction(0.2f);
// Configurar amortiguamiento // Configurar amortiguamiento
Body.LinearDamping = 3f; // Ajustar para controlar la reducción de la velocidad lineal Body.LinearDamping = 3f; // Ajustar para controlar la reducción de la velocidad lineal
Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular
//Body.SetRestitution(0f); // Baja restitución para menos rebote Body.SetRestitution(0f); // Baja restitución para menos rebote
Body.SleepingAllowed = false; Body.SleepingAllowed = false;
Body.IsBullet = true; Body.IsBullet = true;
} }

View File

@ -1,121 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using nkast.Aether.Physics2D.Common;
namespace tainicom.Aether.Physics2D.Fluids
{
/// <summary>
/// Fluid parameters, see pvfs.pdf for a detailed explanation
/// </summary>
public struct FluidDefinition
{
/// <summary>
/// Distance of influence between the particles
/// </summary>
public float InfluenceRadius;
/// <summary>
/// Density of the fluid
/// </summary>
public float DensityRest;
/// <summary>
/// Stiffness of the fluid (when particles are far)
/// </summary>
public float Stiffness;
/// <summary>
/// Stiffness of the fluid (when particles are near)
/// Set by Check()
/// </summary>
public float StiffnessNear;
/// <summary>
/// Toggles viscosity forces
/// </summary>
public bool UseViscosity;
/// <summary>
/// See pvfs.pdf for more information
/// </summary>
public float ViscositySigma;
/// <summary>
/// See pvfs.pdf for more information
/// </summary>
public float ViscosityBeta;
/// <summary>
/// Toggles plasticity computation (springs etc.)
/// </summary>
public bool UsePlasticity;
/// <summary>
/// Plasticity, amount of memory of the shape
/// See pvfs.pdf for more information
/// </summary>
public float Plasticity;
/// <summary>
/// K of the springs used for plasticity
/// </summary>
public float KSpring;
/// <summary>
/// Amount of change of the rest length of the springs (when compressed)
/// </summary>
public float YieldRatioCompress;
/// <summary>
/// Amount of change of the rest length of the springs (when stretched)
/// </summary>
public float YieldRatioStretch;
public static FluidDefinition Default
{
get
{
FluidDefinition def = new FluidDefinition
{
InfluenceRadius = 1.0f,
DensityRest = 10.0f,
Stiffness = 10.0f,
StiffnessNear = 0.0f, // Set by Check()
UseViscosity = false,
ViscositySigma = 10.0f,
ViscosityBeta = 0.0f,
UsePlasticity = false,
Plasticity = 0.3f,
KSpring = 2.0f,
YieldRatioCompress = 0.1f,
YieldRatioStretch = 0.1f
};
def.Check();
return def;
}
}
public void Check()
{
InfluenceRadius = MathUtils.Clamp(InfluenceRadius, 0.1f, 10.0f);
DensityRest = MathUtils.Clamp(DensityRest, 1.0f, 100.0f);
Stiffness = MathUtils.Clamp(Stiffness, 0.1f, 10.0f);
StiffnessNear = Stiffness * 100.0f; // See pvfs.pdf
ViscositySigma = Math.Max(ViscositySigma, 0.0f);
ViscosityBeta = Math.Max(ViscosityBeta, 0.0f);
Plasticity = Math.Max(Plasticity, 0.0f);
KSpring = Math.Max(KSpring, 0.0f);
YieldRatioCompress = Math.Max(YieldRatioCompress, 0.0f);
YieldRatioStretch = Math.Max(YieldRatioStretch, 0.0f);
}
}
}

View File

@ -1,80 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System.Collections.Generic;
using nkast.Aether.Physics2D.Common;
namespace tainicom.Aether.Physics2D.Fluids
{
public class FluidParticle
{
public Vector2 Position;
public Vector2 PreviousPosition;
public Vector2 Velocity;
public Vector2 Acceleration;
internal FluidParticle(Vector2 position)
{
Neighbours = new List<FluidParticle>();
IsActive = true;
MoveTo(position);
Damping = 0.0f;
Mass = 1.0f;
}
public bool IsActive { get; set; }
public List<FluidParticle> Neighbours { get; private set; }
// For gameplay purposes
public float Density { get; internal set; }
public float Pressure { get; internal set; }
// Other properties
public int Index { get; internal set; }
// Physics properties
public float Damping { get; set; }
public float Mass { get; set; }
public void MoveTo(Vector2 p)
{
Position = p;
PreviousPosition = p;
Velocity = Vector2.Zero;
Acceleration = Vector2.Zero;
}
public void ApplyForce(ref Vector2 force)
{
Acceleration += force * Mass;
}
public void ApplyImpulse(ref Vector2 impulse)
{
Velocity += impulse;
}
public void Update(float deltaTime)
{
Velocity += Acceleration * deltaTime;
Vector2 delta = (1.0f - Damping) * Velocity * deltaTime;
PreviousPosition = Position;
Position += delta;
Acceleration = Vector2.Zero;
}
public void UpdateVelocity(float deltaTime)
{
Velocity = (Position - PreviousPosition) / deltaTime;
}
}
}

View File

@ -1,413 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System;
using System.Collections.Generic;
using nkast.Aether.Physics2D.Common;
namespace tainicom.Aether.Physics2D.Fluids
{
public class FluidSystem1
{
private float _influenceRadiusSquared;
private HashGrid _hashGrid = new HashGrid();
private Dictionary<SpringHash, Spring> _springs = new Dictionary<SpringHash, Spring>();
private List<SpringHash> _springsToRemove = new List<SpringHash>();
private Vector2 _totalForce;
public FluidSystem1(Vector2 gravity)
{
Gravity = gravity;
Particles = new List<FluidParticle>();
DefaultDefinition();
}
public FluidDefinition Definition { get; private set; }
public List<FluidParticle> Particles { get; private set; }
public int ParticlesCount { get { return Particles.Count; } }
public Vector2 Gravity { get; set; }
public void DefaultDefinition()
{
SetDefinition(FluidDefinition.Default);
}
public void SetDefinition(FluidDefinition def)
{
Definition = def;
Definition.Check();
_influenceRadiusSquared = Definition.InfluenceRadius * Definition.InfluenceRadius;
}
public FluidParticle AddParticle(Vector2 position)
{
FluidParticle particle = new FluidParticle(position) { Index = Particles.Count };
Particles.Add(particle);
return particle;
}
public void Clear()
{
//TODO
}
public void ApplyForce(Vector2 f)
{
_totalForce += f;
}
private void ApplyForces()
{
Vector2 f = Gravity + _totalForce;
for (int i = 0; i < Particles.Count; ++i)
{
Particles[i].ApplyForce(ref f);
}
_totalForce = Vector2.Zero;
}
private void ApplyViscosity(FluidParticle p, float timeStep)
{
for (int i = 0; i < p.Neighbours.Count; ++i)
{
FluidParticle neighbour = p.Neighbours[i];
if (p.Index >= neighbour.Index)
{
continue;
}
float q;
Vector2.DistanceSquared(ref p.Position, ref neighbour.Position, out q);
if (q > _influenceRadiusSquared)
{
continue;
}
Vector2 direction;
Vector2.Subtract(ref neighbour.Position, ref p.Position, out direction);
if (direction.LengthSquared() < float.Epsilon)
{
continue;
}
direction.Normalize();
Vector2 deltaVelocity;
Vector2.Subtract(ref p.Velocity, ref neighbour.Velocity, out deltaVelocity);
float u;
Vector2.Dot(ref deltaVelocity, ref direction, out u);
if (u > 0.0f)
{
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
float impulseFactor = 0.5f * timeStep * q * (u * (Definition.ViscositySigma + Definition.ViscosityBeta * u));
Vector2 impulse;
Vector2.Multiply(ref direction, -impulseFactor, out impulse);
p.ApplyImpulse(ref impulse);
Vector2.Multiply(ref direction, impulseFactor, out impulse);
neighbour.ApplyImpulse(ref impulse);
}
}
}
private const int MaxNeighbors = 25;
//private int _len2;
//private int _j;
//private float _q;
//private float _qq;
//private Vector2 _rij;
//private float _d;
//private Vector2 _dx;
private float _density;
private float _densityNear;
private float _pressure;
private float _pressureNear;
private float[] _distanceCache = new float[MaxNeighbors];
//private void DoubleDensityRelaxation1(FluidParticle p, float timeStep)
//{
// _density = 0;
// _densityNear = 0;
// _len2 = p.Neighbours.Count;
// if (_len2 > MaxNeighbors)
// _len2 = MaxNeighbors;
// for (_j = 0; _j < _len2; _j++)
// {
// _q = Vector2.DistanceSquared(p.Position, p.Neighbours[_j].Position);
// _distanceCache[_j] = _q;
// if (_q < _influenceRadiusSquared && _q != 0)
// {
// _q = (float)Math.Sqrt(_q);
// _q /= Definition.InfluenceRadius;
// _qq = ((1 - _q) * (1 - _q));
// _density += _qq;
// _densityNear += _qq * (1 - _q);
// }
// }
// _pressure = Definition.Stiffness * (_density - Definition.DensityRest);
// _pressureNear = Definition.StiffnessNear * _densityNear;
// _dx = Vector2.Zero;
// for (_j = 0; _j < _len2; _j++)
// {
// _q = _distanceCache[_j];
// if (_q < _influenceRadiusSquared && _q != 0)
// {
// _q = (float)Math.Sqrt(_q);
// _rij = p.Neighbours[_j].Position;
// _rij -= p.Position;
// _rij *= 1 / _q;
// _q /= _influenceRadiusSquared;
// _d = ((timeStep * timeStep) * (_pressure * (1 - _q) + _pressureNear * (1 - _q) * (1 - _q)));
// _rij *= _d * 0.5f;
// p.Neighbours[_j].Position += _rij;
// _dx -= _rij;
// }
// }
// p.Position += _dx;
//}
private void DoubleDensityRelaxation(FluidParticle particle, float deltaTime2)
{
_density = 0.0f;
_densityNear = 0.0f;
int neightborCount = particle.Neighbours.Count;
if (neightborCount > MaxNeighbors)
neightborCount = MaxNeighbors;
for (int i = 0; i < neightborCount; ++i)
{
FluidParticle neighbour = particle.Neighbours[i];
if (particle.Index == neighbour.Index)
continue;
float q;
Vector2.DistanceSquared(ref particle.Position, ref neighbour.Position, out q);
_distanceCache[i] = q;
if (q > _influenceRadiusSquared)
continue;
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
float densityDelta = q * q;
_density += densityDelta;
_densityNear += densityDelta * q;
}
_pressure = Definition.Stiffness * (_density - Definition.DensityRest);
_pressureNear = Definition.StiffnessNear * _densityNear;
// For gameplay purposes
particle.Density = _density + _densityNear;
particle.Pressure = _pressure + _pressureNear;
Vector2 delta = Vector2.Zero;
for (int i = 0; i < neightborCount; ++i)
{
FluidParticle neighbour = particle.Neighbours[i];
if (particle.Index == neighbour.Index)
continue;
float q = _distanceCache[i];
if (q > _influenceRadiusSquared)
continue;
q = 1.0f - (float)Math.Sqrt(q) / Definition.InfluenceRadius;
float dispFactor = deltaTime2 * (q * (_pressure + _pressureNear * q));
Vector2 direction;
Vector2.Subtract(ref neighbour.Position, ref particle.Position, out direction);
if (direction.LengthSquared() < float.Epsilon)
continue;
direction.Normalize();
Vector2 disp;
Vector2.Multiply(ref direction, dispFactor, out disp);
Vector2.Add(ref neighbour.Position, ref disp, out neighbour.Position);
Vector2.Multiply(ref direction, -dispFactor, out disp);
Vector2.Add(ref delta, ref disp, out delta);
}
Vector2.Add(ref particle.Position, ref delta, out particle.Position);
}
private void CreateSprings(FluidParticle p)
{
for (int i = 0; i < p.Neighbours.Count; ++i)
{
FluidParticle neighbour = p.Neighbours[i];
if (p.Index >= neighbour.Index)
continue;
float q;
Vector2.DistanceSquared(ref p.Position, ref neighbour.Position, out q);
if (q > _influenceRadiusSquared)
continue;
SpringHash hash = new SpringHash { P0 = p, P1 = neighbour };
if (!_springs.ContainsKey(hash))
{
//TODO: Use pool?
Spring spring = new Spring(p, neighbour) { RestLength = (float)Math.Sqrt(q) };
_springs.Add(hash, spring);
}
}
}
private void AdjustSprings(float timeStep)
{
foreach (var pair in _springs)
{
Spring spring = pair.Value;
spring.Update(timeStep, Definition.KSpring, Definition.InfluenceRadius);
if (spring.Active)
{
float L = spring.RestLength;
float distance;
Vector2.Distance(ref spring.P0.Position, ref spring.P1.Position, out distance);
if (distance > (L + (Definition.YieldRatioStretch * L)))
{
spring.RestLength += timeStep * Definition.Plasticity * (distance - L - (Definition.YieldRatioStretch * L));
}
else if (distance < (L - (Definition.YieldRatioCompress * L)))
{
spring.RestLength -= timeStep * Definition.Plasticity * (L - (Definition.YieldRatioCompress * L) - distance);
}
}
else
{
_springsToRemove.Add(pair.Key);
}
}
for (int i = 0; i < _springsToRemove.Count; ++i)
{
_springs.Remove(_springsToRemove[i]);
}
}
private void ComputeNeighbours()
{
_hashGrid.GridSize = Definition.InfluenceRadius;
_hashGrid.Clear();
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
_hashGrid.Add(p);
}
}
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
p.Neighbours.Clear();
_hashGrid.Find(ref p.Position, p.Neighbours);
}
}
public void Update(float deltaTime)
{
if (deltaTime == 0)
return;
float deltaTime2 = 0.5f * deltaTime * deltaTime;
ComputeNeighbours();
ApplyForces();
if (Definition.UseViscosity)
{
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
ApplyViscosity(p, deltaTime);
}
}
}
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
p.Update(deltaTime);
}
}
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
DoubleDensityRelaxation(p, deltaTime2);
}
}
if (Definition.UsePlasticity)
{
for (int i = 0; i < Particles.Count; ++i)
{
FluidParticle p = Particles[i];
if (p.IsActive)
{
CreateSprings(p);
}
}
}
AdjustSprings(deltaTime);
UpdateVelocities(deltaTime);
}
internal void UpdateVelocities(float timeStep)
{
for (int i = 0; i < Particles.Count; ++i)
{
Particles[i].UpdateVelocity(timeStep);
}
}
}
}

View File

@ -1,95 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System;
using System.Collections.Generic;
using nkast.Aether.Physics2D.Common;
namespace tainicom.Aether.Physics2D.Fluids
{
/// <summary>
/// Grid used by particle system to keep track of neightbor particles.
/// </summary>
public class HashGrid
{
private Dictionary<ulong, List<FluidParticle>> _hash = new Dictionary<ulong, List<FluidParticle>>();
private Stack<List<FluidParticle>> _bucketPool = new Stack<List<FluidParticle>>();
public HashGrid()
{
GridSize = 1.0f;
}
public float GridSize { get; set; }
private static ulong HashKey(int x, int y)
{
return ((ulong)x * 2185031351ul) ^ ((ulong)y * 4232417593ul);
}
private ulong HashKey(Vector2 position)
{
return HashKey(
(int)Math.Floor(position.X / GridSize),
(int)Math.Floor(position.Y / GridSize)
);
}
public void Clear()
{
foreach (KeyValuePair<ulong, List<FluidParticle>> pair in _hash)
{
pair.Value.Clear();
_bucketPool.Push(pair.Value);
}
_hash.Clear();
}
public void Add(FluidParticle particle)
{
ulong key = HashKey(particle.Position);
List<FluidParticle> bucket;
if (!_hash.TryGetValue(key, out bucket))
{
if (_bucketPool.Count > 0)
{
bucket = _bucketPool.Pop();
}
else
{
bucket = new List<FluidParticle>();
}
_hash.Add(key, bucket);
}
bucket.Add(particle);
}
public void Find(ref Vector2 position, List<FluidParticle> neighbours)
{
int ix = (int)Math.Floor(position.X / GridSize);
int iy = (int)Math.Floor(position.Y / GridSize);
// Check all 9 neighbouring cells
for (int x = ix - 1; x <= ix + 1; ++x)
{
for (int y = iy - 1; y <= iy + 1; ++y)
{
ulong key = HashKey(x, y);
List<FluidParticle> bucket;
if (_hash.TryGetValue(key, out bucket))
{
for (int i = 0; i < bucket.Count; ++i)
{
if (bucket[i] != null)
{
neighbours.Add(bucket[i]);
}
}
}
}
}
}
}
}

View File

@ -1,57 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using nkast.Aether.Physics2D.Common;
namespace tainicom.Aether.Physics2D.Fluids
{
//TODO: Could be struct?
public class Spring
{
public FluidParticle P0;
public FluidParticle P1;
public Spring(FluidParticle p0, FluidParticle p1)
{
Active = true;
P0 = p0;
P1 = p1;
}
public bool Active { get; set; }
public float RestLength { get; set; }
public void Update(float timeStep, float kSpring, float influenceRadius)
{
if (!Active)
return;
Vector2 dir = P1.Position - P0.Position;
float distance = dir.Length();
dir.Normalize();
// This is to avoid imploding simulation with really springy fluids
if (distance < 0.5f * influenceRadius)
{
Active = false;
return;
}
if (RestLength > influenceRadius)
{
Active = false;
return;
}
//Algorithm 3
float displacement = timeStep * timeStep * kSpring * (1.0f - RestLength / influenceRadius) * (RestLength - distance) * 0.5f;
dir *= displacement;
P0.Position -= dir;
P1.Position += dir;
}
}
}

View File

@ -1,26 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System.Collections.Generic;
namespace tainicom.Aether.Physics2D.Fluids
{
public class SpringHash : IEqualityComparer<SpringHash>
{
public FluidParticle P0;
public FluidParticle P1;
public bool Equals(SpringHash lhs, SpringHash rhs)
{
return (lhs.P0.Index == rhs.P0.Index && lhs.P1.Index == rhs.P1.Index)
|| (lhs.P0.Index == rhs.P1.Index && lhs.P1.Index == rhs.P0.Index);
}
public int GetHashCode(SpringHash s)
{
return (s.P0.Index * 73856093) ^ (s.P1.Index * 19349663) ^ (s.P0.Index * 19349663) ^ (s.P1.Index * 73856093);
}
}
}

View File

@ -1,445 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System;
using System.Collections.Generic;
using nkast.Aether.Physics2D.Common;
namespace tainicom.Aether.Physics2D.Fluids
{
public class FluidSystem2
{
public const int MaxNeighbors = 25;
public const int CellSize = 1;
// Most of these can be tuned at runtime with F1-F9 and keys 1-9 (no numpad)
public const float InfluenceRadius = 20.0f;
public const float InfluenceRadiusSquared = InfluenceRadius * InfluenceRadius;
public const float Stiffness = 0.504f;
public const float StiffnessFarNearRatio = 10.0f;
public const float StiffnessNear = Stiffness * StiffnessFarNearRatio;
public const float ViscositySigma = 0.0f;
public const float ViscosityBeta = 0.3f;
public const float DensityRest = 10.0f;
public const float KSpring = 0.3f;
public const float RestLength = 5.0f;
public const float RestLengthSquared = RestLength * RestLength;
public const float YieldRatioStretch = 0.5f;
public const float YieldRatioCompress = 0.5f;
public const float Plasticity = 0.5f;
public const int VelocityCap = 150;
public const float DeformationFactor = 0f;
public const float CollisionForce = 0.3f;
private bool _isElasticityInitialized;
private bool _elasticityEnabled;
private bool _isPlasticityInitialized;
private bool _plasticityEnabled;
private float _deltaTime2;
private Vector2 _dx = new Vector2(0.0f, 0.0f);
private const int Wpadding = 20;
private const int Hpadding = 20;
public SpatialTable Particles;
// Temp variables
private Vector2 _rij = new Vector2(0.0f, 0.0f);
private Vector2 _tempVect = new Vector2(0.0f, 0.0f);
private Dictionary<int, List<int>> _springPresenceTable;
private List<Spring2> _springs;
private List<Particle> _tempParticles;
private int _worldWidth;
private int _worldHeight;
public int ParticlesCount { get { return Particles.Count; } }
public FluidSystem2(Vector2 gravity, int maxParticleLimit, int worldWidth, int worldHeight)
{
_worldHeight = worldHeight;
_worldWidth = worldWidth;
Particles = new SpatialTable(worldWidth, worldHeight, CellSize);
MaxParticleLimit = maxParticleLimit;
Gravity = gravity;
}
public Vector2 Gravity { get; set; }
public int MaxParticleLimit { get; private set; }
public bool ElasticityEnabled
{
get { return _elasticityEnabled; }
set
{
if (!_isElasticityInitialized)
InitializeElasticity();
_elasticityEnabled = value;
}
}
public bool PlasticityEnabled
{
get { return _plasticityEnabled; }
set
{
if (!_isPlasticityInitialized)
InitializePlasticity();
_plasticityEnabled = value;
}
}
private void UpdateParticleVelocity(float deltaTime)
{
for(int i = 0; i < Particles.Count; i++)
{
Particle particle = Particles[i];
particle.PreviousPosition = particle.Position;
particle.Position = new Vector2(particle.Position.X + (deltaTime * particle.Velocity.X), particle.Position.Y + (deltaTime * particle.Velocity.Y));
}
}
private void WallCollision(Particle pi)
{
float x = 0;
float y = 0;
if (pi.Position.X > (_worldWidth / 2 - Wpadding))
x -= (pi.Position.X - (_worldWidth / 2 - Wpadding)) / CollisionForce;
else if (pi.Position.X < (-_worldWidth / 2 + Wpadding))
x += ((-_worldWidth / 2 + Wpadding) - pi.Position.X) / CollisionForce;
if (pi.Position.Y > (_worldHeight - Hpadding))
y -= (pi.Position.Y - (_worldHeight - Hpadding)) / CollisionForce;
else if (pi.Position.Y < Hpadding)
y += (Hpadding - pi.Position.Y) / CollisionForce;
pi.Velocity.X += x;
pi.Velocity.Y += y;
}
private void CapVelocity(Vector2 v)
{
if (v.X > VelocityCap)
v.X = VelocityCap;
else if (v.X < -VelocityCap)
v.X = -VelocityCap;
if (v.Y > VelocityCap)
v.Y = VelocityCap;
else if (v.Y < -VelocityCap)
v.Y = -VelocityCap;
}
private void InitializePlasticity()
{
_isPlasticityInitialized = true;
_springs.Clear();
float q;
foreach (Particle pa in Particles)
{
foreach (Particle pb in Particles)
{
if (pa.GetHashCode() == pb.GetHashCode())
continue;
Vector2.Distance(ref pa.Position, ref pb.Position, out q);
Vector2.Subtract(ref pb.Position, ref pa.Position, out _rij);
_rij /= q;
if (q < RestLength)
{
_springs.Add(new Spring2(pa, pb, q));
}
}
pa.Velocity = Vector2.Zero;
}
}
private void CalculatePlasticity(float deltaTime)
{
foreach (Spring2 spring in _springs)
{
spring.Update();
if (spring.CurrentDistance == 0)
continue;
Vector2.Subtract(ref spring.PB.Position, ref spring.PA.Position, out _rij);
_rij /= spring.CurrentDistance;
float D = deltaTime * KSpring * (spring.RestLength - spring.CurrentDistance);
_rij *= (D * 0.5f);
spring.PA.Position = new Vector2(spring.PA.Position.X - _rij.X, spring.PA.Position.Y - _rij.Y);
spring.PB.Position = new Vector2(spring.PB.Position.X + _rij.X, spring.PB.Position.Y + _rij.Y);
}
}
private void InitializeElasticity()
{
_isElasticityInitialized = true;
foreach (Particle particle in Particles)
{
_springPresenceTable.Add(particle.GetHashCode(), new List<int>(MaxParticleLimit));
particle.Velocity = Vector2.Zero;
}
}
private void CalculateElasticity(float deltaTime)
{
float sqDist;
for (int i = 0; i < Particles.Count; i++)
{
Particle pa = Particles[i];
if (Particles.CountNearBy(pa) <= 1)
continue;
_tempParticles = Particles.GetNearby(pa);
int len2 = _tempParticles.Count;
if (len2 > MaxNeighbors)
len2 = MaxNeighbors;
for (int j = 0; j < len2; j++)
{
Particle pb = Particles[j];
Vector2.DistanceSquared(ref pa.Position, ref pb.Position, out sqDist);
if (sqDist > RestLengthSquared)
continue;
if (pa.GetHashCode() == pb.GetHashCode())
continue;
if (!_springPresenceTable[pa.GetHashCode()].Contains(pb.GetHashCode()))
{
_springs.Add(new Spring2(pa, pb, RestLength));
_springPresenceTable[pa.GetHashCode()].Add(pb.GetHashCode());
}
}
}
for (int i = _springs.Count - 1; i >= 0; i--)
{
Spring2 spring = _springs[i];
spring.Update();
// Stretch
if (spring.CurrentDistance > (spring.RestLength + DeformationFactor))
{
spring.RestLength += deltaTime * Plasticity * (spring.CurrentDistance - spring.RestLength - (YieldRatioStretch * spring.RestLength));
}
// Compress
else if (spring.CurrentDistance < (spring.RestLength - DeformationFactor))
{
spring.RestLength -= deltaTime * Plasticity * (spring.RestLength - (YieldRatioCompress * spring.RestLength) - spring.CurrentDistance);
}
// Remove springs with restLength longer than REST_LENGTH
if (spring.RestLength > RestLength)
{
_springs.RemoveAt(i);
_springPresenceTable[spring.PA.GetHashCode()].Remove(spring.PB.GetHashCode());
}
else
{
if (spring.CurrentDistance == 0)
continue;
Vector2.Subtract(ref spring.PB.Position, ref spring.PA.Position, out _rij);
_rij /= spring.CurrentDistance;
float D = deltaTime * KSpring * (spring.RestLength - spring.CurrentDistance);
_rij *= (D * 0.5f);
spring.PA.Position = new Vector2(spring.PA.Position.X - _rij.X, spring.PA.Position.Y - _rij.Y);
spring.PB.Position = new Vector2(spring.PB.Position.X + _rij.X, spring.PB.Position.Y + _rij.Y);
}
}
}
private void ApplyGravity(Particle particle)
{
particle.Velocity = new Vector2(particle.Velocity.X + Gravity.X, particle.Velocity.Y + Gravity.Y);
}
private void ApplyViscosity(float deltaTime)
{
float u, q;
for (int i = 0; i < Particles.Count; i++)
{
Particle particle = Particles[i];
_tempParticles = Particles.GetNearby(particle);
int len2 = _tempParticles.Count;
if (len2 > MaxNeighbors)
len2 = MaxNeighbors;
for (int j = 0; j < len2; j++)
{
Particle tempParticle = _tempParticles[j];
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
if ((q < InfluenceRadiusSquared) && (q != 0))
{
q = (float)Math.Sqrt(q);
Vector2.Subtract(ref tempParticle.Position, ref particle.Position, out _rij);
Vector2.Divide(ref _rij, q, out _rij);
Vector2.Subtract(ref particle.Velocity, ref tempParticle.Velocity, out _tempVect);
Vector2.Dot(ref _tempVect, ref _rij, out u);
if (u <= 0.0f)
continue;
q /= InfluenceRadius;
float I = (deltaTime * (1 - q) * (ViscositySigma * u + ViscosityBeta * u * u));
Vector2.Multiply(ref _rij, (I * 0.5f), out _rij);
Vector2.Subtract(ref particle.Velocity, ref _rij, out _tempVect);
particle.Velocity = _tempVect;
_tempVect = tempParticle.Velocity;
_tempVect += _rij;
tempParticle.Velocity = _tempVect;
}
}
}
}
private void DoubleDensityRelaxation()
{
float q;
for (int i = 0; i < Particles.Count; i++)
{
Particle particle = Particles[i];
particle.Density = 0;
particle.NearDensity = 0;
_tempParticles = Particles.GetNearby(particle);
int len2 = _tempParticles.Count;
if (len2 > MaxNeighbors)
len2 = MaxNeighbors;
for (int j = 0; j < len2; j++)
{
Particle tempParticle = _tempParticles[j];
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
if (q < InfluenceRadiusSquared && q != 0)
{
q = (float)Math.Sqrt(q);
q /= InfluenceRadius;
float qq = ((1 - q) * (1 - q));
particle.Density += qq;
particle.NearDensity += qq * (1 - q);
}
}
particle.Pressure = (Stiffness * (particle.Density - DensityRest));
particle.NearPressure = (StiffnessNear * particle.NearDensity);
_dx = Vector2.Zero;
for (int j = 0; j < len2; j++)
{
Particle tempParticle = _tempParticles[j];
Vector2.DistanceSquared(ref particle.Position, ref tempParticle.Position, out q);
if ((q < InfluenceRadiusSquared) && (q != 0))
{
q = (float)Math.Sqrt(q);
Vector2.Subtract(ref tempParticle.Position, ref particle.Position, out _rij);
Vector2.Divide(ref _rij, q, out _rij);
q /= InfluenceRadius;
float D = (_deltaTime2 * (particle.Pressure * (1 - q) + particle.NearPressure * (1 - q) * (1 - q)));
Vector2.Multiply(ref _rij, (D * 0.5f), out _rij);
tempParticle.Position = new Vector2(tempParticle.Position.X + _rij.X, tempParticle.Position.Y + _rij.Y);
Vector2.Subtract(ref _dx, ref _rij, out _dx);
}
}
particle.Position = particle.Position + _dx;
}
}
public void Update(float deltaTime)
{
if (deltaTime == 0)
return;
_deltaTime2 = deltaTime * deltaTime;
ApplyViscosity(deltaTime);
//Update velocity
UpdateParticleVelocity(deltaTime);
Particles.Rehash();
if (_elasticityEnabled)
CalculateElasticity(deltaTime);
if (_plasticityEnabled)
CalculatePlasticity(deltaTime);
DoubleDensityRelaxation();
for(int i = 0; i < Particles.Count; i++)
{
Particle particle = Particles[i];
particle.Velocity = new Vector2((particle.Position.X - particle.PreviousPosition.X) / deltaTime, (particle.Position.Y - particle.PreviousPosition.Y) / deltaTime);
ApplyGravity(particle);
WallCollision(particle);
CapVelocity(particle.Velocity);
}
}
public void AddParticle(Vector2 position)
{
Particles.Add(new Particle(position.X, position.Y));
}
}
public class Particle
{
public float Density;
public float NearDensity;
public float NearPressure;
public Vector2 Position = new Vector2(0, 0);
public float Pressure;
public Vector2 PreviousPosition = new Vector2(0, 0);
public Vector2 Velocity = new Vector2(0, 0);
public Particle(float posX, float posY)
{
Position = new Vector2(posX, posY);
}
}
public class Spring2
{
public float CurrentDistance;
public Particle PA;
public Particle PB;
public float RestLength;
public Spring2(Particle pa, Particle pb, float restLength)
{
PA = pa;
PB = pb;
RestLength = restLength;
}
public void Update()
{
Vector2.Distance(ref PA.Position, ref PB.Position, out CurrentDistance);
}
public bool Contains(Particle p)
{
return (PA.GetHashCode() == p.GetHashCode() || PB.GetHashCode() == p.GetHashCode());
}
}
}

View File

@ -1,179 +0,0 @@
/* Original source Farseer Physics Engine:
* Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com
* Microsoft Permissive License (Ms-PL) v1.1
*/
using System.Collections;
using System.Collections.Generic;
namespace tainicom.Aether.Physics2D.Fluids
{
public class SpatialTable : IEnumerable<Particle>
{
// default nearby table size
private const int DefaultNearbySize = 50;
private List<Particle> _table;
private List<Particle> _voidList = new List<Particle>(1);
private List<Particle>[][] _nearby;
bool _initialized;
private int _row;
private int _column;
private int _cellSize;
public SpatialTable(int column, int row, int cellSize)
{
_row = row;
_cellSize = cellSize;
_column = column;
}
public void Initialize()
{
_table = new List<Particle>((_row * _column) / 2);
_nearby = new List<Particle>[_column][];
for (int i = 0; i < _column; ++i)
{
_nearby[i] = new List<Particle>[_row];
for (int j = 0; j < _row; ++j)
{
_nearby[i][j] = new List<Particle>(DefaultNearbySize);
}
}
_initialized = true;
}
/// <summary>
/// Append value to the table and identify its position in the space.
/// Don't need to rehash table after append operation.</summary>
/// <param name="value"></param>
public void Add(Particle value)
{
if (!_initialized)
Initialize();
AddInterRadius(value);
_table.Add(value);
}
public Particle this[int i]
{
get { return _table[i]; }
set { _table[i] = value; }
}
public void Remove(Particle value)
{
_table.Remove(value);
}
public void Clear()
{
for (int i = 0; i < _column; ++i)
{
for (int j = 0; j < _row; ++j)
{
_nearby[i][j].Clear();
_nearby[i][j] = null;
}
}
_table.Clear();
}
public int Count
{
get { return (_table == null)? 0 : _table.Count; }
}
public List<Particle> GetNearby(Particle value)
{
int x = posX(value);
int y = posY(value);
if (!InRange(x, y))
return _voidList;
return _nearby[x][y];
}
private int posX(Particle value)
{
return (int)((value.Position.X + (_column / 2) + 0.3f) / _cellSize);
}
private int posY(Particle value)
{
return (int)((value.Position.Y + 0.3f) / _cellSize);
}
public int CountNearBy(Particle value)
{
return GetNearby(value).Count;
}
/// <summary>
/// Updates the spatial relationships of objects. Rehash function
/// needed if elements change their position in the space.
/// </summary>
public void Rehash()
{
if (_table == null || _table.Count == 0)
return;
for (int i = 0; i < _column; i++)
{
for (int j = 0; j < _row; j++)
{
if (_nearby[i][j] != null)
_nearby[i][j].Clear();
}
}
foreach (Particle particle in _table)
{
AddInterRadius(particle);
}
}
/// <summary>
/// Add element to its position and neighbor cells.
/// </summary>
/// <param name="value"></param>
private void AddInterRadius(Particle value)
{
for (int i = -1; i < 2; ++i)
{
for (int j = -1; j < 2; ++j)
{
int x = posX(value) + i;
int y = posY(value) + j;
if (InRange(x, y))
_nearby[x][y].Add(value);
}
}
}
/// <summary>
/// Check if a position is out of the spatial range
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>true if position is in range.</returns>
private bool InRange(float x, float y)
{
return (x > 0 && x < _column && y > 0 && y < _row);
}
public IEnumerator<Particle> GetEnumerator()
{
return _table.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -88,41 +88,21 @@ namespace CtrEditor.PopUps
MatrixRows.Clear(); MatrixRows.Clear();
_dataGrid.Columns.Clear(); _dataGrid.Columns.Clear();
if (!MatrixItems.Any()) if (!MatrixItems.Any()) return;
{
Debug.WriteLine("No matrix items found.");
return;
}
Debug.WriteLine($"Total matrix items: {MatrixItems.Count}");
// Add Image column first
var imageColumn = new DataGridTextColumn
{
Header = "Image",
Binding = new Binding("[Image]"),
Width = DataGridLength.Auto
};
_dataGrid.Columns.Add(imageColumn);
// Group items by source image and then by column number // Group items by source image and then by column number
var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image).ToList(); var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image);
Debug.WriteLine($"Images in matrix: {itemsByImage.Count}");
// Sort items by column number // Ordenar items por número de columna
var orderedItems = MatrixItems var orderedItems = MatrixItems
.GroupBy(x => x.ColumnNumber) .GroupBy(x => x.ColumnNumber)
.OrderBy(g => g.Key) .OrderBy(g => g.Key)
.Select(g => g.First()) .Select(g => g.First())
.ToList(); .ToList();
Debug.WriteLine($"Distinct columns: {orderedItems.Count}"); // Crear columnas
// Create data grid columns
foreach (var item in orderedItems) foreach (var item in orderedItems)
{ {
if (item.ColumnNumber <= 0) continue; // Skip invalid column numbers
var column = new DataGridTextColumn var column = new DataGridTextColumn
{ {
Header = $"{item.ColumnNumber}:{item.ColumnName}", Header = $"{item.ColumnNumber}:{item.ColumnName}",
@ -135,25 +115,22 @@ namespace CtrEditor.PopUps
// Process each image's data // Process each image's data
foreach (var imageGroup in itemsByImage) foreach (var imageGroup in itemsByImage)
{ {
Debug.WriteLine($"Processing image: {imageGroup.Key} with {imageGroup.Count()} items");
// Calculate max rows needed for this image // Calculate max rows needed for this image
int maxRows = 1; int maxRows = 1;
var clonedItems = imageGroup.Where(x => x.IsCloned).ToList(); var clonedItems = imageGroup.Where(x => x.IsCloned).ToList();
if (clonedItems.Any()) if (clonedItems.Any())
{ {
maxRows = clonedItems.Max(x => x.Copy_Number) + 1; maxRows = clonedItems.Max(x => x.Copy_Number) + 1;
Debug.WriteLine($"Found cloned items, max rows: {maxRows}");
} }
// Create rows for this image // Create rows for this image
for (int row = 0; row < maxRows; row++) for (int row = 0; row < maxRows; row++)
{ {
var rowData = new Dictionary<string, string>(); var rowData = new Dictionary<string, string>();
// Add image identifier // Add image identifier
rowData["Image"] = imageGroup.Key; rowData["Image"] = imageGroup.Key;
// Add fixed (non-cloned) values in all rows // Add fixed (non-cloned) values in all rows
foreach (var item in imageGroup.Where(x => !x.IsCloned)) foreach (var item in imageGroup.Where(x => !x.IsCloned))
{ {
@ -175,8 +152,6 @@ namespace CtrEditor.PopUps
MatrixRows.Add(rowData); MatrixRows.Add(rowData);
} }
} }
Debug.WriteLine($"Total matrix rows created: {MatrixRows.Count}");
} }
private void UpdateColumnHeaders() private void UpdateColumnHeaders()
@ -208,22 +183,13 @@ namespace CtrEditor.PopUps
{ {
// Store current image // Store current image
var currentImage = _mainViewModel.SelectedImage; var currentImage = _mainViewModel.SelectedImage;
// Change to target image and wait for UI update // Change to target image and wait for UI update
_mainViewModel.SelectedImage = imageName; _mainViewModel.SelectedImage = imageName;
await _mainViewModel.WaitForUIUpdateAsync(); await _mainViewModel.WaitForUIUpdateAsync();
// Analyze current page // Analyze current page
var items = AnalyzePage(); var items = AnalyzePage();
if (items.Count == 0)
{
Debug.WriteLine($"Warning: No items found for image {imageName}");
}
else
{
Debug.WriteLine($"Found {items.Count} items for image {imageName}");
}
foreach (var item in items) foreach (var item in items)
{ {
item.Source_Image = imageName; item.Source_Image = imageName;
@ -238,17 +204,11 @@ namespace CtrEditor.PopUps
} }
} }
} }
catch (Exception ex)
{
Debug.WriteLine($"Error in AnalyzeMatrixMultiPage: {ex.Message}");
MessageBox.Show($"Error analyzing matrix: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally finally
{ {
_mainViewModel.HasUnsavedChanges = originalHasUnsavedChanges; _mainViewModel.HasUnsavedChanges = originalHasUnsavedChanges;
} }
Debug.WriteLine($"Total items collected: {MatrixItems.Count}");
ResolveColumnConflicts(); ResolveColumnConflicts();
UpdateMatrixPreview(); UpdateMatrixPreview();
} }
@ -291,17 +251,13 @@ namespace CtrEditor.PopUps
// Get base tags that are linked to Search Templates // Get base tags that are linked to Search Templates
var osExtraccionTagBaseGrouped_List = _mainViewModel.ObjetosSimulables var osExtraccionTagBaseGrouped_List = _mainViewModel.ObjetosSimulables
.OfType<osExtraccionTag>() .OfType<osExtraccionTag>()
.Where(tag => tag.Show_On_This_Page && .Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "")
!tag.Cloned &&
!string.IsNullOrEmpty(tag.Id_Search_Templates))
.ToList(); .ToList();
// Get fixed tags (not linked to Search Templates) // Get fixed tags (not linked to Search Templates)
var osExtraccionTagBaseFix_List = _mainViewModel.ObjetosSimulables var osExtraccionTagBaseFix_List = _mainViewModel.ObjetosSimulables
.OfType<osExtraccionTag>() .OfType<osExtraccionTag>()
.Where(tag => tag.Show_On_This_Page && .Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == ""))
!tag.Cloned &&
string.IsNullOrEmpty(tag.Id_Search_Templates))
.ToList(); .ToList();
// Get cloned tags (created by Search Templates) // Get cloned tags (created by Search Templates)
@ -310,11 +266,6 @@ namespace CtrEditor.PopUps
.Where(tag => tag.Show_On_This_Page && tag.Cloned) .Where(tag => tag.Show_On_This_Page && tag.Cloned)
.ToList(); .ToList();
// Log counts for debugging
Debug.WriteLine($"Fixed tags: {osExtraccionTagBaseFix_List.Count}");
Debug.WriteLine($"Grouped tags: {osExtraccionTagBaseGrouped_List.Count}");
Debug.WriteLine($"Cloned tags: {osExtraccionTagCloned_List.Count}");
// Process fixed tags // Process fixed tags
foreach (var tag in osExtraccionTagBaseFix_List) foreach (var tag in osExtraccionTagBaseFix_List)
{ {
@ -327,9 +278,7 @@ namespace CtrEditor.PopUps
Value = tag.Tag_extract, Value = tag.Tag_extract,
Type = "Fixed", Type = "Fixed",
IsCloned = false, IsCloned = false,
Id = tag.Id, Id = tag.Id
Source_Image = _mainViewModel.SelectedImage,
Copy_Number = 0
}); });
} }
@ -345,9 +294,7 @@ namespace CtrEditor.PopUps
Value = tag.Tag_extract, Value = tag.Tag_extract,
Type = $"Grouped ({tag.Id_Search_Templates})", Type = $"Grouped ({tag.Id_Search_Templates})",
IsCloned = false, IsCloned = false,
Id = tag.Id, Id = tag.Id
Source_Image = _mainViewModel.SelectedImage,
Copy_Number = 0
}); });
} }
@ -364,7 +311,6 @@ namespace CtrEditor.PopUps
Type = "Cloned", Type = "Cloned",
IsCloned = true, IsCloned = true,
Id = tag.Id, Id = tag.Id,
Source_Image = _mainViewModel.SelectedImage,
Copy_Number = tag.Copy_Number Copy_Number = tag.Copy_Number
}); });
} }

Binary file not shown.

Binary file not shown.

View File

@ -1,132 +0,0 @@
# Script para descargar e instalar modelos PaddleOCR
# Guardar como: download-paddleocr-models.ps1
param(
[string]$OutputDirectory = ".\paddleocr",
[switch]$ForceDownload = $false
)
# URLs de modelos PaddleOCR (inglés por defecto)
$modelUrls = @{
"det" = "https://paddleocr.bj.bcebos.com/PP-OCRv3/english/en_PP-OCRv3_det_slim_infer.tar"
"rec" = "https://paddleocr.bj.bcebos.com/PP-OCRv3/english/en_PP-OCRv3_rec_slim_infer.tar"
"cls" = "https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_cls_slim_infer.tar"
"keys" = "https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/release/2.6/ppocr/utils/en_dict.txt"
}
# Crear directorios si no existen
function EnsureDirectory($path) {
if (-not (Test-Path $path)) {
New-Item -Path $path -ItemType Directory | Out-Null
Write-Host "Creado directorio: $path" -ForegroundColor Green
}
}
# Descargar archivo con barra de progreso
function DownloadFile($url, $outputFile) {
$webClient = New-Object System.Net.WebClient
$webClient.Encoding = [System.Text.Encoding]::UTF8
$downloadExists = Test-Path $outputFile
if ($downloadExists -and -not $ForceDownload) {
Write-Host "El archivo ya existe: $outputFile. Use -ForceDownload para sobrescribir." -ForegroundColor Yellow
return $false
}
Write-Host "Descargando $url..." -ForegroundColor Cyan
try {
$webClient.DownloadFile($url, $outputFile)
Write-Host "Descarga completada: $outputFile" -ForegroundColor Green
return $true
} catch {
Write-Host "Error al descargar $url : $_" -ForegroundColor Red
return $false
}
}
# Extraer archivo TAR
function ExtractTarFile($tarFile, $destDir) {
Write-Host "Extrayendo $tarFile..." -ForegroundColor Cyan
# Verificar si tar está disponible
$tarAvailable = $null -ne (Get-Command "tar" -ErrorAction SilentlyContinue)
if ($tarAvailable) {
# Usar tar nativo
try {
tar -xf $tarFile -C $destDir
Write-Host "Extracción completada: $tarFile" -ForegroundColor Green
return $true
} catch {
Write-Host "Error al extraer con tar: $_" -ForegroundColor Red
}
}
# Fallback a .NET
try {
Add-Type -AssemblyName System.IO.Compression.FileSystem
# Descomprimir TAR con .NET
# Nota: Esto es un ejemplo simplificado. Necesitarías una biblioteca específica para TAR
Write-Host "TAR nativo no disponible. Se requiere una biblioteca para manejar archivos TAR." -ForegroundColor Yellow
# Aquí puedes agregar código para usar SharpCompress u otra biblioteca para TAR
# Por ejemplo:
# [Reflection.Assembly]::LoadFrom("path\to\SharpCompress.dll")
# $reader = [SharpCompress.Readers.ReaderFactory]::Open($tarFile)
# ...
return $false
} catch {
Write-Host "Error al extraer $tarFile : $_" -ForegroundColor Red
return $false
}
}
# Crear directorio principal
EnsureDirectory $OutputDirectory
# Crear estructura de directorios
$modelTypes = @("det", "rec", "cls", "keys")
foreach ($type in $modelTypes) {
$typeDir = Join-Path $OutputDirectory $type
EnsureDirectory $typeDir
if ($type -ne "keys") {
$inferenceDir = Join-Path $typeDir "inference"
EnsureDirectory $inferenceDir
}
}
# Descargar y procesar cada modelo
foreach ($entry in $modelUrls.GetEnumerator()) {
$type = $entry.Key
$url = $entry.Value
$typeDir = Join-Path $OutputDirectory $type
if ($type -eq "keys") {
# Para el archivo de claves, solo descargarlo directamente
$outputFile = Join-Path $typeDir "ppocr_keys.txt"
DownloadFile $url $outputFile
} else {
# Para los modelos, descargar TAR y extraerlo
$tarFile = Join-Path $env:TEMP "$type.tar"
$downloaded = DownloadFile $url $tarFile
if ($downloaded) {
$extracted = ExtractTarFile $tarFile $typeDir
# Limpiar archivo temporal
if (Test-Path $tarFile) {
Remove-Item $tarFile -Force
}
}
}
}
Write-Host "`nInstalación de modelos PaddleOCR completada en: $OutputDirectory" -ForegroundColor Green
Write-Host "Para usar estos modelos, configura PaddleOCREngine con las siguientes rutas:" -ForegroundColor Yellow
Write-Host " - det_infer: $OutputDirectory\det\inference" -ForegroundColor White
Write-Host " - rec_infer: $OutputDirectory\rec\inference" -ForegroundColor White
Write-Host " - cls_infer: $OutputDirectory\cls\inference" -ForegroundColor White
Write-Host " - keys: $OutputDirectory\keys\ppocr_keys.txt" -ForegroundColor White

View File

@ -1,95 +0,0 @@
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/