Compare commits
6 Commits
304bdb06d4
...
d1ec333243
Author | SHA1 | Date |
---|---|---|
|
d1ec333243 | |
|
20bdad509b | |
|
fe8f2119ce | |
|
736068619a | |
|
98c5f2e6ff | |
|
211c518be6 |
|
@ -18,6 +18,12 @@ namespace CtrEditor.Controls
|
|||
UserControlFactory.CargarPropiedadesosDatos(selectedObject, PropertyGridControl);
|
||||
}
|
||||
|
||||
// Add a new method to clear properties
|
||||
public void ClearProperties()
|
||||
{
|
||||
UserControlFactory.LimpiarPropiedadesosDatos(PropertyGridControl);
|
||||
}
|
||||
|
||||
public bool IsKeyboardFocusWithin => PropertyGridControl.IsKeyboardFocusWithin;
|
||||
|
||||
private void ImagePathButton_Click(object sender, RoutedEventArgs e)
|
||||
|
|
|
@ -77,18 +77,19 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aether.Physics2D" Version="2.1.0" />
|
||||
<PackageReference Include="ClosedXML" Version="0.104.2" />
|
||||
<PackageReference Include="Aether.Physics2D" Version="2.2.0" />
|
||||
<PackageReference Include="ClosedXML" Version="0.105.0-rc" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Emgu.CV" Version="4.9.0.5494" />
|
||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
|
||||
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
|
||||
<PackageReference Include="Emgu.CV" Version="4.10.0.5680" />
|
||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.10.0.5680" />
|
||||
<PackageReference Include="Emgu.CV.UI" Version="4.10.0.5680" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.7.25104.5739" />
|
||||
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
||||
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<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.Drawing" Version="5.2.0" />
|
||||
</ItemGroup>
|
||||
|
@ -172,4 +173,11 @@
|
|||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="paddleocr\cls\inference\" />
|
||||
<Folder Include="paddleocr\det\inference\" />
|
||||
<Folder Include="paddleocr\keys\" />
|
||||
<Folder Include="paddleocr\rec\inference\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,623 @@
|
|||
<?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>
|
|
@ -0,0 +1,30 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
|
||||
|
||||
namespace CtrEditor.FuncionesBase
|
||||
|
@ -15,8 +16,10 @@ namespace CtrEditor.FuncionesBase
|
|||
{ "DESCRIPCION", "All uppercase text" },
|
||||
{ "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" },
|
||||
{ "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" },
|
||||
{ "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" },
|
||||
{ "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces allowed)" },
|
||||
{ "LETRASNUMEROS", "Format: ABC...123... (letters and numbers only, other chars become _)" },
|
||||
{ "LETRASNUMEROS/*-+", "Format: ABC...123... (letters, numbers, and /*-+ chars only, other chars become _)" },
|
||||
{ "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" }
|
||||
};
|
||||
|
||||
|
@ -36,7 +39,9 @@ namespace CtrEditor.FuncionesBase
|
|||
"Siemens IO Input" => ApplySiemensPattern(text, "E"),
|
||||
"Siemens IO Output" => ApplySiemensPattern(text, "A"),
|
||||
"LETRASNUMEROS" => ApplyLetrasNumerosPattern(text),
|
||||
"LETRASNUMEROS/*-+" => ApplyLetrasNumerosSpecialPattern(text),
|
||||
"LETRASNUMEROSESPACIOS" => ApplyLetrasNumerosEspaciosPattern(text),
|
||||
"LETRASNUMEROSESPACIOS/*-+" => ApplyLetrasNumerosEspaciosSpecialPattern(text),
|
||||
"Numero" => ApplyNumberPattern(text),
|
||||
_ => text
|
||||
};
|
||||
|
@ -54,7 +59,7 @@ namespace CtrEditor.FuncionesBase
|
|||
if (match.Success)
|
||||
{
|
||||
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;
|
||||
return $"{prefix}{address}.{bit}";
|
||||
}
|
||||
|
@ -63,29 +68,67 @@ namespace CtrEditor.FuncionesBase
|
|||
|
||||
private static string ApplyLetrasNumerosPattern(string text)
|
||||
{
|
||||
// Extract letters and numbers from the text
|
||||
var letters = new string(text.Where(c => char.IsLetter(c)).Take(10).ToArray());
|
||||
var numbers = new string(text.Where(c => char.IsDigit(c)).ToArray());
|
||||
// Replace any character that is not a letter or digit with "_"
|
||||
char[] result = new char[text.Length];
|
||||
|
||||
// If no letters found, return "A" as default
|
||||
if (string.IsNullOrEmpty(letters))
|
||||
letters = "A";
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (char.IsLetter(text[i]) || char.IsDigit(text[i]))
|
||||
result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
|
||||
else
|
||||
result[i] = '_';
|
||||
}
|
||||
|
||||
// If no numbers found, return "0" as default
|
||||
if (string.IsNullOrEmpty(numbers))
|
||||
numbers = "0";
|
||||
|
||||
// Combine letters (uppercase) and numbers
|
||||
return $"{letters.ToUpper()}{numbers}";
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
private static string ApplyLetrasNumerosEspaciosPattern(string text)
|
||||
{
|
||||
// Keep only letters, numbers and spaces
|
||||
var cleanedText = new string(text.Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)).ToArray());
|
||||
|
||||
// Convert to uppercase
|
||||
return cleanedText.ToUpper();
|
||||
// Replace any character that is not a letter, digit, or space 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]))
|
||||
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)
|
||||
|
@ -107,4 +150,4 @@ namespace CtrEditor.FuncionesBase
|
|||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -273,10 +273,9 @@ namespace CtrEditor
|
|||
|
||||
partial void OnSelectedItemOsListChanged(osBase value)
|
||||
{
|
||||
if (value != null)
|
||||
habilitarEliminarUserControl = true;
|
||||
else
|
||||
habilitarEliminarUserControl = false;
|
||||
// Enable delete and duplicate commands when either an individual item is selected
|
||||
// or when there are multiple objects selected
|
||||
habilitarEliminarUserControl = _objectManager.SelectedObjects.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -519,8 +518,66 @@ namespace CtrEditor
|
|||
|
||||
private void DuplicarUserControl()
|
||||
{
|
||||
if (SelectedItemOsList is osBase objDuplicar)
|
||||
DuplicarObjeto(objDuplicar, 0.5f, 0.5f);
|
||||
if (_objectManager.SelectedObjects.Count > 0)
|
||||
{
|
||||
// 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)
|
||||
|
@ -575,6 +632,7 @@ namespace CtrEditor
|
|||
private void EliminarUserControl()
|
||||
{
|
||||
_objectManager.EliminarObjetosSeleccionados();
|
||||
SelectedItemOsList = null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -887,7 +945,15 @@ namespace CtrEditor
|
|||
// Se cargan los datos de cada UserControl en el StackPanel
|
||||
public void CargarPropiedadesosDatos(osBase selectedObject, Controls.PanelEdicionControl PanelEdicion, ResourceDictionary Resources)
|
||||
{
|
||||
PanelEdicion.CargarPropiedades(selectedObject);
|
||||
if (selectedObject == null)
|
||||
{
|
||||
// Clear the property panel when no object is selected
|
||||
PanelEdicion.ClearProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
PanelEdicion.CargarPropiedades(selectedObject);
|
||||
}
|
||||
}
|
||||
|
||||
private RelayCommand saveCommand;
|
||||
|
@ -1010,12 +1076,22 @@ namespace CtrEditor
|
|||
HasUnsavedChanges = true;
|
||||
_objectManager.UpdateSelectionVisuals();
|
||||
}
|
||||
|
||||
// Add this method to MainViewModel class
|
||||
public void NotifySelectionChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(SelectedItemOsList));
|
||||
}
|
||||
}
|
||||
|
||||
public class SimulationData
|
||||
{
|
||||
public ObservableCollection<osBase>? ObjetosSimulables { get; set; }
|
||||
public UnitConverter? UnitConverter { 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
|
||||
|
|
|
@ -319,7 +319,6 @@ namespace CtrEditor
|
|||
|
||||
private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
|
||||
if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject)
|
||||
{
|
||||
// Siempre trabajar con selección única para las propiedades
|
||||
|
@ -327,8 +326,10 @@ namespace CtrEditor
|
|||
|
||||
_objectManager.SelectObject(selectedObject);
|
||||
}
|
||||
else
|
||||
else if (e.RemovedItems.Count > 0 && e.AddedItems.Count == 0)
|
||||
{
|
||||
// This handles the case when an item is deselected and no new item is selected
|
||||
CargarPropiedadesosDatos(null);
|
||||
_objectManager.RemoveResizeRectangles();
|
||||
}
|
||||
}
|
||||
|
@ -367,6 +368,7 @@ namespace CtrEditor
|
|||
{
|
||||
if (e.Key == Key.Delete)
|
||||
{
|
||||
// This will delete all selected objects
|
||||
_objectManager.EliminarObjetosSeleccionados();
|
||||
e.Handled = true;
|
||||
}
|
||||
|
@ -443,7 +445,15 @@ namespace CtrEditor
|
|||
private void CargarPropiedadesosDatos(osBase selectedObject)
|
||||
{
|
||||
if (DataContext is MainViewModel viewModel)
|
||||
{
|
||||
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()
|
||||
|
|
|
@ -74,6 +74,8 @@ namespace CtrEditor
|
|||
private Image _backgroundImage; // Add this line
|
||||
internal bool IsDraggingCanvas { get; set; }
|
||||
private bool _isRectangleSelectionActive;
|
||||
private bool _selectedObjectsAreVisible;
|
||||
|
||||
public bool IsRectangleSelectionActive
|
||||
{
|
||||
get => _isRectangleSelectionActive;
|
||||
|
@ -181,35 +183,37 @@ namespace CtrEditor
|
|||
public void AddResizeRectangles(IEnumerable<osBase> selectedObjects)
|
||||
{
|
||||
double rectHighlightSize = 1;
|
||||
RemoveResizeRectangles();
|
||||
RemoveResizeRectangles();
|
||||
|
||||
// Calcular el bounding box que contenga todos los objetos seleccionados
|
||||
Rect boundingBox = CalculateTotalBoundingBox(selectedObjects);
|
||||
if (_selectedObjectsAreVisible) {
|
||||
|
||||
FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(boundingBox);
|
||||
rectBox.Left -= (float)rectHighlightSize;
|
||||
rectBox.Right += (float)rectHighlightSize;
|
||||
rectBox.Top -= (float)rectHighlightSize;
|
||||
rectBox.Bottom += (float)rectHighlightSize;
|
||||
FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(boundingBox);
|
||||
rectBox.Left -= (float)rectHighlightSize;
|
||||
rectBox.Right += (float)rectHighlightSize;
|
||||
rectBox.Top -= (float)rectHighlightSize;
|
||||
rectBox.Bottom += (float)rectHighlightSize;
|
||||
|
||||
_transformedBoundingBoxCenter = new Point(
|
||||
boundingBox.Left + boundingBox.Width / 2,
|
||||
boundingBox.Top + boundingBox.Height / 2
|
||||
);
|
||||
_transformedBoundingBoxCenter = new Point(
|
||||
boundingBox.Left + boundingBox.Width / 2,
|
||||
boundingBox.Top + boundingBox.Height / 2
|
||||
);
|
||||
|
||||
// Selection rectangle
|
||||
Rectangle selectionRect = CreateSelectionRectangle(rectBox, rectHighlightSize);
|
||||
_resizeRectangles.Add(selectionRect);
|
||||
_canvas.Children.Add(selectionRect);
|
||||
// Selection rectangle
|
||||
Rectangle selectionRect = CreateSelectionRectangle(rectBox, rectHighlightSize);
|
||||
_resizeRectangles.Add(selectionRect);
|
||||
_canvas.Children.Add(selectionRect);
|
||||
|
||||
// Load rotation cursors
|
||||
Cursor rotationCursorRx = new Cursor(Application.GetResourceStream(
|
||||
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationRx.cur")).Stream);
|
||||
Cursor rotationCursorSx = new Cursor(Application.GetResourceStream(
|
||||
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationSx.cur")).Stream);
|
||||
// Load rotation cursors
|
||||
Cursor rotationCursorRx = new Cursor(Application.GetResourceStream(
|
||||
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationRx.cur")).Stream);
|
||||
Cursor rotationCursorSx = new Cursor(Application.GetResourceStream(
|
||||
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationSx.cur")).Stream);
|
||||
|
||||
// Add resize/rotation handles
|
||||
AddResizeHandles(rectBox, 10, rotationCursorRx, rotationCursorSx);
|
||||
// Add resize/rotation handles
|
||||
AddResizeHandles(rectBox, 10, rotationCursorRx, rotationCursorSx);
|
||||
}
|
||||
}
|
||||
|
||||
private Rect CalculateTotalBoundingBox(IEnumerable<osBase> selectedObjects)
|
||||
|
@ -218,10 +222,11 @@ namespace CtrEditor
|
|||
double top = double.MaxValue;
|
||||
double right = double.MinValue;
|
||||
double bottom = double.MinValue;
|
||||
_selectedObjectsAreVisible = false;
|
||||
|
||||
foreach (var obj in selectedObjects)
|
||||
{
|
||||
if (obj.VisualRepresentation != null)
|
||||
if (obj.VisualRepresentation != null && obj.VisualRepresentation.Visibility!=Visibility.Collapsed)
|
||||
{
|
||||
// Obtener el bounding box del objeto actual
|
||||
Rect objectBounds = VisualTreeHelper.GetDescendantBounds(obj.VisualRepresentation);
|
||||
|
@ -233,6 +238,7 @@ namespace CtrEditor
|
|||
top = Math.Min(top, transformedBounds.Top);
|
||||
right = Math.Max(right, transformedBounds.Right);
|
||||
bottom = Math.Max(bottom, transformedBounds.Bottom);
|
||||
_selectedObjectsAreVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,6 +435,9 @@ namespace CtrEditor
|
|||
}
|
||||
|
||||
UpdateSelectionVisuals();
|
||||
|
||||
// Update the view model's selection state
|
||||
vm.NotifySelectionChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -440,6 +449,12 @@ namespace CtrEditor
|
|||
_selectedObjects.Remove(obj);
|
||||
obj.IsSelected = false;
|
||||
RemoveSelectionHighlight(obj.VisualRepresentation);
|
||||
|
||||
// Update the view model's selection state
|
||||
if (_mainWindow.DataContext is MainViewModel vm)
|
||||
{
|
||||
vm.NotifySelectionChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -899,6 +914,9 @@ namespace CtrEditor
|
|||
RemoveResizeRectangles();
|
||||
RemoveAllSelectionHighlights();
|
||||
|
||||
// Ensure the property panel is cleared by explicitly setting SelectedItemOsList to null
|
||||
viewModel.SelectedItemOsList = null;
|
||||
|
||||
// Actualizar el estado de cambios sin guardar
|
||||
if (viewModel != null)
|
||||
{
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
<UserControl x:Class="CtrEditor.ObjetosSim.ucCustomImage"
|
||||
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>
|
||||
<vm:osCustomImage />
|
||||
</UserControl.DataContext>
|
||||
|
||||
<UserControl.Resources>
|
||||
<funcionesbase:BooleanToDoubleConverter x:Key="FlipConverter" TrueValue="-1" FalseValue="1"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid RenderTransformOrigin="0.5,0.5">
|
||||
<Grid.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform
|
||||
ScaleX="{Binding Horizontal_Flip, Converter={StaticResource FlipConverter}}"
|
||||
ScaleY="{Binding Vertical_Flip, Converter={StaticResource FlipConverter}}"/>
|
||||
<RotateTransform Angle="{Binding Angulo}"/>
|
||||
</TransformGroup>
|
||||
</Grid.RenderTransform>
|
||||
|
|
|
@ -33,6 +33,16 @@ namespace CtrEditor.ObjetosSim
|
|||
[property: Category("Image:")]
|
||||
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]
|
||||
[ObservableProperty]
|
||||
|
@ -51,6 +61,16 @@ 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()
|
||||
{
|
||||
Ancho = 0.30f;
|
||||
|
|
|
@ -68,6 +68,13 @@ namespace CtrEditor.ObjetosSim
|
|||
[property: Category("Encoders:")]
|
||||
public float k_encoder_X;
|
||||
|
||||
[ObservableProperty]
|
||||
[property: ReadOnly(true)]
|
||||
[property: Description("Actual Position X")]
|
||||
[property: Category("Encoders:")]
|
||||
public float encoder_X_Position;
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
[property: Description("X in meter offset Left. Position when the encoder is 0")]
|
||||
[property: Category("Encoders:")]
|
||||
|
@ -85,6 +92,12 @@ namespace CtrEditor.ObjetosSim
|
|||
[property: Category("Encoders:")]
|
||||
public float k_encoder_Y;
|
||||
|
||||
[ObservableProperty]
|
||||
[property: ReadOnly(true)]
|
||||
[property: Description("Actual Position Y")]
|
||||
[property: Category("Encoders:")]
|
||||
public float encoder_Y_Position;
|
||||
|
||||
[ObservableProperty]
|
||||
[property: Description("Y in meter offset Top. Position when the encoder is 0")]
|
||||
[property: Category("Encoders:")]
|
||||
|
@ -186,11 +199,16 @@ namespace CtrEditor.ObjetosSim
|
|||
{
|
||||
// Update X position if encoder is available
|
||||
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
|
||||
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)
|
||||
|
|
|
@ -13,7 +13,7 @@ using System.Text.Json.Serialization;
|
|||
namespace CtrEditor.ObjetosSim
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ucTransporteTTop.xaml
|
||||
/// Interaction logic for ucTransporteTTopDualInverter.xaml
|
||||
/// </summary>
|
||||
///
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace CtrEditor.ObjetosSim
|
|||
partial void OnInvertirDireccionChanged(bool value)
|
||||
{
|
||||
SetSpeed();
|
||||
if (_visualRepresentation is ucTransporteTTop uc)
|
||||
if (_visualRepresentation is ucTransporteTTopDualInverter uc)
|
||||
{
|
||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
||||
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
|
||||
|
@ -156,7 +156,7 @@ namespace CtrEditor.ObjetosSim
|
|||
|
||||
private void ActualizarGeometrias()
|
||||
{
|
||||
if (_visualRepresentation is ucTransporteTTop uc)
|
||||
if (_visualRepresentation is ucTransporteTTopDualInverter uc)
|
||||
{
|
||||
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
|
||||
SetSpeed();
|
||||
|
@ -206,6 +206,8 @@ namespace CtrEditor.ObjetosSim
|
|||
else
|
||||
VelocidadActual = 0;
|
||||
}
|
||||
else
|
||||
VelocidadActual = 0;
|
||||
}
|
||||
|
||||
public override void ucLoaded()
|
||||
|
@ -216,7 +218,7 @@ namespace CtrEditor.ObjetosSim
|
|||
OnId_Motor_AChanged(Id_Motor_A); // Link Id_Motor = Motor
|
||||
OnId_Motor_BChanged(Id_Motor_B); // Link Id_Motor = Motor
|
||||
|
||||
if (_visualRepresentation is ucTransporteTTop uc)
|
||||
if (_visualRepresentation is ucTransporteTTopDualInverter uc)
|
||||
{
|
||||
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
|
||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
||||
|
|
|
@ -213,7 +213,7 @@ namespace CtrEditor.ObjetosSim
|
|||
|
||||
// Update local state from ControlWord
|
||||
OUT_Run = control.run;
|
||||
OUT_Stop = control.stop;
|
||||
OUT_Stop = !control.stop;
|
||||
OUT_Reversal = control.reversal;
|
||||
OUT_OUT_VFD_REQ_Speed_Hz = control.reqSpeedHz;
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
|
|||
|
||||
public void CaptureImageAreaAndDoOCR()
|
||||
{
|
||||
string extractedText = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
|
||||
string extractedText = CaptureImageAreaAndDoOCRPPaddle(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
|
||||
|
||||
// Clean up the extracted text if eliminar_enters is true
|
||||
if (Eliminar_enters && !string.IsNullOrEmpty(extractedText))
|
||||
|
|
|
@ -56,6 +56,12 @@ namespace CtrEditor.ObjetosSim
|
|||
[property: Category("PLC link:")]
|
||||
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)
|
||||
{
|
||||
if (Motor != null)
|
||||
|
@ -85,7 +91,16 @@ namespace CtrEditor.ObjetosSim
|
|||
|
||||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
||||
{
|
||||
if (Motor != null && Motor is osVMmotorSim motor)
|
||||
if (Tag_ReadValor != null && Tag_ReadValor.Length > 0)
|
||||
{
|
||||
// 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;
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CtrEditor.FuncionesBase;
|
||||
using CtrEditor.Serialization;
|
||||
using CtrEditor.Services;
|
||||
using CtrEditor.Simulacion;
|
||||
using LibS7Adv;
|
||||
using nkast.Aether.Physics2D.Common;
|
||||
using PaddleOCRSharp;
|
||||
using Siemens.Simatic.Simulation.Runtime;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
@ -380,7 +382,10 @@ namespace CtrEditor.ObjetosSim
|
|||
|
||||
if (Angulo != 0)
|
||||
{
|
||||
RotateTransform rotateTransform = new RotateTransform(-Angulo);
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -413,7 +418,7 @@ namespace CtrEditor.ObjetosSim
|
|||
using (var engine = new TesseractEngine(tesseractPath, "eng", EngineMode.Default))
|
||||
{
|
||||
// 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);
|
||||
return result.GetText();
|
||||
}
|
||||
|
@ -426,6 +431,101 @@ namespace CtrEditor.ObjetosSim
|
|||
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
|
||||
public static Color ObtenerColorMasClaroYSaturado(Color colorOriginal, double incrementoL, double incrementoS)
|
||||
{
|
||||
|
@ -449,10 +549,151 @@ namespace CtrEditor.ObjetosSim
|
|||
[property: Category("Layout:")]
|
||||
private bool enable_On_All_Pages;
|
||||
|
||||
|
||||
partial void OnEnable_On_All_PagesChanged(bool value)
|
||||
{
|
||||
// Si se está desactivando el modo global
|
||||
if (!value)
|
||||
{
|
||||
// Limpiar los datos locales
|
||||
_datosLocales = null;
|
||||
_snapshotGlobal = null;
|
||||
|
||||
// Desactivar datos locales
|
||||
Enable_Local_Data = false;
|
||||
|
||||
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]
|
||||
|
@ -782,6 +1023,16 @@ 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)
|
||||
{
|
||||
if (_plc == null) return;
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Collections.ObjectModel;
|
|||
using System.Windows;
|
||||
using CtrEditor.Simulacion;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace CtrEditor.Serialization
|
||||
{
|
||||
|
@ -30,36 +31,68 @@ namespace CtrEditor.Serialization
|
|||
|
||||
var objetosSimulables = 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)
|
||||
{
|
||||
// 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();
|
||||
|
||||
// Separa objetos globales y locales
|
||||
if (!obj.Enable_On_All_Pages)
|
||||
{
|
||||
objetosSimulables.Add(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
objetosSimulablesAllPages.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// Save current page objects
|
||||
// Paso 2: Guardar datos de la página actual
|
||||
var dataToSerialize = new SimulationData
|
||||
{
|
||||
ObjetosSimulables = objetosSimulables,
|
||||
ObjetosSimulables = objetosSimulables.Count > 0 ? objetosSimulables : null,
|
||||
UnitConverter = PixelToMeter.Instance.calc,
|
||||
PLC_ConnectionData = _mainViewModel.PLCViewModel
|
||||
PLC_ConnectionData = _mainViewModel.PLCViewModel,
|
||||
DatosLocalesObjetos = datosLocalesObjetos.Count > 0 ? datosLocalesObjetos : null
|
||||
};
|
||||
|
||||
var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
|
||||
if (path != null)
|
||||
SerializeAndSave(dataToSerialize, path);
|
||||
|
||||
// Save all pages objects
|
||||
// Paso 3: Guardar objetos globales
|
||||
path = _datosDeTrabajo.ObtenerPathAllPages(".json");
|
||||
if (path != null)
|
||||
SerializeAndSave(objetosSimulablesAllPages, path);
|
||||
|
||||
// Restore original properties
|
||||
// Paso 4: Restaurar estado para seguir trabajando
|
||||
foreach (var obj in _mainViewModel.ObjetosSimulables)
|
||||
{
|
||||
obj.RestaurarDatosNoSerializables();
|
||||
|
||||
// Para objetos globales, restaura los datos locales
|
||||
if (obj.Enable_On_All_Pages && obj.Enable_Local_Data)
|
||||
{
|
||||
obj.RestaurarDatosLocales();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,10 +108,47 @@ namespace CtrEditor.Serialization
|
|||
if (selectedImage != null)
|
||||
{
|
||||
var settings = GetJsonSerializerSettings();
|
||||
LoadCurrentPageState(selectedImage, settings);
|
||||
|
||||
// Paso 1: Cargar objetos globales
|
||||
LoadAllPagesState(settings);
|
||||
|
||||
// Create UserControls for all loaded objects
|
||||
// Paso 2: Cargar datos de la página actual
|
||||
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)
|
||||
{
|
||||
if (objetoSimulable != null)
|
||||
|
@ -95,17 +165,21 @@ namespace CtrEditor.Serialization
|
|||
}
|
||||
}
|
||||
|
||||
private void LoadCurrentPageState(string selectedImage, JsonSerializerSettings settings)
|
||||
private SimulationData LoadCurrentPageState(string selectedImage, JsonSerializerSettings settings)
|
||||
{
|
||||
SimulationData simulationData = null;
|
||||
string jsonPath = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
|
||||
if (File.Exists(jsonPath))
|
||||
{
|
||||
string jsonString = File.ReadAllText(jsonPath);
|
||||
var simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
|
||||
simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
|
||||
if (simulationData != 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)
|
||||
_mainViewModel.PLCViewModel = simulationData.PLC_ConnectionData;
|
||||
|
@ -115,6 +189,7 @@ namespace CtrEditor.Serialization
|
|||
PixelToMeter.Instance.calc = simulationData.UnitConverter;
|
||||
}
|
||||
}
|
||||
return simulationData;
|
||||
}
|
||||
|
||||
private void LoadAllPagesState(JsonSerializerSettings settings)
|
||||
|
@ -158,5 +233,4 @@ namespace CtrEditor.Serialization
|
|||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -553,11 +553,11 @@ namespace CtrEditor.Simulacion
|
|||
Body.OnSeparation += HandleOnSeparation;
|
||||
Body.Tag = this; // Importante para la identificación durante la colisión
|
||||
// Configurar la fricción
|
||||
Body.SetFriction(0.2f);
|
||||
//Body.SetFriction(0.2f);
|
||||
// Configurar amortiguamiento
|
||||
Body.LinearDamping = 3f; // Ajustar para controlar la reducción de la velocidad lineal
|
||||
Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular
|
||||
Body.SetRestitution(0f); // Baja restitución para menos rebote
|
||||
//Body.SetRestitution(0f); // Baja restitución para menos rebote
|
||||
Body.SleepingAllowed = false;
|
||||
Body.IsBullet = true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,445 @@
|
|||
/* 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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/* 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -88,21 +88,41 @@ namespace CtrEditor.PopUps
|
|||
MatrixRows.Clear();
|
||||
_dataGrid.Columns.Clear();
|
||||
|
||||
if (!MatrixItems.Any()) return;
|
||||
if (!MatrixItems.Any())
|
||||
{
|
||||
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
|
||||
var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image);
|
||||
var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image).ToList();
|
||||
Debug.WriteLine($"Images in matrix: {itemsByImage.Count}");
|
||||
|
||||
// Ordenar items por número de columna
|
||||
// Sort items by column number
|
||||
var orderedItems = MatrixItems
|
||||
.GroupBy(x => x.ColumnNumber)
|
||||
.OrderBy(g => g.Key)
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
// Crear columnas
|
||||
Debug.WriteLine($"Distinct columns: {orderedItems.Count}");
|
||||
|
||||
// Create data grid columns
|
||||
foreach (var item in orderedItems)
|
||||
{
|
||||
if (item.ColumnNumber <= 0) continue; // Skip invalid column numbers
|
||||
|
||||
var column = new DataGridTextColumn
|
||||
{
|
||||
Header = $"{item.ColumnNumber}:{item.ColumnName}",
|
||||
|
@ -115,22 +135,25 @@ namespace CtrEditor.PopUps
|
|||
// Process each image's data
|
||||
foreach (var imageGroup in itemsByImage)
|
||||
{
|
||||
Debug.WriteLine($"Processing image: {imageGroup.Key} with {imageGroup.Count()} items");
|
||||
|
||||
// Calculate max rows needed for this image
|
||||
int maxRows = 1;
|
||||
var clonedItems = imageGroup.Where(x => x.IsCloned).ToList();
|
||||
if (clonedItems.Any())
|
||||
{
|
||||
maxRows = clonedItems.Max(x => x.Copy_Number) + 1;
|
||||
Debug.WriteLine($"Found cloned items, max rows: {maxRows}");
|
||||
}
|
||||
|
||||
// Create rows for this image
|
||||
for (int row = 0; row < maxRows; row++)
|
||||
{
|
||||
var rowData = new Dictionary<string, string>();
|
||||
|
||||
|
||||
// Add image identifier
|
||||
rowData["Image"] = imageGroup.Key;
|
||||
|
||||
|
||||
// Add fixed (non-cloned) values in all rows
|
||||
foreach (var item in imageGroup.Where(x => !x.IsCloned))
|
||||
{
|
||||
|
@ -152,6 +175,8 @@ namespace CtrEditor.PopUps
|
|||
MatrixRows.Add(rowData);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Total matrix rows created: {MatrixRows.Count}");
|
||||
}
|
||||
|
||||
private void UpdateColumnHeaders()
|
||||
|
@ -183,13 +208,22 @@ namespace CtrEditor.PopUps
|
|||
{
|
||||
// Store current image
|
||||
var currentImage = _mainViewModel.SelectedImage;
|
||||
|
||||
|
||||
// Change to target image and wait for UI update
|
||||
_mainViewModel.SelectedImage = imageName;
|
||||
await _mainViewModel.WaitForUIUpdateAsync();
|
||||
|
||||
// Analyze current page
|
||||
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)
|
||||
{
|
||||
item.Source_Image = imageName;
|
||||
|
@ -204,11 +238,17 @@ 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
|
||||
{
|
||||
_mainViewModel.HasUnsavedChanges = originalHasUnsavedChanges;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Total items collected: {MatrixItems.Count}");
|
||||
ResolveColumnConflicts();
|
||||
UpdateMatrixPreview();
|
||||
}
|
||||
|
@ -251,13 +291,17 @@ namespace CtrEditor.PopUps
|
|||
// Get base tags that are linked to Search Templates
|
||||
var osExtraccionTagBaseGrouped_List = _mainViewModel.ObjetosSimulables
|
||||
.OfType<osExtraccionTag>()
|
||||
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "")
|
||||
.Where(tag => tag.Show_On_This_Page &&
|
||||
!tag.Cloned &&
|
||||
!string.IsNullOrEmpty(tag.Id_Search_Templates))
|
||||
.ToList();
|
||||
|
||||
// Get fixed tags (not linked to Search Templates)
|
||||
var osExtraccionTagBaseFix_List = _mainViewModel.ObjetosSimulables
|
||||
.OfType<osExtraccionTag>()
|
||||
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == ""))
|
||||
.Where(tag => tag.Show_On_This_Page &&
|
||||
!tag.Cloned &&
|
||||
string.IsNullOrEmpty(tag.Id_Search_Templates))
|
||||
.ToList();
|
||||
|
||||
// Get cloned tags (created by Search Templates)
|
||||
|
@ -266,6 +310,11 @@ namespace CtrEditor.PopUps
|
|||
.Where(tag => tag.Show_On_This_Page && tag.Cloned)
|
||||
.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
|
||||
foreach (var tag in osExtraccionTagBaseFix_List)
|
||||
{
|
||||
|
@ -278,7 +327,9 @@ namespace CtrEditor.PopUps
|
|||
Value = tag.Tag_extract,
|
||||
Type = "Fixed",
|
||||
IsCloned = false,
|
||||
Id = tag.Id
|
||||
Id = tag.Id,
|
||||
Source_Image = _mainViewModel.SelectedImage,
|
||||
Copy_Number = 0
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -294,7 +345,9 @@ namespace CtrEditor.PopUps
|
|||
Value = tag.Tag_extract,
|
||||
Type = $"Grouped ({tag.Id_Search_Templates})",
|
||||
IsCloned = false,
|
||||
Id = tag.Id
|
||||
Id = tag.Id,
|
||||
Source_Image = _mainViewModel.SelectedImage,
|
||||
Copy_Number = 0
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -311,6 +364,7 @@ namespace CtrEditor.PopUps
|
|||
Type = "Cloned",
|
||||
IsCloned = true,
|
||||
Id = tag.Id,
|
||||
Source_Image = _mainViewModel.SelectedImage,
|
||||
Copy_Number = tag.Copy_Number
|
||||
});
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,132 @@
|
|||
# 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
|
|
@ -0,0 +1,95 @@
|
|||
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
|
||||
{
|
||||
|
|
||||
}
|
||||
~
|
||||
!
|
||||
"
|
||||
#
|
||||
$
|
||||
%
|
||||
&
|
||||
'
|
||||
(
|
||||
)
|
||||
*
|
||||
+
|
||||
,
|
||||
-
|
||||
.
|
||||
/
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue