From 4dae750e83987fde8b34087d26fc75407e88f55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Wed, 14 Apr 2021 06:08:37 +0100 Subject: v2.9.0 * **File formats:** * Add Voxeldance Tango (VDT) * Add Makerbase MKS-DLP (MDLPv1) * Add GR1 Workshop (GR1) * Add Creality CXDLP (CXDLP) * When decoding a file and have a empty resolution (Width: 0 or Height: 0) it will auto fix it by get and set the first layer image resolution to the file * Fix when decoding a file it was already set to require a full encode, preventing fast saves on print parameters edits * **GUI:** * When file resolution dismatch from layer resolution, it is now possible to auto fix it by set the layer resolution to the file * When loading a file with auto scan for issues disabled it will force auto scan for issues types that are instant to check (print height and empty layers), if any exists it will auto select issues tab * **(Add) PrusaSlicer printers:** * Creality HALOT-SKY CL-89 * Creality HALOT-SKY CL-60 * (Improvement) Tool - Adjust layer height: Improve the performance when multiplying layers / go with higher layer height * (Fix) PrusaSlicer Printer - Wanhao D7: Change the auto convertion format from .zip to .xml.cws --- CHANGELOG.md | 18 + CREDITS.md | 4 +- CompileLinux.sh | 2 - CompileWindows.bat | 21 - CreateRelease.GUI.ps1 | 9 - CreateRelease.WPF.ps1 | 243 -------- ImportPrusaSlicerData.bat | 89 --- PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini | 38 ++ PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini | 38 ++ PrusaSlicer/printer/Wanhao D7.ini | 4 +- README.md | 8 +- Scripts/ImportPrusaSlicerData.bat | 92 +++ Scripts/cxdlp.bt | 89 +++ Scripts/gr1.bt | 74 +++ Scripts/mdlp.bt | 66 ++ UVtools.Core/Extensions/EmguExtensions.cs | 20 +- UVtools.Core/Extensions/SizeExtensions.cs | 20 + UVtools.Core/FileFormats/CWSFile.cs | 8 +- UVtools.Core/FileFormats/CXDLPFile.cs | 703 ++++++++++++++++++++++ UVtools.Core/FileFormats/ChituboxFile.cs | 16 +- UVtools.Core/FileFormats/ChituboxZipFile.cs | 14 +- UVtools.Core/FileFormats/FDGFile.cs | 14 +- UVtools.Core/FileFormats/FileFormat.cs | 57 +- UVtools.Core/FileFormats/GR1File.cs | 531 ++++++++++++++++ UVtools.Core/FileFormats/ImageFile.cs | 9 +- UVtools.Core/FileFormats/LGSFile.cs | 4 +- UVtools.Core/FileFormats/MDLPFile.cs | 493 +++++++++++++++ UVtools.Core/FileFormats/MakerbaseFile.cs | 240 -------- UVtools.Core/FileFormats/PHZFile.cs | 18 +- UVtools.Core/FileFormats/PhotonSFile.cs | 3 +- UVtools.Core/FileFormats/PhotonWorkshopFile.cs | 6 +- UVtools.Core/FileFormats/SL1File.cs | 11 +- UVtools.Core/FileFormats/UVJFile.cs | 63 +- UVtools.Core/FileFormats/VDTFile.cs | 518 ++++++++++++++++ UVtools.Core/FileFormats/ZCodeFile.cs | 9 +- UVtools.Core/FileFormats/ZCodexFile.cs | 2 - UVtools.Core/GCode/GCodeBuilder.cs | 2 +- UVtools.Core/Helpers.cs | 6 +- UVtools.Core/Layer/LayerManager.cs | 6 +- UVtools.Core/Operations/OperationLayerReHeight.cs | 76 ++- UVtools.Core/UVtools.Core.csproj | 2 +- UVtools.InstallerMM/UVtools.InstallerMM.wxs | 6 + UVtools.WPF/MainWindow.Issues.cs | 39 +- UVtools.WPF/MainWindow.axaml.cs | 31 +- UVtools.WPF/UVtools.WPF.csproj | 2 +- build/CompileLinux.sh | 3 + build/CompileWindows.bat | 22 + build/CreateRelease.GUI.ps1 | 10 + build/CreateRelease.WPF.ps1 | 244 ++++++++ 49 files changed, 3240 insertions(+), 763 deletions(-) delete mode 100644 CompileLinux.sh delete mode 100644 CompileWindows.bat delete mode 100644 CreateRelease.GUI.ps1 delete mode 100644 CreateRelease.WPF.ps1 delete mode 100644 ImportPrusaSlicerData.bat create mode 100644 PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini create mode 100644 PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini create mode 100644 Scripts/ImportPrusaSlicerData.bat create mode 100644 Scripts/cxdlp.bt create mode 100644 Scripts/gr1.bt create mode 100644 Scripts/mdlp.bt create mode 100644 UVtools.Core/FileFormats/CXDLPFile.cs create mode 100644 UVtools.Core/FileFormats/GR1File.cs create mode 100644 UVtools.Core/FileFormats/MDLPFile.cs delete mode 100644 UVtools.Core/FileFormats/MakerbaseFile.cs create mode 100644 UVtools.Core/FileFormats/VDTFile.cs create mode 100644 build/CompileLinux.sh create mode 100644 build/CompileWindows.bat create mode 100644 build/CreateRelease.GUI.ps1 create mode 100644 build/CreateRelease.WPF.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3336159..ef2019c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 14/04/2021 - v2.9.0 + +* **File formats:** + * Add Voxeldance Tango (VDT) + * Add Makerbase MKS-DLP (MDLPv1) + * Add GR1 Workshop (GR1) + * Add Creality CXDLP (CXDLP) + * When decoding a file and have a empty resolution (Width: 0 or Height: 0) it will auto fix it by get and set the first layer image resolution to the file + * Fix when decoding a file it was already set to require a full encode, preventing fast saves on print parameters edits +* **GUI:** + * When file resolution dismatch from layer resolution, it is now possible to auto fix it by set the layer resolution to the file + * When loading a file with auto scan for issues disabled it will force auto scan for issues types that are instant to check (print height and empty layers), if any exists it will auto select issues tab +* **(Add) PrusaSlicer printers:** + * Creality HALOT-SKY CL-89 + * Creality HALOT-SKY CL-60 +* (Improvement) Tool - Adjust layer height: Improve the performance when multiplying layers / go with higher layer height +* (Fix) PrusaSlicer Printer - Wanhao D7: Change the auto convertion format from .zip to .xml.cws + ## 08/04/2021 - v2.8.4 * (Improvement) Layers: "IsBottomLayer" property will now computing the value taking the height into consideration instead of it index, this allow to identify the real bottom layers when using multiple layers with same heights diff --git a/CREDITS.md b/CREDITS.md index a27530d..acefb73 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -52,4 +52,6 @@ * Ben Ford * Mario Molero * Dennis Hansen -* Evert Goor \ No newline at end of file +* Evert Goor +* James Kao +* Finn Newick \ No newline at end of file diff --git a/CompileLinux.sh b/CompileLinux.sh deleted file mode 100644 index 0911e14..0000000 --- a/CompileLinux.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -dotnet build \ No newline at end of file diff --git a/CompileWindows.bat b/CompileWindows.bat deleted file mode 100644 index a717c14..0000000 --- a/CompileWindows.bat +++ /dev/null @@ -1,21 +0,0 @@ -@echo off -SET DIR=%~dp0 - -REM if exist "%SYSTEMROOT%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" SET MSBUILD_PATH="%SYSTEMROOT%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" -if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" SET MSBUILD_PATH="%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" -if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" SET MSBUILD_PATH="%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" - -IF [%MSBUILD_PATH%] == [] GOTO noMSBuild - -echo UVtools.sln Compile -echo %MSBUILD_PATH% -%MSBUILD_PATH% -p:Configuration=Release UVtools.sln -GOTO end - - -:noMSBuild - echo MSBuild.exe path not found! trying 'dotnet' instead - dotnet build - -:end - pause diff --git a/CreateRelease.GUI.ps1 b/CreateRelease.GUI.ps1 deleted file mode 100644 index 45dc54b..0000000 --- a/CreateRelease.GUI.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -cd $PSScriptRoot -$version = (Get-Command UVtools.GUI\bin\Release\UVtools.Core.dll).FileVersionInfo.FileVersion - -Remove-Item "$PSScriptRoot\UVtools.GUI\bin\Release\Logs" -Recurse -ErrorAction Ignore - -Add-Type -A System.IO.Compression.FileSystem -[IO.Compression.ZipFile]::CreateFromDirectory("$PSScriptRoot\UVtools.GUI\bin\Release", "$PSScriptRoot\UVtools.GUI\bin\UVtools_v$version.zip") - -Copy-Item "$PSScriptRoot\UVtools.Installer\bin\Release\UVtools.msi" -Destination "$PSScriptRoot\UVtools.GUI\bin\UVtools_v$version.msi" \ No newline at end of file diff --git a/CreateRelease.WPF.ps1 b/CreateRelease.WPF.ps1 deleted file mode 100644 index 32aac4c..0000000 --- a/CreateRelease.WPF.ps1 +++ /dev/null @@ -1,243 +0,0 @@ -# When using System.IO.Compression.ZipFile.CreateFromDirectory in PowerShell, it still uses backslashes in the zip paths -# despite this https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/mitigation-ziparchiveentry-fullname-path-separator - -# Based upon post by Seth Jackson https://sethjackson.github.io/2016/12/17/path-separators/ - -# -# PowerShell 5 (WMF5) & 6 -# Using class Keyword https://msdn.microsoft.com/powershell/reference/5.1/Microsoft.PowerShell.Core/about/about_Classes -# -# https://gist.github.com/lantrix/738ebfa616d5222a8b1db947793bc3fc -# - -#################################### -### Fix Zip slash ### -#################################### -Add-Type -AssemblyName System.Text.Encoding -Add-Type -AssemblyName System.IO.Compression.FileSystem - -class FixedEncoder : System.Text.UTF8Encoding { - FixedEncoder() : base($true) { } - - [byte[]] GetBytes([string] $s) - { - $s = $s.Replace("\", "/"); - return ([System.Text.UTF8Encoding]$this).GetBytes($s); - } -} - -#################################### -### Configuration ### -#################################### -$enableMSI = $true -#$buildOnly = $null -#$buildOnly = "win-x64" -# Profilling -$stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch -$deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch -$stopWatch.Start() - -# Script working directory -Set-Location $PSScriptRoot - -# Variables -$software = "UVtools" -$project = "UVtools.WPF" -$buildWith = "Release" -$netFolder = "net5.0" -$releaseFolder = "$PSScriptRoot\$project\bin\$buildWith\$netFolder" -$objFolder = "$PSScriptRoot\$project\obj\$buildWith\$netFolder" -$publishFolder = "publish" -$platformsFolder = "UVtools.Platforms" - -$macIcns = "UVtools.CAD/UVtools.icns" - -#$version = (Get-Command "$releaseFolder\UVtools.dll").FileVersionInfo.ProductVersion -$projectXml = [Xml] (Get-Content "$PSScriptRoot\$project\$project.csproj") -$version = "$($projectXml.Project.PropertyGroup.Version)".Trim(); -if([string]::IsNullOrWhiteSpace($version)){ - Write-Error "Can not detect the UVtools version, does $project\$project.csproj exists?" - exit -} - -# MSI Variables -$installers = @("UVtools.InstallerMM", "UVtools.Installer") -$msiSourceFile = "$PSScriptRoot\UVtools.Installer\bin\Release\UVtools.msi" -$msbuild = "`"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe`" /t:Build /p:Configuration=$buildWith /p:MSIProductVersion=$version" - -Write-Output " -#################################### -### UVtools builder & deployer ### -#################################### -Version: $version [$buildWith] -" - -#################################### -### Clean up previous publish ### -#################################### -# Clean up previous publish -Remove-Item $publishFolder -Recurse -ErrorAction Ignore # Clean - -#################################### -### Self-contained runtimes ### -#################################### -$runtimes = -@{ - "win-x64" = @{ - "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @("UVtools.sh") - "include" = @() - } - "linux-x64" = @{ - "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @() - "include" = @("libcvextern.so") - } - "arch-x64" = @{ - "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @() - "include" = @("libcvextern.so") - } - "rhel-x64" = @{ - "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @() - "include" = @("libcvextern.so") - } - "linux-arm64" = @{ - "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @() - "include" = @("libcvextern.so") - } - #"unix-x64" = @{ - # "extraCmd" = "-p:PublishReadyToRun=true" - # "exclude" = @("x86", "x64", "libcvextern.dylib") - #} - "osx-x64" = @{ - "extraCmd" = "-p:PublishReadyToRun=true" - "exclude" = @() - "include" = @("libcvextern.dylib") - } -} - -foreach ($obj in $runtimes.GetEnumerator()) { - if(![string]::IsNullOrWhiteSpace($buildOnly) -and !$buildOnly.Equals($obj.Name)) {continue} - # Configuration - $deployStopWatch.Restart() - $runtime = $obj.Name; # runtime name - $extraCmd = $obj.extraCmd; # extra cmd to run with dotnet - $targetZip = "$publishFolder/${software}_${runtime}_v$version.zip" # Target zip filename - - # Deploy - Write-Output "################################ -Building: $runtime" - dotnet publish $project -o "$publishFolder/$runtime" -c $buildWith -r $runtime $extraCmd - if(!$runtime.Equals('win-x64')) - { - # Fix permissions - wsl chmod +x "$publishFolder/$runtime/UVtools" `|`| : - wsl chmod +x "$publishFolder/$runtime/UVtools.sh" `|`| : - } - - # Cleanup - Remove-Item "$releaseFolder\$runtime" -Recurse -ErrorAction Ignore - Remove-Item "$objFolder\$runtime" -Recurse -ErrorAction Ignore - Write-Output "$releaseFolder\$runtime" - - foreach ($excludeObj in $obj.Value.exclude) { - Write-Output "Excluding: $excludeObj" - Remove-Item "$publishFolder\$runtime\$excludeObj" -Recurse -ErrorAction Ignore - } - - foreach ($includeObj in $obj.Value.include) { - Write-Output "Including: $includeObj" - Copy-Item "$platformsFolder\$runtime\$includeObj" -Destination "$publishFolder\$runtime" -Recurse -ErrorAction Ignore - } - - Write-Output "Compressing $runtime to: $targetZip" - Write-Output $targetZip "$publishFolder/$runtime" - - if($runtime.Equals('osx-x64')){ - $macAppFolder = "${software}.app" - $macPublishFolder = "$publishFolder/${macAppFolder}" - $macInfoplist = "$platformsFolder/$runtime/Info.plist" - $macOutputInfoplist = "$macPublishFolder/Contents" - $macTargetZipLegacy = "$publishFolder/${software}_${runtime}-legacy_v$version.zip" - - New-Item -ItemType directory -Path "$macPublishFolder" - New-Item -ItemType directory -Path "$macPublishFolder/Contents" - New-Item -ItemType directory -Path "$macPublishFolder/Contents/MacOS" - New-Item -ItemType directory -Path "$macPublishFolder/Contents/Resources" - - - Copy-Item "$macIcns" -Destination "$macPublishFolder/Contents/Resources" - ((Get-Content -Path "$macInfoplist") -replace '#VERSION',"$version") | Set-Content -Path "$macOutputInfoplist/Info.plist" - wsl cp -a "$publishFolder/$runtime/." "$macPublishFolder/Contents/MacOS" - - wsl cd "$publishFolder/" `&`& pwd `&`& zip -r "../$targetZip" "$macAppFolder/*" - wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$macTargetZipLegacy" . - - } - else { - wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$targetZip" . - } - - # Zip - #Write-Output "Compressing $runtime to: $targetZip" - #Write-Output $targetZip "$publishFolder/$runtime" - #[System.IO.Compression.ZipFile]::CreateFromDirectory("$publishFolder\$runtime", $targetZip, [System.IO.Compression.CompressionLevel]::Optimal, $false, [FixedEncoder]::new()) - #wsl cd "$publishFolder/$runtime" `&`& pwd `&`& chmod +x -f "./$software" `|`| : `&`& zip -r "../../$targetZip" "." - $deployStopWatch.Stop() - Write-Output "Took: $($deployStopWatch.Elapsed) -################################ -" -} - -# Universal package -<# -$deployStopWatch.Restart() -$runtime = "universal-x86-x64" -$targetZip = "$publishFolder\${software}_${runtime}_v$version.zip" - -Write-Output "################################ -Building: $runtime" -dotnet build $project -c $buildWith - -Write-Output "Compressing $runtime to: $targetZip" -[System.IO.Compression.ZipFile]::CreateFromDirectory($releaseFolder, $targetZip, [System.IO.Compression.CompressionLevel]::Optimal, $false, [FixedEncoder]::new()) -Write-Output "Took: $($deployStopWatch.Elapsed) -################################ -" -$stopWatch.Stop() -#> - -# MSI Installer for Windows -if($enableMSI) -{ - $deployStopWatch.Restart() - $runtime = 'win-x64' - $msiTargetFile = "$publishFolder\${software}_${runtime}_v$version.msi" - Write-Output "################################ - Building: $runtime MSI Installer" - - foreach($installer in $installers) - { - # Clean and build MSI - Remove-Item "$PSScriptRoot\$installer\obj" -Recurse -ErrorAction Ignore - Remove-Item "$PSScriptRoot\$installer\bin" -Recurse -ErrorAction Ignore - iex "& $msbuild $installer\$installer.wixproj" - } - - Write-Output "Coping $runtime MSI to: $msiTargetFile" - Copy-Item $msiSourceFile $msiTargetFile - - Write-Output "Took: $($deployStopWatch.Elapsed) - ################################ - " -} - - -Write-Output " -#################################### -### Completed ### -#################################### -In: $($stopWatch.Elapsed)" \ No newline at end of file diff --git a/ImportPrusaSlicerData.bat b/ImportPrusaSlicerData.bat deleted file mode 100644 index de8770e..0000000 --- a/ImportPrusaSlicerData.bat +++ /dev/null @@ -1,89 +0,0 @@ -@echo off -SET DIR=%~dp0 -SET INPUT_DIR=%AppData%\PrusaSlicer -SET OUTPUT_DIR=%~dp0PrusaSlicer - -SET PRINT_DIR=sla_print -SET PRINTER_DIR=printer - -SET files[0]=UVtools Prusa SL1.ini -SET files[1]=EPAX E6 Mono.ini -SET files[2]=EPAX E10 Mono.ini -SET files[3]=EPAX X1.ini -SET files[4]=EPAX X10.ini -SET files[5]=EPAX X10 4K Mono.ini -SET files[6]=EPAX X133 4K Mono.ini -SET files[7]=EPAX X156 4K Color.ini -SET files[8]=EPAX X1K 2K Mono.ini -SET files[9]=Zortrax Inkspire.ini -SET files[10]=Nova3D Elfin.ini -SET files[11]=Nova3D Bene4 Mono.ini -SET files[12]=AnyCubic Photon.ini -SET files[13]=AnyCubic Photon S.ini -SET files[14]=AnyCubic Photon Zero.ini -SET files[15]=AnyCubic Photon X.ini -SET files[16]=AnyCubic Photon Mono.ini -SET files[17]=AnyCubic Photon Mono SE.ini -SET files[18]=AnyCubic Photon Mono X.ini -SET files[19]=Elegoo Mars.ini -SET files[20]=Elegoo Mars 2 Pro.ini -SET files[21]=Elegoo Mars C.ini -SET files[22]=Elegoo Saturn.ini -SET files[23]=Peopoly Phenom.ini -SET files[24]=Peopoly Phenom L.ini -SET files[25]=Peopoly Phenom Noir.ini -SET files[26]=Peopoly Phenom XXL.ini -SET files[27]=QIDI Shadow5.5.ini -SET files[28]=QIDI Shadow6.0 Pro.ini -SET files[29]=QIDI S-Box.ini -SET files[30]=QIDI I-Box Mono.ini -SET files[31]=Phrozen Shuffle.ini -SET files[32]=Phrozen Shuffle Lite.ini -SET files[33]=Phrozen Shuffle XL.ini -SET files[34]=Phrozen Shuffle XL Lite.ini -SET files[35]=Phrozen Shuffle 16.ini -SET files[36]=Phrozen Shuffle 4K.ini -SET files[37]=Phrozen Sonic.ini -SET files[38]=Phrozen Sonic 4K.ini -SET files[39]=Phrozen Sonic Mighty 4K.ini -SET files[40]=Phrozen Sonic Mini.ini -SET files[41]=Phrozen Sonic Mini 4K.ini -SET files[42]=Phrozen Transform.ini -SET files[43]=Kelant S400.ini -SET files[44]=Wanhao D7.ini -SET files[45]=Wanhao D8.ini -SET files[46]=Wanhao CGR Mini Mono.ini -SET files[47]=Wanhao CGR Mono.ini -SET files[48]=Creality LD-002R.ini -SET files[49]=Creality LD-002H.ini -SET files[50]=Creality LD-006.ini -SET files[51]=Voxelab Polaris 5.5.ini -SET files[52]=Voxelab Proxima 6.ini -SET files[53]=Voxelab Ceres 8.9.ini -SET files[54]=Longer Orange 10.ini -SET files[55]=Longer Orange 30.ini -SET files[56]=Longer Orange4K.ini -SET files[57]=Uniz IBEE.ini - -echo PrusaSlicer Printers Instalation -echo This will replace printers, all changes will be discarded -echo %INPUT_DIR% -echo %OUTPUT_DIR% - -echo Importing Printers -for /F "tokens=2 delims==" %%s in ('set files[') do xcopy /d /y "%INPUT_DIR%\%PRINTER_DIR%\%%s" "%OUTPUT_DIR%\%PRINTER_DIR%\" - -echo Importing Profiles -xcopy /i /y /d %INPUT_DIR%\%PRINT_DIR% %OUTPUT_DIR%\%PRINT_DIR% - -REM /s Copies directories and subdirectories, unless they are empty. If you omit /s, xcopy works within a single directory. -REM /y Suppresses prompting to confirm that you want to overwrite an existing destination file. -REM /i If Source is a directory or contains wildcards and Destination does not exist, -REM xcopy assumes Destination specifies a directory name and creates a new directory. -REM Then, xcopy copies all specified files into the new directory. -REM By default, xcopy prompts you to specify whether Destination is a file or a directory. -REM /d Copies source files changed on or after the specified date only. -REM If you do not include a MM-DD-YYYY value, xcopy copies all Source files that are newer than existing Destination files. -REM This command-line option allows you to update files that have changed. - -pause \ No newline at end of file diff --git a/PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini b/PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini new file mode 100644 index 0000000..0a013c2 --- /dev/null +++ b/PrusaSlicer/printer/Creality HALOT-SKY CL-60.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.0+win64 on 2021-04-14 at 04:57:50 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,130x0,130x82,0x82 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 82 +display_mirror_x = 1 +display_mirror_y = 0 +display_orientation = landscape +display_pixels_x = 2560 +display_pixels_y = 1620 +display_width = 130 +elefant_foot_compensation = 0.2 +elefant_foot_min_width = 0.2 +fast_tilt_time = 5 +gamma_correction = 1 +host_type = octoprint +inherits = Original Prusa SL1 +max_exposure_time = 120 +max_initial_exposure_time = 300 +max_print_height = 160 +min_exposure_time = 1 +min_initial_exposure_time = 1 +print_host = +printer_model = SL1 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_HALOT-SKY_CL-60\nFILEFORMAT_CXDLP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_7\nLiftHeight_7\nBottomLiftSpeed_70\nLiftSpeed_70\nRetractSpeed_80\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_settings_id = +printer_technology = SLA +printer_variant = default +printer_vendor = +printhost_apikey = +printhost_cafile = +relative_correction = 1,1 +slow_tilt_time = 8 +thumbnails = 400x400,800x480 diff --git a/PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini b/PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini new file mode 100644 index 0000000..353849b --- /dev/null +++ b/PrusaSlicer/printer/Creality HALOT-SKY CL-89.ini @@ -0,0 +1,38 @@ +# generated by PrusaSlicer 2.3.0+win64 on 2021-04-14 at 04:53:05 UTC +absolute_correction = 0 +area_fill = 50 +bed_custom_model = +bed_custom_texture = +bed_shape = 0x0,192x0,192x120,0x120 +default_sla_material_profile = Prusa Orange Tough 0.05 +default_sla_print_profile = 0.05 Normal +display_height = 120 +display_mirror_x = 1 +display_mirror_y = 0 +display_orientation = landscape +display_pixels_x = 3840 +display_pixels_y = 2400 +display_width = 192 +elefant_foot_compensation = 0.2 +elefant_foot_min_width = 0.2 +fast_tilt_time = 5 +gamma_correction = 1 +host_type = octoprint +inherits = Original Prusa SL1 +max_exposure_time = 120 +max_initial_exposure_time = 300 +max_print_height = 200 +min_exposure_time = 1 +min_initial_exposure_time = 1 +print_host = +printer_model = SL1 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_HALOT-SKY_CL-89\nFILEFORMAT_CXDLP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_7\nLiftHeight_7\nBottomLiftSpeed_70\nLiftSpeed_70\nRetractSpeed_80\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_settings_id = +printer_technology = SLA +printer_variant = default +printer_vendor = +printhost_apikey = +printhost_cafile = +relative_correction = 1,1 +slow_tilt_time = 8 +thumbnails = 400x400,800x480 diff --git a/PrusaSlicer/printer/Wanhao D7.ini b/PrusaSlicer/printer/Wanhao D7.ini index 1c1636b..e8b4733 100644 --- a/PrusaSlicer/printer/Wanhao D7.ini +++ b/PrusaSlicer/printer/Wanhao D7.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.3.0+win64 on 2021-01-13 at 02:28:38 UTC +# generated by PrusaSlicer 2.3.0+win64 on 2021-04-09 at 19:00:09 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_WANHAO\nPRINTER_MODEL_D7\nFILEFORMAT_ZIP\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_60\nLiftSpeed_60\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_WANHAO\nPRINTER_MODEL_D7\nFILEFORMAT_XML.CWS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_60\nLiftSpeed_60\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/README.md b/README.md index ab525c7..d21e628 100644 --- a/README.md +++ b/README.md @@ -71,9 +71,13 @@ But also, i need victims for test subject. Proceed at your own risk! * CWS (NovaMaker) * RGB.CWS (Nova Bene4 Mono / Elfin2 Mono SE) * XML.CWS (Wanhao Workshop) +* MDLP (Makerbase MKS-DLP v1) +* GR1 (GR1 Workshop) +* CXDLP (Creality Box) * LGS (Longer Orange 10) * LGS30 (Longer Orange 30) -* UVJ (Zip file for manual manipulation format) +* VDT (Voxeldance Tango) +* UVJ (Zip file format for manual manipulation) * Image files (png, jpg, jpeg, gif, bmp) # Available printers for PrusaSlicer @@ -132,6 +136,8 @@ But also, i need victims for test subject. Proceed at your own risk! * LD-002R * LD-002H * LD-006 + * HALOT-SKY CL-89 + * HALOT-SKY CL-60 * **Voxelab** * Polaris 5.5 * Proxima 6 diff --git a/Scripts/ImportPrusaSlicerData.bat b/Scripts/ImportPrusaSlicerData.bat new file mode 100644 index 0000000..525dfcf --- /dev/null +++ b/Scripts/ImportPrusaSlicerData.bat @@ -0,0 +1,92 @@ +@echo off +SET DIR=%~dp0 +cd .. +SET INPUT_DIR=%AppData%\PrusaSlicer +SET OUTPUT_DIR=PrusaSlicer + +SET PRINT_DIR=sla_print +SET PRINTER_DIR=printer + +SET files[0]=UVtools Prusa SL1.ini +SET files[1]=EPAX E6 Mono.ini +SET files[2]=EPAX E10 Mono.ini +SET files[3]=EPAX X1.ini +SET files[4]=EPAX X10.ini +SET files[5]=EPAX X10 4K Mono.ini +SET files[6]=EPAX X133 4K Mono.ini +SET files[7]=EPAX X156 4K Color.ini +SET files[8]=EPAX X1K 2K Mono.ini +SET files[9]=Zortrax Inkspire.ini +SET files[10]=Nova3D Elfin.ini +SET files[11]=Nova3D Bene4 Mono.ini +SET files[12]=AnyCubic Photon.ini +SET files[13]=AnyCubic Photon S.ini +SET files[14]=AnyCubic Photon Zero.ini +SET files[15]=AnyCubic Photon X.ini +SET files[16]=AnyCubic Photon Mono.ini +SET files[17]=AnyCubic Photon Mono SE.ini +SET files[18]=AnyCubic Photon Mono X.ini +SET files[19]=Elegoo Mars.ini +SET files[20]=Elegoo Mars 2 Pro.ini +SET files[21]=Elegoo Mars C.ini +SET files[22]=Elegoo Saturn.ini +SET files[23]=Peopoly Phenom.ini +SET files[24]=Peopoly Phenom L.ini +SET files[25]=Peopoly Phenom Noir.ini +SET files[26]=Peopoly Phenom XXL.ini +SET files[27]=QIDI Shadow5.5.ini +SET files[28]=QIDI Shadow6.0 Pro.ini +SET files[29]=QIDI S-Box.ini +SET files[30]=QIDI I-Box Mono.ini +SET files[31]=Phrozen Shuffle.ini +SET files[32]=Phrozen Shuffle Lite.ini +SET files[33]=Phrozen Shuffle XL.ini +SET files[34]=Phrozen Shuffle XL Lite.ini +SET files[35]=Phrozen Shuffle 16.ini +SET files[36]=Phrozen Shuffle 4K.ini +SET files[37]=Phrozen Sonic.ini +SET files[38]=Phrozen Sonic 4K.ini +SET files[39]=Phrozen Sonic Mighty 4K.ini +SET files[40]=Phrozen Sonic Mini.ini +SET files[41]=Phrozen Sonic Mini 4K.ini +SET files[42]=Phrozen Transform.ini +SET files[43]=Kelant S400.ini +SET files[44]=Wanhao D7.ini +SET files[45]=Wanhao D8.ini +SET files[46]=Wanhao CGR Mini Mono.ini +SET files[47]=Wanhao CGR Mono.ini +SET files[48]=Creality LD-002R.ini +SET files[49]=Creality LD-002H.ini +SET files[50]=Creality LD-006.ini +SET files[51]=Creality HALOT-SKY CL-89.ini +SET files[52]=Creality HALOT-SKY CL-60.ini +SET files[53]=Voxelab Polaris 5.5.ini +SET files[54]=Voxelab Proxima 6.ini +SET files[55]=Voxelab Ceres 8.9.ini +SET files[56]=Longer Orange 10.ini +SET files[57]=Longer Orange 30.ini +SET files[58]=Longer Orange4K.ini +SET files[59]=Uniz IBEE.ini + +echo PrusaSlicer Printers Instalation +echo This will replace printers, all changes will be discarded +echo %INPUT_DIR% +echo %OUTPUT_DIR% + +echo Importing Printers +for /F "tokens=2 delims==" %%s in ('set files[') do xcopy /d /y "%INPUT_DIR%\%PRINTER_DIR%\%%s" "%OUTPUT_DIR%\%PRINTER_DIR%\" + +echo Importing Profiles +xcopy /i /y /d %INPUT_DIR%\%PRINT_DIR% %OUTPUT_DIR%\%PRINT_DIR% + +REM /s Copies directories and subdirectories, unless they are empty. If you omit /s, xcopy works within a single directory. +REM /y Suppresses prompting to confirm that you want to overwrite an existing destination file. +REM /i If Source is a directory or contains wildcards and Destination does not exist, +REM xcopy assumes Destination specifies a directory name and creates a new directory. +REM Then, xcopy copies all specified files into the new directory. +REM By default, xcopy prompts you to specify whether Destination is a file or a directory. +REM /d Copies source files changed on or after the specified date only. +REM If you do not include a MM-DD-YYYY value, xcopy copies all Source files that are newer than existing Destination files. +REM This command-line option allows you to update files that have changed. + +pause \ No newline at end of file diff --git a/Scripts/cxdlp.bt b/Scripts/cxdlp.bt new file mode 100644 index 0000000..6052b06 --- /dev/null +++ b/Scripts/cxdlp.bt @@ -0,0 +1,89 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: Creality +// Authors: Julien Delnatte +//------------------------------------------------ + +typedef struct { + BitfieldDisablePadding(); ushort startY:13; ushort endY:13; ushort x:14; + ubyte grey ; +} layerPointsData; + +typedef struct { + uint32 unknown ; +} layerDef; + +typedef struct() { + uint32 unknown ; + uint32 LayerPointNum ; + + layerPointsData pD()[LayerPointNum]; + BYTE CR_LF2[2] ; +} layerData; + +typedef struct(int size) { + BYTE layerDataBlock[size] ; +} rgbPreviewImageRawData; + +struct HEADER { + uint32 headerSize ; + BYTE header[header.headerSize] ; + + uint16 totalLayers ; + uint16 resolutionX ; + uint16 resolutionY ; + + rgbPreviewImageRawData preview(116*116*2); + BYTE rn0[2] ; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn1[2] ; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn2[2] ; + + uint32 plateformXLength ; + wchar_t plateformX[plateformXLength/2]; + + uint32 plateformYLength ; + wchar_t plateformY[plateformYLength/2]; + + uint32 layerThicknessLength ; + wchar_t layerThickness[layerThicknessLength/2]; + + uint16 exposureTime ; + uint16 turnOffTime ; + uint16 bottomExposure ; + uint16 bottomLayers ; + uint16 bottomRaise ; + uint16 bottomRaiseSpeed ; + uint16 raise ; + uint16 raiseSpeed ; + uint16 fallSpeed ; + uint16 bottomPWM ; + uint16 normalPWM ; + +} header; + +struct LAYER_DEF { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerDef lD(); + } + BYTE rn3[2] ; +} layerDefs; + +struct LAYERS { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerData lD(); + } +} layers; + +struct FOOTER { + uint32 footerSize ; + BYTE str[footer.footerSize] ; + + uint32 unknown ; +} footer; \ No newline at end of file diff --git a/Scripts/gr1.bt b/Scripts/gr1.bt new file mode 100644 index 0000000..308a637 --- /dev/null +++ b/Scripts/gr1.bt @@ -0,0 +1,74 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: GR1 Workshop +// Authors: Julien Delnatte +//------------------------------------------------ + +typedef struct { + uint16 startY ; + uint16 endY ; + uint16 startX ; + +} layerPointsData; + +typedef struct() { + uint32 LayerPointNum ; + + layerPointsData pD()[LayerPointNum]; + BYTE CR_LF2[2] ; +} layerData; + +typedef struct(int size) { + BYTE layerDataBlock[size] ; +} rgbPreviewImageRawData; + +struct HEADER { + char version[2] ; + ushort headerSize ; + BYTE header[header.headerSize] ; + + rgbPreviewImageRawData preview(116*116*2); + BYTE rn0[2] ; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn1[2] ; + + uint16 totalLayers ; + uint16 resolutionX ; + uint16 resolutionY ; + + uint32 plateformXLength ; + wchar_t plateformX[plateformXLength/2]; + + uint32 plateformYLength ; + wchar_t plateformY[plateformYLength/2]; + + uint32 layerThicknessLength ; + wchar_t layerThickness[layerThicknessLength/2]; + + uint16 exposureTime ; + uint16 turnOffTime ; + uint16 bottomExposure ; + uint16 bottomLayers ; + uint16 bottomRaise ; + uint16 bottomRaiseSpeed ; + uint16 raise ; + uint16 raiseSpeed ; + uint16 fallSpeed ; + uint16 bottomPWM ; + uint16 normalPWM ; + +} header; + +struct LAYERS { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerData lD(); + } +} layers; + +struct FOOTER { + uint32 footerSize ; + BYTE str[footer.footerSize] ; +} footer; \ No newline at end of file diff --git a/Scripts/mdlp.bt b/Scripts/mdlp.bt new file mode 100644 index 0000000..5886ee3 --- /dev/null +++ b/Scripts/mdlp.bt @@ -0,0 +1,66 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: Makerbase MKS-DLP mdlp +// Authors: Julien Delnatte +//------------------------------------------------ + +typedef struct { + uint16 startY ; + uint16 endY ; + uint16 startX ; + +} layerPointsData; + +typedef struct() { + uint32 LayerPointNum ; + + layerPointsData pD()[LayerPointNum]; + BYTE CR_LF2[2] ; +} layerData; + +typedef struct(int size) { + BYTE layerDataBlock[size] ; +} rgbPreviewImageRawData; + +struct HEADER { + char version[2] ; + ushort headerSize ; + BYTE header[header.headerSize] ; + + rgbPreviewImageRawData preview(116*116*2); + BYTE rn0[2] ; + + rgbPreviewImageRawData preview2(290*290*2); + BYTE rn1[2] ; + + uint16 totalLayers ; + uint16 resolutionX ; + uint16 resolutionY ; + + uint32 plateformXLength ; + wchar_t plateformX[plateformXLength/2]; + + uint32 plateformYLength ; + wchar_t plateformY[plateformYLength/2]; + + uint32 layerThicknessLength ; + wchar_t layerThickness[layerThicknessLength/2]; + + uint16 exposureTime ; + uint16 turnOffTime ; + uint16 bottomExposure ; + uint16 bottomLayers ; +} header; + +struct LAYERS { + local int i; + for( i = 0; i < header.totalLayers; i++ ){ + layerData lD(); + } +} layers; + +struct FOOTER { + uint32 footerSize ; + BYTE str[footer.footerSize] ; +} footer; \ No newline at end of file diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 48a9da8..9ea852a 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -18,12 +18,12 @@ namespace UVtools.Core.Extensions { public static class EmguExtensions { - public static readonly MCvScalar WhiteByte = new MCvScalar(255); - public static readonly MCvScalar White3Byte = new MCvScalar(255, 255, 255); - public static readonly MCvScalar BlackByte = new MCvScalar(0); - public static readonly MCvScalar Black3Byte = new MCvScalar(0, 0, 0); - public static readonly MCvScalar Transparent4Byte = new MCvScalar(0, 0, 0, 0); - public static readonly MCvScalar Black4Byte = new MCvScalar(0, 0, 0, 255); + public static readonly MCvScalar WhiteByte = new(255); + public static readonly MCvScalar White3Byte = new(255, 255, 255); + public static readonly MCvScalar BlackByte = new(0); + public static readonly MCvScalar Black3Byte = new(0, 0, 0); + public static readonly MCvScalar Transparent4Byte = new(0, 0, 0, 0); + public static readonly MCvScalar Black4Byte = new(0, 0, 0, 255); public static unsafe byte* GetBytePointer(this Mat mat) { @@ -64,10 +64,16 @@ namespace UVtools.Core.Extensions public static unsafe Span GetPixelRowSpan(this Mat mat, int y, int length = 0, int offset = 0) { - return new(IntPtr.Add(mat.DataPointer, y * mat.Step + offset).ToPointer(), length == 0 ? mat.Step : length); + return new(IntPtr.Add(mat.DataPointer, y * mat.Step + offset).ToPointer(), length <= 0 ? mat.Step : length); //return mat.GetPixelSpan().Slice(offset, mat.Step); } + public static unsafe Span GetPixelColSpan(this Mat mat, int x, int length = 0, int offset = 0) + { + var colMat = mat.Col(x); + return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length); + } + /// /// Gets if a is all zeroed /// diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index f170e12..e16893c 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -46,6 +46,16 @@ namespace UVtools.Core.Extensions public static Size Inflate(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels); public static Size Inflate(this Size size, int width, int height) => new (size.Width + width, size.Height + height); + /// + /// Gets if this size have a zero value on width or height + /// + /// + /// + public static bool HaveZero(this Size size) + { + return size.Width <= 0 && size.Height <= 0; + } + /// /// Exchange width with height /// @@ -74,6 +84,16 @@ namespace UVtools.Core.Extensions return round >= 0 ? (float) Math.Round(rect.Width * rect.Height, round) : rect.Width * rect.Height; } + /// + /// Gets if this size have a zero value on width or height + /// + /// + /// + public static bool HaveZero(this SizeF size) + { + return size.Width <= 0 && size.Height <= 0; + } + public static float Area(this SizeF size, int round = -1) { return round >= 0 ? (float)Math.Round(size.Width * size.Height, round) : size.Width * size.Height; diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 3f6e1d7..f541172 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -335,8 +335,6 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightPWM, }; - public override byte ThumbnailsCount { get; } = 0; - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = null; public override uint ResolutionX @@ -383,10 +381,10 @@ namespace UVtools.Core.FileFormats } } - public override float MaxPrintHeight + public override float MachineZ { - get => OutputSettings.PlatformZSize > 0 ? OutputSettings.PlatformZSize : base.MaxPrintHeight; - set => base.MaxPrintHeight = OutputSettings.PlatformZSize = (float)Math.Round(value, 2); + get => OutputSettings.PlatformZSize > 0 ? OutputSettings.PlatformZSize : base.MachineZ; + set => base.MachineZ = OutputSettings.PlatformZSize = (float)Math.Round(value, 2); } public override bool MirrorDisplay diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs new file mode 100644 index 0000000..2e680a3 --- /dev/null +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -0,0 +1,703 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BinarySerialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + public class CXDLPFile : FileFormat + { + #region Constants + private const byte HEADER_SIZE = 9; // CXSW3DV2 + private const string HEADER_VALUE = "CXSW3DV2"; + + + private const uint SlicerInfoAddress = 4 + HEADER_SIZE + 6 + 290 * 290 * 4 + 116 * 116 * 2 + 6; + #endregion + + #region Sub Classes + #region Header + public sealed class Header + { + /// + /// Gets the size of the header + /// + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint HeaderSize { get; set; } = HEADER_SIZE; + + /// + /// Gets the header name + /// + [FieldOrder(1)] + [FieldLength(HEADER_SIZE)] + [SerializeAs(SerializedType.TerminatedString)] + public string HeaderValue { get; set; } = HEADER_VALUE; + + /// + /// Gets the number of records in the layer table + /// + [FieldOrder(2)] + [FieldEndianness(Endianness.Big)] + public ushort LayerCount { get; set; } + + /// + /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images. + /// + [FieldOrder(3)] + [FieldEndianness(Endianness.Big)] + public ushort ResolutionX { get; set; } + + /// + /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images. + /// + [FieldOrder(4)] + [FieldEndianness(Endianness.Big)] + public ushort ResolutionY { get; set; } + + public void Validate() + { + if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE) + { + throw new FileLoadException("Not a valid CXDLP file!"); + } + } + } + + #endregion + + #region SlicerInfo + // Address: 363337 + public sealed class SlicerInfo + { + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint DisplayWidthDataSize { get; set; } = 20; + + [FieldOrder(1)] + [FieldLength(nameof(DisplayWidthDataSize))] + public byte[] DisplayWidth { get; set; } + + [FieldOrder(2)] + [FieldEndianness(Endianness.Big)] + public uint DisplayHeightDataSize { get; set; } = 20; + + [FieldOrder(3)] + [FieldLength(nameof(DisplayHeightDataSize))] + public byte[] DisplayHeight { get; set; } + + [FieldOrder(4)] + [FieldEndianness(Endianness.Big)] + public uint LayerHeightDataSize { get; set; } = 16; + + [FieldOrder(5)] + [FieldLength(nameof(LayerHeightDataSize))] + public byte[] LayerHeight { get; set; } + + [FieldOrder(6)] + [FieldEndianness(Endianness.Big)] + public ushort ExposureTime { get; set; } + + [FieldOrder(7)] + [FieldEndianness(Endianness.Big)] + public ushort LightOffDelay { get; set; } + + [FieldOrder(8)] + [FieldEndianness(Endianness.Big)] + public ushort BottomExposureTime { get; set; } + + [FieldOrder(9)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLayers { get; set; } + + [FieldOrder(10)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLiftHeight { get; set; } + + [FieldOrder(11)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLiftSpeed { get; set; } + + [FieldOrder(12)] + [FieldEndianness(Endianness.Big)] + public ushort LiftHeight { get; set; } + + [FieldOrder(13)] + [FieldEndianness(Endianness.Big)] + public ushort LiftSpeed { get; set; } + + [FieldOrder(14)] + [FieldEndianness(Endianness.Big)] + public ushort RetractSpeed { get; set; } + + [FieldOrder(15)] + [FieldEndianness(Endianness.Big)] + public ushort BottomLightPWM { get; set; } = 255; + + [FieldOrder(16)] + [FieldEndianness(Endianness.Big)] + public ushort LightPWM { get; set; } = 255; + } + #endregion + + #region Layer Def + + public sealed class PreLayer + { + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint Unknown { get; set; } + + public PreLayer() + { + } + + public PreLayer(uint unknown) + { + Unknown = unknown; + } + } + + public sealed class LayerDef + { + [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Unknown { get; set; } + [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; } + [FieldOrder(2)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; } + [FieldOrder(3)] public PageBreak PageBreak { get; set; } = new(); + + public LayerDef() { } + + public LayerDef(uint unknown, uint lineCount, LayerLine[] lines) + { + Unknown = unknown; + LineCount = lineCount; + Lines = lines; + } + } + + public sealed class LayerLine + { + public const byte CoordinateCount = 5; + [FieldOrder(0)] [FieldCount(CoordinateCount)] public byte[] Coordinates { get; set; } = new byte[CoordinateCount]; + //[FieldOrder(0)] [FieldEndianness(Endianness.Big)] [FieldBitLength(13)] public ushort StartY { get; set; } + //[FieldOrder(1)] [FieldEndianness(Endianness.Big)] [FieldBitLength(13)] public ushort EndY { get; set; } + //[FieldOrder(2)] [FieldEndianness(Endianness.Big)] [FieldBitLength(14)] public ushort StartX { get; set; } + [FieldOrder(1)] public byte Gray { get; set; } + + [Ignore] public ushort StartY => (ushort) ((((Coordinates[0] << 8) + Coordinates[1]) >> 3) & 0x1FFF); // 13 bits + + [Ignore] public ushort EndY => (ushort)((((Coordinates[1] << 16) + (Coordinates[2] << 8) + Coordinates[3]) >> 6) & 0x1FFF); // 13 bits + + [Ignore] public ushort StartX => (ushort)(((Coordinates[3] << 8) + Coordinates[4]) & 0x3FFF); // 14 bits + [Ignore] public ushort Length => (ushort) (EndY - StartY); + + public LayerLine() { } + + public LayerLine(ushort startY, ushort endY, ushort startX, byte gray) + { + Coordinates[0] = (byte) ((startY >> 5) & 0xFF); + Coordinates[1] = (byte) (((startY << 3) + (endY >> 10)) & 0xFF); + Coordinates[2] = (byte) ((endY >> 2) & 0xFF); + Coordinates[3] = (byte)(((endY << 6) + (startX >> 8)) & 0xFF); + Coordinates[4] = (byte) startX; + /*StartY = startY; + EndY = endY; + StartX = startX;*/ + Gray = gray; + } + } + + public sealed class PageBreak + { + [FieldOrder(0)] public byte Line { get; set; } = 0x0D; + [FieldOrder(1)] public byte Break { get; set; } = 0x0A; + } + + #endregion + + #region Footer + public sealed class Footer + { + /// + /// Gets the size of the header + /// + [FieldOrder(0)] + [FieldEndianness(Endianness.Big)] + public uint FooterSize { get; set; } = HEADER_SIZE; + + /// + /// Gets the header name + /// + [FieldOrder(1)] + [FieldLength(HEADER_SIZE)] + [SerializeAs(SerializedType.TerminatedString)] + public string FooterValue { get; set; } = HEADER_VALUE; + + [FieldOrder(2)] + public uint Unknown { get; set; } = 7; + + public void Validate() + { + if (FooterSize != HEADER_SIZE || FooterValue != HEADER_VALUE) + { + throw new FileLoadException("Not a valid CXDLP file!"); + } + } + } + #endregion + + #endregion + + #region Properties + + public Header HeaderSettings { get; protected internal set; } = new(); + public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); + public Footer FooterSettings { get; protected internal set; } = new(); + + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new("cxdlp", "Creality CXDLP"), + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = + { + PrintParameterModifier.BottomLayerCount, + PrintParameterModifier.BottomExposureSeconds, + PrintParameterModifier.ExposureSeconds, + + PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + PrintParameterModifier.RetractSpeed, + PrintParameterModifier.LightOffDelay, + + PrintParameterModifier.BottomLightPWM, + PrintParameterModifier.LightPWM, + }; + + public override Size[] ThumbnailsOriginalSize { get; } = + { + new(116, 116), + new(290, 290), + new(290, 290) + }; + + public override uint ResolutionX + { + get => HeaderSettings.ResolutionX; + set + { + HeaderSettings.ResolutionX = (ushort) value; + RaisePropertyChanged(); + } + } + + public override uint ResolutionY + { + get => HeaderSettings.ResolutionY; + set + { + HeaderSettings.ResolutionY = (ushort) value; + RaisePropertyChanged(); + } + } + + public override float DisplayWidth + { + get => float.Parse(SlicerInfoSettings.DisplayWidth.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayWidthDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayWidthDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayWidth = data; + RaisePropertyChanged(); + } + } + + public override float DisplayHeight + { + get => float.Parse(SlicerInfoSettings.DisplayHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.DisplayHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.DisplayHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.DisplayHeight = data; + RaisePropertyChanged(); + } + } + + public override byte AntiAliasing + { + get => 8; + set {} + } + + public override float LayerHeight + { + get => float.Parse(SlicerInfoSettings.LayerHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b))); + set + { + string str = Layer.RoundHeight(value).ToString(CultureInfo.InvariantCulture); + SlicerInfoSettings.LayerHeightDataSize = (uint)(str.Length * 2); + var data = new byte[SlicerInfoSettings.LayerHeightDataSize]; + for (var i = 0; i < str.Length; i++) + { + data[i * 2] = System.Convert.ToByte(str[i]); + } + + SlicerInfoSettings.LayerHeight = data; + RaisePropertyChanged(); + } + } + + public override uint LayerCount + { + get => base.LayerCount; + set => base.LayerCount = HeaderSettings.LayerCount = (ushort) base.LayerCount; + } + + public override ushort BottomLayerCount + { + get => SlicerInfoSettings.BottomLayers; + set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value; + } + + public override float BottomExposureTime + { + get => SlicerInfoSettings.BottomExposureTime; + set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort) value; + } + + public override float ExposureTime + { + get => SlicerInfoSettings.ExposureTime; + set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort) value; + } + + public override float BottomLiftHeight + { + get => SlicerInfoSettings.BottomLiftHeight; + set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort) value; + } + + public override float LiftHeight + { + get => SlicerInfoSettings.LiftHeight; + set => base.LiftHeight = SlicerInfoSettings.LiftHeight = (ushort)value; + } + + public override float BottomLiftSpeed + { + get => SlicerInfoSettings.BottomLiftSpeed; + set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value; + } + + public override float LiftSpeed + { + get => SlicerInfoSettings.LiftSpeed; + set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value; + } + + public override float RetractSpeed + { + get => SlicerInfoSettings.RetractSpeed; + set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)value; + } + + public override float BottomLightOffDelay => SlicerInfoSettings.LightOffDelay; + + public override float LightOffDelay + { + get => SlicerInfoSettings.LightOffDelay; + set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value; + } + + public override byte BottomLightPWM + { + get => (byte) SlicerInfoSettings.BottomLightPWM; + set => base.BottomLightPWM = (byte) (SlicerInfoSettings.BottomLightPWM = value); + } + + public override byte LightPWM + { + get => (byte)SlicerInfoSettings.LightPWM; + set => base.LightPWM = (byte) (SlicerInfoSettings.LightPWM = value); + } + + public override object[] Configs => new object[] { HeaderSettings, SlicerInfoSettings, FooterSettings }; + + #endregion + + #region Constructors + #endregion + + #region Methods + + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + var pageBreak = new PageBreak(); + + byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + } + // Previews + Parallel.For(0, previews.Length, previewIndex => + { + if (progress.Token.IsCancellationRequested) return; + if (Thumbnails[previewIndex] is null) return; + var span = Thumbnails[previewIndex].GetPixelSpanByte(); + int index = 0; + for (int i = 0; i < span.Length; i += 3) + { + byte b = span[i]; + byte g = span[i + 1]; + byte r = span[i + 2]; + + ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); + + previews[previewIndex][index++] = (byte)(rgb15 >> 8); + previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + } + + if (index != previews[previewIndex].Length) + { + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + } + }); + + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + Helpers.SerializeWriteFileStream(outputFile, previews[i]); + Helpers.SerializeWriteFileStream(outputFile, pageBreak); + } + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var preLayers = new PreLayer[LayerCount]; + var layerDefs = new LayerDef[LayerCount]; + + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + var layer = this[layerIndex]; + preLayers[layerIndex] = new(layer.NonZeroPixelCount); + } + Helpers.SerializeWriteFileStream(outputFile, preLayers); + Helpers.SerializeWriteFileStream(outputFile, pageBreak); + + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + List layerLines = new(); + var layer = this[layerIndex]; + using var mat = layer.LayerMat; + var span = mat.GetPixelSpanByte(); + + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) + { + int y = layer.BoundingRectangle.Y; + int startY = -1; + byte lastColor = 0; + for (; y < layer.BoundingRectangle.Bottom; y++) + { + int pos = mat.GetPixelPos(x, y); + byte color = span[pos]; + + if (lastColor == color && color != 0) continue; + + if (startY >= 0) + { + layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); + } + + startY = color == 0 ? -1 : y; + + lastColor = color; + } + + if (startY >= 0) + { + layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); + } + } + + layerDefs[layerIndex] = new LayerDef(layer.NonZeroPixelCount, (uint)layerLines.Count, layerLines.ToArray()); + + progress.LockAndIncrement(); + }); + + progress.Reset(OperationProgress.StatusWritingFile, LayerCount); + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + Helpers.SerializeWriteFileStream(outputFile, layerDefs[layerIndex]); + progress++; + } + + Helpers.SerializeWriteFileStream(outputFile, FooterSettings); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine("-End-"); + } + + protected override void DecodeInternally(string fileFullPath, OperationProgress progress) + { + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + HeaderSettings = Helpers.Deserialize
(inputFile); + HeaderSettings.Validate(); + + byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; + for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) + { + previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; + inputFile.ReadBytes(previews[i]); + inputFile.Seek(2, SeekOrigin.Current); + } + + Parallel.For(0, previews.Length, previewIndex => + { + var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); + var span = mat.GetPixelSpanByte(); + + int spanIndex = 0; + for (int i = 0; i < previews[previewIndex].Length; i += 2) + { + ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); + byte r = (byte)((rgb15 >> 11) << 3); + byte g = (byte)((rgb15 >> 5) << 2); + byte b = (byte)((rgb15 >> 0) << 3); + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + Thumbnails[previewIndex] = mat; + }); + + + SlicerInfoSettings = Helpers.Deserialize(inputFile); + + LayerManager.Init(HeaderSettings.LayerCount); + progress.ItemCount = LayerCount; + var preLayers = new PreLayer[LayerCount]; + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + preLayers[layerIndex] = Helpers.Deserialize(inputFile); + } + + inputFile.Seek(2, SeekOrigin.Current); + var layerDefs = new LayerDef[LayerCount]; + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerDefs[layerIndex] = Helpers.Deserialize(inputFile); + progress++; + } + + /*using TextWriter file = new StreamWriter("D:\\dump.txt"); + for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + file.WriteLine($"Layer: {layerIndex}"); + for (int lineIndex = 0; lineIndex < layerDefs[layerIndex].LineCount; lineIndex++) + { + file.WriteLine($"\tLine: {lineIndex}"); + var line = layerDefs[layerIndex].Lines[lineIndex]; + file.WriteLine($"\t\tb1: {line.Coordinates[0]}"); + file.WriteLine($"\t\tb2: {line.Coordinates[1]}"); + file.WriteLine($"\t\tb3: {line.Coordinates[2]}"); + file.WriteLine($"\t\tb4: {line.Coordinates[3]}"); + file.WriteLine($"\t\tb5: {line.Coordinates[4]}"); + file.WriteLine($"\t\tstartY: {line.StartY}"); + file.WriteLine($"\t\tendY: {line.EndY}"); + file.WriteLine($"\t\tstartX: {line.StartX}"); + file.WriteLine($"\t\tgray: {line.Gray}"); + } + } + file.Close();*/ + + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using var mat = EmguExtensions.InitMat(Resolution); + foreach (var line in layerDefs[layerIndex].Lines) + { + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), new MCvScalar(line.Gray)); + } + + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + progress.LockAndIncrement(); + }); + + FooterSettings = Helpers.Deserialize