From 08a3c895dabed8e021205fb1ce425aa05457772d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Tue, 24 Aug 2021 04:36:50 +0100 Subject: [PATCH] v2.20.0 - **File formats:** - (Add) FlashForge SVGX format of FlashDLPrint - (Improvement) Change `DisplayMirror` from `bool` to a `FlipDirection` enumeration, to be able to identify the exact mirror direction - **(Add) PrusaSlicer Printers:** - FlashForge Explorer MAX - FlashForge Focus 8.9 - FlashForge Focus 13.3 - FlashForge Foto 6.0 - FlashForge Foto 8.9 - FlashForge Foto 13.3 - AnyCubic Photon Mono SQ - AnyCubic Photon Ultra - (Add) Pixel arithmetic: Preset "Elephant foot compensation" --- CHANGELOG.md | 23 + .../printer/AnyCubic Photon Mono SQ.ini | 38 + PrusaSlicer/printer/AnyCubic Photon Ultra.ini | 38 + .../printer/FlashForge Explorer MAX.ini | 38 + PrusaSlicer/printer/FlashForge Focus 13.3.ini | 38 + PrusaSlicer/printer/FlashForge Focus 8.9.ini | 38 + PrusaSlicer/printer/FlashForge Foto 13.3.ini | 38 + PrusaSlicer/printer/FlashForge Foto 6.0.ini | 38 + PrusaSlicer/printer/FlashForge Foto 8.9.ini | 38 + PrusaSlicer/printer/FlashForge Hunter.ini | 38 + Scripts/010 Editor/PhotonWorkshop.bt | 15 +- Scripts/010 Editor/svgx.bt | 50 ++ Scripts/ImportPrusaSlicerData.bat | 101 +-- .../Extensions/FileStreamExtensions.cs | 21 + UVtools.Core/Extensions/SizeExtensions.cs | 2 +- UVtools.Core/Extensions/XmlExtensions.cs | 64 ++ UVtools.Core/FileFormats/CTBEncryptedFile.cs | 6 +- UVtools.Core/FileFormats/CWSFile.cs | 14 +- UVtools.Core/FileFormats/ChituboxFile.cs | 6 +- UVtools.Core/FileFormats/ChituboxZipFile.cs | 8 +- UVtools.Core/FileFormats/FDGFile.cs | 6 +- UVtools.Core/FileFormats/FileFormat.cs | 58 +- .../FileFormats/FlashForgeSVGXFile.cs | 762 ++++++++++++++++++ UVtools.Core/FileFormats/GR1File.cs | 2 +- UVtools.Core/FileFormats/ImageFile.cs | 6 - UVtools.Core/FileFormats/LGSFile.cs | 4 +- UVtools.Core/FileFormats/MDLPFile.cs | 2 +- UVtools.Core/FileFormats/OSLAFile.cs | 6 +- UVtools.Core/FileFormats/PHZFile.cs | 6 +- UVtools.Core/FileFormats/PhotonSFile.cs | 4 +- .../FileFormats/PhotonWorkshopFile.cs | 199 +++-- UVtools.Core/FileFormats/SL1File.cs | 14 +- UVtools.Core/FileFormats/UVJFile.cs | 2 +- UVtools.Core/FileFormats/VDTFile.cs | 17 +- UVtools.Core/FileFormats/ZCodeFile.cs | 2 +- UVtools.Core/FileFormats/ZCodexFile.cs | 4 +- .../OperationCalibrateElephantFoot.cs | 6 +- .../OperationCalibrateExposureFinder.cs | 6 +- .../Operations/OperationCalibrateGrayscale.cs | 6 +- .../OperationCalibrateStressTower.cs | 6 +- .../Operations/OperationCalibrateTolerance.cs | 6 +- .../OperationCalibrateXYZAccuracy.cs | 6 +- .../Operations/OperationLayerExportImage.cs | 5 +- .../Operations/OperationPixelArithmetic.cs | 13 +- UVtools.Core/UVtools.Core.csproj | 2 +- UVtools.InstallerMM/UVtools.InstallerMM.wxs | 167 ++-- UVtools.WPF/ConsoleArguments.cs | 2 +- .../Controls/Tools/ToolControl.axaml.cs | 3 +- .../Tools/ToolPixelArithmeticControl.axaml | 11 +- .../Tools/ToolPixelArithmeticControl.axaml.cs | 6 + UVtools.WPF/MainWindow.axaml.cs | 4 +- UVtools.WPF/UVtools.WPF.csproj | 2 +- 52 files changed, 1699 insertions(+), 298 deletions(-) create mode 100644 PrusaSlicer/printer/AnyCubic Photon Mono SQ.ini create mode 100644 PrusaSlicer/printer/AnyCubic Photon Ultra.ini create mode 100644 PrusaSlicer/printer/FlashForge Explorer MAX.ini create mode 100644 PrusaSlicer/printer/FlashForge Focus 13.3.ini create mode 100644 PrusaSlicer/printer/FlashForge Focus 8.9.ini create mode 100644 PrusaSlicer/printer/FlashForge Foto 13.3.ini create mode 100644 PrusaSlicer/printer/FlashForge Foto 6.0.ini create mode 100644 PrusaSlicer/printer/FlashForge Foto 8.9.ini create mode 100644 PrusaSlicer/printer/FlashForge Hunter.ini create mode 100644 Scripts/010 Editor/svgx.bt create mode 100644 UVtools.Core/Extensions/XmlExtensions.cs create mode 100644 UVtools.Core/FileFormats/FlashForgeSVGXFile.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b701c2c..70fd7cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 24/08/2021 - v2.20.0 + +- **File formats:** + - (Add) FlashForge SVGX format of FlashDLPrint + - (Improvement) Change `DisplayMirror` from `bool` to a `FlipDirection` enumeration, to be able to identify the exact mirror direction +- **(Add) PrusaSlicer Printers:** + - FlashForge Explorer MAX + - FlashForge Focus 8.9 + - FlashForge Focus 13.3 + - FlashForge Foto 6.0 + - FlashForge Foto 8.9 + - FlashForge Foto 13.3 + - AnyCubic Photon Mono SQ + - AnyCubic Photon Ultra +- (Add) Pixel arithmetic: Preset "Elephant foot compensation" + + ## 22/08/2021 - v2.19.5 - **(Fix) CTB:** diff --git a/PrusaSlicer/printer/AnyCubic Photon Mono SQ.ini b/PrusaSlicer/printer/AnyCubic Photon Mono SQ.ini new file mode 100644 index 0000000..b0f9c9b --- /dev/null +++ b/PrusaSlicer/printer/AnyCubic Photon Mono SQ.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-23 at 15:24:23 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,120x0,120x128,0x128 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 128 +display_mirror_x = 1 +display_mirror_y = 0 +display_orientation = landscape +display_pixels_x = 2400 +display_pixels_y = 2560 +display_width = 120 +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_ANYCUBIC\nPRINTER_MODEL_PHOTON_MONO_SQ\nFILEFORMAT_PMSQ\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_8\nLiftHeight_8\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\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/AnyCubic Photon Ultra.ini b/PrusaSlicer/printer/AnyCubic Photon Ultra.ini new file mode 100644 index 0000000..41b441c --- /dev/null +++ b/PrusaSlicer/printer/AnyCubic Photon Ultra.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-23 at 15:46:01 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,102.4x0,102.4x57.6,0x57.6 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 57.6 +display_mirror_x = 1 +display_mirror_y = 0 +display_orientation = landscape +display_pixels_x = 1280 +display_pixels_y = 720 +display_width = 102.4 +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 = 165 +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_ANYCUBIC\nPRINTER_MODEL_PHOTON_ULTRA\nFILEFORMAT_DLP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_6\nLiftHeight_5\nBottomLiftSpeed_150\nLiftSpeed_150\nRetractSpeed_150\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/FlashForge Explorer MAX.ini b/PrusaSlicer/printer/FlashForge Explorer MAX.ini new file mode 100644 index 0000000..79eca85 --- /dev/null +++ b/PrusaSlicer/printer/FlashForge Explorer MAX.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-24 at 03:12:51 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 = 0 +display_mirror_y = 1 +display_orientation = landscape +display_pixels_x = 2560 +display_pixels_y = 1600 +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_FLASHFORGE\nPRINTER_MODEL_EXPLORER_MAX\nFILEFORMAT_SVGX\n\nSTART_CUSTOM_VALUES\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/FlashForge Focus 13.3.ini b/PrusaSlicer/printer/FlashForge Focus 13.3.ini new file mode 100644 index 0000000..81c7584 --- /dev/null +++ b/PrusaSlicer/printer/FlashForge Focus 13.3.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-24 at 03:10:40 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,292x0,292x165,0x165 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 165 +display_mirror_x = 0 +display_mirror_y = 1 +display_orientation = landscape +display_pixels_x = 3842 +display_pixels_y = 2171 +display_width = 292 +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 = 400 +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_FLASHFORGE\nPRINTER_MODEL_FOCUS_13.3\nFILEFORMAT_SVGX\n\nSTART_CUSTOM_VALUES\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/FlashForge Focus 8.9.ini b/PrusaSlicer/printer/FlashForge Focus 8.9.ini new file mode 100644 index 0000000..71ce817 --- /dev/null +++ b/PrusaSlicer/printer/FlashForge Focus 8.9.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-24 at 03:10:45 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 = 0 +display_mirror_y = 1 +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_FLASHFORGE\nPRINTER_MODEL_FOCUS_8.9\nFILEFORMAT_SVGX\n\nSTART_CUSTOM_VALUES\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/FlashForge Foto 13.3.ini b/PrusaSlicer/printer/FlashForge Foto 13.3.ini new file mode 100644 index 0000000..9f8422f --- /dev/null +++ b/PrusaSlicer/printer/FlashForge Foto 13.3.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-24 at 03:10:50 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,292x0,292x165,0x165 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 165 +display_mirror_x = 0 +display_mirror_y = 1 +display_orientation = landscape +display_pixels_x = 3842 +display_pixels_y = 2171 +display_width = 292 +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 = 400 +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_FLASHFORGE\nPRINTER_MODEL_FOTO_13.3\nFILEFORMAT_SVGX\n\nSTART_CUSTOM_VALUES\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/FlashForge Foto 6.0.ini b/PrusaSlicer/printer/FlashForge Foto 6.0.ini new file mode 100644 index 0000000..239f105 --- /dev/null +++ b/PrusaSlicer/printer/FlashForge Foto 6.0.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-24 at 03:10:53 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,130x0,130x78,0x78 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 78 +display_mirror_x = 0 +display_mirror_y = 1 +display_orientation = landscape +display_pixels_x = 2600 +display_pixels_y = 1560 +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 = 155 +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_FLASHFORGE\nPRINTER_MODEL_FOTO_6.0\nFILEFORMAT_SVGX\n\nSTART_CUSTOM_VALUES\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/FlashForge Foto 8.9.ini b/PrusaSlicer/printer/FlashForge Foto 8.9.ini new file mode 100644 index 0000000..f6bc636 --- /dev/null +++ b/PrusaSlicer/printer/FlashForge Foto 8.9.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-24 at 03:10:57 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 = 0 +display_mirror_y = 1 +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_FLASHFORGE\nPRINTER_MODEL_FOTO_8.9\nFILEFORMAT_SVGX\n\nSTART_CUSTOM_VALUES\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/FlashForge Hunter.ini b/PrusaSlicer/printer/FlashForge Hunter.ini new file mode 100644 index 0000000..d32dd71 --- /dev/null +++ b/PrusaSlicer/printer/FlashForge Hunter.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-24 at 03:11:04 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,120x0,120x67.5,0x67.5 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 67.5 +display_mirror_x = 0 +display_mirror_y = 1 +display_orientation = landscape +display_pixels_x = 1920 +display_pixels_y = 1080 +display_width = 120 +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 = 150 +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_FLASHFORGE\nPRINTER_MODEL_HUNTER\nFILEFORMAT_SVGX\n\nSTART_CUSTOM_VALUES\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/Scripts/010 Editor/PhotonWorkshop.bt b/Scripts/010 Editor/PhotonWorkshop.bt index 6c08bed..0c65d80 100644 --- a/Scripts/010 Editor/PhotonWorkshop.bt +++ b/Scripts/010 Editor/PhotonWorkshop.bt @@ -9,12 +9,12 @@ LittleEndian(); struct FILEMARK { char Mark[12] ; - uint Version ; + uint Version ; // 1 or 515 uint AreaNum ; uint HeaderAddress ; uint Padding1 ; uint PreviewAddress ; - uint Padding2 ; + uint PreviewEndAddress ; // Version 515 only uint LayerDefinitionAddress ; uint Padding3 ; uint LayerImageAddress ; @@ -62,12 +62,13 @@ struct PREVIEW { uint ResolutionX ; uint DpiResolution ; uint ResolutionY ; - uint Unknown1 ; + + ubyte Data[ResolutionX*ResolutionY*2] ; + + /*uint Unknown1 ; uint Unknown2 ; uint Unknown3 ; - uint Unknown4 ; - - ubyte Data[ResolutionX*ResolutionY*2] ; + uint Unknown4 ;*/ }; if(fileMark.PreviewAddress > 0){ @@ -97,7 +98,7 @@ struct LAYER_DEF { local uint currentPos = FTell(); FSeek(DataAddress); - ubyte RLE[DataLength]; + ubyte RLE[DataLength] ; FSeek(currentPos); }; diff --git a/Scripts/010 Editor/svgx.bt b/Scripts/010 Editor/svgx.bt new file mode 100644 index 0000000..1a7df0f --- /dev/null +++ b/Scripts/010 Editor/svgx.bt @@ -0,0 +1,50 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: FlashForge SVGX +// Authors: Tiago Conceição +//------------------------------------------------ + +LittleEndian(); + +struct HEADER { + char Identifier[16] ; + uint Preview1Address ; + uint Preview2Address ; + uint SVGDocumentAddress ; +} header; + + +struct PREVIEW { + char Identifier[2] ; // BM (Bitmap?) + uint TableSize ; // This table + RGB Data + uint Padding1 ; + uint dpiX ; // 54 + uint dpiY ; // 40 + uint ResolutionX ; + uint ResolutionY ; + + uint Unknown1 ; // 1572865 + uint Padding2 ; + uint DataSize ; + uint Unknown2 ; // 3780 + uint Unknown3 ; // 3780 + uint Padding3 ; + uint Padding4 ; + + ubyte BGR[DataSize] ; // ResolutionX*ResolutionY*3 ? +}; + +if(header.Preview1Address > 0){ + FSeek(header.Preview1Address); + PREVIEW preview; +} + +if(header.Preview2Address > 0){ + FSeek(header.Preview2Address); + PREVIEW preview; +} + + +FSeek(header.SVGDocumentAddress); +string SVGDocument ; \ No newline at end of file diff --git a/Scripts/ImportPrusaSlicerData.bat b/Scripts/ImportPrusaSlicerData.bat index 84de881..afadec9 100644 --- a/Scripts/ImportPrusaSlicerData.bat +++ b/Scripts/ImportPrusaSlicerData.bat @@ -31,52 +31,61 @@ SET files[20]=AnyCubic Photon.ini SET files[21]=AnyCubic Photon S.ini SET files[22]=AnyCubic Photon Zero.ini SET files[23]=AnyCubic Photon X.ini -SET files[24]=AnyCubic Photon Mono.ini -SET files[25]=AnyCubic Photon Mono SE.ini -SET files[26]=AnyCubic Photon Mono X.ini -SET files[27]=Elegoo Mars.ini -SET files[28]=Elegoo Mars 2 Pro.ini -SET files[29]=Elegoo Mars C.ini -SET files[30]=Elegoo Saturn.ini -SET files[31]=Peopoly Phenom.ini -SET files[32]=Peopoly Phenom L.ini -SET files[33]=Peopoly Phenom Noir.ini -SET files[34]=Peopoly Phenom XXL.ini -SET files[35]=QIDI Shadow5.5.ini -SET files[36]=QIDI Shadow6.0 Pro.ini -SET files[37]=QIDI S-Box.ini -SET files[38]=QIDI I-Box Mono.ini -SET files[39]=Phrozen Shuffle.ini -SET files[40]=Phrozen Shuffle Lite.ini -SET files[41]=Phrozen Shuffle XL.ini -SET files[42]=Phrozen Shuffle XL Lite.ini -SET files[43]=Phrozen Shuffle 16.ini -SET files[44]=Phrozen Shuffle 4K.ini -SET files[45]=Phrozen Sonic.ini -SET files[46]=Phrozen Sonic 4K.ini -SET files[47]=Phrozen Sonic Mighty 4K.ini -SET files[48]=Phrozen Sonic Mini.ini -SET files[49]=Phrozen Sonic Mini 4K.ini -SET files[50]=Phrozen Transform.ini -SET files[51]=Kelant S400.ini -SET files[52]=Wanhao D7.ini -SET files[53]=Wanhao D8.ini -SET files[54]=Wanhao CGR Mini Mono.ini -SET files[55]=Wanhao CGR Mono.ini -SET files[56]=Creality LD-002R.ini -SET files[57]=Creality LD-002H.ini -SET files[58]=Creality LD-006.ini -SET files[59]=Creality HALOT-ONE CL-60.ini -SET files[60]=Creality HALOT-SKY CL-89.ini -SET files[61]=Creality HALOT-MAX CL-133.ini -SET files[62]=Voxelab Polaris 5.5.ini -SET files[63]=Voxelab Proxima 6.ini -SET files[64]=Voxelab Ceres 8.9.ini -SET files[65]=Longer Orange 10.ini -SET files[66]=Longer Orange 30.ini -SET files[67]=Longer Orange 120.ini -SET files[68]=Longer Orange 4K.ini -SET files[69]=Uniz IBEE.ini +SET files[24]=AnyCubic Photon Ultra.ini +SET files[25]=AnyCubic Photon Mono.ini +SET files[26]=AnyCubic Photon Mono SE.ini +SET files[27]=AnyCubic Photon Mono X.ini +SET files[28]=AnyCubic Photon Mono SQ.ini +SET files[29]=Elegoo Mars.ini +SET files[30]=Elegoo Mars 2 Pro.ini +SET files[31]=Elegoo Mars C.ini +SET files[32]=Elegoo Saturn.ini +SET files[33]=Peopoly Phenom.ini +SET files[34]=Peopoly Phenom L.ini +SET files[35]=Peopoly Phenom Noir.ini +SET files[36]=Peopoly Phenom XXL.ini +SET files[37]=QIDI Shadow5.5.ini +SET files[38]=QIDI Shadow6.0 Pro.ini +SET files[39]=QIDI S-Box.ini +SET files[40]=QIDI I-Box Mono.ini +SET files[41]=Phrozen Shuffle.ini +SET files[42]=Phrozen Shuffle Lite.ini +SET files[43]=Phrozen Shuffle XL.ini +SET files[44]=Phrozen Shuffle XL Lite.ini +SET files[45]=Phrozen Shuffle 16.ini +SET files[46]=Phrozen Shuffle 4K.ini +SET files[47]=Phrozen Sonic.ini +SET files[48]=Phrozen Sonic 4K.ini +SET files[49]=Phrozen Sonic Mighty 4K.ini +SET files[50]=Phrozen Sonic Mini.ini +SET files[51]=Phrozen Sonic Mini 4K.ini +SET files[52]=Phrozen Transform.ini +SET files[53]=Kelant S400.ini +SET files[54]=Wanhao D7.ini +SET files[55]=Wanhao D8.ini +SET files[56]=Wanhao CGR Mini Mono.ini +SET files[57]=Wanhao CGR Mono.ini +SET files[58]=Creality LD-002R.ini +SET files[59]=Creality LD-002H.ini +SET files[60]=Creality LD-006.ini +SET files[61]=Creality HALOT-ONE CL-60.ini +SET files[62]=Creality HALOT-SKY CL-89.ini +SET files[63]=Creality HALOT-MAX CL-133.ini +SET files[64]=Voxelab Polaris 5.5.ini +SET files[65]=Voxelab Proxima 6.ini +SET files[66]=Voxelab Ceres 8.9.ini +SET files[67]=Longer Orange 10.ini +SET files[68]=Longer Orange 30.ini +SET files[69]=Longer Orange 120.ini +SET files[70]=Longer Orange 4K.ini +SET files[71]=Uniz IBEE.ini +SET files[72]=FlashForge Explorer MAX.ini +SET files[73]=FlashForge Focus 8.9.ini +SET files[74]=FlashForge Focus 13.3.ini +SET files[75]=FlashForge Foto 6.0.ini +SET files[76]=FlashForge Foto 8.9.ini +SET files[77]=FlashForge Foto 13.3.ini +SET files[78]=FlashForge Hunter.ini echo PrusaSlicer Printers Instalation echo This will replace printers, all changes will be discarded diff --git a/UVtools.Core/Extensions/FileStreamExtensions.cs b/UVtools.Core/Extensions/FileStreamExtensions.cs index 86f84cb..8944a48 100644 --- a/UVtools.Core/Extensions/FileStreamExtensions.cs +++ b/UVtools.Core/Extensions/FileStreamExtensions.cs @@ -8,6 +8,7 @@ using System; using System.IO; +using System.Text; namespace UVtools.Core.Extensions { @@ -28,6 +29,16 @@ namespace UVtools.Core.Extensions public static byte[] ReadBytes(this FileStream fs, uint length, int offset = 0) => fs.ReadBytes((int)length, offset); + /// + /// Read from current position to the end of the file + /// + /// + /// + public static byte[] ReadToEnd(this FileStream fs) + { + return fs.ReadBytes((uint)(fs.Length - fs.Position)); + } + public static uint ReadUShortLittleEndian(this FileStream fs, int offset = 0) { return BitExtensions.ToUShortLittleEndian(fs.ReadBytes(2, offset)); @@ -85,6 +96,16 @@ namespace UVtools.Core.Extensions return (uint)bytes.Length; } + public static uint WriteString(this FileStream fs, string text, int offset = 0) + => fs.WriteString(text, Encoding.UTF8, offset); + + public static uint WriteString(this FileStream fs, string text, Encoding encoding, int offset = 0) + { + var bytes = encoding.GetBytes(text); + fs.Write(bytes, offset, bytes.Length); + return (uint)bytes.Length; + } + public static uint WriteSerialize(this FileStream fs, object data, int offset = 0) { return Helpers.SerializeWriteFileStream(fs, data, offset); diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index 6be074e..4c320bd 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -84,7 +84,7 @@ namespace UVtools.Core.Extensions public static Size Half(this Size size) => new(size.Width / 2, size.Height / 2); - public static SizeF Half(this SizeF size) => new(size.Width / 2, size.Height / 2); + public static SizeF Half(this SizeF size) => new(size.Width / 2f, size.Height / 2f); public static Point ToPoint(this Size size) => new(size.Width, size.Height); public static PointF ToPoint(this SizeF size) => new(size.Width, size.Height); diff --git a/UVtools.Core/Extensions/XmlExtensions.cs b/UVtools.Core/Extensions/XmlExtensions.cs new file mode 100644 index 0000000..ef24788 --- /dev/null +++ b/UVtools.Core/Extensions/XmlExtensions.cs @@ -0,0 +1,64 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace UVtools.Core.Extensions +{ + public static class XmlExtensions + { + public static string SerializeObject(object toSerialize) + { + var xmlSerializer = new XmlSerializer(toSerialize.GetType()); + + using var textWriter = new StringWriter(); + xmlSerializer.Serialize(textWriter, toSerialize); + return textWriter.ToString(); + } + + public static string SerializeObject(object toSerialize, XmlWriterSettings settings, bool standalone = false) + { + settings.CloseOutput = false; + + using var ms = new MemoryStream(); + using (var xw = XmlWriter.Create(ms, settings)) + { + xw.WriteStartDocument(standalone); // that bool parameter is called "standalone" + + var s = new XmlSerializer(toSerialize.GetType()); + s.Serialize(xw, toSerialize); + } + + return settings.Encoding.GetString(ms.ToArray()); + } + + public static string SerializeObject(object toSerialize, Encoding encoding, bool indent = true, bool omitXmlDeclaration = false, bool standalone = false) + { + var settings = new XmlWriterSettings + { + // If set to true XmlWriter would close MemoryStream automatically and using would then do double dispose + // Code analysis does not understand that. That's why there is a suppress message. + CloseOutput = false, + Encoding = encoding, + OmitXmlDeclaration = omitXmlDeclaration, + Indent = indent, + }; + return SerializeObject(toSerialize, settings, standalone); + } + + public static T DeserializeObject(string text) + { + var serializer = new XmlSerializer(typeof(T)); + using TextReader reader = new StringReader(text); + return (T)serializer.Deserialize(reader); + } + + } +} diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index f38329d..38d0ee1 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -719,12 +719,12 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = Settings.MachineZ = (float)Math.Round(value, 2); } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => Settings.ProjectorType > 0; + get => Settings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally; set { - Settings.ProjectorType = value ? 1u : 0; + Settings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1; RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 44f3c8a..2fed48b 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -410,13 +410,19 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = OutputSettings.PlatformZSize = (float)Math.Round(value, 2); } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => OutputSettings.FlipX; + get + { + if (OutputSettings.FlipX && OutputSettings.FlipY) return Enumerations.FlipDirection.Both; + if (OutputSettings.FlipX) return Enumerations.FlipDirection.Horizontally; + if (OutputSettings.FlipY) return Enumerations.FlipDirection.Vertically; + return Enumerations.FlipDirection.None; + } set { - OutputSettings.FlipX = value; - OutputSettings.FlipY = false; + OutputSettings.FlipX = value is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both; + OutputSettings.FlipY = value is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both; RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index e1d169f..2556605 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -1246,12 +1246,12 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => HeaderSettings.ProjectorType > 0; + get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally; set { - HeaderSettings.ProjectorType = value ? 1u : 0; + HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1; RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index 3982e02..77b5def 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -184,13 +184,13 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2); } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => HeaderSettings.Mirror > 0; + get => HeaderSettings.Mirror == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally; set { - HeaderSettings.ProjectType = value ? "LCD_mirror" : "Normal"; - HeaderSettings.Mirror = (byte)(value ? 1 : 0); + HeaderSettings.ProjectType = value == Enumerations.FlipDirection.None ? "Normal" : "LCD_mirror"; + HeaderSettings.Mirror = (byte)(value == Enumerations.FlipDirection.None ? 0 : 1); RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index 118958a..edeae11 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -705,12 +705,12 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2); } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => HeaderSettings.ProjectorType > 0; + get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally; set { - HeaderSettings.ProjectorType = value ? 1u : 0; + HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1; RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index d7fb022..4c504e8 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -294,6 +294,7 @@ namespace UVtools.Core.FileFormats //new CXDLPv1File(), // Creality Box v1 new CXDLPFile(), // Creality Box new LGSFile(), // LGS, LGS30 + new FlashForgeSVGXFile(), // SVGX new VDAFile(), // VDA new VDTFile(), // VDT new UVJFile(), // UVJ @@ -596,7 +597,8 @@ namespace UVtools.Core.FileFormats var mat = new Mat(resolution, DepthType.Cv8U, 3); var span = mat.GetDataByteSpan(); var pixel = 0; - for (int i = 0; i < bytes.Length;) + int i = 0; + while (i < bytes.Length && pixel < span.Length) { switch (dataType) { @@ -675,6 +677,12 @@ namespace UVtools.Core.FileFormats break; } } + + for (; pixel < span.Length; pixel++) // Fill leftovers + { + span[pixel] = 0; + } + return mat; } @@ -996,7 +1004,7 @@ namespace UVtools.Core.FileFormats /// /// Gets or sets if images need to be mirrored on lcd to print on the correct orientation /// - public virtual bool DisplayMirror { get; set; } + public virtual Enumerations.FlipDirection DisplayMirror { get; set; } = Enumerations.FlipDirection.None; /// /// Gets if the display is in portrait mode @@ -3534,7 +3542,7 @@ namespace UVtools.Core.FileFormats public void UpdatePrintTime() { PrintTime = PrintTimeComputed; - Debug.WriteLine($"Time updated: {_printTime}s"); + //Debug.WriteLine($"Time updated: {_printTime}s"); } public void UpdatePrintTimeQueued() @@ -3546,6 +3554,50 @@ namespace UVtools.Core.FileFormats } } + /// + /// From a pixel position get the equivalent position on the display + /// + /// X position in pixels + /// Display position in millimeters + public float PixelToDisplayPositionX(int x) => (float)Math.Round(PixelWidth * x, 3); + + /// + /// From a pixel position get the equivalent position on the display + /// + /// Y position in pixels + /// Display position in millimeters + public float PixelToDisplayPositionY(int y) => (float)Math.Round(PixelHeight * y, 3); + + /// + /// From a pixel position get the equivalent position on the display + /// + /// X position in pixels + /// Y position in pixels + /// Resolution position in pixels + public PointF PixelToDisplayPosition(int x, int y) =>new(PixelToDisplayPositionX(x), PixelToDisplayPositionY(y)); + + /// + /// From a pixel position get the equivalent position on the display + /// + /// X position in millimeters + /// Resolution position in pixels + public int DisplayToPixelPositionX(float x) => (int)(x * Xppmm); + + /// + /// From a pixel position get the equivalent position on the display + /// + /// Y position in millimeters + /// Resolution position in pixels + public int DisplayToPixelPositionY(float y) => (int)(y * Yppmm); + + /// + /// From a pixel position get the equivalent position on the display + /// + /// X position in millimeters + /// Y position in millimeters + /// Resolution position in pixels + public Point DisplayToPixelPosition(float x, float y) => new(DisplayToPixelPositionX(x), DisplayToPixelPositionY(y)); + #endregion } } diff --git a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs new file mode 100644 index 0000000..6c0ebea --- /dev/null +++ b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs @@ -0,0 +1,762 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; +using BinarySerialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Util; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + [XmlRoot(ElementName = "svg", Namespace = "http://www.w3.org/2000/svg")] + + public class FlashForgeSVGXSvg + { + //public string Xmlns { get; set; } = "http://www.w3.org/2000/svg"; + + //[XmlAttribute("svg", Namespace = "http://www.w3.org/2000/svg")] public string Svg { get; set; } = "http://www.w3.org/2000/svg"; + [XmlAttribute("version")] public string Version { get; set; } = "1.1"; + + [XmlElement("printparams")] public FlashForgeSVGXSvgPrintParams PrintParameters { get; set; } = new(); + + [XmlElement("g")] public List Groups { get; set; } = new(); + + public override string ToString() + { + return $"{nameof(Version)}: {Version}, {nameof(PrintParameters)}: {PrintParameters}, {nameof(Groups)}: {Groups.Count}"; + } + + public string SerializeToString() + { + var settings = new XmlWriterSettings + { + // If set to true XmlWriter would close MemoryStream automatically and using would then do double dispose + // Code analysis does not understand that. That's why there is a suppress message. + CloseOutput = false, + Encoding = new UTF8Encoding(false), + Indent = true, + NewLineChars = "\n" + }; + + var svg = XmlExtensions.SerializeObject(this, settings); + return svg.Replace(" ", string.Empty) + .Replace("", "\n") + + '\n'; + } + } + + [XmlRoot("printparams")] + public class FlashForgeSVGXSvgPrintParams + { + [XmlAttribute("machinename")] public string MachineName { get; set; } = "Unknown"; + [XmlAttribute("materialname")] public string MaterialName { get; set; } = "Unknown"; + [XmlAttribute("layerheight")] public float LayerHeight { get; set; } + [XmlAttribute("volume")] public float MaterialMilliliters { get; set; } + [XmlAttribute("layercount")] public uint LayerCount { get; set; } + [XmlAttribute("lightintensity")] public float LightIntensity { get; set; } = 1; + [XmlAttribute("resolutionx")] public uint ResolutionX { get; set; } + [XmlAttribute("resolutiony")] public uint ResolutionY { get; set; } + [XmlAttribute("displaywidth")] public float DisplayWidth { get; set; } + [XmlAttribute("displayheight")] public float DisplayHeight { get; set; } + [XmlAttribute("machinez")] public float MachineZ { get; set; } + + [XmlElement("projectiontime")] public FlashForgeSVGXSvgProjectionTime ProjectionTime { get; set; } = new(); + [XmlElement("projectionadjust")] public FlashForgeSVGXSvgProjectionAdjust ProjectionAdjust { get; set; } = new(); + [XmlElement("printrange")] public FlashForgeSVGXSvgPrintRange PrintRange { get; set; } = new(); + + public override string ToString() + { + return $"{nameof(MaterialName)}: {MaterialName}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(MaterialMilliliters)}: {MaterialMilliliters}, {nameof(LayerCount)}: {LayerCount}, {nameof(LightIntensity)}: {LightIntensity}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(MachineZ)}: {MachineZ}, {nameof(ProjectionTime)}: {ProjectionTime}, {nameof(ProjectionAdjust)}: {ProjectionAdjust}, {nameof(PrintRange)}: {PrintRange}"; + } + } + + [XmlRoot("projectiontime")] + public class FlashForgeSVGXSvgProjectionTime + { + [XmlAttribute("attachlayer")] public ushort BottomLayerCount { get; set; } = 3; + [XmlAttribute("buildinlayer")] public ushort TransitionLayerCount { get; set; } = 5; + [XmlAttribute("attachtime")] public float BottomExposureTime { get; set; } + [XmlAttribute("basetime")] public float ExposureTime { get; set; } + + public override string ToString() + { + return $"{nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(ExposureTime)}: {ExposureTime}"; + } + } + + [XmlRoot("projectionadjust")] + public class FlashForgeSVGXSvgProjectionAdjust + { + [XmlAttribute("x")] public float X { get; set; } = 100; + [XmlAttribute("y")] public float Y { get; set; } = 100; + + public override string ToString() + { + return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}"; + } + } + + [XmlRoot("printrange")] + public class FlashForgeSVGXSvgPrintRange + { + [XmlAttribute("minx")] public float MinX { get; set; } + [XmlAttribute("miny")] public float MinY { get; set; } + [XmlAttribute("minz")] public float MinZ { get; set; } + [XmlAttribute("maxx")] public float MaxX { get; set; } + [XmlAttribute("maxy")] public float MaxY { get; set; } + [XmlAttribute("maxz")] public float MaxZ { get; set; } + + public override string ToString() + { + return $"{nameof(MinX)}: {MinX}, {nameof(MinY)}: {MinY}, {nameof(MinZ)}: {MinZ}, {nameof(MaxX)}: {MaxX}, {nameof(MaxY)}: {MaxY}, {nameof(MaxZ)}: {MaxZ}"; + } + } + + [XmlRoot("g")] + public class FlashForgeSVGXSvgGroup + { + [XmlAttribute("id")] public string Id { get; set; } + [XmlAttribute("area")] public float Area { get; set; } + [XmlAttribute("perimeter")] public float Perimeter { get; set; } + [XmlElement("path")] public List Paths { get; set; } = new(); + + public FlashForgeSVGXSvgGroup() { } + + public FlashForgeSVGXSvgGroup(string id, float area = 0, float perimeter = 0) + { + Id = id; + Area = area; + Perimeter = perimeter; + } + + public override string ToString() + { + return $"{nameof(Id)}: {Id}, {nameof(Area)}: {Area}, {nameof(Perimeter)}: {Perimeter}, {nameof(Paths)}: {Paths}"; + } + } + + [XmlRoot("path")] + public class FlashForgeSVGXSvgPath + { + [XmlAttribute("d")] public string Value { get; set; } + [XmlAttribute("style")] public string Style { get; set; } = "fill:white"; + [XmlAttribute("fill-rule")] public string FillRule { get; set; } = "evenodd"; + + public FlashForgeSVGXSvgPath() { } + + public FlashForgeSVGXSvgPath(string value) + { + Value = value; + } + + public override string ToString() + { + return $"{nameof(Value)}: {Value}, {nameof(Style)}: {Style}, {nameof(FillRule)}: {FillRule}"; + } + } + + + public class FlashForgeSVGXFile : FileFormat + { + #region Constants + #endregion + + #region Sub Classes + #region Header + public class Header + { + public const byte IdentifierLength = 16; + public const string IdentifierText = "DLP-II 1.1\n"; + + [FieldOrder(0)] [FieldLength(IdentifierLength)] [SerializeAs(SerializedType.TerminatedString)] public string Identifier { get; set; } = IdentifierText; + [FieldOrder(1)] public uint Preview1Address { get; set; } + [FieldOrder(2)] public uint Preview2Address { get; set; } + [FieldOrder(3)] public uint SVGDocumentAddress { get; set; } + + public override string ToString() + { + return $"{nameof(Identifier)}: {Identifier}, {nameof(Preview1Address)}: {Preview1Address}, {nameof(Preview2Address)}: {Preview2Address}, {nameof(SVGDocumentAddress)}: {SVGDocumentAddress}"; + } + } + #endregion + + #region Preview + /// + /// The files contain two preview images. + /// These are shown on the printer display when choosing which file to print, sparing the poor printer from needing to render a 3D image from scratch. + /// + public class Preview + { + public const byte IdentifierLength = 2; + public const string IdentifierText = "BM"; + + /// + /// Gets or sets the identifier, BM = bitmap? + /// + [FieldOrder(0)] [FieldLength(IdentifierLength)] public string Identifier { get; set; } = IdentifierText; + + /// + /// Gets or sets the table total size + /// + [FieldOrder(1)] public uint TableSize { get; set; } + [FieldOrder(2)] public uint Padding1 { get; set; } + [FieldOrder(3)] public uint DpiX { get; set; } = 54; + [FieldOrder(4)] public uint DpiY { get; set; } = 40; + + [FieldOrder(5)] public uint ResolutionX { get; set; } + + /// + /// Gets the Y dimension of the preview image, in pixels. + /// + [FieldOrder(6)] public uint ResolutionY { get; set; } + + [FieldOrder(7)] public uint Unknown1 { get; set; } = 0x180001; + [FieldOrder(8)] public uint Padding2 { get; set; } + [FieldOrder(9)] public uint DataSize { get; set; } + [FieldOrder(10)] public uint Unknown2 { get; set; } = 3780; + [FieldOrder(11)] public uint Unknown3 { get; set; } = 3780; + [FieldOrder(12)] public uint Padding3 { get; set; } + [FieldOrder(13)] public uint Padding4 { get; set; } + + [FieldOrder(14)] [FieldLength(nameof(DataSize))] public byte[] BGR { get; set; } + + public override string ToString() + { + return $"{nameof(Identifier)}: {Identifier}, {nameof(TableSize)}: {TableSize}, {nameof(Padding1)}: {Padding1}, {nameof(DpiX)}: {DpiX}, {nameof(DpiY)}: {DpiY}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Unknown1)}: {Unknown1}, {nameof(Padding2)}: {Padding2}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(BGR)}: {BGR.Length}"; + } + } + + #endregion + + #endregion + + #region Properties + + public Header HeaderSettings { get; protected internal set; } = new(); + + public FlashForgeSVGXSvg SVGDocument { get; protected internal set; } = new(); + + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new (typeof(FlashForgeSVGXFile), "svgx", "Flashforge SVGX"), + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = + { + PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.BottomExposureTime, + PrintParameterModifier.ExposureTime, + //PrintParameterModifier.LightPWM, + }; + + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(128, 128), + new(200, 240) + }; + + public override uint ResolutionX + { + get => SVGDocument.PrintParameters.ResolutionX; + set + { + SVGDocument.PrintParameters.ResolutionX = value; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get => SVGDocument.PrintParameters.ResolutionY; + set + { + SVGDocument.PrintParameters.ResolutionY = value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => SVGDocument.PrintParameters.DisplayWidth; + set + { + SVGDocument.PrintParameters.DisplayWidth = (float)Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + + public override float DisplayHeight + { + get => SVGDocument.PrintParameters.DisplayHeight; + set + { + SVGDocument.PrintParameters.DisplayHeight = (float)Math.Round(value, 2); + RaisePropertyChanged(); + } + } + + public override Enumerations.FlipDirection DisplayMirror + { + get => Enumerations.FlipDirection.Vertically; + set {} + } + + public override float MachineZ + { + get => SVGDocument.PrintParameters.MachineZ > 0 ? SVGDocument.PrintParameters.MachineZ : base.MachineZ; + set => base.MachineZ = SVGDocument.PrintParameters.MachineZ = (float)Math.Round(value, 2); + } + + public override float LayerHeight + { + get => SVGDocument.PrintParameters.LayerHeight; + set + { + SVGDocument.PrintParameters.LayerHeight = Layer.RoundHeight(value); + RaisePropertyChanged(); + } + } + + public override float PrintHeight + { + get => base.PrintHeight; + set => base.PrintHeight = SVGDocument.PrintParameters.PrintRange.MaxZ = base.PrintHeight; + } + + public override uint LayerCount + { + get => base.LayerCount; + set => SVGDocument.PrintParameters.LayerCount = base.LayerCount; + } + + public override ushort BottomLayerCount + { + get => SVGDocument.PrintParameters.ProjectionTime.BottomLayerCount; + set => base.BottomLayerCount = SVGDocument.PrintParameters.ProjectionTime.BottomLayerCount = value; + } + + public override float BottomExposureTime + { + get => SVGDocument.PrintParameters.ProjectionTime.BottomExposureTime; + set => base.BottomExposureTime = SVGDocument.PrintParameters.ProjectionTime.BottomExposureTime = (float)Math.Round(value, 2); + } + + + public override float ExposureTime + { + get => SVGDocument.PrintParameters.ProjectionTime.ExposureTime; + set => base.ExposureTime = SVGDocument.PrintParameters.ProjectionTime.ExposureTime = (float)Math.Round(value, 2); + } + + /*public override float BottomLiftHeight + { + get => HeaderSettings.BottomLiftHeight; + set => base.BottomLiftHeight = HeaderSettings.BottomLiftHeight = (float)Math.Round(value, 2); + } + + public override float LiftHeight + { + get => HeaderSettings.LiftHeight; + set => base.LiftHeight = HeaderSettings.LiftHeight = (float)Math.Round(value, 2); + } + + public override float BottomLiftSpeed + { + get => HeaderSettings.BottomLiftSpeed; + set => base.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed = (float)Math.Round(value, 2); + } + + public override float LiftSpeed + { + get => HeaderSettings.LiftSpeed; + set => base.LiftSpeed = HeaderSettings.LiftSpeed = (float)Math.Round(value, 2); + } + + public override float BottomRetractSpeed => RetractSpeed; + + public override float RetractSpeed + { + get => HeaderSettings.RetractSpeed; + set => base.RetractSpeed = HeaderSettings.RetractSpeed = (float)Math.Round(value, 2); + } + + public override byte BottomLightPWM + { + get => (byte) HeaderSettings.BottomLightPWM; + set => base.BottomLightPWM = (byte) (HeaderSettings.BottomLightPWM = value); + } + + public override byte LightPWM + { + get => (byte) HeaderSettings.LightPWM; + set => base.LightPWM = (byte) (HeaderSettings.LightPWM = value); + }*/ + + public override float MaterialMilliliters + { + get => SVGDocument.PrintParameters.MaterialMilliliters; + set + { + base.MaterialMilliliters = value; + SVGDocument.PrintParameters.MaterialMilliliters = base.MaterialMilliliters; + } + } + + public override string MaterialName + { + get => SVGDocument.PrintParameters.MaterialName; + set => base.MaterialName = SVGDocument.PrintParameters.MaterialName = value; + } + + public override string MachineName + { + get => SVGDocument.PrintParameters.MachineName; + set => base.MachineName = SVGDocument.PrintParameters.MachineName = value; + } + + public override object[] Configs => new object[] { HeaderSettings, SVGDocument.PrintParameters, SVGDocument.PrintParameters.ProjectionAdjust, SVGDocument.PrintParameters.PrintRange, SVGDocument.PrintParameters.ProjectionTime }; + + #endregion + + #region Constructors + public FlashForgeSVGXFile() + { + } + #endregion + + #region Methods + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + if (SVGDocument.PrintParameters.ResolutionX == 0 || SVGDocument.PrintParameters.ResolutionY == 0 || + SVGDocument.PrintParameters.DisplayWidth == 0 || SVGDocument.PrintParameters.DisplayHeight == 0) + { + throw new FileLoadException("This file does not contain a resolution and/or display size information needed to generate the layer images.\n" + + "Note that FlashDLPrint slicer is unable to output files with the required information to load in here.\n" + + "Please use other compatible slicer capable of output the correct information to load the file in here.", fileFullPath); + } + + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin); + + HeaderSettings.Preview1Address = 0; + HeaderSettings.Preview2Address = 0; + + progress.Reset(OperationProgress.StatusEncodePreviews, (uint)ThumbnailsOriginalSize.Length); + if (Thumbnails is not null) + { + foreach (var mat in Thumbnails) + { + if (HeaderSettings.Preview2Address > 0) break; + if(mat is null) continue; + + var preview = new Preview + { + ResolutionX = (uint)mat.Width, + ResolutionY = (uint)mat.Height + }; + + using var matFlip = new Mat(); + CvInvoke.Flip(mat, matFlip, FlipType.Vertical); + + var bytes = EncodeImage(DATATYPE_BGR888, matFlip); + preview.BGR = bytes; + preview.DataSize = (uint)bytes.Length; + preview.TableSize = (uint)Helpers.Serializer.SizeOf(preview); + + if (HeaderSettings.Preview1Address == 0) + { + HeaderSettings.Preview1Address = (uint)outputFile.Position; + } + else + { + HeaderSettings.Preview2Address = (uint)outputFile.Position; + } + + outputFile.WriteSerialize(preview); + progress++; + } + } + + var halfDisplay = Display.Half(); + var ppmm = Ppmm; + var pixelUm = PixelSizeMicronsMax; + + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + SVGDocument.Groups = new List { new("background") }; + var groups = new FlashForgeSVGXSvgGroup[LayerCount]; + + Parallel.For(0, LayerCount, /*new ParallelOptions{MaxDegreeOfParallelism = 1},*/ layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + + groups[layerIndex] = new FlashForgeSVGXSvgGroup($"layer-{layerIndex}"); + + using var mat = this[layerIndex].LayerMat; + //CvInvoke.Threshold(mat, mat, 127, 255, ThresholdType.Binary); // no AA + + using var contours = new VectorOfVectorOfPoint(); + using var hierarchy = new Mat(); + CvInvoke.FindContours(mat, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxSimple); + var hierarchyJagged = hierarchy.GetData(); + + float minx = SVGDocument.PrintParameters.PrintRange.MinX; + float miny = SVGDocument.PrintParameters.PrintRange.MinY; + float maxx = SVGDocument.PrintParameters.PrintRange.MaxX; + float maxy = SVGDocument.PrintParameters.PrintRange.MaxY; + + var path = new StringBuilder(); + for (int i = 0; i < contours.Size; i++) + { + if (contours[i].Size == 0) continue; + if ((int)hierarchyJagged.GetValue(0, i, 3) == -1) // Top hierarchy + { + if (path.Length > 0) + { + groups[layerIndex].Paths.Add(new FlashForgeSVGXSvgPath(path.ToString())); + } + path.Clear(); + + groups[layerIndex].Area = (float)Math.Round(Math.Cbrt(CvInvoke.ContourArea(contours[i]) / pixelUm), 3); + groups[layerIndex].Perimeter = (float)Math.Round(CvInvoke.ArcLength(contours[i], true) / pixelUm, 3); + } + else + { + path.Append(' '); + } + + var mmX = (float)Math.Round(contours[i][0].X / ppmm.Width - halfDisplay.Width, 3); + var mmY = (float)Math.Round(contours[i][0].Y / ppmm.Height - halfDisplay.Height, 3); + + minx = Math.Min(minx, mmX); + miny = Math.Min(miny, mmY); + maxx = Math.Max(maxx, mmX); + maxy = Math.Max(maxy, mmY); + + path.Append($"M {mmX} {mmY} L"); + for (int x = 1; x < contours[i].Size; x++) + { + mmX = (float)Math.Round(contours[i][x].X / ppmm.Width - halfDisplay.Width, 3); + mmY = (float)Math.Round(contours[i][x].Y / ppmm.Height - halfDisplay.Height, 3); + path.Append($" {mmX} {mmY}"); + + minx = Math.Min(minx, mmX); + miny = Math.Min(miny, mmY); + maxx = Math.Max(maxx, mmX); + maxy = Math.Max(maxy, mmY); + } + path.Append(" Z"); + } + + if (path.Length > 0) // Left over + { + groups[layerIndex].Paths.Add(new FlashForgeSVGXSvgPath(path.ToString())); + } + + lock (progress.Mutex) + { + SVGDocument.PrintParameters.PrintRange.MinX = Math.Min(SVGDocument.PrintParameters.PrintRange.MinX, minx); + SVGDocument.PrintParameters.PrintRange.MinY = Math.Min(SVGDocument.PrintParameters.PrintRange.MinY, miny); + SVGDocument.PrintParameters.PrintRange.MaxX = Math.Max(SVGDocument.PrintParameters.PrintRange.MaxX, maxx); + SVGDocument.PrintParameters.PrintRange.MaxY = Math.Max(SVGDocument.PrintParameters.PrintRange.MaxY, maxy); + progress++; + } + }); + + SVGDocument.Groups.AddRange(groups); + + HeaderSettings.SVGDocumentAddress = (uint)outputFile.Position; + + outputFile.WriteString(SVGDocument.SerializeToString()); + + outputFile.Seek(0, SeekOrigin.Begin); + outputFile.WriteSerialize(HeaderSettings); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine(SVGDocument); + Debug.WriteLine("-End-"); + } + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + HeaderSettings = Helpers.Deserialize
(inputFile); + if (HeaderSettings.Identifier != Header.IdentifierText) + { + throw new FileLoadException("Not a valid Flashforge SVGX file!", fileFullPath); + } + + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); + Debug.Write("Header -> "); + Debug.WriteLine(HeaderSettings); + + byte thumbnailCount = 0; + if (HeaderSettings.Preview1Address > 0) + { + inputFile.Seek(HeaderSettings.Preview1Address, SeekOrigin.Begin); + var preview = Helpers.Deserialize(inputFile); + Thumbnails[thumbnailCount] = DecodeImage(DATATYPE_BGR888, preview.BGR, preview.ResolutionX, preview.ResolutionY); + CvInvoke.Flip(Thumbnails[thumbnailCount], Thumbnails[thumbnailCount], FlipType.Vertical); + Debug.Write($"Preview[{thumbnailCount}] -> "); + Debug.WriteLine(preview); + thumbnailCount++; + + } + progress++; + if (HeaderSettings.Preview2Address > 0) + { + inputFile.Seek(HeaderSettings.Preview2Address, SeekOrigin.Begin); + var preview = Helpers.Deserialize(inputFile); + Thumbnails[thumbnailCount] = DecodeImage(DATATYPE_BGR888, preview.BGR, preview.ResolutionX, preview.ResolutionY); + CvInvoke.Flip(Thumbnails[thumbnailCount], Thumbnails[thumbnailCount], FlipType.Vertical); + Debug.Write($"Preview[{thumbnailCount}] -> "); + Debug.WriteLine(preview); + thumbnailCount++; + + } + progress++; + + inputFile.Seek(HeaderSettings.SVGDocumentAddress, SeekOrigin.Begin); + string svgDocument = Encoding.UTF8.GetString(inputFile.ReadToEnd()); + SVGDocument = XmlExtensions.DeserializeObject(svgDocument); + + Debug.WriteLine(SVGDocument); + + if (SVGDocument.PrintParameters.ResolutionX == 0 || SVGDocument.PrintParameters.ResolutionY == 0 || + SVGDocument.PrintParameters.DisplayWidth == 0 || SVGDocument.PrintParameters.DisplayHeight == 0) + { + throw new FileLoadException("This file does not contain a resolution and/or display size information needed to generate the layer images.\n" + + "Note that FlashDLPrint slicer is unable to output files with the required information to load in here.\n" + + "Please use other compatible slicer capable of output the correct information to load the file in here.", fileFullPath); + } + + var halfDisplay = Display.Half(); + var ppmm = Ppmm; + + LayerManager.Init(SVGDocument.PrintParameters.LayerCount); + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + + Parallel.For(0, LayerCount, /*new ParallelOptions{MaxDegreeOfParallelism = 1},*/ layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + + var mat = EmguExtensions.InitMat(Resolution); + + var group = SVGDocument.Groups.FirstOrDefault(g => g.Id == $"layer-{layerIndex}"); + + if (group is not null) + { + var pointsOfPoints = new List(); + var points = new List(); + foreach (var path in group.Paths) + { + if (progress.Token.IsCancellationRequested) break; + var spaceSplit = path.Value.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + + for (int i = 0; i < spaceSplit.Length; i++) + { + if (spaceSplit[i] == "M") + { + if (points.Count > 0) + { + pointsOfPoints.Add(points.ToArray()); + points.Clear(); + } + continue; + } + if (spaceSplit[i] == "Z") + { + if (points.Count > 0) + { + pointsOfPoints.Add(points.ToArray()); + points.Clear(); + } + continue; + } + if (spaceSplit[i].Length == 1 && !char.IsDigit(spaceSplit[i][0])) continue; // Ignore any other not processed 1 char that's not a digit (L) + + if (i + 1 >= spaceSplit.Length) break; // No more to see + + + if (!float.TryParse(spaceSplit[i], out var mmX)) continue; + if (!float.TryParse(spaceSplit[++i], out var mmY)) continue; + + + var mmAbsX = Math.Clamp(halfDisplay.Width + mmX, 0, DisplayWidth); + var mmAbsY = Math.Clamp(halfDisplay.Height + mmY, 0, DisplayHeight); + + int x = (int)(mmAbsX * ppmm.Width); + int y = (int)(mmAbsY * ppmm.Height); + + points.Add(new Point(x, y)); + } + + if (points.Count > 0) // Leftovers, still this should never happen! + { + pointsOfPoints.Add(points.ToArray()); + points.Clear(); + } + } + + if (pointsOfPoints.Count > 0) + { + using var vecPoints = new VectorOfVectorOfPoint(pointsOfPoints.ToArray()); + CvInvoke.DrawContours(mat, vecPoints, -1, EmguExtensions.WhiteColor, -1); + } + + } + + progress.Token.ThrowIfCancellationRequested(); + + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + progress.LockAndIncrement(); + }); + } + + 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(HeaderSettings.SVGDocumentAddress, SeekOrigin.Begin); + outputFile.SetLength(outputFile.Position); + outputFile.WriteString(SVGDocument.SerializeToString()); + } + + #endregion + } +} diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs index ad0a7ff..77f2cce 100644 --- a/UVtools.Core/FileFormats/GR1File.cs +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -230,7 +230,7 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror { get; set; } + public override Enumerations.FlipDirection DisplayMirror { get; set; } public override float LayerHeight { diff --git a/UVtools.Core/FileFormats/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs index 59ce84a..4ce65cb 100644 --- a/UVtools.Core/FileFormats/ImageFile.cs +++ b/UVtools.Core/FileFormats/ImageFile.cs @@ -66,12 +66,6 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror - { - get => false; - set { } - } - public override float LayerHeight { get; set; } = 0.01f; /*public override float PrintTime { get; } = 0; public override float UsedMaterial { get; } = 0; diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 5c79a18..e00e6cd 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -344,9 +344,9 @@ namespace UVtools.Core.FileFormats set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2); } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => true; + get => Enumerations.FlipDirection.Horizontally; set { } } diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index d3516b8..f62c05a 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -234,7 +234,7 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror { get; set; } + public override Enumerations.FlipDirection DisplayMirror { get; set; } public override float LayerHeight { get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray())); diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs index 82d26f6..20e4834 100644 --- a/UVtools.Core/FileFormats/OSLAFile.cs +++ b/UVtools.Core/FileFormats/OSLAFile.cs @@ -365,12 +365,12 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => HeaderSettings.DisplayMirror is >= 1 and <= 3; + get => (Enumerations.FlipDirection)HeaderSettings.DisplayMirror; set { - HeaderSettings.DisplayMirror = value ? (byte)1 : (byte)0; + HeaderSettings.DisplayMirror = (byte)value; RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index 8341204..94f34fb 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -728,12 +728,12 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => HeaderSettings.ProjectorType > 0; + get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally; set { - HeaderSettings.ProjectorType = value ? 1u : 0; + HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1; RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index da588cb..f4492f8 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -308,9 +308,9 @@ namespace UVtools.Core.FileFormats set { } } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => true; + get => Enumerations.FlipDirection.Horizontally; set { } } diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 20ffd28..7724596 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -14,6 +14,7 @@ using System.IO; using System.Threading.Tasks; using BinarySerialization; using Emgu.CV; +using Emgu.CV.CvEnum; using UVtools.Core.Extensions; using UVtools.Core.Operations; @@ -22,6 +23,9 @@ namespace UVtools.Core.FileFormats public class PhotonWorkshopFile : FileFormat { #region Constants + public const byte VERSION_1 = 1; + public const ushort VERSION_515 = 515; + public const byte MarkSize = 12; public const byte RLE1EncodingLimit = 0x7d; // 125; public const ushort RLE4EncodingLimit = 0xfff; // 4095; @@ -76,9 +80,11 @@ namespace UVtools.Core.FileFormats AnyCubicPhotonS, AnyCubicPhotonZero, AnyCubicPhotonX, + AnyCubicPhotonUltra, AnyCubicPhotonMono, AnyCubicPhotonMonoSE, AnyCubicPhotonMonoX, + AnyCubicPhotonMonoSQ, } #endregion @@ -103,7 +109,7 @@ namespace UVtools.Core.FileFormats /// Gets the file format version /// 0C ///
- [FieldOrder(1)] public uint Version { get; set; } = 1; + [FieldOrder(1)] public uint Version { get; set; } = VERSION_1; /// /// Gets the area num @@ -129,9 +135,9 @@ namespace UVtools.Core.FileFormats [FieldOrder(5)] public uint PreviewAddress { get; set; } /// - /// 20 + /// 20, Spoted on version 515 only /// - [FieldOrder(6)] public uint Padding2 { get; set; } + [FieldOrder(6)] public uint PreviewEndAddress { get; set; } /// /// Gets the layer definition start address @@ -142,7 +148,7 @@ namespace UVtools.Core.FileFormats /// /// 28 /// - [FieldOrder(8)] public uint Padding3 { get; set; } + [FieldOrder(8)] public uint Padding2 { get; set; } /// /// Gets layer image start address @@ -152,7 +158,7 @@ namespace UVtools.Core.FileFormats public override string ToString() { - return $"{nameof(Mark)}: {Mark}, {nameof(Version)}: {Version}, {nameof(AreaNum)}: {AreaNum}, {nameof(HeaderAddress)}: {HeaderAddress}, {nameof(Padding1)}: {Padding1}, {nameof(PreviewAddress)}: {PreviewAddress}, {nameof(Padding2)}: {Padding2}, {nameof(LayerDefinitionAddress)}: {LayerDefinitionAddress}, {nameof(Padding3)}: {Padding3}, {nameof(LayerImageAddress)}: {LayerImageAddress}"; + return $"{nameof(Mark)}: {Mark}, {nameof(Version)}: {Version}, {nameof(AreaNum)}: {AreaNum}, {nameof(HeaderAddress)}: {HeaderAddress}, {nameof(Padding1)}: {Padding1}, {nameof(PreviewAddress)}: {PreviewAddress}, {nameof(PreviewEndAddress)}: {PreviewEndAddress}, {nameof(LayerDefinitionAddress)}: {LayerDefinitionAddress}, {nameof(Padding2)}: {Padding2}, {nameof(LayerImageAddress)}: {LayerImageAddress}"; } } #endregion @@ -395,10 +401,10 @@ namespace UVtools.Core.FileFormats /// [FieldOrder(3)] public uint ResolutionY { get; set; } = 168; - [FieldOrder(4)] public uint Unknown1 { get; set; } + /*[FieldOrder(4)] public uint Unknown1 { get; set; } [FieldOrder(5)] public uint Unknown2 { get; set; } [FieldOrder(6)] public uint Unknown3 { get; set; } - [FieldOrder(7)] public uint Unknown4 { get; set; } + [FieldOrder(7)] public uint Unknown4 { get; set; }*/ [Ignore] public uint DataSize => ResolutionX * ResolutionY * 2; @@ -422,7 +428,7 @@ namespace UVtools.Core.FileFormats /*public unsafe Mat Decode(bool consumeData = true) { - Mat image = new(new Size((int) Width, (int) Height), DepthType.Cv8U, 3); + Mat image = new(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3); var span = image.GetBytePointer(); int pixel = 0; @@ -467,8 +473,8 @@ namespace UVtools.Core.FileFormats } return preview; - } - */ + }*/ + public override string ToString() { return $"{nameof(Section)}: {Section}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(DpiResolution)}: {DpiResolution}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Data)}: {Data?.Length ?? 0}"; @@ -922,12 +928,15 @@ namespace UVtools.Core.FileFormats public override FileExtension[] FileExtensions { get; } = { + new(typeof(PhotonWorkshopFile), "pws", "Photon / Photon S (PWS)"), + new(typeof(PhotonWorkshopFile), "pw0", "Photon Zero (PW0)"), + new(typeof(PhotonWorkshopFile), "pwx", "Photon X (PWX)"), + new(typeof(PhotonWorkshopFile), "dlp", "Photon Ultra (DLP)", false, false), new(typeof(PhotonWorkshopFile), "pwmx", "Photon Mono X (PWMX)"), new(typeof(PhotonWorkshopFile), "pwms", "Photon Mono SE (PWMS)"), new(typeof(PhotonWorkshopFile), "pwmo", "Photon Mono (PWMO)"), - new(typeof(PhotonWorkshopFile), "pwx", "Photon X (PWX)"), - new(typeof(PhotonWorkshopFile), "pws", "Photon / Photon S (PWS)"), - new(typeof(PhotonWorkshopFile), "pw0", "Photon Zero (PW0)"), + new(typeof(PhotonWorkshopFile), "pmsq", "Photon Mono SQ (PMSQ)", false, false), + }; @@ -980,23 +989,18 @@ namespace UVtools.Core.FileFormats { get { - switch (PrinterModel) + return PrinterModel switch { - case AnyCubicMachine.AnyCubicPhotonS: - return 68.04f; - case AnyCubicMachine.AnyCubicPhotonZero: - return 55.44f; - case AnyCubicMachine.AnyCubicPhotonX: - return 192; - case AnyCubicMachine.AnyCubicPhotonMono: - return 82.62f; - case AnyCubicMachine.AnyCubicPhotonMonoSE: - return 82.62f; - case AnyCubicMachine.AnyCubicPhotonMonoX: - return 192; - default: - return 0; - } + AnyCubicMachine.AnyCubicPhotonS => 68.04f, + AnyCubicMachine.AnyCubicPhotonZero => 55.44f, + AnyCubicMachine.AnyCubicPhotonX => 192, + AnyCubicMachine.AnyCubicPhotonUltra => 102.40f, + AnyCubicMachine.AnyCubicPhotonMono => 82.62f, + AnyCubicMachine.AnyCubicPhotonMonoSE => 82.62f, + AnyCubicMachine.AnyCubicPhotonMonoX => 192, + AnyCubicMachine.AnyCubicPhotonMonoSQ => 120, + _ => 0 + }; } set { } } @@ -1004,23 +1008,18 @@ namespace UVtools.Core.FileFormats { get { - switch (PrinterModel) + return PrinterModel switch { - case AnyCubicMachine.AnyCubicPhotonS: - return 120.96f; - case AnyCubicMachine.AnyCubicPhotonZero: - return 98.637f; - case AnyCubicMachine.AnyCubicPhotonX: - return 120; - case AnyCubicMachine.AnyCubicPhotonMono: - return 130.56f; - case AnyCubicMachine.AnyCubicPhotonMonoSE: - return 130.56f; - case AnyCubicMachine.AnyCubicPhotonMonoX: - return 120; - default: - return 0; - } + AnyCubicMachine.AnyCubicPhotonS => 120.96f, + AnyCubicMachine.AnyCubicPhotonZero => 98.637f, + AnyCubicMachine.AnyCubicPhotonX => 120, + AnyCubicMachine.AnyCubicPhotonUltra => 57.60f, + AnyCubicMachine.AnyCubicPhotonMono => 130.56f, + AnyCubicMachine.AnyCubicPhotonMonoSE => 130.56f, + AnyCubicMachine.AnyCubicPhotonMonoX => 120, + AnyCubicMachine.AnyCubicPhotonMonoSQ => 128, + _ => 0 + }; } set { } } @@ -1029,30 +1028,25 @@ namespace UVtools.Core.FileFormats { get { - switch (PrinterModel) + return PrinterModel switch { - case AnyCubicMachine.AnyCubicPhotonS: - return 165; - case AnyCubicMachine.AnyCubicPhotonZero: - return 150; - case AnyCubicMachine.AnyCubicPhotonX: - return 245; - case AnyCubicMachine.AnyCubicPhotonMono: - return 165; - case AnyCubicMachine.AnyCubicPhotonMonoSE: - return 160; - case AnyCubicMachine.AnyCubicPhotonMonoX: - return 245; - default: - return 0; - } + AnyCubicMachine.AnyCubicPhotonS => 165, + AnyCubicMachine.AnyCubicPhotonZero => 150, + AnyCubicMachine.AnyCubicPhotonX => 245, + AnyCubicMachine.AnyCubicPhotonUltra => 165, + AnyCubicMachine.AnyCubicPhotonMono => 165, + AnyCubicMachine.AnyCubicPhotonMonoSE => 160, + AnyCubicMachine.AnyCubicPhotonMonoX => 245, + AnyCubicMachine.AnyCubicPhotonMonoSQ => 200, + _ => 0 + }; } set { } } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => true; + get => Enumerations.FlipDirection.Horizontally; set {} } @@ -1198,23 +1192,18 @@ namespace UVtools.Core.FileFormats { get { - switch (PrinterModel) + return PrinterModel switch { - case AnyCubicMachine.AnyCubicPhotonS: - return "AnyCubic Photon S"; - case AnyCubicMachine.AnyCubicPhotonZero: - return "AnyCubic Photon Zero"; - case AnyCubicMachine.AnyCubicPhotonX: - return "AnyCubic Photon X"; - case AnyCubicMachine.AnyCubicPhotonMono: - return "AnyCubic Photon Mono"; - case AnyCubicMachine.AnyCubicPhotonMonoSE: - return "AnyCubic Photon Mono SE"; - case AnyCubicMachine.AnyCubicPhotonMonoX: - return "AnyCubic Photon Mono X"; - default: - return base.MachineName; - } + AnyCubicMachine.AnyCubicPhotonS => "AnyCubic Photon S", + AnyCubicMachine.AnyCubicPhotonZero => "AnyCubic Photon Zero", + AnyCubicMachine.AnyCubicPhotonX => "AnyCubic Photon X", + AnyCubicMachine.AnyCubicPhotonUltra => "AnyCubic Photon Ultra", + AnyCubicMachine.AnyCubicPhotonMono => "AnyCubic Photon Mono", + AnyCubicMachine.AnyCubicPhotonMonoSE => "AnyCubic Photon Mono SE", + AnyCubicMachine.AnyCubicPhotonMonoX => "AnyCubic Photon Mono X", + AnyCubicMachine.AnyCubicPhotonMonoSQ => "AnyCubic Photon Mono SQ", + _ => base.MachineName + }; } } @@ -1244,6 +1233,11 @@ namespace UVtools.Core.FileFormats return AnyCubicMachine.AnyCubicPhotonX; } + if (FileEndsWith(".dlp")) + { + return AnyCubicMachine.AnyCubicPhotonUltra; + } + if (FileEndsWith(".pwmo")) { return AnyCubicMachine.AnyCubicPhotonMono; @@ -1259,6 +1253,11 @@ namespace UVtools.Core.FileFormats return AnyCubicMachine.AnyCubicPhotonMonoX; } + if (FileEndsWith(".pmsq")) + { + return AnyCubicMachine.AnyCubicPhotonMonoSQ; + } + return AnyCubicMachine.AnyCubicPhotonS; } } @@ -1280,30 +1279,20 @@ namespace UVtools.Core.FileFormats protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - switch (PrinterModel) + HeaderSettings.PixelSizeUm = PixelSizeMicronsMax; + + /*PrinterModel switch { - case AnyCubicMachine.AnyCubicPhotonS: - HeaderSettings.PixelSizeUm = 47.25f; - break; - case AnyCubicMachine.AnyCubicPhotonZero: - HeaderSettings.PixelSizeUm = 115.5f; - break; - case AnyCubicMachine.AnyCubicPhotonX: - HeaderSettings.PixelSizeUm = 75; - break; - case AnyCubicMachine.AnyCubicPhotonMono: - HeaderSettings.PixelSizeUm = 51; - break; - case AnyCubicMachine.AnyCubicPhotonMonoSE: - HeaderSettings.PixelSizeUm = 51; - break; - case AnyCubicMachine.AnyCubicPhotonMonoX: - HeaderSettings.PixelSizeUm = 50; - break; - default: - HeaderSettings.PixelSizeUm = 47.25f; - break; - } + AnyCubicMachine.AnyCubicPhotonS => 47.25f, + AnyCubicMachine.AnyCubicPhotonZero => 115.5f, + AnyCubicMachine.AnyCubicPhotonX => 75, + AnyCubicMachine.AnyCubicPhotonUltra => 80, + AnyCubicMachine.AnyCubicPhotonMono => 51, + AnyCubicMachine.AnyCubicPhotonMonoSE => 51, + AnyCubicMachine.AnyCubicPhotonMonoX => 50, + AnyCubicMachine.AnyCubicPhotonMonoSQ => 50, + _ => 47.25f + };*/ HeaderSettings.PerLayerOverride = (byte)(LayerManager.AllLayersAreUsingGlobalParameters ? 0 : 1); @@ -1323,6 +1312,7 @@ namespace UVtools.Core.FileFormats }; Helpers.SerializeWriteFileStream(outputFile, preview); outputFile.WriteBytes(preview.Data); + FileMarkSettings.PreviewEndAddress = (uint)outputFile.Position; } progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); @@ -1393,9 +1383,9 @@ namespace UVtools.Core.FileFormats $"Invalid Filemark {FileMarkSettings.Mark}, expected {FileMark.SectionMarkFile}", fileFullPath); } - if (FileMarkSettings.Version != 1) + if (FileMarkSettings.Version is not VERSION_1 and not VERSION_515) { - throw new FileLoadException($"Invalid Version {FileMarkSettings.Version}, expected 1", + throw new FileLoadException($"Invalid Version {FileMarkSettings.Version}, expected {VERSION_1} or {VERSION_515}", fileFullPath); } @@ -1423,6 +1413,7 @@ namespace UVtools.Core.FileFormats inputFile.ReadBytes(PreviewSettings.Data); Thumbnails[0] = DecodeImage(DATATYPE_RGB565, PreviewSettings.Data, PreviewSettings.ResolutionX, PreviewSettings.ResolutionY); + //Thumbnails[0] = PreviewSettings.Decode(); PreviewSettings.Data = null; } diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 34c0d72..30a285b 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -384,13 +384,19 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => PrinterSettings.DisplayMirrorX || PrinterSettings.DisplayMirrorY; + get + { + if (PrinterSettings.DisplayMirrorX && PrinterSettings.DisplayMirrorY) return Enumerations.FlipDirection.Both; + if (PrinterSettings.DisplayMirrorX) return Enumerations.FlipDirection.Horizontally; + if (PrinterSettings.DisplayMirrorY) return Enumerations.FlipDirection.Vertically; + return Enumerations.FlipDirection.None; + } set { - PrinterSettings.DisplayMirrorX = value; - PrinterSettings.DisplayMirrorY = false; + PrinterSettings.DisplayMirrorX = value is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both; + PrinterSettings.DisplayMirrorY = value is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both; RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index 7ae70da..3712712 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -211,7 +211,7 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror { get; set; } + public override Enumerations.FlipDirection DisplayMirror { get; set; } public override byte AntiAliasing { diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index 2e07dbf..5c4e39f 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -283,7 +283,22 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror => ManifestFile.Machine.XMirror || ManifestFile.Machine.YMirror; + public override Enumerations.FlipDirection DisplayMirror + { + get + { + if (ManifestFile.Machine.XMirror && ManifestFile.Machine.YMirror) return Enumerations.FlipDirection.Both; + if (ManifestFile.Machine.XMirror) return Enumerations.FlipDirection.Horizontally; + if (ManifestFile.Machine.YMirror) return Enumerations.FlipDirection.Vertically; + return Enumerations.FlipDirection.None; + } + set + { + ManifestFile.Machine.XMirror = value is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both; + ManifestFile.Machine.YMirror = value is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both; + RaisePropertyChanged(); + } + } public override byte AntiAliasing { diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index 50a7a45..01cd3e7 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -276,7 +276,7 @@ namespace UVtools.Core.FileFormats } } - public override bool DisplayMirror => true; + public override Enumerations.FlipDirection DisplayMirror => Enumerations.FlipDirection.Vertically; public override byte AntiAliasing { diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 72027f4..35ff0b0 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -207,9 +207,9 @@ namespace UVtools.Core.FileFormats set { } } - public override bool DisplayMirror + public override Enumerations.FlipDirection DisplayMirror { - get => true; + get => Enumerations.FlipDirection.Horizontally; set { } } diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs index 14744f8..8785c31 100644 --- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs +++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs @@ -370,7 +370,7 @@ namespace UVtools.Core.Operations if (_bottomLayers <= 0) _bottomLayers = (ushort) Slicer.Slicer.MillimetersToLayers(1M, _layerHeight); if (_normalLayers <= 0) _normalLayers = (ushort) Slicer.Slicer.MillimetersToLayers(3.5M, _layerHeight); - _mirrorOutput = SlicerFile.DisplayMirror; + _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None; } #endregion @@ -659,7 +659,9 @@ namespace UVtools.Core.Operations if (_mirrorOutput) { - Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, FlipType.Horizontal)); + var flip = SlicerFile.DisplayMirror; + if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally; + Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip))); } // Preview diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index 3afa268..11ed778 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -1155,7 +1155,7 @@ namespace UVtools.Core.Operations { base.InitWithSlicerFile(); - _mirrorOutput = SlicerFile.DisplayMirror; + _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; @@ -2232,7 +2232,9 @@ namespace UVtools.Core.Operations if (_mirrorOutput) { - new OperationFlip(SlicerFile) { FlipDirection = FlipType.Horizontal }.Execute(progress); + var flip = SlicerFile.DisplayMirror; + if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally; + new OperationFlip(SlicerFile) { FlipDirection = Enumerations.ToOpenCVFlipType(flip) }.Execute(progress); } } diff --git a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs index b92d339..47308d3 100644 --- a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs +++ b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs @@ -112,7 +112,7 @@ namespace UVtools.Core.Operations if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.DisplayMirror; + _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None; } #endregion @@ -447,7 +447,9 @@ namespace UVtools.Core.Operations if (_mirrorOutput) { - Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, FlipType.Horizontal)); + var flip = SlicerFile.DisplayMirror; + if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally; + Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip))); } return layers; diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index 2ca4b29..70df44d 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -111,7 +111,7 @@ namespace UVtools.Core.Operations if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.DisplayMirror; + _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; @@ -385,7 +385,9 @@ namespace UVtools.Core.Operations if (_mirrorOutput) { - Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, FlipType.Horizontal)); + var flip = SlicerFile.DisplayMirror; + if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally; + Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip))); } return layers; diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs index 20036a7..a6a8f50 100644 --- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs +++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs @@ -431,7 +431,7 @@ namespace UVtools.Core.Operations if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.DisplayMirror; + _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; @@ -686,7 +686,9 @@ namespace UVtools.Core.Operations if (_mirrorOutput) { - Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, FlipType.Horizontal)); + var flip = SlicerFile.DisplayMirror; + if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally; + Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip))); } return layers; diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs index 392cdf2..9e98e24 100644 --- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs +++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs @@ -463,7 +463,7 @@ namespace UVtools.Core.Operations if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount; if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime; if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime; - _mirrorOutput = SlicerFile.DisplayMirror; + _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None; if (SlicerFile.DisplayWidth > 0) DisplayWidth = (decimal)SlicerFile.DisplayWidth; @@ -719,7 +719,9 @@ namespace UVtools.Core.Operations if (_mirrorOutput) { - Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, FlipType.Horizontal)); + var flip = SlicerFile.DisplayMirror; + if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally; + Parallel.ForEach(layers, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip))); } return layers; diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs index 0f8fb69..5925b67 100644 --- a/UVtools.Core/Operations/OperationLayerExportImage.cs +++ b/UVtools.Core/Operations/OperationLayerExportImage.cs @@ -7,7 +7,6 @@ */ using System; -using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Threading.Tasks; @@ -274,10 +273,10 @@ namespace UVtools.Core.Operations tw.Write(" "); } - tw.Write($"M{contours[i][0].X} {contours[i][0].Y}"); + tw.Write($"M {contours[i][0].X} {contours[i][0].Y} L"); for (int x = 1; x < contours[i].Size; x++) { - tw.Write($" L{contours[i][x].X} {contours[i][x].Y}"); + tw.Write($" {contours[i][x].X} {contours[i][x].Y}"); } tw.Write(" Z"); } diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs index 10f360c..b6a1b9c 100644 --- a/UVtools.Core/Operations/OperationPixelArithmetic.cs +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -794,11 +794,22 @@ namespace UVtools.Core.Operations and not PixelArithmeticOperators.KeepRegion and not PixelArithmeticOperators.DiscardRegion ? EmguExtensions.InitMat(HaveROI ? ROI.Size : SlicerFile.Resolution, new MCvScalar(_value)) : null; + public void PresetElephantFootCompensation() + { + SelectBottomLayers(); + Operator = PixelArithmeticOperators.Set; + ApplyMethod = PixelArithmeticApplyMethod.ModelWalls; + //Value = 190; + //WallThickness = 20; + WallChamfer = false; + UsePattern = false; + } + public void PresetPixelDimming() { Operator = PixelArithmeticOperators.Subtract; ApplyMethod = PixelArithmeticApplyMethod.ModelInner; - WallThickness = 20; + //WallThickness = 20; WallChamfer = false; UsePattern = true; } diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 84bc982..98d7617 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 2.19.5 + 2.20.0 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index 543738a..25de3e7 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -882,214 +882,241 @@ - + - + + + + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/UVtools.WPF/ConsoleArguments.cs b/UVtools.WPF/ConsoleArguments.cs index 8ae50e9..940b22b 100644 --- a/UVtools.WPF/ConsoleArguments.cs +++ b/UVtools.WPF/ConsoleArguments.cs @@ -141,7 +141,7 @@ namespace UVtools.WPF return true; } - if (args[0] is "--crypt-ctb" or "--encrypt-ctb" or "--decrypt-ctb") + if (args[0] == "--crypt-ctb") { if (!File.Exists(args[1])) { diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs index 89631a8..db3e05e 100644 --- a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Threading.Tasks; +using System.Threading.Tasks; using Avalonia.Markup.Xaml; using UVtools.Core.Objects; using UVtools.Core.Operations; diff --git a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml index fcd5b4b..2d6f2bb 100644 --- a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml @@ -137,14 +137,23 @@ Grid.ColumnSpan="9" VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> + +