From c94b5fb7f13ffa15bf714aff4623978402ec11db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Sun, 8 Aug 2021 23:57:00 +0100 Subject: [PATCH] v2.17.0 - **Windows MSI:** - (Fix) Use the folder programs x64 instead of x86 for new installation path (#254) - (Improvement) Mark program and all files as x64 - (Improvement) Add UVtools logo to side panel and top banner - (Improvement) Add open-source logo to side panel - (Improvement) License text aligment and bold title - (Add) File format: OSLA / ODLP / OMSLA - Universal binary file format - (Add) Calibration - Lift height: Generates test models with various strategies and increments to measure the optimal lift height or peel forces for layers given the printed area - (Add) Layer Actions - Export layers to images (PNG, JPG/JPEG, JP2, TIF/TIFF, BMP, PBM, PGM, SR/RAS and SVG) - (Add) About box: License with link - (Add) Include a copy of the LICENSE on the packages - (Improvement) File formats: Implement `Wait time before cure` properties on file formats with light-off delay, when used it will calculate the right light-off delay with that extra time and set to `LightOffDelay` property - (Improvement) Change all date times to Utc instead of local - (Fix) Tool - Flip: 'Both' were not working correctly - (Fix) Linux: File 'UVtools.sh' with incorrect line break type, changed to \n (#258) --- CHANGELOG.md | 18 + CREDITS.md | 4 +- Documentation/osla.md | 4 +- README.md | 1 + UVtools.Core/About.cs | 17 +- UVtools.Core/Enumerations.cs | 43 + UVtools.Core/Extensions/EnumExtensions.cs | 2 +- .../Extensions/FileStreamExtensions.cs | 23 + UVtools.Core/FileFormats/CWSFile.cs | 10 +- UVtools.Core/FileFormats/CXDLPFile.cs | 20 + UVtools.Core/FileFormats/CXDLPv1File.cs | 20 + UVtools.Core/FileFormats/ChituboxFile.cs | 53 +- UVtools.Core/FileFormats/ChituboxZipFile.cs | 7 +- UVtools.Core/FileFormats/FDGFile.cs | 24 +- UVtools.Core/FileFormats/FileFormat.cs | 344 ++++---- UVtools.Core/FileFormats/GR1File.cs | 22 +- UVtools.Core/FileFormats/ImageFile.cs | 2 +- UVtools.Core/FileFormats/LGSFile.cs | 22 +- UVtools.Core/FileFormats/MDLPFile.cs | 22 +- UVtools.Core/FileFormats/OSLAFile.cs | 800 ++++++++++++++++++ UVtools.Core/FileFormats/PHZFile.cs | 24 +- UVtools.Core/FileFormats/PhotonSFile.cs | 24 +- .../FileFormats/PhotonWorkshopFile.cs | 22 +- UVtools.Core/FileFormats/SL1File.cs | 4 +- UVtools.Core/FileFormats/UVJFile.cs | 22 +- UVtools.Core/FileFormats/VDAFile.cs | 4 +- UVtools.Core/FileFormats/VDTFile.cs | 6 +- UVtools.Core/FileFormats/ZCodeFile.cs | 4 +- UVtools.Core/FileFormats/ZCodexFile.cs | 4 +- UVtools.Core/GCode/GCodeBuilder.cs | 35 +- UVtools.Core/Layer/Layer.cs | 32 +- UVtools.Core/Layer/LayerManager.cs | 5 +- .../OperationCalibrateElephantFoot.cs | 2 +- .../OperationCalibrateExposureFinder.cs | 4 +- .../Operations/OperationCalibrateGrayscale.cs | 2 +- .../OperationCalibrateLiftHeight.cs | 442 ++++++++++ .../OperationCalibrateStressTower.cs | 2 +- .../Operations/OperationCalibrateTolerance.cs | 2 +- .../OperationCalibrateXYZAccuracy.cs | 2 +- UVtools.Core/Operations/OperationFlip.cs | 43 +- .../Operations/OperationLayerExportGif.cs | 49 +- .../Operations/OperationLayerExportImage.cs | 303 +++++++ UVtools.Core/Operations/OperationProgress.cs | 4 +- UVtools.Core/UVtools.Core.csproj | 2 +- UVtools.Installer/Code/Features.wxs | 2 +- UVtools.Installer/Code/Product.wxs | 2 +- UVtools.Installer/Resources/Banner.fw.png | Bin 0 -> 53292 bytes UVtools.Installer/Resources/Banner.jpg | Bin 1241 -> 15829 bytes UVtools.Installer/Resources/Dialog.fw.png | Bin 0 -> 93029 bytes UVtools.Installer/Resources/Dialog.jpg | Bin 3284 -> 14898 bytes UVtools.Installer/Resources/EULA.rtf | 198 +++-- UVtools.Installer/UVtools.Installer.wixproj | 2 +- .../UVtools.InstallerMM.wixproj | 2 +- UVtools.InstallerMM/UVtools.InstallerMM.wxs | 579 ++++++------- .../ScriptTestPerLayerSettingsSample.cs | 2 +- .../Assets/Icons/balance-scale-16x16.png | Bin 0 -> 224 bytes .../Assets/Icons/long-arrow-alt-up-16x16.png | Bin 0 -> 103 bytes .../CalibrateElephantFootControl.axaml.cs | 1 - .../CalibrateLiftHeightControl.axaml | 234 +++++ .../CalibrateLiftHeightControl.axaml.cs | 75 ++ .../Controls/Tools/ToolFlipControl.axaml | 8 +- .../Tools/ToolLayerExportImageControl.axaml | 67 ++ .../ToolLayerExportImageControl.axaml.cs | 34 + UVtools.WPF/MainWindow.axaml.cs | 19 +- UVtools.WPF/Structures/OperationProfiles.cs | 1 + UVtools.WPF/UVtools.WPF.csproj | 3 +- UVtools.WPF/Windows/AboutWindow.axaml | 12 +- UVtools.WPF/Windows/AboutWindow.axaml.cs | 6 +- build/CreateRelease.WPF.ps1 | 2 +- 69 files changed, 3000 insertions(+), 750 deletions(-) create mode 100644 UVtools.Core/FileFormats/OSLAFile.cs create mode 100644 UVtools.Core/Operations/OperationCalibrateLiftHeight.cs create mode 100644 UVtools.Core/Operations/OperationLayerExportImage.cs create mode 100644 UVtools.Installer/Resources/Banner.fw.png create mode 100644 UVtools.Installer/Resources/Dialog.fw.png create mode 100644 UVtools.WPF/Assets/Icons/balance-scale-16x16.png create mode 100644 UVtools.WPF/Assets/Icons/long-arrow-alt-up-16x16.png create mode 100644 UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml create mode 100644 UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs create mode 100644 UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml create mode 100644 UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3989409..16e1e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 08/08/2021 - v2.17.0 + +- **Windows MSI:** + - (Fix) Use the folder programs x64 instead of x86 for new installation path (#254) + - (Improvement) Mark program and all files as x64 + - (Improvement) Add UVtools logo to side panel and top banner + - (Improvement) Add open-source logo to side panel + - (Improvement) License text aligment and bold title +- (Add) File format: OSLA / ODLP / OMSLA - Universal binary file format +- (Add) Calibration - Lift height: Generates test models with various strategies and increments to measure the optimal lift height or peel forces for layers given the printed area +- (Add) Layer Actions - Export layers to images (PNG, JPG/JPEG, JP2, TIF/TIFF, BMP, PBM, PGM, SR/RAS and SVG) +- (Add) About box: License with link +- (Add) Include a copy of the LICENSE on the packages +- (Improvement) File formats: Implement `Wait time before cure` properties on file formats with light-off delay, when used it will calculate the right light-off delay with that extra time and set to `LightOffDelay` property +- (Improvement) Change all date times to Utc instead of local +- (Fix) Tool - Flip: 'Both' were not working correctly +- (Fix) Linux: File 'UVtools.sh' with incorrect line break type, changed to \n (#258) + ## 01/08/2021 - v2.16.0 - **(Add) PrusaSlicer printers:** diff --git a/CREDITS.md b/CREDITS.md index 3127e5a..21d9c01 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -26,6 +26,7 @@ # Supporters / Contributors +- https://github.com/sponsors/sn4k3 - Ingo Strohmenger - Sven Vogt - Paul Hammerstrom @@ -57,4 +58,5 @@ - Finn Newick - Thomas Wilbert - Gary Kyle -- Peter Csaki \ No newline at end of file +- Peter Csaki +- Chris Luginbuhl \ No newline at end of file diff --git a/Documentation/osla.md b/Documentation/osla.md index 436a396..c4e83f5 100644 --- a/Documentation/osla.md +++ b/Documentation/osla.md @@ -91,8 +91,8 @@ eg: `PreviewDataType=RGB565` and `LayerDataType=PNG` ## Structure 1. [File] (150 bytes) -2. [Header] (197 bytes) -3. [Custom table] (0 or more bytes) +2. [Header] (199 bytes) +3. [Custom table] (4 bytes + 0 or more bytes) 4. [Previews] (0 or more) - Preview 1 (8 bytes + sizeof(preview image data)) - Preview 2 (8 bytes + sizeof(preview image data)) diff --git a/README.md b/README.md index d92f3a5..5c9e39c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/sn4k3/UVtools?include_prereleases&style=flat-square)](https://github.com/sn4k3/UVtools/releases) [![Downloads](https://img.shields.io/github/downloads/sn4k3/UVtools/total?style=flat-square)](https://github.com/sn4k3/UVtools/releases) +**Download the latest version at:** https://github.com/sn4k3/UVtools/releases/latest **MSLA/DLP, file analysis, calibration, repair, conversion and manipulation** diff --git a/UVtools.Core/About.cs b/UVtools.Core/About.cs index ba904d5..1bc41d5 100644 --- a/UVtools.Core/About.cs +++ b/UVtools.Core/About.cs @@ -13,15 +13,20 @@ namespace UVtools.Core { public static class About { - public static string Software = "UVtools"; - public static string Author = "Tiago Conceição"; - public static string Company = "PTRTECH"; - public static string Website = "https://github.com/sn4k3/UVtools"; - public static string Donate = "https://paypal.me/SkillTournament"; + public const string Software = "UVtools"; + public static string SoftwareWithVersion => $"{Software} v{VersionStr}"; + public const string Author = "Tiago Conceição"; + public const string Company = "PTRTECH"; + public const string License = "GNU Affero General Public License v3.0 (AGPL)"; + public const string LicenseUrl = "https://github.com/sn4k3/UVtools/blob/master/LICENSE"; + public const string Website = "https://github.com/sn4k3/UVtools"; + public const string Donate = "https://paypal.me/SkillTournament"; + public const string Sponsor = "https://github.com/sponsors/sn4k3"; - public static string DemoFile = "UVtools_demo_file.sl1"; + public const string DemoFile = "UVtools_demo_file.sl1"; public static Version Version => Assembly.GetExecutingAssembly().GetName().Version; public static string VersionStr => Assembly.GetExecutingAssembly().GetName().Version.ToString(3); + public static string Arch => Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; } } diff --git a/UVtools.Core/Enumerations.cs b/UVtools.Core/Enumerations.cs index 468b389..98be0a5 100644 --- a/UVtools.Core/Enumerations.cs +++ b/UVtools.Core/Enumerations.cs @@ -5,7 +5,10 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ + +using System; using System.ComponentModel; +using Emgu.CV.CvEnum; namespace UVtools.Core { @@ -24,11 +27,27 @@ namespace UVtools.Core public enum FlipDirection : byte { + None, Horizontally, Vertically, Both, } + public enum RotateDirection : sbyte + { + [Description("None")] + None = -1, + /// Rotate 90 degrees clockwise (0) + [Description("Rotate 90º CW")] + Rotate90Clockwise = 0, + /// Rotate 180 degrees clockwise (1) + [Description("Rotate 180º")] + Rotate180 = 1, + /// Rotate 270 degrees clockwise (2) + [Description("Rotate 90º CCW")] + Rotate90CounterClockwise = 2, + } + public enum Anchor : byte { TopLeft, TopCenter, TopRight, @@ -51,5 +70,29 @@ namespace UVtools.Core [Description("Disabled")] NoAction } + + public static FlipType ToOpenCVFlipType(FlipDirection flip) + { + return flip switch + { + FlipDirection.None => throw new NotSupportedException($"Flip type: {flip} is not supported by OpenCV."), + FlipDirection.Horizontally => FlipType.Horizontal, + FlipDirection.Vertically => FlipType.Vertical, + FlipDirection.Both => FlipType.Both, + _ => throw new ArgumentOutOfRangeException(nameof(flip), flip, null) + }; + } + + public static RotateFlags ToOpenCVRotateFlags(RotateDirection rotate) + { + return rotate switch + { + RotateDirection.None => throw new NotSupportedException($"Rotate direction: {rotate} is not supported by OpenCV."), + RotateDirection.Rotate90Clockwise => RotateFlags.Rotate90Clockwise, + RotateDirection.Rotate90CounterClockwise => RotateFlags.Rotate90CounterClockwise, + RotateDirection.Rotate180 => RotateFlags.Rotate180, + _ => throw new ArgumentOutOfRangeException(nameof(rotate), rotate, null) + }; + } } } diff --git a/UVtools.Core/Extensions/EnumExtensions.cs b/UVtools.Core/Extensions/EnumExtensions.cs index 493418c..6847164 100644 --- a/UVtools.Core/Extensions/EnumExtensions.cs +++ b/UVtools.Core/Extensions/EnumExtensions.cs @@ -33,7 +33,7 @@ namespace UVtools.Core.Extensions if (!t.IsEnum) throw new ArgumentException($"{nameof(t)} must be an enum type"); - return Enum.GetValues(t).Cast().Select(e => new ValueDescription(e, e.GetDescription())).ToList(); + return Enum.GetValues(t).Cast().OrderBy(e => e).Select(e => new ValueDescription(e, e.GetDescription())).ToList(); } } } diff --git a/UVtools.Core/Extensions/FileStreamExtensions.cs b/UVtools.Core/Extensions/FileStreamExtensions.cs index 02fba02..c9b530c 100644 --- a/UVtools.Core/Extensions/FileStreamExtensions.cs +++ b/UVtools.Core/Extensions/FileStreamExtensions.cs @@ -24,6 +24,9 @@ namespace UVtools.Core.Extensions return buffer; } + public static byte[] ReadBytes(this FileStream fs, uint length, int offset = 0) + => fs.ReadBytes((int)length, offset); + public static uint ReadUShortLittleEndian(this FileStream fs, int offset = 0) { return BitExtensions.ToUShortLittleEndian(fs.ReadBytes(2, offset)); @@ -44,6 +47,26 @@ namespace UVtools.Core.Extensions return BitExtensions.ToUIntBigEndian(fs.ReadBytes(4, offset)); } + public static void WriteUShortLittleEndian(this FileStream fs, ushort value, int offset = 0) + { + fs.WriteBytes(BitExtensions.ToBytesLittleEndian(value), offset); + } + + public static void WriteUShortBigEndian(this FileStream fs, ushort value, int offset = 0) + { + fs.WriteBytes(BitExtensions.ToBytesBigEndian(value), offset); + } + + public static void WriteUIntLittleEndian(this FileStream fs, uint value, int offset = 0) + { + fs.WriteBytes(BitExtensions.ToBytesLittleEndian(value), offset); + } + + public static void WriteUIntBigEndian(this FileStream fs, uint value, int offset = 0) + { + fs.WriteBytes(BitExtensions.ToBytesBigEndian(value), offset); + } + public static uint WriteStream(this FileStream fs, MemoryStream stream, int offset = 0) { return fs.WriteBytes(stream.ToArray(), offset); diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index ca8388b..40f4ca1 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -398,7 +398,7 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = OutputSettings.PlatformZSize = (float)Math.Round(value, 2); } - public override bool MirrorDisplay + public override bool DisplayMirror { get => OutputSettings.FlipX; set @@ -546,7 +546,7 @@ namespace UVtools.Core.FileFormats GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Partial, GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute, GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds, - GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndexZero, + GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started, LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G1, EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1 }; @@ -629,7 +629,7 @@ namespace UVtools.Core.FileFormats var entry = outputFile.CreateEntry("slice.conf"); using TextWriter tw = new StreamWriter(entry.Open()); - tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.Now}"); + tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}"); tw.WriteLine("# conf version 1.0"); tw.WriteLine(""); @@ -875,7 +875,7 @@ namespace UVtools.Core.FileFormats if (!SupportsGCode || SuppressRebuildGCode) return; //string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; //GCode.Clear(); - //GCode.AppendLine($"; {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.Now}"); + //GCode.AppendLine($"; {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}"); StringBuilder sb = new(); sb.AppendLine(";(**** Build and Slicing Parameters ****)"); @@ -1005,7 +1005,7 @@ namespace UVtools.Core.FileFormats using (TextWriter tw = new StreamWriter(stream)) { - tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.Now}"); + tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}"); tw.WriteLine("# conf version 1.0"); tw.WriteLine(""); diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index 1e93bda..f6f9d6d 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -492,6 +492,26 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => SlicerInfoSettings.BottomExposureTime; diff --git a/UVtools.Core/FileFormats/CXDLPv1File.cs b/UVtools.Core/FileFormats/CXDLPv1File.cs index 0e96f5a..752cebc 100644 --- a/UVtools.Core/FileFormats/CXDLPv1File.cs +++ b/UVtools.Core/FileFormats/CXDLPv1File.cs @@ -441,6 +441,26 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => SlicerInfoSettings.BottomExposureTime; diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 512ae46..d2fdc78 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -27,21 +27,31 @@ namespace UVtools.Core.FileFormats { #region Constants - private const uint MAGIC_CBDDLP = 0x12FD0019; // 318570521 - private const uint MAGIC_CBT = 0x12FD0086; // 318570630 - private const uint MAGIC_CBTv4 = 0x12FD0106; // 318570758 - private const ushort REPEATRGB15MASK = 0x20; + public const uint MAGIC_CBDDLP = 0x12FD0019; // 318570521 + public const uint MAGIC_CBT = 0x12FD0086; // 318570630 + public const uint MAGIC_CBTv4 = 0x12FD0106; // 318570758 + public const uint MAGIC_CBT_ENCRYPTED = 0x12FD0107; // 318570759 + public const ushort REPEATRGB15MASK = 0x20; - private const byte RLE8EncodingLimit = 0x7d; // 125; - private const ushort RLE16EncodingLimit = 0xFFF; + public const byte RLE8EncodingLimit = 0x7d; // 125; + public const ushort RLE16EncodingLimit = 0xFFF; - private const uint ENCRYPTYION_MODE_CBDDLP = 0x8; // 0 or 8 - private const uint ENCRYPTYION_MODE_CTBv2 = 0xF; // 15 for ctb v2 files - private const uint ENCRYPTYION_MODE_CTBv3 = 536870927; // 536870927 for ctb v3 files (This allow per layer settings, while 15 don't) - private const uint ENCRYPTYION_MODE_CTBv4 = 1073741839; // 1073741839 for ctb v3 files (This allow per layer settings, while 15 don't) + public const uint ENCRYPTYION_MODE_CBDDLP = 0x8; // 0 or 8 + public const uint ENCRYPTYION_MODE_CTBv2 = 0xF; // 15 for ctb v2 files + public const uint ENCRYPTYION_MODE_CTBv3 = 536870927; // 536870927 for ctb v3 files (This allow per layer settings, while 15 don't) + public const uint ENCRYPTYION_MODE_CTBv4 = 1073741839; // 1073741839 for ctb v3 files (This allow per layer settings, while 15 don't) private const string CTBv4_DISCLAIMER = "Layout and record format for the ctb and cbddlp file types are the copyrighted programs or codes of CBD Technology (China) Inc..The Customer or User shall not in any manner reproduce, distribute, modify, decompile, disassemble, decrypt, extract, reverse engineer, lease, assign, or sublicense the said programs or codes."; private const ushort CTBv4_DISCLAIMER_SIZE = 320; + + public static readonly byte[] SomethingNew1 = { + 0xD0, 0x5B, 0x8E, 0x33, 0x71, 0xDE, 0x3D, 0x1A, 0xE5, 0x4F, 0x22, 0xDD, 0xDF, 0x5B, 0xFD, 0x94, + 0xAB, 0x5D, 0x64, 0x3A, 0x9D, 0x7E, 0xBF, 0xAF, 0x42, 0x03, 0xF3, 0x10, 0xD8, 0x52, 0x2A, 0xEA + }; + + public static readonly byte[] SomethingNew2 = { + 0x0F, 0x01, 0x0A, 0x05, 0x05, 0x0B, 0x06, 0x07, 0x08, 0x06, 0x0A, 0x0C, 0x0C, 0x0D, 0x09, 0x0F + }; #endregion #region Sub Classes @@ -1060,7 +1070,7 @@ namespace UVtools.Core.FileFormats public LayerData[,] LayerDefinitions { get; private set; } - public Dictionary LayersHash { get; } = new Dictionary(); + public Dictionary LayersHash { get; } = new(); public override FileFormatType FileType => FileFormatType.Binary; @@ -1233,7 +1243,7 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); } - public override bool MirrorDisplay + public override bool DisplayMirror { get => HeaderSettings.ProjectorType > 0; set @@ -1321,7 +1331,21 @@ namespace UVtools.Core.FileFormats } } - public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure; + public override float BottomWaitTimeBeforeCure + { + get => WaitTimeBeforeCure; + set + { + if (HeaderSettings.Version < 4) + { + if (value > 0) + { + SetBottomLightOffDelay(value); + } + } + } + } + public override float WaitTimeBeforeCure { get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeAfterRetract : 0; @@ -1331,7 +1355,6 @@ namespace UVtools.Core.FileFormats { if (value > 0) { - SetBottomLightOffDelay(value); SetNormalLightOffDelay(value); } @@ -1729,7 +1752,7 @@ namespace UVtools.Core.FileFormats FileFullPath = fileFullPath; - progress.Reset(OperationProgress.StatusDecodeThumbnails, ThumbnailsCount); + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); Debug.Write("Header -> "); Debug.WriteLine(HeaderSettings); diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index 6a29dff..7d663e8 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -168,7 +168,7 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2); } - public override bool MirrorDisplay + public override bool DisplayMirror { get => HeaderSettings.Mirror > 0; set @@ -360,7 +360,7 @@ namespace UVtools.Core.FileFormats GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute, GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute, GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds, - GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenameNonZeroPNG, + GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenamePng1Started, LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0, EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1 }; @@ -468,9 +468,6 @@ namespace UVtools.Core.FileFormats progress.ItemCount = LayerCount; - var gcode = GCodeStr; - float lastPostZ = LayerHeight; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { if (progress.Token.IsCancellationRequested) break; diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index cab1b55..a80e0c1 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -751,7 +751,7 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); } - public override bool MirrorDisplay + public override bool DisplayMirror { get => HeaderSettings.ProjectorType > 0; set @@ -811,6 +811,26 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2); } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => HeaderSettings.BottomExposureSeconds; @@ -1070,7 +1090,7 @@ namespace UVtools.Core.FileFormats FileFullPath = fileFullPath; - progress.Reset(OperationProgress.StatusDecodeThumbnails, ThumbnailsCount); + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); Debug.Write("Header -> "); Debug.WriteLine(HeaderSettings); diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 5f627c4..465e68a 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -235,6 +235,7 @@ namespace UVtools.Core.FileFormats new FDGFile(), // fdg new PhotonWorkshopFile(), // PSW new CWSFile(), // CWS + new OSLAFile(), // OSLA new ZCodeFile(), // zcode new ZCodexFile(), // zcodex new MDLPFile(), // MKS v1 @@ -487,6 +488,9 @@ namespace UVtools.Core.FileFormats /// public string FileFullPath { get; set; } + public string Filename => Path.GetFileName(FileFullPath); + public string FilenameNoExt => GetFileNameStripExtensions(FileFullPath); + /// /// Gets the thumbnails count present in this file format /// @@ -640,7 +644,7 @@ namespace UVtools.Core.FileFormats /// /// Gets or sets if images need to be mirrored on lcd to print on the correct orientation /// - public virtual bool MirrorDisplay { get; set; } + public virtual bool DisplayMirror { get; set; } /// /// Gets if the display is in portrait mode @@ -827,157 +831,6 @@ namespace UVtools.Core.FileFormats /// public uint NormalLayerCount => LayerCount - BottomLayerCount; - public virtual float BottomWaitTimeBeforeCure - { - get => _bottomWaitTimeBeforeCure; - set - { - RaiseAndSet(ref _bottomWaitTimeBeforeCure, value); - RaisePropertyChanged(nameof(WaitTimeRepresentation)); - } - } - - public virtual float WaitTimeBeforeCure - { - get => _waitTimeBeforeCure; - set - { - RaiseAndSet(ref _waitTimeBeforeCure, value); - RaisePropertyChanged(nameof(WaitTimeRepresentation)); - } - } - - /// - /// Gets or sets the initial exposure time for in seconds - /// - public virtual float BottomExposureTime - { - get => _bottomExposureTime; - set - { - RaiseAndSet(ref _bottomExposureTime, value); - RaisePropertyChanged(nameof(ExposureRepresentation)); - } - } - - /// - /// Gets or sets the normal layer exposure time in seconds - /// - public virtual float ExposureTime - { - get => _exposureTime; - set - { - RaiseAndSet(ref _exposureTime, value); - RaisePropertyChanged(nameof(ExposureRepresentation)); - } - } - - public virtual float BottomWaitTimeAfterCure - { - get => _bottomWaitTimeAfterCure; - set - { - RaiseAndSet(ref _bottomWaitTimeAfterCure, value); - RaisePropertyChanged(nameof(WaitTimeRepresentation)); - } - } - - public virtual float WaitTimeAfterCure - { - get => _waitTimeAfterCure; - set - { - RaiseAndSet(ref _waitTimeAfterCure, value); - RaisePropertyChanged(nameof(WaitTimeRepresentation)); - } - } - - /// - /// Gets or sets the bottom lift height in mm - /// - public virtual float BottomLiftHeight - { - get => _bottomLiftHeight; - set - { - RaiseAndSet(ref _bottomLiftHeight, value); - RaisePropertyChanged(nameof(LiftRepresentation)); - } - } - - /// - /// Gets or sets the lift height in mm - /// - public virtual float LiftHeight - { - get => _liftHeight; - set - { - RaiseAndSet(ref _liftHeight, value); - RaisePropertyChanged(nameof(LiftRepresentation)); - } - } - - /// - /// Gets or sets the bottom lift speed in mm/min - /// - public virtual float BottomLiftSpeed - { - get => _bottomLiftSpeed; - set - { - RaiseAndSet(ref _bottomLiftSpeed, value); - RaisePropertyChanged(nameof(LiftRepresentation)); - } - } - - /// - /// Gets or sets the speed in mm/min - /// - public virtual float LiftSpeed - { - get => _liftSpeed; - set - { - RaiseAndSet(ref _liftSpeed, value); - RaisePropertyChanged(nameof(LiftRepresentation)); - } - } - - public virtual float BottomWaitTimeAfterLift - { - get => _bottomWaitTimeAfterLift; - set - { - RaiseAndSet(ref _bottomWaitTimeAfterLift, value); - RaisePropertyChanged(nameof(WaitTimeRepresentation)); - } - } - - public virtual float WaitTimeAfterLift - { - get => _waitTimeAfterLift; - set - { - RaiseAndSet(ref _waitTimeAfterLift, value); - RaisePropertyChanged(nameof(WaitTimeRepresentation)); - } - } - - /// - /// Gets the speed in mm/min for the retracts - /// - public virtual float RetractSpeed - { - get => _retractSpeed; - set - { - RaiseAndSet(ref _retractSpeed, value); - RaisePropertyChanged(nameof(RetractRepresentation)); - } - } - /// /// Gets or sets the bottom layer off time in seconds /// @@ -986,7 +839,7 @@ namespace UVtools.Core.FileFormats get => _bottomLightOffDelay; set { - RaiseAndSet(ref _bottomLightOffDelay, value); + RaiseAndSet(ref _bottomLightOffDelay, (float)Math.Round(value, 2)); RaisePropertyChanged(nameof(LightOffDelayRepresentation)); } } @@ -999,11 +852,163 @@ namespace UVtools.Core.FileFormats get => _lightOffDelay; set { - RaiseAndSet(ref _lightOffDelay, value); + RaiseAndSet(ref _lightOffDelay, (float)Math.Round(value, 2)); RaisePropertyChanged(nameof(LightOffDelayRepresentation)); } } + public virtual float BottomWaitTimeBeforeCure + { + get => _bottomWaitTimeBeforeCure; + set + { + RaiseAndSet(ref _bottomWaitTimeBeforeCure, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(WaitTimeRepresentation)); + } + } + + + public virtual float WaitTimeBeforeCure + { + get => _waitTimeBeforeCure; + set + { + RaiseAndSet(ref _waitTimeBeforeCure, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(WaitTimeRepresentation)); + } + } + + /// + /// Gets or sets the initial exposure time for in seconds + /// + public virtual float BottomExposureTime + { + get => _bottomExposureTime; + set + { + RaiseAndSet(ref _bottomExposureTime, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(ExposureRepresentation)); + } + } + + /// + /// Gets or sets the normal layer exposure time in seconds + /// + public virtual float ExposureTime + { + get => _exposureTime; + set + { + RaiseAndSet(ref _exposureTime, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(ExposureRepresentation)); + } + } + + public virtual float BottomWaitTimeAfterCure + { + get => _bottomWaitTimeAfterCure; + set + { + RaiseAndSet(ref _bottomWaitTimeAfterCure, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(WaitTimeRepresentation)); + } + } + + public virtual float WaitTimeAfterCure + { + get => _waitTimeAfterCure; + set + { + RaiseAndSet(ref _waitTimeAfterCure, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(WaitTimeRepresentation)); + } + } + + /// + /// Gets or sets the bottom lift height in mm + /// + public virtual float BottomLiftHeight + { + get => _bottomLiftHeight; + set + { + RaiseAndSet(ref _bottomLiftHeight, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftRepresentation)); + } + } + + /// + /// Gets or sets the lift height in mm + /// + public virtual float LiftHeight + { + get => _liftHeight; + set + { + RaiseAndSet(ref _liftHeight, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftRepresentation)); + } + } + + /// + /// Gets or sets the bottom lift speed in mm/min + /// + public virtual float BottomLiftSpeed + { + get => _bottomLiftSpeed; + set + { + RaiseAndSet(ref _bottomLiftSpeed, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftRepresentation)); + } + } + + /// + /// Gets or sets the speed in mm/min + /// + public virtual float LiftSpeed + { + get => _liftSpeed; + set + { + RaiseAndSet(ref _liftSpeed, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftRepresentation)); + } + } + + public virtual float BottomWaitTimeAfterLift + { + get => _bottomWaitTimeAfterLift; + set + { + RaiseAndSet(ref _bottomWaitTimeAfterLift, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(WaitTimeRepresentation)); + } + } + + public virtual float WaitTimeAfterLift + { + get => _waitTimeAfterLift; + set + { + RaiseAndSet(ref _waitTimeAfterLift, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(WaitTimeRepresentation)); + } + } + + /// + /// Gets the speed in mm/min for the retracts + /// + public virtual float RetractSpeed + { + get => _retractSpeed; + set + { + RaiseAndSet(ref _retractSpeed, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(RetractRepresentation)); + } + } + /// /// Gets or sets the bottom pwm value from 0 to 255 /// @@ -1547,7 +1552,7 @@ namespace UVtools.Core.FileFormats if (Thumbnails is not null) { - for (int i = 0; i < ThumbnailsCount; i++) + for (int i = 0; i < Thumbnails.Length; i++) { Thumbnails[i]?.Dispose(); } @@ -2316,6 +2321,7 @@ namespace UVtools.Core.FileFormats public float CalculateLightOffDelay(bool isBottomLayer, float extraTime = 0) { + extraTime = (float)Math.Round(extraTime, 2); if (SupportsGCode) return extraTime; return isBottomLayer ? OperationCalculator.LightOffDelayC.CalculateSeconds(BottomLiftHeight, BottomLiftSpeed, RetractSpeed, extraTime) @@ -2401,34 +2407,38 @@ namespace UVtools.Core.FileFormats slicerFile.DisplayWidth = DisplayWidth; slicerFile.DisplayHeight = DisplayHeight; slicerFile.MachineZ = MachineZ; - slicerFile.MirrorDisplay = MirrorDisplay; + slicerFile.DisplayMirror = DisplayMirror; + // Exposure + slicerFile.BottomExposureTime = BottomExposureTime; + slicerFile.ExposureTime = ExposureTime; + + // Lift + slicerFile.BottomLiftHeight = BottomLiftHeight; + slicerFile.LiftHeight = LiftHeight; + + slicerFile.BottomLiftSpeed = BottomLiftSpeed; + slicerFile.LiftSpeed = LiftSpeed; + + slicerFile.RetractSpeed = RetractSpeed; + + // Wait times slicerFile.BottomLightOffDelay = BottomLightOffDelay; slicerFile.LightOffDelay = LightOffDelay; slicerFile.BottomWaitTimeBeforeCure = BottomWaitTimeBeforeCure; slicerFile.WaitTimeBeforeCure = WaitTimeBeforeCure; - slicerFile.BottomExposureTime = BottomExposureTime; - slicerFile.ExposureTime = ExposureTime; - slicerFile.BottomWaitTimeAfterCure = BottomWaitTimeAfterCure; slicerFile.WaitTimeAfterCure = WaitTimeAfterCure; - slicerFile.BottomLiftHeight = BottomLiftHeight; - slicerFile.LiftHeight = LiftHeight; - - slicerFile.BottomLiftSpeed = BottomLiftSpeed; - slicerFile.LiftSpeed = LiftSpeed; - slicerFile.BottomWaitTimeAfterLift = BottomWaitTimeAfterLift; slicerFile.WaitTimeAfterLift = WaitTimeAfterLift; - slicerFile.RetractSpeed = RetractSpeed; - slicerFile.BottomLightPWM = BottomLightPWM; slicerFile.LightPWM = LightPWM; + slicerFile.MachineName = MachineName; slicerFile.MaterialName = MaterialName; slicerFile.MaterialMilliliters = MaterialMilliliters; diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs index 34993bf..fdf8ef2 100644 --- a/UVtools.Core/FileFormats/GR1File.cs +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -230,7 +230,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay { get; set; } + public override bool DisplayMirror { get; set; } public override byte AntiAliasing { @@ -276,6 +276,26 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => SlicerInfoSettings.BottomExposureTime; diff --git a/UVtools.Core/FileFormats/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs index 42c4323..07f5139 100644 --- a/UVtools.Core/FileFormats/ImageFile.cs +++ b/UVtools.Core/FileFormats/ImageFile.cs @@ -59,7 +59,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay + public override bool DisplayMirror { get => false; set { } diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index b5dc348..5ecbe22 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -326,7 +326,7 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2); } - public override bool MirrorDisplay + public override bool DisplayMirror { get => true; set { } @@ -384,6 +384,26 @@ namespace UVtools.Core.FileFormats } } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.BottomExposureTimeMs); diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index 2899843..babe25b 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -234,7 +234,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay { get; set; } + public override bool DisplayMirror { get; set; } public override byte AntiAliasing { @@ -280,6 +280,26 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => SlicerInfoSettings.BottomExposureTime; diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs new file mode 100644 index 0000000..94d956d --- /dev/null +++ b/UVtools.Core/FileFormats/OSLAFile.cs @@ -0,0 +1,800 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +// https://github.com/sn4k3/UVtools/blob/master/Documentation/osla.md + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using BinarySerialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using MoreLinq; +using UVtools.Core.Extensions; +using UVtools.Core.GCode; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + public class OSLAFile : FileFormat + { + #region Constants + + public const string MARKER = "OSLATiCo"; + #endregion + + #region Sub Classes + #region Header + public class FileDef + { + [FieldOrder(0)] + [FieldLength(8)] + public string Marker { get; set; } = MARKER; + + [FieldOrder(1)] + public ushort Version { get; set; } = 0; + + [FieldOrder(2)] + [FieldLength(20)] + public string CreatedDateTime { get; set; } = DateTime.UtcNow.ToString("u"); + + [FieldOrder(3)] + [FieldLength(50)] + [SerializeAs(SerializedType.TerminatedString)] + public string CreatedBy { get; set; } = About.SoftwareWithVersion; + + [FieldOrder(4)] + [FieldLength(20)] + public string ModifiedDateTime { get; set; } = DateTime.UtcNow.ToString("u"); + + [FieldOrder(5)] + [FieldLength(50)] + [SerializeAs(SerializedType.TerminatedString)] + public string ModifiedBy { get; set; } = About.SoftwareWithVersion; + + public override string ToString() + { + return $"{nameof(Marker)}: {Marker}, {nameof(Version)}: {Version}, {nameof(CreatedDateTime)}: {CreatedDateTime}, {nameof(CreatedBy)}: {CreatedBy}, {nameof(ModifiedDateTime)}: {ModifiedDateTime}, {nameof(ModifiedBy)}: {ModifiedBy}"; + } + + public void Update() + { + ModifiedDateTime= DateTime.UtcNow.ToString("u"); + ModifiedBy = About.SoftwareWithVersion;; + } + + public void Validate() + { + if (Marker != MARKER) + { + throw new FileLoadException($"Invalid marker: {Marker}, not a valid OSLA file."); + } + } + } + + + public class Header + { + [FieldOrder(0)] public uint TableSize { get; set; } + [FieldOrder(1)] public uint ResolutionX { get; set; } + [FieldOrder(2)] public uint ResolutionY { get; set; } + [FieldOrder(3)] public float MachineZ { get; set; } + [FieldOrder(4)] public float DisplayWidth { get; set; } + [FieldOrder(5)] public float DisplayHeight { get; set; } + [FieldOrder(6)] public byte DisplayMirror { get; set; } // 0 = No mirror | 1 = Horizontally | 2 = Vertically | 3 = Horizontally+Vertically | >3 = No mirror + [FieldOrder(7)] [FieldLength(16)] [SerializeAs(SerializedType.TerminatedString)] public string PreviewDataType { get; set; } = "RGB565"; + [FieldOrder(8)] [FieldLength(16)] [SerializeAs(SerializedType.TerminatedString)] public string LayerDataType { get; set; } = "PNG"; + [FieldOrder(9)] public uint PreviewTableSize { get; set; } = 8; + [FieldOrder(10)] public uint PreviewCount { get; set; } + [FieldOrder(11)] public float LayerHeight { get; set; } = 0.05f; + [FieldOrder(12)] public ushort BottomLayersCount { get; set; } = 4; + [FieldOrder(13)] public uint LayerCount { get; set; } + [FieldOrder(14)] public uint LayerTableSize { get; set; } = 4; + [FieldOrder(15)] public uint LayerDefinitionsAddress { get; set; } + [FieldOrder(16)] public uint GCodeAddress { get; set; } + [FieldOrder(17)] public uint PrintTime { get; set; } + [FieldOrder(18)] public float MaterialMilliliters { get; set; } + [FieldOrder(19)] public float MaterialCost { get; set; } + [FieldOrder(20)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string MaterialName { get; set; } + [FieldOrder(21)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; } = "Unknown"; + + public override string ToString() + { + return $"{nameof(TableSize)}: {TableSize}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(MachineZ)}: {MachineZ}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(DisplayMirror)}: {DisplayMirror}, {nameof(PreviewDataType)}: {PreviewDataType}, {nameof(LayerDataType)}: {LayerDataType}, {nameof(PreviewTableSize)}: {PreviewTableSize}, {nameof(PreviewCount)}: {PreviewCount}, {nameof(LayerTableSize)}: {LayerTableSize}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LayerCount)}: {LayerCount}, {nameof(LayerDefinitionsAddress)}: {LayerDefinitionsAddress}, {nameof(GCodeAddress)}: {GCodeAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(MaterialMilliliters)}: {MaterialMilliliters}, {nameof(MaterialCost)}: {MaterialCost}, {nameof(MaterialName)}: {MaterialName}, {nameof(MachineName)}: {MachineName}"; + } + } + #endregion + + #region Custom Table + + public class CustomTable + { + [FieldOrder(0)] public uint TableSize { get; set; } + + [FieldOrder(1)] [FieldCount(nameof(TableSize))] public byte[] Bytes { get; set; } = Array.Empty(); + + public override string ToString() + { + return $"{nameof(TableSize)}: {TableSize}, {nameof(Bytes)}: {Bytes.Length}"; + } + } + + #endregion + + #region Preview + public class Preview + { + /// + /// Gets the X dimension of the preview image, in pixels. + /// + [FieldOrder(0)] public ushort ResolutionX { get; set; } + + /// + /// Gets the Y dimension of the preview image, in pixels. + /// + [FieldOrder(1)] public ushort ResolutionY { get; set; } + + /// + /// Gets the image length in bytes. + /// + [FieldOrder(2)] public uint ImageLength { get; set; } + //[FieldOrder(3)] [FieldCount(nameof(ImageLength))] public byte[] ImageData { get; set; } + + + public override string ToString() + { + return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageLength)}: {ImageLength}"; + } + } + + #endregion + + #region Layer + public class LayerDef + { + [FieldOrder(0)] public uint DataAddress { get; set; } + + [Ignore] public byte[] ImageData { get; set; } + + public LayerDef() + { + } + + public override string ToString() + { + return $"{nameof(DataAddress)}: {DataAddress}, {nameof(ImageData)}: {ImageData.Length}"; + } + } + #endregion + + #region GCode + + public class GCodeDef + { + [FieldOrder(0)] + public uint GCodeSize { get; set; } + + [FieldOrder(1)] [FieldLength(nameof(GCodeSize))] + public string GCodeText { get; set; } + } + #endregion + + #endregion + + #region Properties + + public FileDef FileSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; protected internal set; } = new(); + public CustomTable CustomTableSettings { get; protected internal set; } = new(); + public Preview[] Previews { get; protected internal set; } + + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new ("osla", "Open SLA universal binary file"), + //new ("omsla", "Open mSLA universal binary file"), + //new ("odlp", "Open DLP universal binary file"), + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = + { + PrintParameterModifier.BottomLayerCount, + + PrintParameterModifier.BottomWaitTimeBeforeCure, + PrintParameterModifier.WaitTimeBeforeCure, + + PrintParameterModifier.BottomExposureTime, + PrintParameterModifier.ExposureTime, + + PrintParameterModifier.BottomWaitTimeAfterCure, + PrintParameterModifier.WaitTimeAfterCure, + + PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + + PrintParameterModifier.BottomWaitTimeAfterLift, + PrintParameterModifier.WaitTimeAfterLift, + + PrintParameterModifier.RetractSpeed, + + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM, + }; + + public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = { + PrintParameterModifier.WaitTimeBeforeCure, + PrintParameterModifier.ExposureTime, + PrintParameterModifier.WaitTimeAfterCure, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.WaitTimeAfterLift, + PrintParameterModifier.RetractSpeed, + PrintParameterModifier.LightPWM, + }; + + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(400, 400), + new(200, 200) + }; + + public override uint ResolutionX + { + get => HeaderSettings.ResolutionX; + set + { + HeaderSettings.ResolutionX = value; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get => HeaderSettings.ResolutionY; + set + { + HeaderSettings.ResolutionY = value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => HeaderSettings.DisplayWidth; + set + { + HeaderSettings.DisplayWidth = (float)Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + + public override float DisplayHeight + { + get => HeaderSettings.DisplayHeight; + set + { + HeaderSettings.DisplayHeight = (float)Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + public override float MachineZ + { + get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ; + set + { + HeaderSettings.MachineZ = (float)Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + public override bool DisplayMirror + { + get => HeaderSettings.DisplayMirror is >= 1 and <= 3; + set + { + HeaderSettings.DisplayMirror = value ? (byte)1 : (byte)0; + RaisePropertyChanged(); + } + } + + public override byte AntiAliasing + { + get => 8; + set => RaisePropertyChanged(); + } + + public override float LayerHeight + { + get => HeaderSettings.LayerHeight; + set + { + HeaderSettings.LayerHeight = Layer.RoundHeight(value); + RaisePropertyChanged(); + } + } + + public override uint LayerCount + { + get => base.LayerCount; + set => HeaderSettings.LayerCount = base.LayerCount; + } + + public override ushort BottomLayerCount + { + get => HeaderSettings.BottomLayersCount; + set => base.BottomLayerCount = HeaderSettings.BottomLayersCount = value; + } + + public override float PrintTime + { + get => base.PrintTime; + set + { + base.PrintTime = value; + HeaderSettings.PrintTime = (uint)base.PrintTime; + } + } + + public override float MaterialMilliliters + { + get => base.MaterialMilliliters; + set + { + base.MaterialMilliliters = value; + HeaderSettings.MaterialMilliliters = base.MaterialMilliliters; + } + } + + public override float MaterialCost + { + get => (float) Math.Round(HeaderSettings.MaterialCost, 3); + set => base.MaterialCost = HeaderSettings.MaterialCost = (float)Math.Round(value, 3); + } + + public override string MaterialName + { + get => HeaderSettings.MaterialName; + set => base.MaterialName = HeaderSettings.MaterialName = value; + } + + public override string MachineName + { + get => HeaderSettings.MachineName; + set => base.MachineName = HeaderSettings.MachineName = value; + } + + public override object[] Configs => new object[] { FileSettings, HeaderSettings }; + + #endregion + + #region Constructors + public OSLAFile() + { + //Previews = new Preview[ThumbnailsCount]; + GCode = new GCodeBuilder + { + UseTailComma = true, + UseComments = true, + GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute, + GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute, + GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds, + GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started, + LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0, + EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G0, + CommandShowImageM6054 = {Arguments = "{0}"}, + }; + } + #endregion + + #region Methods + public override void Clear() + { + base.Clear(); + + Previews = null; + } + + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + FileSettings.Update(); + var fileDefSize = Helpers.SerializeWriteFileStream(outputFile, FileSettings); + HeaderSettings.TableSize = (uint)Helpers.Serializer.SizeOf(HeaderSettings); + + outputFile.Seek((int)HeaderSettings.TableSize, SeekOrigin.Current); + + Helpers.SerializeWriteFileStream(outputFile, CustomTableSettings); // Custom table + + // Previews + progress.Reset(OperationProgress.StatusEncodePreviews, ThumbnailsCount); + HeaderSettings.PreviewCount = 0; + uint sizeofPreview = 0; + for (byte i = 0; i < ThumbnailsCount; i++) + { + var image = Thumbnails[i]; + if(image is null) continue; + + progress.Token.ThrowIfCancellationRequested(); + + var bytes = EncodeImage(image, HeaderSettings.PreviewDataType); + if (bytes.Length == 0) continue; + var preview = new Preview + { + ResolutionX = (ushort) image.Width, + ResolutionY = (ushort) image.Height, + ImageLength = (uint) bytes.Length, + }; + + if (sizeofPreview == 0) + { + sizeofPreview = (uint) Helpers.Serializer.SizeOf(preview); + } + + HeaderSettings.PreviewCount++; + + Helpers.SerializeWriteFileStream(outputFile, preview); + // Need to fill what we don't know + if (HeaderSettings.PreviewTableSize > sizeofPreview) + { + outputFile.Seek(HeaderSettings.LayerTableSize - sizeofPreview, SeekOrigin.Current); + } + outputFile.WriteBytes(bytes); + + progress++; + } + + uint[] layerDataAddresses = new uint[LayerCount]; + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + HeaderSettings.LayerDefinitionsAddress = (uint) outputFile.Position; + + outputFile.Seek(HeaderSettings.LayerTableSize * LayerCount, SeekOrigin.Current); // Start of layer data + + var layerHash = new Dictionary(); + + var range = Enumerable.Range(0, (int)LayerCount); + foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + { + var layerBytes = new byte[LayerCount][]; + + Parallel.ForEach(batch, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using var mat = this[layerIndex].LayerMat; + layerBytes[layerIndex] = EncodeImage(mat, HeaderSettings.LayerDataType); + progress.LockAndIncrement(); + }); + + foreach (var layerIndex in batch) + { + progress.Token.ThrowIfCancellationRequested(); + + // Try to reuse layers + var hash = Helpers.ComputeSHA1Hash(layerBytes[layerIndex]); + if (layerHash.TryGetValue(hash, out var address)) + { + layerDataAddresses[layerIndex] = address; + } + else + { + layerDataAddresses[layerIndex] = (uint)outputFile.Position; + outputFile.WriteUIntLittleEndian((uint)layerBytes[layerIndex].Length); + outputFile.WriteBytes(layerBytes[layerIndex]); + layerHash.Add(hash, layerDataAddresses[layerIndex]); + } + + + + layerBytes[layerIndex] = null; // Clean + } + } + + HeaderSettings.GCodeAddress = (uint)outputFile.Position; + + outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin); + for (int i = 0; i < layerDataAddresses.Length; i++) + { + progress.Token.ThrowIfCancellationRequested(); + outputFile.WriteUIntLittleEndian(layerDataAddresses[i]); + // Need to fill what we don't know + if (HeaderSettings.LayerTableSize > 4) + { + outputFile.Seek(HeaderSettings.LayerTableSize - 4, SeekOrigin.Current); + } + } + + progress.Reset(OperationProgress.StatusEncodeGcode); + outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin); + RebuildGCode(); + var gcodeSettings = new GCodeDef { GCodeText = GCodeStr }; + gcodeSettings.GCodeSize = (uint)gcodeSettings.GCodeText.Length; + Helpers.SerializeWriteFileStream(outputFile, gcodeSettings); + + outputFile.Seek(fileDefSize, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(FileSettings); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine("-End-"); + } + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + FileSettings = Helpers.Deserialize(inputFile); + Debug.Write("File -> "); + Debug.WriteLine(FileSettings); + FileSettings.Validate(); + + HeaderSettings = Helpers.Deserialize
(inputFile); + Debug.Write("Header -> "); + Debug.WriteLine(HeaderSettings); + + var headerSize = Helpers.Serializer.SizeOf(HeaderSettings); + if (HeaderSettings.TableSize > headerSize) // By pass what we dont know + { + inputFile.Seek(HeaderSettings.TableSize - headerSize, SeekOrigin.Current); + } + + CustomTableSettings = Helpers.Deserialize(inputFile); + Debug.Write("Custom table -> "); + Debug.WriteLine(CustomTableSettings); + + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); + + Previews = new Preview[HeaderSettings.PreviewCount]; + for (byte i = 0; i < HeaderSettings.PreviewCount; i++) + { + progress.Token.ThrowIfCancellationRequested(); + Previews[i] = Helpers.Deserialize(inputFile); + + Debug.Write($"Preview {i} -> "); + Debug.WriteLine(Previews[i]); + + // Need to fill what we don't know + if (HeaderSettings.PreviewTableSize > 8) + { + inputFile.Seek(HeaderSettings.LayerTableSize - 8, SeekOrigin.Current); + } + + + var bytes = inputFile.ReadBytes((int)Previews[i].ImageLength); + + Thumbnails[i] = DecodeImage(bytes, HeaderSettings.PreviewDataType, Previews[i].ResolutionX, Previews[i].ResolutionY); + progress++; + } + + inputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin); + + LayerManager.Init(HeaderSettings.LayerCount); + progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount); + uint[] layerDataAddresses = new uint[LayerCount]; + for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerDataAddresses[layerIndex] = inputFile.ReadUIntLittleEndian(); + if (HeaderSettings.LayerTableSize > 4) + { + inputFile.Seek(HeaderSettings.LayerTableSize - 4, SeekOrigin.Current); + } + + progress++; + } + + + + progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); + var range = Enumerable.Range(0, (int)LayerCount); + foreach (var batch in MoreEnumerable.Batch(range, Environment.ProcessorCount * 10)) + { + var layerBytes = new byte[LayerCount][]; + + foreach (var layerIndex in batch) + { + progress.Token.ThrowIfCancellationRequested(); + + inputFile.Seek(layerDataAddresses[layerIndex], SeekOrigin.Begin); + layerBytes[layerIndex] = inputFile.ReadBytes(inputFile.ReadUIntLittleEndian()); + } + + Parallel.ForEach(batch, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using var mat = DecodeImage(layerBytes[layerIndex], HeaderSettings.LayerDataType, Resolution); + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + layerBytes[layerIndex] = null; // Clean + + progress.LockAndIncrement(); + }); + } + + progress.Reset(OperationProgress.StatusDecodeGcode); + inputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin); + var gcodeDef = Helpers.Deserialize(inputFile); + GCodeStr = gcodeDef.GCodeText; + GCode.ParseLayersFromGCode(this); + } + + public override void SaveAs(string filePath = null, OperationProgress progress = null) + { + if (RequireFullEncode) + { + if (!string.IsNullOrEmpty(filePath)) + { + FileFullPath = filePath; + } + Encode(FileFullPath, progress); + return; + } + + if (!string.IsNullOrEmpty(filePath)) + { + File.Copy(FileFullPath, filePath, true); + FileFullPath = filePath; + } + + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(0, SeekOrigin.Begin); + FileSettings.Update(); + Helpers.SerializeWriteFileStream(outputFile, FileSettings); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + + outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin); + outputFile.SetLength(HeaderSettings.GCodeAddress); + + RebuildGCode(); + var gcodeSettings = new GCodeDef {GCodeText = GCodeStr}; + gcodeSettings.GCodeSize = (uint) gcodeSettings.GCodeText.Length; + Helpers.SerializeWriteFileStream(outputFile, gcodeSettings); + } + #endregion + + #region Static Methods + + public static byte[] EncodeImage(Mat mat, string encodeTo) + { + encodeTo = encodeTo.ToUpperInvariant(); + if (encodeTo is "PNG" or "JPG" or "JPEG" or "JP2" or "BMP" or "TIF" or "TIFF" or "PPM" or "PMG" or "SR" or "RAS") + { + return CvInvoke.Imencode($".{encodeTo.ToLowerInvariant()}", mat); + } + + if (encodeTo is "RGB555" or "RGB565" or "RGB888" + or "BGR555" or "BGR565" or "BGR888") + { + var bytesPerPixel = encodeTo is "RGB888" or "BGR888" ? 3 : 2; + var bytes = new byte[mat.Width * mat.Height * bytesPerPixel]; + uint index = 0; + var span = mat.GetDataByteSpan(); + for (int i = 0; i < span.Length;) + { + byte b = span[i++]; + byte g; + byte r; + + if (mat.NumberOfChannels == 1) // 8 bit safe-guard + { + r = g = b; + } + else + { + g = span[i++]; + r = span[i++]; + } + + if (mat.NumberOfChannels == 4) i++; // skip alpha + + switch (encodeTo) + { + case "RGB555": + var rgb555 = (ushort) (((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3)); + BitExtensions.ToBytesLittleEndian(rgb555, bytes, index); + index += 2; + break; + case "RGB565": + var rgb565 = (ushort) (((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3)); + BitExtensions.ToBytesLittleEndian(rgb565, bytes, index); + index += 2; + break; + case "RGB888": + bytes[index++] = r; + bytes[index++] = g; + bytes[index++] = b; + break; + } + } + + return bytes; + } + + throw new NotSupportedException($"The encode type: {encodeTo} is not supported."); + } + + public static Mat DecodeImage(byte[] bytes, string decodeFrom, Size resolution) + { + if (decodeFrom is "PNG" or "JPG" or "JPEG" or "JP2" or "BMP" or "TIF" or "TIFF" or "PPM" or "PMG" or "SR" or "RAS") + { + var mat = new Mat(); + CvInvoke.Imdecode(bytes, ImreadModes.AnyColor, mat); + return mat; + } + + if (decodeFrom is "RGB555" or "RGB565" or "RGB888" + or "BGR555" or "BGR565" or "BGR888") + { + var mat = new Mat(resolution, DepthType.Cv8U, 3); + var span = mat.GetDataByteSpan(); + var pixel = 0; + for (int i = 0; i < bytes.Length;) + { + switch (decodeFrom) + { + case "RGB555": + ushort rgb555 = BitExtensions.ToUShortLittleEndian(bytes, i); + span[pixel++] = (byte)((rgb555 & 0b00000000_00011111) << 3); // b + span[pixel++] = (byte)((rgb555 & 0b00000011_11100000) >> 2); // g + span[pixel++] = (byte)((rgb555 & 0b01111100_00000000) >> 7); // r + i += 2; + break; + case "RGB565": + ushort rgb565 = BitExtensions.ToUShortLittleEndian(bytes, i); + span[pixel++] = (byte)((rgb565 & 0b00000000_00011111) << 3); // b + span[pixel++] = (byte)((rgb565 & 0b00000111_11100000) >> 3); // g + span[pixel++] = (byte)((rgb565 & 0b11111000_00000000) >> 8); // r + i += 2; + break; + case "RGB888": + span[pixel++] = bytes[i + 2]; // b + span[pixel++] = bytes[i + 1]; // g + span[pixel++] = bytes[i]; // r + i += 3; + break; + case "BGR555": + ushort bgr555 = BitExtensions.ToUShortLittleEndian(bytes, i); + span[pixel++] = (byte)((bgr555 & 0b01111100_00000000) >> 7); // b + span[pixel++] = (byte)((bgr555 & 0b00000011_11100000) >> 2); // g + span[pixel++] = (byte)((bgr555 & 0b00000000_00011111) << 3); // r + i += 2; + break; + case "BGR565": + ushort bgr565 = BitExtensions.ToUShortLittleEndian(bytes, i); + span[pixel++] = (byte)((bgr565 & 0b11111000_00000000) >> 8); // b + span[pixel++] = (byte)((bgr565 & 0b00000111_11100000) >> 3); // g + span[pixel++] = (byte)((bgr565 & 0b00000000_00011111) << 3); // r + i += 2; + break; + case "BGR888": + span[pixel++] = bytes[i]; // b + span[pixel++] = bytes[i+1]; // g + span[pixel++] = bytes[i+2]; // r + i += 3; + break; + } + } + return mat; + } + + throw new NotSupportedException($"The decode type: {decodeFrom} is not supported."); + } + + public static Mat DecodeImage(byte[] bytes, string decodeFrom, uint resolutionX = 0, uint resolutionY = 0) + => DecodeImage(bytes, decodeFrom, new Size((int) resolutionX, (int) resolutionY)); + + + #endregion + } +} diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index c9a34ab..65e7819 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -775,7 +775,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay + public override bool DisplayMirror { get => HeaderSettings.ProjectorType > 0; set @@ -841,6 +841,26 @@ namespace UVtools.Core.FileFormats set => base.BottomExposureTime = HeaderSettings.BottomExposureSeconds = (float)Math.Round(value, 2); } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float ExposureTime { get => HeaderSettings.LayerExposureSeconds; @@ -1094,7 +1114,7 @@ namespace UVtools.Core.FileFormats HeaderSettings.AntiAliasLevel = 1; - progress.Reset(OperationProgress.StatusDecodeThumbnails, ThumbnailsCount); + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); Debug.Write("Header -> "); Debug.WriteLine(HeaderSettings); diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index 2195e9d..21436be 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -273,7 +273,7 @@ namespace UVtools.Core.FileFormats set { } } - public override bool MirrorDisplay + public override bool DisplayMirror { get => true; set { } @@ -321,10 +321,30 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = (float)(HeaderSettings.LightOffDelay = Math.Round(value, 2)); } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => (float) HeaderSettings.BottomExposureSeconds; - set => base.BottomExposureTime = (float) (HeaderSettings.BottomExposureSeconds = value); + set => base.BottomExposureTime = (float) (HeaderSettings.BottomExposureSeconds = Math.Round(value, 2)); } public override float ExposureTime diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 0c733b9..b442a57 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -1041,7 +1041,7 @@ namespace UVtools.Core.FileFormats set { } } - public override bool MirrorDisplay + public override bool DisplayMirror { get => true; set {} @@ -1090,6 +1090,26 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2); } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomExposureTime { get => HeaderSettings.BottomExposureSeconds; diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index c4580cd..763d021 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -267,7 +267,7 @@ namespace UVtools.Core.FileFormats get { //2021-01-23 at 04:07:36 UTC - var now = DateTime.Now; + var now = DateTime.UtcNow; return $"{now.Year}-{now.Month:D2}-{now.Day:D2} at {now.Hour:D2}:{now.Minute:D2}:{now.Second:D2} {now.Kind}"; } set{} @@ -375,7 +375,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay + public override bool DisplayMirror { get => PrinterSettings.DisplayMirrorX || PrinterSettings.DisplayMirrorY; set diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index f6e96ab..24e160f 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -210,7 +210,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay { get; set; } + public override bool DisplayMirror { get; set; } public override byte AntiAliasing { @@ -251,6 +251,26 @@ namespace UVtools.Core.FileFormats set => base.LightOffDelay = JsonSettings.Properties.Exposure.LightOffTime = (float)Math.Round(value, 2); } + public override float BottomWaitTimeBeforeCure + { + get => base.BottomWaitTimeBeforeCure; + set + { + SetBottomLightOffDelay(value); + base.BottomWaitTimeBeforeCure = value; + } + } + + public override float WaitTimeBeforeCure + { + get => base.WaitTimeBeforeCure; + set + { + SetNormalLightOffDelay(value); + base.WaitTimeBeforeCure = value; + } + } + public override float BottomLiftHeight { get => JsonSettings.Properties.Bottom.LiftHeight; diff --git a/UVtools.Core/FileFormats/VDAFile.cs b/UVtools.Core/FileFormats/VDAFile.cs index 126c325..ab4a0fd 100644 --- a/UVtools.Core/FileFormats/VDAFile.cs +++ b/UVtools.Core/FileFormats/VDAFile.cs @@ -60,11 +60,11 @@ namespace UVtools.Core.FileFormats public VDABy By { get; set; } = new(); - public string When { get; set; } = DateTime.Now.ToString("u"); + public string When { get; set; } = DateTime.UtcNow.ToString("u"); public void Reset() { - When = DateTime.Now.ToString("u"); + When = DateTime.UtcNow.ToString("u"); By.Reset(); } } diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index 7657917..9dfc917 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -44,7 +44,7 @@ namespace UVtools.Core.FileFormats [JsonProperty("application_version")] public string ApplicationVersion { get; set; } = "2.1.15.14"; //2021-04-09 17:48:46 - [JsonProperty("create_datetime")] public string CreateDateTime { get; set; } = DateTime.Now.ToString("u"); + [JsonProperty("create_datetime")] public string CreateDateTime { get; set; } = DateTime.UtcNow.ToString("u"); [JsonProperty("file_version")] public byte FileVersion { get; set; } = 1; @@ -257,7 +257,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay => ManifestFile.Machine.XMirror || ManifestFile.Machine.YMirror; + public override bool DisplayMirror => ManifestFile.Machine.XMirror || ManifestFile.Machine.YMirror; public override byte AntiAliasing { @@ -442,7 +442,7 @@ namespace UVtools.Core.FileFormats public void RebuildVDTLayers() { - ManifestFile.CreateDateTime = DateTime.Now.ToString("u"); + ManifestFile.CreateDateTime = DateTime.UtcNow.ToString("u"); var layers = new VDTLayer[LayerCount]; for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index 4b62ea3..643f14d 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -273,7 +273,7 @@ namespace UVtools.Core.FileFormats } } - public override bool MirrorDisplay => true; + public override bool DisplayMirror => true; public override byte AntiAliasing { @@ -456,7 +456,7 @@ namespace UVtools.Core.FileFormats GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute, GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.CentimetersPerMinute, GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds, - GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenameNonZeroPNG, + GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenamePng1Started, LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0, EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1, MaxLEDPower = MaxLEDPower, diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 0239e4d..a268d32 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -209,7 +209,7 @@ namespace UVtools.Core.FileFormats set { } } - public override bool MirrorDisplay + public override bool DisplayMirror { get => true; set { } @@ -364,7 +364,7 @@ namespace UVtools.Core.FileFormats GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Partial, GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute, GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds, - GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndexZero, + GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started, LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G1, EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1 }; diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 282c624..e53faa2 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -15,7 +15,6 @@ using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; -using Org.BouncyCastle.Asn1.Cms; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Objects; @@ -89,10 +88,10 @@ namespace UVtools.Core.GCode public enum GCodeShowImageTypes : byte { - FilenameZeroPNG, - FilenameNonZeroPNG, - LayerIndexZero, - LayerIndexNonZero, + FilenamePng0Started, + FilenamePng1Started, + LayerIndex0Started, + LayerIndex1Started, } #endregion @@ -103,7 +102,7 @@ namespace UVtools.Core.GCode private GCodePositioningTypes _gCodePositioningType = GCodePositioningTypes.Absolute; private GCodeTimeUnits _gCodeTimeUnit = GCodeTimeUnits.Milliseconds; private GCodeSpeedUnits _gCodeSpeedUnit = GCodeSpeedUnits.MillimetersPerMinute; - private GCodeShowImageTypes _gCodeShowImageType = GCodeShowImageTypes.FilenameNonZeroPNG; + private GCodeShowImageTypes _gCodeShowImageType = GCodeShowImageTypes.FilenamePng1Started; private bool _syncMovementsWithDelay; private bool _useTailComma = true; private bool _useComments = true; @@ -308,9 +307,7 @@ namespace UVtools.Core.GCode public void AppendUVtools() { - string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; - var version = Assembly.GetExecutingAssembly().GetName().Version; - AppendComment($"Generated by {About.Software} v{version.ToString(3)} {arch} @ {DateTime.Now}"); + AppendComment($"Generated by {About.Software} v{About.VersionStr} {About.Arch} @ {DateTime.UtcNow}"); } public void AppendStartGCode() @@ -538,19 +535,19 @@ namespace UVtools.Core.GCode public string GetShowImageString(uint layerIndex) => _gCodeShowImageType switch { - GCodeShowImageTypes.FilenameZeroPNG => $"{layerIndex}.png", - GCodeShowImageTypes.FilenameNonZeroPNG => $"{layerIndex + 1}.png", - GCodeShowImageTypes.LayerIndexZero => $"{layerIndex}", - GCodeShowImageTypes.LayerIndexNonZero => $"{layerIndex + 1}", + GCodeShowImageTypes.FilenamePng0Started => $"{layerIndex}.png", + GCodeShowImageTypes.FilenamePng1Started => $"{layerIndex + 1}.png", + GCodeShowImageTypes.LayerIndex0Started => $"{layerIndex}", + GCodeShowImageTypes.LayerIndex1Started => $"{layerIndex + 1}", _ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}") }; public string GetShowImageString(string value) => _gCodeShowImageType switch { - GCodeShowImageTypes.FilenameZeroPNG => $"{value}.png", - GCodeShowImageTypes.FilenameNonZeroPNG => $"{value}.png", - GCodeShowImageTypes.LayerIndexZero => $"{value}", - GCodeShowImageTypes.LayerIndexNonZero => $"{value}", + GCodeShowImageTypes.FilenamePng0Started => $"{value}.png", + GCodeShowImageTypes.FilenamePng1Started => $"{value}.png", + GCodeShowImageTypes.LayerIndex0Started => $"{value}", + GCodeShowImageTypes.LayerIndex1Started => $"{value}", _ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}") }; @@ -752,8 +749,8 @@ namespace UVtools.Core.GCode if (match.Success && match.Groups.Count >= 2) // Begin new layer { var layerIndex = uint.Parse(match.Groups[1].Value); - if (_gCodeShowImageType is GCodeShowImageTypes.FilenameNonZeroPNG or GCodeShowImageTypes - .LayerIndexNonZero) layerIndex--; + if (_gCodeShowImageType is GCodeShowImageTypes.FilenamePng1Started or GCodeShowImageTypes + .LayerIndex1Started) layerIndex--; if (layerIndex > slicerFile.LayerCount) { throw new FileLoadException( diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs index c8ed94d..7ae01d0 100644 --- a/UVtools.Core/Layer/Layer.cs +++ b/UVtools.Core/Layer/Layer.cs @@ -561,16 +561,16 @@ namespace UVtools.Core $"{nameof(BoundingRectangle)}: {BoundingRectangle}, " + $"{nameof(IsBottomLayer)}: {IsBottomLayer}, " + $"{nameof(IsNormalLayer)}: {IsNormalLayer}, " + - $"{nameof(LayerHeight)}: {LayerHeight}, " + - $"{nameof(PositionZ)}: {PositionZ}, " + - $"{nameof(LightOffDelay)}: {LightOffDelay}, " + - $"{nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, " + - $"{nameof(ExposureTime)}: {ExposureTime}, " + - $"{nameof(WaitTimeAfterCure)}: {WaitTimeAfterCure}, " + - $"{nameof(LiftHeight)}: {LiftHeight}, " + - $"{nameof(LiftSpeed)}: {LiftSpeed}, " + - $"{nameof(WaitTimeAfterLift)}: {WaitTimeAfterLift}, " + - $"{nameof(RetractSpeed)}: {RetractSpeed}, " + + $"{nameof(LayerHeight)}: {LayerHeight}mm, " + + $"{nameof(PositionZ)}: {PositionZ}mm, " + + $"{nameof(LightOffDelay)}: {LightOffDelay}s, " + + $"{nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}s, " + + $"{nameof(ExposureTime)}: {ExposureTime}s, " + + $"{nameof(WaitTimeAfterCure)}: {WaitTimeAfterCure}s, " + + $"{nameof(LiftHeight)}: {LiftHeight}mm, " + + $"{nameof(LiftSpeed)}: {LiftSpeed}mm/mim, " + + $"{nameof(WaitTimeAfterLift)}: {WaitTimeAfterLift}s, " + + $"{nameof(RetractSpeed)}: {RetractSpeed}mm/mim, " + $"{nameof(LightPWM)}: {LightPWM}, " + $"{nameof(IsModified)}: {IsModified}, " + $"{nameof(HaveGlobalParameters)}: {HaveGlobalParameters}"; @@ -606,21 +606,21 @@ namespace UVtools.Core WaitTimeAfterLift = 0; } - public string FormatFileName(string prepend, byte padDigits, bool layerIndexZeroStarted = true) + public string FormatFileName(string prepend, byte padDigits, bool layerIndexZeroStarted = true, string appendExt = ".png") { var index = Index; if (!layerIndexZeroStarted) { index++; } - return $"{prepend}{index.ToString().PadLeft(padDigits, '0')}.png"; + return $"{prepend}{index.ToString().PadLeft(padDigits, '0')}{appendExt}"; } - public string FormatFileName(string prepend = "", bool layerIndexZeroStarted = true) - => FormatFileName(prepend, ParentLayerManager.LayerDigits, layerIndexZeroStarted); + public string FormatFileName(string prepend = "", bool layerIndexZeroStarted = true, string appendExt = ".png") + => FormatFileName(prepend, ParentLayerManager.LayerDigits, layerIndexZeroStarted, appendExt); - public string FormatFileName(byte padDigits, bool layerIndexZeroStarted = true) - => FormatFileName(string.Empty, padDigits, layerIndexZeroStarted); + public string FormatFileName(byte padDigits, bool layerIndexZeroStarted = true, string appendExt = ".png") + => FormatFileName(string.Empty, padDigits, layerIndexZeroStarted, appendExt); public Rectangle GetBoundingRectangle(Mat mat = null, bool reCalculate = false) diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index 307b1df..4d4c2a4 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -595,12 +595,15 @@ namespace UVtools.Core /// If true also set light off to 0, otherwise current value will be kept. /// public void SetNoLiftForSamePositionedLayers(bool zeroLightOffDelay = false) + => SetLiftForSamePositionedLayers(0, zeroLightOffDelay); + + public void SetLiftForSamePositionedLayers(float liftHeight = 0, bool zeroLightOffDelay = false) { for (int layerIndex = 1; layerIndex < LayerCount; layerIndex++) { var layer = this[layerIndex]; if (this[layerIndex - 1].PositionZ != layer.PositionZ) continue; - layer.LiftHeight = 0; + layer.LiftHeight = liftHeight; layer.WaitTimeAfterLift = 0; if (zeroLightOffDelay) { diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs index 855a2f8..14744f8 100644 --- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs +++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs @@ -370,7 +370,7 @@ namespace UVtools.Core.Operations if (_bottomLayers <= 0) _bottomLayers = (ushort) Slicer.Slicer.MillimetersToLayers(1M, _layerHeight); if (_normalLayers <= 0) _normalLayers = (ushort) Slicer.Slicer.MillimetersToLayers(3.5M, _layerHeight); - _mirrorOutput = SlicerFile.MirrorDisplay; + _mirrorOutput = SlicerFile.DisplayMirror; } #endregion diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index 5ba5bd5..608b8ec 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -1144,7 +1144,7 @@ namespace UVtools.Core.Operations { base.InitWithSlicerFile(); - _mirrorOutput = SlicerFile.MirrorDisplay; + _mirrorOutput = SlicerFile.DisplayMirror; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; @@ -2224,7 +2224,7 @@ namespace UVtools.Core.Operations if (_mirrorOutput) { - new OperationFlip(SlicerFile) { FlipDirection = Enumerations.FlipDirection.Horizontally }.Execute(progress); + new OperationFlip(SlicerFile) { FlipDirection = FlipType.Horizontal }.Execute(progress); } } diff --git a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs index 9810dee..b92d339 100644 --- a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs +++ b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs @@ -112,7 +112,7 @@ namespace UVtools.Core.Operations if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.MirrorDisplay; + _mirrorOutput = SlicerFile.DisplayMirror; } #endregion diff --git a/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs b/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs new file mode 100644 index 0000000..4caf862 --- /dev/null +++ b/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs @@ -0,0 +1,442 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Drawing; +using System.Text; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; + +namespace UVtools.Core.Operations +{ + [Serializable] + public sealed class OperationCalibrateLiftHeight : Operation + { + #region Members + private decimal _layerHeight; + private ushort _bottomLayers = 3; + private ushort _normalLayers = 2; + private decimal _bottomExposure; + private decimal _normalExposure; + private decimal _bottomLiftHeight; + private decimal _liftHeight; + private decimal _bottomLiftSpeed; + private decimal _liftSpeed; + private decimal _retractSpeed; + private ushort _leftRightMargin = 200; + private ushort _topBottomMargin = 200; + private bool _decreaseImage = true; + private byte _decreaseImageFactor = 10; + private byte _minimumImageFactor = 10; + + #endregion + + #region Overrides + + public override bool CanROI => false; + + public override bool CanCancel => false; + + public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None; + + public override string Title => "Lift height"; + public override string Description => + "Generates test models with various strategies and increments to measure the optimal lift height or peel forces for layers given the printed area.\n" + + "You must have a tool to measure the lift height / forces as it moves up, record the values to determine the lowest safe value for the lift.\n" + + "After find the height where it peels, you must give from 1mm to 2mm more for safeness.\n" + + "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" + + "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file."; + + public override string ConfirmationText => + $"generate the lift height test?"; + + public override string ProgressTitle => + $"Generating the lift height test"; + + public override string ProgressAction => "Generated"; + + public override string ValidateInternally() + { + var sb = new StringBuilder(); + + if (SlicerFile.ResolutionX - _leftRightMargin * 2 <= 0) + sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution."); + + if (SlicerFile.ResolutionY - _topBottomMargin * 2 <= 0) + sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution."); + + if (_decreaseImage) + { + if(_decreaseImageFactor is 0 or >= 100) + sb.AppendLine("The image decrease factor must be between 1 and 99%"); + + if(_minimumImageFactor is 0 or >= 100) + sb.AppendLine("The minimum image decrease factor must be between 1 and 99%"); + } + + return sb.ToString(); + } + + public override string ToString() + { + var result = $"[Layer Height: {_layerHeight}] " + + $"[Layers: {_bottomLayers}/{_normalLayers}] " + + $"[Exposure: {_bottomExposure}/{_normalExposure}s] " + + $"[Lift: {_bottomLiftHeight}/{_liftHeight}mm @ {_bottomLiftSpeed}/{_liftSpeed}mm/min]" + + $"[Retract speed: {_retractSpeed}mm/min]" + + $"[Decrease image: {_decreaseImage} @ {_decreaseImageFactor}-{_minimumImageFactor}%]"; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + + #endregion + + #region Properties + + public decimal LayerHeight + { + get => _layerHeight; + set + { + if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return; + RaisePropertyChanged(nameof(BottomHeight)); + RaisePropertyChanged(nameof(NormalHeight)); + RaisePropertyChanged(nameof(TotalHeight)); + } + } + + public ushort Microns => (ushort) (LayerHeight * 1000); + + public ushort BottomLayers + { + get => _bottomLayers; + set + { + if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return; + RaisePropertyChanged(nameof(BottomHeight)); + RaisePropertyChanged(nameof(TotalHeight)); + RaisePropertyChanged(nameof(LayerCount)); + } + } + + public ushort NormalLayers + { + get => _normalLayers; + set + { + if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return; + RaisePropertyChanged(nameof(NormalHeight)); + RaisePropertyChanged(nameof(TotalHeight)); + RaisePropertyChanged(nameof(LayerCount)); + } + } + + public uint LayerCount + { + get + { + uint layerCount = (uint)(_bottomLayers + _normalLayers); + if (_decreaseImage) + { + layerCount += (100u - _minimumImageFactor) / _decreaseImageFactor; + //layerCount += (uint)Math.Ceiling((100.0 - _minimumImageFactor - _decreaseImageFactor) / _decreaseImageFactor); + //for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor) + // layerCount++; + } + return layerCount; + } + } + + public decimal BottomHeight => Layer.RoundHeight(_layerHeight * _bottomLayers); + public decimal NormalHeight => Layer.RoundHeight(_layerHeight * (LayerCount - _bottomLayers)); + + public decimal TotalHeight => BottomHeight + NormalHeight; + + public decimal BottomExposure + { + get => _bottomExposure; + set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2)); + } + + public decimal NormalExposure + { + get => _normalExposure; + set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2)); + } + + public decimal BottomLiftHeight + { + get => _bottomLiftHeight; + set => RaiseAndSetIfChanged(ref _bottomLiftHeight, value); + } + + public decimal LiftHeight + { + get => _liftHeight; + set => RaiseAndSetIfChanged(ref _liftHeight, value); + } + + public decimal BottomLiftSpeed + { + get => _bottomLiftSpeed; + set => RaiseAndSetIfChanged(ref _bottomLiftSpeed, value); + } + + public decimal LiftSpeed + { + get => _liftSpeed; + set => RaiseAndSetIfChanged(ref _liftSpeed, value); + } + + public decimal RetractSpeed + { + get => _retractSpeed; + set => RaiseAndSetIfChanged(ref _retractSpeed, value); + } + + public ushort LeftRightMargin + { + get => _leftRightMargin; + set => RaiseAndSetIfChanged(ref _leftRightMargin, value); + } + + public ushort MaxLeftRightMargin => (ushort)((SlicerFile.ResolutionX - 100) / 2); + + public ushort TopBottomMargin + { + get => _topBottomMargin; + set => RaiseAndSetIfChanged(ref _topBottomMargin, value); + } + + public ushort MaxTopBottomMargin => (ushort) ((SlicerFile.ResolutionY - 100) / 2); + + public bool DecreaseImage + { + get => _decreaseImage; + set + { + RaiseAndSetIfChanged(ref _decreaseImage, value); + RaisePropertyChanged(nameof(TotalHeight)); + RaisePropertyChanged(nameof(LayerCount)); + } + } + + public byte DecreaseImageFactor + { + get => _decreaseImageFactor; + set + { + RaiseAndSetIfChanged(ref _decreaseImageFactor, value); + RaisePropertyChanged(nameof(TotalHeight)); + RaisePropertyChanged(nameof(LayerCount)); + } + } + + public byte MinimumImageFactor + { + get => _minimumImageFactor; + set + { + RaiseAndSetIfChanged(ref _minimumImageFactor, value); + RaisePropertyChanged(nameof(TotalHeight)); + RaisePropertyChanged(nameof(LayerCount)); + } + } + + public Rectangle WhiteBlock + { + get + { + int width = (int) (SlicerFile.ResolutionX - _leftRightMargin * 2); + int height = (int) (SlicerFile.ResolutionY - _topBottomMargin * 2); + int x = (int) (SlicerFile.ResolutionX - width) / 2; + int y = (int) (SlicerFile.ResolutionY - height) / 2; + + return new Rectangle(x, y, width, height); + } + } + + #endregion + + #region Constructor + + public OperationCalibrateLiftHeight() { } + + public OperationCalibrateLiftHeight(FileFormat slicerFile) : base(slicerFile) + { } + + public override void InitWithSlicerFile() + { + base.InitWithSlicerFile(); + if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight; + if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; + if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; + if (_bottomLiftHeight <= 0) _bottomLiftHeight = (decimal)SlicerFile.BottomLiftHeight; + if (_liftHeight <= 0) _liftHeight = (decimal)SlicerFile.LiftHeight; + if (_bottomLiftSpeed <= 0) _bottomLiftSpeed = (decimal) SlicerFile.BottomLiftSpeed; + if (_liftSpeed <= 0) _liftSpeed = (decimal) SlicerFile.LiftSpeed; + if (_retractSpeed <= 0) _retractSpeed = (decimal) SlicerFile.RetractSpeed; + } + + #endregion + + #region Equality + + private bool Equals(OperationCalibrateLiftHeight other) + { + return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _bottomLiftHeight == other._bottomLiftHeight && _liftHeight == other._liftHeight && _bottomLiftSpeed == other._bottomLiftSpeed && _liftSpeed == other._liftSpeed && _retractSpeed == other._retractSpeed && _leftRightMargin == other._leftRightMargin && _topBottomMargin == other._topBottomMargin && _decreaseImage == other._decreaseImage && _decreaseImageFactor == other._decreaseImageFactor && _minimumImageFactor == other._minimumImageFactor; + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is OperationCalibrateLiftHeight other && Equals(other); + } + + public override int GetHashCode() + { + var hashCode = new HashCode(); + hashCode.Add(_layerHeight); + hashCode.Add(_bottomLayers); + hashCode.Add(_normalLayers); + hashCode.Add(_bottomExposure); + hashCode.Add(_normalExposure); + hashCode.Add(_bottomLiftHeight); + hashCode.Add(_liftHeight); + hashCode.Add(_bottomLiftSpeed); + hashCode.Add(_liftSpeed); + hashCode.Add(_retractSpeed); + hashCode.Add(_leftRightMargin); + hashCode.Add(_topBottomMargin); + hashCode.Add(_decreaseImage); + hashCode.Add(_decreaseImageFactor); + hashCode.Add(_minimumImageFactor); + return hashCode.ToHashCode(); + } + + #endregion + + #region Methods + + /// + /// Gets the bottom and normal layers, 0 = bottom | 1 = normal + /// + /// + public Mat[] GetLayers() + { + var layers = new Mat[1]; + + layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution); + CvInvoke.Rectangle(layers[0], WhiteBlock, EmguExtensions.WhiteColor, -1); + + return layers; + } + + public Mat GetThumbnail() + { + Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3); + var fontFace = FontFace.HersheyDuplex; + var fontScale = 1; + var fontThickness = 2; + const byte xSpacing = 45; + const byte ySpacing = 45; + CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1); + CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.PutText(thumbnail, "Lift Height Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); + CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + //CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness); + + return thumbnail; + } + + protected override bool ExecuteInternally(OperationProgress progress) + { + progress.ItemCount = LayerCount; + + var newLayers = new Layer[LayerCount]; + + var layers = GetLayers(); + progress++; + + var layer = new Layer(0, layers[0], SlicerFile.LayerManager) + { + IsModified = true + }; + + uint layerIndex = 0; + for (; layerIndex < _bottomLayers + _normalLayers; layerIndex++) + { + newLayers[layerIndex] = layer.Clone(); + progress++; + } + + if (_decreaseImage) + { + var rect = WhiteBlock; + for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor) + { + using var mat = layers[0].NewBlank(); + + // size - 100 + // x - factor + + int width = rect.Width * factor / 100; + int height = rect.Height * factor / 100; + int x = (int)(SlicerFile.ResolutionX - width) / 2; + int y = (int)(SlicerFile.ResolutionY - height) / 2; + + CvInvoke.Rectangle(mat, + new Rectangle(x, y, width, height), + EmguExtensions.WhiteColor, -1); + + newLayers[layerIndex] = new Layer(0, mat, SlicerFile.LayerManager) + { + IsModified = true + }; + + layerIndex++; + progress++; + } + } + + foreach (var mat in layers) + { + mat.Dispose(); + } + + + if (SlicerFile.ThumbnailsCount > 0) + SlicerFile.SetThumbnails(GetThumbnail()); + + progress++; + + SlicerFile.SuppressRebuildPropertiesWork(() => + { + SlicerFile.LayerHeight = (float)_layerHeight; + SlicerFile.BottomExposureTime = (float)_bottomExposure; + SlicerFile.ExposureTime = (float)_normalExposure; + SlicerFile.BottomLiftHeight = (float)_bottomLiftHeight; + SlicerFile.LiftHeight = (float)_liftHeight; + SlicerFile.BottomLiftSpeed = (float)_bottomLiftSpeed; + SlicerFile.LiftSpeed = (float)_liftSpeed; + SlicerFile.RetractSpeed = (float)_retractSpeed; + SlicerFile.BottomLayerCount = _bottomLayers; + + SlicerFile.LayerManager.Layers = newLayers; + }, true); + + return !progress.Token.IsCancellationRequested; + } + + #endregion + } +} diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index a11f630..2ca4b29 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -111,7 +111,7 @@ namespace UVtools.Core.Operations if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.MirrorDisplay; + _mirrorOutput = SlicerFile.DisplayMirror; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs index 033c487..20036a7 100644 --- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs +++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs @@ -431,7 +431,7 @@ namespace UVtools.Core.Operations if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.MirrorDisplay; + _mirrorOutput = SlicerFile.DisplayMirror; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs index 54f195a..392cdf2 100644 --- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs +++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs @@ -463,7 +463,7 @@ namespace UVtools.Core.Operations if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.MirrorDisplay; + _mirrorOutput = SlicerFile.DisplayMirror; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs index 047117d..3510d3e 100644 --- a/UVtools.Core/Operations/OperationFlip.cs +++ b/UVtools.Core/Operations/OperationFlip.cs @@ -19,7 +19,7 @@ namespace UVtools.Core.Operations { #region Members private bool _makeCopy; - private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.Horizontally; + private FlipType _flipDirection = FlipType.Horizontal; #endregion #region Overrides @@ -29,14 +29,14 @@ namespace UVtools.Core.Operations "Note: Before perform this operation, un-rotate the layer preview to see the real orientation."; public override string ConfirmationText => - FlipDirection == Enumerations.FlipDirection.Both - ? $"flip {(MakeCopy == true? "and blend ":"")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically?" - : $"flip {(MakeCopy == true ? "and blend " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}?"; + FlipDirection == FlipType.Both + ? $"flip {(_makeCopy ? "and blend ":"")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically?" + : $"flip {(_makeCopy ? "and blend " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}?"; public override string ProgressTitle => - FlipDirection == Enumerations.FlipDirection.Both - ? $"Flipping {(MakeCopy == true ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically" - : $"Flipping {(MakeCopy == true ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}"; + FlipDirection == FlipType.Both + ? $"Flipping {(_makeCopy ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically" + : $"Flipping {(_makeCopy ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}"; public override string ProgressAction => "Flipped layers"; @@ -50,7 +50,7 @@ namespace UVtools.Core.Operations #region Properties - public Enumerations.FlipDirection FlipDirection + public FlipType FlipDirection { get => _flipDirection; set => RaiseAndSetIfChanged(ref _flipDirection, value); @@ -62,29 +62,6 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _makeCopy, value); } - public static Array FlipDirections => Enum.GetValues(typeof(Enumerations.FlipDirection)); - - public FlipType FlipTypeOpenCV - { - get - { - var flipType = FlipType.Horizontal; - switch (FlipDirection) - { - case Enumerations.FlipDirection.Horizontally: - flipType = FlipType.Horizontal; - break; - case Enumerations.FlipDirection.Vertically: - flipType = FlipType.Vertical; - break; - case Enumerations.FlipDirection.Both: - flipType = FlipType.Horizontal | FlipType.Vertical; - break; - } - - return flipType; - } - } #endregion #region Constructor @@ -144,12 +121,12 @@ namespace UVtools.Core.Operations if (MakeCopy) { using Mat dst = new(); - CvInvoke.Flip(target, dst, FlipTypeOpenCV); + CvInvoke.Flip(target, dst, _flipDirection); CvInvoke.Add(target, dst, target); } else { - CvInvoke.Flip(target, target, FlipTypeOpenCV); + CvInvoke.Flip(target, target, _flipDirection); } ApplyMask(original, target); diff --git a/UVtools.Core/Operations/OperationLayerExportGif.cs b/UVtools.Core/Operations/OperationLayerExportGif.cs index 0762608..ae93843 100644 --- a/UVtools.Core/Operations/OperationLayerExportGif.cs +++ b/UVtools.Core/Operations/OperationLayerExportGif.cs @@ -24,30 +24,6 @@ namespace UVtools.Core.Operations [Serializable] public sealed class OperationLayerExportGif : Operation { - #region Enums - public enum ExportGifRotateDirection : byte - { - None = 9, - /// Rotate 90 degrees clockwise - [Description("Rotate 90º CW")] - Rotate90Clockwise = 0, - /// Rotate 180 degrees clockwise - [Description("Rotate 180º")] - Rotate180 = 1, - /// Rotate 270 degrees clockwise - [Description("Rotate 90º CCW")] - Rotate90CounterClockwise = 2, - } - - public enum ExportGifFlipDirection : byte - { - None, - Horizontally, - Vertically, - Both, - } - #endregion - #region Members private string _filePath; private bool _clipByVolumeBounds; @@ -56,8 +32,8 @@ namespace UVtools.Core.Operations private ushort _repeats; private ushort _skip; private decimal _scale = 50; - private ExportGifRotateDirection _rotateDirection = ExportGifRotateDirection.None; - private ExportGifFlipDirection _flipDirection = ExportGifFlipDirection.None; + private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None; + private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None; #endregion @@ -188,13 +164,13 @@ namespace UVtools.Core.Operations public float ScaleFactor => (float)_scale / 100f; - public ExportGifRotateDirection RotateDirection + public Enumerations.RotateDirection RotateDirection { get => _rotateDirection; set => RaiseAndSetIfChanged(ref _rotateDirection, value); } - public ExportGifFlipDirection FlipDirection + public Enumerations.FlipDirection FlipDirection { get => _flipDirection; set => RaiseAndSetIfChanged(ref _flipDirection, value); @@ -257,23 +233,14 @@ namespace UVtools.Core.Operations CvInvoke.Resize(matRoi, matRoi, new Size((int) (matRoi.Width * ScaleFactor), (int)(matRoi.Height * ScaleFactor))); } - if (_flipDirection != ExportGifFlipDirection.None) + if (_flipDirection != Enumerations.FlipDirection.None) { - FlipType? flipType = _flipDirection switch - { - ExportGifFlipDirection.Horizontally => FlipType.Horizontal, - ExportGifFlipDirection.Vertically => FlipType.Vertical, - ExportGifFlipDirection.Both => FlipType.Both, - _ => null - }; - - if (flipType is not null) - CvInvoke.Flip(matRoi, matRoi, flipType.Value); + CvInvoke.Flip(matRoi, matRoi, Enumerations.ToOpenCVFlipType(_flipDirection)); } - if (_rotateDirection != ExportGifRotateDirection.None) + if (_rotateDirection != Enumerations.RotateDirection.None) { - CvInvoke.Rotate(matRoi, matRoi, (RotateFlags) _rotateDirection); + CvInvoke.Rotate(matRoi, matRoi, Enumerations.ToOpenCVRotateFlags(_rotateDirection)); } if (_renderLayerCount) diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs new file mode 100644 index 0000000..7e52359 --- /dev/null +++ b/UVtools.Core/Operations/OperationLayerExportImage.cs @@ -0,0 +1,303 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.ComponentModel; +using System.IO; +using System.Threading.Tasks; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Util; +using UVtools.Core.FileFormats; + +namespace UVtools.Core.Operations +{ + [Serializable] + public sealed class OperationLayerExportImage : Operation + { + #region Enums + + public enum LayerExportImageTypes : byte + { + [Description("PNG: Portable Network Graphics")] + PNG, + [Description("JPG: Joint Photographic Experts Group")] + JPG, + [Description("JPEG: Joint Photographic Experts Group")] + JPEG, + [Description("JP2: Joint Photographic Experts Group (JPEG 2000)")] + JP2, + [Description("TIF: Tag Image File Format")] + TIF, + [Description("TIFF: Tag Image File Format")] + TIFF, + [Description("BMP: Bitmap")] + BMP, + [Description("PBM: Portable Bitmap")] + PBM, + [Description("PGM: Portable Greymap")] + PGM, + //[Description("PPM: Portable Pixmap")] + //PPM, + [Description("SR: Sun raster")] + SR, + [Description("RAS: Sun raster")] + RAS, + [Description("SVG: Scalable Vector Graphics")] + SVG + } + #endregion + + #region Members + + private string _outputFolder; + private string _filename = "layer"; + private LayerExportImageTypes _imageType = LayerExportImageTypes.PNG; + private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None; + private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None; + private bool _padLayerIndex = true; + private bool _cropByRoi = true; + + #endregion + + #region Overrides + + public override bool CanHaveProfiles => false; + public override string Title => "Export layers to images"; + + public override string Description => + "Export a layer range to images."; + + public override string ConfirmationText => + $"export {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}?"; + + public override string ProgressTitle => + $"Exporting {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressAction => $"Exported {_imageType} images"; + + /*public override string ValidateInternally() + { + var sb = new StringBuilder(); + + if (LayerRangeCount < 2) + { + sb.AppendLine("To generate a heat map at least two layers are required."); + } + + return sb.ToString(); + }*/ + + public override string ToString() + { + var result = $"[Crop by ROI: {_cropByRoi}] [Pad index: {_padLayerIndex}]" + + LayerRangeString; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + + #endregion + + #region Properties + + public string OutputFolder + { + get => _outputFolder; + set => RaiseAndSetIfChanged(ref _outputFolder, value); + } + + public string Filename + { + get => _filename; + set => RaiseAndSetIfChanged(ref _filename, value); + } + + public LayerExportImageTypes ImageType + { + get => _imageType; + set => RaiseAndSetIfChanged(ref _imageType, value); + } + + public Enumerations.RotateDirection RotateDirection + { + get => _rotateDirection; + set => RaiseAndSetIfChanged(ref _rotateDirection, value); + } + + public Enumerations.FlipDirection FlipDirection + { + get => _flipDirection; + set => RaiseAndSetIfChanged(ref _flipDirection, value); + } + + public bool PadLayerIndex + { + get => _padLayerIndex; + set => RaiseAndSetIfChanged(ref _padLayerIndex, value); + } + + public bool CropByROI + { + get => _cropByRoi; + set => RaiseAndSetIfChanged(ref _cropByRoi, value); + } + + #endregion + + #region Constructor + + public OperationLayerExportImage() + { } + + public OperationLayerExportImage(FileFormat slicerFile) : base(slicerFile) + { + _outputFolder = Path.Combine(Path.GetDirectoryName(SlicerFile.FileFullPath) ?? string.Empty, FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath)); + } + + #endregion + + #region Methods + + protected override bool ExecuteInternally(OperationProgress progress) + { + if (!Directory.Exists(_outputFolder)) + { + Directory.CreateDirectory(_outputFolder); + } + + var slicedFileNameNoExt = SlicerFile.FilenameNoExt; + + Parallel.For(LayerIndexStart, LayerIndexEnd+1, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + + using var mat = SlicerFile[layerIndex].LayerMat; + var matRoi = mat; + if (_cropByRoi && HaveROI) + { + matRoi = GetRoiOrDefault(mat); + } + + if (_flipDirection != Enumerations.FlipDirection.None) + { + CvInvoke.Flip(matRoi, matRoi, Enumerations.ToOpenCVFlipType(_flipDirection)); + } + + if (_rotateDirection != Enumerations.RotateDirection.None) + { + CvInvoke.Rotate(matRoi, matRoi, Enumerations.ToOpenCVRotateFlags(_rotateDirection)); + } + + var filename = SlicerFile[layerIndex].FormatFileName(_filename, _padLayerIndex ? SlicerFile.LayerManager.LayerDigits : byte.MinValue, true, string.Empty); + var fileFullPath = Path.Combine(_outputFolder, $"{filename}.{_imageType.ToString().ToLower()}"); + + if (_imageType != LayerExportImageTypes.SVG) + { + matRoi.Save(fileFullPath); + } + else + { + // SVG + + CvInvoke.Threshold(matRoi, matRoi, 127, byte.MaxValue, ThresholdType.Binary); // Remove AA + + using var contours = new VectorOfVectorOfPoint(); + using var hierarchy = new Mat(); + CvInvoke.FindContours(matRoi, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxSimple); + var hierarchyJagged = hierarchy.GetData(); + + using TextWriter tw = new StreamWriter(fileFullPath); + tw.WriteLine(""); + tw.WriteLine(""); + tw.WriteLine("\t"); + tw.WriteLine("\t\t"); + tw.WriteLine("\t"); + tw.WriteLine($"\t{slicedFileNameNoExt} #{layerIndex}"); + tw.WriteLine($"\t"); + + // + //hierarchy[i][0]: the index of the next contour of the same level + //hierarchy[i][1]: the index of the previous contour of the same level + //hierarchy[i][2]: the index of the first child + //hierarchy[i][3]: the index of the parent + // + + for (int i = 0; i < contours.Size; i++) + { + if (contours[i].Size == 0) continue; + + var style = "white"; + + int parentIndex = i; + int count = 0; + while ((parentIndex = (int)hierarchyJagged.GetValue(0, parentIndex, 3)) != -1) + { + count++; + } + + if (count % 2 != 0) + style = "black"; + + tw.Write($"\t"); + } + + tw.WriteLine(""); + } + + progress.LockAndIncrement(); + }); + + return !progress.Token.IsCancellationRequested; + } + + #endregion + + #region Equality + + private bool Equals(OperationLayerExportImage other) + { + return _outputFolder == other._outputFolder && _filename == other._filename && _imageType == other._imageType && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _padLayerIndex == other._padLayerIndex && _cropByRoi == other._cropByRoi; + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is OperationLayerExportImage other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(_outputFolder, _filename, (int) _imageType, (int) _rotateDirection, (int) _flipDirection, _padLayerIndex, _cropByRoi); + } + + #endregion + } +} diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 732434a..a567638 100644 --- a/UVtools.Core/Operations/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -15,11 +15,13 @@ namespace UVtools.Core.Operations { public sealed class OperationProgress : BindableBase { - public const string StatusDecodeThumbnails = "Decoded Thumbnails"; + public const string StatusDecodePreviews = "Decoded Previews"; public const string StatusGatherLayers = "Gathered Layers"; public const string StatusDecodeLayers = "Decoded Layers"; + public const string StatusEncodePreviews = "Encoded Previews"; public const string StatusEncodeLayers = "Encoded Layers"; public const string StatusWritingFile = "Writing File"; + public const string StatusDecodeGcode = "Decoding GCode"; public const string StatusEncodeGcode = "Encoding GCode"; public const string StatusOptimizingBounds = "Gathering Bounds"; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 51050a2..b2d41a2 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 2.16.0 + 2.17.0 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 diff --git a/UVtools.Installer/Code/Features.wxs b/UVtools.Installer/Code/Features.wxs index 820a881..987a66d 100644 --- a/UVtools.Installer/Code/Features.wxs +++ b/UVtools.Installer/Code/Features.wxs @@ -3,7 +3,7 @@ - + diff --git a/UVtools.Installer/Code/Product.wxs b/UVtools.Installer/Code/Product.wxs index 9a0b9fb..4e4ae42 100644 --- a/UVtools.Installer/Code/Product.wxs +++ b/UVtools.Installer/Code/Product.wxs @@ -10,7 +10,7 @@ Version="$(var.MSIProductVersion)" --> - + diff --git a/UVtools.Installer/Resources/Banner.fw.png b/UVtools.Installer/Resources/Banner.fw.png new file mode 100644 index 0000000000000000000000000000000000000000..ac99ff2c7b2bc0908a7a63c9d5daaa6fef0768ba GIT binary patch literal 53292 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 E1s%9#xc~qF literal 1241 zcmex=kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVFIc#WB`&3f20;!6E5^6Xj7khlf{e_9jQ@`?$OB!^3JfDqFhKz$ z6Eh1d8_-Ey+&~3e1%S?HW@Z99mX(DCsJa#?&%h$cDx_%W$R-?^$gWfnAuRebI{N?Mn?>~P20{M%Pff?d0xX;l1 zB?$Bv6EF@~*g^hcWGV+@WL#u diff --git a/UVtools.Installer/Resources/Dialog.fw.png b/UVtools.Installer/Resources/Dialog.fw.png new file mode 100644 index 0000000000000000000000000000000000000000..823885ab9cd05d2f190db163deeba5247686d210 GIT binary patch literal 93029 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

}=M56gA(@4Oj^YnS9M`2r*eO=2?j!;? z-8nG9_Z3$J$%UHo&peHv)v4L7zl4=)U8gp@)a4>EkIOY~J5&J5uEDhT4!FHxelZpg z*A#vT;{kM;-!~)(?)@Mi^&y@~?)$h+t{B~8`GT!Lf&4w$Mt8KCr%)El(AJSwVB9mm zo@9yJa=s|$3)d*8@O-P9+f>Trd+GmT4Yj&b?De!kmb@k$Hpm0`9;w$qWPY zc7vmkJp2$_oHm0aEyOqU?Q3)$Bjq%U^8eB=h4@B1K9$}|5@X9wux{_%JB=jHNl>|E z4!tuz)*h1OGzgF9YP-~GaM*T6@b@;v)z)-_{hu^9DgQ0ZT(@`aT}Cq7B%Juy=s($P zx`zC>Q0>2m9RC*nbIeUPag-m7)@~K@(oVcB??p?kshGM)Fu+Ra9NaY%H~NayN&dQ! zWhG=2%wqTWmO3rYUn#0&IXk5+x5)jVv*(Jq3uh;L304p^y@h^c6CBct+>wH+jn_yc zy(e~WMxUOWB3MTfo1@alZ<+^xrgMRCp_5AIaYs$=LA+~!CDe)R57_DeSN%$K)(Lpg zL}e+tXwElHD$hpqarz(YK!;&YMZ_b*+H!p|g!uXgU(k4Gb`Pn-@5c8!|DGQFm3g%bHL4SRxyHl?NI zhLij54H6raoxx2Aj!<*CfRFEvBE#?=yG=Bb-dK)uDb@}0>He;q8U++IO$bee^Zj(2 zTKJUMCF zn#f%)Q5zh-DE653So2bHGBcWBUnX~o^7oQron}Du82Qtu$7794j(TR!llt_&;Le>G zZz?c)?M03ag$z&knp!Uk&ooauf6nhD&IV(g?Vip)iYJusKTk7&`VZM@1I#m)ZR;za zL}Q#8zM~Xd2~c@{Kjk|#byh4AmZZI|r`*8myjfS7xd>JGNXxIWxrz*)e^L?djYHK| zxKu{8{#t2oUukoflUwdJ52lB~3{G^n(kk>IWC2oXYnlqn-o@XaN)JpRr7QFvW2?psAZ3fk<`3co~r-uSP))qnlYJFyt4 zDvSQLP`rwp9d>6{WN%KtlN^REVDNexoSCn+EnRarkVN<48^R^7fSHtCfl(^5 zERw+6LLZjP{P-%qMP%xx_t1l>uthT-ft@II?bwhpAtYwItPnm3$(l8xTDe?ZStFH$ zA+tm_BqCR~qY1mTiL603W}%s?cY%FNaKdG*HCXT9F?#XyiQy(B2k2;XDzv z6^oaA{cZ3;yz;5BLibXMr3IxDEJ4<=w7{6BD-|oTF-kD2zW*>fLI#;%?xs2z=FW0%z4bJKn({iaX z>W8T$lm3moL`YAUj_!-ipWo6trU}M=xo>HUi}`v-3C%yI&N4a1XVMbBjO8Ie+AC*w zZ%UBH$pc8Tflgvw$*>&dy2rROomSHU-WCT|LU&%O1MKh4QTPVxbE^Y;tc+n)YL_;w zJ?r@%Xk#;J&hHHO9AhsHWz8gQ{P)ec9@O;lLGnXhiveJ_pu(2h-`-%KgK5s;*5{eA z#UQg4euDe7IwXXhNEdTq?=dlyLF)=k&+ScfXR?h6t{0(iIlbJLo88NL3Uu*rwU^cc zjLBL`Cy&oM`3UI>xbHm~{vc>{IuvwqsSwplI6rr=%AHp_ zd$vKdzs%RONZK{J%YpsTWYL(E8W|IJp1EUmH&_J)zASs(?ihUB*6kue;E0XIEfqsR zi>o`K;~WfLu1f?HX}hIn;xlA>IedS7F$i{n^J{Vm@#`o-8@`>ko0Sq4=E)z6K=Xjs znU)fcO8Q+TBzd84f+@urtY5#t#M7&*Z}sFd5JAX}5NYO~J@q`@D;H~j!0ga&uoDt) z$BeG7d#jrjK1(Z-;j)Q@edu(}`Lmz*X2mR5+^|^QTRZD4LEeayZshTaOP{eRJ+!iM z!T;3Ower5$^ri1wx-J!ap|F5{&LGaeu8@k~TCwXR-U*`#b_DIlM~-LgVk^M&^TYW$ zW)Xh@^E&c!J(1*kaa^#T(1|kM<|##C%m=ukns3G}rBLt7e8zn6r8)l?_Qtm+xfcX) zI|qxcmD0{BeBR=AO0U))SdkQW2RUHIeqkFM8o{LGGTTRgew@(jsIL)sfsLCwl?wGj zisahlABQid8Fo?}oxB_Rw^J321tl|K;2^EQb5JY)9B6i-0d~5u0#wp!f)H)8MQA=5 z;3wRLU=-5Q0E#|@)s8haE)T=Wp5^993cw`D#v=If2q zu=crv-OTfua{{;tSlaDjGhu&b6MULV=Gj6|RkbqVnG?c^X|>lH9S-G?IkcCh!wfG#uHWPB2X&`Di z>><`Hxx`%8bn3#`9qcs*cEu39xG<4$6UpG{BZZmOUcQP^jO!s!gf?70GdKFN6wh|Y zX=I{EKs|&U+JBVA9BwhE5b-131?aDnNGl>>Qox#B@t1q+lg^ zQMI>h8i!(dvv>ygb8o=HR{4p6&mJTT+`zmIE;YhkUk24j3%2|8PZ(Fa_niAcQ7F&J z?=&B1XW8g|-Pdqzmrusu4-v({R2b?pY$+Ok_!VQ@%y8Du?tSoU_X6Sgt88SsgE{*K zXn|VZ7iJlxLIWjEJ4@Wlq7QovDegFjh6LPGJ6l&_aF6T~FXo5rHPf-+mThYp9=E>U zWOe(g#ISLSlyg!p9@4NF!j((+6ggHda_A+X(85Zu^2K}D>SN56mf+RR*NbP0RT@~O z#o6b3CW=uay%+@D<*h?H2hl9u>7lNj?Q7qb?N?_B6e|O0U~zxVva(iG6)O2l{dxWS znyl;Lvo~`0=8w9-N2Qy!Vj}7?As&~>+m5;t$lNWPaA2^Ra^<>ldo4Cqo=)Z5|^b$DvLpL zjR9}he{Wv>JY&9Jj;C&Z!pl0j*;bccNm`Cg#0RssuUrkZCO~(XZBxs)<5NS{{uzF4Z z%4JpG7Y&w}|0BbM9a_lv64NW~TrXo^MiA;p1GD9X{G*{I4k*F_G9!@TV$QQ@x}9>C z&ZV)?B1(@hZaqEE`kZG&OYAJ>qqbYaf_&UeFvEW@9;VT2jS8g_b!cN{v@L@1phb}! zvZ-a))!32#wotiZSI^|x$IO#U?KWVv@IR-PBx=OW-iGXP{QHK+8zV%fd&K`DOI)Gz zcvzq+DaNs;Nt7+5_+g%keC!-G;nnMK3a`a*RAKDADN`e^3`5|s2~!!cluc$bkqIfR zLDTYy;der-tBcYuTOQlfAaCS9TsgJZa_WTVq-$TTI$L*)h?gZ;z-sRNCHgO1qEGSd zOr8BStZCNQo3adPRvHT}py;wXdzKU>)|7h7JCR9@A{rddJb?Da*qKLqOJs;2&Sk+> zq5q;9DL8K!mt*h{p9k20@29WHgHPRbWE#a4I0G=_oU7py1UI>8V=81;9+@V0e|?dl zLCmp7bh9vz1xm>j;)8T6pt059|@V*NBnxRX>8Qr;MUr50d3ZziITGds6Z=M~V=~lH9SQ?&#R<=ClVd`=ugi>h3a)dZbYas)#doNKTo-@B@r9oC(CKML-bs_+G(RC`2`%w|$|;e| zBKFLlehn%dM<}a1a!UTQNyJ1ci42hLe0>ZL`YX z{{QQgLDK1e|CaE73&CZ`t+%@kM(vtHtVAR1g8w7trAHZ=OF6;rvEph83u26iJ;vbz zw1eTIy}LF9JL5fQeSitv-+qYV9o@~&qL0w5Kn2rtwDxhc4!>`c1<%&%dydd^9UW zAn0RBB#J~SG~qHISO##nea-O+G9{>&VKIFtg}e3xbj~ylYvtNjN6gzsa;t;L0;g5w zutuG5Qz>C)S&LHb26~OL7%`WAKx7>orO+CuK%QKeX%Q_-R?~X1J(uaaW-Benp_`5fv1)@TYJ=?QMOEaiv*F#N(4GNnJbNSj#v^in;tyMTzYBZA z^+6eWe43^l za|v(UoPjmitmAUF$R@sl{Q$T6a>GwphE=ErsO{RM-5U%jH7l`=DkD+?PnrZPpyU4=7rzB?I307*l01nHDFV(=}1;^7ac4p;2r~1Dwny+E!<@( z9T$qKMB{2!LMe@mJxkJr4E%5O-!#pdTGNt!NSLD$bL8LkA~&?CGoQpsob8s;;g4uw zZVHJsDbCxve<|{FV)F-zi=;u03Mszna6;g?uFAU%4Y$TNyfxH8|kqIwsizn)4! z)qc}w(yAKUs`VePBm1uA;CxJh^tJW-@lu^CjrCzwTYt%jkmNyVtj?82!ZNX3HXKz# zDKQqgO9HdfK;jUcKOf5ZEN{BID!WgSTw!uLDNWxGYIN#)HB!I*Z@m;odi>bb+Vu71 zF*k6}4yi#>_+V0rO9u&h8wH~XnG$$skHjAooo;X~az4;%pWOa@D)`0)S3W@$`&`vm zM=5;lqBh&)`n(tVS+jb{0^0}_52?laBUfWey8o7m6DhUMixToS(YEa7F%An7L7g_>LNNyrbL=nI z`+plHFQ0CM^@3x|aW1+(Z(U!@yWa>ufnq1^11{TZ4{Q5>BP#aibOpFn*UIU?@rjKq zhkbjJoq~p&2JYpr76nTX0eN}pK})L-Z_nZ#ai7D{3A zLW*S%+_bgw!0tAg8e6g@ya1oC57IK8dAvA}Rc{(XGILq`bMxpwBH?Q0VeNynY4nt@ z{a?wEGv-+5!I?=*W)vgrQDSxMI2FxH1V@7Cr1Js`Ie;3RK26U}vR4t@D;DONUu9da` z+HwC|%Ak(SZ{P&{V=2`P5xPY+ss&Qq#v$qXY}iAIJz5B$DzFU}oZHcu-NHx7P>rgN z^V6&f3g^?Fq5)sn(jHWlK5hDJ|ZJICIpKb-~`up4jM*j0e7BKBfgp44}e&(i-} zFLk|i;jA^w*ruFtUe4xOgZFYuZXz#ajkGmmt@EI|+O4Y3ki<>YTdmJ9!0-lNea!G* zjK~Gt=Zdxf4)KpW^!dyJhm2-TX}|`v~yrRm)YYYn@Otd=C>`EP8hX~ zvD`H_z+jmOO}DJ>UWkJ)!#06%%TNF)Y=vcBc5ytw;6T`=u8s4z8 zEfmy$_-$=iugYz>iEQb1mXAV8iEK5|gBO@T4MOoMnNW$*xw|Jjgnme^ zrZJJ}jo4x}W7PdnkBL z-eIe$+A=?`Z=~m8$JL@Wpm*|``tpK*{_8;7XbuY`10)E|2t_E+IcK>)IiRnF5cU!s zEoNwJeiiNai__cgfPT4)=_qXFm47*UMlw}MY zz!umYT>vf_hA@V49I+P@bxve-qJd<+T(_zr*`H{CY2kHx>w!M*q$>b@+0%`}xIsr0 zGk!(bpynbko?N1#n_vTheet!$_rQx(zd|xXtwe4z3{~-yY4La^XYpqxPgpPC;yma~ z-h-)*iP5pn(M~C_m91NRoeu#hodEFvH7_FS_f=W;8_k@I25|JT@y>?~*7_C>TvpEw znjn(Ba~CfUro7U#A=KG_^4P`G+j(aiXjx^RpopmIo-j(6c(@H&YF+lcsmZzlHxUhMbu`Zd~nuNB_yjXBD^YzlPfXJzT~4*XTdT z^qD09{_Lz-ujQv7Ki_&uF+4O>bIt?YHDGz0+NZ2H))?}Ghwx0Lxf4}WcO-X4EZ4v5 zsYQE76pW1VVu0%o>&4n;$Gb_!pRgJVedt#x+01$KgjE~QX%?G~bP?^)yw*j}ohWpd z8Xn$6Ppu^$wcIowv+dU-wYir8Z^>4GiD-z+>NGQ*Zfx1f`B86Q zRdjYGnPvC<=3aCkjB6SuXPG8iraPRf8e%nToR`UwDbwC5w}`w9oo*YGqe0Xh%X*y{ z-}Iy5Rp*?uPSZ;?#Y|xwhEfLKK7`joE-+C8Ex-x4(<_~pc|rZ__OQ2V-yh{LM3>TY z)P#v}f9*n=0DopZ=j~mn(+G3q$BQ02z{?rpgFMSsS8vh_F;otDAKNX`&`v-Dx(OKn z6@<7Snt9u`t-Dr0Yqz`Er`a`cO2LYhG5w1)d|^5sCcH5(zbXQlvO7)ck#UE$!Z3uS z5a{~~HH=C@yBED$On$97#I0L5&2RB#W&KBVa^VugQoI^=y|RMk%qHc%w1J*NQq(+4u)=?C6lev@@8x{SjgIeSr(y5t3d4i8{=UE&h3mHD8I7&vX zE{fzP|14_QLxB5*a!qWt{3bxQD)~^n{Rt~)u>v;V#a_Ea5(`i=53=u*d$I=Up+=oJs_g^($+E_YYzhjS6LstQ|9!`a@q1lB|C|F+h$}HbADX|Ze-;X6c40l9IwcY6O z<}SSe(uK+Na*kiaSoLgxcq|C)eH|`%HxT6AjtKoc{;urNifLpyiWlG}a|7HoDorrG z<`m~NS7eZlHDY`GgAcl2j0`;r+Hla*2!np>tVG?1_`zHZNXX>ELne^~t-~LuN#HWWClwljAa1j$^#UgPbQfddaZ* z#)6y`FK4Q(gyet|zuX4i2QxmI!%b1DubyY z+ep!_1^F(goBymz)Y>iY?iCckF}zWHGW6j;GrvJmajjlzu*edSM$zp(EI@(_|KaFz z+zeUH;E*O8|K|T-rl%k2nJ2#yAXx9T!X|AdhsPAkv(_u?@x_lUT!h~z$o*jq_f3m* zslv6$jOo5g6yc^Qtr~9nQWbacX_nbGNLSGgKVhEcJmg^AfC;co+oLzjbI>N|tRNI)yWsGhBh~ zkZg+IG#mnE=S<%0O-A)n|X*Z`}7pb(GJv1F%Urmj{K60ZAd2t?x>b0t1 zt)*fKs}b{!g1v>Le7ZnWIdI~>(Q_x!^IKNpM7U<0A6-UHvy?M_!aUx1vT*&NVNhJT}g)C)+Nk*;HsjWVRsmmFIVqXl4y1ds~`Ew5WA}L zx>`K1X2`%J*5nisI}K<@KI2 z@i9XCM@HQn*1qT!BRG0Ls%c#}OCe7~pYj@08Ar7ivy_t}V}tOr+QWPyWm=i0iz{#8 zN4s4WO5x62Vu>zHlzP1;cYbXRVmU5 zj6seMdp%lM;}+a;6O!y5#y79zZ4}G;uIh!+W5fipXR-ZYI%pA6(Zl7DG%`LESrtifb*HL9LW zTcKzruLSK%t6tV7?iYhbOFO2AC|+vT4BuVkKTRk&tBadz1ev}XOFre8L1}Z z?MWd#9bv!ABf~_R4CXX?YjLr01F8;1x;LJWEV=n8VO?VfG@@dT7R_N?LjmLwsDB^g z7DImrV~{N264v$z)}Ei8%~WSWVj^dnB6}fU{q*50BYJhZtM0E`51cU&kk@4H*?kNR z)p(ul_R*n!T$+T-zQ~aS_#wrLx%XXt#&5nP*lr>_M2v)_jmd<>YM)>xYHB{+8qe%- zy;_VD?n0&{9}I+xT!HD^Uc&Y*h&QE!ITT)bd`(>uz@0tblAxP_DTMUub>i~J$|Pv6 zO1?6yZGigbt%g%ERrG0uOrQ zuzOlPa{`AxE+QU{ECDBLi9}omQ z3jH?_lzOCl{p_4qW$P--xW#@3sAi@gZ4T5LtkE{dy)ApXcotLlz8v#b&m5jN^L5X- zy4)wB-j$~8As(6w1^@8V9nsPM6{5Bnv}1+)s-^wI?l5tG>L!pN~w1V-6sra%n4Ku z9Xlu!&P0Fp0|B|K^`jBc{+6bK{#L90tt{1*8$}La`g%6Ld4&!UxP>~>8&Zi^irV2{ z3#Trdo;v>CPEQPT$dZtjH9qu>U1QE$Bu8rt4|rnQ7A|A-_2QU1QA_~7bOJv*Cgao` zWoD-7?o$9VqRkOt`*=HJ{4W(|fCpp7CKLW)Q+Vzrv`O6r^@$-tJd8S> z=m#B{4AU2FcZ+lD$$|p(w?Gvq0P}+c99S{jx=#WSuv8)%Iz9E_3^;ozN8bHKtsE|> zbp>5gtglIY{#oPiOn(2JiOS2w-@02r!PEbxqTB!Y^51&fUPvSeVu;Tq;Ki38W>8RE zOXk|Y|Htj*woFC!|FqpdVi)WB{kFmPV}*c#Z~Wi0R&V=%DC?}~xK%DhF=YunNfH|J z=)zwZ@nT96w!bpmZZ8ldhBd1RSKV2&=TAZlVbSs-e!V<9;e;6c=gVCZU0RrzdcXLv_ItTIUfi_X1iii9?2r1rIgFEg)2UjStX;8t zPH@fAw$Rd_uQiyIn#g)*qI(F8nj17IFE$6diym|-brm)%#GWC{!zbzg zaW36OaRxUN#A_ByK5p$8FEwx0Ux;*0tlBv4tK5S|kIpS&CJ)RcvaXUJK<&wow^^@~ zOV_d9As*R}E}%Y!&ZO6mkoQ|Go%g$XgUR0F7+K0Ux8v1b>^-YvvaHEpFXRFfy3bIu zhjxWgI1$`=I2HfymJd=BM{@`r5iQH;5+0q z^}q4<-cd~j-QF;WiUkE!dIuGdCLp~8ML?v9NRy`Yj`U7cq$phkL?8hXkRnL$L^{$! z4?Xk{N&*B3A?3V0_ul6|>wbSc-(BB&*Ozrx&Pis@oS8i{d-mS*J9`J%;DM(~+d(L= zL8qt3+lS^>pBJmo96HpYX~cI*g)^7Eyus?}Bv>ja9I2|-OEiQ28Ag6R5ELU(t&RT$ zgj!8gK=9+!tg6bExWn!_7S!n?`r_foxyw!6O{@Wwo-Z)?_L@O-#|)x*>~&+}y^L&ii!# zIG)*A>|~LYRbft+D#vfx>|i`~;rrEkZE;synZTW-PA4a)@h#HQ)aA|Vy&q8Xe-6bS zS#RUqY@9%Y2e}oRpzQqWQeGf!0TFONENnV)gku`4(p&Td_N)gp=Y;azsb7`ob>^8) z+1I|->qnlCU_peY9H34;t&76n)nQ+5oX|G~KZa~);NUy;IGN2@G=MVu3oZam1NIIJ ziVV0ogm?Lgm3H@sIR^sFmr|#@uhIdimj~Feh^0oGI}-2m)_h()IS1vKo9|&c30Y$={d;oGCNhWA z%W^`pG@`6t4O1=`25iILC3W|sH`jOj-l^4GuxnwtY=XF~HzMg7ER7jd!Pe`G`>tjUnQcVp20XhcwPI(ak?vF}nK$e0Z&K zL6UT93b)q2ujPaQc0)zRl4$BbgAv^B#6V?gvP!ttLhH_~X3x)L>r-K#qnvkjJ0aR! zZ`QP6X{4(at2#SWWmDP1p+liFe~}J-*sH{ET4~@ENhx&i+^-6_cSTXAi@%2>OGc_H zOdK{zSqLvHAKE_1E{}k`-uY*b{r+a*Ujxe$*^@gl3h*xL#O`cN{W=aFjzV4GY6UI6 zPXQ-|v)1LUXS16TP=UMMbt$s}gCOMkZ)EWv{_!60E{X7RE_F}LTOu8Vtln3FZ7DMj z3`W$&XT=DRkuw{=fSHYE)w=N+O*bHZj^CSd67rkc=NHUy)9bQAPnS=j=t(T*SCQwC zxrGwvZjsl9BDFABjlMzb6{1LNS}u+x6gtdNiLA&n8rv;eIBiy|=%`&s9zk{1unIhz z7}8WO>~BLVSe0H8>e()hWfrDfww*Ue%pEMbgHYadH(>6C^ZSXsV5A*mz1kO zs2j?j-;+iwJo$hQRTVsHLHVFXwim2KSnVx6^d;dpDf9omT!k(L4a8?!c7qo`EfBZf za0Q|2K=%=GEi3|n3u9 zBY2>N1LHIK*3|B~F7(}=C>eHzit1t(h-oi8qTW*J)*L8+;+dGS+kA}rEbB1nQ-B;L z!*Is7MiBvoB1*n>Bi@+KK+Zr29i}8gFoL7C4oMHfK+}%9JT<@wzV05V*C#OpZyEuY zf_ruAQ_^Rk{b$_3h@t=>BJ2}im!iI+4lq+O^M8>Zx1Pkw9`AVkF#{}sg{PD#a&pA5 zulVYXS(@PNQ6SkP^1)1uuG%|`k3Z7?IfK4?8cNm1pgat3w|Ny!u2@uS*r>iQh4|8S9`7wyzcQKuwc zwp=FalUL08b+$5mg$2>pI)Ia9#0p9(JWF;re4-jwzl;@boK7`Z#EiN2vQ1xZ)Lx5PD+f3b3#MJ0(KRNBII5mA6KOpx@elgO->>{HE*nV ziT~BVRr~$bo%;A|yJJ7x6_TP_ZlKb3A$6IYlNE3Q1GsUB$Y#4S08+w`ZxVbHQSIH_7ZT%&j8csEo&CH;W`OiE8$Ls^Yapr$vVCDaTDV z>k0z-RXE+U14a6^$5vIc^R8u%IhhAex?i`?hvZ09h)lOygeGPpxte_T7Ss>*%FPkp zg%Qh}7R{WYNx^X~`Q-;g!R(jqF7$;wJTv4RHXo)eDve_Tu^&n+-}WkGl-&&Z>l!S3Xr39Ni?ON9{*){$`Wws7LH685*8!}&v^ zmgWSp29gFbID(y%9i@~X+Z-952Ba0HB6g949IF=xWozfH*jud&2FjgwTS8UexS~o! zws&~e-H`r`HD00I-{^xclu#;y9gY%-&@%^H_|Q$UG4jd&D!w%+y6LCH)yImh+-T{? z6v*9!*p@~Xhf?b&EQq7vty~uY2?H&z!w^yeyaeb){zFTMz+6!z`sz819th}tflHf1 z1yt}-#AZ4}$lRr@UgSt-;YVz|g{7~G273-pz9w8l^@kQqUorXx<|Oy(Y*jxO;ssC1 z8AN+O96>o9WGW+pO&q5HG&rZ_-5_Pl>!*=P;0|YqN%*MvN~FYy>d%OG$|gPmO=_zB zd>}lNaSoY~3?D$U0a!_vhHlEH@~OZPX03pdMh@p%`BOKoLeaF~J!@h_blIWdY}_MU zo_?nIUcI=;Zc3xI1K557)k@O!60xxC7CSEDa%;w}U9&$n1;=2APKCl3$VpqW81Du0 z?KXjGP{@dEI{MLg@sJBkANIHxbf8OGXB(0$=UbKDF|aWVAAwT)A7Qt=*pQsxxHHif zrz(&FdOx>P2g@fIEl&< zit_l31@O>;SG!8X2N8KO+=KA$IY@`ydRKuN+}`_LsI4(&YlaCr<^46Oqja!-ntk(Ra0v~(^fNd^#3GNqbV>6tqC5D)lTu6FjEMiJ@UX#HOn$sG{hw!NBq_s1zqHr_4kZ>{Cj)^?%RJ zD_w1|gzWeJyl!8OMN4#U`p$7tP~6igGH*S(%{R(L@fT6HW`Ut};0r4}Ww5}hy9=jB znM5|}azn(IcPIK~dxj1H2%gJHR` z2>MVPjFXSv64)h&1L>hBy0YTmP^rkN{$QLrdBhbgtpefY0MaZDWo>qejkAEXl`+|70JDST zJS+j+7OjAa4X_n}sQS;c7hQq3iDIhJ%TT{C^odA@iwvc7-V=BWSuP!hIyJo^XG`$EK}Y3NK!}7});vOWP5B!fP)Qh03D<`Xtv9p28;&ACsM$!W z>;CJe!N2Uw`pe2I#_U5l|2cW?ZO&VQOy&P*<$oSP@bFN>SI(I?6S)T`72lJ`;E$P{ zN{{W@oRs6P4jxO1R5iTC%uJF2!EMu8rb^jXh_~l7G!#PXv+0lG4{T5#d*OCCES|}I zXqoHtBdyJIox@;>h@Dh5-bwm;Gr}%L-~}juU!;5+5`2seZ80Qth$e|z=Xh&!NtksL z1LT{;r;43!E+eGFcl#Pv&KW!-fOnGUB0TS`Ivdk2YCX7KggOzjAx8OnhM9)HaJn00 zZMS`xDyq3lj;iY=F#YZv{nc>&p!Bd-WcAZk<@SC+KqOpZ)i_Z8(eQI;8+bo?GMRIznFB$@D!Gav;vTo zASb0GQIscgtd(ZgD^R}4s=Rs(kw&S2BW=NWpbxVOI_)(uLAR(78eY!W;D&nuE@Hj_ zEWm*TTHXw39M-on?E!MC6PZemU_OiHx^K5( z1SnH)zqAcG+aR8!wWLm5zQt~^^QIuN;({%6o9FEa=DqN;vlkk@Hbc^+S@%c3Uh~jd zy}DVt8erTrOtG?VcB#>CqF-EZFy(r9+$>Oo4@%trQi?v8vf+yC_v<>^;k@d7HKAIO zHXjDpP#L)va%U@oLK2r_7Gi%EGgP&`W#qvb_>4fNYEa{2PWzWVL z1q5aw3r|!mVDJdQs&bHuG<3 z*jJd=-cv4v2KD}R8dEDmI#W=hqv_GHlWyx{Bsowuu_}tl zf@c_$RVHePKQv#trk7XwZYc@2C9P8$d{e!+fmMQq@E|>()2CP1MWvQb$b*&Izt~+wT0@{XQNyfbq$BiclfrmCfs^*4ro zEJ&_$YNtN`9>zF40rTs5`5hdaNNWR3z5Hew2{vbgx?DdCjnK9v8y+JfD&4@Oi^0Wk z0h@Zj)r&Ubaj@|^{hxB{l2gbl86Y3>CccSwotfy?h>7v41u6dEoE#`8|5fk3UfYNX zocT)&E+;_;wpBsnIsx!?#fNniT)2i}ec!N6sVApM=qSY>=v3LMz`?fWeR8X_@ZLkI zZG+WTs;a<%{b)F=Nb3m+zRn(|+Y)uOvUGI+7-*IxQT`#KVxrv1^`9Kx)*qG=5}g#I z$`MWbb)Rw|uS^kmQRxALRZwYb3bj>6+XYJn;wp%cJVV=#B<;L7`_2Md32P~lynaI!uvzH5ebX{XqqY4kzdftS)JGu z@GpzIa1|2R5lFNMiw5xYM|j`gA(&?dFdVpe^B-JD(*zOSbBfl(ZPj<1DCK=WAsEh$X}xXe}y3}{O*47P6f=a zqfOv=2CZ5z3U~F|`*jc)dNdh*%FPfn#j#Koqf0zbP$~5=-P}6plk&Iuahy;F$WK?K zucM)^UJy|8EJu|acyZwI=?M~9nRg8KXo)7118FBz4?C~#2$biB7takaBaZ4d6w7uD z=JE`blz}q9H-{9Qk%h^d2+yLP&1!S|7#?vNkExsttJV7)jTMwRxnlx53bRlwFx(< zz&IcXUp7)&=aP<>$a{XeOW8)UQ14gj!0w{d{$ z@ZVBVAIV3S{JW+DgNs({xUzl<$v~-1N>HtBXH_l2Pm5!iq}WtSDnW&=FZjRfr)&^+ynXuE+pgcY16!G?s06b9rQ&7}{;&1ef0OY{#kHa}`_el_VW`Y- z{`66DcSAGZ@MgYg%3`3)z*t2TfF&?$@Dzf35qv&fO%~Uwm8jZW=fxYOiT^puGicI0+8p{sykYEn@8=% zNU2=-%nN-^Q3L`^`1`w0*B=<)n)!0(?ntDc)*jCGvjg-?g30WyrO6i_peEqO`kjD# zWwLF6L9Xgj-lafBF%A2BW!r%OkEGG#mhaBjMnzrxSemM_+>hulPQo&8GNoZ`o_N0O zERf-!1-(kIgB6Y3B!7t56ghMV-n{E)2i}%A)Z)pbAAkzudRDi@4jo-a>T1Lry30S&P{8=dQ_;HmmL68t@x1wwvtCh@f9@I`xc@H}0ac(B?EyLR< ze4qCX;FRH)J@ZzNN+-!a7uJ1AQWw8zuH#Jqm#XC;$ZhxP7( z*gprn5rGVf_Zu(YO%KiKc#Mk}rrwueS=%c|C$JW=((OLDHQuAwS*Bfm|Mk*&zoXqN zGlEi2&R<^byrrn-{Vbdha>q-1WTshK6VI8Dnd->zVtYgLh&9R#Z+0Sk=-@DGzB#yM ztg%hCKclhPVSnJU6qktMl6)?CLwhy#zTFsI{FcHM??vxDx6NJ%`ktRd@OpUU<=?Zn zNJa`i3x|wh49+~UKW4P$!Q0B8Vu&kF1IJ>lvd*k6?7@65E;(S?J5?rw<&@;Q&iA|; zJ#3^Wp0h>u}2qu_kHXnQ*gqOX>E6}p1ys3+6(9|;D%z^TQ1(WRH0_1lYbOW~yU zp=&^{ubESWiLY_L2S3*n*>tcq7CkpGe3k2w($YwQIy+&6IYO7h$PEqQ)xBi5o69#J zcuTRPIPvC1dbapExK~EzhQPiwI!oIb2njWzME`IT71Argm-|~udUnXqi?x)d&UdzP zUnDjimw|GR2X34Qs{Ay+k zj->6{rP`{oI}2!BVZQ(5RH3_$zqrJ?$@RYJmLutKaH|#9i956;_00`1S_qpI z`DtT|7Xr)I<7+vl*t0R~78%Q=Z_-V3ei9i0Z^v@IOSZGhpQz-xCJ)|{1?t!qy@Ewh z(oEf;0=@8h(oKar>`ZxJ#0#8E;G*UEiR-V{jo4XU;At<>EQtl>>!`6<{%x{-)}(w$ zhyA$Mzr1b&*ty${7>(;^=ya$nhZ%; zP$kb|MW~3!rGf&zT}a&0d+kWUyk{iVZx|d3F6ng4`K0uzdDDhFP0&IHB3aHVr~HQYuA)RWT!I>yEdyz z6=h~n$wLcOA2JC_l=18gbMARlDvj`a1>9whLFLVUBS3Sf)yf zx}w@P*Y0+0@AtI0r|ITa?xJRGROQ&Fsy6uJZN%MW=r+|a?I07}RTw?7O?-nrziUC)OK4uOWN$0x>$tOUw))j{`6bz?CcX=&?b!F%I@S-KCC5dM zIm+&w4sVW^CXI2I>3P1?!XleNmRrqVJ2u>yDTJ3)X!`~|dCNubhknl<92a7YH&zXY z1#aKD+m16bIGH*!_Iag?)WP~JTrIhAz6hUlKew04-m&#%OpE_Eb4bt82NW%2uL1*y z{E7{!(4%$cGBeS;n1Qz(DYcWZcW@G3r$`eG=(d&UxOswqJ8hcZ9Pp5$7~>vGXezy%wNq6yMx{N#Q&xn)519 z`z#YWXI{^U<$}9wZY;e!)tI$lCz#(p=2-wr0OfFQaT%+7VK5t)+~bjEh}^JskTHPg zK_>k)$e(6K=!RzeB3WE2J2UOpA9$?;;3)97xeGAHcphd-ZV#9RDjpB^owiT_451o) zM`k7LKzXed8<75!h7|S%zOCv?8vo{eSTqV2rU>(p!ZOP0=&GFtO*RC#O_=%TG^7%p zfx_Z}69k*OiboYrx8u(`~dDVIlwe6%btHG+aue1Ye%?XUWp#_crT`X zr{&7}5S>p}Hn>f&wsnLXY6@`bq>Ba{1Jzyvnl~Z~9~sFOtvk)d|7C%~$F0xbD`I|h zwIO2bQ>P%k$aw3U<8v>kTrY>VLnaFzcJRi1rG3uDXYM@ww5qmb;R;HkRZ-ga@X)Y9 z&bt@YfhtoeP=0P*oM_GtkXi(HY42cGJ69_0(Kjq%-c~N~u+u}~0PJFdw?NNBN&#o$ zdx22Iv(UR_6@#$F9Z>{o5gD`GE32s z8ewm9qcNq6=ZRf|rlt_(@oDo(zQ4UYHR-k#E)7>GRkzB%un~gY9ivX&gTsBs2pUy( z!>69i<)DW02|EwB`=NzZIji; zn%Y5HIQOf*-+X0U_q*z}(Kql9#UIN19X~5Zmy{Rew1nNrYs)Lcq~Y1r+N>X<6guxL z%LlL9qkT%xzm&LkeA&?_Ym=^Zln8oq{pEmPIVzr@gUaY{u4@^k?CMwMOmp9GrD@>) za`9sc~kRHvGgAPgnHGy z*=_CQZ1E$g?zBrr&P>lfJL5|ZPwqd^n*{*h1NdI?M(4o`aTLgP;ESxGUB3(Ekg#{7 zB|dH7HVUli&l#n>k276`{`Q|UIWfkI$WbD<&c9yoPt(M~f+)5A;iT3@ zH80ZQ^niulmuKbSFpYAb7|n__o_nz~wa6e|A4kNt#dPW09!v5P>WJ@R)5L+4 zUdv_?ONKx8XMwA1MwM?jg+I`@mUnppN#p`W7pE54&Yrk@Rec>&%nLlRi%xoYuw|d` z_jVSL6zKg^`AvYM_qjs43}hoT_LF)>pmT&e(GIqSOEYvZ-fMYF_*l|MfvWjOyJHsp zTlTScqMTES&OTe}pJWHj0Tq0}Ka?tTZ6N zb1s_W#I2#7ozsoDN3&@U8d@;l59zfPt=Ko&s(|E$`>&s$gD|Vg2w53tEtMpcGOXM> zhefo^L^QAX=TP8bSEoKdJWap#@LDA6k83~Nb+xd2)ukn}q9MIS<@D#~F4z(jk8s@4 zeU^J=RO3auPAD6H1raw-%HNA>M}LaH-7JJ>Cl+03@_qR3_(N}0Pi1gxc{m$1^fMX0 zeN)1nf_2?i(6#*iCfJRUqtKs9v7zy4ofaHWT4i<@KD>;u_4iIl!w6k!OSL zoLq=8;BlO8auX@k&%9#J!KT!yNxp;@suiG2l9m^zxyBvOcD0fw#s)(D^)*=pJT*B`n+|t1uixFjo$>P{nKx1v}&ZduwNCIyzSPKxYiSWYFF#`OEscNr!B`&zfoi zp4#R9IesS}LJ=zExKl7tEFNYO>xQUU&g$}dbG%}{2KtL|zAD9IZ+p_Wycaf?N?`H} z>U@`Dy6ns8vHbCb5HMUnn5d0yfT0uLkuR_@L!qBz;!MT)C50IgQswBoc-*6G zZBQgbJ;QpVja`s56Hub47H&Cr>XGsQbeRxL5E2#b!m+?74~};T(z1?9q3fbe0b8Hy z9$7^8E*@UTKU|vaViE@bSkk84?5T9Mazd+y_H2>rtmb$L&)-XYkXSlN7bpx@x@L~A znpW!WgpflSn7E>lfh223mG#%CwwzcjYmyBZFcuRJi5_bR#BzWB{CR0Fou=(p+lN3M zFZYGYv|q%}@$Bn&D7$}C1y4M%UVE;~-3L#1<33K6>(x=3v4eZ%UDD|dn1CKvKu0{A(p6V5H!Zl6Ow)o63q^G{4jQvbcE9Xy=ot{YMtPWwBvSg?%5;Hpk{CRU4MuYJ_8n5&De{AjqE zyvFxo`or&o?<%=#29axpj<^5kQGb6#qdjTqon%NS@Tk++xR%rwo({Ewn zIezcQZvQ+j6xVh_;~C}{YRVVcX+p2r6YpPR%P*~?&Zw~J8KG7mEp+7Q^H_K2{{w&D zZTiV+scn0x!QgKh$<3`OzQ&X-&C?va7p^;0`oIbG!3){yaUZaU-ky72T{85gWt%^; z&BsCt0LwfxH-hShN*3vV?cW)VK7isy>3v~21IP8(m@k?JEtZkCFJde_(SbTCrmv>I zF=O!vQcX1axyET`{XqqfGq+B*Sa$u05XI#mS{IW$^i$?B#)2S6(+5%W?YJy(_qXc& zGm0-{jTfDDyHLl30x0HIvd>64h&eQPuSU0%oaXz~%A7>!5U!8FVkVRU>Is|r-yZE@ zG#swL%;}s-P03XMcPwZ1K?Rq+fqkG^?V)B!K+~||ACIFbfOwicx2y3`+kQ6ei!1kj#N|0t$Q1EH=OtDwyK*5 zkbV`T)%Toy*_=?{(GO!Xq5S%NW|B(-i=4@|uh)@RL;w@Bvm{PZ#(KSh>6bK3Z=QFB zXZv4Y<*)tKa`ACa?bcPjq89?N)V~{_HTC8wE*BC`BGJ($L5JCHY;tlL7#t~BIZQDN z;?aNBg|+WaB2RElEv6~9<|iTF?<@jc{?b)Gx5M0s{JHv6=Ulfh?{fh!DVxuV zBVTeGA3-sfjw;af6(QD_!ju^Jura*352?bo2=`%%C90NwVnvCvTznOLPTnW5<)mc# zZh7RDx2E9|=_c+4X0^&CZ8~nPYf8xwT*Ws0VA-Y@x?soU$x?pP#?pF4;m6BME!MdA zhyKE;0u6@u&n?ou4Gi~svi*e7{La`dRUX}b5T53-lM#)@Mca+2ek?^CifPX7as1Et(acI9@4~Rg$i$QSB7*r43ZTgTLA;lk_0rLa?5ziKBL!)uD_v7wYQIg01|8M20dfD1TcY7&|Enzpt{U zvd2JwbkB4$`AGC?UV^%L_7^P_eU3Nj#JiSU+jRaQuQFia<%U5#KI6s4paEfSxQrXk zA*cd#ATmEuO}W2_)-B2-rRq*VpM2BVyKfU3oC0bcf_e1}PvkTNS2zRvqc!=lZcI+z zN6;-A0_?Nw=SQR3?@|^tZJkkhn;hdTokV)A+2RMaEljs=@j6&txg~jp4z2v*wHe~| z=2RZX?bPIEJ{C4JpL@$OCvI`d$(%68EG`M&DA+U*C-xvj5G58!ioRLxYn znetKH=E}P4GM7EQ8rmD9rCVHs+MDy_qVe2`mE%49WiG5DZZ~s8BxlGx=MQSt>|Q+5 zO=Iobu}=;YYClcbo;(A_?%U~dDlqB?=*O1dNcefr@af@ibbFYEMw7-$zwwZ23TgYK z_Bhg4%;~%_eFuww+n=`(n!P17O)ctY8)l(3gJ81U##M!YM+c8 zEezmzOwCE-#7RCAz_Xbjdm0fBtU!_Z23}Zs)g)N>>9DL2lh!M6c{4FT}#UFUHClLAs zCtz(r#sNu;z8>c#pn98|Tb8@O+2*HsGBA7rDK@gS<$7xDcYyA~T>MnV_)${5$5~lD z&mu_fWR4)>@CpUHSN~^_O*$pD0#lz5=6?;Vn0*xJm+?iC z;XSW4_|`>`g}Os0$sqcuC-U5p>{0|KT$f8JEvG%vm7zOKt@xs=fWD3DwR*(-M9<|e zHo_F0Crf-Bi-hauKua<1>&^raBYSPK84yfv)60_5D^=ZCTYF`&#Z9**cWKwP-VT*Q zAP3w%zF=oa@&#v((f?=&(;oNYCMXyBtWrz})2TgcmEx81&|vFb0?CPBrWh#v^71bR zg5m8mk3R*+N|UW=r6wKCpX$H0Cbs>(_%JYq~aoQo29n7 zIS`|{|69^Qpo2YHlK3HygsbTTStAmyQbNRy1-K^FcH0YcvUtg6{XLModt}!Xk`WP$ zmjl^b4vh#!Djnx=kyF8M)t10q<6p$JTXr+Dz}w?QJcy zD!G?UNfjuqeY-sL#aHn_m@K#KWmX@-b>+&WhmN^|`6mB%$1JBTY_bFF7;yKr_s}UY z{y8Mp>H5n+{Da;8%?))W_~^~eInt!$g<587w+E4v~2?%JE2 zf_*qP<=UwhLupnw4Pk&=$yGEgi`aozu?dic?g-60iO^4&jIrDM<|$N9-Y`8Jh9*%a zVpHA0+D54i>$jr*B%R|6Ot+i)?9exWupHp$$kS4xud}vzPKLLoCh&zv=T_?3ou9b+ z_6Ow0rK3B0!9%nMPkkQ0NK}*1khrb;>QgZ;dBaLo%+p$JmH{b!g$bQJ3 z+Em-M>6DjH!EkM*tmrc|^YTQa(M#yr3nT5P=c@$M905V%ai+A>-gdN2rSF`+jJstu zBj(xyU#Y0wD%&?;cM-;ca~$kU7FQnuJ?EV375?qgdDle0yYst7+>-gYkw?P(r9mNj zik$mQZaZiOc)9|u5UdHVzt(J^hD?qSMnk{AA|z^;Sc%E6hQq|548_U0s0T^S1Bd-!m5` zp1{x&)(4B~kKZ+(6v@hw^55)C*Q*Oi_})PM4*8)M)A%b6lm4A3y&?WJjtIwYzi3ms zzN6;;N))%CzAJz)9z<9cgX_WL+&GF1ec)Bh)-BXuH^EE;9^%vEexi!=j)v;@4iJ5B z%7Y<=N7{M3Fe>VVZq;^^_iWnIei7J5#}^JaXK5e-kp2Ui_fXW?E_uz(6!LQ(-CgRjbqAN{;CASCiqq z6QGeMs+kDK+k-MFSVZCU;*o|a zwtQ0@Ju>w`d8j?TcOn~8o?C+1)AL&~=kb?tp&x#DH-Nm<_ z+4E*cT&!IySSvebDX-;bB5^S#6=gK#NCC3b11lzZ2wB9I6wENQZs?0KXDHQ{`BnaX}5^LlYgCd7(@j-R6u(I!myW6Z|SOZ$8sTyA>njoi}j4n_!qL zb=Au=s02y8k`ANLcQ>f>oc$TRgO`Yr;%Y=~-cg1A2eyLK*&4`?eHwCL5X+2I_$5@u zWa_6{Ho{}he}itxiV6A=3*`MCz6Lz)GrcuAXC}~Ss=*^!?)!UW zx||ugm^6s8C#N}*C!jb!58l;BKP1nhLkIs0rt@9)-=J^)*GXqEoo^pKw(m1su=*b_ zf5(DkPwbT4pUhO+pM0aG^GjZ-98l-4MT!)BdHz)RA75ky9*?JNZ@WE>LPl0`XXp}d zplytdp$ndJ>o}Y*aC&;`8!_Nt#BDq!X50o24px{x10`4dIs}MBqQq1GvJg-{;6j9- zg;Y~jRgIXX@}>63FBgdqX!nnrOgUj1ZZ-erKPX>ab6V}M&o&17Vfsw9U-GH%k58s9 zCIE_*LbmbO+0)>Lp zsY(pRd9q1?x@-VDO5qUQrzgL6%y#egzUY63mnOeh6sR~v{^W83%J-h~@Jtchuk&&{ ztdRw=DxaoKKv6?qz0fFv=iA-N*T>sCS%5- zNfP@W$F&fBu*Bx7c4F-e-VtXw6-f#S?b}x#w;x2yqU|$12}+w4v;9Cpm0=rAx(6{d z6fd)ToBW4(-et5fXyX>(q}+4+fPcuk?T*S8sp3C+3SyKoiyHF7I!F+vzHIYm{C*wI zG1TUEil4BlJr1q~l_^Yo`|Cawk}w8@dkDaAT1b+P8(&%0AVRBlO%!$cv)GhxG>5@Pq!}qpCOY)ga%T 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⪼#nvOC5<>P_s8NrOsj z;xg>1$`1@TPrW%IpGt5dQi@%-9uw{^S&3fOef~%bc*&iR$SpOw6yDGaD^o-r*xKEIyT-h6GiJoK zJ{uwKQyQhCQo(7@bou+1pz=zGFG zXLk#Dpf4Zjx;N>#lRZKY$&m3;hUk=5{2;=ZK|xxwKZkt3qEe^7!l@P)io?}R><}Hl zoHT~G2>M+5KBjB|#Q0K<+a$(eSAMjoRenjXbSKaT#M4V^E?PwepS$YojCxr`du3F0 zP0-y(ByXI0rc%^9SMPi(DdOGpWSg%yjaJigf-QFvjgt!4EtEg#>gbm6V>rVrspsl7 z$KNy;K78tOc=e5&`d49Oh|x)OgCEqtuv8L1LJB5-$9ux6kOmKj`HPe(@MwiS57!nk zR4(1_AB z&+>AIOUPBJnPa`{<$T|QF8mX&Tp)5H;8M*XmJePyt{3$ftuAJUzW-`Q8fswO*q0KH zY>7;D;)|-JjPAp<2g2j|cITwAq6+^nQi?Nv4dfXmakZHeYY-wRCrhX=xY2`@rnp01 zZF0qz;}XQrXexr(>#1AW{LJrqO0r5U@Oi;2+Ang`4EMKu3B;kAATs$rDey6}r@ed! zE{w?DrO`UiGR+MvBEP=t=4q$)uiOtMM}oC|7G}2YXNdsz6W4cGD+bxz5P^bhi2w;X8R zn2WS5l|m0snSIsHWVx-Hb{?vIhO<=`cOO0_#*Cm!-=^tz06ba?!6Toc*iWgHYU+1wMGD$aP9 zY)n|(so}SF-)=d&`P5!!6rM8ZpJ1Zn?VFE0Z)WuQa?8?$9eI*&+$c{=At&`5amB#% zEq}Wlz0@IP7gBT)fbj{UidGQtr&Ks+`d7@p|$r531M?`+sm&#+UF?#KBfLfO41+QTDYo zv0NH$KFPjU{ymNtmiXsIE5R#rSz%uHy}IOHc-l)+NP!o{BgsoZL`3nDzI@|LW$u)?4fJ`QK*+84r3H`0`ZLiU=eS znb%U)eo{T9co?GiM%2EPC$uhEjP##-sX$y}=UQupGZkN@E{til=uGXZFWw;SECy6m zW4tHe@>u$WkJ~+V36Y`io<_`S+-apyuaVequ>LB-k`K$VemS^c6BgQoB*A8HP zsN?RuVb)eHDMfl|>-;-9DS>abw^i{1?)uF1~KEIx?df((#IRzca#Ox2&@{ zmfn$M3w%>df)TxQoDgdejeBUt547ldWwpU4 zU&mi%Rh{bz=WnVxOa&ga1h-jq31<46Xe|yAy*z&2&vXYra2%vDQL1X^_^xf=(hI(7 z1m|Rsw-5bFR1pifO1+b!M-}!q#)vXLWlYC;scd`4Nk3DsR@Y&h!<331`16QGWjj{p z%xQC)oJi-;nUT-uMR<2TB7k`3T#DGdSpUyyA4imomE8Qp>4WAoqi5hiv}IX^fNs-Y z*X7*umUtrOVG?mTKVLxVrXbUA(jW6W@Uyx5ohZ48NNu#E0anRQsBPlG3!v zFUwt$%M_Ln9Wp0;1stC`BVY{)z>-px<=R=YTde)NZF>0uk3Q26+Z476*x9!^<~3Yg zKk}k};;c7X|Dq{qIRS;6Jq=W<-M{_r(ZmxY1!Lrv#COW)> zBcN2756a_c!m?ye4rid6&qFE9MipEZFJB_us`AvQEEz;RDTv75ArW6Kb(5eParHXf zIXT?YzoD0s%Y4^?4K>TT@g7Zu42amUrwfy5ZN-?bx969y_!++~L-uo~I0cHeCD~*z zy5_FL-n-4H)|@2UJmV@;{oIabmw#!9Y52D%SD4B)gF5&CX3mcE9MOwUc}-QAL2-30 zz#;fnnfvi!+VC$=Tqi@5w$5>q6FsNv)H2jSxvvgmGT|COJ!*1FibN7Yk#A#{iG~bf z6>JHE!TQV;!bm^oxFo(M`F-lWJswmkm)lm*H!W9-Vr|iPwf1Q;#+Ocjc4XLJ{4nay zM8puKV+&!u2VI#x4B=V-B*?VV%D9|*S8^_LX5%c%mV*oBQDzzvSJr4pVKv1W6#zb_ z3bu;?7|B#FdBm8~Cg`574E<$*E2><%0S{p=!46^c`M5STcx6nc*T5CT#(5W(Ue`xU zry6@JD?eWT+T@?`?@@z}TwM&>_`j!zku+^`xR%kDheS2Lkzm^>Rpm}qs;|9P-W)wa zN>#AGJSj=E=s$%qB6|v+7WmC6^W8O3^pHX4-I;D^S2hyKagn=9Bg>iX1>gHuiqK!z zM}wV$l+)_P_0y^!u`V}d$+UNpH9-Re>R-@^o@0Vk9Ue-|#HSU*NE7t&XvGiP)COY# za8bW^!HsT);e}kp74N)gsBwRE?A0Nj9MPafk17)0E>eyXnr?#FVx17gCPIe0 z{OYmRvh6f`eSy68GHq4wC5mj<&Bi?oiyXwKJ?L$bW_&ee>5AG6WL}XN{aoP%u;9xL z(`T>^6i?PtQMx6N%Z#jteyWx};lBhx2XIiAw&|b(ohSnKZQ1?^>y*p?29)kMp?Z|} z%ndq=)N*oTQr$rsxkq|%#w47-u%_sihXgACr;{S;5-WB@(v%!mq{DFm1HJVw(%%>O&Bw^9W^py z*6rg}3(WVB?Gk=Jlzie4uH>IY-)Q*yZ+9*NC{gO6mqEJp_Jbm>C+Ru{Ika*i#?Lj0 zVbiAzPHOVQBSPV$InVo~3}9+F&UT}3{ump9=J7Cj_vd5<}2#GUP3t76*JfHdQL z(5GVZFVRFl4fTJm4!habasK+o_B&CTt$P4s0rG_N%M}chj58#_Cg{9JP63WcIz%$7%ggA z=tBdT%eXWtws4lxe?NAVs+1XIL(*K9r-(?xdS5Ybs;2{hHxY9WCBb0W`%iAtWf9b- z8<`1a>=s%CM3WwuHN#Q9?RB*T z;H52gd&DsP_I9Vp#%9)$f6Y{uwKBzMu z1V%X~>q$x|#Q@d?d3<&)%0`-o$?KepLSrW~Ebe~_yV zqs*_s%EL|8*&|4Ys0Vh9rGf^8s5xAb=EJdm9iu%iGB_)s;w#Zo2mE#JixYi2osLjR z-oDImqi>SE5SKAYNy;}7OtrZ_fh+#p^`PGcmQKWICm8Ry@rXNBUu|iONNyu?bymUf5f^DaP|@$Z5h=KUj-bCq;lo6-IzB-s+7$yvSlqc0{j zM*Kb%R2pje1))u^P1cTCw&4{rnO?22Db!7;o{%uglSk~Yola;(CW%!Us`Nv~mQI7~;ouqJXn4OVlsn(=D)!0?^^LVPccnjeP1K(W_pGjRE{aerc2oyMa+#9VMUER$ zZ+%!0GneI>y% zecoE7x$4AJ^mKGABcdpyjS-G7zzL9<96Ka`-=t57O;p-KoDlwNo=_!ip6ELSEmMTM ztI8+RisK=|sKuE=^c>e~uNS|ILAV)9U~usBL%w7v;%jR}VMN;O-FFr7G^e6V{NZ-I z__c}yvohTk|9s=b6U^piFZ}R2C3*7BJ+i?A_E@A33As!P~sy6EjBVrM40L7Hw#?wSGk_bml>8LZvh`Z@UPY?zcYG7+zxB)JtdDg?z7U zpFn{AXel^0xQws+ih{Mr>1g8z(7^NCo*MlP#(vZHMS;8TADg}j&|oE6OO-j&mmrQP zvTC#JiiFzQa-~$w=cu%b=GZtHyo}i0{=*ASKO4z3;bJ*E$;yB_L}y$CL=8Rtxtlb> zwpQgP$DXP(R9fs=PkW6`emfV)YR!cE9>C7IGZ(gr=m@w}DDhy=55W9x{LtG><6&`R z`9sKHw0CPaln^oWTG6jlUpdQtJQ3uO;Sh5(&Ggg$X@rVM!Vl~Fzl!DubSXFfTeQ^6 z!YN|9J>BQ62CYNTrad{q-HVjB$M52JmrKs0GM_SIh~eiX#aV8KarkoU1SX=3%tk%IOU8&-SYJ*B60oI<26^^ zixni8XHeir#>F~AM`n20AH&C9S(m3Il3p8Ry%I6KTnqRY501CQ%FF6bX?(Y@U&37@ zm~v;ZkT0#;XuCMj`oyDF`u}d;#Mzo(^R$!F!V29_P9UHVNDU9q{gxtg`i`MNRD?Te zg$D_hY3`<1%D?=M(|DNWJu6KqMw4_Dt$eTD-N<~GOx#+YomUoa-xOzc)VAcnE1w~d zW-*%_OAI8+?4Y1lyHaF~tfOg@I7wutbeb=dJoU?NC)AJ;J~ex&aCAm-gXK!zfqxJC z)DZe-m%By=f9p2lO^)i33nUV`iBQrEv88WV<`K)(hWwVea$lSRYc5vtTl38Mduza6`+e9DVk3NqPW6y3YH+fIr^iT21NaTCsaJR^mkT zfhG7L|4*Ycok@kUNTfBCSMY9M4?hf7UC@rR``c~r6cSqjc?D4nMzP%N@(usfS8;iW zD_S$Sl=;aYmOI;mryM8%>L)P%{ifOc7jUTKgt}GRyKu-Q!B=BS&V2gD;zykS$sBAy z6i?j>3(pIQY=uNi!M`UKuo@?}KM1kbA0khB?`nebeOW3Gk-9SA8zriV_0Ys8ZP!m9 zH9W5aWF52JxP7>zB?SXq&~uDk)(Yh?qvOs5?8w<+xa!M?%WE26Q3p~C4QBmktBEU* zmn5qA-vo>*Q)s4(9~OTK&QAq}(Gc_9S56aRO&cQ~WwAxWYe4Wma z0DFBu`rP>%zG(k5ejFD)bmyzFP9R$>MQ=Xlm=b9*bAxYsZpgZ1rIdcCJAH!}@P||} z!l`9j2C||$;`yg-^v{fwbdJh(u%wy)wO6MU++^w6f>WOQ9YDK(YZ(-(Z& zeI&+3`T1Y2-Z|CNZFFA`T4Ir39TPWy%l2~Wzf!gTO0Hkq!WcbBvn3O)+j`u!!89AS z{X=%pMo=B7cYA3DM}vqlf-gHlynY+-lY5A7@z3cFQYALr-k@ z(epzl3@arwz{`Vkdmm21ncj}hd_OL3vilvrjmqcln@f$x%Y6g(0&Bdel!bk_2X!L( z>vXS4$}-CiW*GCQAZxs#@xg1BHXnI?Iay@lN~3U@)4xW7J)EnlAgDhahuw%JuBmMm zi*M_ z?Tu%HNL#O>{CF)#s3`8i32}Exu85M$=0&DIZ${-amy{Pqj;Ey(Q-H5tON6_nm<|B} zN)6~=-)}Ukkep#!ro@Dm{G}`N4MHlJc@*=)6?^0I)4Hu~a$jqvO{iOj$5DG-10xb6 zpjpdInM1PQt?hg~{bT)TFIM_uyM%H;52aA6ZY!5MO5dBkeTnnRhlFC(gOeg*g|lmoPVp-*W-Ch0RpVe2JYIw<1?hBZ;c``yCzPC^Ky> zgwbzA|K`wC5{JLf-s_JZ*c=O+X##uTA z#(twne>qeVb48v6{3}y7^&R2sc+6Rht{NW#wK7K6vxxVxhg1pe_nU{d|5Hff#2iK* zdIp?JwNwV3n(2g9%WqXmw4&)$`j`cd)LS=XRJBZFR2& z*sZhsKQpaXmJEYMBQi1XdTps_;wA`$7Qgr|AP?-s^I@#TM{tZmPrv-eTAGrvy%O9n2hW`6>luaQ+ncV2!d?sX z?Hw^3bX5%W@H5kycw__=>B2nuF;?CN!S*)KWe#&sogGX3I{&+#*~{7T78Iuib88I! zU`RHBRy^F*=u~1pnTYy20{1L(@ zPFKJCFc0ueo+IfyFVD(c8)d?mws=c>{n}lvl$K_X>&wmD08nsS~EgFQM2=2CZz1Gpe~BvHq0%g z6pX-F@7<7JoUUU5`iiQ)SgFI&23c?UbE1O9p~Z`xg-e!n)n*Weq z*C{bZA-DPlY#ViScrQYt2&ZDH(H-7Dc`O0?mWD<5PZa4zmTW2&LcYDO>R(f}Abo9| zb~~ez*S}~eYfARt@1Og{X_$GbAjHd@st5YSN0hz3aINJHKWaqp$byWL$$h^5FfcJ< zbzkK903XP!OV^TnBx7=tX=qZo6CeSozE*OC^iQ7x9+t|6)vu(f_LIC9Bio~WQRTz> zVNv*jGas7Qre*O*yG=SuYGJ)2OSLhOwR6F#4W~Rowb#zcpX)k1=ALlZBIjT|8DImU zIMJK3uBqUk&5V(qV%=|tg!9aKHtkQnwv(U7#1e@uNh zN3h!E9WFV#-*D@%EhbO;St31aGW6y*D&xN8o7jN9_9LrUd>_B{S$!t$dFbRh`&%&q z?Z6rbLJ(m`AIzU8_4{wL8-oA$4@tujm|OXjd43!}AxKC}EdLJ?y*mB>!=L}N*?)+r zDQ2+V(N=#ry94PxamHrQ6<)-JSn~uL8e=pr$^@+nlFXl&GF&BEy~65hg-z7|7T*)v zik)a}G_<=q(Ym;7g*w!IIY*J%1_x~4(Ft#EQ=@bVG45SMB?eSyB*Aw(U|~VV=(@Nv zU|QPB5pDqBs|;8zZpVwoy^8E!;>2;%@Z9;rT8KVrMNK`&2u_zQHk0ucQNw zwA+q@$LNny5XrPTu}gla_ce_A`bP6wt~eBJhlyyDHH8hBha$=qOH;zFW1ky8U6Mjr zJf(;OT(XXF)2L~4L2QOuKiFq#l*xiwl=K=!y%6@T9YHLNpz(cGsvF|+5w#4?Z(OlY%*sR0 z02G$!Cq&vlls%OyYOhlfvZO}KpO(uoRD>fMzJxX4|3Y9Ys5ohnG+ZZ^L0J=ejh&)F zw>(S_NpB;9K0{>!+|@k8l7j|P?K^QibN3oAWb7@-)}glcnGjWS?*$$X<98{lnT1+_ z7H(RuTkvmUmfe)XoIK&J1SMZ3mDQbN>nY#Ke}EVfp~4g!HoY^t?NCY(TaqfNN?V>W zp;YfV?z3xbFm5dU*_q&30>zOPC;9DbqTm`2Wp64vxXmylag&8u{$?au)Z9t7=e_`@ z1J?IJs{2gGfNJaHowqN5b5$ReOo?5vOB{8>8BgBcS>gPm5tZcY6ZAlGuU>T8_xACD z_Ey8Z*dUKJDfp<3{f?j94N?9D_zMwxnhLLPkW046%RM9O*4Ko3|vgC{VbNXX_f1TH!g? zZX%@Dr_a>0@pEp}ZSvoF&8PYXlKLDPj+#f6D@~KXA~=OPQmfSeTIazwPXX=Lx2@vo zLAq_NT}RYL_Y{2}0#cbUkIoA!X-KxjR4V>Pa8Yod<4Q4n>7(X#uVBFp8buk^-I?s| z!Qa&XIn~=oDA~T>M7Fi;C$JKfhC0bwlI?+VM-*h5qb%Exb)`R8vR1UfNf{scVOCLW zmoHD35imn&2ujToEWZ3Ff8Af+jwJbQyeqUf$@p}{V&%&LEp7eJ!g|+pI9_f`I|@8) zv&A*H&7ze(_p!h0Tx3frt4_h>dX*$S$SZl4VI`r_e%zMs4ldCHUAAz6%X5Rstj)NjfaFF2@B6n;;|0VxiTYakD6jtOAN=J zLN7zF&LN*M2#LkFQ7FS{u?ENV?hLP`;7HjK%xi9JUAZr5j5Oz{k;WfkcZmF6k4DNrsK*;DfwViHj6 z&VcX!A_0-bz<3-qm`(5t{Jav9>Mq!=u&urn|7;{W@ORCAvm4sUz1A^H71bmQwL)FZ zP1rd+xE22CE*4y#dXSh&6_kW~?}^@fP$-i+AJRb)c;>F%kKesRbz!rODhBY(uAc)y zvMH^lmkfsL)6mC|i>EA6&stXXy*VxrmcC)BdkE$0fzjCzCAs!%Q{%M&9;twL9Zqud z+sDkuk7l|Y&D?Yxyff5{yfgKLdE`l1A^0@bpI}26efSDHn=1VYkoRLDrhns6skYqY z5W20!2dK?^kR9u3po!FTr-{fHzAFJUJ+Gsu+A=7${38qC)6N*5&1K^Xia_2=!wn)| zZSVVcbo^i@7qh(afjZw6f8b0b@;K%KNPm14OVFY$)}%3R6ghd^$+wZ95S-(26|XTD zvKEH+Dv_I#{>*w_H!bo^Gv3x87a9_=1B`GJcwt>-droT9yohSooF@BfpB=vk(OibU zqf2OLc)zjAd7Ja!xnT3$-=;&QN2FKGPFWG5PoSzRr)0WxFY7`t$?H)bgF}~-i5R9| z@8t&kX77+Y$6EZ)X^3l>oGw03%r5*+XsSfe@Q&yB-~-eKrB_D=f@<>LGg zfPL92L<|&&frz3hEtDGq%k1{%E`RU46Ny8U2q_G8ld38X(i1BP2C4)Nijsmbml^H4 zTzg$H>5@xuwZ?dlbLTW%{hlPsPf#CG@x>x_RzqiMNPSN%&I)yY6?i1n{P$4;7BOKp zejl9v$?UPtIkHI84_yL26c$wS%m7*}{D+h~&ah>9^`{lI3<##St0vzi$Kf`H34_)> z0@XX`yvXhmyRMQ0?xFyRrftA8k?Wx_sis;&$VS~PV4K%XRjfrmr7dc@E}V|dn;}IK z1vC=}3k7s%-=nKbYIoL}q2Y3w1zrsqclRe@drEh1 zzle@f0jrRjP@m)3?U)0LJ3bLi5XXsnWXnOf*LS`{LwRb95=M+vb<$I>$@^Zb5Z&n` zgJo8R5e_yIcQsttPy6pJ&=;hhDX`(ue;!hB9v42R{-))SWunBZsx9C_FSw($Du9PZ z*CUI0_qnn#6^VZE)o84dH1*FAM(bIC#kyf-Yc4_t_v~^EE<-sxK%NvBHmRZz*yp2; zd&ho8e-NT4r}=wk7lv>e)T`a8d>ie&oxb#>%UY=a47av77LoZ)BEXR8q2AF@hmmIX3#mmcCi3; zv|PA#t%=zb02Cr_0aaMwCF=*tc?>@mFB)IP!z%@RFJ_HbVU32Lj{BwEffS?FO-;+*L zFP6ghb2|I0LR2x%*maSE?wedDOy`_C^9FQ8+y?xz-t&u-we>f%=t44d*gh6 z`^N4se~Eji=IJ&+E_H#J&3q$27Ztc;F@1?>RvG4a2AW0tiIx?dg=O0%J)o^3K0c>v zi>bUCM>Ri=G*LfxPlMC!3*Q0)zy4%C2~oj0^%B;#CuasHh2VwX{^!0TU%EVC5KeKX z)iYo`7DgJM-9GW^{1l#ar*w7`rgkUQXU25LGX6cpYB@uA46$HgENQQ%`YPgTOwUGg z4r?$x+|uD%^v@GX>yXq4^X=W{C}#H-43hGB+M37wWS-H_I|k!3Kkg2`xRWz^Rl1g1 zT_Yh6>Y84vfbu#vLv zs=jEwsZOCG#XD(~G*F`W=iXPHmvFY9-~J`Ld#K_xJr7KWGx#F$OF=XlB)5`-fk`yP z{2%d}rXBM9MuWxEjLuo%o1V7=q~Oe*_!?Kn0 z+f)18saMLw2*_?I>Ta0T4IP`P>^Ap5^I+iApU~-}Oe-@;x&;E9pGM*9NECvgZY=t` z^&PQ2!53FldB+`+SGzD~3&T9P2K*dvqMRU-CQ{C>`NV-542SNPq3^jg!;Ldz@P znLOR9d0$Tbgrl}&OxzMh_I6+T903u+z*OPe9na)9^&IATA6HVj^_)V-7Qr7r6_59I z8NE=hzTVxy;5{M;L!((p%EeS90Y#wB-b`Db^_FiI@)-df2Y_;N@Z2I23h~!gxq^+e{#4b|6IrZ7ET!0lpH6>MkV%LMbVt zVMmf85KPh0hXx8%_d+M}^;FS`!otTjueJ-~tOGxeF#lNSki7==Lqx=k;*Q=f+(|tJ zzT`r-Qg*N!lksK1q=Py1U>TLSm?qGZ=-?>zW_qN%3A~dqG_g&52qSn3yWQ*eRtvU5 zYT-7Zoj+&qSRVJV_ngLXaU8sZ3XVmYp-`@|0$!&I1yq-_n#q%L4wer31o0Zo8(Yn0 zwCEvMB@0AVjCaBu9*=nO-kw;Ur{)i&t=3xF;!qaR7cVO?@y}RoXYO zxajXTGqOvCsO6prDD?>b66xbSN#%{!z5jI&^0QDq!1S*vXBt*ssB#J77jPis96maQ z8NG%)=5!IHOi}WP-PzzKSM~BWdCiS)83b!MJPo+*;jt}hBrB4{lz10gs5LHp>NU!F zx+Q+KfwwEkaKJHOhBGZ(C2PJQG;=xGEj@RO!DC}YClZ40TkU5oNDA9Yf$vO!)fO36 zdip=-mihzXawbDvEX@tQb0I+lc~E=B-(=UG!LhYYUd4Q%!!fU9O%Xwy6(DllY8B3r$@{LE&4yB4OqLE{l^LU#^dS;ldc|V-7s5 z@(~@szgt!IUu7OjozzKh&eg<)@!_^}RcX{CQ!YaP(@C?RFyK-=m&`>WJvTml;qpB! zXyp6wXo*ic@C=I~yPg35ouEp)p&d6E|5{^KFfQ&E4ZHfZzh7{Z1aF;1zA;Uco2ys# zt8WN5DR{P68_P+78mxbPC{kRW8=|!eaGR1`oa?e%GCwjHz&>R<8b#rOkvoQ*;XgvN ze)~w}ftzLy9mYe+Z(^5|LC=>sI)vxFT(&=uo1|TGuFb?W8nC@>)H_LEo+kDP_{ZC9 z_n_vp`N2vKP(fCI5&!21_?N=FDIe(6VZIOTAstY^HMlt`%+Owzdhx!(2xw-}JN0yh zV4(`YyYp(?;IHbsV#WP~I^4^fl) zY02*&Y*fTi4v5QB+lNOHm?{zcR!NXTMOZXPt@Aq{3Oeh-$WcN{oO z0}*(def9ZCqd#mUy(amHOyyM~;#YVO&p!at?uA%B+KIFakwQ4)#7RWqSGaX^MN$53 zkxmi&5JJ)(x})!hr+g0o1wMH_iu|5m3e$R+m5i_I$p`k04e1BCSq?Osl;ia;3#gaq zxAAd1yBXIs4f2B6>;>{PcRPTCDW@y4^$4c(p;~kkJ`H+EN3K_B-p(gqwE}Ziug`Tu zcwM)mrd(nrmyY5e2FRiBFZNo}qChuL%Yo8dY;wd*EBZRyei z1DhtfH!M*Xg(8#PWIUajx;iv{2j1Y{ne>FQ^}m4#lNxYtFrJvAhX~UHrIr@S8|m6F zOt}uH-XxNOV@-mQDts3Lm_`}mb~b*85sWhf-2X8V$>_`RkP|Qcv&T07KQo00;A{g4 zm*6EM2sQ!s=D#*(@poQ0Nmlxac>m9{CknE`@H);kWF0;rlLLnLvil9d9M$Ld33V+|J-5xA2@3--SCa_gAx8kB))JbMEaS z@L;!WAic@_C}(=e4bO`aeJgr1#=#B?=9qs;TOp=o#t!Z)O?!(W&Yx*A)^4g0?#Y0G zgN4zwgF6MH0U0_;2x@!W4)3n-q|?>@`)5h*T!9%8VTAq1r*tR(KTq14oM zue^e-J|SI)DuBp=;m6ei3`!An?Cn{}>B@O>*L4Te4HxQ0bXz>#5}WlsX`| z9Kyeh_&w=Q73kYa_U$WYFy_Krks|AM}lvOr)TXwDB^8MnDIV_~T&ZAW%|wTR-KI0_=Qy@2im$4Dnm; zwznKGLA>XXyesLUqVxyZ{(Fy*-@K-?a`E6AQb#gikK=9UN&}iAa}0gj`e6^E5L~Yq zy=i^OE7|WVjt>3;Ca@L?WJh4UCnTWYZJFlr zTRrDple*^L@1caEf-x!H>_T0mwr_$&3M`q)eNZsDIswqmow&3%JfrcIALs<}l2AA$ z{t2vUcBq_YK{N(u^rRn#T05{EiH<|YY0zjf^nVBUoE7WN?Q4U{gGGY^(x%apM>fum zD6B|*OQAxcD1-jH)e`q|^pRT&5hbX8NVzDLL^B>1x_#ZQ=51jVp;Qr63}3mSJxT}H z(1?z3NQ7U+4(bZ>Hv7tc5SI?14;6-OfKNm03$uWU(09^}CwSJqt_DX2f_cs;`d3hP znJmKBbIRMd(ES(NM{c}WuZ6GMhcPV3W~CX}1iJ#eUw$x6Hj!K16%2v~zw9dQ z8t+=~Vq3(Y_I5pi+0MFg7NIaX7pStSefnw0^!Ft71|$Spdo?t;?y2*`g{g)fxQnt= zYohW-sN}ps&{X69u=mzcaX-txXo#SJ;O-ncGtE#)PpyUV$h8NpAS5M33 z7hovobxQNYaA;h*yV$SjDIZ^^$Ia>OxRd$nse@ca;~>0*DSoKlPjpJ72r|<*sLt#3 zTM`^Hls}+Me;-Eii(>X)nsH5#+UfE?6)U2rd^wzA}x-OW?P9psTtJE@NiAy(qMCEXx z){E4g@H;%I66Ev9vo{ny=gaI`P@LwqfPYm+uo2=iV0MKAfN_aTkF) zJ&&4&*hUgXsRV2MAju@!YPF*K$vqsUt!dD zvJiZAFp@kwbL7uHQR6vjyjcx|y$QSZhf5vs@5Cda2ZC)TRDhVqHPfQ4F zg8y?qxWFkY+HqLg=DscOMy6XpYStMnnUq1gLA(QFo@7lZG=~3f4r$SQ%GN&IqPwqM za)9AGlwqLK>S=ZlV{*AoQ%?84@Dd>rYZBG261x+mYUh12;RKk4@>NL8?M1JUqJ?{h zVy$3aZ>m=NKPPO`jzAH{UA8;EOJx**XKVvHhSz*hy~LJS_}g7j^?Qx)5!Y)ep1}W{ za5_!>Lfb=QbjrSi-`zg(Dd8f>LjiXoWP^IrZz% zMU3H_G*H5jG};>M1hMC$YMV6}gJwwW(E74G#Ku#g?#+rhmu}q>JFpp$e=@H}^42R0 zbTK{HW}4^36j}~Lckx@ppS8Gtd2KO!F3tDV)-%46c3)cS_G_5m&Z6v1FAQOaZ;k2* zEV+L(Lt4+h{%bj-QbD^-)1cuxcf{oW)DZs=zL4@rk?g2=7OZvbcT0Miucwy-WiK@> zc6b}-CHch1@lzPA2;^ylT!xlwmtH?t+P9eY>$ZxO9$liDSfOrfzqb9ti4wh=5 zc6ufTLxqi8W?Yjo!psk#V6FxDZs3sQ=pU0a`!=7-rqRq zrDFV}5vRODbN$C>pX{F-UqL1~^9t`auNB>wI?20OFW7_GcCxE%n!M+2s+wnGBs=dy2L+cJ8!h1SXY?E6S}hhBv~*NOFp-rP)k@6Uv*2O_|@bgf%%sQOY$ zQzL4`<XGj*>87)0%a$pm#=>^9-A;Jlor04VV>8Q4bb1L#ewJG=WDaKuvfRS5 zY3GyHz|vP_a~F_2<4eDOo{i}1Ucw}0F@ps~@D#-|KJb%9($s$m~{S25-Hig;dkQ2-E{-i85?8ep6?dcneg=vG3is7rqZG0!#leO6*N&zh65dH72 ztYEx-6rXO*^SUtvr@(@{UgdAsD5frHjE=CL3$7NwVPX~KR{cnIc-mO+vxJE!PH{a3H z`wGOqkGfw=F}_!qmBw)atJG7_&0>tg_9Jo>)oyDAB zq<3x9&0g&P!ltL?l`7F0==uI{m)gll`j(_77F;zdksl-IGPuA} zfWg2KVqyOX82LcU@IMO)`R}Rze&*UE#5NXKBRV`A&o63~^0cJ82>tuItEEl96c}*! z+MkDi2VoX2?)k8g*`7x}&?s2{Y?REU{~qrD*{}b45FX7!%$7=5OawW5<1g)RUjG-4 zzWYyy?bTh8{SWGZEEG@oyd5$8(K-N6_&?zJ$PWDrVRN^3 zr{EzN*Pn-f2Vw3O26y0?F#oYC$^X$Kb}dU)O=nGcIX+`M5R;*aoslV%JIMZT*b4}| z+Z!5Nn>v#lnVMVL3Q`_6fhox?O#~@5IOSR7?Zr$jETugiO;tP z-1&e6K&H-y|QZi5cHJaml}D z2mB{UY2oZ_&&SN{=H|xa#?EBtXwJ;a%gg(a3>zCGK*8wbVe4$@&S>jI^;aVQk&d{j zld+?vy|bmAE%`&bhDLTS&VrPb4;lUA^H;r`EzSO!ldaR=!2@6fj_xxvV`gPyVg5(j z0IR^mTRufcOH&}{hxCP51^!0)pWgj5pTFoe-+64K`6q{(t);UN+usZJZ?FH(CBXau%)bEjw^n$l;lI8AUjYb+$v;}^KjG(YX#Zc~ z1E~4L98C?K?HpC@>}-Vo5*RyS`ABE6FWC2IsxW?C;yi!|Klf9 zaYJWQAvP8^Rz?;cMivfLRvtcf4n9_v_y5BG7tz15%iEb)ntA*ec3wUfc0M-Vzq9`f z(f@AfOpO15oV|;q&EF*4#F*LC#uNmImlJ><>wgwL6JtIzJ4cYAvydgo(A<>S-qu`z z`JXTU+w%R@;6M)pIydt_diQ^{`agQb|B;7(;{Jam`M(nVlgV#^`v1J`dM@LR-xr0Wk{zlFeW5&w~{KXCmP0>4H4N4oyN^;-!17V#hH z`UBT*A@Ezof28XVT)&0DZxR2Iu0L@776QLT{71U}!1Y@Q{1)*a>G}iLZz1qo#DApg z4_v>6z;6-%k*+^*{T2ehMf^v){=oHH2>cfDAL;r7*KZ;4Tf~2)>knMNg}`qS|B1J`dM@LRf?kXyz7TC>#6EhyLeG(TGv2 zEKxV?QG9$Kdh3@zUebQ*#S9EYq_`1xLU3={diOwT!964b6iJoc)yre|3krm}FitSW14HO|2#79?0Q#N?3k&X+pPlg;wiHTaZCttLY4n&ePjzn^ zH7^!i062^mr68tFZ-J)7|1r9Fli$*OT*SR)w@p^?x)TwlCS>|*H%;6to1ww!ZEXrw zAiA9w73So4MVOe_=3UMyeTf?o*sg#-H0TRq< z2s*o2C2af7=5G6!4Yvd$#o{kE!r7Qo#1xo0yN}d0n^rOY6QE39u=<2NjpWJ&Y>Ghl zxqb@^yX!vcMyRW4r8H}>_+tz%;d-s~#{hZ^eVP_-dwZD<^2T}LSjMWELT$rKdxNg* zf#|CsI^(RC{TNWE*s&ZAO?Ua7;vvmXoW8$)qoK-L7#|hejaS#&*xPSYK#QFMD`o7* zBT{tEDM*UmjG}~;uc}jrWAd&Z?R1>DF$JRM*i2`Ex)|Sc*X6mzU_ylEDz_JxC5=vXB~aq?d9sr4* zeQy-47M^_!jhrBrj}MLbDbi0-6o*w>?Bt*2dnVFe$M2*mm~^&35ZboD!xIt;_UJjR zZpk1JF)$GG9IfPpQrhe}H&xP|OGrx5y!jw>E&W-#ZlF_KD*wY{WV)y1k__zowLcQGIAfQ_lPb z=@qt$z}~X!3Z-bESu*Q@eBq{sGJ8XCWilJXT=tqYLdXSao|7WY!q9@Ml0TU+2Bj9N zEsa8Zpk>yx*O@lz1e_|?v_(u}+u=~EeGXEKZ>Nz8jmb^l^O6NlLa|1|R(t`;e}c+i z&FOQg7&$Y|OH%IlfQ;_LI*XaZgz8kIsHwr%qmS~1hRKOGRbNP$elCVaakx|OA~p4I_F zBzcY5P_p6Oz}-m;^LTxpL+?e6ir^lP!%n2<>(Y|t4Di)RD)&|MxZcgD9={({7eb=P z2S)w09qW6+)@eN3xd{PR2)wX0h|>l4VV)|z;0CA9+3{VOUxu*X;fUOBYB0ix))c=E zAD?nIF)dG9f0O!260=XRk<>9=*>+C!4cJk*2nZsYzwg`Rhp4HvQ(C--G786>47nh^(&sgQ$$y5KJ0n7|On z*6T8Tc?4*Iue$H8_i3x0<2Hn8E2!kB3Dz(n}YcY{8#`{OUea zClBuoh*DQ?gH-j^h6FRfGz)nuH8+=Q`pu9}gAKN52t4Eu^A<04QC3M728}{~PORJF znF5ohJ)HgZvfNu>-i`K2y}h~$tR5v;$me9)%0?MCcibO1BhKm6VkIz@I9A*;b)+TTxC{WJVYh@3PQFb&7lj4B8k&npV&j>{&odG(j~Yd0IQ zDw{jESsBn|D{Vj1rdCMltv_*~5xh{fnd`1(Fw^Hq5o%%1K}krJ-qCaI#WDI7RP_^t zAO58olUgAyG%~FLQVUG+5%tB;>JsC!)sIHu$e)7M)IHRA6*@d5TO?6Xh*G{Je=%H` zQj<~Uu0X@i!S#;s$hPvH+*TBb%t*JPz-`b?F^xAt9>zR?Y?&*9-3QhXj-S#0APo8iQS>Q*MTH zTr#L72}{Q3_9n2Hvk{R8ZN^bdJ)~!?lP)XpfMmf&w~6!WUJG7%=aw_Nj6yi>MXHd1 zyqpYemZ8rRe(pWucP(68@@evW?zRZ8!oE~Bx_;;2@OB`J+$EeDuQu=9u<wnf6ncl}*{^XF(U%KC8|H<@(ZhFtEj?)l|w&x`YS3Jrs# zcz}p}nH*K8HR}AH6ZK0&`zoUhrsxP^$Wch6r5E#|m7eK@x2g6h<5mD{2N4Tb0KTPAAu z8o$@V*F9v%M|O+1?X2I2p5HROxZQyxZMjgky0)%t6iWi8#H>C~izhFompl84dl}FK zyg#HcHzeij`C5&76dYNi)E@62`lO8L-tu}AdB-K0n)}$PXN6+sCK~0rDsCJz3k)wU zom|)mm0x{3w-v1a>UVVpK3akuEyO`j%EuT)Of5Hp<@5f5lsp-R{zhIt%IH)+PAw@mn z24W|_KBC~^#2{M{SVOGvs zm#iF_`-obWJuSjB?B&k4AH^ki9=*E8dHFugs-fBbrC{DMS-PNu(Q)f|^W>nfI-j&vp2Q=| zPg)yA^`%=CE+mT)*(+*~b8yHLRqX?d8wb~JlfSKBj9C8ja>i&d7;=jn#bwpSW2BCjg$-%r>wW{v(*F2 zgNE;vwf{pbm1k~RQY;(JFH6`*Zk+Q>G!104h8|+S((Z3OJz|;P`tm7adD4(GAK$Tu z_qG$U0s^ak#CdUktb+IDu!ZrLm^`J+qcI7*T`fg$8CeI*&z?IyVYsP^5pfz)fvqjJ zSRG(^i}RfW)%m#aJmYAdesj2@7d#gxyANDzj&d_I5^*uI9N^~jd4LK094t&QA8e>3 z*pS>DF7RHggxG@j4PCC79>>cXo-omhXdhxmp8jWbI&Jc9^PL`_Mmj8c;OsqgCWZ<+ zn92+6O7d&UzRJ7~X(=DD|K_X5Kl3(|vh52|g8iwvo!MBn1b&o>{f&=rVV2p8|}?(76FzyoWBCVZ@c#MjvH@R1xp1xr(i4qE+pg}nu9 zp4Gf@j6uh*FNMy^TVL%*`wbMmXzBOf%s78-TCigOL2&Pfmw!TvkQbK5f<2D!ElqJ< zhHtvp2`}m0h?J#oMN$_7wSoZskNvhEwyrA94j5$3rvW-w>Ga3eG`4P`=p(n=1ir_9 zej+0Sf`bNtF72?|fo7}Kt%^t?Ig+?#+2k-TP37+ZCv@6oemTA56XEUVcwV9Uh(wW2 zLRXDspiFOgrPr{wctMnR!(1(2bfvS8$#ummB$`?H%#_KmXQSRetz0jty&!ckRVX$I zZ^&f4Ri5L}4meU(S(idS%YHdC-4l69trskGRHoCsXQZ?9a$miVS-i?@&}b(}MGI^$ zSc34LE|){_9ZTwR{NGj`>BWnQ^5?EzTUk}#V_)3Oh19R~6MgBixe4_5xvhBI@&R_a z>Bq9MNyjl3=(?AyaQbn*+1 z*j8%+p08#D^oVZYoZv4f%6 zmV)f6SLy@SMX6$EEr^(>{zCiWr|zV@@Lgi!%ZIF$E(~1~i|oM3I8GX=4lM;vd8&#l zP`f3CfE>e#$J$xt6_&1?PNy2SX5IA!f6Y9&3i`d1ZaVU9MC8);6BkeLfHj%37Agn< z&2vj+WU66H5N|~}o#HEdt`+jYVqFBh+Y_mp9Oo^P>fL>syUARaimxTAOoi)}Zu+jj z&T}6h-A%P-^hLYoMT z?VpSHPreco9%cprY=c??G=Pc_sI=pqi(Til$<=SwKblS0n3us88ds8to7nwDHlD?4 zbC6;ZB{QdLJ^590m%Wxn_j9`ReEIDo zr&(trkC#y+s+gLxhJO^(oC`9AI9lKAQN5_4VOG|PYrWISV&S{+kL{0%t(0#Hh8(_yc4T>9yG!9Vzg*$i|Og7R3emNh#pZ{%<@)qTs~Q?CWoia1ST{I;^*~( z^{J&4Q%FkSI#PDSsD2q^KE9XB7HeaM36o3X8Tb4Twj-iSA%TN-32kE{`Ez+}c}Bu_ zyfSpPHVf@eih?2Wqocs3?9wygrS^Cz!`f|}Q+fdd+k_|R9xBFIcb>vo?ugvB9Mvkp z7k#29a-~R^-3|P;hbKgP+F$W{IfY_ZIZnNSJX-?J?X@vL{!GTY2f@Kgl|tk(*6C1MLzEyiFDKuQfY=Z9&Bv!LXFt_ux-Q)!Al=am zJl~*s4};g;FVEY{@%=9pj=%(aQ#m`X@R>ZrJL-(oaG`3q{{G3-+UfOh;Z#wak<=ZU zE6kJ|&;AENiCAeCYsySf^{(^ud3^Q4-h}p-+hnH(Bj+e7i9rZ4+PzH5{66l#5Fy$@t0gWZy zjfJ=~7V;KhF>-3Ymvc&|Ly_!~PSN`iDMB)!PzlFl9pj~RY#Gua2Qx(9P3Y57MPg+o z+ao@H{+UWw6l*el+3|Gh?u*Et=nNLasK#i_LX_hJ#g-IB-|N`RBUB41TH^ND1n;w5 zxml2LmFwDjy~{2X?01Rt!?ERmTX%#OmkO3DB_>`T8NOoRI+&KeulA-^OpRD!fww_t zTjtAsBcg9dcHwg_wCH|`nm4O5;y%R~M5&PK*l)RSWxCs1GT3ZEog0+9mF(N@iG!Sr zjl?cu>_qiK=$3o~XXCP2h!_oF+JH77_fvx73}b==twX5pjoTO@D7U0OvIy+Wx#YR7 zt#dLgo5*H}JZU-!T$ao3acjb)<3KUxckB81{*m9@2{&I(+EbawgN-h@u=^@M$c}7j zcNqIZygi4UnDG-~*v3dfNlDl4X?p8|`p1??ud8m7CGtw4cI-TK3bwtr_!^*Z8l9Q= zbbMC*fU6R*qb6`V&n_NL#gN*9j!i3{MaM`Y9S_2Y?wRp(7uLU8)tEktMP)F6!~LZt zNR`%@uP4u0B3~kKcRL<8#Aqx!`BdrKuH2NhR$z|duTt%S1+tcuDZhI@zpH)yEl$rQ zHYa4Jd=zm&-n_}Mrx>@HI0hYFtsAKPJSS&gPv^=CeH>mjn>(KNnd_y%} zW7V-~YiG~su(hSw(+bDhw-w$mCBssVO2)bOJkRyUDksu2CtYi5aCK*{)NC=;BQ7B! zN2$8Hw%iM1n||9ByLZ`vwzKLIVN5J|R-Vw}zUg_{+d{VDbLc_lajoDo+X4{*!vZ`*$4bhy( z4n(nHO_4tlJ^c;u2El@YYV9VrXy_QbnJB-6n3xnz=3@@)q2<7AB#AAiG<&JU#dy8u z+61d2LT=mD&JbO5tG8-IDO{71O~0b%AbAGfa9mt>LwA9VV%XYUsxxZadY)D(gCCH~ z_2))9i>MuMG;GbUJO*Ofr$Z`?2%0^}uFp%#PCLr(KDYSI4q#g&cey(7xvOAJy;q}O zc+VZ>jc~M#g1=B8yM|SylQ9svWfU z1sCx;4eWyE_B;#xj;J7awphrN=$$?_=6gpP_JyD0`jV1XD|ipOH@=`&hM@23f}4q8 z(6_^+=(9p&E-q>_$LV=br<1AU5%rAXG*dHz$rUTT%Wag*4#A5BF-wQ-&$D9-b3-Hd z5OaaM*nBMl-LN2+S*~wT6mAA%rMU-3MJsDd@J3ksrkr_bw0MiTW8@Qr*2L(-%Gaq( z5V|s6oVqD_)3>kEzawxTup=*JRRb%3@dNzvtTgL)^yvins&Q%tJdxx2&X~3f@@gO) zG~&v^@4%e)MwEYzqk8?$-pd_poq>U}0+E^;jrgbWgUci<&9Z=CV<>V+ygh~@0V^ul z5@a*f$0++Sr9Ma6fBp$8|06Qjy$4lR>*(i!?Sn0|@lPqVxp6!WT2_Ec85roA_exqx z0_WUrqRQb^(%|SLUH%t7x2a*><_ak4&i8@ds;vjlOhzCSQHhp1CH87XPt39d=g(%M*B)zPPCZr1*K16izui!dxH=L~(GR-(eM$1d{+4Y$_* zw7SJUXa>j}4n@-W6+58qHJHnlg~k`X<4MVIiQk2acJLivzq4FRP_K2TMla6B4>i zC`%G7G&NbyF2pS!iA(hagwV-RL<<;ohN8!`0Zqgj3P7jwvTo`*hNxIhs-yGSS-G24 zz?Y{;?SbPdz5rtAcCWGoW!|lj&k`fdg``q zM1?1#>JBd(Q`(g%ga#7O^47dE z3~}-X#w8{QUX6SWS>XT{v7JV&C)pm@!N?A43Uv_Y6*FTjWem?#PoxG>{6_x)2qkVf z##?cF30>@RSn&l}V`b;#U#Fg-DaE&|b4@RYxzmQ@rE!_4WH8Ibk-EKTInX0+ zd=kABkX9ZhwI)79>nWN~{_c%00~yI(1UL~u7N5V8be4+?9=@a+GaYAB*RA!_Yek$N zO6a-3eenAnsT}7V$E!Zft5$9+onb*i{vy#kS#)J#w@FtzXa1iO!8Sr`-H{!T)3Nb6 zF=H{R6m`2*J1~qWCIEw4ih8YEKJ^l5_vdIR`pV}E^!q;F_*Of*PfYK{C0w4p>Uss( zBF7dNhp#n$RUN$8wU}8HbtxwV@Aq_eb_325>Cez`SKRwG(Rc<;(M{p51m3aqK*rJP=Taw!r;CzoVgqKH_uGwAu`CjjZ!}%xeOu7~@ zCDW%|wP^^oF_JX|WjAO0o2oyGU0F57Gxi-`Pl&$zBGr0^_zVH%dyQ2XmXugdw2=3P z(4yCZmsX>_<@HJFJrkds%gIS{LP0Jxt`51DU{3SwSmJgd0TvXtfrXHv$7rHb%BfGs4?Bda(0@Ew-bTRAA49W*8;)_Hu=c z-EJ1}gXnKDtqswY>NI7_?%b{q!=>>+7%5F07f|B_7>U@{g z^F~sKHSF$Qqoxohwmr4kTfaL^lz2%@U@y-=NQD{k?N9=&Rv6Nvy>GSw)9M5JUg;p#w(P7XW zKjVXm26MIH4Lto}U6>D^*R)t5cL%#@V0<`k>*>v*{o>x~aN%HXRe~eL?eL`#rveIN zJ^nSMx_nFHj>nw;s$Ec@$I!rTsV>c2U9cnB#oA)q7OpK8^23m4-S@#@{#`0H4>*@j?--wRnYMgmnc#qS~J_b1Q2xDE|}4!)f~@+UgN zPd{_W#n#Yqel}-2+g+va6Oa6QbadD=l#f$BN+>}h~w*4r6` zqV`?Wd4Vz&D=m=9aK7FYz5%FhW^qWZCb9`Fh_sn^T#o{`R9t zR?GQ$$)2|j&Dumg92^IQkUw>DVyhz`gjtYGmx!mc`Fe8F`*eKio1G$Aml>-fpk1JlA@k0kVYLCswEXK@ok@Myv&a zn>q7>?eAI1R-%C{V$`U@pv*;OMdfdym@?n$9vn;-a?hjlNrN2UE1Vc#ZR_9Hli9U@ z`iTG^`wm%*`>5xx@6^cmwqhLynenI|ZO>p#`7vFey3q7EAM9-0#wlqPQQ@EDq=yGp9@khK1oV2aSnt zK7+6h6LW9z+CFpyL%Bfc@r1U%Ojf~AOX_!#wNrDn zRRcf8#$Uw&!}|2~s=DW@;FDoy20D{pKee2HhMmykmgxvA*MsKv1nOh&Mh^4&^wRW{ zB(f3{6MF~19!WX!QV2T6TktO9JC8y!_;)2t-uYW=!h6*o5=`t)#dXubwqtJ%hF)l% zZA;i(*qKuijj#Ar_JIJMr@NIEZ?+>#DG+g`1p4m14j(N#kt@d=`paW1nshyp!Jtoz zwR-R8v=LGdU7HHNeC0nM4s1%vP@jN4Y}SdHS|$v~nMJe#}_YO0z` ziOpl2+KSJr)U4u9R?GBXS|A{hoL+e|=r^W%wYt6#Ubs|YYPi7UD);x?aabLkcB}x( zB00s2Giz^Cfm;`&qqfgDSRWQY3Lh9B6oi5|_9hOx35A0YHHuhT(xDM+HcGrT?aO4l zX}WQE4f(KQMnr^whA7Iy!=u{b%N#rfTpryEw^JdI$S;4;dU}9GszED*ESo6k!sX_> zqHy51;(MKUUXgLvk=y`S&?&u!`$i=pfkg!~@sX92nVE9BIsEYOEv7UXS+(U#GAs7a z)9cLNZz3JO$p6KnF}v)Vn3rTTz~b4Ai;IPcHGMpacE9q=&tM_lg+{P$T7OV2L9a{q z#bOZWOm>o4lE{N!ZYvc9go?^;l+IF(S|-w-Bt0^%z(_;*DtvQ{wv>U?-nU&2eG3Jh zCMtb&+vCI zKJrglX2bTtC@+c7<(Ir+pja;~Yb{y&5@7wRGDtT;@;zte7{1j9QfDbJ&+~mRyOOf` z)uQ2oB48SgA?eW5c~$f4LNkW^2T%8L57}Kz(UKRZX^`o*v0i=xzpxvPP)0PWqD^K_ zegFZA`OyzBj2utTekIdU9bBE9BNfFK?NJ@9J#z#>tzMjy2?86nHPqdWgACet6Eu^EfosHwcb7V1{RkBTJ zT+o60s{bkV(a-CI%Y_{98g$N%Y&3^h*j7OQvLA2Sr^B{Ai=}4GI*G^j=s=VI;&6SX zOnC6QnSs$4DN-nD%Aw!QjrX0kKAF9(fBpwWzm@U5R#$GH1)p_12)ozyo`@-zE7wWq z6UF5{*LUI=Ezf<4pJ%dwKhGW1M-D8y5O}2fr{teO&rVtt{ayg}2CZ?k}+s{J2T zsfuTM;bDC`npAppD4*PihSg;S0X7h zJC9aBJxUE}*zcZ$*I8@(kxJmC@Q$)tYPJEEe)Hhc*p{OaS)E28xYc^zVX!%vG?>ck zbOfoj@jdGziz&wnycTu;nt(H)UT!plJbOJGDw$L7jJF}NL369ad<%V+Dng^zEFOl& z2Z^9l;9=JT7FfN2&_kv+a^eVMdO~b$I6@?SSK4yLPESsbHf|Jn!ek$Osd4WLqg0P$ zM__@_oc;N;7u?F=v&5P!yE@M$;Z5bWIT+ zxjG()I7d`_x(2&sogklJel^65D)ucd+razBwjvNt7sJ|tw!d1?#p9zuDTu+AHV3%O zG*bmpy&lx-6(#&!y45hA$dbtXc{@JwP&&Dl<;4I2 cf_jRAXP8FN{sC+h|Ho5YW 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

4t}Uh(XV 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 literal 3284 zcmex=kR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVFIc#WB`&3f20;!+3&yw1j7khlf{e_9jQ@`?$OB!^3JfDqFhKz$ z6Eh1d8_-Ey+&~3e1%S?HW@Z99mX(DCsJa#?&%h$cDx_%W$R-?^$gWfnAuRebI{N?Mn?>~P20{M%Pff?d0xX;l1 zB?$Bv6EF@~*g^hcWGV+@WL# \par \hich\af1\dbch\af31505\loch\f1 Everyone is permitted to copy and distribute verbatim copies \par \hich\af1\dbch\af31505\loch\f1 of this license document, but changing it is not allowed. \par \par \hich\af1\dbch\af31505\loch\f1 Preamble \par -\par \hich\af1\dbch\af31505\loch\f1 The GNU Affero General Public License is a free, copyleft license for -\par \hich\af1\dbch\af31505\loch\f1 software and other kinds\hich\af1\dbch\af31505\loch\f1 of works, specifically designed to ensure +\par \hich\af1\dbch\af31505\loch\f1 The GNU Affero General P\hich\af1\dbch\af31505\loch\f1 ublic License is a free, copyleft license for +\par \hich\af1\dbch\af31505\loch\f1 software and other kinds of works, specifically designed to ensure \par \hich\af1\dbch\af31505\loch\f1 cooperation with the community in the case of network server software. \par \par \hich\af1\dbch\af31505\loch\f1 The licenses for most software and other practical works are designed @@ -65,46 +71,46 @@ Normal Table;}}{\*\rsidtbl \rsid11478430\rsid12386343}{\mmathPr\mmathFont34\mbrk \par \hich\af1\dbch\af31505\loch\f1 share and change all versions of a program--to make sure it remains free \par \hich\af1\dbch\af31505\loch\f1 software for all its users. \par -\par \hich\af1\dbch\af31505\loch\f1 When we speak of free software, we are referring to freedom, not -\par \hich\af1\dbch\af31505\loch\f1 price. Our Gen\hich\af1\dbch\af31505\loch\f1 eral Public Licenses are designed to make sure that you +\par \hich\af1\dbch\af31505\loch\f1 When we sp\hich\af1\dbch\af31505\loch\f1 eak of free software, we are referring to freedom, not +\par \hich\af1\dbch\af31505\loch\f1 price. Our General Public Licenses are designed to make sure that you \par \hich\af1\dbch\af31505\loch\f1 have the freedom to distribute copies of free software (and charge for -\par \hich\af1\dbch\af31505\loch\f1 them if you wish), that you receive source code or can get it if you -\par \hich\af1\dbch\af31505\loch\f1 want it, that you can change the software or use pieces of i\hich\af1\dbch\af31505\loch\f1 t in new +\par \hich\af1\dbch\af31505\loch\f1 them if you wish), that you receive source code or can get \hich\af1\dbch\af31505\loch\f1 it if you +\par \hich\af1\dbch\af31505\loch\f1 want it, that you can change the software or use pieces of it in new \par \hich\af1\dbch\af31505\loch\f1 free programs, and that you know you can do these things. \par \par \hich\af1\dbch\af31505\loch\f1 Developers that use our General Public Licenses protect your rights -\par \hich\af1\dbch\af31505\loch\f1 with two steps: (1) assert copyright on the software, and (2) offer -\par \hich\af1\dbch\af31505\loch\f1 you this License which gives you legal permission \hich\af1\dbch\af31505\loch\f1 to copy, distribute +\par \hich\af1\dbch\af31505\loch\f1 with two steps: (1) assert copyright on the soft\hich\af1\dbch\af31505\loch\f1 ware, and (2) offer +\par \hich\af1\dbch\af31505\loch\f1 you this License which gives you legal permission to copy, distribute \par \hich\af1\dbch\af31505\loch\f1 and/or modify the software. \par \par \hich\af1\dbch\af31505\loch\f1 A secondary benefit of defending all users' freedom is that \par \hich\af1\dbch\af31505\loch\f1 improvements made in alternate versions of the program, if they \par \hich\af1\dbch\af31505\loch\f1 receive widespread use, become available for other developers to \par \hich\af1\dbch\af31505\loch\f1 incorporate. Many developers of free software are heartened and -\par \hich\af1\dbch\af31505\loch\f1 encouraged by the resulting cooperation. However, in the case of -\par \hich\af1\dbch\af31505\loch\f1 software used on network servers, this result may fail to co\hich\af1\dbch\af31505\loch\f1 me about. +\par \hich\af1\dbch\af31505\loch\f1 encouraged by the resulting cooperation. However, in the case\hich\af1\dbch\af31505\loch\f1 of +\par \hich\af1\dbch\af31505\loch\f1 software used on network servers, this result may fail to come about. \par \hich\af1\dbch\af31505\loch\f1 The GNU General Public License permits making a modified version and \par \hich\af1\dbch\af31505\loch\f1 letting the public access it on a server without ever releasing its \par \hich\af1\dbch\af31505\loch\f1 source code to the public. \par -\par \hich\af1\dbch\af31505\loch\f1 The GNU Affero General Public License is designed specifically to -\par \hich\af1\dbch\af31505\loch\f1 ensure that, \hich\af1\dbch\af31505\loch\f1 in such cases, the modified source code becomes available +\par \hich\af1\dbch\af31505\loch\f1 The GNU Affero \hich\af1\dbch\af31505\loch\f1 General Public License is designed specifically to +\par \hich\af1\dbch\af31505\loch\f1 ensure that, in such cases, the modified source code becomes available \par \hich\af1\dbch\af31505\loch\f1 to the community. It requires the operator of a network server to -\par \hich\af1\dbch\af31505\loch\f1 provide the source code of the modified version running there to the -\par \hich\af1\dbch\af31505\loch\f1 users of that server. Therefore, public use of a modified ver\hich\af1\dbch\af31505\loch\f1 sion, on +\par \hich\af1\dbch\af31505\loch\f1 provide the source code of the modified version running there to th\hich\af1\dbch\af31505\loch\f1 e +\par \hich\af1\dbch\af31505\loch\f1 users of that server. Therefore, public use of a modified version, on \par \hich\af1\dbch\af31505\loch\f1 a publicly accessible server, gives the public access to the source \par \hich\af1\dbch\af31505\loch\f1 code of the modified version. \par \par \hich\af1\dbch\af31505\loch\f1 An older license, called the Affero General Public License and -\par \hich\af1\dbch\af31505\loch\f1 published by Affero, was designed to accomplish similar goals. This is -\par \hich\af1\dbch\af31505\loch\f1 a different\hich\af1\dbch\af31505\loch\f1 license, not a version of the Affero GPL, but Affero has +\par \hich\af1\dbch\af31505\loch\f1 published by Affero\hich\af1\dbch\af31505\loch\f1 , was designed to accomplish similar goals. This is +\par \hich\af1\dbch\af31505\loch\f1 a different license, not a version of the Affero GPL, but Affero has \par \hich\af1\dbch\af31505\loch\f1 released a new version of the Affero GPL which permits relicensing under \par \hich\af1\dbch\af31505\loch\f1 this license. \par -\par \hich\af1\dbch\af31505\loch\f1 The precise terms and conditions for copying, distribution and +\par \hich\af1\dbch\af31505\loch\f1 The precise terms and conditions for copying\hich\af1\dbch\af31505\loch\f1 , distribution and \par \hich\af1\dbch\af31505\loch\f1 modification follow. \par -\par \hich\af1\dbch\af31505\loch\f1 \hich\af1\dbch\af31505\loch\f1 TERMS AND CONDITIONS +\par \hich\af1\dbch\af31505\loch\f1 TERMS AND CONDITIONS \par \par \hich\af1\dbch\af31505\loch\f1 0. Definitions. \par @@ -113,24 +119,24 @@ Normal Table;}}{\*\rsidtbl \rsid11478430\rsid12386343}{\mmathPr\mmathFont34\mbrk \par \hich\af1\dbch\af31505\loch\f1 "Copyright" also means copyright-like laws that apply to other kinds of \par \hich\af1\dbch\af31505\loch\f1 works, such as semiconductor masks. \par -\par \hich\af1\dbch\af31505\loch\f1 "The Program" refers to any copyrightable work licensed under this +\par \hich\af1\dbch\af31505\loch\f1 "The Program" refers to any copyrightable work licensed under t\hich\af1\dbch\af31505\loch\f1 his \par \hich\af1\dbch\af31505\loch\f1 License. Each licensee is addressed as "you". "Licensees" and -\par \hich\af1\dbch\af31505\loch\f1 "recipients"\hich\af1\dbch\af31505\loch\f1 may be individuals or organizations. +\par \hich\af1\dbch\af31505\loch\f1 "recipients" may be individuals or organizations. \par \par \hich\af1\dbch\af31505\loch\f1 To "modify" a work means to copy from or adapt all or part of the work -\par \hich\af1\dbch\af31505\loch\f1 in a fashion requiring copyright permission, other than the making of an +\par \hich\af1\dbch\af31505\loch\f1 in a fashion requiring copyright permission, other than the maki\hich\af1\dbch\af31505\loch\f1 ng of an \par \hich\af1\dbch\af31505\loch\f1 exact copy. The resulting work is called a "modified version" of the -\par \hich\af1\dbch\af31505\loch\f1 e\hich\af1\dbch\af31505\loch\f1 arlier work or a work "based on" the earlier work. +\par \hich\af1\dbch\af31505\loch\f1 earlier work or a work "based on" the earlier work. \par \par \hich\af1\dbch\af31505\loch\f1 A "covered work" means either the unmodified Program or a work based \par \hich\af1\dbch\af31505\loch\f1 on the Program. \par -\par \hich\af1\dbch\af31505\loch\f1 To "propagate" a work means to do anything with it that, without -\par \hich\af1\dbch\af31505\loch\f1 permission, would make you directly or secondaril\hich\af1\dbch\af31505\loch\f1 y liable for +\par \hich\af1\dbch\af31505\loch\f1 To "propagate" a work means to do \hich\af1\dbch\af31505\loch\f1 anything with it that, without +\par \hich\af1\dbch\af31505\loch\f1 permission, would make you directly or secondarily liable for \par \hich\af1\dbch\af31505\loch\f1 infringement under applicable copyright law, except executing it on a \par \hich\af1\dbch\af31505\loch\f1 computer or modifying a private copy. Propagation includes copying, -\par \hich\af1\dbch\af31505\loch\f1 distribution (with or without modification), making available to the -\par \hich\af1\dbch\af31505\loch\f1 public, and in some countries other\hich\af1\dbch\af31505\loch\f1 activities as well. +\par \hich\af1\dbch\af31505\loch\f1 distribution (with or wi\hich\af1\dbch\af31505\loch\f1 thout modification), making available to the +\par \hich\af1\dbch\af31505\loch\f1 public, and in some countries other activities as well. \par \par \hich\af1\dbch\af31505\loch\f1 To "convey" a work means any kind of propagation that enables other \par \hich\af1\dbch\af31505\loch\f1 parties to make or receive copies. Mere interaction with a user through @@ -476,7 +482,7 @@ Normal Table;}}{\*\rsidtbl \rsid11478430\rsid12386343}{\mmathPr\mmathFont34\mbrk \par \hich\af1\dbch\af31505\loch\f1 to receive a copy likewise does not require acceptance. However, \par \hich\af1\dbch\af31505\loch\f1 nothing other than this Licens\hich\af1\dbch\af31505\loch\f1 e grants you permission to propagate or \par \hich\af1\dbch\af31505\loch\f1 modify any covered work. These actions infringe copyright if you do -\par }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid12386343\charrsid12386343 \hich\af1\dbch\af31505\loch\f1 not accept this License. Therefore, by modifying or propagating a +\par }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2070\langnp1033\insrsid12386343\charrsid11416405 \hich\af1\dbch\af31505\loch\f1 not accept this License. Therefore, by modifying or propagating a \par \hich\af1\dbch\af31505\loch\f1 covered work, you indicate your acceptance of this License to do so. \par \par \hich\af1\dbch\af31505\loch\f1 10. Auto\hich\af1\dbch\af31505\loch\f1 matic Licensing of Downstream Recipients. @@ -540,32 +546,32 @@ Normal Table;}}{\*\rsidtbl \rsid11478430\rsid12386343}{\mmathPr\mmathFont34\mbrk \par \hich\af1\dbch\af31505\loch\f1 available, or (2) arrange to deprive yourself of the benefit of the \par \hich\af1\dbch\af31505\loch\f1 patent license for this particular work, or (3) arrange, in a manner \par \hich\af1\dbch\af31505\loch\f1 consistent with the requirements of this License, to extend the patent -\par \hich\af1\dbch\af31505\loch\f1 license to downstream recipients. "Knowingly relying" means you have +\par \hich\af1\dbch\af31505\loch\f1 license to downstream \hich\af1\dbch\af31505\loch\f1 recipients. "Knowingly relying" means you have \par \hich\af1\dbch\af31505\loch\f1 actual knowledge that, but for the patent license, your conveying the \par \hich\af1\dbch\af31505\loch\f1 covered work in a country, or your recipient's use of the covered work -\par \hich\af1\dbch\af31505\loch\f1 in a country, would infringe one or more iden\hich\af1\dbch\af31505\loch\f1 tifiable patents in that +\par \hich\af1\dbch\af31505\loch\f1 in a country, would infringe one or more identifiable patents in th\hich\af1\dbch\af31505\loch\f1 at \par \hich\af1\dbch\af31505\loch\f1 country that you have reason to believe are valid. \par \par \hich\af1\dbch\af31505\loch\f1 If, pursuant to or in connection with a single transaction or \par \hich\af1\dbch\af31505\loch\f1 arrangement, you convey, or propagate by procuring conveyance of, a -\par \hich\af1\dbch\af31505\loch\f1 covered work, and grant a patent license to som\hich\af1\dbch\af31505\loch\f1 e of the parties -\par \hich\af1\dbch\af31505\loch\f1 receiving the covered work authorizing them to use, propagate, modify +\par \hich\af1\dbch\af31505\loch\f1 covered work, and grant a patent license to some of the parties +\par \hich\af1\dbch\af31505\loch\f1 recei\hich\af1\dbch\af31505\loch\f1 ving the covered work authorizing them to use, propagate, modify \par \hich\af1\dbch\af31505\loch\f1 or convey a specific copy of the covered work, then the patent license \par \hich\af1\dbch\af31505\loch\f1 you grant is automatically extended to all recipients of the covered \par \hich\af1\dbch\af31505\loch\f1 work and works based on it. \par -\par \hich\af1\dbch\af31505\loch\f1 A patent license is "discriminatory" if it does not include within +\par \hich\af1\dbch\af31505\loch\f1 A patent license is \hich\af1\dbch\af31505\loch\f1 "discriminatory" if it does not include within \par \hich\af1\dbch\af31505\loch\f1 the scope of its coverage, prohibits the exercise of, or is \par \hich\af1\dbch\af31505\loch\f1 conditioned on the non-exercise of one or more of the rights that are -\par \hich\af1\dbch\af31505\loch\f1 specifically granted under this License. You may not con\hich\af1\dbch\af31505\loch\f1 vey a covered -\par \hich\af1\dbch\af31505\loch\f1 work if you are a party to an arrangement with a third party that is +\par \hich\af1\dbch\af31505\loch\f1 specifically granted under this License. You may not convey a covered +\par \hich\af1\dbch\af31505\loch\f1 work if \hich\af1\dbch\af31505\loch\f1 you are a party to an arrangement with a third party that is \par \hich\af1\dbch\af31505\loch\f1 in the business of distributing software, under which you make payment \par \hich\af1\dbch\af31505\loch\f1 to the third party based on the extent of your activity of conveying -\par \hich\af1\dbch\af31505\loch\f1 the work, and under which the thi\hich\af1\dbch\af31505\loch\f1 rd party grants, to any of the +\par \hich\af1\dbch\af31505\loch\f1 the work, and under which the third party grants, to an\hich\af1\dbch\af31505\loch\f1 y of the \par \hich\af1\dbch\af31505\loch\f1 parties who would receive the covered work from you, a discriminatory \par \hich\af1\dbch\af31505\loch\f1 patent license (a) in connection with copies of the covered work \par \hich\af1\dbch\af31505\loch\f1 conveyed by you (or copies made from those copies), or (b) primarily -\par \hich\af1\dbch\af31505\loch\f1 for and in connection\hich\af1\dbch\af31505\loch\f1 with specific products or compilations that +\par \hich\af1\dbch\af31505\loch\f1 for and in connection with specific product\hich\af1\dbch\af31505\loch\f1 s or compilations that \par \hich\af1\dbch\af31505\loch\f1 contain the covered work, unless you entered into that arrangement, \par \hich\af1\dbch\af31505\loch\f1 or that patent license was granted, prior to 28 March 2007. \par @@ -706,7 +712,9 @@ Normal Table;}}{\*\rsidtbl \rsid11478430\rsid12386343}{\mmathPr\mmathFont34\mbrk \par \hich\af1\dbch\af31505\loch\f1 You should also get your employer (if you work as a programmer) or school, \par \hich\af1\dbch\af31505\loch\f1 if any, to sign a "copyright disclaimer" for the program, if necessary. \par \hich\af1\dbch\af31505\loch\f1 For m\hich\af1\dbch\af31505\loch\f1 ore information on this, and how to apply and follow the GNU AGPL, see -\par \hich\af1\dbch\af31505\loch\f1 .}{\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid11478430\charrsid12386343 +\par \hich\af1\dbch\af31505\loch\f1 <}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2070\langnp1033\insrsid12386343\charrsid6292107 \hich\af1\dbch\af31505\loch\f1 https://www.g\hich\af1\dbch\af31505\loch\f1 nu.org/licenses}{\rtlch\fcs1 +\af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2070\langnp1033\insrsid11416405\charrsid6292107 \hich\af1\dbch\af31505\loch\f1 /}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang1033\langfe2070\langnp1033\insrsid12386343\charrsid11416405 +\hich\af1\dbch\af31505\loch\f1 >.}{\rtlch\fcs1 \af0 \ltrch\fcs0 \lang1033\langfe2070\langnp1033\insrsid11478430\charrsid11416405 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 @@ -791,35 +799,25 @@ d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995 \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text; -\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; -\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; -\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; -\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; -\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; -\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; -\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; -\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; -\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; -\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; -\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; -\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; -\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; -\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; -\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; -\lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; +\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; +\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; +\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; +\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; +\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; \lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; @@ -861,8 +859,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e50000000000000000000000004016 -b0f74b47d601feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000c0f7 +b1ee6289d701feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/UVtools.Installer/UVtools.Installer.wixproj b/UVtools.Installer/UVtools.Installer.wixproj index f342739..ccf01d4 100644 --- a/UVtools.Installer/UVtools.Installer.wixproj +++ b/UVtools.Installer/UVtools.Installer.wixproj @@ -1,7 +1,7 @@  Debug - x86 + x64 3.7 {41224896-08e9-4f22-9e56-5f9d46dec7d2} 2.0 diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wixproj b/UVtools.InstallerMM/UVtools.InstallerMM.wixproj index d308515..8223831 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wixproj +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wixproj @@ -1,7 +1,7 @@  Debug - x86 + x64 3.7 {e53baa5d-29a8-4287-b3aa-1aff5a4bdc6c} 2.0 diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index 7cd6c1e..e762ac3 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -4,851 +4,854 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -858,22 +861,22 @@ - + - + - + - + - + - + diff --git a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs index 1280131..1511af6 100644 --- a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs +++ b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs @@ -128,7 +128,7 @@ namespace UVtools.ScriptSample if (InputDoNotUseLift.Value) { - SlicerFile.LayerManager.SetNoLiftForSamePositionedLayers(); + SlicerFile.LayerManager.SetLiftForSamePositionedLayers(SlicerFile.SupportsGCode ? 0 : 0.1f); } Progress++; diff --git a/UVtools.WPF/Assets/Icons/balance-scale-16x16.png b/UVtools.WPF/Assets/Icons/balance-scale-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..f740ea029a7c57e79e882c83ad762a56d6d31f8f GIT binary patch literal 224 zcmV<603ZK}P)?^`7aG)Se<$@`J;s8bjD9&=hnF7KH zr}B%2)dhw{j-7HY(gk&a1(BZ9Sasvpnl`U!ZM$=0RReVN8&FjjsI~rT=O2+A(0FVdQ&MBb@0MW%8 ABLDyZ literal 0 HcmV?d00001 diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs index f8cb3cd..b19b633 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs @@ -1,5 +1,4 @@ using System.Timers; -using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml new file mode 100644 index 0000000..f734917 --- /dev/null +++ b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs new file mode 100644 index 0000000..2e0a78d --- /dev/null +++ b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs @@ -0,0 +1,75 @@ +using System.Timers; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media.Imaging; +using Avalonia.Threading; +using UVtools.Core.Operations; +using UVtools.WPF.Controls.Tools; +using UVtools.WPF.Extensions; +using UVtools.WPF.Windows; + +namespace UVtools.WPF.Controls.Calibrators +{ + public class CalibrateLiftHeightControl : ToolControl + { + public OperationCalibrateLiftHeight Operation => BaseOperation as OperationCalibrateLiftHeight; + + private readonly Timer _timer; + + private Bitmap _previewImage; + public Bitmap PreviewImage + { + get => _previewImage; + set => RaiseAndSetIfChanged(ref _previewImage, value); + } + + public CalibrateLiftHeightControl() + { + BaseOperation = new OperationCalibrateLiftHeight(SlicerFile); + if (!ValidateSpawn()) return; + + InitializeComponent(); + + + _timer = new Timer(20) + { + AutoReset = false + }; + _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public override void Callback(ToolWindow.Callbacks callback) + { + if (App.SlicerFile is null) return; + switch (callback) + { + case ToolWindow.Callbacks.Init: + case ToolWindow.Callbacks.Loaded: + Operation.PropertyChanged += (sender, e) => + { + _timer.Stop(); + _timer.Start(); + }; + _timer.Stop(); + _timer.Start(); + break; + } + } + + public void UpdatePreview() + { + var layers = Operation.GetLayers(); + _previewImage?.Dispose(); + PreviewImage = layers[0].ToBitmap(); + foreach (var layer in layers) + { + layer.Dispose(); + } + } + } +} diff --git a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml index dc56f52..906159a 100644 --- a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml @@ -6,10 +6,10 @@ x:Class="UVtools.WPF.Controls.Tools.ToolFlipControl"> - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs new file mode 100644 index 0000000..08f5df5 --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs @@ -0,0 +1,34 @@ +using System.IO; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using UVtools.Core.Operations; + +namespace UVtools.WPF.Controls.Tools +{ + public partial class ToolLayerExportImageControl : ToolControl + { + public OperationLayerExportImage Operation => BaseOperation as OperationLayerExportImage; + public ToolLayerExportImageControl() + { + BaseOperation = new OperationLayerExportImage(SlicerFile); + if (!ValidateSpawn()) return; + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public async void ChooseFolder() + { + var dialog = new OpenFolderDialog + { + Directory = Path.GetDirectoryName(SlicerFile.FileFullPath), + }; + var folder = await dialog.ShowAsync(ParentWindow); + if (string.IsNullOrWhiteSpace(folder)) return; + Operation.OutputFolder = folder; + } + } +} diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 22a966e..cdbc8de 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -21,7 +21,6 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Avalonia.Controls.Primitives; using UVtools.AvaloniaControls; using UVtools.Core; using UVtools.Core.Extensions; @@ -292,6 +291,14 @@ namespace UVtools.WPF } }, new() + { + Tag = new OperationCalibrateLiftHeight(), + Icon = new Avalonia.Controls.Image + { + Source = new Bitmap(App.GetAsset("/Assets/Icons/long-arrow-alt-up-16x16.png")) + } + }, + new() { Tag = new OperationCalibrateTolerance(), Icon = new Avalonia.Controls.Image @@ -352,6 +359,14 @@ namespace UVtools.WPF } }, new() + { + Tag = new OperationLayerExportImage(), + Icon = new Avalonia.Controls.Image + { + Source = new Bitmap(App.GetAsset("/Assets/Icons/file-image-16x16.png")) + } + }, + new() { Tag = new OperationLayerExportGif(), Icon = new Avalonia.Controls.Image @@ -1245,7 +1260,7 @@ namespace UVtools.WPF _showLayerImageRotated = mat.Height > mat.Width; } - if (SlicerFile.MirrorDisplay) + if (SlicerFile.DisplayMirror) { _showLayerImageFlipped = true; } diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs index 5372361..479e196 100644 --- a/UVtools.WPF/Structures/OperationProfiles.cs +++ b/UVtools.WPF/Structures/OperationProfiles.cs @@ -47,6 +47,7 @@ namespace UVtools.WPF.Structures [XmlElement(typeof(OperationCalibrateExposureFinder))] [XmlElement(typeof(OperationCalibrateElephantFoot))] [XmlElement(typeof(OperationCalibrateXYZAccuracy))] + [XmlElement(typeof(OperationCalibrateLiftHeight))] [XmlElement(typeof(OperationCalibrateTolerance))] [XmlElement(typeof(OperationCalibrateGrayscale))] [XmlElement(typeof(OperationCalibrateStressTower))] diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 88b3e89..35bb601 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ LICENSE https://github.com/sn4k3/UVtools Git - 2.16.0 + 2.17.0 @@ -59,6 +59,7 @@ True + PreserveNewest diff --git a/UVtools.WPF/Windows/AboutWindow.axaml b/UVtools.WPF/Windows/AboutWindow.axaml index 9d71d9c..654dae7 100644 --- a/UVtools.WPF/Windows/AboutWindow.axaml +++ b/UVtools.WPF/Windows/AboutWindow.axaml @@ -75,7 +75,7 @@ @@ -83,7 +83,15 @@ - + + + + + + + diff --git a/UVtools.WPF/Windows/AboutWindow.axaml.cs b/UVtools.WPF/Windows/AboutWindow.axaml.cs index b8c5f8a..51cbcf1 100644 --- a/UVtools.WPF/Windows/AboutWindow.axaml.cs +++ b/UVtools.WPF/Windows/AboutWindow.axaml.cs @@ -5,7 +5,6 @@ using System.Text.RegularExpressions; using Avalonia; using Avalonia.Markup.Xaml; using Emgu.CV; -using Emgu.CV.Stitching; using UVtools.Core; using UVtools.WPF.Controls; @@ -17,6 +16,7 @@ namespace UVtools.WPF.Windows public string Version => $"Version: {App.VersionStr} {RuntimeInformation.ProcessArchitecture}"; public string Copyright => App.AssemblyCopyright; public string Company => App.AssemblyCompany; + public string License => About.License; public string Description => App.AssemblyDescription; public string OSDescription => $"{RuntimeInformation.OSDescription} {RuntimeInformation.OSArchitecture}"; @@ -74,6 +74,8 @@ namespace UVtools.WPF.Windows public void SetOpenCVInformationToClipboard() { Application.Current.Clipboard.SetTextAsync(CvInvoke.BuildInformation); - } + } + + public void OpenLicense() => App.OpenBrowser(About.LicenseUrl); } } diff --git a/build/CreateRelease.WPF.ps1 b/build/CreateRelease.WPF.ps1 index bcce755..4985e72 100644 --- a/build/CreateRelease.WPF.ps1 +++ b/build/CreateRelease.WPF.ps1 @@ -69,7 +69,7 @@ if([string]::IsNullOrWhiteSpace($version)){ # MSI Variables $installers = @("UVtools.InstallerMM", "UVtools.Installer") -$msiSourceFile = "UVtools.Installer\bin\Release\UVtools.msi" +$msiSourceFile = "UVtools.Installer\bin\x64\Release\UVtools.msi" $msbuild = "`"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe`" /t:Build /p:Configuration=$buildWith /p:MSIProductVersion=$version" Write-Output "