diff options
46 files changed, 2903 insertions, 426 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3336159..ef2019c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 14/04/2021 - v2.9.0 + +* **File formats:** + * Add Voxeldance Tango (VDT) + * Add Makerbase MKS-DLP (MDLPv1) + * Add GR1 Workshop (GR1) + * Add Creality CXDLP (CXDLP) + * When decoding a file and have a empty resolution (Width: 0 or Height: 0) it will auto fix it by get and set the first layer image resolution to the file + * Fix when decoding a file it was already set to require a full encode, preventing fast saves on print parameters edits +* **GUI:** + * When file resolution dismatch from layer resolution, it is now possible to auto fix it by set the layer resolution to the file + * When loading a file with auto scan for issues disabled it will force auto scan for issues types that are instant to check (print height and empty layers), if any exists it will auto select issues tab +* **(Add) PrusaSlicer printers:** + * Creality HALOT-SKY CL-89 + * Creality HALOT-SKY CL-60 +* (Improvement) Tool - Adjust layer height: Improve the performance when multiplying layers / go with higher layer height +* (Fix) PrusaSlicer Printer - Wanhao D7: Change the auto convertion format from .zip to .xml.cws + ## 08/04/2021 - v2.8.4 * (Improvement) Layers: "IsBottomLayer" property will now computing the value taking the height into consideration instead of it index, this allow to identify the real bottom layers when using multiple layers with same heights @@ -52,4 +52,6 @@ * Ben Ford * Mario Molero * Dennis Hansen -* Evert Goor
\ No newline at end of file +* Evert Goor +* James Kao +* Finn Newick
\ No newline at end of file diff --git a/CompileLinux.sh b/CompileLinux.sh deleted file mode 100644 index 0911e14..0000000 --- a/CompileLinux.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -dotnet build
\ No newline at end of file diff --git a/CreateRelease.GUI.ps1 b/CreateRelease.GUI.ps1 deleted file mode 100644 index 45dc54b..0000000 --- a/CreateRelease.GUI.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -cd $PSScriptRoot -$version = (Get-Command UVtools.GUI\bin\Release\UVtools.Core.dll).FileVersionInfo.FileVersion - -Remove-Item "$PSScriptRoot\UVtools.GUI\bin\Release\Logs" -Recurse -ErrorAction Ignore - -Add-Type -A System.IO.Compression.FileSystem -[IO.Compression.ZipFile]::CreateFromDirectory("$PSScriptRoot\UVtools.GUI\bin\Release", "$PSScriptRoot\UVtools.GUI\bin\UVtools_v$version.zip") - -Copy-Item "$PSScriptRoot\UVtools.Installer\bin\Release\UVtools.msi" -Destination "$PSScriptRoot\UVtools.GUI\bin\UVtools_v$version.msi"
\ No newline at end of file diff --git a/PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini b/PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini new file mode 100644 index 0000000..0a013c2 --- /dev/null +++ b/PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.0+win64 on 2021-04-14 at 04:57:50 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,130x0,130x82,0x82 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 82 +display_mirror_x = 1 +display_mirror_y = 0 +display_orientation = landscape +display_pixels_x = 2560 +display_pixels_y = 1620 +display_width = 130 +elefant_foot_compensation = 0.2 +elefant_foot_min_width = 0.2 +fast_tilt_time = 5 +gamma_correction = 1 +host_type = octoprint +inherits = Original Prusa SL1 +max_exposure_time = 120 +max_initial_exposure_time = 300 +max_print_height = 160 +min_exposure_time = 1 +min_initial_exposure_time = 1 +print_host = +printer_model = SL1 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_HALOT-SKY_CL-60\nFILEFORMAT_CXDLP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_7\nLiftHeight_7\nBottomLiftSpeed_70\nLiftSpeed_70\nRetractSpeed_80\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_settings_id = +printer_technology = SLA +printer_variant = default +printer_vendor = +printhost_apikey = +printhost_cafile = +relative_correction = 1,1 +slow_tilt_time = 8 +thumbnails = 400x400,800x480 diff --git a/PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini b/PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini new file mode 100644 index 0000000..353849b --- /dev/null +++ b/PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.0+win64 on 2021-04-14 at 04:53:05 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,192x0,192x120,0x120 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 120 +display_mirror_x = 1 +display_mirror_y = 0 +display_orientation = landscape +display_pixels_x = 3840 +display_pixels_y = 2400 +display_width = 192 +elefant_foot_compensation = 0.2 +elefant_foot_min_width = 0.2 +fast_tilt_time = 5 +gamma_correction = 1 +host_type = octoprint +inherits = Original Prusa SL1 +max_exposure_time = 120 +max_initial_exposure_time = 300 +max_print_height = 200 +min_exposure_time = 1 +min_initial_exposure_time = 1 +print_host = +printer_model = SL1 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_HALOT-SKY_CL-89\nFILEFORMAT_CXDLP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_7\nLiftHeight_7\nBottomLiftSpeed_70\nLiftSpeed_70\nRetractSpeed_80\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_settings_id = +printer_technology = SLA +printer_variant = default +printer_vendor = +printhost_apikey = +printhost_cafile = +relative_correction = 1,1 +slow_tilt_time = 8 +thumbnails = 400x400,800x480 diff --git a/PrusaSlicer/printer/Wanhao D7.ini b/PrusaSlicer/printer/Wanhao D7.ini index 1c1636b..e8b4733 100644 --- a/PrusaSlicer/printer/Wanhao D7.ini +++ b/PrusaSlicer/printer/Wanhao D7.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.3.0+win64 on 2021-01-13 at 02:28:38 UTC +# generated by PrusaSlicer 2.3.0+win64 on 2021-04-09 at 19:00:09 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_WANHAO\nPRINTER_MODEL_D7\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_60\nLiftSpeed_60\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_WANHAO\nPRINTER_MODEL_D7\nFILEFORMAT_XML.CWS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_60\nLiftSpeed_60\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default @@ -71,9 +71,13 @@ But also, i need victims for test subject. Proceed at your own risk! * CWS (NovaMaker) * RGB.CWS (Nova Bene4 Mono / Elfin2 Mono SE) * XML.CWS (Wanhao Workshop) +* MDLP (Makerbase MKS-DLP v1) +* GR1 (GR1 Workshop) +* CXDLP (Creality Box) * LGS (Longer Orange 10) * LGS30 (Longer Orange 30) -* UVJ (Zip file for manual manipulation format) +* VDT (Voxeldance Tango) +* UVJ (Zip file format for manual manipulation) * Image files (png, jpg, jpeg, gif, bmp) # Available printers for PrusaSlicer @@ -132,6 +136,8 @@ But also, i need victims for test subject. Proceed at your own risk! * LD-002R * LD-002H * LD-006 + * HALOT-SKY CL-89 + * HALOT-SKY CL-60 * **Voxelab** * Polaris 5.5 * Proxima 6 diff --git a/ImportPrusaSlicerData.bat b/Scripts/ImportPrusaSlicerData.bat index de8770e..525dfcf 100644 --- a/ImportPrusaSlicerData.bat +++ b/Scripts/ImportPrusaSlicerData.bat @@ -1,7 +1,8 @@ @echo off SET DIR=%~dp0 +cd .. SET INPUT_DIR=%AppData%\PrusaSlicer -SET OUTPUT_DIR=%~dp0PrusaSlicer +SET OUTPUT_DIR=PrusaSlicer SET PRINT_DIR=sla_print SET PRINTER_DIR=printer @@ -57,13 +58,15 @@ SET files[47]=Wanhao CGR Mono.ini SET files[48]=Creality LD-002R.ini SET files[49]=Creality LD-002H.ini SET files[50]=Creality LD-006.ini -SET files[51]=Voxelab Polaris 5.5.ini -SET files[52]=Voxelab Proxima 6.ini -SET files[53]=Voxelab Ceres 8.9.ini -SET files[54]=Longer Orange 10.ini -SET files[55]=Longer Orange 30.ini -SET files[56]=Longer Orange4K.ini -SET files[57]=Uniz IBEE.ini +SET files[51]=Creality HALOT-SKY CL-89.ini +SET files[52]=Creality HALOT-SKY CL-60.ini +SET files[53]=Voxelab Polaris 5.5.ini +SET files[54]=Voxelab Proxima 6.ini +SET files[55]=Voxelab Ceres 8.9.ini +SET files[56]=Longer Orange 10.ini +SET files[57]=Longer Orange 30.ini +SET files[58]=Longer Orange4K.ini +SET files[59]=Uniz IBEE.ini echo PrusaSlicer Printers Instalation echo This will replace printers, all changes will be discarded diff --git a/Scripts/cxdlp.bt b/Scripts/cxdlp.bt new file mode 100644 index 0000000..6052b06 --- /dev/null +++ b/Scripts/cxdlp.bt @@ -0,0 +1,89 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: Creality +// Authors: Julien Delnatte +//------------------------------------------------ + +typedef struct { + BitfieldDisablePadding();
ushort startY:13;
ushort endY:13;
ushort x:14; + ubyte grey <fgcolor=cBlack, bgcolor=cWhite>; +} layerPointsData; + +typedef struct { + uint32 unknown <fgcolor=cBlack, bgcolor=cWhite>; +} layerDef; + +typedef struct() { + uint32 unknown <fgcolor=cBlack, bgcolor=cWhite>; + uint32 LayerPointNum <fgcolor=cBlack, bgcolor=cWhite>; + + layerPointsData pD()[LayerPointNum]; + BYTE CR_LF2[2] <fgcolor=cBlack, bgcolor=cRed>; +} layerData; + +typedef struct(int size) { + BYTE layerDataBlock[size] <fgcolor=cBlack, bgcolor=0x00FF00>; +} rgbPreviewImageRawData; + +struct HEADER { + uint32 headerSize <fgcolor=cBlack, bgcolor=cWhite>; + BYTE header[header.headerSize] <fgcolor=cBlack, bgcolor=cRed>; + + uint16 totalLayers <fgcolor=cBlack, bgcolor=cWhite>; + uint16 resolutionX <fgcolor=cBlack, bgcolor=cWhite>; + uint16 resolutionY <fgcolor=cBlack, bgcolor=cWhite>; + + rgbPreviewImageRawData preview(116*116*2); + BYTE rn0[2] <fgcolor=cBlack, bgcolor=cRed>; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn1[2] <fgcolor=cBlack, bgcolor=cRed>; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn2[2] <fgcolor=cBlack, bgcolor=cRed>; + + uint32 plateformXLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t plateformX[plateformXLength/2]; + + uint32 plateformYLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t plateformY[plateformYLength/2]; + + uint32 layerThicknessLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t layerThickness[layerThicknessLength/2]; + + uint16 exposureTime <fgcolor=cBlack, bgcolor=cWhite>; + uint16 turnOffTime <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomExposure <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomLayers <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomRaise <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomRaiseSpeed <fgcolor=cBlack, bgcolor=cWhite>; + uint16 raise <fgcolor=cBlack, bgcolor=cWhite>; + uint16 raiseSpeed <fgcolor=cBlack, bgcolor=cWhite>; + uint16 fallSpeed <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomPWM <fgcolor=cBlack, bgcolor=cWhite>; + uint16 normalPWM <fgcolor=cBlack, bgcolor=cWhite>; + +} header; + +struct LAYER_DEF { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerDef lD(); + } + BYTE rn3[2] <fgcolor=cBlack, bgcolor=cRed>; +} layerDefs; + +struct LAYERS { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerData lD(); + } +} layers; + +struct FOOTER { + uint32 footerSize <fgcolor=cBlack, bgcolor=cWhite>; + BYTE str[footer.footerSize] <fgcolor=cBlack, bgcolor=cRed>; + + uint32 unknown <fgcolor=cBlack, bgcolor=cWhite>; +} footer;
\ No newline at end of file diff --git a/Scripts/gr1.bt b/Scripts/gr1.bt new file mode 100644 index 0000000..308a637 --- /dev/null +++ b/Scripts/gr1.bt @@ -0,0 +1,74 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: GR1 Workshop +// Authors: Julien Delnatte +//------------------------------------------------ + +typedef struct { + uint16 startY <fgcolor=cBlack, bgcolor=cWhite>; + uint16 endY <fgcolor=cBlack, bgcolor=cWhite>; + uint16 startX <fgcolor=cBlack, bgcolor=cWhite>; + +} layerPointsData; + +typedef struct() { + uint32 LayerPointNum <fgcolor=cBlack, bgcolor=cWhite>; + + layerPointsData pD()[LayerPointNum]; + BYTE CR_LF2[2] <fgcolor=cBlack, bgcolor=cRed>; +} layerData; + +typedef struct(int size) { + BYTE layerDataBlock[size] <fgcolor=cBlack, bgcolor=0x00FF00>; +} rgbPreviewImageRawData; + +struct HEADER { + char version[2] <fgcolor=cBlack, bgcolor=cWhite>; + ushort headerSize <fgcolor=cBlack, bgcolor=cWhite>; + BYTE header[header.headerSize] <fgcolor=cBlack, bgcolor=cRed>; + + rgbPreviewImageRawData preview(116*116*2); + BYTE rn0[2] <fgcolor=cBlack, bgcolor=cRed>; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn1[2] <fgcolor=cBlack, bgcolor=cRed>; + + uint16 totalLayers <fgcolor=cBlack, bgcolor=cWhite>; + uint16 resolutionX <fgcolor=cBlack, bgcolor=cWhite>; + uint16 resolutionY <fgcolor=cBlack, bgcolor=cWhite>; + + uint32 plateformXLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t plateformX[plateformXLength/2]; + + uint32 plateformYLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t plateformY[plateformYLength/2]; + + uint32 layerThicknessLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t layerThickness[layerThicknessLength/2]; + + uint16 exposureTime <fgcolor=cBlack, bgcolor=cWhite>; + uint16 turnOffTime <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomExposure <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomLayers <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomRaise <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomRaiseSpeed <fgcolor=cBlack, bgcolor=cWhite>; + uint16 raise <fgcolor=cBlack, bgcolor=cWhite>; + uint16 raiseSpeed <fgcolor=cBlack, bgcolor=cWhite>; + uint16 fallSpeed <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomPWM <fgcolor=cBlack, bgcolor=cWhite>; + uint16 normalPWM <fgcolor=cBlack, bgcolor=cWhite>; + +} header; + +struct LAYERS { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerData lD(); + } +} layers; + +struct FOOTER { + uint32 footerSize <fgcolor=cBlack, bgcolor=cWhite>; + BYTE str[footer.footerSize] <fgcolor=cBlack, bgcolor=cRed>; +} footer;
\ No newline at end of file diff --git a/Scripts/mdlp.bt b/Scripts/mdlp.bt new file mode 100644 index 0000000..5886ee3 --- /dev/null +++ b/Scripts/mdlp.bt @@ -0,0 +1,66 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: Makerbase MKS-DLP mdlp +// Authors: Julien Delnatte +//------------------------------------------------ + +typedef struct { + uint16 startY <fgcolor=cBlack, bgcolor=cWhite>; + uint16 endY <fgcolor=cBlack, bgcolor=cWhite>; + uint16 startX <fgcolor=cBlack, bgcolor=cWhite>; + +} layerPointsData; + +typedef struct() { + uint32 LayerPointNum <fgcolor=cBlack, bgcolor=cWhite>; + + layerPointsData pD()[LayerPointNum]; + BYTE CR_LF2[2] <fgcolor=cBlack, bgcolor=cRed>; +} layerData; + +typedef struct(int size) { + BYTE layerDataBlock[size] <fgcolor=cBlack, bgcolor=0x00FF00>; +} rgbPreviewImageRawData; + +struct HEADER { + char version[2] <fgcolor=cBlack, bgcolor=cWhite>; + ushort headerSize <fgcolor=cBlack, bgcolor=cWhite>; + BYTE header[header.headerSize] <fgcolor=cBlack, bgcolor=cRed>; + + rgbPreviewImageRawData preview(116*116*2); + BYTE rn0[2] <fgcolor=cBlack, bgcolor=cRed>; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn1[2] <fgcolor=cBlack, bgcolor=cRed>; + + uint16 totalLayers <fgcolor=cBlack, bgcolor=cWhite>; + uint16 resolutionX <fgcolor=cBlack, bgcolor=cWhite>; + uint16 resolutionY <fgcolor=cBlack, bgcolor=cWhite>; + + uint32 plateformXLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t plateformX[plateformXLength/2]; + + uint32 plateformYLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t plateformY[plateformYLength/2]; + + uint32 layerThicknessLength <fgcolor=cBlack, bgcolor=cWhite>; + wchar_t layerThickness[layerThicknessLength/2]; + + uint16 exposureTime <fgcolor=cBlack, bgcolor=cWhite>; + uint16 turnOffTime <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomExposure <fgcolor=cBlack, bgcolor=cWhite>; + uint16 bottomLayers <fgcolor=cBlack, bgcolor=cWhite>; +} header; + +struct LAYERS { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerData lD(); + } +} layers; + +struct FOOTER { + uint32 footerSize <fgcolor=cBlack, bgcolor=cWhite>; + BYTE str[footer.footerSize] <fgcolor=cBlack, bgcolor=cRed>; +} footer;
\ No newline at end of file diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 48a9da8..9ea852a 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -18,12 +18,12 @@ namespace UVtools.Core.Extensions { public static class EmguExtensions { - public static readonly MCvScalar WhiteByte = new MCvScalar(255); - public static readonly MCvScalar White3Byte = new MCvScalar(255, 255, 255); - public static readonly MCvScalar BlackByte = new MCvScalar(0); - public static readonly MCvScalar Black3Byte = new MCvScalar(0, 0, 0); - public static readonly MCvScalar Transparent4Byte = new MCvScalar(0, 0, 0, 0); - public static readonly MCvScalar Black4Byte = new MCvScalar(0, 0, 0, 255); + public static readonly MCvScalar WhiteByte = new(255); + public static readonly MCvScalar White3Byte = new(255, 255, 255); + public static readonly MCvScalar BlackByte = new(0); + public static readonly MCvScalar Black3Byte = new(0, 0, 0); + public static readonly MCvScalar Transparent4Byte = new(0, 0, 0, 0); + public static readonly MCvScalar Black4Byte = new(0, 0, 0, 255); public static unsafe byte* GetBytePointer(this Mat mat) { @@ -64,10 +64,16 @@ namespace UVtools.Core.Extensions public static unsafe Span<T> GetPixelRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0) { - return new(IntPtr.Add(mat.DataPointer, y * mat.Step + offset).ToPointer(), length == 0 ? mat.Step : length); + return new(IntPtr.Add(mat.DataPointer, y * mat.Step + offset).ToPointer(), length <= 0 ? mat.Step : length); //return mat.GetPixelSpan<T>().Slice(offset, mat.Step); } + public static unsafe Span<T> GetPixelColSpan<T>(this Mat mat, int x, int length = 0, int offset = 0) + { + var colMat = mat.Col(x); + return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length); + } + /// <summary> /// Gets if a <see cref="Mat"/> is all zeroed /// </summary> diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index f170e12..e16893c 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -47,6 +47,16 @@ namespace UVtools.Core.Extensions public static Size Inflate(this Size size, int width, int height) => new (size.Width + width, size.Height + height); /// <summary> + /// Gets if this size have a zero value on width or height + /// </summary> + /// <param name="size"></param> + /// <returns></returns> + public static bool HaveZero(this Size size) + { + return size.Width <= 0 && size.Height <= 0; + } + + /// <summary> /// Exchange width with height /// </summary> /// <param name="size"></param> @@ -74,6 +84,16 @@ namespace UVtools.Core.Extensions return round >= 0 ? (float) Math.Round(rect.Width * rect.Height, round) : rect.Width * rect.Height; } + /// <summary> + /// Gets if this size have a zero value on width or height + /// </summary> + /// <param name="size"></param> + /// <returns></returns> + public static bool HaveZero(this SizeF size) + { + return size.Width <= 0 && size.Height <= 0; + } + public static float Area(this SizeF size, int round = -1) { return round >= 0 ? (float)Math.Round(size.Width * size.Height, round) : size.Width * size.Height; diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 3f6e1d7..f541172 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -335,8 +335,6 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightPWM, }; - public override byte ThumbnailsCount { get; } = 0; - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = null; public override uint ResolutionX @@ -383,10 +381,10 @@ namespace UVtools.Core.FileFormats } } - public override float MaxPrintHeight + public override float MachineZ { - get => OutputSettings.PlatformZSize > 0 ? OutputSettings.PlatformZSize : base.MaxPrintHeight; - set => base.MaxPrintHeight = OutputSettings.PlatformZSize = (float)Math.Round(value, 2); + get => OutputSettings.PlatformZSize > 0 ? OutputSettings.PlatformZSize : base.MachineZ; + set => base.MachineZ = OutputSettings.PlatformZSize = (float)Math.Round(value, 2); } public override bool MirrorDisplay diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs new file mode 100644 index 0000000..2e680a3 --- /dev/null +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -0,0 +1,703 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BinarySerialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + public class CXDLPFile : FileFormat + { + #region Constants + private const byte HEADER_SIZE = 9; // CXSW3DV2 + private const string HEADER_VALUE = "CXSW3DV2"; + + + private const uint SlicerInfoAddress = 4 + HEADER_SIZE + 6 + 290 * 290 * 4 + 116 * 116 * 2 + 6; + #endregion + + #region Sub Classes + #region Header + public sealed class Header + { + /// <summary> + /// Gets the size of the header + /// </summary> + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint HeaderSize { get; set; } = HEADER_SIZE; + + /// <summary> + /// Gets the header name + /// </summary> + [FieldOrder(1)] + [FieldLength(HEADER_SIZE)] + [SerializeAs(SerializedType.TerminatedString)] + public string HeaderValue { get; set; } = HEADER_VALUE; + + /// <summary> + /// Gets the number of records in the layer table + /// </summary> + [FieldOrder(2)] + [FieldEndianness(Endianness.Big)] + public ushort LayerCount { get; set; } + + /// <summary> + /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images. + /// </summary> + [FieldOrder(3)] + [FieldEndianness(Endianness.Big)] + public ushort ResolutionX { get; set; } + + /// <summary> + /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images. + /// </summary> + [FieldOrder(4)] + [FieldEndianness(Endianness.Big)] + public ushort ResolutionY { get; set; } + + public void Validate() + { + if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE) + { + throw new FileLoadException("Not a valid CXDLP file!"); + } + } + } + + #endregion + + #region SlicerInfo + // Address: 363337 + public sealed class SlicerInfo + { + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint DisplayWidthDataSize { get; set; } = 20; + + [FieldOrder(1)] + [FieldLength(nameof(DisplayWidthDataSize))] + public byte[] DisplayWidth { get; set; } + + [FieldOrder(2)] + [FieldEndianness(Endianness.Big)] + public uint DisplayHeightDataSize { get; set; } = 20; + + [FieldOrder(3)] + [FieldLength(nameof(DisplayHeightDataSize))] + public byte[] DisplayHeight { get; set; } + + [FieldOrder(4)] + [FieldEndianness(Endianness.Big)] + public uint LayerHeightDataSize { get; set; } = 16; + + [FieldOrder(5)] + [FieldLength(nameof(LayerHeightDataSize))] + public byte[] LayerHeight { get; set; } + + [FieldOrder(6)] + [FieldEndianness(Endianness.Big)] + public ushort ExposureTime { get; set; } + + [FieldOrder(7)] + [FieldEndianness(Endianness.Big)] + public ushort LightOffDelay { get; set; } + + [FieldOrder(8)] + [FieldEndianness(Endianness.Big)] + public ushort BottomExposureTime { get; set; } + + [FieldOrder(9)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLayers { get; set; } + + [FieldOrder(10)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLiftHeight { get; set; } + + [FieldOrder(11)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLiftSpeed { get; set; } + + [FieldOrder(12)] + [FieldEndianness(Endianness.Big)] + public ushort LiftHeight { get; set; } + + [FieldOrder(13)] + [FieldEndianness(Endianness.Big)] + public ushort LiftSpeed { get; set; } + + [FieldOrder(14)] + [FieldEndianness(Endianness.Big)] + public ushort RetractSpeed { get; set; } + + [FieldOrder(15)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLightPWM { get; set; } = 255; + + [FieldOrder(16)] + [FieldEndianness(Endianness.Big)] + public ushort LightPWM { get; set; } = 255; + } + #endregion + + #region Layer Def + + public sealed class PreLayer + { + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint Unknown { get; set; } + + public PreLayer() + { + } + + public PreLayer(uint unknown) + { + Unknown = unknown; + } + } + + public sealed class LayerDef + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Unknown { get; set; } + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; } + [FieldOrder(2)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; } + [FieldOrder(3)] public PageBreak PageBreak { get; set; } = new(); + + public LayerDef() { } + + public LayerDef(uint unknown, uint lineCount, LayerLine[] lines) + { + Unknown = unknown; + LineCount = lineCount; + Lines = lines; + } + } + + public sealed class LayerLine + { + public const byte CoordinateCount = 5; + [FieldOrder(0)] [FieldCount(CoordinateCount)] public byte[] Coordinates { get; set; } = new byte[CoordinateCount]; + //[FieldOrder(0)] [FieldEndianness(Endianness.Big)] [FieldBitLength(13)] public ushort StartY { get; set; } + //[FieldOrder(1)] [FieldEndianness(Endianness.Big)] [FieldBitLength(13)] public ushort EndY { get; set; } + //[FieldOrder(2)] [FieldEndianness(Endianness.Big)] [FieldBitLength(14)] public ushort StartX { get; set; } + [FieldOrder(1)] public byte Gray { get; set; } + + [Ignore] public ushort StartY => (ushort) ((((Coordinates[0] << 8) + Coordinates[1]) >> 3) & 0x1FFF); // 13 bits + + [Ignore] public ushort EndY => (ushort)((((Coordinates[1] << 16) + (Coordinates[2] << 8) + Coordinates[3]) >> 6) & 0x1FFF); // 13 bits + + [Ignore] public ushort StartX => (ushort)(((Coordinates[3] << 8) + Coordinates[4]) & 0x3FFF); // 14 bits + [Ignore] public ushort Length => (ushort) (EndY - StartY); + + public LayerLine() { } + + public LayerLine(ushort startY, ushort endY, ushort startX, byte gray) + { + Coordinates[0] = (byte) ((startY >> 5) & 0xFF); + Coordinates[1] = (byte) (((startY << 3) + (endY >> 10)) & 0xFF); + Coordinates[2] = (byte) ((endY >> 2) & 0xFF); + Coordinates[3] = (byte)(((endY << 6) + (startX >> 8)) & 0xFF); + Coordinates[4] = (byte) startX; + /*StartY = startY; + EndY = endY; + StartX = startX;*/ + Gray = gray; + } + } + + public sealed class PageBreak + { + [FieldOrder(0)] public byte Line { get; set; } = 0x0D; + [FieldOrder(1)] public byte Break { get; set; } = 0x0A; + } + + #endregion + + #region Footer + public sealed class Footer + { + /// <summary> + /// Gets the size of the header + /// </summary> + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint FooterSize { get; set; } = HEADER_SIZE; + + /// <summary> + /// Gets the header name + /// </summary> + [FieldOrder(1)] + [FieldLength(HEADER_SIZE)] + [SerializeAs(SerializedType.TerminatedString)] + public string FooterValue { get; set; } = HEADER_VALUE; + + [FieldOrder(2)] + public uint Unknown { get; set; } = 7; + + public void Validate() + { + if (FooterSize != HEADER_SIZE || FooterValue != HEADER_VALUE) + { + throw new FileLoadException("Not a valid CXDLP file!"); + } + } + } + #endregion + + #endregion + + #region Properties + + public Header HeaderSettings { get; protected internal set; } = new(); + public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); + public Footer FooterSettings { get; protected internal set; } = new(); + + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new("cxdlp", "Creality CXDLP"), + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = + { + PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.BottomExposureSeconds, + PrintParameterModifier.ExposureSeconds, + + PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.RetractSpeed, + PrintParameterModifier.LightOffDelay, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM, + }; + + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(116, 116), + new(290, 290), + new(290, 290) + }; + + public override uint ResolutionX + { + get => HeaderSettings.ResolutionX; + set + { + HeaderSettings.ResolutionX = (ushort) value; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get => HeaderSettings.ResolutionY; + set + { + HeaderSettings.ResolutionY = (ushort) value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => float.Parse(SlicerInfoSettings.DisplayWidth.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayWidthDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayWidthDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayWidth = data; + RaisePropertyChanged(); + } + } + + public override float DisplayHeight + { + get => float.Parse(SlicerInfoSettings.DisplayHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayHeight = data; + RaisePropertyChanged(); + } + } + + public override byte AntiAliasing + { + get => 8; + set {} + } + + public override float LayerHeight + { + get => float.Parse(SlicerInfoSettings.LayerHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Layer.RoundHeight(value).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.LayerHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.LayerHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.LayerHeight = data; + RaisePropertyChanged(); + } + } + + public override uint LayerCount + { + get => base.LayerCount; + set => base.LayerCount = HeaderSettings.LayerCount = (ushort) base.LayerCount; + } + + public override ushort BottomLayerCount + { + get => SlicerInfoSettings.BottomLayers; + set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value; + } + + public override float BottomExposureTime + { + get => SlicerInfoSettings.BottomExposureTime; + set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort) value; + } + + public override float ExposureTime + { + get => SlicerInfoSettings.ExposureTime; + set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort) value; + } + + public override float BottomLiftHeight + { + get => SlicerInfoSettings.BottomLiftHeight; + set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort) value; + } + + public override float LiftHeight + { + get => SlicerInfoSettings.LiftHeight; + set => base.LiftHeight = SlicerInfoSettings.LiftHeight = (ushort)value; + } + + public override float BottomLiftSpeed + { + get => SlicerInfoSettings.BottomLiftSpeed; + set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value; + } + + public override float LiftSpeed + { + get => SlicerInfoSettings.LiftSpeed; + set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value; + } + + public override float RetractSpeed + { + get => SlicerInfoSettings.RetractSpeed; + set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)value; + } + + public override float BottomLightOffDelay => SlicerInfoSettings.LightOffDelay; + + public override float LightOffDelay + { + get => SlicerInfoSettings.LightOffDelay; + set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; + } + + public override byte BottomLightPWM + { + get => (byte) SlicerInfoSettings.BottomLightPWM; + set => base.BottomLightPWM = (byte) (SlicerInfoSettings.BottomLightPWM = value); + } + + public override byte LightPWM + { + get => (byte)SlicerInfoSettings.LightPWM; + set => base.LightPWM = (byte) (SlicerInfoSettings.LightPWM = value); + } + + public override object[] Configs => new object[] { HeaderSettings, SlicerInfoSettings, FooterSettings }; + + #endregion + + #region Constructors + #endregion + + #region Methods + + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + var pageBreak = new PageBreak(); + + byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + } + // Previews + Parallel.For(0, previews.Length, previewIndex => + { + if (progress.Token.IsCancellationRequested) return; + if (Thumbnails[previewIndex] is null) return; + var span = Thumbnails[previewIndex].GetPixelSpanByte(); + int index = 0; + for (int i = 0; i < span.Length; i += 3) + { + byte b = span[i]; + byte g = span[i + 1]; + byte r = span[i + 2]; + + ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); + + previews[previewIndex][index++] = (byte)(rgb15 >> 8); + previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + } + + if (index != previews[previewIndex].Length) + { + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + } + }); + + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + Helpers.SerializeWriteFileStream(outputFile, previews[i]); + Helpers.SerializeWriteFileStream(outputFile, pageBreak); + } + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var preLayers = new PreLayer[LayerCount]; + var layerDefs = new LayerDef[LayerCount]; + + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + var layer = this[layerIndex]; + preLayers[layerIndex] = new(layer.NonZeroPixelCount); + } + Helpers.SerializeWriteFileStream(outputFile, preLayers); + Helpers.SerializeWriteFileStream(outputFile, pageBreak); + + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + List<LayerLine> layerLines = new(); + var layer = this[layerIndex]; + using var mat = layer.LayerMat; + var span = mat.GetPixelSpanByte(); + + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) + { + int y = layer.BoundingRectangle.Y; + int startY = -1; + byte lastColor = 0; + for (; y < layer.BoundingRectangle.Bottom; y++) + { + int pos = mat.GetPixelPos(x, y); + byte color = span[pos]; + + if (lastColor == color && color != 0) continue; + + if (startY >= 0) + { + layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); + } + + startY = color == 0 ? -1 : y; + + lastColor = color; + } + + if (startY >= 0) + { + layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); + } + } + + layerDefs[layerIndex] = new LayerDef(layer.NonZeroPixelCount, (uint)layerLines.Count, layerLines.ToArray()); + + progress.LockAndIncrement(); + }); + + progress.Reset(OperationProgress.StatusWritingFile, LayerCount); + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + Helpers.SerializeWriteFileStream(outputFile, layerDefs[layerIndex]); + progress++; + } + + Helpers.SerializeWriteFileStream(outputFile, FooterSettings); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine("-End-"); + } + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + HeaderSettings.Validate(); + + byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + inputFile.ReadBytes(previews[i]); + inputFile.Seek(2, SeekOrigin.Current); + } + + Parallel.For(0, previews.Length, previewIndex => + { + var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); + var span = mat.GetPixelSpanByte(); + + int spanIndex = 0; + for (int i = 0; i < previews[previewIndex].Length; i += 2) + { + ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); + byte r = (byte)((rgb15 >> 11) << 3); + byte g = (byte)((rgb15 >> 5) << 2); + byte b = (byte)((rgb15 >> 0) << 3); + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + Thumbnails[previewIndex] = mat; + }); + + + SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile); + + LayerManager.Init(HeaderSettings.LayerCount); + progress.ItemCount = LayerCount; + var preLayers = new PreLayer[LayerCount]; + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + preLayers[layerIndex] = Helpers.Deserialize<PreLayer>(inputFile); + } + + inputFile.Seek(2, SeekOrigin.Current); + var layerDefs = new LayerDef[LayerCount]; + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerDefs[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile); + progress++; + } + + /*using TextWriter file = new StreamWriter("D:\\dump.txt"); + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + file.WriteLine($"Layer: {layerIndex}"); + for (int lineIndex = 0; lineIndex < layerDefs[layerIndex].LineCount; lineIndex++) + { + file.WriteLine($"\tLine: {lineIndex}"); + var line = layerDefs[layerIndex].Lines[lineIndex]; + file.WriteLine($"\t\tb1: {line.Coordinates[0]}"); + file.WriteLine($"\t\tb2: {line.Coordinates[1]}"); + file.WriteLine($"\t\tb3: {line.Coordinates[2]}"); + file.WriteLine($"\t\tb4: {line.Coordinates[3]}"); + file.WriteLine($"\t\tb5: {line.Coordinates[4]}"); + file.WriteLine($"\t\tstartY: {line.StartY}"); + file.WriteLine($"\t\tendY: {line.EndY}"); + file.WriteLine($"\t\tstartX: {line.StartX}"); + file.WriteLine($"\t\tgray: {line.Gray}"); + } + } + file.Close();*/ + + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using var mat = EmguExtensions.InitMat(Resolution); + foreach (var line in layerDefs[layerIndex].Lines) + { + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), new MCvScalar(line.Gray)); + } + + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + progress.LockAndIncrement(); + }); + + FooterSettings = Helpers.Deserialize<Footer>(inputFile); + FooterSettings.Validate(); + } + + public override void SaveAs(string filePath = null, OperationProgress progress = null) + { + if (RequireFullEncode) + { + if (!string.IsNullOrEmpty(filePath)) + { + FileFullPath = filePath; + } + Encode(FileFullPath, progress); + return; + } + + if (!string.IsNullOrEmpty(filePath)) + { + File.Copy(FileFullPath, filePath, true); + FileFullPath = filePath; + } + + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + } + + #endregion + } +} diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index f317809..ab86e58 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -904,7 +904,7 @@ namespace UVtools.Core.FileFormats #endregion - #region KeyRing + #region KeyRing public class KeyRing { @@ -1026,9 +1026,11 @@ namespace UVtools.Core.FileFormats - public override byte ThumbnailsCount { get; } = 2; - - public override Size[] ThumbnailsOriginalSize { get; } = {new Size(400, 300), new Size(200, 125)}; + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(400, 300), + new(200, 125) + }; public override uint ResolutionX { @@ -1070,10 +1072,10 @@ namespace UVtools.Core.FileFormats } } - public override float MaxPrintHeight + public override float MachineZ { - get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MaxPrintHeight; - set => base.MaxPrintHeight = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); + get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ; + set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); } public override bool MirrorDisplay diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index ed8a707..4456db2 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -106,9 +106,11 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightPWM, }; - public override byte ThumbnailsCount { get; } = 2; - - public override Size[] ThumbnailsOriginalSize { get; } = {new Size(954, 850), new Size(168, 150)}; + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(954, 850), + new(168, 150) + }; public override uint ResolutionX { @@ -150,10 +152,10 @@ namespace UVtools.Core.FileFormats } } - public override float MaxPrintHeight + public override float MachineZ { - get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MaxPrintHeight; - set => base.MaxPrintHeight = HeaderSettings.MachineZ = (float)Math.Round(value, 2); + get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ; + set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2); } public override bool MirrorDisplay diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index 9aa0eda..71fc401 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -697,9 +697,11 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightOffDelay, }; - public override byte ThumbnailsCount { get; } = 2; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(400, 300), new System.Drawing.Size(200, 125)}; + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(400, 300), + new(200, 125) + }; public override uint ResolutionX { @@ -742,10 +744,10 @@ namespace UVtools.Core.FileFormats } } - public override float MaxPrintHeight + public override float MachineZ { - get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MaxPrintHeight; - set => base.MaxPrintHeight = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); + get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ; + set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); } public override bool MirrorDisplay diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index d5cdbbf..2fe8a04 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -211,8 +211,11 @@ namespace UVtools.Core.FileFormats new CWSFile(), // CWS new ZCodeFile(), // zcode new ZCodexFile(), // zcodex - //new MakerbaseFile(), // MKS + new MDLPFile(), // MKS v1 + new GR1File(), // GR1 Workshop + new CXDLPFile(), // Creality Box new LGSFile(), // LGS, LGS30 + new VDTFile(), // VDT new UVJFile(), // UVJ new ImageFile(), // images }; @@ -237,7 +240,7 @@ namespace UVtools.Core.FileFormats { var result = new List<KeyValuePair<string, List<string>>> { - new KeyValuePair<string, List<string>>("All slicer files", new List<string>()) + new("All slicer files", new List<string>()) }; for (int i = 0; i < AvailableFormats.Length; i++) @@ -287,10 +290,14 @@ namespace UVtools.Core.FileFormats /// <returns><see cref="FileFormat"/> object or null if not found</returns> public static FileFormat FindByExtension(string extension, bool isFilePath = false, bool createNewInstance = false) { - return (from fileFormat in AvailableFormats where fileFormat.IsExtensionValid(extension, isFilePath) select createNewInstance ? (FileFormat) Activator.CreateInstance(fileFormat.GetType()) : fileFormat).FirstOrDefault(); + if (isFilePath) + { + GetFileNameStripExtensions(extension, out extension); + } + return (from fileFormat in AvailableFormats where fileFormat.IsExtensionValid(extension) select createNewInstance ? (FileFormat) Activator.CreateInstance(fileFormat.GetType()) : fileFormat).FirstOrDefault(); } - public static FileExtension FindExtension(string extension, bool isFilePath = false, bool createNewInstance = false) + public static FileExtension FindExtension(string extension) { return AvailableFormats.SelectMany(format => format.FileExtensions).FirstOrDefault(ext => ext.Equals(extension)); } @@ -324,7 +331,7 @@ namespace UVtools.Core.FileFormats private LayerManager _layerManager; private float _printTime; private float _materialMilliliters; - private float _maxPrintHeight; + private float _machineZ; private ushort _bottomLayerCount = DefaultBottomLayerCount; private float _bottomExposureTime = DefaultBottomExposureTime; private float _exposureTime = DefaultExposureTime; @@ -444,7 +451,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the thumbnails count present in this file format /// </summary> - public abstract byte ThumbnailsCount { get; } + public byte ThumbnailsCount => (byte)(ThumbnailsOriginalSize?.Length ?? 0); /// <summary> /// Gets the number of created thumbnails @@ -594,10 +601,10 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets or sets the maximum printer build Z volume /// </summary> - public virtual float MaxPrintHeight + public virtual float MachineZ { - get => _maxPrintHeight > 0 ? _maxPrintHeight : PrintHeight; - set => RaiseAndSetIfChanged(ref _maxPrintHeight, value); + get => _machineZ > 0 ? _machineZ : PrintHeight; + set => RaiseAndSetIfChanged(ref _machineZ, value); } /// <summary> @@ -727,7 +734,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Checks if this file format supports per layer settings /// </summary> - public virtual bool SupportPerLayerSettings => PrintParameterPerLayerModifiers is not null && PrintParameterPerLayerModifiers.Length > 0; + public bool SupportPerLayerSettings => PrintParameterPerLayerModifiers is not null && PrintParameterPerLayerModifiers.Length > 0; /// <summary> /// Gets or sets the layer count @@ -1161,7 +1168,10 @@ namespace UVtools.Core.FileFormats /// <returns>True if valid, otherwise false</returns> public bool IsExtensionValid(string extension, bool isFilePath = false) { - extension = isFilePath ? Path.GetExtension(extension)?.Remove(0, 1) : extension; + if (isFilePath) + { + GetFileNameStripExtensions(extension, out extension); + } return FileExtensions.Any(fileExtension => fileExtension.Equals(extension)); } @@ -1352,7 +1362,28 @@ namespace UVtools.Core.FileFormats "Lower and fix your layer height on slicer to avoid precision errors.", fileFullPath); } - if (_layerManager.Sanitize()) + bool reSaveFile = false; + + if(ResolutionX == 0 || ResolutionY == 0) + { + var layer = FirstLayer; + if (layer is not null) + { + using var mat = layer.LayerMat; + + if (mat.Size.HaveZero()) + { + throw new FileLoadException($"File resolution ({Resolution}) is invalid and can't be auto fixed due invalid layers with same problem ({mat.Size}).", fileFullPath); + } + + Resolution = mat.Size; + reSaveFile = true; + } + } + + reSaveFile |= _layerManager.Sanitize(); + + if (reSaveFile) { Save(progress); } @@ -1815,7 +1846,7 @@ namespace UVtools.Core.FileFormats slicerFile.ResolutionY = ResolutionY; slicerFile.DisplayWidth = DisplayWidth; slicerFile.DisplayHeight = DisplayHeight; - slicerFile.MaxPrintHeight = MaxPrintHeight; + slicerFile.MachineZ = MachineZ; slicerFile.MirrorDisplay = MirrorDisplay; slicerFile.BottomExposureTime = BottomExposureTime; slicerFile.ExposureTime = ExposureTime; diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs new file mode 100644 index 0000000..74b4019 --- /dev/null +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -0,0 +1,531 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using BinarySerialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + public class GR1File : FileFormat + { + #region Constants + + private const uint SlicerInfoAddress = 4 + 7 + 290 * 290 * 2 + 116 * 116 * 2 + 4; + #endregion + + #region Sub Classes + + #region Header + + public sealed class Header + { + public const byte HEADER_SIZE = 7; + public const string HEADER_VALUE = "MKSDLP"; + + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint HeaderSize { get; set; } = HEADER_SIZE; + + /// <summary> + /// Gets the file tag = MKSDLP + /// </summary> + [FieldOrder(1)] + [FieldLength(HEADER_SIZE)] + [SerializeAs(SerializedType.TerminatedString)] + public string HeaderValue { get; set; } = HEADER_VALUE; + } + #endregion + + #region SlicerInfo + + public sealed class SlicerInfo + { + // 290 * 290 * 2 + 116 * 116 * 2 + 4 + //[FieldOrder(0)] [FieldLength(195116)] public byte[] PreviewData { get; set; } + + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort LayerCount { get; set; } + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; } + [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; } + [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint DisplayWidthDataSize { get; set; } = 6; + [FieldOrder(4)] [FieldLength(nameof(DisplayWidthDataSize))] public byte[] DisplayWidth { get; set; } + [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DisplayHeightDataSize { get; set; } = 6; + [FieldOrder(6)] [FieldLength(nameof(DisplayHeightDataSize))] public byte[] DisplayHeight { get; set; } + + [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint LayerHeightDataSize { get; set; } = 6; + [FieldOrder(8)] [FieldLength(nameof(LayerHeightDataSize))] public byte[] LayerHeight { get; set; } + [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public ushort ExposureTime { get; set; } + [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LightOffDelay { get; set; } + [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort BottomExposureTime { get; set; } + [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort BottomLayers { get; set; } + [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftHeight { get; set; } + [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeed { get; set; } + [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public ushort LiftHeight { get; set; } + [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeed { get; set; } + [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeed { get; set; } + [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public ushort BottomLightPWM { get; set; } + [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public ushort LightPWM { get; set; } + } + #endregion + + #region LayerDef + + public sealed class LayerDef + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; } + [FieldOrder(1)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; } + [FieldOrder(2)] public PageBreak PageBreak { get; set; } = new(); + + public LayerDef() { } + + public LayerDef(uint lineCount, LayerLine[] lines) + { + LineCount = lineCount; + Lines = lines; + } + } + + public sealed class LayerLine + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; } + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort EndY { get; set; } + [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartX { get; set; } + //[FieldOrder(3)] [FieldEndianness(Endianness.Big)] public byte Gray { get; set; } + + + public LayerLine() + { } + + public LayerLine(ushort startY, ushort endY, ushort startX) + { + StartY = startY; + EndY = endY; + StartX = startX; + } + } + + public sealed class PageBreak + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public byte Line { get; set; } = 0x0D; + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public byte Break { get; set; } = 0x0A; + } + + #endregion + #endregion + + #region Properties + + public Header HeaderSettings { get; protected internal set; } = new(); + public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new ("gr1", "GR1 Workshop") + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = + { + PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.BottomExposureSeconds, + PrintParameterModifier.ExposureSeconds, + + PrintParameterModifier.LightOffDelay, + PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.RetractSpeed, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM, + }; + + public override Size[] ThumbnailsOriginalSize { get; } = + { + new (116, 116), + new (290, 290) + }; + + public override uint ResolutionX + { + get => SlicerInfoSettings.ResolutionX; + set + { + SlicerInfoSettings.ResolutionX = (ushort) value; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get => SlicerInfoSettings.ResolutionY; + set + { + SlicerInfoSettings.ResolutionY = (ushort) value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => float.Parse(SlicerInfoSettings.DisplayWidth.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayWidthDataSize = (uint) (str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayWidthDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayWidth = data; + RaisePropertyChanged(); + } + } + + public override float DisplayHeight + { + get => float.Parse(SlicerInfoSettings.DisplayHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayHeight = data; + RaisePropertyChanged(); + } + } + + public override bool MirrorDisplay { get; set; } + + public override byte AntiAliasing + { + get => 1; + set { } + } + + public override float LayerHeight + { + get => float.Parse(SlicerInfoSettings.LayerHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Layer.RoundHeight(value).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.LayerHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.LayerHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.LayerHeight = data; + RaisePropertyChanged(); + } + } + + public override uint LayerCount + { + get => base.LayerCount; + set => base.LayerCount = SlicerInfoSettings.LayerCount = (ushort)base.LayerCount; + } + + public override ushort BottomLayerCount + { + get => SlicerInfoSettings.BottomLayers; + set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value; + } + + public override float BottomExposureTime + { + get => SlicerInfoSettings.BottomExposureTime; + set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value; + } + + public override float ExposureTime + { + get => SlicerInfoSettings.ExposureTime; + set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value; + } + + public override float BottomLiftHeight + { + get => SlicerInfoSettings.BottomLiftHeight; + set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value; + } + + public override float LiftHeight + { + get => SlicerInfoSettings.LiftHeight; + set => base.LiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value; + } + + public override float BottomLiftSpeed + { + get => SlicerInfoSettings.BottomLiftSpeed; + set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value; + } + + public override float LiftSpeed + { + get => SlicerInfoSettings.LiftSpeed; + set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value; + } + + public override float RetractSpeed + { + get => SlicerInfoSettings.RetractSpeed; + set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)value; + } + + public override float BottomLightOffDelay => SlicerInfoSettings.LightOffDelay; + + public override float LightOffDelay + { + get => SlicerInfoSettings.LightOffDelay; + set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; + } + + public override byte BottomLightPWM + { + get => (byte)SlicerInfoSettings.BottomLightPWM; + set => base.BottomLightPWM = (byte)(SlicerInfoSettings.BottomLightPWM = value); + } + + public override byte LightPWM + { + get => (byte)SlicerInfoSettings.LightPWM; + set => base.LightPWM = (byte)(SlicerInfoSettings.LightPWM = value); + } + + public override object[] Configs => new[] { (object)HeaderSettings, SlicerInfoSettings }; + + #endregion + + #region Constructors + + #endregion + + #region Methods + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + var pageBreak = new PageBreak(); + + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + + byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + } + // Previews + Parallel.For(0, previews.Length, previewIndex => + { + if (progress.Token.IsCancellationRequested) return; + if (Thumbnails[previewIndex] is null) return; + var span = Thumbnails[previewIndex].GetPixelSpanByte(); + int index = 0; + for (int i = 0; i < span.Length; i += 3) + { + byte b = span[i]; + byte g = span[i + 1]; + byte r = span[i + 2]; + + ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); + + previews[previewIndex][index++] = (byte)(rgb15 >> 8); + previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + } + + if (index != previews[previewIndex].Length) + { + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + } + }); + + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + Helpers.SerializeWriteFileStream(outputFile, previews[i]); + Helpers.SerializeWriteFileStream(outputFile, pageBreak); + } + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var layerDefs = new LayerDef[LayerCount]; + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + List<LayerLine> layerLines = new(); + var layer = this[layerIndex]; + using var mat = layer.LayerMat; + var span = mat.GetPixelSpanByte(); + + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) + { + int y = layer.BoundingRectangle.Y; + int startY = -1; + for (; y < layer.BoundingRectangle.Bottom; y++) + { + int pos = mat.GetPixelPos(x, y); + if (span[pos] < 128) // Black pixel + { + if(startY == -1) continue; // Keep ignoring + layerLines.Add(new LayerLine((ushort) startY, (ushort) (y-1), (ushort) x)); + startY = -1; + } + else // White pixel + { + if (startY >= 0) continue; // Keep sum + startY = y; + } + } + + if (startY >= 0) + { + layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort) x)); + } + } + + layerDefs[layerIndex] = new LayerDef((uint) layerLines.Count, layerLines.ToArray()); + + progress.LockAndIncrement(); + }); + + progress.Reset(OperationProgress.StatusWritingFile, LayerCount); + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + Helpers.SerializeWriteFileStream(outputFile, layerDefs[layerIndex]); + progress++; + } + + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine("-End-"); + } + + + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + if (HeaderSettings.HeaderValue != Header.HEADER_VALUE) + { + throw new FileLoadException("Not a valid Makerbase file!", fileFullPath); + } + + byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + inputFile.ReadBytes(previews[i]); + inputFile.Seek(2, SeekOrigin.Current); + } + + Parallel.For(0, previews.Length, previewIndex => + { + var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); + var span = mat.GetPixelSpanByte(); + + int spanIndex = 0; + for (int i = 0; i < previews[previewIndex].Length; i += 2) + { + ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); + byte r = (byte)((rgb15 >> 11) << 3); + byte g = (byte)((rgb15 >> 5) << 2); + byte b = (byte)((rgb15 >> 0) << 3); + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + Thumbnails[previewIndex] = mat; + }); + + + SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile); + + LayerManager.Init(SlicerInfoSettings.LayerCount); + progress.ItemCount = LayerCount; + LayerDef[] layerDefs = new LayerDef[LayerCount]; + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerDefs[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile); + progress++; + } + + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using var mat = EmguExtensions.InitMat(Resolution); + foreach (var line in layerDefs[layerIndex].Lines) + { + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), EmguExtensions.WhiteByte); + } + + this[layerIndex] = new Layer((uint) layerIndex, mat, this); + progress.LockAndIncrement(); + }); + + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + if (HeaderSettings.HeaderValue != Header.HEADER_VALUE) + { + throw new FileLoadException("Not a valid Makerbase file!", fileFullPath); + } + } + + public override void SaveAs(string filePath = null, OperationProgress progress = null) + { + if (RequireFullEncode) + { + if (!string.IsNullOrEmpty(filePath)) + { + FileFullPath = filePath; + } + Encode(FileFullPath, progress); + return; + } + + + if (!string.IsNullOrEmpty(filePath)) + { + File.Copy(FileFullPath, filePath, true); + FileFullPath = filePath; + } + + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + } + + #endregion + } +} diff --git a/UVtools.Core/FileFormats/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs index 668e2a8..42c4323 100644 --- a/UVtools.Core/FileFormats/ImageFile.cs +++ b/UVtools.Core/FileFormats/ImageFile.cs @@ -20,8 +20,13 @@ namespace UVtools.Core.FileFormats new ("tga", "TGA"), }; public override PrintParameterModifier[] PrintParameterModifiers { get; } = null; - public override byte ThumbnailsCount { get; } = 4; - public override Size[] ThumbnailsOriginalSize { get; } = null; + + public override Size[] ThumbnailsOriginalSize { get; } = { + Size.Empty, + Size.Empty, + Size.Empty, + Size.Empty + }; public override uint ResolutionX { get => (uint) ImageMat.Width; diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 03143e3..cb3d38d 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -225,9 +225,7 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightOffDelay, }; - public override byte ThumbnailsCount { get; } = 1; - - public override Size[] ThumbnailsOriginalSize { get; } = {new Size(120, 150)}; + public override Size[] ThumbnailsOriginalSize { get; } = {new(120, 150)}; public override uint ResolutionX { diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs new file mode 100644 index 0000000..784ad28 --- /dev/null +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -0,0 +1,493 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using BinarySerialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + public class MDLPFile : FileFormat + { + #region Constants + + private const uint SlicerInfoAddress = 4 + 7 + 290 * 290 * 2 + 116 * 116 * 2 + 4; + #endregion + + #region Sub Classes + + #region Header + + public sealed class Header + { + public const byte HEADER_SIZE = 7; + public const string HEADER_VALUE = "MKSDLP"; + + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint HeaderSize { get; set; } = HEADER_SIZE; + + /// <summary> + /// Gets the file tag = MKSDLP + /// </summary> + [FieldOrder(1)] + [FieldLength(HEADER_SIZE)] + [SerializeAs(SerializedType.TerminatedString)] + public string HeaderValue { get; set; } = HEADER_VALUE; + + public void Validate() + { + if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE) + { + throw new FileLoadException("Not a valid Makerbase mdlp file!"); + } + } + } + + public sealed class SlicerInfo + { + // 290 * 290 * 2 + 116 * 116 * 2 + 4 + //[FieldOrder(0)] [FieldLength(195116)] public byte[] PreviewData { get; set; } + + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort LayerCount { get; set; } + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; } + [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; } + [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint DisplayWidthDataSize { get; set; } = 6; + [FieldOrder(4)] [FieldLength(nameof(DisplayWidthDataSize))] public byte[] DisplayWidth { get; set; } + [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DisplayHeightDataSize { get; set; } = 6; + [FieldOrder(6)] [FieldLength(nameof(DisplayHeightDataSize))] public byte[] DisplayHeight { get; set; } + + [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint LayerHeightDataSize { get; set; } = 6; + [FieldOrder(8)] [FieldLength(nameof(LayerHeightDataSize))] public byte[] LayerHeight { get; set; } + [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public ushort ExposureTime { get; set; } + [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LightOffDelay { get; set; } + [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort BottomExposureTime { get; set; } + [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort BottomLayers { get; set; } + /* [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftHeight { get; set; } + [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeed { get; set; } + [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public ushort LiftHeight { get; set; } + [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeed { get; set; } + [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeed { get; set; } + [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public ushort BottomLightPWM { get; set; } + [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public ushort LightPWM { get; set; }*/ + } + #endregion + + #region LayerDef + + public sealed class LayerDef + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; } + [FieldOrder(1)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; } + [FieldOrder(2)] public PageBreak PageBreak { get; set; } = new(); + + + public LayerDef(uint lineCount, LayerLine[] lines) + { + LineCount = lineCount; + Lines = lines; + } + } + + public sealed class LayerLine + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; } + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort EndY { get; set; } + [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartX { get; set; } + + + public LayerLine() + { } + + public LayerLine(ushort startY, ushort endY, ushort startX) + { + StartY = startY; + EndY = endY; + StartX = startX; + } + } + + public sealed class PageBreak + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public byte Line { get; set; } = 0x0D; + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public byte Break { get; set; } = 0x0A; + } + + #endregion + #endregion + + #region Properties + + public Header HeaderSettings { get; protected internal set; } = new(); + public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new ("mdlp", "Makerbase MDLP v1"), + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = + { + PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.BottomExposureSeconds, + PrintParameterModifier.ExposureSeconds, + + //PrintParameterModifier.BottomLightOffDelay, + PrintParameterModifier.LightOffDelay, + /*PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.RetractSpeed, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM,*/ + }; + + public override Size[] ThumbnailsOriginalSize { get; } = + { + new (116, 116), + new (290, 290) + }; + + public override uint ResolutionX + { + get => SlicerInfoSettings.ResolutionX; + set + { + SlicerInfoSettings.ResolutionX = (ushort) value; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get => SlicerInfoSettings.ResolutionY; + set + { + SlicerInfoSettings.ResolutionY = (ushort) value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => float.Parse(SlicerInfoSettings.DisplayWidth.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayWidthDataSize = (uint) (str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayWidthDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayWidth = data; + RaisePropertyChanged(); + } + } + + public override float DisplayHeight + { + get => float.Parse(SlicerInfoSettings.DisplayHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayHeight = data; + RaisePropertyChanged(); + } + } + + public override bool MirrorDisplay { get; set; } + + public override byte AntiAliasing + { + get => 1; + set { } + } + + public override float LayerHeight + { + get => float.Parse(SlicerInfoSettings.LayerHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Layer.RoundHeight(value).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.LayerHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.LayerHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.LayerHeight = data; + RaisePropertyChanged(); + } + } + + public override uint LayerCount + { + get => base.LayerCount; + set => base.LayerCount = SlicerInfoSettings.LayerCount = (ushort)base.LayerCount; + } + + public override ushort BottomLayerCount + { + get => SlicerInfoSettings.BottomLayers; + set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value; + } + + public override float BottomExposureTime + { + get => SlicerInfoSettings.BottomExposureTime; + set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value; + } + + public override float ExposureTime + { + get => SlicerInfoSettings.ExposureTime; + set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value; + } + + public override float BottomLightOffDelay + { + get => LightOffDelay; + set { } + } + + public override float LightOffDelay + { + get => SlicerInfoSettings.LightOffDelay; + set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; + } + + public override object[] Configs => new[] { (object)HeaderSettings, SlicerInfoSettings }; + + #endregion + + #region Constructors + + #endregion + + #region Methods + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + var pageBreak = new PageBreak(); + + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + + byte[][] previews = new byte[2][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + } + // Previews + Parallel.For(0, previews.Length, previewIndex => + { + if (progress.Token.IsCancellationRequested) return; + if (Thumbnails[previewIndex] is null) return; + var span = Thumbnails[previewIndex].GetPixelSpanByte(); + int index = 0; + for (int i = 0; i < span.Length; i += 3) + { + byte b = span[i]; + byte g = span[i + 1]; + byte r = span[i + 2]; + + ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); + + previews[previewIndex][index++] = (byte)(rgb15 >> 8); + previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + } + + if (index != previews[previewIndex].Length) + { + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + } + }); + + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + Helpers.SerializeWriteFileStream(outputFile, previews[i]); + Helpers.SerializeWriteFileStream(outputFile, pageBreak); + } + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var layerDefs = new LayerDef[LayerCount]; + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + List<LayerLine> layerLines = new(); + var layer = this[layerIndex]; + using var mat = layer.LayerMat; + var span = mat.GetPixelSpanByte(); + + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) + { + int y = layer.BoundingRectangle.Y; + int startY = -1; + for (; y < layer.BoundingRectangle.Bottom; y++) + { + int pos = mat.GetPixelPos(x, y); + if (span[pos] < 128) // Black pixel + { + if(startY == -1) continue; // Keep ignoring + layerLines.Add(new LayerLine((ushort) startY, (ushort) (y-1), (ushort) x)); + startY = -1; + } + else // White pixel + { + if (startY >= 0) continue; // Keep sum + startY = y; + } + } + + if (startY >= 0) + { + layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort) x)); + } + } + + layerDefs[layerIndex] = new LayerDef((uint) layerLines.Count, layerLines.ToArray()); + + progress.LockAndIncrement(); + }); + + progress.Reset(OperationProgress.StatusWritingFile, LayerCount); + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + Helpers.SerializeWriteFileStream(outputFile, layerDefs[layerIndex]); + progress++; + } + + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine("-End-"); + } + + + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); + //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + HeaderSettings.Validate(); + + byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + inputFile.ReadBytes(previews[i]); + inputFile.Seek(2, SeekOrigin.Current); + } + + Parallel.For(0, previews.Length, previewIndex => + { + var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); + var span = mat.GetPixelSpanByte(); + + int spanIndex = 0; + for (int i = 0; i < previews[previewIndex].Length; i += 2) + { + ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); + byte r = (byte)((rgb15 >> 11) << 3); + byte g = (byte)((rgb15 >> 5) << 2); + byte b = (byte)((rgb15 >> 0) << 3); + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + Thumbnails[previewIndex] = mat; + }); + + + SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile); + + LayerManager.Init(SlicerInfoSettings.LayerCount); + progress.ItemCount = LayerCount; + LayerDef[] layerDefs = new LayerDef[LayerCount]; + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerDefs[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile); + progress++; + } + + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using var mat = EmguExtensions.InitMat(Resolution); + foreach (var line in layerDefs[layerIndex].Lines) + { + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), EmguExtensions.WhiteByte); + } + + this[layerIndex] = new Layer((uint) layerIndex, mat, this); + progress.LockAndIncrement(); + }); + + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + HeaderSettings.Validate(); + } + + public override void SaveAs(string filePath = null, OperationProgress progress = null) + { + if (RequireFullEncode) + { + if (!string.IsNullOrEmpty(filePath)) + { + FileFullPath = filePath; + } + Encode(FileFullPath, progress); + return; + } + + + if (!string.IsNullOrEmpty(filePath)) + { + File.Copy(FileFullPath, filePath, true); + FileFullPath = filePath; + } + + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + } + + #endregion + } +} diff --git a/UVtools.Core/FileFormats/MakerbaseFile.cs b/UVtools.Core/FileFormats/MakerbaseFile.cs deleted file mode 100644 index 96c49d1..0000000 --- a/UVtools.Core/FileFormats/MakerbaseFile.cs +++ /dev/null @@ -1,240 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc - -using System; -using System.Diagnostics; -using System.Drawing; -using System.IO; -using BinarySerialization; -using UVtools.Core.Operations; - -namespace UVtools.Core.FileFormats -{ - public class MakerbaseFile : FileFormat - { - #region Constants - private const uint MAGIC_CBDDLP = 0x12FD0019; - private const uint MAGIC_CBT = 0x12FD0086; - private const ushort REPEATRGB15MASK = 0x20; - - private const byte RLE8EncodingLimit = 0x7d; // 125; - private const ushort RLE16EncodingLimit = 0xFFF; - #endregion - - #region Sub Classes - - #region Header - public class Header - { - public const string TagValue = "MKSDLP"; - //[FieldOrder(0)] public uint Offset1 { get; set; } - - /// <summary> - /// Gets the file tag = MKSDLP - /// </summary> - //[SerializeAs(SerializedType.TerminatedString)] - [FieldOrder(0)] [FieldOffset(4)] [FieldLength(6)] public string Tag { get; set; } = TagValue; - - // 290 * 290 * 2 + 116 * 116 * 2 + 4 + 1 - [FieldOrder(1)] [FieldLength(195116)] public byte[] PreviewData { get; set; } - - [FieldOrder(2)] public ushort MaxSize { get; set; } - [FieldOrder(3)] public ushort ResolutionX { get; set; } - [FieldOrder(4)] public ushort ResolutionY { get; set; } - - - } - #endregion - - #endregion - - #region Properties - - public Header HeaderSettings { get; protected internal set; } = new Header(); - public override FileFormatType FileType => FileFormatType.Binary; - - public override FileExtension[] FileExtensions { get; } = { - new ("mdlp", "Makerbase MDLP Files"), - new ("gr1", "Workshop GR1 Files"), - }; - - public override PrintParameterModifier[] PrintParameterModifiers { get; } = - { - PrintParameterModifier.BottomLayerCount, - PrintParameterModifier.BottomExposureSeconds, - PrintParameterModifier.ExposureSeconds, - - PrintParameterModifier.BottomLightOffDelay, - PrintParameterModifier.LightOffDelay, - PrintParameterModifier.BottomLiftHeight, - PrintParameterModifier.BottomLiftSpeed, - PrintParameterModifier.LiftHeight, - PrintParameterModifier.LiftSpeed, - PrintParameterModifier.RetractSpeed, - - PrintParameterModifier.BottomLightPWM, - PrintParameterModifier.LightPWM, - }; - - public override byte ThumbnailsCount { get; } = 0; - - public override Size[] ThumbnailsOriginalSize { get; } = {new (290, 290), new (116, 116) }; - - public override uint ResolutionX - { - get => 0; - set - { - } - } - - public override uint ResolutionY - { - get => 0; - set { } - } - - public override float DisplayWidth { get; set; } - public override float DisplayHeight { get; set; } - public override bool MirrorDisplay { get; set; } - - public override byte AntiAliasing - { - get => 1; - set { } - } - - public override float LayerHeight - { - get => 0; - set { } - } - - public override uint LayerCount - { - set - { - - /*HeaderSettings.LayerCount = LayerCount; - HeaderSettings.OverallHeightMilimeter = TotalHeight;*/ - } - } - - public override ushort BottomLayerCount => 0; - - public override float BottomExposureTime => 0; - - public override float ExposureTime => 0; - public override float LiftHeight => 0; - public override float LiftSpeed => 0; - public override float RetractSpeed => 0; - - public override float PrintTime => 0; - - public override float MaterialMilliliters => 0; - - public override float MaterialCost => 0; - - public override string MaterialName => "Unknown"; - public override string MachineName => null; - - public override object[] Configs => new[] { (object)HeaderSettings }; - - #endregion - - #region Constructors - - #endregion - - #region Methods - protected override void EncodeInternally(string fileFullPath, OperationProgress progress) - { - uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) - { - - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); - - - - } - - Debug.WriteLine("Encode Results:"); - Debug.WriteLine(HeaderSettings); - Debug.WriteLine("-End-"); - } - - - - protected override void DecodeInternally(string fileFullPath, OperationProgress progress) - { - using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); - //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); - //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - if (HeaderSettings.Tag != Header.TagValue) - { - throw new FileLoadException("Not a valid Makerbase file!", fileFullPath); - } - - /*var rng = 290 * 290 * 2 + 116 * 116 * 2 + 4; - byte[] buffer = new byte[rng]; - inputFile.Read(buffer, 0, rng);*/ - } - - public override void SaveAs(string filePath = null, OperationProgress progress = null) - { - if (RequireFullEncode) - { - if (!string.IsNullOrEmpty(filePath)) - { - FileFullPath = filePath; - } - Encode(FileFullPath, progress); - return; - } - - - if (!string.IsNullOrEmpty(filePath)) - { - File.Copy(FileFullPath, filePath, true); - FileFullPath = filePath; - } - - /*using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - - if (HeaderSettings.Version >= 2 && HeaderSettings.PrintParametersOffsetAddress > 0) - { - outputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings); - Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); - } - - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) - { - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - outputFile.Seek(layerOffset, SeekOrigin.Begin); - layerOffset += Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[aaIndex, layerIndex]); - } - } - }*/ - - //Decode(FileFullPath, progress); - } - - #endregion - } -} diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index 2a6491f..3131b43 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -678,18 +678,18 @@ namespace UVtools.Core.FileFormats #region Properties - public Header HeaderSettings { get; protected internal set; } = new Header(); + public Header HeaderSettings { get; protected internal set; } = new(); public Preview[] Previews { get; protected internal set; } public LayerData[] LayersDefinitions { get; private set; } - public Dictionary<string, LayerData> LayersHash { get; } = new Dictionary<string, LayerData>(); + public Dictionary<string, LayerData> LayersHash { get; } = new (); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { - new FileExtension("phz", "Chitubox PHZ"), + new ("phz", "Chitubox PHZ"), }; public override PrintParameterModifier[] PrintParameterModifiers { get; } = @@ -715,9 +715,11 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightOffDelay, }; - public override byte ThumbnailsCount { get; } = 2; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new System.Drawing.Size(400, 300), new System.Drawing.Size(200, 125)}; + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(400, 300), + new(200, 125) + }; public override uint ResolutionX { @@ -760,9 +762,9 @@ namespace UVtools.Core.FileFormats } } - public override float MaxPrintHeight + public override float MachineZ { - get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MaxPrintHeight; + get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ; set { HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index d881a4f..e2391a7 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -220,9 +220,8 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.RetractSpeed, }; - public override byte ThumbnailsCount { get; } = 1; - public override Size[] ThumbnailsOriginalSize { get; } = {new Size(225, 168) }; + public override Size[] ThumbnailsOriginalSize { get; } = {new(225, 168) }; public override uint ResolutionX { diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 00e9670..4fbe61f 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -941,9 +941,7 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LiftSpeed, }; - public override byte ThumbnailsCount { get; } = 1; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new Size(224, 168)}; + public override Size[] ThumbnailsOriginalSize { get; } = {new(224, 168)}; public override uint ResolutionX { @@ -1014,7 +1012,7 @@ namespace UVtools.Core.FileFormats set { } } - public override float MaxPrintHeight + public override float MachineZ { get { diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 05e32ae..d17adab 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -311,10 +311,11 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.ExposureSeconds, }; - public override byte ThumbnailsCount { get; } = 2; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = { new System.Drawing.Size(400, 400), new System.Drawing.Size(800, 480) }; - //public override Image<Rgba32>[] Thumbnails { get; set; } + public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = + { + new(400, 400), + new(800, 480) + }; public override uint ResolutionX { @@ -356,7 +357,7 @@ namespace UVtools.Core.FileFormats } } - public override float MaxPrintHeight + public override float MachineZ { get => PrinterSettings.MaxPrintHeight; set diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index bc45523..2838811 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -161,9 +161,11 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightPWM, }; - public override byte ThumbnailsCount { get; } = 2; - - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new(400, 400), new(800, 480) }; + public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = + { + new(400, 400), + new(800, 480) + }; public override uint ResolutionX { @@ -340,39 +342,37 @@ namespace UVtools.Core.FileFormats }); } - using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create)) - { - outputFile.PutFileContent(FileConfigName, JsonConvert.SerializeObject(JsonSettings), ZipArchiveMode.Create); + using ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create); + outputFile.PutFileContent(FileConfigName, JsonConvert.SerializeObject(JsonSettings), ZipArchiveMode.Create); - if (CreatedThumbnailsCount > 0) - { - using var stream = outputFile.CreateEntry(FilePreviewTinyName).Open(); - using var vec = new VectorOfByte(); - CvInvoke.Imencode(".png", Thumbnails[0], vec); - stream.WriteBytes(vec.ToArray()); - stream.Close(); - } + if (CreatedThumbnailsCount > 0) + { + using var stream = outputFile.CreateEntry(FilePreviewTinyName).Open(); + using var vec = new VectorOfByte(); + CvInvoke.Imencode(".png", Thumbnails[0], vec); + stream.WriteBytes(vec.ToArray()); + stream.Close(); + } - if (CreatedThumbnailsCount > 1) - { - using Stream stream = outputFile.CreateEntry(FilePreviewHugeName).Open(); - using var vec = new VectorOfByte(); - CvInvoke.Imencode(".png", Thumbnails[1], vec); - stream.WriteBytes(vec.ToArray()); - stream.Close(); - } + if (CreatedThumbnailsCount > 1) + { + using Stream stream = outputFile.CreateEntry(FilePreviewHugeName).Open(); + using var vec = new VectorOfByte(); + CvInvoke.Imencode(".png", Thumbnails[1], vec); + stream.WriteBytes(vec.ToArray()); + stream.Close(); + } - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - progress.Token.ThrowIfCancellationRequested(); + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); - Layer layer = this[layerIndex]; + Layer layer = this[layerIndex]; - var layerimagePath = $"{FolderImageName}/{layerIndex:D8}.png"; - outputFile.PutFileContent(layerimagePath, layer.CompressedBytes, ZipArchiveMode.Create); + var layerimagePath = $"{FolderImageName}/{layerIndex:D8}.png"; + outputFile.PutFileContent(layerimagePath, layer.CompressedBytes, ZipArchiveMode.Create); - progress++; - } + progress++; } } @@ -418,7 +418,8 @@ namespace UVtools.Core.FileFormats entry = inputFile.GetEntry($"{FolderImageName}/{layerIndex:D8}.png"); if (entry is null) continue; - this[layerIndex] = new Layer(layerIndex, entry.Open(), LayerManager) + using var stream = entry.Open(); + this[layerIndex] = new Layer(layerIndex, stream, LayerManager) { PositionZ = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int) layerIndex].Z : GetHeightFromLayer(layerIndex), LiftHeight = JsonSettings.Layers.Count >= layerIndex ? JsonSettings.Layers[(int)layerIndex].Exposure.LiftHeight : GetInitialLayerValueOrNormal(layerIndex, BottomLiftHeight, LiftHeight), diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs new file mode 100644 index 0000000..5dfa02c --- /dev/null +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -0,0 +1,518 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.IO; +using System.IO.Compression; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Util; +using Newtonsoft.Json; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + public class VDTFile : FileFormat + { + #region Constants + + private const string FileManifestName = "manifest.json"; + private static string[] FilePreviewNames = { + "Preview_Top.png", + "Preview_Top_48.png", + "Preview_Right.png", + "Preview_Right_48.png", + "Preview_Left.png", + "Preview_Left_48.png", + "Preview_Front.png", + "Preview_Front_48.png", + "Preview_FLT.png", + "Preview_FLT_48.png", + }; + #endregion + + #region Sub Classes + + public sealed class VDTManifest + { + [JsonProperty("application_name")] public string ApplicationName { get; set; } = "Voxeldance Tango"; + + [JsonProperty("application_version")] public string ApplicationVersion { get; set; } = "2.1.15.14"; + //2021-04-09 17:48:46 + [JsonProperty("create_datetime")] public string CreateDateTime { get; set; } = DateTime.Now.ToString("u"); + + [JsonProperty("file_version")] public byte FileVersion { get; set; } = 1; + + [JsonProperty("project_name")] public string ProjectName { get; set; } = "UVtools"; + + [JsonProperty("machine")] public VDTMachine Machine { get; set; } = new(); + [JsonProperty("advanced_parameters")] public VDTAdvancedParameters AdvancedParameters { get; set; } = new(); + [JsonProperty("resin")] public VDTResin Resin { get; set; } = new(); + [JsonProperty("print")] public VDTPrint Print { get; set; } = new(); + [JsonProperty("print_statistics")] public VDTPrintStatistics PrintStatistics { get; set; } = new(); + [JsonProperty("layers")] public VDTLayer[] Layers { get; set; } + } + + public sealed class VDTAdvancedParameters + { + [JsonProperty("antialasing_level")] public byte AntialasingLevel { get; set; } = 1; + [JsonProperty("grey_level")] public byte GreyLevel { get; set; } + [JsonProperty("image_blur_level")] public byte ImageBlurLevel { get; set; } + + [JsonProperty("bottom_light_pwm")] public byte BottomLightPWM { get; set; } = DefaultBottomLightPWM; + [JsonProperty("light_pwm")] public byte LightPWM { get; set; } = DefaultLightPWM; + + [JsonProperty("beam_compensation")] public float BeamCompensation { get; set; } + } + + public sealed class VDTMachine + { + [JsonProperty("name")] public string Name { get; set; } = "Unknown"; + [JsonProperty("type")] public string Type { get; set; } = "Default"; + [JsonProperty("lcd_width")] public float DisplayWidth { get; set; } + [JsonProperty("lcd_height")] public float DisplayHeight { get; set; } + [JsonProperty("z_height")] public float ZHeight { get; set; } + [JsonProperty("resolution_x")] public ushort ResolutionX { get; set; } + [JsonProperty("resolution_y")] public ushort ResolutionY { get; set; } + [JsonProperty("x_mirror")] public bool XMirror { get; set; } + [JsonProperty("y_mirror")] public bool YMirror { get; set; } + } + + public sealed class VDTResin + { + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("cost")] public float Cost { get; set; } + [JsonProperty("density")] public float Density { get; set; } = 1; + } + + public sealed class VDTPrint + { + [JsonProperty("layer_thickness")] public float LayerHeight { get; set; } + [JsonProperty("bottom_layers")] public ushort BottomLayers { get; set; } = DefaultBottomLayerCount; + [JsonProperty("bottom_exposure_time")] public float BottomExposureTime { get; set; } = DefaultBottomExposureTime; + [JsonProperty("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime; + [JsonProperty("bottom_lift_distance")] public float BottomLiftHeight { get; set; } = DefaultBottomLiftHeight; + [JsonProperty("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight; + [JsonProperty("bottom_lift_speed")] public float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed; + [JsonProperty("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed; + [JsonProperty("bottom_retract_speed")] public float BottomRetractSpeed { get; set; } = DefaultRetractSpeed; + [JsonProperty("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed; + [JsonProperty("bottom_light_off_delay")] public float BottomLightOffDelay { get; set; } = DefaultBottomLightOffDelay; + [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; } = DefaultLightOffDelay; + } + + public sealed class VDTPrintStatistics + { + [JsonProperty("price")] public float Price { get; set; } + [JsonProperty("price_currency")] public string PriceCurrency { get; set; } = "€"; + [JsonProperty("estimated_time")] public uint EstimatedTime { get; set; } + [JsonProperty("volume")] public float Volume { get; set; } + [JsonProperty("weight")] public float Weight { get; set; } + } + + public sealed class VDTLayer + { + [JsonProperty("height")] public float PositionZ { get; set; } + [JsonProperty("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime; + [JsonProperty("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight; + [JsonProperty("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed; + [JsonProperty("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed; + [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; } = DefaultLightOffDelay; + [JsonProperty("light_pwm")] public byte LightPWM { get; set; } = DefaultLightPWM; + } + + #endregion + + #region Properties + public VDTManifest ManifestFile { get; set; } = new(); + + public override FileFormatType FileType => FileFormatType.Archive; + + public override FileExtension[] FileExtensions { get; } = { + new("vdt", "Voxeldance Tango VDT") + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = { + PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.BottomExposureSeconds, + PrintParameterModifier.ExposureSeconds, + + PrintParameterModifier.BottomLightOffDelay, + PrintParameterModifier.LightOffDelay, + PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.RetractSpeed, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM, + }; + + public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = { + PrintParameterModifier.ExposureSeconds, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.RetractSpeed, + PrintParameterModifier.LightOffDelay, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM, + }; + + public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = + { + new(200, 200), + new(48, 48), + new(200, 200), + new(48, 48), + new(200, 200), + new(48, 48), + new(200, 200), + new(48, 48), + new(200, 200), + new(48, 48), + }; + + public override uint ResolutionX + { + get => ManifestFile.Machine.ResolutionX; + set + { + ManifestFile.Machine.ResolutionX = (ushort) value; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get => ManifestFile.Machine.ResolutionY; + set + { + ManifestFile.Machine.ResolutionY = (ushort) value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => ManifestFile.Machine.DisplayWidth; + set + { + ManifestFile.Machine.DisplayWidth = (float) Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + public override float DisplayHeight + { + get => ManifestFile.Machine.DisplayHeight; + set + { + ManifestFile.Machine.DisplayHeight = (float)Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + public override float MachineZ + { + get => ManifestFile.Machine.ZHeight > 0 ? ManifestFile.Machine.ZHeight : base.MachineZ; + set + { + ManifestFile.Machine.ZHeight = (float)Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + public override bool MirrorDisplay => ManifestFile.Machine.XMirror || ManifestFile.Machine.YMirror; + + public override byte AntiAliasing + { + get => ManifestFile.AdvancedParameters.AntialasingLevel; + set + { + ManifestFile.AdvancedParameters.AntialasingLevel = value.Clamp(1, 16); + RaisePropertyChanged(); + } + + } + + public override float LayerHeight + { + get => ManifestFile.Print.LayerHeight; + set + { + ManifestFile.Print.LayerHeight = Layer.RoundHeight(value); + RaisePropertyChanged(); + } + } + + /* public override uint LayerCount + { + get => base.LayerCount; + set => base.LayerCount = base.LayerCount; + }*/ + + public override ushort BottomLayerCount + { + get => ManifestFile.Print.BottomLayers; + set => base.BottomLayerCount = ManifestFile.Print.BottomLayers = value; + } + + public override float BottomExposureTime + { + get => ManifestFile.Print.BottomExposureTime; + set => base.BottomExposureTime = ManifestFile.Print.BottomExposureTime = (float)Math.Round(value, 2); + } + + public override float ExposureTime + { + get => ManifestFile.Print.ExposureTime; + set => base.ExposureTime = ManifestFile.Print.ExposureTime = (float)Math.Round(value, 2); + } + + public override float BottomLightOffDelay + { + get => ManifestFile.Print.BottomLightOffDelay; + set => base.BottomLightOffDelay = ManifestFile.Print.BottomLightOffDelay = (float)Math.Round(value, 2); + } + + public override float LightOffDelay + { + get => ManifestFile.Print.LightOffDelay; + set => base.LightOffDelay = ManifestFile.Print.LightOffDelay = (float)Math.Round(value, 2); + } + + public override float BottomLiftHeight + { + get => ManifestFile.Print.BottomLiftHeight; + set => base.BottomLiftHeight = ManifestFile.Print.BottomLiftHeight = (float)Math.Round(value, 2); + } + + public override float LiftHeight + { + get => ManifestFile.Print.LiftHeight; + set => base.LiftHeight = ManifestFile.Print.LiftHeight = (float)Math.Round(value, 2); + } + + public override float BottomLiftSpeed + { + get => ManifestFile.Print.BottomLiftSpeed; + set => base.BottomLiftSpeed = ManifestFile.Print.BottomLiftSpeed = (float)Math.Round(value, 2); + } + + public override float LiftSpeed + { + get => ManifestFile.Print.LiftSpeed; + set => base.LiftSpeed = ManifestFile.Print.LiftSpeed = (float)Math.Round(value, 2); + } + + public override float RetractSpeed + { + get => ManifestFile.Print.RetractSpeed; + set => base.RetractSpeed = ManifestFile.Print.RetractSpeed = ManifestFile.Print.BottomRetractSpeed = (float)Math.Round(value, 2); + } + + public override byte BottomLightPWM + { + get => ManifestFile.AdvancedParameters.BottomLightPWM; + set => base.BottomLightPWM = ManifestFile.AdvancedParameters.BottomLightPWM = value; + } + + public override byte LightPWM + { + get => ManifestFile.AdvancedParameters.LightPWM; + set => base.LightPWM = ManifestFile.AdvancedParameters.LightPWM = value; + } + + public override float PrintTime + { + get => base.PrintTime; + set + { + base.PrintTime = value; + ManifestFile.PrintStatistics.EstimatedTime = (uint)base.PrintTime; + } + } + + public override float MaterialMilliliters + { + get => base.MaterialMilliliters; + set + { + base.MaterialMilliliters = value; + ManifestFile.PrintStatistics.Volume = base.MaterialMilliliters; + } + } + + public override float MaterialGrams + { + get => (float)Math.Round(ManifestFile.PrintStatistics.Weight, 3); + set => base.MaterialGrams = ManifestFile.PrintStatistics.Weight = (float)Math.Round(value, 3); + } + + public override string MaterialName + { + get => ManifestFile.Resin.Name; + set => base.MaterialName = ManifestFile.Resin.Name = value; + } + + public override float MaterialCost + { + get => (float)Math.Round(ManifestFile.Resin.Cost, 3); + set => base.MaterialCost = ManifestFile.Resin.Cost = (float)Math.Round(value, 3); + } + + public override string MachineName + { + get => ManifestFile.Machine.Name; + set => base.MachineName = ManifestFile.Machine.Name = value; + } + + public override object[] Configs => new[] {(object)ManifestFile, ManifestFile.Machine, ManifestFile.AdvancedParameters, ManifestFile.Resin, ManifestFile.Print, ManifestFile.PrintStatistics}; + #endregion + + #region Methods + + public void RebuildVDTLayers() + { + ManifestFile.CreateDateTime = DateTime.Now.ToString("u"); + var layers = new VDTLayer[LayerCount]; + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + var layer = this[layerIndex]; + layers[layerIndex] = new VDTLayer + { + PositionZ = layer.PositionZ, + ExposureTime = layer.ExposureTime, + LiftHeight = layer.LiftHeight, + LiftSpeed = layer.LiftSpeed, + RetractSpeed = layer.RetractSpeed, + LightOffDelay = layer.LightOffDelay, + LightPWM = layer.LightPWM + }; + } + + ManifestFile.Layers = layers; + } + + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + // Redo layer data + RebuildVDTLayers(); + + using var outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create); + outputFile.PutFileContent(FileManifestName, JsonConvert.SerializeObject(ManifestFile), ZipArchiveMode.Create); + + if (CreatedThumbnailsCount > 0) + { + for (int i = 0; i < FilePreviewNames.Length; i++) + { + if(Thumbnails.Length <= i) break; + if(Thumbnails[i] is null) continue; + + using var stream = outputFile.CreateEntry(FilePreviewNames[i]).Open(); + using var vec = new VectorOfByte(); + CvInvoke.Imencode(".png", Thumbnails[i], vec); + stream.WriteBytes(vec.ToArray()); + stream.Close(); + } + } + + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + + var layer = this[layerIndex]; + var layerImagePath = $"{layerIndex}.png"; + outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create); + progress++; + } + } + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using (var inputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Read)) + { + var entry = inputFile.GetEntry(FileManifestName); + if (entry is null) + { + Clear(); + throw new FileLoadException($"{FileManifestName} not found", fileFullPath); + } + + ManifestFile = Helpers.JsonDeserializeObject<VDTManifest>(entry.Open()); + + LayerManager.Init((uint) ManifestFile.Layers.Length); + + for (int i = 0; i < FilePreviewNames.Length; i++) + { + if (Thumbnails.Length <= i) break; + + entry = inputFile.GetEntry(FilePreviewNames[i]); + if (entry is null) continue; + using var stream = entry.Open(); + Mat image = new(); + CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image); + Thumbnails[i] = image; + stream.Close(); + } + + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + var manifestLayer = ManifestFile.Layers[layerIndex]; + entry = inputFile.GetEntry($"{layerIndex}.png"); + if (entry is null) continue; + using var stream = entry.Open(); + this[layerIndex] = new Layer(layerIndex, stream, LayerManager) + { + PositionZ = manifestLayer.PositionZ, + ExposureTime = manifestLayer.ExposureTime, + LiftHeight = manifestLayer.LiftHeight, + LiftSpeed = manifestLayer.LiftSpeed, + RetractSpeed = manifestLayer.RetractSpeed, + LightOffDelay = manifestLayer.LightOffDelay, + LightPWM = manifestLayer.LightPWM, + }; + } + + progress.ProcessedItems++; + } + + LayerManager.GetBoundingRectangle(progress); + } + + public override void SaveAs(string filePath = null, OperationProgress progress = null) + { + if (RequireFullEncode) + { + if (!string.IsNullOrEmpty(filePath)) + { + FileFullPath = filePath; + } + Encode(FileFullPath, progress); + return; + } + + if (!string.IsNullOrEmpty(filePath)) + { + File.Copy(FileFullPath, filePath, true); + FileFullPath = filePath; + + } + + RebuildVDTLayers(); + using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update); + outputFile.PutFileContent(FileManifestName, JsonConvert.SerializeObject(ManifestFile), ZipArchiveMode.Update); + + //Decode(FileFullPath, progress); + } + #endregion + } +} diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index 4d42c1d..412adb5 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -22,7 +22,6 @@ using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.OpenSsl; using UVtools.Core.Extensions; using UVtools.Core.GCode; -using UVtools.Core.Objects; using UVtools.Core.Operations; namespace UVtools.Core.FileFormats @@ -190,7 +189,7 @@ namespace UVtools.Core.FileFormats public override FileFormatType FileType => FileFormatType.Archive; public override FileExtension[] FileExtensions { get; } = { - new("zcode", "UnizMaker ZCode files") + new("zcode", "UnizMaker ZCode") }; public override PrintParameterModifier[] PrintParameterModifiers { get; } = { @@ -219,8 +218,6 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightPWM, }; - public override byte ThumbnailsCount { get; } = 1; - public override Size[] ThumbnailsOriginalSize { get; } = {new(640, 480)}; public override uint ResolutionX @@ -255,9 +252,9 @@ namespace UVtools.Core.FileFormats set => RaisePropertyChanged(); } - public override float MaxPrintHeight + public override float MachineZ { - get => ManifestFile.Device.MachineZ > 0 ? ManifestFile.Device.MachineZ : base.MaxPrintHeight; + get => ManifestFile.Device.MachineZ > 0 ? ManifestFile.Device.MachineZ : base.MachineZ; set { ManifestFile.Device.MachineZ = (ushort) value; diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index e0137a4..297ad99 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -182,8 +182,6 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightPWM, }; - public override byte ThumbnailsCount { get; } = 1; - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new(320, 180)}; public override uint ResolutionX diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index c59bb56..2ac2609 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -545,7 +545,7 @@ namespace UVtools.Core.GCode lastZPosition = layer.PositionZ; } - float finalRaiseZPosition = slicerFile.MaxPrintHeight; + float finalRaiseZPosition = slicerFile.MachineZ; switch (GCodePositioningType) { diff --git a/UVtools.Core/Helpers.cs b/UVtools.Core/Helpers.cs index 5217f80..5ba461c 100644 --- a/UVtools.Core/Helpers.cs +++ b/UVtools.Core/Helpers.cs @@ -41,10 +41,8 @@ namespace UVtools.Core public static uint SerializeWriteFileStream(FileStream fs, object value, int offset = 0) { - using (MemoryStream stream = Serialize(value)) - { - return fs.WriteStream(stream, offset); - } + using MemoryStream stream = Serialize(value); + return fs.WriteStream(stream, offset); } public static T JsonDeserializeObject<T>(Stream stream) diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index 1895bcd..84ab961 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -166,7 +166,7 @@ namespace UVtools.Core public void Clear() { //Layers = Array.Empty<Layer>(); - Layers = null; + _layers = null; } public bool Contains(Layer layer) @@ -869,9 +869,9 @@ namespace UVtools.Core return true; } - if (printHeightConfig.Enabled && SlicerFile.MaxPrintHeight > 0) + if (printHeightConfig.Enabled && SlicerFile.MachineZ > 0) { - float printHeightWithOffset = Layer.RoundHeight(SlicerFile.MaxPrintHeight + printHeightConfig.Offset); + float printHeightWithOffset = Layer.RoundHeight(SlicerFile.MachineZ + printHeightConfig.Offset); if (SlicerFile.PrintHeight > printHeightWithOffset) { foreach (var layer in this) diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs index a7fa89e..6b45374 100644 --- a/UVtools.Core/Operations/OperationLayerReHeight.cs +++ b/UVtools.Core/Operations/OperationLayerReHeight.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; using Emgu.CV; @@ -22,6 +23,8 @@ namespace UVtools.Core.Operations { #region Members private OperationLayerReHeightItem _item; + private bool _antiAliasing = true; + #endregion #region Overrides @@ -66,10 +69,23 @@ namespace UVtools.Core.Operations #endregion #region Properties + public OperationLayerReHeightItem Item { get => _item; - set => RaiseAndSetIfChanged(ref _item, value); + set + { + if(!RaiseAndSetIfChanged(ref _item, value)) return; + RaisePropertyChanged(nameof(AntiAliasing)); + } + } + + public bool CanAntiAliasing => _item?.IsMultiply ?? false; + + public bool AntiAliasing + { + get => _antiAliasing; + set => RaiseAndSetIfChanged(ref _antiAliasing, value); } @@ -135,19 +151,19 @@ namespace UVtools.Core.Operations #region Methods protected override bool ExecuteInternally(OperationProgress progress) { - progress.ItemCount = Item.LayerCount; + progress.ItemCount = _item.LayerCount; - var layers = new Layer[Item.LayerCount]; + var layers = new Layer[_item.LayerCount]; - uint newLayerIndex = 0; - for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) + if (_item.IsDivision) { - progress.Token.ThrowIfCancellationRequested(); - - var oldLayer = SlicerFile[layerIndex]; - if (Item.IsDivision) + uint newLayerIndex = 0; + for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) { - for (byte i = 0; i < Item.Modifier; i++) + progress.Token.ThrowIfCancellationRequested(); + + var oldLayer = SlicerFile[layerIndex]; + for (byte i = 0; i < _item.Modifier; i++) { var newLayer = oldLayer.Clone(); //newLayer.Index = newLayerIndex; @@ -157,26 +173,50 @@ namespace UVtools.Core.Operations progress++; } } - else + } + else + { + var layerIndexes = new uint[SlicerFile.LayerCount / _item.Modifier]; + for (uint i = 0; i < layerIndexes.Length; i++) { - using var mat = SlicerFile[layerIndex++].LayerMat; + layerIndexes[i] = i * _item.Modifier; + } + + Parallel.ForEach(layerIndexes, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + var oldLayer = SlicerFile[layerIndex]; + using var mat = oldLayer.LayerMat; + using var original = mat.Clone(); + for (byte i = 1; i < Item.Modifier; i++) { - using var nextMat = SlicerFile[layerIndex++].LayerMat; + using var nextMat = SlicerFile[layerIndex+i].LayerMat; CvInvoke.Add(mat, nextMat, mat); } + /*if (_antiAliasing) + { + CvInvoke.Subtract(mat, original, mat); + CvInvoke.PyrDown(mat, mat); + CvInvoke.PyrUp(mat, mat); + CvInvoke.Add(original, mat, mat); + }*/ + var newLayer = oldLayer.Clone(); //newLayer.Index = newLayerIndex; //newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1)); newLayer.LayerMat = mat; - layers[newLayerIndex] = newLayer; - newLayerIndex++; - layerIndex--; - progress++; - } + layers[layerIndex / _item.Modifier] = newLayer; + + progress.LockAndIncrement(); + }); + + } + + SlicerFile.LayerHeight = (float)Item.LayerHeight; SlicerFile.LayerManager.Layers = layers; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 01dd2dc..63a0232 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>2.8.4</Version> + <Version>2.9.0</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index a7dca7f..b868cfd 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -903,6 +903,12 @@ <Component Id="owc1C3DE0BEF10A5791E37D4C3D97AED5E1" Guid="b1851a14-1200-d448-1acd-b229bf6ac918"> <File Id="owf1C3DE0BEF10A5791E37D4C3D97AED5E1" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\AnyCubic Photon.ini" KeyPath="yes" /> </Component> + <Component Id="owcFE63AE2337EB232D5C139086B8A44942" Guid="02bb8e1d-14a7-f786-5cb9-e7b37337918b"> + <File Id="owfFE63AE2337EB232D5C139086B8A44942" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\Creality HALOT-SKY CL-60.ini" KeyPath="yes" /> + </Component> + <Component Id="owc88F58B3D3A8E53CEFC3FE84F6C20FAB8" Guid="8f66ab9b-8ff1-f324-9ec5-5cbb688dd5f6"> + <File Id="owf88F58B3D3A8E53CEFC3FE84F6C20FAB8" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\Creality HALOT-SKY CL-89.ini" KeyPath="yes" /> + </Component> <Component Id="owc0402794BACCC21B2ADA93D76665B1076" Guid="f9949f30-9613-fe85-e9f8-432d4764ed1f"> <File Id="owf0402794BACCC21B2ADA93D76665B1076" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\Creality LD-002H.ini" KeyPath="yes" /> </Component> diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs index 31668ef..669b180 100644 --- a/UVtools.WPF/MainWindow.Issues.cs +++ b/UVtools.WPF/MainWindow.Issues.cs @@ -531,11 +531,12 @@ namespace UVtools.WPF } } - public IslandDetectionConfiguration GetIslandDetectionConfiguration() + + public IslandDetectionConfiguration GetIslandDetectionConfiguration(bool enable) { - return new IslandDetectionConfiguration + return new() { - Enabled = Settings.Issues.ComputeIslands, + Enabled = enable, EnhancedDetection = Settings.Issues.IslandEnhancedDetection, AllowDiagonalBonds = Settings.Issues.IslandAllowDiagonalBonds, BinaryThreshold = Settings.Issues.IslandBinaryThreshold, @@ -546,34 +547,38 @@ namespace UVtools.WPF RequiredPixelBrightnessToSupport = Settings.Issues.IslandRequiredPixelBrightnessToSupport }; } + public IslandDetectionConfiguration GetIslandDetectionConfiguration() => GetIslandDetectionConfiguration(Settings.Issues.ComputeIslands); - public OverhangDetectionConfiguration GetOverhangDetectionConfiguration() + public OverhangDetectionConfiguration GetOverhangDetectionConfiguration(bool enable) { - return new OverhangDetectionConfiguration + return new() { - Enabled = Settings.Issues.ComputeOverhangs, + Enabled = enable, IndependentFromIslands = Settings.Issues.OverhangIndependentFromIslands, ErodeIterations = Settings.Issues.OverhangErodeIterations, }; } + public OverhangDetectionConfiguration GetOverhangDetectionConfiguration() => GetOverhangDetectionConfiguration(Settings.Issues.ComputeOverhangs); + - public ResinTrapDetectionConfiguration GetResinTrapDetectionConfiguration() + public ResinTrapDetectionConfiguration GetResinTrapDetectionConfiguration(bool enable) { - return new ResinTrapDetectionConfiguration + return new() { - Enabled = Settings.Issues.ComputeResinTraps, + Enabled = enable, BinaryThreshold = Settings.Issues.ResinTrapBinaryThreshold, RequiredAreaToProcessCheck = Settings.Issues.ResinTrapRequiredAreaToProcessCheck, RequiredBlackPixelsToDrain = Settings.Issues.ResinTrapRequiredBlackPixelsToDrain, MaximumPixelBrightnessToDrain = Settings.Issues.ResinTrapMaximumPixelBrightnessToDrain }; } + public ResinTrapDetectionConfiguration GetResinTrapDetectionConfiguration() => GetResinTrapDetectionConfiguration(Settings.Issues.ComputeResinTraps); - public TouchingBoundDetectionConfiguration GetTouchingBoundsDetectionConfiguration() + public TouchingBoundDetectionConfiguration GetTouchingBoundsDetectionConfiguration(bool enable) { - return new TouchingBoundDetectionConfiguration + return new() { - Enabled = Settings.Issues.ComputeTouchingBounds, + Enabled = enable, MinimumPixelBrightness = UserSettings.Instance.Issues.TouchingBoundMinimumPixelBrightness, MarginLeft = UserSettings.Instance.Issues.TouchingBoundMarginLeft, MarginTop = UserSettings.Instance.Issues.TouchingBoundMarginTop, @@ -581,15 +586,19 @@ namespace UVtools.WPF MarginBottom = UserSettings.Instance.Issues.TouchingBoundMarginBottom, }; } + public TouchingBoundDetectionConfiguration GetTouchingBoundsDetectionConfiguration() => GetTouchingBoundsDetectionConfiguration(Settings.Issues.ComputeTouchingBounds); - public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration() + + public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration(bool enable) { - return new PrintHeightDetectionConfiguration + return new () { - Enabled = Settings.Issues.ComputePrintHeight, + Enabled = enable, Offset = (float) Settings.Issues.PrintHeightOffset }; } + public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration() => GetPrintHeightDetectionConfiguration(Settings.Issues.ComputePrintHeight); + #endregion } diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 5e4b3d6..8ed3006 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -1140,11 +1140,18 @@ namespace UVtools.WPF if (mat.Size != SlicerFile.Resolution) { - await this.MessageBoxWaring($"Layer image resolution of {mat.Size} mismatch with printer resolution of {SlicerFile.Resolution}.\n" + - "Printing this file can lead to problems or malformed model, please verify your slicer printer settings;\n" + - "Processing this file with some of the tools can lead to program crash or misfunction;\n" + - "If you used PrusaSlicer to slice this file, you must use it with compatible UVtools printer profiles (Help - Install profiles into PrusaSlicer).", - "File and layer resolution mismatch!"); + var result = await this.MessageBoxWaring($"Layer image resolution of {mat.Size} mismatch with printer resolution of {SlicerFile.Resolution}.\n" + + "1) Printing this file can lead to problems or malformed model, please verify your slicer printer settings;\n" + + "2) Processing this file with some of the tools can lead to program crash or misfunction;\n" + + "3) If you used PrusaSlicer to slice this file, you must use it with compatible UVtools printer profiles (Help - Install profiles into PrusaSlicer).\n\n" + + "Click 'Yes' to auto fix and set the file resolution with the layer resolution, but only use this option if you are sure it's ok to!\n" + + "Click 'No' to continue as it is and ignore this warning, you can still repair issues and use some of the tools.", + "File and layer resolution mismatch!", ButtonEnum.YesNo); + if(result == ButtonResult.Yes) + { + SlicerFile.Resolution = mat.Size; + RaisePropertyChanged(nameof(LayerResolutionStr)); + } } if (Settings.Issues.ComputeIssuesOnLoad) @@ -1158,6 +1165,20 @@ namespace UVtools.WPF await RunOperation(ToolRepairLayersControl.GetOperationRepairLayers()); } } + else + { + await ComputeIssues( + GetIslandDetectionConfiguration(false), + GetOverhangDetectionConfiguration(false), + GetResinTrapDetectionConfiguration(false), + GetTouchingBoundsDetectionConfiguration(false), + GetPrintHeightDetectionConfiguration(true), + true); + if (Issues.Count > 0) + { + SelectedTabItem = TabIssues; + } + } } private async void ShowProgressWindow(string title) diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 14e4907..524c96d 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>2.8.4</Version> + <Version>2.9.0</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> diff --git a/build/CompileLinux.sh b/build/CompileLinux.sh new file mode 100644 index 0000000..6e62f54 --- /dev/null +++ b/build/CompileLinux.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd .. +dotnet build
\ No newline at end of file diff --git a/CompileWindows.bat b/build/CompileWindows.bat index a717c14..350c372 100644 --- a/CompileWindows.bat +++ b/build/CompileWindows.bat @@ -1,5 +1,6 @@ @echo off SET DIR=%~dp0 +cd .. REM if exist "%SYSTEMROOT%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" SET MSBUILD_PATH="%SYSTEMROOT%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" SET MSBUILD_PATH="%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" diff --git a/build/CreateRelease.GUI.ps1 b/build/CreateRelease.GUI.ps1 new file mode 100644 index 0000000..1312b43 --- /dev/null +++ b/build/CreateRelease.GUI.ps1 @@ -0,0 +1,10 @@ +cd $PSScriptRoot +cd .. +$version = (Get-Command UVtools.GUI\bin\Release\UVtools.Core.dll).FileVersionInfo.FileVersion + +Remove-Item "UVtools.GUI\bin\Release\Logs" -Recurse -ErrorAction Ignore + +Add-Type -A System.IO.Compression.FileSystem +[IO.Compression.ZipFile]::CreateFromDirectory("UVtools.GUI\bin\Release", "UVtools.GUI\bin\UVtools_v$version.zip") + +Copy-Item "UVtools.Installer\bin\Release\UVtools.msi" -Destination "UVtools.GUI\bin\UVtools_v$version.msi"
\ No newline at end of file diff --git a/CreateRelease.WPF.ps1 b/build/CreateRelease.WPF.ps1 index 32aac4c..e2f658c 100644 --- a/CreateRelease.WPF.ps1 +++ b/build/CreateRelease.WPF.ps1 @@ -26,6 +26,9 @@ class FixedEncoder : System.Text.UTF8Encoding { } } +# Script working directory +Set-Location $PSScriptRoot\.. + #################################### ### Configuration ### #################################### @@ -37,23 +40,21 @@ $stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch $deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch $stopWatch.Start() -# Script working directory -Set-Location $PSScriptRoot # Variables $software = "UVtools" $project = "UVtools.WPF" $buildWith = "Release" $netFolder = "net5.0" -$releaseFolder = "$PSScriptRoot\$project\bin\$buildWith\$netFolder" -$objFolder = "$PSScriptRoot\$project\obj\$buildWith\$netFolder" +$releaseFolder = "$project\bin\$buildWith\$netFolder" +$objFolder = "$project\obj\$buildWith\$netFolder" $publishFolder = "publish" $platformsFolder = "UVtools.Platforms" $macIcns = "UVtools.CAD/UVtools.icns" #$version = (Get-Command "$releaseFolder\UVtools.dll").FileVersionInfo.ProductVersion -$projectXml = [Xml] (Get-Content "$PSScriptRoot\$project\$project.csproj") +$projectXml = [Xml] (Get-Content "$project\$project.csproj") $version = "$($projectXml.Project.PropertyGroup.Version)".Trim(); if([string]::IsNullOrWhiteSpace($version)){ Write-Error "Can not detect the UVtools version, does $project\$project.csproj exists?" @@ -62,7 +63,7 @@ if([string]::IsNullOrWhiteSpace($version)){ # MSI Variables $installers = @("UVtools.InstallerMM", "UVtools.Installer") -$msiSourceFile = "$PSScriptRoot\UVtools.Installer\bin\Release\UVtools.msi" +$msiSourceFile = "UVtools.Installer\bin\Release\UVtools.msi" $msbuild = "`"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe`" /t:Build /p:Configuration=$buildWith /p:MSIProductVersion=$version" Write-Output " @@ -222,8 +223,8 @@ if($enableMSI) foreach($installer in $installers) { # Clean and build MSI - Remove-Item "$PSScriptRoot\$installer\obj" -Recurse -ErrorAction Ignore - Remove-Item "$PSScriptRoot\$installer\bin" -Recurse -ErrorAction Ignore + Remove-Item "$installer\obj" -Recurse -ErrorAction Ignore + Remove-Item "$installer\bin" -Recurse -ErrorAction Ignore iex "& $msbuild $installer\$installer.wixproj" } |