From 57f077f444f847e4ca81abe057f1782ee630effd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Wed, 17 Aug 2022 21:23:51 +0100 Subject: [PATCH] v3.6.0 - **File formats:** - (Add) OSF (Vlare Open File Format) - (Fix) CTB Encrypted: Bottom Retract Height for TSMC was constraining incorrectly with the normal total retract height - (Fix) CWS: Only issue `;` command when the exposure is about to happen (#514) - **GCode:** - (Add) Command `CommandWaitSyncDelay` for movement wait sync delay instead of depending on G4 wait command - (Fix) Wrong parsing and set of wait times when using a wait after lift / wait before retract - (Improvement) Auto update: Make sure the download url exists before attempt the download, if not, instead it will prompt for manual download and update - (Improvement) MacOS: Remove `com.apple.quarantine` flag from the auto downloaded files - (Upgrade) .NET from 6.0.7 to 6.0.8 - (Upgrade) AvaloniaUI from 0.10.17 to 0.10.18 --- CHANGELOG.md | 16 +- README.md | 31 +- RELEASE_NOTES.md | 21 +- Scripts/010 Editor/osf.bt | 6 +- .../UVtools.AvaloniaControls.csproj | 2 +- .../Extensions/CompressionExtensions.cs | 181 ++-- UVtools.Core/Extensions/EmguExtensions.cs | 2 +- UVtools.Core/FileFormats/CTBEncryptedFile.cs | 2 +- UVtools.Core/FileFormats/CWSFile.cs | 33 +- UVtools.Core/FileFormats/ChituboxFile.cs | 2 +- UVtools.Core/FileFormats/FileFormat.cs | 36 +- UVtools.Core/FileFormats/JXSFile.cs | 9 +- UVtools.Core/FileFormats/LGSFile.cs | 8 +- UVtools.Core/FileFormats/OSFFile.cs | 778 ++++++++++++------ UVtools.Core/FileFormats/OSLAFile.cs | 2 +- UVtools.Core/FileFormats/UVJFile.cs | 2 +- UVtools.Core/FileFormats/VDTFile.cs | 2 +- UVtools.Core/GCode/GCodeBuilder.cs | 128 ++- UVtools.Core/GCode/GCodeLayer.cs | 2 +- UVtools.Core/Layers/Layer.cs | 9 +- UVtools.Core/Objects/UInt24BigEndian.cs | 9 + UVtools.Core/Slicer/Slicer.cs | 2 +- UVtools.Core/UVtools.Core.csproj | 4 +- UVtools.Installer/Code/Product.wxs | 2 +- UVtools.InstallerMM/UVtools.InstallerMM.wxs | 10 +- UVtools.WPF/MainWindow.axaml.cs | 73 +- UVtools.WPF/Structures/AppVersionChecker.cs | 63 +- UVtools.WPF/UVtools.WPF.csproj | 18 +- .../Windows/MissingInformationWindow.axaml | 1 + 29 files changed, 946 insertions(+), 508 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35c6894c..585c8248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 17/08/2022 - v3.6.0 + +- **File formats:** + - (Add) OSF (Vlare Open File Format) + - (Fix) CTB Encrypted: Bottom Retract Height for TSMC was constraining incorrectly with the normal total retract height + - (Fix) CWS: Only issue `;` command when the exposure is about to happen (#514) +- **GCode:** + - (Add) Command `CommandWaitSyncDelay` for movement wait sync delay instead of depending on G4 wait command + - (Fix) Wrong parsing and set of wait times when using a wait after lift / wait before retract +- (Improvement) Auto update: Make sure the download url exists before attempt the download, if not, instead it will prompt for manual download and update +- (Improvement) MacOS: Remove `com.apple.quarantine` flag from the auto downloaded files +- (Upgrade) .NET from 6.0.7 to 6.0.8 +- (Upgrade) AvaloniaUI from 0.10.17 to 0.10.18 + ## 29/07/2022 - v3.5.6 - **Tools** @@ -7,7 +21,7 @@ - (Add) Able to choose the size midpoint rounding method (#520) - (Fix) Allow to flash alone D03 commands (#520) - (Fix) Correct line thickness to have at least 1px error (#523) - - (Improvement) Layer arithmetic: Use ; to split and start a new arithmetic operation + - (Improvement) Layer arithmetic: Use ; to split and start a new arithmetic operation (#519) - (Add) Cmd: Convert command now allow to pass 'auto' as target type to auto convert specific files, valid for SL1 files configured with FILEFORMAT_xxx (#522) - (Add) GCode: Command to sync and wait for movement completion [Only enabled for cws format] (#514) - (Add) VDT: Transition layer count diff --git a/README.md b/README.md index dcf730a2..c537974c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # UVtools -[![License](https://img.shields.io/github/license/sn4k3/UVtools?style=flat-square)](https://github.com/sn4k3/UVtools/blob/master/LICENSE) -[![GitHub repo size](https://img.shields.io/github/repo-size/sn4k3/UVtools?style=flat-square)](#) -[![Code size](https://img.shields.io/github/languages/code-size/sn4k3/UVtools?style=flat-square)](#) -[![Total code](https://img.shields.io/tokei/lines/github/sn4k3/UVtools?style=flat-square)](#) -[![Nuget](https://img.shields.io/nuget/v/UVtools.Core?style=flat-square)](https://www.nuget.org/packages/UVtools.Core) -[![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) -[![Chocolatey](https://img.shields.io/chocolatey/dt/uvtools?color=brown&label=Chocolatey&style=flat-square)](https://community.chocolatey.org/packages/uvtools) +[![License](https://img.shields.io/github/license/sn4k3/UVtools?style=for-the-badge)](https://github.com/sn4k3/UVtools/blob/master/LICENSE) +[![GitHub repo size](https://img.shields.io/github/repo-size/sn4k3/UVtools?style=for-the-badge)](#) +[![Code size](https://img.shields.io/github/languages/code-size/sn4k3/UVtools?style=for-the-badge)](#) +[![Total code](https://img.shields.io/tokei/lines/github/sn4k3/UVtools?style=for-the-badge)](#) +[![Nuget](https://img.shields.io/nuget/v/UVtools.Core?style=for-the-badge)](https://www.nuget.org/packages/UVtools.Core) +[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/sn4k3/UVtools?include_prereleases&style=for-the-badge)](https://github.com/sn4k3/UVtools/releases) +[![Downloads](https://img.shields.io/github/downloads/sn4k3/UVtools/total?style=for-the-badge)](https://github.com/sn4k3/UVtools/releases) +[![Chocolatey](https://img.shields.io/chocolatey/dt/uvtools?color=brown&label=Chocolatey&style=for-the-badge)](https://community.chocolatey.org/packages/uvtools) +[![GitHub Sponsors](https://img.shields.io/github/sponsors/sn4k3?color=red&style=for-the-badge)](https://github.com/sponsors/sn4k3) ## Download the latest version at: https://github.com/sn4k3/UVtools/releases/latest @@ -106,6 +107,7 @@ But also, i need victims for test subject. Proceed at your own risk! - ZIP (Generic / Phrozen Zip) - VDA.ZIP (Voxeldance Additive) - VDT (Voxeldance Tango) +- OSF (Vlare Open File Format) - UVJ (Zip file format for manual manipulation) - Image files (png, jpg, jpeg, jp2, tif, bmp, pbm, pgm, ras, sr) @@ -339,7 +341,7 @@ Easy to use calls that allow you work with the formats. For more information nav Nuget package: https://www.nuget.org/packages/UVtools.Core -[![Nuget](https://img.shields.io/nuget/v/UVtools.Core?style=flat-square)](https://www.nuget.org/packages/UVtools.Core) +[![Nuget](https://img.shields.io/nuget/v/UVtools.Core?style=for-the-badge)](https://www.nuget.org/packages/UVtools.Core) ```powershell dotnet add package UVtools.Core @@ -376,13 +378,12 @@ The fastest way to compile the project is by run the `build/compile.bat`, howeve # Support my work / Donate All my work here is given for free (OpenSource), it took some hours to build, test and polish the program. -If you're happy to contribute for a better program and for my work i will appreciate the tip. - -- Sponsor: https://github.com/sponsors/sn4k3 -- PayPal: https://paypal.me/SkillTournament +If you're happy to contribute for a better program and for my work i will appreciate the tip. +Use one of the following methods: +[![GitHub Sponsors](https://img.shields.io/badge/Donate-Sponsor-red?style=for-the-badge)](https://github.com/sponsors/sn4k3) +[![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-blue?style=for-the-badge)](https://paypal.me/SkillTournament) # Contributors - +[![GitHub contributors](https://img.shields.io/github/contributors/sn4k3/UVtools?style=for-the-badge)](https://github.com/sn4k3/UVtools/graphs/contributors) [![Contributors](https://contrib.rocks/image?repo=sn4k3/UVtools)](https://github.com/sn4k3/UVtools/graphs/contributors) - diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9fe95e0f..73c7297e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,11 +1,12 @@ -- **Tools** - - **PCB Exposure:** - - (Add) Able to choose the size midpoint rounding method (#520) - - (Fix) Allow to flash alone D03 commands (#520) - - (Fix) Correct line thickness to have at least 1px error (#523) - - (Improvement) Layer arithmetic: Use ; to split and start a new arithmetic operation -- (Add) Cmd: Convert command now allow to pass 'auto' as target type to auto convert specific files, valid for SL1 files configured with FILEFORMAT_xxx (#522) -- (Add) GCode: Command to sync and wait for movement completion [Only enabled for cws format] (#514) -- (Add) VDT: Transition layer count -- (Upgrade) AvaloniaUI from 0.10.16 to 0.10.17 +- **File formats:** + - (Add) OSF (Vlare Open File Format) + - (Fix) CTB Encrypted: Bottom Retract Height for TSMC was constraining incorrectly with the normal total retract height + - (Fix) CWS: Only issue `;` command when the exposure is about to happen (#514) +- **GCode:** + - (Add) Command `CommandWaitSyncDelay` for movement wait sync delay instead of depending on G4 wait command + - (Fix) Wrong parsing and set of wait times when using a wait after lift / wait before retract +- (Improvement) Auto update: Make sure the download url exists before attempt the download, if not, instead it will prompt for manual download and update +- (Improvement) MacOS: Remove `com.apple.quarantine` flag from the auto downloaded files +- (Upgrade) .NET from 6.0.7 to 6.0.8 +- (Upgrade) AvaloniaUI from 0.10.17 to 0.10.18 diff --git a/Scripts/010 Editor/osf.bt b/Scripts/010 Editor/osf.bt index bbe1cc1c..c8b96380 100644 --- a/Scripts/010 Editor/osf.bt +++ b/Scripts/010 Editor/osf.bt @@ -98,7 +98,11 @@ struct HEADER { ushort RetractSpeedEnd ; // 7 ubyte RetractDecelerationChange ; // 5 - ubyte Unknown[8] ; + ushort BottomWaitTimeAfterCureMagnified100Times ; // s * 100 + ushort BottomWaitTimeAfterLiftMagnified100Times ; // s * 100 + ushort BottomWaitTimeBeforeCureMagnified100Times ; // s * 100 + + ushort Reserved ; ubyte ProtocolType ; // 0 } header; diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj index a47c56ea..d7f6add7 100644 --- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj +++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj @@ -38,7 +38,7 @@ - + diff --git a/UVtools.Core/Extensions/CompressionExtensions.cs b/UVtools.Core/Extensions/CompressionExtensions.cs index 210c0e22..d21de95f 100644 --- a/UVtools.Core/Extensions/CompressionExtensions.cs +++ b/UVtools.Core/Extensions/CompressionExtensions.cs @@ -10,123 +10,122 @@ using System.IO; using System.IO.Compression; using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance; +using CommunityToolkit.HighPerformance; -namespace UVtools.Core.Extensions +namespace UVtools.Core.Extensions; + +public static class CompressionExtensions { - public static class CompressionExtensions + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetGzipUncompressedLength(ReadOnlyMemory compressedData) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetGzipUncompressedLength(ReadOnlyMemory compressedData) - { - return BitConverter.ToInt32(compressedData.Slice(compressedData.Length - 4, 4).Span); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetGzipUncompressedLength(Stream stream) - { - Span uncompressedLength = stackalloc byte[4]; - stream.Position = stream.Length - 4; - stream.Read(uncompressedLength); - stream.Seek(0, SeekOrigin.Begin); - return BitConverter.ToInt32(uncompressedLength); - } + return BitConverter.ToInt32(compressedData.Slice(compressedData.Length - 4, 4).Span); + } - public static MemoryStream GZipCompress(Stream inputStream, CompressionLevel compressionLevel, bool leaveStreamOpen = false) - { - if (inputStream.Position == inputStream.Length) { inputStream.Seek(0, SeekOrigin.Begin); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetGzipUncompressedLength(Stream stream) + { + Span uncompressedLength = stackalloc byte[4]; + stream.Position = stream.Length - 4; + stream.Read(uncompressedLength); + stream.Seek(0, SeekOrigin.Begin); + return BitConverter.ToInt32(uncompressedLength); + } - var compressedStream = new MemoryStream(); - using (var gzipStream = new GZipStream(compressedStream, compressionLevel, true)) - { - inputStream.CopyTo(gzipStream); - } - if (!leaveStreamOpen) { inputStream.Close(); } + public static MemoryStream GZipCompress(Stream inputStream, CompressionLevel compressionLevel, bool leaveStreamOpen = false) + { + if (inputStream.Position == inputStream.Length) { inputStream.Seek(0, SeekOrigin.Begin); } - compressedStream.Seek(0, SeekOrigin.Begin); - return compressedStream; + var compressedStream = new MemoryStream(); + using (var gzipStream = new GZipStream(compressedStream, compressionLevel, true)) + { + inputStream.CopyTo(gzipStream); } + if (!leaveStreamOpen) { inputStream.Close(); } - public static MemoryStream GZipCompress(byte[] inputStream, CompressionLevel compressionLevel) => - GZipCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); + compressedStream.Seek(0, SeekOrigin.Begin); + return compressedStream; + } - public static byte[] GZipCompressToBytes(byte[] inputStream, CompressionLevel compressionLevel) - { - using var ms = GZipCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); - return ms.ToArray(); - } + public static MemoryStream GZipCompress(byte[] inputStream, CompressionLevel compressionLevel) => + GZipCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); + + public static byte[] GZipCompressToBytes(byte[] inputStream, CompressionLevel compressionLevel) + { + using var ms = GZipCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); + return ms.ToArray(); + } - public static MemoryStream GZipDecompress(Stream compressedStream, bool leaveStreamOpen = false) - { - if (compressedStream.Position == compressedStream.Length) { compressedStream.Seek(0, SeekOrigin.Begin); } + public static MemoryStream GZipDecompress(Stream compressedStream, bool leaveStreamOpen = false) + { + if (compressedStream.Position == compressedStream.Length) { compressedStream.Seek(0, SeekOrigin.Begin); } - var uncompressedStream = new MemoryStream(GetGzipUncompressedLength(compressedStream)); - using var gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress, leaveStreamOpen); - gzipStream.CopyTo(uncompressedStream); + var uncompressedStream = new MemoryStream(GetGzipUncompressedLength(compressedStream)); + using var gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress, leaveStreamOpen); + gzipStream.CopyTo(uncompressedStream); - return uncompressedStream; - } + return uncompressedStream; + } - public static ArraySegment GZipDecompress(ReadOnlyMemory compressedData) + public static ArraySegment GZipDecompress(ReadOnlyMemory compressedData) + { + using var uncompressedStream = new MemoryStream(GetGzipUncompressedLength(compressedData)); + using (var gzipStream = new GZipStream(compressedData.AsStream(), CompressionMode.Decompress, false)) { - using var uncompressedStream = new MemoryStream(GetGzipUncompressedLength(compressedData)); - using (var gzipStream = new GZipStream(compressedData.AsStream(), CompressionMode.Decompress, false)) - { - gzipStream.CopyTo(uncompressedStream); - } - - return uncompressedStream.TryGetBuffer(out var buffer) - ? buffer - : uncompressedStream.ToArray(); + gzipStream.CopyTo(uncompressedStream); } - public static MemoryStream DeflateCompress(Stream inputStream, CompressionLevel compressionLevel, bool leaveStreamOpen = false) - { - if (inputStream.Position == inputStream.Length) { inputStream.Seek(0, SeekOrigin.Begin); } + return uncompressedStream.TryGetBuffer(out var buffer) + ? buffer + : uncompressedStream.ToArray(); + } - var compressedStream = new MemoryStream(); - using (var gzipStream = new DeflateStream(compressedStream, compressionLevel, true)) - { - inputStream.CopyTo(gzipStream); - } - if (!leaveStreamOpen) { inputStream.Close(); } + public static MemoryStream DeflateCompress(Stream inputStream, CompressionLevel compressionLevel, bool leaveStreamOpen = false) + { + if (inputStream.Position == inputStream.Length) { inputStream.Seek(0, SeekOrigin.Begin); } - compressedStream.Seek(0, SeekOrigin.Begin); - return compressedStream; + var compressedStream = new MemoryStream(); + using (var gzipStream = new DeflateStream(compressedStream, compressionLevel, true)) + { + inputStream.CopyTo(gzipStream); } + if (!leaveStreamOpen) { inputStream.Close(); } - public static MemoryStream DeflateCompress(byte[] inputStream, CompressionLevel compressionLevel) => - DeflateCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); + compressedStream.Seek(0, SeekOrigin.Begin); + return compressedStream; + } - public static byte[] DeflateCompressToBytes(byte[] inputStream, CompressionLevel compressionLevel) - { - using var ms = DeflateCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); - return ms.ToArray(); - } + public static MemoryStream DeflateCompress(byte[] inputStream, CompressionLevel compressionLevel) => + DeflateCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); - public static MemoryStream DeflateDecompress(Stream compressedStream, bool leaveStreamOpen = false) - { - if (compressedStream.Position == compressedStream.Length) { compressedStream.Seek(0, SeekOrigin.Begin); } + public static byte[] DeflateCompressToBytes(byte[] inputStream, CompressionLevel compressionLevel) + { + using var ms = DeflateCompress(new ReadOnlyMemory(inputStream).AsStream(), compressionLevel); + return ms.ToArray(); + } - var uncompressedStream = new MemoryStream(); - using var gzipStream = new DeflateStream(compressedStream, CompressionMode.Decompress, leaveStreamOpen); - gzipStream.CopyTo(uncompressedStream); + public static MemoryStream DeflateDecompress(Stream compressedStream, bool leaveStreamOpen = false) + { + if (compressedStream.Position == compressedStream.Length) { compressedStream.Seek(0, SeekOrigin.Begin); } - return uncompressedStream; - } + var uncompressedStream = new MemoryStream(); + using var gzipStream = new DeflateStream(compressedStream, CompressionMode.Decompress, leaveStreamOpen); + gzipStream.CopyTo(uncompressedStream); - public static ArraySegment DeflateDecompress(ReadOnlyMemory compressedData) + return uncompressedStream; + } + + public static ArraySegment DeflateDecompress(ReadOnlyMemory compressedData) + { + using var uncompressedStream = new MemoryStream(); + using (var gzipStream = new DeflateStream(compressedData.AsStream(), CompressionMode.Decompress, false)) { - using var uncompressedStream = new MemoryStream(); - using (var gzipStream = new DeflateStream(compressedData.AsStream(), CompressionMode.Decompress, false)) - { - gzipStream.CopyTo(uncompressedStream); - } - - return uncompressedStream.TryGetBuffer(out var buffer) - ? buffer - : uncompressedStream.ToArray(); + gzipStream.CopyTo(uncompressedStream); } + + return uncompressedStream.TryGetBuffer(out var buffer) + ? buffer + : uncompressedStream.ToArray(); } -} +} \ No newline at end of file diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index bb12f398..abf84de0 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -15,7 +15,7 @@ using System.Drawing; using System.IO; using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance; +using CommunityToolkit.HighPerformance; using UVtools.Core.EmguCV; using UVtools.Core.Objects; diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index b19f122c..0af2e2ff 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -955,7 +955,7 @@ public override float BottomRetractHeight2 get => Settings.BottomRetractHeight2; set { - value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); + value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal); base.BottomRetractHeight2 = Settings.BottomRetractHeight2 = value; } } diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 91417d58..1cf7fc7b 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -570,23 +570,33 @@ public CWSFile() { GCode = new GCodeBuilder { - SyncMovementsWithDelay = true, UseComments = true, GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Relative, GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute, GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds, GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started, + GCodeShowImagePosition = GCodeBuilder.GCodeShowImagePositions.WhenRequired, LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G1, - EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1 + EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1, + CommandSyncMovements = + { + Enabled = true + }, + CommandWaitSyncDelay = + { + Enabled = true, + } }; + GCode.CommandShowImageM6054.Set(";", "{0}"); + GCode.CommandWaitSyncDelay.Set(";", "0{0}"); GCode.CommandWaitG4.Set(";", "{0}"); - GCode.CommandSyncMovements.Enabled = true; + } #endregion #region Methods - + protected override void EncodeInternally(OperationProgress progress) { //var filename = fileFullPath.EndsWith(TemporaryFileAppend) ? Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(fileFullPath)) : Path.GetFileNameWithoutExtension(fileFullPath); @@ -799,7 +809,7 @@ protected override void DecodeInternally(OperationProgress progress) // Must discover png depth grayscale or color if (DecodeType == FileDecodeType.Full && Printer == PrinterType.Unknown) { - var inputFilename = Path.GetFileNameWithoutExtension(FileFullPath)!; + //var inputFilename = Path.GetFileNameWithoutExtension(FileFullPath)!; foreach (var pngEntry in inputFile.Entries) { if (!pngEntry.Name.EndsWith(".png")) continue; @@ -836,6 +846,17 @@ protected override void DecodeInternally(OperationProgress progress) public override void RebuildGCode() { if (!SupportsGCode || SuppressRebuildGCode) return; + + switch (Printer) + { + case PrinterType.Wanhao: + GCode!.CommandWaitSyncDelay.Command = ";"; + break; + default: + GCode!.CommandWaitSyncDelay.Command = ";"; + break; + } + //string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits"; //GCode.Clear(); //GCode.AppendLine($"; {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}"); @@ -981,5 +1002,7 @@ protected override void PartialSaveInternally(OperationProgress progress) //Decode(FileFullPath, progress); } + + #endregion } \ No newline at end of file diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 8c09d8a0..9c78dc7c 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -29,7 +29,7 @@ public class ChituboxFile : FileFormat { #region Constants - public const byte USED_VERSION = 3; // 318570521 + public const byte USED_VERSION = 3; public const uint MAGIC_CBDDLP = 0x12FD0019; // 318570521 public const uint MAGIC_CTB = 0x12FD0086; // 318570630 diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 322cfd15..d1796be7 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -384,11 +384,11 @@ public override string ToString() //new CXDLPv1File(), // Creality Box v1 new CXDLPFile(), // Creality Box new LGSFile(), // LGS, LGS30 - //new OSFFile(), // OSF new FlashForgeSVGXFile(), // SVGX new GenericZIPFile(), // Generic zip files new VDAFile(), // VDA new VDTFile(), // VDT + new OSFFile(), // OSF new UVJFile(), // UVJ new ImageFile(), // images }; @@ -1505,9 +1505,21 @@ public Size Resolution ResolutionX = (uint) value.Width; ResolutionY = (uint) value.Height; RaisePropertyChanged(); + RaisePropertyChanged(nameof(DisplayAspectRatio)); + RaisePropertyChanged(nameof(DisplayAspectRatioStr)); RaisePropertyChanged(nameof(Xppmm)); RaisePropertyChanged(nameof(Yppmm)); RaisePropertyChanged(nameof(Ppmm)); + RaisePropertyChanged(nameof(PpmmMax)); + RaisePropertyChanged(nameof(PixelSizeMicrons)); + RaisePropertyChanged(nameof(PixelArea)); + RaisePropertyChanged(nameof(PixelAreaMicrons)); + RaisePropertyChanged(nameof(PixelHeight)); + RaisePropertyChanged(nameof(PixelHeightMicrons)); + RaisePropertyChanged(nameof(PixelSize)); + RaisePropertyChanged(nameof(PixelSizeMax)); + RaisePropertyChanged(nameof(PixelWidth)); + RaisePropertyChanged(nameof(PixelWidthMicrons)); } } @@ -1534,9 +1546,25 @@ public SizeF Display get => new(DisplayWidth, DisplayHeight); set { - DisplayWidth = value.Width; - DisplayHeight = value.Height; + DisplayWidth = (float)Math.Round(value.Width, 4); + DisplayHeight = (float)Math.Round(value.Height, 4); RaisePropertyChanged(); + RaisePropertyChanged(nameof(DisplayAspectRatio)); + RaisePropertyChanged(nameof(DisplayAspectRatioStr)); + RaisePropertyChanged(nameof(DisplayDiagonal)); + RaisePropertyChanged(nameof(DisplayDiagonalInches)); + RaisePropertyChanged(nameof(Xppmm)); + RaisePropertyChanged(nameof(Yppmm)); + RaisePropertyChanged(nameof(Ppmm)); + RaisePropertyChanged(nameof(PpmmMax)); + RaisePropertyChanged(nameof(PixelSizeMicrons)); + RaisePropertyChanged(nameof(PixelArea)); + RaisePropertyChanged(nameof(PixelAreaMicrons)); + RaisePropertyChanged(nameof(PixelHeight)); + RaisePropertyChanged(nameof(PixelHeightMicrons)); + RaisePropertyChanged(nameof(PixelSize)); + RaisePropertyChanged(nameof(PixelSizeMax)); + RaisePropertyChanged(nameof(PixelWidth)); } } @@ -5266,7 +5294,7 @@ public void Reallocate(uint newLayerCount, bool initBlack = false) if (differenceLayerCount > 0 && initBlack) { - using var blackMat = EmguExtensions.InitMat(Resolution); + using var blackMat = CreateMat(false); var pngBytes = blackMat.GetPngByes(); for (var layerIndex = oldLayerCount; layerIndex < newLayerCount; layerIndex++) { diff --git a/UVtools.Core/FileFormats/JXSFile.cs b/UVtools.Core/FileFormats/JXSFile.cs index 27631c1a..a9a95579 100644 --- a/UVtools.Core/FileFormats/JXSFile.cs +++ b/UVtools.Core/FileFormats/JXSFile.cs @@ -327,7 +327,10 @@ public JXSFile() GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenamePng0Started, LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0, EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G0, - SyncMovementsWithDelay = true + CommandWaitSyncDelay = + { + Enabled = true + } }; } #endregion @@ -528,7 +531,7 @@ protected override void DecodeInternally(OperationProgress progress) { case "gcode": lastCommand = value; - if (value.StartsWith("M106 S") && value.StartsWith("M106 S0")) GCode.AppendWaitG4(0); + if (value.StartsWith("M106 S0")) GCode.AppendWaitG4(0); GCode.AppendLine(value); break; case "slice": @@ -545,7 +548,7 @@ protected override void DecodeInternally(OperationProgress progress) if (lastCommand.StartsWith("G0") || lastCommand.StartsWith("G1")) { lastCommand = $"G4 0{value}"; - GCode.AppendWaitG4($"0{value}"); + GCode.AppendWaitSyncDelay(value); break; } GCode.AppendWaitG4(value); diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index c4da12a0..9e40f47f 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -263,10 +263,10 @@ public Mat Decode(bool consumeRle = true) public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { - new (typeof(LGSFile), "lgs", "Longer Orange 10"), - new (typeof(LGSFile), "lgs30", "Longer Orange 30"), - new (typeof(LGSFile), "lgs120", "Longer Orange 120"), - new (typeof(LGSFile), "lgs4k", "Longer Orange 4k"), + new (typeof(LGSFile), "lgs", "Longer Orange 10 (LGS)"), + new (typeof(LGSFile), "lgs30", "Longer Orange 30 (LGS30)"), + new (typeof(LGSFile), "lgs120", "Longer Orange 120 (LGS120)"), + new (typeof(LGSFile), "lgs4k", "Longer Orange 4k (LGS4K)"), }; public override PrintParameterModifier[]? PrintParameterModifiers { get; } = diff --git a/UVtools.Core/FileFormats/OSFFile.cs b/UVtools.Core/FileFormats/OSFFile.cs index 2fb3f7f0..a7ddde58 100644 --- a/UVtools.Core/FileFormats/OSFFile.cs +++ b/UVtools.Core/FileFormats/OSFFile.cs @@ -8,14 +8,12 @@ using BinarySerialization; using Emgu.CV; -using Emgu.CV.CvEnum; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Threading.Tasks; -using UVtools.Core.Converters; using UVtools.Core.Extensions; using UVtools.Core.Layers; using UVtools.Core.Objects; @@ -25,22 +23,37 @@ namespace UVtools.Core.FileFormats; public class OSFFile : FileFormat { + #region Constants + public const ushort USED_VERSION = 4; + #endregion #region Sub Classes - + #region Header - public class Header + public class OSFHeader { - [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint HeaderLength { get; set; } = 350001; - [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort Version { get; set; } = 4; - [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public byte ImageLog { get; set; } = 2; + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint HeaderLength { get; set; } = 350001; + + [FieldOrder(1)] + [FieldEndianness(Endianness.Big)] + public ushort Version { get; set; } = USED_VERSION; + + [FieldOrder(2)] + [FieldEndianness(Endianness.Big)] + public byte ImageLog { get; set; } = 2; /// /// 148 * 80 * 2 /// - //[FieldOrder(3)] public Uint24BitBigEndian Preview1Length { get; set; } = new(23680); - [FieldOrder(3)] [FieldEndianness(Endianness.Big)] [FieldBitLength(24)] public uint Preview1Length { get; set; } = 23680; - + //[FieldOrder(3)] public UInt24BigEndian Preview1Length { get; set; } = new(23680); + //[FieldOrder(3)] [FieldCount(nameof(UInt24BigEndian.Value), )] public UInt24BigEndian Preview1sssLength { get; set; } = new(23680); + /*[FieldOrder(3)] + [FieldEndianness(Endianness.Big)] + [FieldBitLength(24)] + public uint Preview1Length { get; set; } = 23680; + */ /// /// RGB565 /// @@ -56,202 +69,258 @@ public class Header /// [FieldOrder(6)] [FieldLength(nameof(Preview2Length))] public byte[] Preview2Data { get; set; }*/ - - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; } - [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; } - [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort PixelUmMagnified100Times { get; set; } + public override string ToString() + { + return $"{nameof(HeaderLength)}: {HeaderLength}, {nameof(Version)}: {Version}, {nameof(ImageLog)}: {ImageLog}"; + } + } + + public sealed class OSFSettings + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; } + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; } + [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort PixelUmMagnified100Times { get; set; } /// /// (0x00 not mirrored, 0x01 X-axis mirroring, 0x02 Y-axis mirroring, 0x03 XY-axis mirroring) /// - [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public byte Mirror { get; set; } - [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public byte BottomLightPWM { get; set; } = DefaultBottomLightPWM; - [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public byte LightPWM { get; set; } = DefaultLightPWM; - [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public byte AntiAliasEnabled { get; set; } - [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public byte DistortionEnabled { get; set; } - [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public byte DelayedExposureActivationEnabled { get; set; } - [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public uint LayerCount { get; set; } - [FieldOrder(20)] [FieldEndianness(Endianness.Big)] public ushort NumberParameterSets { get; set; } = 1; - [FieldOrder(21)] [FieldEndianness(Endianness.Big)] public uint LastLayerIndex { set; get; } - [FieldOrder(22)] [FieldEndianness(Endianness.Big)] public uint LayerHeightUmMagnified100Times { set; get; } - [FieldOrder(23)] [FieldEndianness(Endianness.Big)] public byte BottomLayerCount { set; get; } - [FieldOrder(24)] [FieldEndianness(Endianness.Big)] public uint ExposureTimeMagnified100Times { set; get; } - [FieldOrder(25)] [FieldEndianness(Endianness.Big)] public uint BottomExposureTimeMagnified100Times { set; get; } - [FieldOrder(26)] [FieldEndianness(Endianness.Big)] public uint SupportDelayTimeMagnified100Times { set; get; } - [FieldOrder(27)] [FieldEndianness(Endianness.Big)] public uint BottomSupportDelayTimeMagnified100Times { set; get; } - [FieldOrder(28)] [FieldEndianness(Endianness.Big)] public byte TransitionLayers { set; get; } + [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public byte Mirror { get; set; } + [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public byte BottomLightPWM { get; set; } = DefaultBottomLightPWM; + [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public byte LightPWM { get; set; } = DefaultLightPWM; + [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public bool AntiAliasEnabled { get; set; } + [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public bool DistortionEnabled { get; set; } + [FieldOrder(8)] [FieldEndianness(Endianness.Big)] public bool DelayedExposureActivationEnabled { get; set; } + [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public uint LayerCount { get; set; } + [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort NumberParameterSets { get; set; } = 1; + [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public uint LastLayerIndex { set; get; } + [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian LayerHeightUmMagnified100Times { set; get; } = new(); + [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public byte BottomLayerCount { set; get; } + [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian ExposureTimeMagnified100Times { set; get; } = new((uint) (DefaultExposureTime*1000)); + [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomExposureTimeMagnified100Times { set; get; } = new((uint)(DefaultBottomExposureTime * 1000)); + [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian SupportDelayTimeMagnified100Times { set; get; } = new(50); + [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomSupportDelayTimeMagnified100Times { set; get; } = new(50); + [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public byte TransitionLayerCount { set; get; } + /// /// (0x00 linear transition) /// - [FieldOrder(29)] [FieldEndianness(Endianness.Big)] public byte TransitionType { set; get; } - /* - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint TransitionLayerIntervalTimeDifferenceMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint WaitTimeAfterCureMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint WaitTimeAfterLiftMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint WaitTimeBeforeCureMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint BottomLiftHeightSlowMagnified1000Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint BottomLiftHeightTotalMagnified1000Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint LiftHeightSlowMagnified1000Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint LiftHeightTotalMagnified1000Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint BottomRetractHeightTotalMagnified1000Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint RetractHeightSlowMagnified1000Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint RetractHeightTotalMagnified1000Times { set; get; } + [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public byte TransitionType { set; get; } + + [FieldOrder(20)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian TransitionLayerIntervalTimeDifferenceMagnified100Times { set; get; } = new(); + [FieldOrder(21)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian WaitTimeAfterCureMagnified100Times { set; get; } = new(); + [FieldOrder(22)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian WaitTimeAfterLiftMagnified100Times { set; get; } = new(); + [FieldOrder(23)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian WaitTimeBeforeCureMagnified100Times { set; get; } = new(); + [FieldOrder(24)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomLiftHeightSlowMagnified1000Times { set; get; } = new(); + [FieldOrder(25)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomLiftHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultBottomLiftHeight * 1000)); + [FieldOrder(26)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian LiftHeightSlowMagnified1000Times { set; get; } = new(); + [FieldOrder(27)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian LiftHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultLiftHeight * 1000)); + [FieldOrder(28)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomRetractHeightSlowMagnified1000Times { set; get; } = new(); + [FieldOrder(29)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomRetractHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultBottomLiftHeight * 1000)); + [FieldOrder(30)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian RetractHeightSlowMagnified1000Times { set; get; } = new(); + [FieldOrder(31)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian RetractHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultLiftHeight * 1000)); /// /// (0x00: S-shaped acceleration, 0x01: T-shaped acceleration, Default Value: S-shaped acceleration, currently only supports S-shaped acceleration) /// - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte AccelerationType { set; get; } - - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedStartMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedSlowMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedFastMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte BottomLiftAccelerationChange { set; get; } // 5 - - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedStartMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedSlowMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedFastMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte LiftAccelerationChange { set; get; } // 5 - - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedStartMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedSlowMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractFastMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte BottomRetractAccelerationChange { set; get; } // 5 - - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedStartMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedSlowMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort RetractFastMagnified100Times { set; get; } - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte RetractAccelerationChange { set; get; } // 5 - - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] [FieldCount(23)] public byte[] Reserved { set; get; } = new byte[23]; // 23 - [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte ProtocolType { set; get; } // 0 - */ + [FieldOrder(32)] [FieldEndianness(Endianness.Big)] public byte AccelerationType { set; get; } + + [FieldOrder(33)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedStart { set; get; } = 80; + [FieldOrder(34)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedSlow { set; get; } = (ushort)DefaultBottomLiftSpeed; + [FieldOrder(35)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedFast { set; get; } = (ushort)DefaultBottomLiftSpeed2; + [FieldOrder(36)] [FieldEndianness(Endianness.Big)] public byte BottomLiftAccelerationChange { set; get; } = 5; + + [FieldOrder(37)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedStart { set; get; } = 80; + [FieldOrder(38)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedSlow { set; get; } = (ushort)DefaultLiftSpeed; + [FieldOrder(39)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedFast { set; get; } = (ushort)DefaultLiftSpeed2; + [FieldOrder(40)] [FieldEndianness(Endianness.Big)] public byte LiftAccelerationChange { set; get; } = 5; + + [FieldOrder(41)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedStart { set; get; } = 80; + [FieldOrder(42)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedSlow { set; get; } = (ushort)DefaultBottomRetractSpeed2; + [FieldOrder(43)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedFast { set; get; } = (ushort)DefaultBottomRetractSpeed; + [FieldOrder(44)] [FieldEndianness(Endianness.Big)] public byte BottomRetractAccelerationChange { set; get; } = 5; + + [FieldOrder(45)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedStart { set; get; } = 80; + [FieldOrder(46)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedSlow { set; get; } = (ushort)DefaultRetractSpeed2; + [FieldOrder(47)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedFast { set; get; } = (ushort)DefaultRetractSpeed; + [FieldOrder(48)] [FieldEndianness(Endianness.Big)] public byte RetractAccelerationChange { set; get; } = 5; + + [FieldOrder(49)][FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedEnd { set; get; } = 7; + [FieldOrder(50)][FieldEndianness(Endianness.Big)] public byte BottomLiftDecelerationChange { set; get; } = 5; + + [FieldOrder(51)][FieldEndianness(Endianness.Big)] public ushort LiftSpeedEnd { set; get; } = 7; + [FieldOrder(52)][FieldEndianness(Endianness.Big)] public byte LiftDecelerationChange { set; get; } = 5; + + [FieldOrder(53)][FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedEnd { set; get; } = 7; + [FieldOrder(54)][FieldEndianness(Endianness.Big)] public byte BottomRetractDecelerationChange { set; get; } = 5; + [FieldOrder(55)][FieldEndianness(Endianness.Big)] public ushort RetractSpeedEnd { set; get; } = 7; + [FieldOrder(56)][FieldEndianness(Endianness.Big)] public byte RetractDecelerationChange { set; get; } = 5; + + [FieldOrder(57)][FieldEndianness(Endianness.Big)] public ushort BottomWaitTimeAfterCureMagnified100Times { set; get; } + [FieldOrder(58)][FieldEndianness(Endianness.Big)] public ushort BottomWaitTimeAfterLiftMagnified100Times { set; get; } + [FieldOrder(59)][FieldEndianness(Endianness.Big)] public ushort BottomWaitTimeBeforeCureMagnified100Times { set; get; } + + [FieldOrder(60)] [FieldEndianness(Endianness.Big)] public ushort Reserved { set; get; } + [FieldOrder(61)] [FieldEndianness(Endianness.Big)] public byte ProtocolType { set; get; } // 0 + + public override string ToString() + { + return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PixelUmMagnified100Times)}: {PixelUmMagnified100Times}, {nameof(Mirror)}: {Mirror}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LightPWM)}: {LightPWM}, {nameof(AntiAliasEnabled)}: {AntiAliasEnabled}, {nameof(DistortionEnabled)}: {DistortionEnabled}, {nameof(DelayedExposureActivationEnabled)}: {DelayedExposureActivationEnabled}, {nameof(LayerCount)}: {LayerCount}, {nameof(NumberParameterSets)}: {NumberParameterSets}, {nameof(LastLayerIndex)}: {LastLayerIndex}, {nameof(LayerHeightUmMagnified100Times)}: {LayerHeightUmMagnified100Times}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(ExposureTimeMagnified100Times)}: {ExposureTimeMagnified100Times}, {nameof(BottomExposureTimeMagnified100Times)}: {BottomExposureTimeMagnified100Times}, {nameof(SupportDelayTimeMagnified100Times)}: {SupportDelayTimeMagnified100Times}, {nameof(BottomSupportDelayTimeMagnified100Times)}: {BottomSupportDelayTimeMagnified100Times}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(TransitionType)}: {TransitionType}, {nameof(TransitionLayerIntervalTimeDifferenceMagnified100Times)}: {TransitionLayerIntervalTimeDifferenceMagnified100Times}, {nameof(WaitTimeAfterCureMagnified100Times)}: {WaitTimeAfterCureMagnified100Times}, {nameof(WaitTimeAfterLiftMagnified100Times)}: {WaitTimeAfterLiftMagnified100Times}, {nameof(WaitTimeBeforeCureMagnified100Times)}: {WaitTimeBeforeCureMagnified100Times}, {nameof(BottomLiftHeightSlowMagnified1000Times)}: {BottomLiftHeightSlowMagnified1000Times}, {nameof(BottomLiftHeightTotalMagnified1000Times)}: {BottomLiftHeightTotalMagnified1000Times}, {nameof(LiftHeightSlowMagnified1000Times)}: {LiftHeightSlowMagnified1000Times}, {nameof(LiftHeightTotalMagnified1000Times)}: {LiftHeightTotalMagnified1000Times}, {nameof(BottomRetractHeightSlowMagnified1000Times)}: {BottomRetractHeightSlowMagnified1000Times}, {nameof(BottomRetractHeightTotalMagnified1000Times)}: {BottomRetractHeightTotalMagnified1000Times}, {nameof(RetractHeightSlowMagnified1000Times)}: {RetractHeightSlowMagnified1000Times}, {nameof(RetractHeightTotalMagnified1000Times)}: {RetractHeightTotalMagnified1000Times}, {nameof(AccelerationType)}: {AccelerationType}, {nameof(BottomLiftSpeedStart)}: {BottomLiftSpeedStart}, {nameof(BottomLiftSpeedSlow)}: {BottomLiftSpeedSlow}, {nameof(BottomLiftSpeedFast)}: {BottomLiftSpeedFast}, {nameof(BottomLiftAccelerationChange)}: {BottomLiftAccelerationChange}, {nameof(LiftSpeedStart)}: {LiftSpeedStart}, {nameof(LiftSpeedSlow)}: {LiftSpeedSlow}, {nameof(LiftSpeedFast)}: {LiftSpeedFast}, {nameof(LiftAccelerationChange)}: {LiftAccelerationChange}, {nameof(BottomRetractSpeedStart)}: {BottomRetractSpeedStart}, {nameof(BottomRetractSpeedSlow)}: {BottomRetractSpeedSlow}, {nameof(BottomRetractSpeedFast)}: {BottomRetractSpeedFast}, {nameof(BottomRetractAccelerationChange)}: {BottomRetractAccelerationChange}, {nameof(RetractSpeedStart)}: {RetractSpeedStart}, {nameof(RetractSpeedSlow)}: {RetractSpeedSlow}, {nameof(RetractSpeedFast)}: {RetractSpeedFast}, {nameof(RetractAccelerationChange)}: {RetractAccelerationChange}, {nameof(BottomLiftSpeedEnd)}: {BottomLiftSpeedEnd}, {nameof(BottomLiftDecelerationChange)}: {BottomLiftDecelerationChange}, {nameof(LiftSpeedEnd)}: {LiftSpeedEnd}, {nameof(LiftDecelerationChange)}: {LiftDecelerationChange}, {nameof(BottomRetractSpeedEnd)}: {BottomRetractSpeedEnd}, {nameof(BottomRetractDecelerationChange)}: {BottomRetractDecelerationChange}, {nameof(RetractSpeedEnd)}: {RetractSpeedEnd}, {nameof(RetractDecelerationChange)}: {RetractDecelerationChange}, {nameof(BottomWaitTimeAfterCureMagnified100Times)}: {BottomWaitTimeAfterCureMagnified100Times}, {nameof(BottomWaitTimeAfterLiftMagnified100Times)}: {BottomWaitTimeAfterLiftMagnified100Times}, {nameof(BottomWaitTimeBeforeCureMagnified100Times)}: {BottomWaitTimeBeforeCureMagnified100Times}, {nameof(Reserved)}: {Reserved}, {nameof(ProtocolType)}: {ProtocolType}"; + } } #endregion #region LayerDef - public class LayerDef + public class OSFLayerDef { - [Ignore] public LGSFile Parent { get; set; } = null!; - - [FieldOrder(0)] - public uint DataSize { get; set; } + /// + /// OD OA begins, indicating that the model + support is included; the beginning of 0D 0B, indicating that the layer only has support data + /// + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort Mark { get; set; } = 0x0D_0A; - [FieldOrder(1)] - [FieldLength(nameof(DataSize))] - public byte[] EncodedRle { get; set; } = null!; + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint NumberOfPixels { get; set; } + [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; } + [Ignore] public byte[] EncodedRle { get; set; } = Array.Empty(); - public LayerDef() { } + public OSFLayerDef() { } - public LayerDef(LGSFile parent) + public override string ToString() { - Parent = parent; + return $"{nameof(Mark)}: {Mark}, {nameof(NumberOfPixels)}: {NumberOfPixels}, {nameof(StartY)}: {StartY}"; } - public unsafe byte[] Encode(Mat mat) + internal unsafe void EncodeImage(Mat mat) { List rawData = new(); - List chunk = new(); + byte color = byte.MaxValue >> 1; + uint stride = 0; + var span = mat.GetBytePointer(); + var imageLength = mat.GetLength(); - if (Parent.HeaderSettings.PrinterModel is 4000 or 4500) + void AddRep() { - CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90Clockwise); - } + switch (stride) + { + case 0: + return; + case 1: + color &= 0xfe; + break; + case > 1: + color |= 0x01; + break; + } - var spanMat = mat.GetBytePointer(); - var imageLength = mat.GetLength(); + rawData.Add(color); - uint span = 0; - byte lc = 0; + if (stride <= 1) + { + // no run needed + return; + } - void addSpan(){ - chunk.Clear(); - for (; span > 0; span >>= 4) { - chunk.Insert(0, (byte)((byte)(span & 0xf) | (lc & 0xf0))); + if (stride <= 0x7f) + { + rawData.Add((byte)stride); + return; } - rawData.AddRange(chunk.ToArray()); - } - for (int i = 0; i < imageLength; i++) - { - byte c = (byte) (spanMat[i] & 0xf0); - - if (c == lc) + if (stride <= 0x3fff) { - span++; + rawData.Add((byte)((stride >> 8) | 0x80)); + rawData.Add((byte)stride); + return; } - else + + if (stride <= 0x1fffff) { - addSpan(); - span = 1; + rawData.Add((byte)((stride >> 16) | 0xc0)); + rawData.Add((byte)(stride >> 8)); + rawData.Add((byte)stride); + return; } - lc = c; + if (stride <= 0xfffffff) + { + rawData.Add((byte)((stride >> 24) | 0xe0)); + rawData.Add((byte)(stride >> 16)); + rawData.Add((byte)(stride >> 8)); + rawData.Add((byte)stride); + } } - addSpan(); - EncodedRle = rawData.ToArray(); - DataSize = (uint) EncodedRle.Length; - if (Parent.HeaderSettings.PrinterModel is 4000 or 4500) + for (int pixel = StartY * mat.GetRealStep(); pixel < imageLength; pixel++) { - CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise); + var grey = span[pixel]; + + if (grey == color) + { + stride++; + } + else + { + AddRep(); + color = grey; + stride = 1; + } } - return EncodedRle; + EncodedRle = rawData.ToArray(); } - public Mat Decode(bool consumeRle = true) + internal Mat DecodeImage(OSFFile parent) { - // lgs10/30 --------> - // lgs120/4k From Y bottom to top Y - var mat = EmguExtensions.InitMat(Parent.HeaderSettings.PrinterModel is 4000 or 4500 ? Parent.Resolution.Exchange() : Parent.Resolution); - //var matSpan = mat.GetBytePointer(); - var imageLength = mat.GetLength(); - - int pixelPos = 0; + var mat = parent.CreateMat(); - for (var i = 0; i < EncodedRle.Length; i++) + int pixel = (int)(StartY * parent.ResolutionX); + for (var n = 0; n < EncodedRle.Length; n++) { - var b = EncodedRle[i]; - byte colorNibble = (byte)(b >> 4); - byte color = (byte)(colorNibble << 4 | colorNibble); - int repeat = b & 0xf; + byte code = EncodedRle[n]; + int stride = 1; - while (i + 1 < EncodedRle.Length && (EncodedRle[i + 1] >> 4) == colorNibble) + if ((code & 0x01) == 0x01) // It's a run { - i++; - repeat = (repeat << 4) | (EncodedRle[i] & 0xf); + code &= 0xfe; // Get the grey value + n++; + + var slen = EncodedRle[n]; + + if ((slen & 0x80) == 0) + { + stride = slen; + } + else if ((slen & 0xc0) == 0x80) + { + stride = ((slen & 0x3f) << 8) + EncodedRle[n + 1]; + n++; + } + else if ((slen & 0xe0) == 0xc0) + { + stride = ((slen & 0x1f) << 16) + (EncodedRle[n + 1] << 8) + EncodedRle[n + 2]; + n += 2; + } + else if ((slen & 0xf0) == 0xe0) + { + stride = ((slen & 0xf) << 24) + (EncodedRle[n + 1] << 16) + (EncodedRle[n + 2] << 8) + EncodedRle[n + 3]; + n += 3; + } + else + { + mat.Dispose(); + throw new FileLoadException("Corrupted RLE data"); + } } - if (pixelPos >= imageLength) + // Bit extend from 7-bit to 8-bit greymap + if (code != 0) { - throw new FileLoadException($"Too much buffer, expected: {imageLength}, got: {pixelPos}"); + code = (byte)(code | 1); } - mat.FillSpan(ref pixelPos, repeat, color); - - //if (repeat <= 0) continue; - /*while (repeat-- > 0) - { - matSpan[pixel++] = color; - }*/ - - } - - if (pixelPos != imageLength) - { - throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {pixelPos}"); - } - - if (consumeRle) - EncodedRle = null!; - - if (Parent.HeaderSettings.PrinterModel is 4000 or 4500) - { - CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise); + mat.FillSpan(ref pixel, stride, code); } return mat; @@ -263,28 +332,50 @@ public Mat Decode(bool consumeRle = true) #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); + public OSFHeader Header { get; protected internal set; } = new(); + public OSFSettings Settings { get; protected internal set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { - new (typeof(OSFFile), "osf", "Vlare Open File Format"), + new (typeof(OSFFile), "osf", "Vlare Open File Format (OSF)"), }; public override PrintParameterModifier[]? PrintParameterModifiers { get; } = { PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.TransitionLayerCount, - PrintParameterModifier.BottomLightOffDelay, - PrintParameterModifier.LightOffDelay, - + PrintParameterModifier.BottomWaitTimeBeforeCure, + PrintParameterModifier.WaitTimeBeforeCure, + PrintParameterModifier.BottomExposureTime, PrintParameterModifier.ExposureTime, + PrintParameterModifier.BottomWaitTimeAfterCure, + PrintParameterModifier.WaitTimeAfterCure, + PrintParameterModifier.BottomLiftHeight, PrintParameterModifier.BottomLiftSpeed, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, - + + PrintParameterModifier.BottomLiftHeight2, + PrintParameterModifier.BottomLiftSpeed2, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, + + PrintParameterModifier.BottomWaitTimeAfterLift, + PrintParameterModifier.WaitTimeAfterLift, + + PrintParameterModifier.BottomRetractSpeed, + PrintParameterModifier.RetractSpeed, + PrintParameterModifier.BottomRetractHeight2, + PrintParameterModifier.BottomRetractSpeed2, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM, }; public override Size[]? ThumbnailsOriginalSize { get; } = @@ -295,22 +386,36 @@ public Mat Decode(bool consumeRle = true) new(404, 240), }; + public override uint[] AvailableVersions { get; } = { 4 }; + + public override uint DefaultVersion => USED_VERSION; + + public override uint Version + { + get => Header.Version; + set + { + base.Version = value; + Header.Version = (ushort) base.Version; + } + } + public override uint ResolutionX { - get => HeaderSettings.ResolutionX; + get => Settings.ResolutionX; set { - HeaderSettings.ResolutionX = (ushort) value; + Settings.ResolutionX = (ushort) value; RaisePropertyChanged(); } } public override uint ResolutionY { - get => (uint)HeaderSettings.ResolutionY; + get => Settings.ResolutionY; set { - HeaderSettings.ResolutionY = (ushort)value; + Settings.ResolutionY = (ushort)value; RaisePropertyChanged(); } } @@ -319,7 +424,7 @@ public override uint ResolutionY public override FlipDirection DisplayMirror { - get => HeaderSettings.Mirror switch + get => Settings.Mirror switch { 1 => FlipDirection.Horizontally, 2 => FlipDirection.Vertically, @@ -328,7 +433,7 @@ public override FlipDirection DisplayMirror }; set { - HeaderSettings.Mirror = value switch + Settings.Mirror = value switch { FlipDirection.None => 0, FlipDirection.Horizontally => 1, @@ -341,10 +446,10 @@ public override FlipDirection DisplayMirror public override float LayerHeight { - get => Layer.RoundHeight(HeaderSettings.LayerHeightUmMagnified100Times / 100000f); + get => Layer.RoundHeight(Settings.LayerHeightUmMagnified100Times.Value / 1000_00f); set { - HeaderSettings.LayerHeightUmMagnified100Times = (ushort)(value * 100000); + Settings.LayerHeightUmMagnified100Times.Value = (ushort)(value * 1000_00f); RaisePropertyChanged(); } } @@ -354,197 +459,390 @@ public override uint LayerCount get => base.LayerCount; set { - base.LayerCount = HeaderSettings.LayerCount = base.LayerCount; - HeaderSettings.LastLayerIndex = HeaderSettings.LayerCount - 1; + base.LayerCount = Settings.LayerCount = base.LayerCount; + Settings.LastLayerIndex = Settings.LayerCount - 1; } } public override ushort BottomLayerCount { - get => HeaderSettings.BottomLayerCount; + get => Settings.BottomLayerCount; set { - HeaderSettings.BottomLayerCount = (byte)value; + Settings.BottomLayerCount = (byte)value; base.BottomLayerCount = value; } } - /*public override float BottomLightOffDelay + public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software; + + public override ushort TransitionLayerCount + { + get => Settings.TransitionLayerCount; + set => base.TransitionLayerCount = Settings.TransitionLayerCount = (byte)Math.Min(byte.MaxValue, Math.Min(value, MaximumPossibleTransitionLayerCount)); + } + + public override float BottomLightOffDelay => BottomWaitTimeBeforeCure; + + public override float LightOffDelay => WaitTimeBeforeCure; + + public override float BottomWaitTimeBeforeCure { - get => TimeConverter.MillisecondsToSeconds(HeaderSettings.BottomLightOffDelayMs); + get => (float)Math.Round(Settings.BottomWaitTimeBeforeCureMagnified100Times / 100f, 2); set { - HeaderSettings.BottomLightOffDelayMs = TimeConverter.SecondsToMilliseconds(value); - base.BottomLightOffDelay = value; + Settings.BottomWaitTimeBeforeCureMagnified100Times = (ushort) (value * 100); + base.BottomWaitTimeBeforeCure = (float)Math.Round(value, 2); } } - public override float LightOffDelay + public override float WaitTimeBeforeCure { - get => TimeConverter.MillisecondsToSeconds(HeaderSettings.LightOffDelayMs); + get => (float)Math.Round(Settings.WaitTimeBeforeCureMagnified100Times.Value / 100f, 2); set { - HeaderSettings.LightOffDelayMs = TimeConverter.SecondsToMilliseconds(value); - base.LightOffDelay = value; + Settings.WaitTimeBeforeCureMagnified100Times.Value = (uint)(value * 100); + base.WaitTimeBeforeCure = (float)Math.Round(value, 2); } } - public override float BottomWaitTimeBeforeCure + public override float BottomExposureTime { - get => base.BottomWaitTimeBeforeCure; + get => (float)Math.Round(Settings.BottomExposureTimeMagnified100Times.Value / 100f, 2); set { - SetBottomLightOffDelay(value); - base.BottomWaitTimeBeforeCure = value; + Settings.BottomExposureTimeMagnified100Times.Value = (uint)(value * 100); + base.BottomExposureTime = (float)Math.Round(value, 2); } } - public override float WaitTimeBeforeCure + public override float ExposureTime { - get => base.WaitTimeBeforeCure; + get => (float)Math.Round(Settings.ExposureTimeMagnified100Times.Value / 100f, 2); set { - SetNormalLightOffDelay(value); - base.WaitTimeBeforeCure = value; + Settings.ExposureTimeMagnified100Times.Value = (uint)(value * 100); + base.ExposureTime = (float)Math.Round(value, 2); } } - public override float BottomExposureTime + public override float BottomWaitTimeAfterCure { - get => TimeConverter.MillisecondsToSeconds(HeaderSettings.BottomExposureTimeMs); + get => (float)Math.Round(Settings.BottomWaitTimeAfterCureMagnified100Times / 100f, 2); set { - HeaderSettings.BottomExposureTimeMs = TimeConverter.SecondsToMilliseconds(value); - base.BottomExposureTime = value; + Settings.BottomWaitTimeAfterCureMagnified100Times = (ushort)(value * 100); + base.BottomWaitTimeAfterCure = (float)Math.Round(value, 2); } } - public override float ExposureTime + public override float WaitTimeAfterCure { - get => TimeConverter.MillisecondsToSeconds(HeaderSettings.ExposureTimeMs); + get => (float)Math.Round(Settings.WaitTimeAfterCureMagnified100Times.Value / 100f, 2); set { - HeaderSettings.ExposureTimeMs = TimeConverter.SecondsToMilliseconds(value); - base.ExposureTime = value; + Settings.WaitTimeAfterCureMagnified100Times.Value = (uint)(value * 100); + base.WaitTimeAfterCure = (float)Math.Round(value, 2); } } - + public override float BottomLiftHeight { - get => HeaderSettings.BottomLiftHeight; - set => base.BottomLiftHeight = HeaderSettings.BottomLiftHeight = value; + get => (float)Math.Round(Settings.BottomLiftHeightSlowMagnified1000Times.Value / 1000f, 2); + set + { + value = (float)Math.Round(value, 2); + Settings.BottomLiftHeightTotalMagnified1000Times.Value -= Settings.BottomLiftHeightSlowMagnified1000Times.Value; + Settings.BottomLiftHeightSlowMagnified1000Times.Value = (uint)(value * 1000); + Settings.BottomLiftHeightTotalMagnified1000Times.Value += Settings.BottomLiftHeightSlowMagnified1000Times.Value; + base.BottomLiftHeight = value; + } } public override float LiftHeight { - get => HeaderSettings.LiftHeight; - set => base.LiftHeight = HeaderSettings.LiftHeight = value; + get => (float)Math.Round(Settings.LiftHeightSlowMagnified1000Times.Value / 1000f, 2); + set + { + value = (float)Math.Round(value, 2); + Settings.LiftHeightTotalMagnified1000Times.Value -= Settings.LiftHeightSlowMagnified1000Times.Value; + Settings.LiftHeightSlowMagnified1000Times.Value = (uint)(value * 1000); + Settings.LiftHeightTotalMagnified1000Times.Value += Settings.LiftHeightSlowMagnified1000Times.Value; + base.LiftHeight = value; + } } public override float BottomLiftSpeed { - get => HeaderSettings.BottomLiftSpeed; - set => base.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed_ = value; + get => Settings.BottomLiftSpeedSlow; + set => base.BottomLiftSpeed = Settings.BottomLiftSpeedSlow = (ushort)value; } public override float LiftSpeed { - get => HeaderSettings.LiftSpeed; - set => base.LiftSpeed = HeaderSettings.LiftSpeed = HeaderSettings.LiftSpeed_ = value; + get => Settings.LiftSpeedSlow; + set => base.LiftSpeed = Settings.LiftSpeedSlow = (ushort)value; } - */ - public override float BottomRetractSpeed => RetractSpeed; - public override float RetractSpeed => LiftSpeed; + public override float BottomLiftHeight2 + { + get => (float)Math.Max(0, Math.Round((Settings.BottomLiftHeightTotalMagnified1000Times - Settings.BottomLiftHeightSlowMagnified1000Times) / 1000f, 2)); + set + { + value = (float)Math.Round(value, 2); + Settings.BottomLiftHeightTotalMagnified1000Times.Value = Settings.BottomLiftHeightSlowMagnified1000Times.Value + (uint)(value * 1000); + base.BottomLiftHeight2 = value; + } + } + public override float BottomLiftSpeed2 + { + get =>Settings.BottomLiftSpeedFast; + set => base.BottomLiftSpeed2 = Settings.BottomLiftSpeedFast = (ushort) value; + } - public override object[] Configs => new object[] { HeaderSettings }; + public override float LiftHeight2 + { + get => (float)Math.Max(0, Math.Round((Settings.LiftHeightTotalMagnified1000Times - Settings.LiftHeightSlowMagnified1000Times) / 1000f, 2)); + set + { + value = (float)Math.Round(value, 2); + Settings.LiftHeightTotalMagnified1000Times.Value = Settings.LiftHeightSlowMagnified1000Times.Value + (uint)(value * 1000); + base.LiftHeight2 = value; + } + } - #endregion + public override float LiftSpeed2 + { + get => Settings.LiftSpeedFast; + set => base.LiftSpeed2 = Settings.LiftSpeedFast = (ushort)value; + } - #region Constructors - public OSFFile() + public override float BottomWaitTimeAfterLift { + get => (float)Math.Round(Settings.BottomWaitTimeAfterLiftMagnified100Times / 100f, 2); + set + { + Settings.BottomWaitTimeAfterLiftMagnified100Times = (ushort)(value * 100); + base.BottomWaitTimeAfterLift = (float)Math.Round(value, 2); + } + } + + public override float WaitTimeAfterLift + { + get => (float)Math.Round(Settings.WaitTimeAfterLiftMagnified100Times.Value / 100f, 2); + set + { + Settings.WaitTimeAfterLiftMagnified100Times.Value = (uint)(value * 100); + base.WaitTimeAfterLift = (float)Math.Round(value, 2); + } + } + + public override float BottomRetractSpeed + { + get => Settings.BottomRetractSpeedFast; + set => base.BottomRetractSpeed = Settings.BottomRetractSpeedFast = (ushort)value; + } + + public override float RetractSpeed + { + get => Settings.RetractSpeedFast; + set => base.RetractSpeed = Settings.RetractSpeedFast = (ushort)value; + } + + public override float BottomRetractHeight2 + { + get => (float)Math.Round(Settings.BottomRetractHeightSlowMagnified1000Times.Value / 1000f, 2); + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal); + Settings.BottomRetractHeightSlowMagnified1000Times.Value = (uint)(value * 1000); + Settings.BottomRetractHeightTotalMagnified1000Times.Value = (uint)(BottomRetractHeightTotal * 1000); + base.BottomRetractHeight2 = value; + } + } + + public override float BottomRetractSpeed2 { + get => Settings.BottomRetractSpeedSlow; + set => base.BottomRetractSpeed2 = Settings.BottomRetractSpeedSlow = (ushort)value; } + + public override float RetractHeight2 + { + get => (float)Math.Round(Settings.RetractHeightSlowMagnified1000Times.Value / 1000f, 2); + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); + Settings.RetractHeightSlowMagnified1000Times.Value = (uint)(value * 1000); + Settings.RetractHeightTotalMagnified1000Times.Value = (uint)(RetractHeightTotal * 1000); + base.RetractHeight2 = value; + } + } + + public override float RetractSpeed2 + { + get => Settings.RetractSpeedSlow; + set => base.RetractSpeed2 = Settings.RetractSpeedSlow = (ushort)value; + } + + public override byte BottomLightPWM + { + get => Settings.BottomLightPWM; + set => base.BottomLightPWM = Settings.BottomLightPWM = value; + } + + public override byte LightPWM + { + get => Settings.LightPWM; + set => base.LightPWM = Settings.LightPWM = value; + } + + + public override object[] Configs => new object[] { Settings }; + #endregion - #region Methods + #region Constructors + public OSFFile() { } + #endregion + #region Methods + protected override void EncodeInternally(OperationProgress progress) { using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Create, FileAccess.Write); - outputFile.WriteSerialize(HeaderSettings); - outputFile.WriteBytes(EncodeImage(DATATYPE_RGB565_BE, Thumbnails[0]!)); + + Settings.PixelUmMagnified100Times = (ushort)(PixelSizeMicronsMax * 100); + + Header.HeaderLength = (uint) (Helpers.Serializer.SizeOf(Header) + Helpers.Serializer.SizeOf(Settings) + 3 * 4); + + var previews = new[] + { + Array.Empty(), + Array.Empty(), + Array.Empty(), + Array.Empty() + }; + + for (var i = 0; i < previews.Length; i++) + { + if (Thumbnails[i] is null) continue; + previews[i] = EncodeImage(DATATYPE_RGB565, Thumbnails[i]!); + Header.HeaderLength += (uint)previews[i].Length; + } + + outputFile.WriteSerialize(Header); + + for (var i = 0; i < previews.Length; i++) + { + outputFile.WriteSerialize(new UInt24BigEndian((uint) previews[i].Length)); + outputFile.WriteBytes(previews[i]); + } + + outputFile.WriteSerialize(Settings); progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - var layerData = new LayerDef[LayerCount]; + var layerDef = new OSFLayerDef[LayerCount]; foreach (var batch in BatchLayersIndexes()) { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { - /*using (var mat = this[layerIndex].LayerMat) + var layer = this[layerIndex]; + + using (var mat = layer.LayerMat) { - layerData[layerIndex] = new LayerDef(this); - layerData[layerIndex].Encode(mat); - }*/ + layerDef[layerIndex] = new OSFLayerDef + { + NumberOfPixels = layer.NonZeroPixelCount, + StartY = (ushort)layer.BoundingRectangle.Y, + }; + layerDef[layerIndex].EncodeImage(mat); + } progress.LockAndIncrement(); }); foreach (var layerIndex in batch) { progress.ThrowIfCancellationRequested(); - outputFile.WriteSerialize(layerData[layerIndex]); - layerData[layerIndex].EncodedRle = null!; // Free this + outputFile.WriteSerialize(layerDef[layerIndex]); + outputFile.WriteBytes(layerDef[layerIndex].EncodedRle); + layerDef[layerIndex].EncodedRle = null!; // Free this } } - progress.ItemName = "Saving layers"; - progress.ProcessedItems = 0; - - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - progress.ThrowIfCancellationRequested(); - outputFile.WriteSerialize(layerData[layerIndex]); - progress++; - } - Debug.WriteLine("Encode Results:"); - Debug.WriteLine(HeaderSettings); + Debug.WriteLine(Header); + Debug.WriteLine(Settings); Debug.WriteLine("-End-"); } protected override void DecodeInternally(OperationProgress progress) { using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read); - HeaderSettings = Helpers.Deserialize
(inputFile); + Header = Helpers.Deserialize(inputFile); + + Debug.WriteLine(Header); + + for (byte i = 0; i < ThumbnailsOriginalSize!.Length; i++) + { + var previewSize = Helpers.Deserialize(inputFile); + var previewData = inputFile.ReadBytes(previewSize.Value); + Thumbnails[i] = DecodeImage(DATATYPE_RGB565, previewData, ThumbnailsOriginalSize[i]); + } + - //var previewSize = HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2; - //var previewData = inputFile.ReadBytes(previewSize); - //Thumbnails[0] = DecodeImage(DATATYPE_RGB565_BE, previewData, HeaderSettings.PreviewSizeX, HeaderSettings.PreviewSizeY); + Settings = Helpers.Deserialize(inputFile); + Debug.WriteLine(Settings); - - Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial); - var layerData = new LayerDef[LayerCount]; + Display = new SizeF( + ResolutionX * (Settings.PixelUmMagnified100Times / 100_000f), + ResolutionY * (Settings.PixelUmMagnified100Times / 100_000f) + ); + + Init(Settings.LayerCount, DecodeType == FileDecodeType.Partial); + if (DecodeType == FileDecodeType.Full) { + inputFile.Seek(Header.HeaderLength, SeekOrigin.Begin); + var layerDef = new OSFLayerDef[LayerCount]; + int buffer; + var rle = new List(); progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); foreach (var batch in BatchLayersIndexes()) { foreach (var layerIndex in batch) { progress.ThrowIfCancellationRequested(); - - layerData[layerIndex] = Helpers.Deserialize(inputFile); + layerDef[layerIndex] = Helpers.Deserialize(inputFile); + while ((buffer = inputFile.ReadByte()) > -1) + { + if (buffer == 0x0D) + { + if ((buffer = inputFile.ReadByte()) >= -1 && buffer is 0x0A or 0x0B) + { + inputFile.Seek(-2, SeekOrigin.Current); + break; + } + + rle.Add(0x0D); + } + + rle.Add((byte)buffer); + } + + layerDef[layerIndex].EncodedRle = rle.ToArray(); + rle.Clear(); } Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { - using var mat = layerData[layerIndex].Decode(); + using var mat = layerDef[layerIndex].DecodeImage(this); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this); + layerDef[layerIndex].EncodedRle = null!; progress.LockAndIncrement(); }); @@ -557,8 +855,8 @@ protected override void DecodeInternally(OperationProgress progress) protected override void PartialSaveInternally(OperationProgress progress) { using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Open, FileAccess.Write); - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + outputFile.Seek(Header.HeaderLength - Helpers.Serializer.SizeOf(Settings), SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, Settings); } #endregion diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs index a195bb88..47e3342a 100644 --- a/UVtools.Core/FileFormats/OSLAFile.cs +++ b/UVtools.Core/FileFormats/OSLAFile.cs @@ -248,7 +248,7 @@ public class GCodeDef public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { - new (typeof(OSLAFile), "osla", "Open SLA universal binary file"), + new (typeof(OSLAFile), "osla", "Open SLA universal binary file (OSLA)"), //new ("omsla", "Open mSLA universal binary file"), //new ("odlp", "Open DLP universal binary file"), }; diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index ebd7dc49..650a1416 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -177,7 +177,7 @@ public override string ToString() public override FileFormatType FileType => FileFormatType.Archive; public override FileExtension[] FileExtensions { get; } = { - new(typeof(UVJFile), "uvj", "UVJ") + new(typeof(UVJFile), "uvj", "Vendor-neutral format (UVJ)") }; public override PrintParameterModifier[]? PrintParameterModifiers { get; } = { diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index b82145be..f63a821f 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -198,7 +198,7 @@ public void CopyTo(Layer layer) public override FileFormatType FileType => FileFormatType.Archive; public override FileExtension[] FileExtensions { get; } = { - new(typeof(VDTFile), "vdt", "Voxeldance Tango VDT") + new(typeof(VDTFile), "vdt", "Voxeldance Tango (VDT)") }; public override PrintParameterModifier[]? PrintParameterModifiers { get; } = { diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 04b0fe66..382a60e9 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -26,6 +26,20 @@ namespace UVtools.Core.GCode; public class GCodeBuilder : BindableBase { + #region Events + public event EventHandler? BeforeRebuildGCode; + protected virtual void OnBeforeRebuildGCode() + { + BeforeRebuildGCode?.Invoke(this, EventArgs.Empty); + } + + public event EventHandler? AfterRebuildGCode; + protected virtual void OnAfterRebuildGCode() + { + AfterRebuildGCode?.Invoke(this, EventArgs.Empty); + } + #endregion + #region Commands public GCodeCommand CommandUnitsMillimetersG21 { get; } = new("G21", null, "Set units to be mm"); @@ -42,6 +56,7 @@ public class GCodeBuilder : BindableBase public GCodeCommand CommandSyncMovements { get; } = new("G4", "P0", "Sync movements", false); + public GCodeCommand CommandWaitSyncDelay { get; } = new("G4", "P0{0}", "Sync movement", false); public GCodeCommand CommandWaitG4 { get; } = new("G4", "P{0}", "Delay"); public GCodeCommand CommandShowImageM6054 = new("M6054", "\"{0}\"", "Show image"); @@ -98,6 +113,20 @@ public enum GCodeShowImageTypes : byte LayerIndex0Started, LayerIndex1Started, } + + public enum GCodeShowImagePositions : byte + { + /// + /// Show image at start of each layer block commands + /// + FirstLine, + + /// + /// Show image just before exposing / turning LED ON + /// + WhenRequired + } + #endregion #region Members @@ -108,13 +137,13 @@ public enum GCodeShowImageTypes : byte private GCodeTimeUnits _gCodeTimeUnit = GCodeTimeUnits.Milliseconds; private GCodeSpeedUnits _gCodeSpeedUnit = GCodeSpeedUnits.MillimetersPerMinute; private GCodeShowImageTypes _gCodeShowImageType = GCodeShowImageTypes.FilenamePng1Started; - private bool _syncMovementsWithDelay; private bool _useTailComma = true; private bool _useComments = true; private ushort _maxLedPower = byte.MaxValue; private uint _lineCount; private GCodeMoveCommands _layerMoveCommand; private GCodeMoveCommands _endGCodeMoveCommand; + private GCodeShowImagePositions _gCodeShowImagePosition = GCodeShowImagePositions.FirstLine; #endregion @@ -144,6 +173,12 @@ public GCodeShowImageTypes GCodeShowImageType set => RaiseAndSetIfChanged(ref _gCodeShowImageType, value); } + public GCodeShowImagePositions GCodeShowImagePosition + { + get => _gCodeShowImagePosition; + set => RaiseAndSetIfChanged(ref _gCodeShowImagePosition, value); + } + public GCodeMoveCommands LayerMoveCommand { get => _layerMoveCommand; @@ -156,12 +191,6 @@ public GCodeMoveCommands EndGCodeMoveCommand set => RaiseAndSetIfChanged(ref _endGCodeMoveCommand, value); } - public bool SyncMovementsWithDelay - { - get => _syncMovementsWithDelay; - set => RaiseAndSetIfChanged(ref _syncMovementsWithDelay, value); - } - public bool UseTailComma { get => _useTailComma; @@ -183,14 +212,12 @@ public ushort MaxLEDPower public string BeginStartGCodeComments { get; set; } = ";START_GCODE_BEGIN"; public string EndStartGCodeComments { get; set; } = ";END_GCODE_BEGIN"; - public string BeginLayerComments { get; set; } = ";LAYER_START:{0}" + Environment.NewLine + - ";PositionZ:{1}mm"; + public string BeginLayerComments { get; set; } = $";LAYER_START:{{0}}{Environment.NewLine};PositionZ:{{1}}mm"; public string EndLayerComments { get; set; } = ";LAYER_END"; public string BeginEndGCodeComments { get; set; } = ";START_GCODE_END"; - public string EndEndGCodeComments { get; set; } = ";END_GCODE_END" + Environment.NewLine + - ";"; + public string EndEndGCodeComments { get; set; } = $";END_GCODE_END{Environment.NewLine};"; public uint LineCount { @@ -373,12 +400,12 @@ public void AppendEndGCode(FileFormat slicerFile) else AppendMoveG1(finalRaiseZPosition, ConvertFromMillimetersPerMinute(slicerFile.MaximumSpeed)); - if (_syncMovementsWithDelay) + if (CommandWaitSyncDelay.Enabled) { var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly( finalRaiseZPositionRelative + lastLiftHeight, slicerFile.MaximumSpeed, 0.75f); var time = ConvertFromSeconds(seconds); - if (seconds > 0) AppendWaitG4($"0{time}", "Sync movement"); + if (seconds > 0) AppendWaitSyncDelay(time); } AppendSyncMovements(); @@ -401,11 +428,11 @@ public void AppendEndGCode(float raiseZ = 0, float feedRate = 0, float absRaiseZ else AppendMoveG1(raiseZ, feedRate); - if (_syncMovementsWithDelay) + if (CommandWaitSyncDelay.Enabled) { var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(absRaiseZ, rawSpeed, 0.75f); var time = ConvertFromSeconds(seconds); - AppendWaitG4($"0{time}", "Sync movement"); + AppendWaitSyncDelay(time); } } @@ -495,11 +522,11 @@ public void AppendLiftMoveG0(List<(float z, float feedrate)> lifts, List<(float AppendLineOverrideComment(CommandMoveG0, $"Z Lift ({i+1})", lifts[i].z, lifts[i].feedrate); // Z Lift } - if (_syncMovementsWithDelay && layer is not null) + if (CommandWaitSyncDelay.Enabled && layer is not null) { var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f); var time = ConvertFromSeconds(seconds); - AppendWaitG4($"0{time}", "Sync movement"); + AppendWaitSyncDelay(time); } AppendSyncMovements(); @@ -517,11 +544,11 @@ public void AppendLiftMoveG0(List<(float z, float feedrate)> lifts, List<(float AppendLineOverrideComment(CommandMoveG0, $"Retract ({i+1})", retracts[i].z, retracts[i].feedrate); } - if (_syncMovementsWithDelay && layer is not null) + if (CommandWaitSyncDelay.Enabled && layer is not null) { var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f); var time = ConvertFromSeconds(seconds); - AppendWaitG4($"0{time}", "Sync movement"); + AppendWaitSyncDelay(time); } AppendSyncMovements(); @@ -556,11 +583,11 @@ public void AppendLiftMoveG1(List<(float z, float feedrate)> lifts, List<(float AppendLineOverrideComment(CommandMoveG1, $"Z Lift ({i+1})", lifts[i].z, lifts[i].feedrate); // Z Lift } - if (_syncMovementsWithDelay && layer is not null) + if (CommandWaitSyncDelay.Enabled && layer is not null) { var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f); var time = ConvertFromSeconds(seconds); - AppendWaitG4($"0{time}", "Sync movement"); + AppendWaitSyncDelay(time); } AppendSyncMovements(); @@ -578,11 +605,11 @@ public void AppendLiftMoveG1(List<(float z, float feedrate)> lifts, List<(float AppendLineOverrideComment(CommandMoveG1, $"Retract ({i+1})", retracts[i].z, retracts[i].feedrate); } - if (_syncMovementsWithDelay && layer is not null) + if (CommandWaitSyncDelay.Enabled && layer is not null) { var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f); var time = ConvertFromSeconds(seconds); - AppendWaitG4($"0{time}", "Sync movement"); + AppendWaitSyncDelay(time); } AppendSyncMovements(); @@ -601,6 +628,19 @@ public void AppendLiftMoveG1(float upZ, float upFeedrate, float downZ, float dow new List<(float, float)> { new(downZ, downFeedrate) }, waitAfterLift, waitAfterRetract, layer); + public void AppendWaitSyncDelay(float time, string? comment = null) + { + if (time < 0) return; + AppendLineOverrideComment(CommandWaitSyncDelay, comment, time); + } + + public void AppendWaitSyncDelay(string timeStr, string? comment = null) + { + if (!float.TryParse(timeStr, out var time)) return; + if (time < 0) return; + AppendLineOverrideComment(CommandWaitSyncDelay, comment, timeStr); + } + public void AppendWaitG4(float time, string? comment = null) { if (time < 0) return; @@ -668,6 +708,8 @@ public void AppendShowImageM6054(uint layerIndex) public void RebuildGCode(FileFormat slicerFile, StringBuilder? header) => RebuildGCode(slicerFile, header?.ToString()); public void RebuildGCode(FileFormat slicerFile, string? header = null) { + OnBeforeRebuildGCode(); + Clear(); AppendUVtools(); @@ -752,7 +794,10 @@ public void RebuildGCode(FileFormat slicerFile, string? header = null) //if (layer.CanExpose) //{ Dont check this for compability - AppendShowImageM6054(GetShowImageString(layerIndex)); + if (_gCodeShowImagePosition == GCodeShowImagePositions.FirstLine) + { + AppendShowImageM6054(GetShowImageString(layerIndex)); + } //} if (liftHeightTotal > 0 && Layer.RoundHeight(liftHeightTotal + layer.PositionZ) > layer.PositionZ) @@ -774,6 +819,10 @@ public void RebuildGCode(FileFormat slicerFile, string? header = null) } AppendWaitG4(waitBeforeCure, "Wait before cure"); // Safer to parse if present + if (_gCodeShowImagePosition == GCodeShowImagePositions.WhenRequired && layer.CanExpose) + { + AppendShowImageM6054(GetShowImageString(layerIndex)); + } AppendExposure(exposureTime, pwmValue); if(waitAfterCure > 0) AppendWaitG4(waitAfterCure, "Wait after cure"); @@ -784,6 +833,7 @@ public void RebuildGCode(FileFormat slicerFile, string? header = null) } AppendEndGCode(slicerFile); + OnAfterRebuildGCode(); } public void RebuildGCode(FileFormat slicerFile, object[]? configs, string separator = ":") @@ -879,6 +929,23 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu Match match; + if (line[0] == ';') + { + match = Regex.Match(line, @"^;.*(layer\s+|layer:\s*|LAYER_START:\s*)(\d+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + if (match.Success && match.Groups.Count > 2 && uint.TryParse(match.Groups[2].Value, out var layerIndex)) + { + if (layerIndex > slicerFile.LayerCount) + { + throw new FileLoadException( + $"GCode parser detected the layer {layerIndex}, but the file was sliced to {slicerFile.LayerCount} layers.", + slicerFile.FileFullPath); + } + layerBlock.SetLayer(true); + layerBlock.LayerIndex = layerIndex; + continue; + } + } + // Display image if (line.StartsWith(CommandShowImageM6054.Command)) { @@ -888,8 +955,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu if (match.Success && match.Groups.Count >= 2) // Begin new layer { var layerIndex = uint.Parse(match.Groups[1].Value); - if (_gCodeShowImageType is GCodeShowImageTypes.FilenamePng1Started or GCodeShowImageTypes - .LayerIndex1Started) layerIndex--; + if (_gCodeShowImageType is GCodeShowImageTypes.FilenamePng1Started or GCodeShowImageTypes.LayerIndex1Started) layerIndex--; if (layerIndex > slicerFile.LayerCount) { throw new FileLoadException( @@ -898,7 +964,11 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu } // Propagate values before switch to the new layer - layerBlock.SetLayer(true); + if (_gCodeShowImagePosition == GCodeShowImagePositions.FirstLine && layerBlock.LayerIndex != layerIndex) + { + layerBlock.SetLayer(true); + } + layerBlock.LayerIndex = layerIndex; continue; @@ -970,12 +1040,12 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu RegexOptions.IgnoreCase); if (match.Success && match.Groups.Count >= 2) { - if (_syncMovementsWithDelay && match.Groups[1].Value.StartsWith('0')) continue; // Sync movement delay, skip + if (/*CommandWaitSyncDelay.Enabled && */match.Groups[1].Value.StartsWith('0')) continue; // Sync movement delay, skip var waitTime = float.Parse(match.Groups[1].Value); if (layerBlock.PositionZ.HasValue && - layerBlock.LiftHeight.HasValue && + //layerBlock.LiftHeight.HasValue && !layerBlock.RetractSpeed.HasValue) // Must be wait time after lift, if not, don't blame me! { layerBlock.WaitTimeAfterLift ??= 0; diff --git a/UVtools.Core/GCode/GCodeLayer.cs b/UVtools.Core/GCode/GCodeLayer.cs index 3f9501e7..afbbcf2f 100644 --- a/UVtools.Core/GCode/GCodeLayer.cs +++ b/UVtools.Core/GCode/GCodeLayer.cs @@ -304,7 +304,7 @@ public void SetLayer(bool reinit = false) layer.RetractSpeed2 = RetractSpeed2 ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2); layer.LightPWM = LightPWM ?? 0;//SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); - if (SlicerFile.GCode!.SyncMovementsWithDelay) // Dirty fix of the value + if (SlicerFile.GCode!.CommandWaitSyncDelay.Enabled) // Dirty fix of the value { var syncTime = OperationCalculator.LightOffDelayC.CalculateSeconds(layer, 1.5f); if (syncTime < layer.WaitTimeBeforeCure) diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index 3c5071ad..2f2ea025 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -756,12 +756,12 @@ public Mat LayerMat CvInvoke.Imdecode(_compressedBytes, ImreadModes.Grayscale, mat); break; case LayerCompressionCodec.Lz4: - mat = new Mat(SlicerFile.Resolution, DepthType.Cv8U, 1); + mat = SlicerFile.CreateMat(false); LZ4Codec.Decode(_compressedBytes.AsSpan(), mat.GetDataByteSpan()); break; case LayerCompressionCodec.GZip: { - mat = new(SlicerFile.Resolution, DepthType.Cv8U, 1); + mat = SlicerFile.CreateMat(false); unsafe { fixed (byte* pBuffer = _compressedBytes) @@ -777,7 +777,7 @@ public Mat LayerMat } case LayerCompressionCodec.Deflate: { - mat = new(SlicerFile.Resolution, DepthType.Cv8U, 1); + mat = SlicerFile.CreateMat(false); unsafe { fixed (byte* pBuffer = _compressedBytes) @@ -1619,7 +1619,8 @@ public static byte[] CompressMat(Mat mat, LayerCompressionCodec codec) var span = mat.GetDataByteSpan(); var target = new byte[LZ4Codec.MaximumOutputSize(span.Length)]; var encodedLength = LZ4Codec.Encode(span, target.AsSpan()); - return target[..encodedLength]; + Array.Resize(ref target, encodedLength); + return target; } case LayerCompressionCodec.GZip: { diff --git a/UVtools.Core/Objects/UInt24BigEndian.cs b/UVtools.Core/Objects/UInt24BigEndian.cs index 9515ea36..c1bf3a6c 100644 --- a/UVtools.Core/Objects/UInt24BigEndian.cs +++ b/UVtools.Core/Objects/UInt24BigEndian.cs @@ -56,4 +56,13 @@ public override int GetHashCode() { return (int)Value; } + + + public static uint operator +(UInt24BigEndian a, UInt24BigEndian b) => a.Value + b.Value; + + public static uint operator -(UInt24BigEndian a, UInt24BigEndian b) => a.Value - b.Value; + + public static uint operator *(UInt24BigEndian a, UInt24BigEndian b) => a.Value * b.Value; + + public static uint operator /(UInt24BigEndian a, UInt24BigEndian b) => a.Value / b.Value; } \ No newline at end of file diff --git a/UVtools.Core/Slicer/Slicer.cs b/UVtools.Core/Slicer/Slicer.cs index aa6727a2..848e3478 100644 --- a/UVtools.Core/Slicer/Slicer.cs +++ b/UVtools.Core/Slicer/Slicer.cs @@ -202,7 +202,7 @@ public void SliceModel2() public static uint MillimetersToLayers(float millimeters, float layerHeight) => (uint)(millimeters / layerHeight); public static uint MillimetersToLayers(double millimeters, double layerHeight) => (uint)(millimeters / layerHeight); - public static uint MillimetersToLayers(decimal millimeters, decimal layerHeight) => (uint)(millimeters / layerHeight); + public static uint MillimetersToLayers(decimal millimeters, decimal layerHeight) => layerHeight > 0 ? (uint)(millimeters / layerHeight) : 0; public static uint LayersToMillimeters(uint layers, float layerHeight) => (uint)(layers * layerHeight); public static uint LayersToMillimeters(uint layers, double layerHeight) => (uint)(layers * layerHeight); diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 783bd22d..f870d8e6 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 - 3.5.6 + 3.6.0 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 @@ -61,13 +61,13 @@ + - diff --git a/UVtools.Installer/Code/Product.wxs b/UVtools.Installer/Code/Product.wxs index b14c5f7c..ce6a4b42 100644 --- a/UVtools.Installer/Code/Product.wxs +++ b/UVtools.Installer/Code/Product.wxs @@ -42,7 +42,7 @@ - + diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index afa2e149..7784883c 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -2,7 +2,7 @@ - + @@ -1554,11 +1554,11 @@ - - + + - - + + diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index e5c7d0cb..2227fad8 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -1178,55 +1178,48 @@ public async void MenuHelpInstallProfilesClicked() public async void MenuNewVersionClicked() { - var result = - await this.MessageBoxWithHeaderQuestion( - $"Do you like to auto-update {About.Software} v{About.VersionStr} to v{VersionChecker.Version}?", - "Yes: Auto update \n" + - "No: Manual update \n" + - "Cancel: No action \n\n" + - "Changelog: \n\n" + - $"{VersionChecker.Changelog}", + if (string.IsNullOrWhiteSpace(VersionChecker.DownloadLink)) + { + var result = await this.MessageBoxWithHeaderQuestion( + $"Do you like to manually download and update {About.Software} v{About.VersionStr} to v{VersionChecker.Version}?", + "## Changelog: \n\n" + + $"{VersionChecker.Changelog}", $"Update UVtools to v{VersionChecker.Version}?", - - ButtonEnum.YesNoCancel, true); + ButtonEnum.YesNoCancel, true); - if (result == ButtonResult.No) - { - SystemAware.OpenBrowser(VersionChecker.UrlLatestRelease); - return; + if (result == ButtonResult.Yes) + { + SystemAware.OpenBrowser(VersionChecker.UrlLatestRelease); + } } - if (result == ButtonResult.Yes) + else { - IsGUIEnabled = false; - ShowProgressWindow($"Downloading: {VersionChecker.Filename}"); + var result = await this.MessageBoxWithHeaderQuestion( + $"Do you like to auto-update {About.Software} v{About.VersionStr} to v{VersionChecker.Version}?", + "Yes: Auto update \n" + + "No: Manual download and update \n" + + "Cancel: No action \n\n" + + "## Changelog: \n\n" + + $"{VersionChecker.Changelog}", + $"Update UVtools to v{VersionChecker.Version}?", - await VersionChecker.AutoUpgrade(Progress); + ButtonEnum.YesNoCancel, true); - /*var task = await Task.Factory.StartNew( () => - { - try - { - VersionChecker.AutoUpgrade(Progress); - return true; - } - catch (OperationCanceledException) - { - } - catch (Exception exception) - { - Dispatcher.UIThread.InvokeAsync(async () => - await this.MessageBoxError(exception.ToString(), "Error opening the file")); - } - return false; - }); - */ - IsGUIEnabled = true; - - return; + switch (result) + { + case ButtonResult.No: + SystemAware.OpenBrowser(VersionChecker.UrlLatestRelease); + break; + case ButtonResult.Yes: + IsGUIEnabled = false; + ShowProgressWindow($"Downloading: {VersionChecker.Filename}"); + await VersionChecker.AutoUpgrade(Progress); + IsGUIEnabled = true; + break; + } } - } #endregion diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs index d12d7334..e4018b80 100644 --- a/UVtools.WPF/Structures/AppVersionChecker.cs +++ b/UVtools.WPF/Structures/AppVersionChecker.cs @@ -16,6 +16,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; using Avalonia.Threading; using UVtools.Core; using UVtools.Core.Extensions; @@ -42,15 +43,12 @@ public string Filename { try { - var package = File.ReadAllText(file); + var package = File.ReadAllText(file).Trim(); if (!string.IsNullOrWhiteSpace(package) && (package.EndsWith("-x64") || package.EndsWith("-arm64"))) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return $"{About.Software}_{package}_v{_version}.msi"; - - return SystemAware.IsRunningLinuxAppImage() - ? $"{About.Software}_{package}_v{_version}.AppImage" - : $"{About.Software}_{package}_v{_version}.zip"; + if (OperatingSystem.IsWindows()) return $"{About.Software}_{package}_v{_version}.msi"; + if (SystemAware.IsRunningLinuxAppImage()) return $"{About.Software}_{package}_v{_version}.AppImage"; + return $"{About.Software}_{package}_v{_version}.zip"; } } catch (Exception e) @@ -59,17 +57,17 @@ public string Filename } } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (OperatingSystem.IsWindows()) { return $"{About.Software}_win-x64_v{_version}.msi"; } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + if (OperatingSystem.IsLinux()) { return SystemAware.IsRunningLinuxAppImage() ? $"{About.Software}_linux-x64_v{_version}.AppImage" : $"{About.Software}_linux-x64_v{_version}.zip"; } - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if (OperatingSystem.IsMacOS()) { return RuntimeInformation.ProcessArchitecture is Architecture.Arm or Architecture.Arm64 ? $"{About.Software}_osx-arm64_v{_version}.zip" @@ -122,7 +120,7 @@ public string Changelog public string UrlLatestRelease = $"{About.Website}/releases/latest"; - public string DownloadLink => string.IsNullOrEmpty(Filename) ? null : $"{About.Website}/releases/download/v{_version}/{Filename}"; + public string DownloadLink { get; private set; } public bool HaveNewVersion => !string.IsNullOrEmpty(Version); @@ -132,6 +130,8 @@ public bool Check() { try { + _version = null; + DownloadLink = null; var request = new HttpRequestMessage { RequestUri = new Uri(GitHubReleaseApi), @@ -149,31 +149,23 @@ public bool Check() Debug.WriteLine($"Version checker: v{About.VersionStr} <=> v{tag_name}"); Version checkVersion = new(tag_name); Changelog = json["body"]?.ToString(); - //if (string.Compare(tag_name, App.VersionStr, StringComparison.OrdinalIgnoreCase) > 0) if (About.Version.CompareTo(checkVersion) < 0) { - Debug.WriteLine($"New version detected: {DownloadLink}\n" + - $"{_changelog}"); - Dispatcher.UIThread.InvokeAsync(() => + var assets = json["assets"].AsArray(); + + Version = tag_name; + var fileName = Filename; + foreach (var asset in assets) { - Version = tag_name; - }); + if (asset["name"]!.ToString() != fileName) continue; + DownloadLink = asset["browser_download_url"]!.ToString(); + break; + } + + Debug.WriteLine($"New version detected: {DownloadLink}\n{_changelog}"); + return true; } - /*string htmlCode = client.DownloadString($"{About.Website}/releases"); - const string searchFor = "/releases/tag/"; - var startIndex = htmlCode.IndexOf(searchFor, StringComparison.InvariantCultureIgnoreCase) + - searchFor.Length; - var endIndex = htmlCode.IndexOf("\"", startIndex, StringComparison.InvariantCultureIgnoreCase); - var version = htmlCode.Substring(startIndex, endIndex - startIndex); - if (string.Compare(version, $"v{AppSettings.VersionStr}", StringComparison.OrdinalIgnoreCase) > 0) - { - Dispatcher.UIThread.InvokeAsync(() => - { - Version = version;; - }); - return true; - }*/ } catch (Exception e) { @@ -185,7 +177,7 @@ public bool Check() public async Task AutoUpgrade(OperationProgress progress) { - if (!HaveNewVersion) return false; + if (!HaveNewVersion || string.IsNullOrWhiteSpace(DownloadLink)) return false; progress.ItemName = "Megabytes"; try { @@ -228,7 +220,7 @@ public async Task AutoUpgrade(OperationProgress progress) Thread.Sleep(500); SystemAware.StartProcess(newFullPath); } - else // others + else // MacOS and generic linux (no AppImage) { var upgradeFolder = "UPDATED_VERSION"; var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder); @@ -249,16 +241,17 @@ public async Task AutoUpgrade(OperationProgress progress) await stream.WriteLineAsync($"echo {About.Software} v{About.Version} updater script"); await stream.WriteLineAsync($"cd '{App.ApplicationPath}'"); await stream.WriteLineAsync($"killall {About.Software}"); - await stream.WriteLineAsync("sleep 0.5"); + await stream.WriteLineAsync("sleep 1"); //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &"); if (SystemAware.IsRunningMacOSApp) { + await stream.WriteLineAsync($"find {upgradeFolder} -print0 | xargs -0 xattr -d com.apple.quarantine"); await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ../../../"); await stream.WriteLineAsync($"open ../../../{About.Software}.app"); } - else + else // Linux generic { await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ."); await stream.WriteLineAsync($"if [ -f '{About.Software}' ]; then"); diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 988e66aa..8c2eca61 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ LICENSE https://github.com/sn4k3/UVtools Git - 3.5.6 + 3.6.0 AnyCPU;x64 UVtools.png README.md @@ -39,15 +39,15 @@ 1701;1702; - - - - + + + + - - - - + + + + diff --git a/UVtools.WPF/Windows/MissingInformationWindow.axaml b/UVtools.WPF/Windows/MissingInformationWindow.axaml index 1d1f9cd8..7923aa91 100644 --- a/UVtools.WPF/Windows/MissingInformationWindow.axaml +++ b/UVtools.WPF/Windows/MissingInformationWindow.axaml @@ -36,6 +36,7 @@ Minimum="0" Maximum="0.200" Increment="0.01" + FormatString="F3" Value="{Binding LayerHeight}"/>