Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2022-03-13 00:04:47 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-03-13 00:04:47 +0300
commit4600e81b08760741dcd0d096f1c0c80c39d15bd9 (patch)
treed9980fbc597daa05b02d2116462140f41db70c40
parentde000f7bc9f9bfb904b6f7af5ab2aa664c35df42 (diff)
v3.0.0v3.0.0
- **(Add) Suggestions:** - A new module that detect bad or parameters out of a defined range and suggest a change on the file, those can be auto applied if configured to do so - **Avaliable suggestions:** - **Bottom layer count:** Bottom layers should be kept to a minimum, usually from 2 to 3, it function is to provide a good adhesion to the first layer on the build plate, using a high count have disadvantages. - **Wait time before cure:** Rest some time before cure the layer is crucial to let the resin settle after the lift sequence and allow some time for the arm settle at the correct Z position as the resin will offer some resistance and push the structure. This lead to better quality with more successful prints, less lamination problems, better first layers with more success of stick to the build plate and less elephant foot effect. - **Wait time after cure:** Rest some time after cure the layer and before the lift sequence can be important to allow the layer to cooldown a bit and detach better from the FEP. - **Layer height:** Using the right layer height is important to get successful prints: Thin layers may cause problems on adhesion, lamination, will print much slower and have no real visual benefits. Thick layers may not fully cure no matter the exposure time you use, causing lamination and other hazards. Read your resin dtasheet to know the limits. Using layer height with too many decimal digits may produce a wrong positioning due stepper step loss and/or Z axis quality. - **Core:** - Convert the project to Nullable aware and "null-safe" - **File Formats:** - (Add) `Volume` property to get the total model volume - (Add) `SanitizeLayers` method to reassign indexes and force attribute parent file - (Improvement) Merge `LayerManager` into `FileFormat` and cleanup: This affects the whole project and external scripts. If using scripts please update them, search for `.LayerManager.` and replace by `.` - (Change) Chitubox encrypted format can now be saved as normal - (Fix) Converted files layers was pointing to the source file and related to it - **Layers:** - (Add) Methods: `ResetParameters`, `CopyParametersTo`, `CopyExposureTo`, `CopyWaitTimesTo` - (Improvement) `IsBottomLayer` property will also return true when the index is inside bottom layer count - **Scripting:** - (Add) Configuration variable: `MinimumVersionToRun` - Sets the minimum version able to run the script - (Improvement) Allow run scripts written in C# 10 with the new namespace; style as well as nullables methods - (Improvement) Convert scripts to use Nullable code - **UI:** - (Add) Fluent Dark theme - (Add) Default Light theme - (Add) Default Dark theme - (Change) Use fontawesome and material design to render the icons instead of static png images - (Change) Some icons - (Change) Move log tab to clipboard tab - (Change) Tooltip overlay default color - (Improvement) Windows position for tool windows, sometimes framework can return negative values affecting positions, now limits to 0 (#387) - (Fix) Center image icon for layer action button - (Fix) Center image icon for save layer image button - **Tools:** - (Add) Layer re-height: Offset mode, change layers position by a defined offset (#423) - (Improvement) Rotate: Unable to use an angle of 0 - (Improvement) Remove layers: Will not recalcualte and reset properties of layers anymore, allowing removing layers on dynamic layer height models and others - (Improvement) Clone layers: Will not recalcualte and reset properties of layers anymore, allowing cloning layers on dynamic layer height models and others - (Fix) Exposure time finder: Very small printers may not print the stock object as it is configured, lead to a unknown error while generating the test. It will now show a better error message and advice a solution (#426) - **Terminal:** - (Add) More default namespaces - (Improvement) Set a MinHeight for the rows to prevent spliter from eat the elements - (Change) Set working space to the MainWindow instead of TerminalWindow - **(Upgrade) .NET from 5.0.14 to 6.0.3** - This brings big performance improvements, better JIT, faster I/O operations and others - Read more: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6 - Due this macOS requirement starts at 10.15 (Catalina) - Read more: https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md - (Add) Native support for MacOS ARM64 architecture (Mac M1 and upcomming Mac's) (#187) - (Exchange) Dependency Newtonsoft Json by System.Text.Json to parse the json documents - (Remove) "Automations - Light-off delay" in favor of new suggestion "wait time before cure" module - (Fix) File - Send to: Winrar or 7zip have a wrong extension on the list (uvt) when should be (uvj) - (Upgrade) AvaloniaUI from 0.10.12 to 0.10.13
-rw-r--r--CHANGELOG.md67
-rw-r--r--CREDITS.md4
-rw-r--r--README.md16
-rw-r--r--UVtools.AvaloniaControls/AdvancedImageBox.axaml4
-rw-r--r--UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs3507
-rw-r--r--UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj5
-rw-r--r--UVtools.Core/About.cs35
-rw-r--r--UVtools.Core/Converters/NullTerminatedConverter.cs21
-rw-r--r--UVtools.Core/Converters/NullTerminatedLengthConverter.cs35
-rw-r--r--UVtools.Core/CoreSettings.cs104
-rw-r--r--UVtools.Core/EmguCV/EmguContour.cs387
-rw-r--r--UVtools.Core/EmguCV/EmguContours.cs387
-rw-r--r--UVtools.Core/Enumerations.cs145
-rw-r--r--UVtools.Core/Extensions/BitExtensions.cs299
-rw-r--r--UVtools.Core/Extensions/ClassExtensions.cs27
-rw-r--r--UVtools.Core/Extensions/CryptExtensions.cs165
-rw-r--r--UVtools.Core/Extensions/DateTimeExtensions.cs73
-rw-r--r--UVtools.Core/Extensions/DrawingExtensions.cs145
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs2333
-rw-r--r--UVtools.Core/Extensions/EnumExtensions.cs37
-rw-r--r--UVtools.Core/Extensions/FileStreamExtensions.cs319
-rw-r--r--UVtools.Core/Extensions/JsonExtensions.cs18
-rw-r--r--UVtools.Core/Extensions/MathExtensions.cs223
-rw-r--r--UVtools.Core/Extensions/NetworkExtensions.cs71
-rw-r--r--UVtools.Core/Extensions/ParallelExtensions.cs53
-rw-r--r--UVtools.Core/Extensions/PathExtensions.cs79
-rw-r--r--UVtools.Core/Extensions/PointExtensions.cs105
-rw-r--r--UVtools.Core/Extensions/RectangleExtensions.cs15
-rw-r--r--UVtools.Core/Extensions/SizeExtensions.cs179
-rw-r--r--UVtools.Core/Extensions/SpanExtensions.cs45
-rw-r--r--UVtools.Core/Extensions/StreamExtensions.cs85
-rw-r--r--UVtools.Core/Extensions/StringExtensions.cs74
-rw-r--r--UVtools.Core/Extensions/TimeExtensions.cs49
-rw-r--r--UVtools.Core/Extensions/TypeExtensions.cs48
-rw-r--r--UVtools.Core/Extensions/UnitExtensions.cs17
-rw-r--r--UVtools.Core/Extensions/XmlExtensions.cs145
-rw-r--r--UVtools.Core/Extensions/ZipArchiveExtensions.cs631
-rw-r--r--UVtools.Core/FileFormats/CTBEncryptedFile.cs2415
-rw-r--r--UVtools.Core/FileFormats/CWSFile.cs1605
-rw-r--r--UVtools.Core/FileFormats/CXDLPFile.cs1605
-rw-r--r--UVtools.Core/FileFormats/CXDLPv1File.cs1093
-rw-r--r--UVtools.Core/FileFormats/ChituboxFile.cs3435
-rw-r--r--UVtools.Core/FileFormats/ChituboxZipFile.cs874
-rw-r--r--UVtools.Core/FileFormats/FDGFile.cs1866
-rw-r--r--UVtools.Core/FileFormats/FileExtension.cs211
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs7850
-rw-r--r--UVtools.Core/FileFormats/FlashForgeSVGXFile.cs1102
-rw-r--r--UVtools.Core/FileFormats/GR1File.cs815
-rw-r--r--UVtools.Core/FileFormats/GenericZIPFile.cs433
-rw-r--r--UVtools.Core/FileFormats/ImageFile.cs178
-rw-r--r--UVtools.Core/FileFormats/LGSFile.cs929
-rw-r--r--UVtools.Core/FileFormats/MDLPFile.cs743
-rw-r--r--UVtools.Core/FileFormats/OSLAFile.cs1149
-rw-r--r--UVtools.Core/FileFormats/PHZFile.cs1883
-rw-r--r--UVtools.Core/FileFormats/PhotonSFile.cs787
-rw-r--r--UVtools.Core/FileFormats/PhotonWorkshopFile.cs2951
-rw-r--r--UVtools.Core/FileFormats/SL1File.cs1298
-rw-r--r--UVtools.Core/FileFormats/UVJFile.cs889
-rw-r--r--UVtools.Core/FileFormats/VDAFile.cs588
-rw-r--r--UVtools.Core/FileFormats/VDTFile.cs1238
-rw-r--r--UVtools.Core/FileFormats/ZCodeFile.cs943
-rw-r--r--UVtools.Core/FileFormats/ZCodexFile.cs966
-rw-r--r--UVtools.Core/GCode/GCodeBuilder.cs1753
-rw-r--r--UVtools.Core/GCode/GCodeCommand.cs185
-rw-r--r--UVtools.Core/GCode/GCodeLayer.cs489
-rw-r--r--UVtools.Core/Helpers.cs175
-rw-r--r--UVtools.Core/Layers/Issue.cs131
-rw-r--r--UVtools.Core/Layers/IssueOfContours.cs65
-rw-r--r--UVtools.Core/Layers/IssueOfPoints.cs51
-rw-r--r--UVtools.Core/Layers/Layer.cs2143
-rw-r--r--UVtools.Core/Layers/LayerIssue.cs437
-rw-r--r--UVtools.Core/Layers/LayerIssueConfiguration.cs460
-rw-r--r--UVtools.Core/Layers/LayerManager.cs1202
-rw-r--r--UVtools.Core/Layers/MainIssue.cs273
-rw-r--r--UVtools.Core/Managers/ClipboardManager.cs639
-rw-r--r--UVtools.Core/Managers/IssueManager.cs1448
-rw-r--r--UVtools.Core/Managers/KernelCacheManager.cs65
-rw-r--r--UVtools.Core/Managers/MatCacheManager.cs517
-rw-r--r--UVtools.Core/Managers/MaterialManager.cs419
-rw-r--r--UVtools.Core/Managers/OperationSessionManager.cs207
-rw-r--r--UVtools.Core/Managers/SuggestionManager.cs168
-rw-r--r--UVtools.Core/MeshFormats/AMFMeshFile.cs209
-rw-r--r--UVtools.Core/MeshFormats/Consortium3MFMeshFile.cs211
-rw-r--r--UVtools.Core/MeshFormats/MeshFile.cs245
-rw-r--r--UVtools.Core/MeshFormats/OBJMeshFile.cs139
-rw-r--r--UVtools.Core/MeshFormats/OFFMeshFile.cs133
-rw-r--r--UVtools.Core/MeshFormats/PLYMeshFile.cs217
-rw-r--r--UVtools.Core/MeshFormats/STLMeshFile.cs143
-rw-r--r--UVtools.Core/MeshFormats/WRLMeshFile.cs135
-rw-r--r--UVtools.Core/Network/MappedDevice.cs193
-rw-r--r--UVtools.Core/Network/RemotePrinter.cs340
-rw-r--r--UVtools.Core/Network/RemotePrinterRequest.cs255
-rw-r--r--UVtools.Core/Objects/BindableBase.cs133
-rw-r--r--UVtools.Core/Objects/ExposureItem.cs169
-rw-r--r--UVtools.Core/Objects/KernelConfiguration.cs343
-rw-r--r--UVtools.Core/Objects/MappedProcess.cs248
-rw-r--r--UVtools.Core/Objects/Material.cs431
-rw-r--r--UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs85
-rw-r--r--UVtools.Core/Objects/RangeObservableCollection.cs1170
-rw-r--r--UVtools.Core/Objects/StaticObjects.cs26
-rw-r--r--UVtools.Core/Objects/ValueDescription.cs103
-rw-r--r--UVtools.Core/Operations/Operation.cs1044
-rw-r--r--UVtools.Core/Operations/OperationBlur.cs285
-rw-r--r--UVtools.Core/Operations/OperationCalculator.cs636
-rw-r--r--UVtools.Core/Operations/OperationCalibrateElephantFoot.cs1197
-rw-r--r--UVtools.Core/Operations/OperationCalibrateExposureFinder.cs3563
-rw-r--r--UVtools.Core/Operations/OperationCalibrateExternalTests.cs58
-rw-r--r--UVtools.Core/Operations/OperationCalibrateGrayscale.cs867
-rw-r--r--UVtools.Core/Operations/OperationCalibrateLiftHeight.cs685
-rw-r--r--UVtools.Core/Operations/OperationCalibrateStressTower.cs687
-rw-r--r--UVtools.Core/Operations/OperationCalibrateTolerance.cs1189
-rw-r--r--UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs1269
-rw-r--r--UVtools.Core/Operations/OperationChangeResolution.cs396
-rw-r--r--UVtools.Core/Operations/OperationDoubleExposure.cs620
-rw-r--r--UVtools.Core/Operations/OperationDynamicLayerHeight.cs1260
-rw-r--r--UVtools.Core/Operations/OperationDynamicLifts.cs686
-rw-r--r--UVtools.Core/Operations/OperationEditParameters.cs289
-rw-r--r--UVtools.Core/Operations/OperationFadeExposureTime.cs280
-rw-r--r--UVtools.Core/Operations/OperationFlip.cs204
-rw-r--r--UVtools.Core/Operations/OperationIPrintedThisFile.cs247
-rw-r--r--UVtools.Core/Operations/OperationInfill.cs565
-rw-r--r--UVtools.Core/Operations/OperationLayerArithmetic.cs528
-rw-r--r--UVtools.Core/Operations/OperationLayerClone.cs211
-rw-r--r--UVtools.Core/Operations/OperationLayerExportGif.cs466
-rw-r--r--UVtools.Core/Operations/OperationLayerExportHeatMap.cs313
-rw-r--r--UVtools.Core/Operations/OperationLayerExportImage.cs512
-rw-r--r--UVtools.Core/Operations/OperationLayerExportMesh.cs915
-rw-r--r--UVtools.Core/Operations/OperationLayerExportSkeleton.cs191
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs816
-rw-r--r--UVtools.Core/Operations/OperationLayerReHeight.cs406
-rw-r--r--UVtools.Core/Operations/OperationLayerRemove.cs244
-rw-r--r--UVtools.Core/Operations/OperationLightBleedCompensation.cs379
-rw-r--r--UVtools.Core/Operations/OperationMask.cs130
-rw-r--r--UVtools.Core/Operations/OperationMorph.cs348
-rw-r--r--UVtools.Core/Operations/OperationMove.cs473
-rw-r--r--UVtools.Core/Operations/OperationPattern.cs528
-rw-r--r--UVtools.Core/Operations/OperationPixelArithmetic.cs2146
-rw-r--r--UVtools.Core/Operations/OperationPixelDimming.cs1112
-rw-r--r--UVtools.Core/Operations/OperationProgress.cs372
-rw-r--r--UVtools.Core/Operations/OperationRaftRelief.cs420
-rw-r--r--UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs258
-rw-r--r--UVtools.Core/Operations/OperationRedrawModel.cs362
-rw-r--r--UVtools.Core/Operations/OperationRepairLayers.cs726
-rw-r--r--UVtools.Core/Operations/OperationResize.cs306
-rw-r--r--UVtools.Core/Operations/OperationRotate.cs158
-rw-r--r--UVtools.Core/Operations/OperationScripting.cs215
-rw-r--r--UVtools.Core/Operations/OperationSolidify.cs181
-rw-r--r--UVtools.Core/Operations/OperationThreshold.cs202
-rw-r--r--UVtools.Core/Operations/OperationTimelapse.cs730
-rw-r--r--UVtools.Core/PixelEditor/PixelDrainHole.cs33
-rw-r--r--UVtools.Core/PixelEditor/PixelDrawing.cs159
-rw-r--r--UVtools.Core/PixelEditor/PixelEraser.cs29
-rw-r--r--UVtools.Core/PixelEditor/PixelHistory.cs79
-rw-r--r--UVtools.Core/PixelEditor/PixelOperation.cs179
-rw-r--r--UVtools.Core/PixelEditor/PixelSupport.cs63
-rw-r--r--UVtools.Core/PixelEditor/PixelText.cs160
-rw-r--r--UVtools.Core/Scripting/ScriptBaseInput.cs47
-rw-r--r--UVtools.Core/Scripting/ScriptCheckBoxInput.cs9
-rw-r--r--UVtools.Core/Scripting/ScriptConfiguration.cs55
-rw-r--r--UVtools.Core/Scripting/ScriptFileDialogInput.cs49
-rw-r--r--UVtools.Core/Scripting/ScriptGlobals.cs41
-rw-r--r--UVtools.Core/Scripting/ScriptNumericalInput.cs41
-rw-r--r--UVtools.Core/Scripting/ScriptOpenFileDialogInput.cs25
-rw-r--r--UVtools.Core/Scripting/ScriptOpenFolderDialogInput.cs17
-rw-r--r--UVtools.Core/Scripting/ScriptParser.cs81
-rw-r--r--UVtools.Core/Scripting/ScriptSaveFileDialogInput.cs17
-rw-r--r--UVtools.Core/Scripting/ScriptTextBoxInput.cs17
-rw-r--r--UVtools.Core/Scripting/ScriptToggleSwitchInput.cs25
-rw-r--r--UVtools.Core/Slicer/LinAlgUtils.cs149
-rw-r--r--UVtools.Core/Slicer/RangeUtils.cs115
-rw-r--r--UVtools.Core/Slicer/Slice.cs69
-rw-r--r--UVtools.Core/Slicer/SliceLine.cs93
-rw-r--r--UVtools.Core/Slicer/Slicer.cs303
-rw-r--r--UVtools.Core/Statistics.cs61
-rw-r--r--UVtools.Core/Suggestions/Suggestion.cs280
-rw-r--r--UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs157
-rw-r--r--UVtools.Core/Suggestions/SuggestionLayerHeight.cs121
-rw-r--r--UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs335
-rw-r--r--UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs393
-rw-r--r--UVtools.Core/SystemOS/SystemAware.cs393
-rw-r--r--UVtools.Core/SystemOS/Windows/USB.cs217
-rw-r--r--UVtools.Core/UVtools.Core.csproj9
-rw-r--r--UVtools.Core/UVtools.Core.csproj.DotSettings2
-rw-r--r--UVtools.Core/Voxel/Voxelizer.cs379
-rw-r--r--UVtools.InstallerMM/UVtools.InstallerMM.wxs52
-rw-r--r--UVtools.Platforms/osx-arm64/Info.plist36
-rw-r--r--UVtools.Platforms/osx-arm64/libcvextern.dylibbin0 -> 42853870 bytes
-rw-r--r--UVtools.Platforms/osx-x64/Info.plist18
-rw-r--r--UVtools.ScriptSample/README.md2
-rw-r--r--UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs165
-rw-r--r--UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs83
-rw-r--r--UVtools.ScriptSample/ScriptCloneSettings.cs668
-rw-r--r--UVtools.ScriptSample/ScriptCustomGCode.cs225
-rw-r--r--UVtools.ScriptSample/ScriptDebandingZSample.cs258
-rw-r--r--UVtools.ScriptSample/ScriptInsetSample.cs193
-rw-r--r--UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs167
-rw-r--r--UVtools.ScriptSample/ScriptSetLiftHeightSample.cs111
-rw-r--r--UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs240
-rw-r--r--UVtools.ScriptSample/ScriptTester.cs131
-rw-r--r--UVtools.ScriptSample/ScriptTimelapseSample.cs304
-rw-r--r--UVtools.ScriptSample/ScriptVATClean.cs212
-rw-r--r--UVtools.ScriptSample/UVtools.ScriptSample.csproj3
-rw-r--r--UVtools.WPF/App.axaml9
-rw-r--r--UVtools.WPF/App.axaml.cs482
-rw-r--r--UVtools.WPF/AppSettings.cs51
-rw-r--r--UVtools.WPF/Assets/Icons/CNCMachine-16x16.pngbin261 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/accept-16x16.pngbin382 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/angle-double-up-16x16.pngbin126 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/arrow-down-16x16.pngbin190 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/arrow-down-double-16x16.pngbin179 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/arrow-end-16x16.pngbin154 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/arrow-top-16x16.pngbin206 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/arrow-up-16x16.pngbin193 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/back-16x16.pngbin138 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/balance-scale-16x16.pngbin224 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/blur-16x16.pngbin202 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/book-32x32.pngbin170 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/bookmark-16x16.pngbin114 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/bowling-ball-16x16.pngbin190 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/bug-16x16.pngbin201 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/burn-16x16.pngbin212 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/button-info-16x16.pngbin197 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/calculator-16x16.pngbin119 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/camera-16x16.pngbin145 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/cancel-24x24.pngbin513 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/cancel-32x32.pngbin588 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/chart-pie-16x16.pngbin226 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/checkbox-marked-16x16.pngbin232 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/checkbox-unmarked-16x16.pngbin121 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/chess-rook-16x16.pngbin142 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/chessboard-16x16.pngbin83 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/clipboard-16x16.pngbin207 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/clipboard-32x32.pngbin313 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/code-16x16.pngbin199 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/code-32x32.pngbin380 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/code-branch-16x16.pngbin187 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/code-branch-24x24.pngbin282 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/cog-16x16.pngbin190 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/compress-alt-16x16.pngbin142 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/convert-16x16.pngbin629 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/cookie-16x16.pngbin183 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/copy-16x16.pngbin114 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/crop-16x16.pngbin104 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/crosshairs-16x16.pngbin307 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/cubes-16x16.pngbin268 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/cursor-16x16.pngbin507 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/data-list-16x16.pngbin179 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/delete-16x16.pngbin139 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/donate-16x16.pngbin212 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/dot-circle-16x16.pngbin195 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/dynamic-layers-16x16.pngbin113 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/elephant-foot-16x16.pngbin335 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/equals-16x16.pngbin87 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/eraser-24x24.pngbin219 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/exit-16x16.pngbin174 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/expand-16x16.pngbin98 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/expand-alt-16x16.pngbin129 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/extract-object-16x16.pngbin490 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/eye-16x16.pngbin402 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/eye-24x24.pngbin402 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/eye-slash-16x16.pngbin250 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/facebook-16x16.pngbin127 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/file-close-16x16.pngbin247 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/file-code-16x16.pngbin178 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/file-export-16x16.pngbin161 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/file-gif-16x16.pngbin266 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/file-image-16x16.pngbin234 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/file-import-16x16.pngbin136 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/file-refresh-16x16.pngbin196 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/filter-filled-16x16.pngbin126 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/flask-16x16.pngbin162 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/flip-16x16.pngbin232 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/folder-16x16.pngbin97 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/folder-open-16x16.pngbin156 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/font-24x24.pngbin263 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/geometry-16x16.pngbin259 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/heart-16x16.pngbin189 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/history-16x16.pngbin233 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/info-circle-16x16.pngbin192 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/info-circle-32x32.pngbin319 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/internet-explorer-16x16.pngbin276 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/island-16x16.pngbin647 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/ladder-16x16.pngbin241 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/layers-16x16.pngbin358 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/layers-alt-16x16.pngbin194 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/layers-alt-32x32.pngbin268 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/level-up-alt-16x16.pngbin128 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/lightbulb-16x16.pngbin227 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/lightbulb-solid-16x16.pngbin199 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/list-16x16.pngbin90 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/lock-16x16.pngbin281 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/log-16x16.pngbin284 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/long-arrow-alt-up-16x16.pngbin103 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/map-marker-16x16.pngbin193 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/mask-16x16.pngbin171 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/microchip-16x16.pngbin116 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/minus-16x16.pngbin89 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/move-16x16.pngbin155 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/network-wired-16x16.pngbin133 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/next-16x16.pngbin140 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/object-group-16x16.pngbin139 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/ok-24x24.pngbin350 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/open-16x16.pngbin248 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/pattern-16x16.pngbin268 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/pencil-alt-16x16.pngbin177 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/pencil-alt-24x24.pngbin215 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/photo-16x16.pngbin221 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/photo-info-16x16.pngbin196 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/pixel-16x16.pngbin454 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/pixel-32x32.pngbin897 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/plus-16x16.pngbin114 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/pointer-16x16.pngbin179 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/question-16x16.pngbin195 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/redo-16x16.pngbin231 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/refresh-16x16.pngbin245 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/resize-16x16.pngbin229 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/ring-24x24.pngbin285 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/save-16x16.pngbin247 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/save-as-16x16.pngbin244 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/search-16x16.pngbin344 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/settings-16x16.pngbin290 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/share-square-16x16.pngbin951 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/shield-virus-32x32.pngbin444 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/sort-alpha-up-16x16.pngbin174 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/square-root-16x16.pngbin223 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/square-solid-16x16.pngbin99 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/stroopwafel-16x16.pngbin249 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/sun-16x16.pngbin198 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/sync-16x16.pngbin245 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/terminal-16x16.pngbin123 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/th-16x16.pngbin99 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/toolbox-16x16.pngbin116 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/trash-16x16.pngbin105 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/undo-16x16.pngbin231 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/undo-alt-16x16.pngbin259 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/usb-16x16.pngbin164 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/vector-square-16x16.pngbin112 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/warning-16x16.pngbin699 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/warning-32x32.pngbin1338 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/wikipedia-16x16.pngbin233 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/wrench-16x16.pngbin237 -> 0 bytes
-rw-r--r--UVtools.WPF/Assets/Styles/Styles.xaml21
-rw-r--r--UVtools.WPF/Assets/Styles/StylesDark.xaml43
-rw-r--r--UVtools.WPF/Assets/Styles/StylesLight.xaml39
-rw-r--r--UVtools.WPF/ConsoleArguments.cs526
-rw-r--r--UVtools.WPF/Controls/ButtonWithIcon.cs123
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs107
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml81
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs199
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs35
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs107
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs95
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs47
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs175
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml6
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs167
-rw-r--r--UVtools.WPF/Controls/DummyControl.axaml.cs21
-rw-r--r--UVtools.WPF/Controls/Helpers.cs209
-rw-r--r--UVtools.WPF/Controls/KernelControl.axaml.cs73
-rw-r--r--UVtools.WPF/Controls/StaticControls.cs15
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml91
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml.cs22
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml8
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml.cs23
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml61
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml.cs22
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml157
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml.cs22
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml232
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml.cs22
-rw-r--r--UVtools.WPF/Controls/ToggleButtonWithIcon.cs109
-rw-r--r--UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs111
-rw-r--r--UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs65
-rw-r--r--UVtools.WPF/Controls/Tools/ToolControl.axaml.cs171
-rw-r--r--UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs85
-rw-r--r--UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs51
-rw-r--r--UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs343
-rw-r--r--UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs65
-rw-r--r--UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolIPrintedThisFileControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs57
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs77
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs59
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml10
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs49
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs45
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs73
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml8
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs49
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml54
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs301
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml56
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs50
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml17
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs82
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLightBleedCompensationControl.axaml.cs27
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs243
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs77
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPatternControl.axaml27
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs75
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs59
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs51
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs37
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs93
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs125
-rw-r--r--UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml21
-rw-r--r--UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs1094
-rw-r--r--UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs27
-rw-r--r--UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs135
-rw-r--r--UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml.cs29
-rw-r--r--UVtools.WPF/Controls/UserControlEx.cs103
-rw-r--r--UVtools.WPF/Controls/WindowEx.cs163
-rw-r--r--UVtools.WPF/Converters/EnumToCollectionConverter.cs25
-rw-r--r--UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs27
-rw-r--r--UVtools.WPF/ErrorLog.cs37
-rw-r--r--UVtools.WPF/Extensions/BitmapExtension.cs301
-rw-r--r--UVtools.WPF/Extensions/DrawingExtensions.cs37
-rw-r--r--UVtools.WPF/Extensions/PrimitivesExtensions.cs21
-rw-r--r--UVtools.WPF/Extensions/WindowExtensions.cs138
-rw-r--r--UVtools.WPF/LayerCache.cs151
-rw-r--r--UVtools.WPF/MainWindow.Clipboard.cs135
-rw-r--r--UVtools.WPF/MainWindow.GCode.cs97
-rw-r--r--UVtools.WPF/MainWindow.Information.cs541
-rw-r--r--UVtools.WPF/MainWindow.Issues.cs1076
-rw-r--r--UVtools.WPF/MainWindow.LayerPreview.cs3451
-rw-r--r--UVtools.WPF/MainWindow.Log.cs55
-rw-r--r--UVtools.WPF/MainWindow.PixelEditor.cs870
-rw-r--r--UVtools.WPF/MainWindow.Progress.cs123
-rw-r--r--UVtools.WPF/MainWindow.Suggestions.cs150
-rw-r--r--UVtools.WPF/MainWindow.axaml1401
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs3707
-rw-r--r--UVtools.WPF/Program.cs145
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs371
-rw-r--r--UVtools.WPF/Structures/BenchmarkTest.cs41
-rw-r--r--UVtools.WPF/Structures/Color.cs187
-rw-r--r--UVtools.WPF/Structures/LogItem.cs93
-rw-r--r--UVtools.WPF/Structures/OperationProfiles.cs446
-rw-r--r--UVtools.WPF/Structures/PSProfileFolder.cs209
-rw-r--r--UVtools.WPF/Structures/PixelPicker.cs97
-rw-r--r--UVtools.WPF/Structures/RecentFiles.cs279
-rw-r--r--UVtools.WPF/Structures/SlicerProperty.cs55
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj23
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj.DotSettings2
-rw-r--r--UVtools.WPF/UserSettings.cs3012
-rw-r--r--UVtools.WPF/Windows/AboutWindow.axaml195
-rw-r--r--UVtools.WPF/Windows/AboutWindow.axaml.cs231
-rw-r--r--UVtools.WPF/Windows/BenchmarkWindow.axaml2
-rw-r--r--UVtools.WPF/Windows/BenchmarkWindow.axaml.cs697
-rw-r--r--UVtools.WPF/Windows/ColorPickerWindow.axaml28
-rw-r--r--UVtools.WPF/Windows/ColorPickerWindow.axaml.cs49
-rw-r--r--UVtools.WPF/Windows/MaterialManagerWindow.axaml48
-rw-r--r--UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs131
-rw-r--r--UVtools.WPF/Windows/MissingInformationWindow.axaml24
-rw-r--r--UVtools.WPF/Windows/MissingInformationWindow.axaml.cs123
-rw-r--r--UVtools.WPF/Windows/ProgressWindow.axaml.cs153
-rw-r--r--UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml149
-rw-r--r--UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs149
-rw-r--r--UVtools.WPF/Windows/SettingsWindow.axaml331
-rw-r--r--UVtools.WPF/Windows/SettingsWindow.axaml.cs437
-rw-r--r--UVtools.WPF/Windows/ShortcutsWindow.axaml.cs23
-rw-r--r--UVtools.WPF/Windows/SuggestionSettingsWindow.axaml137
-rw-r--r--UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs204
-rw-r--r--UVtools.WPF/Windows/TerminalWindow.axaml9
-rw-r--r--UVtools.WPF/Windows/TerminalWindow.axaml.cs290
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml199
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml.cs1340
-rw-r--r--UVtools.WPF/Windows/VersionSelectorWindow.axaml22
-rw-r--r--UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs101
-rw-r--r--build/CreateRelease.WPF.ps1170
481 files changed, 65949 insertions, 64849 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66823e9..29f0e14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,67 @@
# Changelog
+## 12/03/2022 - v3.0.0
+
+- **(Add) Suggestions:**
+ - A new module that detect bad or parameters out of a defined range and suggest a change on the file, those can be auto applied if configured to do so
+ - **Avaliable suggestions:**
+ - **Bottom layer count:** Bottom layers should be kept to a minimum, usually from 2 to 3, it function is to provide a good adhesion to the first layer on the build plate, using a high count have disadvantages.
+ - **Wait time before cure:** Rest some time before cure the layer is crucial to let the resin settle after the lift sequence and allow some time for the arm settle at the correct Z position as the resin will offer some resistance and push the structure.
+ This lead to better quality with more successful prints, less lamination problems, better first layers with more success of stick to the build plate and less elephant foot effect.
+ - **Wait time after cure:** Rest some time after cure the layer and before the lift sequence can be important to allow the layer to cooldown a bit and detach better from the FEP.
+ - **Layer height:** Using the right layer height is important to get successful prints:
+ Thin layers may cause problems on adhesion, lamination, will print much slower and have no real visual benefits.
+ Thick layers may not fully cure no matter the exposure time you use, causing lamination and other hazards. Read your resin dtasheet to know the limits.
+ Using layer height with too many decimal digits may produce a wrong positioning due stepper step loss and/or Z axis quality.
+- **Core:**
+ - Convert the project to Nullable aware and "null-safe"
+- **File Formats:**
+ - (Add) `Volume` property to get the total model volume
+ - (Add) `SanitizeLayers` method to reassign indexes and force attribute parent file
+ - (Improvement) Merge `LayerManager` into `FileFormat` and cleanup: This affects the whole project and external scripts.
+ If using scripts please update them, search for `.LayerManager.` and replace by `.`
+ - (Change) Chitubox encrypted format can now be saved as normal
+ - (Fix) Converted files layers was pointing to the source file and related to it
+- **Layers:**
+ - (Add) Methods: `ResetParameters`, `CopyParametersTo`, `CopyExposureTo`, `CopyWaitTimesTo`
+ - (Improvement) `IsBottomLayer` property will also return true when the index is inside bottom layer count
+- **Scripting:**
+ - (Add) Configuration variable: `MinimumVersionToRun` - Sets the minimum version able to run the script
+ - (Improvement) Allow run scripts written in C# 10 with the new namespace; style as well as nullables methods
+ - (Improvement) Convert scripts to use Nullable code
+- **UI:**
+ - (Add) Fluent Dark theme
+ - (Add) Default Light theme
+ - (Add) Default Dark theme
+ - (Change) Use fontawesome and material design to render the icons instead of static png images
+ - (Change) Some icons
+ - (Change) Move log tab to clipboard tab
+ - (Change) Tooltip overlay default color
+ - (Improvement) Windows position for tool windows, sometimes framework can return negative values affecting positions, now limits to 0 (#387)
+ - (Fix) Center image icon for layer action button
+ - (Fix) Center image icon for save layer image button
+- **Tools:**
+ - (Add) Layer re-height: Offset mode, change layers position by a defined offset (#423)
+ - (Improvement) Rotate: Unable to use an angle of 0
+ - (Improvement) Remove layers: Will not recalcualte and reset properties of layers anymore, allowing removing layers on dynamic layer height models and others
+ - (Improvement) Clone layers: Will not recalcualte and reset properties of layers anymore, allowing cloning layers on dynamic layer height models and others
+ - (Fix) Exposure time finder: Very small printers may not print the stock object as it is configured, lead to a unknown error while generating the test. It will now show a better error message and advice a solution (#426)
+- **Terminal:**
+ - (Add) More default namespaces
+ - (Improvement) Set a MinHeight for the rows to prevent spliter from eat the elements
+ - (Change) Set working space to the MainWindow instead of TerminalWindow
+- **(Upgrade) .NET from 5.0.14 to 6.0.3**
+ - This brings big performance improvements, better JIT, faster I/O operations and others
+ - Read more: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6
+ - Due this macOS requirement starts at 10.15 (Catalina)
+ - Read more: https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md
+- (Add) Native support for MacOS ARM64 architecture (Mac M1 and upcomming Mac's) (#187)
+- (Exchange) Dependency Newtonsoft Json by System.Text.Json to parse the json documents
+- (Remove) Dependency: MoreLinq
+- (Remove) "Automations - Light-off delay" in favor of new suggestion "wait time before cure" module
+- (Fix) File - Send to: Winrar or 7zip have a wrong extension on the list (uvt) when should be (uvj)
+- (Upgrade) AvaloniaUI from 0.10.12 to 0.10.13
+
## 21/02/2022 - v2.29.0
- **File formats:**
@@ -9,7 +71,7 @@
- (Add) SL1: Keyword `TransitionLayerCount_xxx` - Sets the number of transition layers
- (Improvement) CTB, PHZ, FDG: Implement the `ModifiedTimestampMinutes` field, it was the MysteriousId before as an unknown field
- (Fix) CWS: Open in partial mode will cause an exception and prevent file from load
-- **CCode:**
+- **GCode:**
- (Add) Allow inverse lifts to work as an retract
- (Fix) Parsing of WaitTimeAfterLift was incorrect when lacking a lift sequence
- (Fix) Layers lacking an exposure time was defaulting to global time, now defaults to 0
@@ -19,7 +81,7 @@
- (Add) Information: Raise Layer count equivalence
- (Add) Information: Additional lifts to be generated
- (Add) Option: Ensure the last layer - If enabled, it will generate an obligatory layer to cover the last layer
- - (Improvement) Optimize lift for virtual layer mode, allowing set a slow and fast lift / retract by using another virtual layyer to emulate a lift
+ - (Improvement) Optimize lift for virtual layer mode, allowing set a slow and fast lift / retract by using another virtual layer to emulate a lift
- (Improvement) Allow to define slow and fast speed for virtual layer mode even if TSMC isn't supported
- (Add) Fade exposure time: Setting 'Disable firmware transition layers' - Attempt to disable firmware strict transition layers in favor of this tool
- (Add) Calibration tests: Attempt to auto disable the firmware transifiton layers
@@ -769,6 +831,7 @@
- (Fix) macOS: Include libusb-1.0.0.dylib
- Note: `brew install libusb` still required
- **UI:**
+ - (Add) Shorcuts: Arrow up and down to navigate layers while layer image is on focus
- (Fix) Refresh gcode does not update text on UI for ZIP, CWS, ZCODEX files
- (Fix) Operations: Import a .uvtop file by drag and drop into the UI would not load the layer range
- (Change) When convert a file, the result dialog will have Yes, No and Cancel actions, where No will open the converted file on current window, while Cancel will not perform any action (The old No behaviour)
diff --git a/CREDITS.md b/CREDITS.md
index bcdabe4..7f9976e 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -68,4 +68,6 @@
- Riccardo Kocmann
- Joshua Pitts
- Tim Anderson
-- Sakari Toivonen \ No newline at end of file
+- Sakari Toivonen
+- Ed Wagaman
+- Marcin Chomiczuk \ No newline at end of file
diff --git a/README.md b/README.md
index 119719b..3c94132 100644
--- a/README.md
+++ b/README.md
@@ -196,7 +196,7 @@ The UVtools executable allow to set some arguments to do special functions:
- Type: appwiz.cpl (and press Enter key)
- Click on: Turn Windows features on or off
- Check the "Media Extensions" and click Ok
-<!-- 1. [.NET 5.0](https://dotnet.microsoft.com/download/dotnet/5.0) installed (Comes pre-installed on Windows 10 with last updates)!-->
+<!-- 1. [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0) installed (Comes pre-installed on Windows 10 with last updates)!-->
1. 4GB RAM or higher
1. 1920 x 1080 @ 100% scale as minimum resolution
@@ -215,7 +215,7 @@ sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y apt-transport-https
-dotnet-runtime-5.0
+dotnet-runtime-6.0
!-->
```bash
@@ -348,7 +348,7 @@ this process is very slow but only need to run once. Open a terminal on any fold
```bash
sudo yum update -y
sudo yum groupinstall -y "Development Tools" "Development Libraries"
-sudo yum install -y cmake gcc-c++ dotnet-sdk-5.0 gtk3-devel gstreamer1-devel ffmpeg ffmpeg-devel libdc1394 libv4l-devel cmake-gui ocl-icd-devel freeglut libgeotiff libusb
+sudo yum install -y cmake gcc-c++ dotnet-sdk-6.0 gtk3-devel gstreamer1-devel ffmpeg ffmpeg-devel libdc1394 libv4l-devel cmake-gui ocl-icd-devel freeglut libgeotiff libusb
git clone https://github.com/emgucv/emgucv emgucv
cd emgucv
git submodule update --init --recursive
@@ -370,11 +370,11 @@ anyone with same system version can make use of it without the need of the compi
## Mac
-1. macOS 10.13 High Sierra
+1. macOS 10.15 Catalina or higher
1. 4GB RAM or higher
<!---
-* Donwload and install: https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.101-macos-x64-installer
+* Donwload and install: https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-6.0.101-macos-x64-installer
brew install libjpeg libpng libgeotiff libdc1394 ffmpeg openexr tbb
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
@@ -420,7 +420,7 @@ There are multiple ways to open your file:
# Library -> Developers
Are you a developer?
-This project include a .NET 5.0 library (UVtools.Core) that can be referenced in your application to make use of my work.
+This project include a .NET 6.0 library (UVtools.Core) that can be referenced in your application to make use of my work.
Easy to use calls that allow you work with the formats. For more information navigate main code to see some calls.
Nuget package: https://www.nuget.org/packages/UVtools.Core
@@ -437,8 +437,8 @@ dotnet add package UVtools.Core
The fastest way to compile the project is by run the `build/CompileWindows.bat`, however if you wish to develop with visual studio follow the following steps:
1. Install Visual Studio and include .NET development support
-1. Install the .NET 5.0 SDK if not included on previous instalation
- - https://dotnet.microsoft.com/download/dotnet/5.0
+1. Install the .NET 6.0 SDK if not included on previous instalation
+ - https://dotnet.microsoft.com/download/dotnet/6.0
1. Install the Avalonia for Visual Sutdio:
- https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio
1. Install the Wix Toolset: (Required only for MSI build, **optional**)
diff --git a/UVtools.AvaloniaControls/AdvancedImageBox.axaml b/UVtools.AvaloniaControls/AdvancedImageBox.axaml
index 6e08627..ad3c8ea 100644
--- a/UVtools.AvaloniaControls/AdvancedImageBox.axaml
+++ b/UVtools.AvaloniaControls/AdvancedImageBox.axaml
@@ -30,10 +30,12 @@
Maximum="0"
Visibility="Auto"/>
+ <!--
<Border
Grid.Row="1"
Grid.Column="1"
- Background="WhiteSmoke"/>
+ Background="{DynamicResource ThemeBackgroundColor}"/>
+ -->
</Grid>
</UserControl>
diff --git a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs
index 6ff899d..f8c88c2 100644
--- a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs
+++ b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs
@@ -27,2178 +27,2185 @@ using Pen = Avalonia.Media.Pen;
using Point = Avalonia.Point;
using Size = Avalonia.Size;
-namespace UVtools.AvaloniaControls
+namespace UVtools.AvaloniaControls;
+
+public class AdvancedImageBox : UserControl
{
- public class AdvancedImageBox : UserControl
+ #region Bindable Base
+ /// <summary>
+ /// Multicast event for property change notifications.
+ /// </summary>
+ private PropertyChangedEventHandler? _propertyChanged;
+
+ public new event PropertyChangedEventHandler PropertyChanged
{
- #region Bindable Base
- /// <summary>
- /// Multicast event for property change notifications.
- /// </summary>
- private PropertyChangedEventHandler _propertyChanged;
+ add => _propertyChanged += value;
+ remove => _propertyChanged -= value;
+ }
+ protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
+ {
+ if (EqualityComparer<T>.Default.Equals(field, value)) return false;
+ field = value;
+ RaisePropertyChanged(propertyName);
+ return true;
+ }
- public new event PropertyChangedEventHandler PropertyChanged
- {
- add => _propertyChanged += value;
- remove => _propertyChanged -= value;
- }
- protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
- {
- if (EqualityComparer<T>.Default.Equals(field, value)) return false;
- field = value;
- RaisePropertyChanged(propertyName);
- return true;
- }
+ protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ }
+
+ /// <summary>
+ /// Notifies listeners that a property value has changed.
+ /// </summary>
+ /// <param name="propertyName">
+ /// Name of the property used to notify listeners. This
+ /// value is optional and can be provided automatically when invoked from compilers
+ /// that support <see cref="CallerMemberNameAttribute" />.
+ /// </param>
+ protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ var e = new PropertyChangedEventArgs(propertyName);
+ OnPropertyChanged(e);
+ _propertyChanged?.Invoke(this, e);
+ }
+ #endregion
- protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
- {
- }
+ #region Sub Classes
+
+ /// <summary>
+ /// Represents available levels of zoom in an <see cref="AdvancedImageBox"/> control
+ /// </summary>
+ public class ZoomLevelCollection : IList<int>
+ {
+ #region Public Constructors
/// <summary>
- /// Notifies listeners that a property value has changed.
+ /// Initializes a new instance of the <see cref="ZoomLevelCollection"/> class.
/// </summary>
- /// <param name="propertyName">
- /// Name of the property used to notify listeners. This
- /// value is optional and can be provided automatically when invoked from compilers
- /// that support <see cref="CallerMemberNameAttribute" />.
- /// </param>
- protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+ public ZoomLevelCollection()
{
- var e = new PropertyChangedEventArgs(propertyName);
- OnPropertyChanged(e);
- _propertyChanged?.Invoke(this, e);
+ List = new SortedList<int, int>();
}
- #endregion
-
- #region Sub Classes
/// <summary>
- /// Represents available levels of zoom in an <see cref="ImageBox"/> control
+ /// Initializes a new instance of the <see cref="ZoomLevelCollection"/> class.
/// </summary>
- public class ZoomLevelCollection : IList<int>
+ /// <param name="collection">The default values to populate the collection with.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <c>collection</c> parameter is null</exception>
+ public ZoomLevelCollection(IEnumerable<int> collection)
+ : this()
{
- #region Public Constructors
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ZoomLevelCollection"/> class.
- /// </summary>
- public ZoomLevelCollection()
+ if (collection == null)
{
- List = new SortedList<int, int>();
+ throw new ArgumentNullException(nameof(collection));
}
- /// <summary>
- /// Initializes a new instance of the <see cref="ZoomLevelCollection"/> class.
- /// </summary>
- /// <param name="collection">The default values to populate the collection with.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <c>collection</c> parameter is null</exception>
- public ZoomLevelCollection(IEnumerable<int> collection)
- : this()
- {
- if (collection == null)
- {
- throw new ArgumentNullException(nameof(collection));
- }
-
- AddRange(collection);
- }
-
- #endregion
-
- #region Public Class Properties
-
- /// <summary>
- /// Returns the default zoom levels
- /// </summary>
- public static ZoomLevelCollection Default =>
- new(new[] {
- 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200
- });
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// Gets the number of elements contained in the <see cref="ZoomLevelCollection" />.
- /// </summary>
- /// <returns>
- /// The number of elements contained in the <see cref="ZoomLevelCollection" />.
- /// </returns>
- public int Count => List.Count;
-
- /// <summary>
- /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
- /// </summary>
- /// <value><c>true</c> if this instance is read only; otherwise, <c>false</c>.</value>
- /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.
- /// </returns>
- public bool IsReadOnly => false;
-
- /// <summary>
- /// Gets or sets the zoom level at the specified index.
- /// </summary>
- /// <param name="index">The index.</param>
- public int this[int index]
- {
- get => List.Values[index];
- set
- {
- List.RemoveAt(index);
- Add(value);
- }
- }
-
- #endregion
-
- #region Protected Properties
-
- /// <summary>
- /// Gets or sets the backing list.
- /// </summary>
- protected SortedList<int, int> List { get; set; }
-
- #endregion
-
- #region Public Members
-
- /// <summary>
- /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
- /// </summary>
- /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
- public void Add(int item)
- {
- List.Add(item, item);
- }
-
- /// <summary>
- /// Adds a range of items to the <see cref="ZoomLevelCollection"/>.
- /// </summary>
- /// <param name="collection">The items to add to the collection.</param>
- /// <exception cref="System.ArgumentNullException">Thrown if the <c>collection</c> parameter is null.</exception>
- public void AddRange(IEnumerable<int> collection)
- {
- if (collection == null)
- {
- throw new ArgumentNullException(nameof(collection));
- }
-
- foreach (int value in collection)
- {
- Add(value);
- }
- }
-
- /// <summary>
- /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />.
- /// </summary>
- public void Clear()
- {
- List.Clear();
- }
-
- /// <summary>
- /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
- /// </summary>
- /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
- /// <returns>true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.</returns>
- public bool Contains(int item)
- {
- return List.ContainsKey(item);
- }
-
- /// <summary>
- /// Copies a range of elements this collection into a destination <see cref="Array"/>.
- /// </summary>
- /// <param name="array">The <see cref="Array"/> that receives the data.</param>
- /// <param name="arrayIndex">A 64-bit integer that represents the index in the <see cref="Array"/> at which storing begins.</param>
- public void CopyTo(int[] array, int arrayIndex)
- {
- for (int i = 0; i < Count; i++)
- {
- array[arrayIndex + i] = List.Values[i];
- }
- }
-
- /// <summary>
- /// Finds the index of a zoom level matching or nearest to the specified value.
- /// </summary>
- /// <param name="zoomLevel">The zoom level.</param>
- public int FindNearest(int zoomLevel)
- {
- int nearestValue = List.Values[0];
- int nearestDifference = Math.Abs(nearestValue - zoomLevel);
- for (int i = 1; i < Count; i++)
- {
- int value = List.Values[i];
- int difference = Math.Abs(value - zoomLevel);
- if (difference < nearestDifference)
- {
- nearestValue = value;
- nearestDifference = difference;
- }
- }
- return nearestValue;
- }
-
- /// <summary>
- /// Returns an enumerator that iterates through the collection.
- /// </summary>
- /// <returns>A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.</returns>
- public IEnumerator<int> GetEnumerator()
- {
- return List.Values.GetEnumerator();
- }
-
- /// <summary>
- /// Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1" />.
- /// </summary>
- /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1" />.</param>
- /// <returns>The index of <paramref name="item" /> if found in the list; otherwise, -1.</returns>
- public int IndexOf(int item)
- {
- return List.IndexOfKey(item);
- }
-
- /// <summary>
- /// Not implemented.
- /// </summary>
- /// <param name="index">The index.</param>
- /// <param name="item">The item.</param>
- /// <exception cref="System.NotImplementedException">Not implemented</exception>
- public void Insert(int index, int item)
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Returns the next increased zoom level for the given current zoom.
- /// </summary>
- /// <param name="zoomLevel">The current zoom level.</param>
- /// <returns>The next matching increased zoom level for the given current zoom if applicable, otherwise the nearest zoom.</returns>
- public int NextZoom(int zoomLevel)
- {
- var index = IndexOf(FindNearest(zoomLevel));
- if (index < Count - 1)
- {
- index++;
- }
-
- return this[index];
- }
-
- /// <summary>
- /// Returns the next decreased zoom level for the given current zoom.
- /// </summary>
- /// <param name="zoomLevel">The current zoom level.</param>
- /// <returns>The next matching decreased zoom level for the given current zoom if applicable, otherwise the nearest zoom.</returns>
- public int PreviousZoom(int zoomLevel)
- {
- var index = IndexOf(FindNearest(zoomLevel));
- if (index > 0)
- {
- index--;
- }
-
- return this[index];
- }
-
- /// <summary>
- /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
- /// </summary>
- /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
- /// <returns>true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.</returns>
- public bool Remove(int item)
- {
- return List.Remove(item);
- }
-
- /// <summary>
- /// Removes the element at the specified index of the <see cref="ZoomLevelCollection"/>.
- /// </summary>
- /// <param name="index">The zero-based index of the element to remove.</param>
- public void RemoveAt(int index)
- {
- List.RemoveAt(index);
- }
-
- /// <summary>
- /// Copies the elements of the <see cref="ZoomLevelCollection"/> to a new array.
- /// </summary>
- /// <returns>An array containing copies of the elements of the <see cref="ZoomLevelCollection"/>.</returns>
- public int[] ToArray()
- {
- int[] results;
-
- results = new int[Count];
- CopyTo(results, 0);
-
- return results;
- }
-
- #endregion
-
- #region IList<int> Members
-
- /// <summary>
- /// Returns an enumerator that iterates through a collection.
- /// </summary>
- /// <returns>An <see cref="ZoomLevelCollection" /> object that can be used to iterate through the collection.</returns>
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- #endregion
+ AddRange(collection);
}
#endregion
- #region Enums
+ #region Public Class Properties
/// <summary>
- /// Determines the sizing mode of an image hosted in an <see cref="AdvancedImageBox" /> control.
+ /// Returns the default zoom levels
/// </summary>
- public enum SizeModes : byte
- {
- /// <summary>
- /// The image is displayed according to current zoom and scroll properties.
- /// </summary>
- Normal,
+ public static ZoomLevelCollection Default =>
+ new(new[] {
+ 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200
+ });
- /// <summary>
- /// The image is stretched to fill the client area of the control.
- /// </summary>
- Stretch,
-
- /// <summary>
- /// The image is stretched to fill as much of the client area of the control as possible, whilst retaining the same aspect ratio for the width and height.
- /// </summary>
- Fit
- }
+ #endregion
- [Flags]
- public enum MouseButtons : byte
- {
- None = 0,
- LeftButton = 1,
- MiddleButton = 2,
- RightButton = 4
- }
+ #region Public Properties
/// <summary>
- /// Describes the zoom action occuring
+ /// Gets the number of elements contained in the <see cref="ZoomLevelCollection" />.
/// </summary>
- [Flags]
- public enum ZoomActions : byte
- {
- /// <summary>
- /// No action.
- /// </summary>
- None = 0,
-
- /// <summary>
- /// The control is increasing the zoom.
- /// </summary>
- ZoomIn = 1,
-
- /// <summary>
- /// The control is decreasing the zoom.
- /// </summary>
- ZoomOut = 2,
-
- /// <summary>
- /// The control zoom was reset.
- /// </summary>
- ActualSize = 4
- }
-
- public enum SelectionModes
- {
- /// <summary>
- /// No selection.
- /// </summary>
- None,
-
- /// <summary>
- /// Rectangle selection.
- /// </summary>
- Rectangle,
-
- /// <summary>
- /// Zoom selection.
- /// </summary>
- Zoom
- }
-
- #endregion
+ /// <returns>
+ /// The number of elements contained in the <see cref="ZoomLevelCollection" />.
+ /// </returns>
+ public int Count => List.Count;
- #region UI Controls
- public ScrollBar HorizontalScrollBar { get; }
- public ScrollBar VerticalScrollBar { get; }
- public ContentPresenter ViewPort { get; }
+ /// <summary>
+ /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
+ /// </summary>
+ /// <value><c>true</c> if this instance is read only; otherwise, <c>false</c>.</value>
+ /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.
+ /// </returns>
+ public bool IsReadOnly => false;
- public Vector Offset
+ /// <summary>
+ /// Gets or sets the zoom level at the specified index.
+ /// </summary>
+ /// <param name="index">The index.</param>
+ public int this[int index]
{
- get => new(HorizontalScrollBar.Value, VerticalScrollBar.Value);
+ get => List.Values[index];
set
{
- HorizontalScrollBar.Value = value.X;
- VerticalScrollBar.Value = value.Y;
- RaisePropertyChanged();
- TriggerRender();
+ List.RemoveAt(index);
+ Add(value);
}
}
- public Size ViewPortSize => ViewPort.Bounds.Size;
- #endregion
-
- #region Private Members
- private Point _startMousePosition;
- private Vector _startScrollPosition;
- private bool _isPanning;
- private bool _isSelecting;
- private Bitmap _trackerImage;
- private bool _canRender = true;
- private Point _pointerPosition;
#endregion
- #region Properties
- public static readonly DirectProperty<AdvancedImageBox, bool> CanRenderProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
- nameof(CanRender),
- o => o.CanRender);
+ #region Protected Properties
/// <summary>
- /// Gets or sets if control can render the image
+ /// Gets or sets the backing list.
/// </summary>
- public bool CanRender
- {
- get => _canRender;
- set
- {
- if (!SetAndRaise(CanRenderProperty, ref _canRender, value)) return;
- if (_canRender) TriggerRender();
- }
- }
+ protected SortedList<int, int> List { get; set; }
+
+ #endregion
- public static readonly StyledProperty<byte> GridCellSizeProperty =
- AvaloniaProperty.Register<AdvancedImageBox, byte>(nameof(GridCellSize), 15);
+ #region Public Members
/// <summary>
- /// Gets or sets the grid cell size
+ /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
- public byte GridCellSize
+ /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
+ public void Add(int item)
{
- get => GetValue(GridCellSizeProperty);
- set => SetValue(GridCellSizeProperty, value);
+ List.Add(item, item);
}
- public static readonly StyledProperty<ISolidColorBrush> GridColorProperty =
- AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColor), Brushes.Gainsboro);
-
/// <summary>
- /// Gets or sets the color used to create the checkerboard style background
+ /// Adds a range of items to the <see cref="ZoomLevelCollection"/>.
/// </summary>
- public ISolidColorBrush GridColor
+ /// <param name="collection">The items to add to the collection.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <c>collection</c> parameter is null.</exception>
+ public void AddRange(IEnumerable<int> collection)
{
- get => GetValue(GridColorProperty);
- set => SetValue(GridColorProperty, value);
- }
+ if (collection == null)
+ {
+ throw new ArgumentNullException(nameof(collection));
+ }
- public static readonly StyledProperty<ISolidColorBrush> GridColorAlternateProperty =
- AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColorAlternate), Brushes.White);
+ foreach (int value in collection)
+ {
+ Add(value);
+ }
+ }
/// <summary>
- /// Gets or sets the color used to create the checkerboard style background
+ /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
- public ISolidColorBrush GridColorAlternate
+ public void Clear()
{
- get => GetValue(GridColorAlternateProperty);
- set => SetValue(GridColorAlternateProperty, value);
+ List.Clear();
}
- public static readonly StyledProperty<Bitmap> ImageProperty =
- AvaloniaProperty.Register<AdvancedImageBox, Bitmap>(nameof(Image));
-
/// <summary>
- /// Gets or sets the image to be displayed
+ /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
/// </summary>
- public Bitmap Image
+ /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
+ /// <returns>true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.</returns>
+ public bool Contains(int item)
{
- get => GetValue(ImageProperty);
- set
- {
- SetValue(ImageProperty, value);
-
- if (value is null)
- {
- SelectNone();
- }
-
- UpdateViewPort();
- TriggerRender();
-
- RaisePropertyChanged(nameof(IsImageLoaded));
- }
+ return List.ContainsKey(item);
}
- public WriteableBitmap ImageAsWriteableBitmap => (WriteableBitmap) Image;
-
- public bool IsImageLoaded => Image is not null;
-
- public static readonly DirectProperty<AdvancedImageBox, Bitmap> TrackerImageProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, Bitmap>(
- nameof(TrackerImage),
- o => o.TrackerImage,
- (o, v) => o.TrackerImage = v);
-
/// <summary>
- /// Gets or sets an image to follow the mouse pointer
+ /// Copies a range of elements this collection into a destination <see cref="Array"/>.
/// </summary>
- public Bitmap TrackerImage
+ /// <param name="array">The <see cref="Array"/> that receives the data.</param>
+ /// <param name="arrayIndex">A 64-bit integer that represents the index in the <see cref="Array"/> at which storing begins.</param>
+ public void CopyTo(int[] array, int arrayIndex)
{
- get => _trackerImage;
- set
+ for (int i = 0; i < Count; i++)
{
- if (!SetAndRaise(TrackerImageProperty, ref _trackerImage, value)) return;
- TriggerRender();
- RaisePropertyChanged(nameof(HaveTrackerImage));
+ array[arrayIndex + i] = List.Values[i];
}
}
- public bool HaveTrackerImage => _trackerImage is not null;
-
- public static readonly StyledProperty<bool> TrackerImageAutoZoomProperty =
- AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(TrackerImageAutoZoom), true);
-
/// <summary>
- /// Gets or sets if the tracker image will be scaled to the current zoom
+ /// Finds the index of a zoom level matching or nearest to the specified value.
/// </summary>
- public bool TrackerImageAutoZoom
+ /// <param name="zoomLevel">The zoom level.</param>
+ public int FindNearest(int zoomLevel)
{
- get => GetValue(TrackerImageAutoZoomProperty);
- set => SetValue(TrackerImageAutoZoomProperty, value);
- }
-
- public bool IsHorizontalBarVisible
- {
- get
+ int nearestValue = List.Values[0];
+ int nearestDifference = Math.Abs(nearestValue - zoomLevel);
+ for (int i = 1; i < Count; i++)
{
- if (Image is null) return false;
- if (SizeMode != SizeModes.Normal) return false;
- return ScaledImageWidth > ViewPortSize.Width;
+ int value = List.Values[i];
+ int difference = Math.Abs(value - zoomLevel);
+ if (difference < nearestDifference)
+ {
+ nearestValue = value;
+ nearestDifference = difference;
+ }
}
+ return nearestValue;
}
- public bool IsVerticalBarVisible
+ /// <summary>
+ /// Returns an enumerator that iterates through the collection.
+ /// </summary>
+ /// <returns>A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.</returns>
+ public IEnumerator<int> GetEnumerator()
{
- get
- {
- if (Image is null) return false;
- if (SizeMode != SizeModes.Normal) return false;
- return ScaledImageHeight > ViewPortSize.Height;
- }
+ return List.Values.GetEnumerator();
}
- public static readonly StyledProperty<bool> ShowGridProperty =
- AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(ShowGrid), true);
-
/// <summary>
- /// Gets or sets the grid visibility when reach high zoom levels
+ /// Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1" />.
/// </summary>
- public bool ShowGrid
+ /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1" />.</param>
+ /// <returns>The index of <paramref name="item" /> if found in the list; otherwise, -1.</returns>
+ public int IndexOf(int item)
{
- get => GetValue(ShowGridProperty);
- set => SetValue(ShowGridProperty, value);
+ return List.IndexOfKey(item);
}
- public static readonly DirectProperty<AdvancedImageBox, Point> PointerPositionProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, Point>(
- nameof(PointerPosition),
- o => o.PointerPosition);
-
/// <summary>
- /// Gets the current pointer position
+ /// Not implemented.
/// </summary>
- public Point PointerPosition
+ /// <param name="index">The index.</param>
+ /// <param name="item">The item.</param>
+ /// <exception cref="System.NotImplementedException">Not implemented</exception>
+ public void Insert(int index, int item)
{
- get => _pointerPosition;
- private set => SetAndRaise(PointerPositionProperty, ref _pointerPosition, value);
+ throw new NotImplementedException();
}
- public static readonly DirectProperty<AdvancedImageBox, bool> IsPanningProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
- nameof(IsPanning),
- o => o.IsPanning);
-
/// <summary>
- /// Gets if control is currently panning
+ /// Returns the next increased zoom level for the given current zoom.
/// </summary>
- public bool IsPanning
+ /// <param name="zoomLevel">The current zoom level.</param>
+ /// <returns>The next matching increased zoom level for the given current zoom if applicable, otherwise the nearest zoom.</returns>
+ public int NextZoom(int zoomLevel)
{
- get => _isPanning;
- protected set
+ var index = IndexOf(FindNearest(zoomLevel));
+ if (index < Count - 1)
{
- if (!SetAndRaise(IsPanningProperty, ref _isPanning, value)) return;
- _startScrollPosition = Offset;
-
- if (value)
- {
- Cursor = new Cursor(StandardCursorType.SizeAll);
- //this.OnPanStart(EventArgs.Empty);
- }
- else
- {
- Cursor = Cursor.Default;
- //this.OnPanEnd(EventArgs.Empty);
- }
+ index++;
}
- }
- public static readonly DirectProperty<AdvancedImageBox, bool> IsSelectingProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
- nameof(IsSelecting),
- o => o.IsSelecting);
-
- /// <summary>
- /// Gets if control is currently selecting a ROI
- /// </summary>
- public bool IsSelecting
- {
- get => _isSelecting;
- protected set => SetAndRaise(IsSelectingProperty, ref _isSelecting, value);
+ return this[index];
}
/// <summary>
- /// Gets the center point of the viewport
+ /// Returns the next decreased zoom level for the given current zoom.
/// </summary>
- public Point CenterPoint
+ /// <param name="zoomLevel">The current zoom level.</param>
+ /// <returns>The next matching decreased zoom level for the given current zoom if applicable, otherwise the nearest zoom.</returns>
+ public int PreviousZoom(int zoomLevel)
{
- get
+ var index = IndexOf(FindNearest(zoomLevel));
+ if (index > 0)
{
- var viewport = GetImageViewPort();
- return new(viewport.Width / 2, viewport.Height / 2);
+ index--;
}
- }
- public static readonly StyledProperty<bool> AutoPanProperty =
- AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoPan), true);
+ return this[index];
+ }
/// <summary>
- /// Gets or sets if the control can pan with the mouse
+ /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
/// </summary>
- public bool AutoPan
+ /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
+ /// <returns>true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.</returns>
+ public bool Remove(int item)
{
- get => GetValue(AutoPanProperty);
- set => SetValue(AutoPanProperty, value);
+ return List.Remove(item);
}
- public static readonly StyledProperty<MouseButtons> PanWithMouseButtonsProperty =
- AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(PanWithMouseButtons), MouseButtons.LeftButton | MouseButtons.MiddleButton | MouseButtons.RightButton);
-
/// <summary>
- /// Gets or sets the mouse buttons to pan the image
+ /// Removes the element at the specified index of the <see cref="ZoomLevelCollection"/>.
/// </summary>
- public MouseButtons PanWithMouseButtons
+ /// <param name="index">The zero-based index of the element to remove.</param>
+ public void RemoveAt(int index)
{
- get => GetValue(PanWithMouseButtonsProperty);
- set => SetValue(PanWithMouseButtonsProperty, value);
+ List.RemoveAt(index);
}
- public static readonly StyledProperty<bool> PanWithArrowsProperty =
- AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(PanWithArrows), true);
-
/// <summary>
- /// Gets or sets if the control can pan with the keyboard arrows
+ /// Copies the elements of the <see cref="ZoomLevelCollection"/> to a new array.
/// </summary>
- public bool PanWithArrows
+ /// <returns>An array containing copies of the elements of the <see cref="ZoomLevelCollection"/>.</returns>
+ public int[] ToArray()
{
- get => GetValue(PanWithArrowsProperty);
- set => SetValue(PanWithArrowsProperty, value);
+ int[] results;
+
+ results = new int[Count];
+ CopyTo(results, 0);
+
+ return results;
}
- public static readonly StyledProperty<MouseButtons> SelectWithMouseButtonsProperty =
- AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(SelectWithMouseButtons), MouseButtons.LeftButton | MouseButtons.RightButton);
+ #endregion
+ #region IList<int> Members
/// <summary>
- /// Gets or sets the mouse buttons to select a region on image
+ /// Returns an enumerator that iterates through a collection.
/// </summary>
- public MouseButtons SelectWithMouseButtons
+ /// <returns>An <see cref="ZoomLevelCollection" /> object that can be used to iterate through the collection.</returns>
+ IEnumerator IEnumerable.GetEnumerator()
{
- get => GetValue(SelectWithMouseButtonsProperty);
- set => SetValue(SelectWithMouseButtonsProperty, value);
+ return GetEnumerator();
}
- public static readonly StyledProperty<bool> InvertMousePanProperty =
- AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(InvertMousePan), false);
+ #endregion
+ }
- /// <summary>
- /// Gets or sets if mouse pan is inverted
- /// </summary>
- public bool InvertMousePan
- {
- get => GetValue(InvertMousePanProperty);
- set => SetValue(InvertMousePanProperty, value);
- }
+ #endregion
- public static readonly StyledProperty<bool> AutoCenterProperty =
- AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoCenter), true);
+ #region Enums
+ /// <summary>
+ /// Determines the sizing mode of an image hosted in an <see cref="AdvancedImageBox" /> control.
+ /// </summary>
+ public enum SizeModes : byte
+ {
/// <summary>
- /// Gets or sets if image is auto centered
+ /// The image is displayed according to current zoom and scroll properties.
/// </summary>
- public bool AutoCenter
- {
- get => GetValue(AutoCenterProperty);
- set => SetValue(AutoCenterProperty, value);
- }
-
- public static readonly StyledProperty<SizeModes> SizeModeProperty =
- AvaloniaProperty.Register<AdvancedImageBox, SizeModes>(nameof(SizeMode), SizeModes.Normal);
+ Normal,
/// <summary>
- /// Gets or sets the image size mode
+ /// The image is stretched to fill the client area of the control.
/// </summary>
- public SizeModes SizeMode
- {
- get => GetValue(SizeModeProperty);
- set
- {
- SetValue(SizeModeProperty, value);
- SizeModeChanged();
- RaisePropertyChanged(nameof(IsHorizontalBarVisible));
- RaisePropertyChanged(nameof(IsVerticalBarVisible));
- }
- }
+ Stretch,
- private void SizeModeChanged()
- {
- switch (SizeMode)
- {
- case SizeModes.Normal:
- HorizontalScrollBar.Visibility = ScrollBarVisibility.Auto;
- VerticalScrollBar.Visibility = ScrollBarVisibility.Auto;
- break;
- case SizeModes.Stretch:
- case SizeModes.Fit:
- HorizontalScrollBar.Visibility = ScrollBarVisibility.Hidden;
- VerticalScrollBar.Visibility = ScrollBarVisibility.Hidden;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
- }
- }
+ /// <summary>
+ /// The image is stretched to fill as much of the client area of the control as possible, whilst retaining the same aspect ratio for the width and height.
+ /// </summary>
+ Fit
+ }
- public static readonly StyledProperty<bool> AllowZoomProperty =
- AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AllowZoom), true);
+ [Flags]
+ public enum MouseButtons : byte
+ {
+ None = 0,
+ LeftButton = 1,
+ MiddleButton = 2,
+ RightButton = 4
+ }
+ /// <summary>
+ /// Describes the zoom action occurring
+ /// </summary>
+ [Flags]
+ public enum ZoomActions : byte
+ {
/// <summary>
- /// Gets or sets if zoom is allowed
+ /// No action.
/// </summary>
- public bool AllowZoom
- {
- get => GetValue(AllowZoomProperty);
- set => SetValue(AllowZoomProperty, value);
- }
+ None = 0,
- public static readonly DirectProperty<AdvancedImageBox, ZoomLevelCollection> ZoomLevelsProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, ZoomLevelCollection>(
- nameof(ZoomLevels),
- o => o.ZoomLevels,
- (o, v) => o.ZoomLevels = v);
+ /// <summary>
+ /// The control is increasing the zoom.
+ /// </summary>
+ ZoomIn = 1,
- ZoomLevelCollection _zoomLevels = ZoomLevelCollection.Default;
/// <summary>
- /// Gets or sets the zoom levels.
+ /// The control is decreasing the zoom.
/// </summary>
- /// <value>The zoom levels.</value>
- public ZoomLevelCollection ZoomLevels
- {
- get => _zoomLevels;
- set => SetAndRaise(ZoomLevelsProperty, ref _zoomLevels, value);
- }
+ ZoomOut = 2,
- public static readonly StyledProperty<int> MinZoomProperty =
- AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MinZoom), 10);
+ /// <summary>
+ /// The control zoom was reset.
+ /// </summary>
+ ActualSize = 4
+ }
+ public enum SelectionModes
+ {
/// <summary>
- /// Gets or sets the minimum possible zoom.
+ /// No selection.
/// </summary>
- /// <value>The zoom.</value>
- public int MinZoom
- {
- get => GetValue(MinZoomProperty);
- set => SetValue(MinZoomProperty, value);
- }
+ None,
- public static readonly StyledProperty<int> MaxZoomProperty =
- AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MaxZoom), 3500);
+ /// <summary>
+ /// Rectangle selection.
+ /// </summary>
+ Rectangle,
/// <summary>
- /// Gets or sets the maximum possible zoom.
+ /// Zoom selection.
/// </summary>
- /// <value>The zoom.</value>
- public int MaxZoom
- {
- get => GetValue(MaxZoomProperty);
- set => SetValue(MaxZoomProperty, value);
- }
+ Zoom
+ }
+ #endregion
- public static readonly DirectProperty<AdvancedImageBox, int> OldZoomProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, int>(
- nameof(OldZoom),
- o => o.OldZoom);
+ #region UI Controls
+ public ScrollBar HorizontalScrollBar { get; }
+ public ScrollBar VerticalScrollBar { get; }
+ public ContentPresenter ViewPort { get; }
- private int _oldZoom = 100;
+ public Vector Offset
+ {
+ get => new(HorizontalScrollBar.Value, VerticalScrollBar.Value);
+ set
+ {
+ HorizontalScrollBar.Value = value.X;
+ VerticalScrollBar.Value = value.Y;
+ RaisePropertyChanged();
+ TriggerRender();
+ }
+ }
- /// <summary>
- /// Gets the previous zoom value
- /// </summary>
- /// <value>The zoom.</value>
- public int OldZoom
+ public Size ViewPortSize => ViewPort.Bounds.Size;
+ #endregion
+
+ #region Private Members
+ private Point _startMousePosition;
+ private Vector _startScrollPosition;
+ private bool _isPanning;
+ private bool _isSelecting;
+ private Bitmap? _trackerImage;
+ private bool _canRender = true;
+ private Point _pointerPosition;
+ #endregion
+
+ #region Properties
+ public static readonly DirectProperty<AdvancedImageBox, bool> CanRenderProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(CanRender),
+ o => o.CanRender);
+
+ /// <summary>
+ /// Gets or sets if control can render the image
+ /// </summary>
+ public bool CanRender
+ {
+ get => _canRender;
+ set
{
- get => _oldZoom;
- private set => SetAndRaise(OldZoomProperty, ref _oldZoom, value);
+ if (!SetAndRaise(CanRenderProperty, ref _canRender, value)) return;
+ if (_canRender) TriggerRender();
}
+ }
- public static readonly StyledProperty<int> ZoomProperty =
- AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(Zoom), 100);
+ public static readonly StyledProperty<byte> GridCellSizeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, byte>(nameof(GridCellSize), 15);
- /// <summary>
- /// Gets or sets the zoom.
- /// </summary>
- /// <value>The zoom.</value>
- public int Zoom
- {
- get => GetValue(ZoomProperty);
- set
- {
- var newZoom = Math.Clamp(value, MinZoom, MaxZoom);
+ /// <summary>
+ /// Gets or sets the grid cell size
+ /// </summary>
+ public byte GridCellSize
+ {
+ get => GetValue(GridCellSizeProperty);
+ set => SetValue(GridCellSizeProperty, value);
+ }
- var previousZoom = Zoom;
- if (previousZoom == newZoom) return;
- OldZoom = previousZoom;
- SetValue(ZoomProperty, value);
+ public static readonly StyledProperty<ISolidColorBrush> GridColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColor), Brushes.Gainsboro);
- UpdateViewPort();
- TriggerRender();
+ /// <summary>
+ /// Gets or sets the color used to create the checkerboard style background
+ /// </summary>
+ public ISolidColorBrush GridColor
+ {
+ get => GetValue(GridColorProperty);
+ set => SetValue(GridColorProperty, value);
+ }
- RaisePropertyChanged(nameof(IsHorizontalBarVisible));
- RaisePropertyChanged(nameof(IsVerticalBarVisible));
- }
- }
+ public static readonly StyledProperty<ISolidColorBrush> GridColorAlternateProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColorAlternate), Brushes.White);
- public bool IsActualSize => Zoom == 100;
+ /// <summary>
+ /// Gets or sets the color used to create the checkerboard style background
+ /// </summary>
+ public ISolidColorBrush GridColorAlternate
+ {
+ get => GetValue(GridColorAlternateProperty);
+ set => SetValue(GridColorAlternateProperty, value);
+ }
- /// <summary>
- /// Gets the zoom factor, the zoom / 100
- /// </summary>
- public double ZoomFactor => Zoom / 100.0;
+ public static readonly StyledProperty<Bitmap?> ImageProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, Bitmap?>(nameof(Image));
- /// <summary>
- /// Gets the width of the scaled image.
- /// </summary>
- /// <value>The width of the scaled image.</value>
- public double ScaledImageWidth => Image.Size.Width * ZoomFactor;
+ /// <summary>
+ /// Gets or sets the image to be displayed
+ /// </summary>
+ public Bitmap? Image
+ {
+ get => GetValue(ImageProperty);
+ set
+ {
+ SetValue(ImageProperty, value);
- /// <summary>
- /// Gets the height of the scaled image.
- /// </summary>
- /// <value>The height of the scaled image.</value>
- public double ScaledImageHeight => Image.Size.Height * ZoomFactor;
+ if (value is null)
+ {
+ SelectNone();
+ }
- public static readonly StyledProperty<ISolidColorBrush> PixelGridColorProperty =
- AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(PixelGridColor), Brushes.DimGray);
+ UpdateViewPort();
+ TriggerRender();
- /// <summary>
- /// Gets or sets the color of the pixel grid.
- /// </summary>
- /// <value>The color of the pixel grid.</value>
- public ISolidColorBrush PixelGridColor
+ RaisePropertyChanged(nameof(IsImageLoaded));
+ }
+ }
+
+ public WriteableBitmap? ImageAsWriteableBitmap
+ {
+ get
{
- get => GetValue(PixelGridColorProperty);
- set => SetValue(PixelGridColorProperty, value);
+ if (Image is null) return null;
+ return (WriteableBitmap) Image;
}
+ }
- public static readonly StyledProperty<int> PixelGridZoomThresholdProperty =
- AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(PixelGridZoomThreshold), 5);
+ public bool IsImageLoaded => Image is not null;
- /// <summary>
- /// Gets or sets the minimum size of zoomed pixel's before the pixel grid will be drawn
- /// </summary>
- /// <value>The pixel grid threshold.</value>
+ public static readonly DirectProperty<AdvancedImageBox, Bitmap?> TrackerImageProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, Bitmap?>(
+ nameof(TrackerImage),
+ o => o.TrackerImage,
+ (o, v) => o.TrackerImage = v);
- public int PixelGridZoomThreshold
+ /// <summary>
+ /// Gets or sets an image to follow the mouse pointer
+ /// </summary>
+ public Bitmap? TrackerImage
+ {
+ get => _trackerImage;
+ set
{
- get => GetValue(PixelGridZoomThresholdProperty);
- set => SetValue(PixelGridZoomThresholdProperty, value);
+ if (!SetAndRaise(TrackerImageProperty, ref _trackerImage, value)) return;
+ TriggerRender();
+ RaisePropertyChanged(nameof(HaveTrackerImage));
}
+ }
+
+ public bool HaveTrackerImage => _trackerImage is not null;
- public static readonly StyledProperty<SelectionModes> SelectionModeProperty =
- AvaloniaProperty.Register<AdvancedImageBox, SelectionModes>(nameof(SelectionMode), SelectionModes.None);
+ public static readonly StyledProperty<bool> TrackerImageAutoZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(TrackerImageAutoZoom), true);
- public SelectionModes SelectionMode
+ /// <summary>
+ /// Gets or sets if the tracker image will be scaled to the current zoom
+ /// </summary>
+ public bool TrackerImageAutoZoom
+ {
+ get => GetValue(TrackerImageAutoZoomProperty);
+ set => SetValue(TrackerImageAutoZoomProperty, value);
+ }
+
+ public bool IsHorizontalBarVisible
+ {
+ get
{
- get => GetValue(SelectionModeProperty);
- set => SetValue(SelectionModeProperty, value);
+ if (Image is null) return false;
+ if (SizeMode != SizeModes.Normal) return false;
+ return ScaledImageWidth > ViewPortSize.Width;
}
+ }
- public static readonly StyledProperty<ISolidColorBrush> SelectionColorProperty =
- AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(SelectionColor), new SolidColorBrush(new Color(127, 0, 128, 255)));
-
- public ISolidColorBrush SelectionColor
+ public bool IsVerticalBarVisible
+ {
+ get
{
- get => GetValue(SelectionColorProperty);
- set => SetValue(SelectionColorProperty, value);
+ if (Image is null) return false;
+ if (SizeMode != SizeModes.Normal) return false;
+ return ScaledImageHeight > ViewPortSize.Height;
}
+ }
- public static readonly StyledProperty<Rect> SelectionRegionProperty =
- AvaloniaProperty.Register<AdvancedImageBox, Rect>(nameof(SelectionRegion), Rect.Empty);
+ public static readonly StyledProperty<bool> ShowGridProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(ShowGrid), true);
+ /// <summary>
+ /// Gets or sets the grid visibility when reach high zoom levels
+ /// </summary>
+ public bool ShowGrid
+ {
+ get => GetValue(ShowGridProperty);
+ set => SetValue(ShowGridProperty, value);
+ }
- public Rect SelectionRegion
- {
- get => GetValue(SelectionRegionProperty);
- set
- {
- SetValue(SelectionRegionProperty, value);
- //if (!RaiseAndSetIfChanged(ref _selectionRegion, value)) return;
- TriggerRender();
- RaisePropertyChanged(nameof(HaveSelection));
- RaisePropertyChanged(nameof(SelectionRegionNet));
- RaisePropertyChanged(nameof(SelectionPixelSize));
- }
- }
+ public static readonly DirectProperty<AdvancedImageBox, Point> PointerPositionProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, Point>(
+ nameof(PointerPosition),
+ o => o.PointerPosition);
- public Rectangle SelectionRegionNet
+ /// <summary>
+ /// Gets the current pointer position
+ /// </summary>
+ public Point PointerPosition
+ {
+ get => _pointerPosition;
+ private set => SetAndRaise(PointerPositionProperty, ref _pointerPosition, value);
+ }
+
+ public static readonly DirectProperty<AdvancedImageBox, bool> IsPanningProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(IsPanning),
+ o => o.IsPanning);
+
+ /// <summary>
+ /// Gets if control is currently panning
+ /// </summary>
+ public bool IsPanning
+ {
+ get => _isPanning;
+ protected set
{
- get
+ if (!SetAndRaise(IsPanningProperty, ref _isPanning, value)) return;
+ _startScrollPosition = Offset;
+
+ if (value)
{
- var rect = SelectionRegion;
- return new Rectangle((int) Math.Ceiling(rect.X), (int)Math.Ceiling(rect.Y),
- (int)rect.Width, (int)rect.Height);
+ Cursor = new Cursor(StandardCursorType.SizeAll);
+ //this.OnPanStart(EventArgs.Empty);
}
- }
-
- public PixelSize SelectionPixelSize
- {
- get
+ else
{
- var rect = SelectionRegion;
- return new PixelSize((int) rect.Width, (int) rect.Height);
+ Cursor = Cursor.Default;
+ //this.OnPanEnd(EventArgs.Empty);
}
}
+ }
- public bool HaveSelection => !SelectionRegion.IsEmpty;
- #endregion
+ public static readonly DirectProperty<AdvancedImageBox, bool> IsSelectingProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(IsSelecting),
+ o => o.IsSelecting);
+
+ /// <summary>
+ /// Gets if control is currently selecting a ROI
+ /// </summary>
+ public bool IsSelecting
+ {
+ get => _isSelecting;
+ protected set => SetAndRaise(IsSelectingProperty, ref _isSelecting, value);
+ }
- #region Constructor
- public AdvancedImageBox()
+ /// <summary>
+ /// Gets the center point of the viewport
+ /// </summary>
+ public Point CenterPoint
+ {
+ get
{
- InitializeComponent();
+ var viewport = GetImageViewPort();
+ return new(viewport.Width / 2, viewport.Height / 2);
+ }
+ }
- FocusableProperty.OverrideDefaultValue(typeof(AdvancedImageBox), true);
- AffectsRender<AdvancedImageBox>(ShowGridProperty);
+ public static readonly StyledProperty<bool> AutoPanProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoPan), true);
- HorizontalScrollBar = this.FindControl<ScrollBar>("HorizontalScrollBar");
- VerticalScrollBar = this.FindControl<ScrollBar>("VerticalScrollBar");
- ViewPort = this.FindControl<ContentPresenter>("ViewPort");
+ /// <summary>
+ /// Gets or sets if the control can pan with the mouse
+ /// </summary>
+ public bool AutoPan
+ {
+ get => GetValue(AutoPanProperty);
+ set => SetValue(AutoPanProperty, value);
+ }
- SizeModeChanged();
+ public static readonly StyledProperty<MouseButtons> PanWithMouseButtonsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(PanWithMouseButtons), MouseButtons.LeftButton | MouseButtons.MiddleButton | MouseButtons.RightButton);
- HorizontalScrollBar.Scroll += ScrollBarOnScroll;
- VerticalScrollBar.Scroll += ScrollBarOnScroll;
- ViewPort.PointerWheelChanged += FillContainerOnPointerWheelChanged;
- }
+ /// <summary>
+ /// Gets or sets the mouse buttons to pan the image
+ /// </summary>
+ public MouseButtons PanWithMouseButtons
+ {
+ get => GetValue(PanWithMouseButtonsProperty);
+ set => SetValue(PanWithMouseButtonsProperty, value);
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
- #endregion
+ public static readonly StyledProperty<bool> PanWithArrowsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(PanWithArrows), true);
- #region Render methods
- public void TriggerRender(bool renderOnlyCursorTracker = false)
- {
- if (!_canRender) return;
- if (renderOnlyCursorTracker && _trackerImage is null) return;
-
- InvalidateVisual();
- }
+ /// <summary>
+ /// Gets or sets if the control can pan with the keyboard arrows
+ /// </summary>
+ public bool PanWithArrows
+ {
+ get => GetValue(PanWithArrowsProperty);
+ set => SetValue(PanWithArrowsProperty, value);
+ }
- public override void Render(DrawingContext context)
- {
- //Debug.WriteLine($"Render: {DateTime.Now.Ticks}");
- base.Render(context);
+ public static readonly StyledProperty<MouseButtons> SelectWithMouseButtonsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(SelectWithMouseButtons), MouseButtons.LeftButton | MouseButtons.RightButton);
- // Draw Grid
- var gridCellSize = GridCellSize;
- if (ShowGrid & gridCellSize > 0 && (!IsHorizontalBarVisible || !IsVerticalBarVisible))
- {
- // draw the background
- var gridColor = GridColor;
- var altColor = GridColorAlternate;
- var currentColor = gridColor;
- for (int y = 0; y < ViewPortSize.Height; y += gridCellSize)
- {
- var firstRowColor = currentColor;
- for (int x = 0; x < ViewPortSize.Width; x += gridCellSize)
- {
- context.FillRectangle(currentColor, new Rect(x, y, gridCellSize, gridCellSize));
- currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
- }
+ /// <summary>
+ /// Gets or sets the mouse buttons to select a region on image
+ /// </summary>
+ public MouseButtons SelectWithMouseButtons
+ {
+ get => GetValue(SelectWithMouseButtonsProperty);
+ set => SetValue(SelectWithMouseButtonsProperty, value);
+ }
- if (Equals(firstRowColor, currentColor))
- currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
- }
+ public static readonly StyledProperty<bool> InvertMousePanProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(InvertMousePan), false);
- }
- /*else
- {
- context.FillRectangle(Background, new Rect(0, 0, Viewport.Width, Viewport.Height));
- }*/
+ /// <summary>
+ /// Gets or sets if mouse pan is inverted
+ /// </summary>
+ public bool InvertMousePan
+ {
+ get => GetValue(InvertMousePanProperty);
+ set => SetValue(InvertMousePanProperty, value);
+ }
- var image = Image;
- if (image is null) return;
- // Draw iamge
- context.DrawImage(image,
- GetSourceImageRegion(),
- GetImageViewPort()
- );
+ public static readonly StyledProperty<bool> AutoCenterProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoCenter), true);
- var zoomFactor = ZoomFactor;
+ /// <summary>
+ /// Gets or sets if image is auto centered
+ /// </summary>
+ public bool AutoCenter
+ {
+ get => GetValue(AutoCenterProperty);
+ set => SetValue(AutoCenterProperty, value);
+ }
+ public static readonly StyledProperty<SizeModes> SizeModeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, SizeModes>(nameof(SizeMode), SizeModes.Normal);
- if (HaveTrackerImage && _pointerPosition.X >= 0 && _pointerPosition.Y >= 0)
- {
- var destSize = TrackerImageAutoZoom
- ? new Size(_trackerImage.Size.Width * zoomFactor, _trackerImage.Size.Height * zoomFactor)
- : image.Size;
-
- var destPos = new Point(
- _pointerPosition.X - destSize.Width / 2,
- _pointerPosition.Y - destSize.Height / 2
- );
- context.DrawImage(_trackerImage,
- new Rect(destPos, destSize)
- );
- }
+ /// <summary>
+ /// Gets or sets the image size mode
+ /// </summary>
+ public SizeModes SizeMode
+ {
+ get => GetValue(SizeModeProperty);
+ set
+ {
+ SetValue(SizeModeProperty, value);
+ SizeModeChanged();
+ RaisePropertyChanged(nameof(IsHorizontalBarVisible));
+ RaisePropertyChanged(nameof(IsVerticalBarVisible));
+ }
+ }
- //SkiaContext.SkCanvas.dr
- // Draw pixel grid
- if (zoomFactor > PixelGridZoomThreshold && SizeMode == SizeModes.Normal)
- {
- var viewport = GetImageViewPort();
- var offsetX = Offset.X % zoomFactor;
- var offsetY = Offset.Y % zoomFactor;
+ private void SizeModeChanged()
+ {
+ switch (SizeMode)
+ {
+ case SizeModes.Normal:
+ HorizontalScrollBar.Visibility = ScrollBarVisibility.Auto;
+ VerticalScrollBar.Visibility = ScrollBarVisibility.Auto;
+ break;
+ case SizeModes.Stretch:
+ case SizeModes.Fit:
+ HorizontalScrollBar.Visibility = ScrollBarVisibility.Hidden;
+ VerticalScrollBar.Visibility = ScrollBarVisibility.Hidden;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
+ }
+ }
- Pen pen = new(PixelGridColor);
- for (double x = viewport.X + zoomFactor - offsetX; x < viewport.Right; x += zoomFactor)
- {
- context.DrawLine(pen, new Point(x, viewport.X), new Point(x, viewport.Bottom));
- }
+ public static readonly StyledProperty<bool> AllowZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AllowZoom), true);
- for (double y = viewport.Y + zoomFactor - offsetY; y < viewport.Bottom; y += zoomFactor)
- {
- context.DrawLine(pen, new Point(viewport.Y, y), new Point(viewport.Right, y));
- }
+ /// <summary>
+ /// Gets or sets if zoom is allowed
+ /// </summary>
+ public bool AllowZoom
+ {
+ get => GetValue(AllowZoomProperty);
+ set => SetValue(AllowZoomProperty, value);
+ }
- context.DrawRectangle(pen, viewport);
- }
+ public static readonly DirectProperty<AdvancedImageBox, ZoomLevelCollection> ZoomLevelsProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, ZoomLevelCollection>(
+ nameof(ZoomLevels),
+ o => o.ZoomLevels,
+ (o, v) => o.ZoomLevels = v);
+
+ ZoomLevelCollection _zoomLevels = ZoomLevelCollection.Default;
+ /// <summary>
+ /// Gets or sets the zoom levels.
+ /// </summary>
+ /// <value>The zoom levels.</value>
+ public ZoomLevelCollection ZoomLevels
+ {
+ get => _zoomLevels;
+ set => SetAndRaise(ZoomLevelsProperty, ref _zoomLevels, value);
+ }
- if (!SelectionRegion.IsEmpty)
- {
- var rect = GetOffsetRectangle(SelectionRegion);
- var selectionColor = SelectionColor;
- context.FillRectangle(selectionColor, rect);
- Color color = Color.FromArgb(255, selectionColor.Color.R, selectionColor.Color.G, selectionColor.Color.B);
- context.DrawRectangle(new Pen(color.ToUint32()), rect);
- }
- }
+ public static readonly StyledProperty<int> MinZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MinZoom), 10);
- private bool UpdateViewPort()
- {
- if (Image is null)
- {
- HorizontalScrollBar.Maximum = 0;
- VerticalScrollBar.Maximum = 0;
- return true;
- }
+ /// <summary>
+ /// Gets or sets the minimum possible zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int MinZoom
+ {
+ get => GetValue(MinZoomProperty);
+ set => SetValue(MinZoomProperty, value);
+ }
- var scaledImageWidth = ScaledImageWidth;
- var scaledImageHeight = ScaledImageHeight;
- var width = scaledImageWidth - HorizontalScrollBar.ViewportSize;
- var height = scaledImageHeight - VerticalScrollBar.ViewportSize;
- //var width = scaledImageWidth <= Viewport.Width ? Viewport.Width : scaledImageWidth;
- //var height = scaledImageHeight <= Viewport.Height ? Viewport.Height : scaledImageHeight;
+ public static readonly StyledProperty<int> MaxZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MaxZoom), 3500);
- bool changed = false;
- if (Math.Abs(HorizontalScrollBar.Maximum - width) > 0.01)
- {
- HorizontalScrollBar.Maximum = width;
- changed = true;
- }
+ /// <summary>
+ /// Gets or sets the maximum possible zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int MaxZoom
+ {
+ get => GetValue(MaxZoomProperty);
+ set => SetValue(MaxZoomProperty, value);
+ }
- if (Math.Abs(VerticalScrollBar.Maximum - scaledImageHeight) > 0.01)
- {
- VerticalScrollBar.Maximum = height;
- changed = true;
- }
- /*if (changed)
- {
- var newContainer = new ContentControl
- {
- Width = width,
- Height = height
- };
- FillContainer.Content = SizedContainer = newContainer;
- Debug.WriteLine($"Updated ViewPort: {DateTime.Now.Ticks}");
- //TriggerRender();
- }*/
+ public static readonly DirectProperty<AdvancedImageBox, int> OldZoomProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, int>(
+ nameof(OldZoom),
+ o => o.OldZoom);
- return changed;
- }
- #endregion
+ private int _oldZoom = 100;
- #region Events and Overrides
+ /// <summary>
+ /// Gets the previous zoom value
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int OldZoom
+ {
+ get => _oldZoom;
+ private set => SetAndRaise(OldZoomProperty, ref _oldZoom, value);
+ }
- private void ScrollBarOnScroll(object? sender, ScrollEventArgs e)
- {
- TriggerRender();
- }
+ public static readonly StyledProperty<int> ZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(Zoom), 100);
- /*protected override void OnScrollChanged(ScrollChangedEventArgs e)
+ /// <summary>
+ /// Gets or sets the zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int Zoom
+ {
+ get => GetValue(ZoomProperty);
+ set
{
- Debug.WriteLine($"ViewportDelta: {e.ViewportDelta} | OffsetDelta: {e.OffsetDelta} | ExtentDelta: {e.ExtentDelta}");
- if (!e.ViewportDelta.IsDefault)
- {
- UpdateViewPort();
- }
+ var newZoom = Math.Clamp(value, MinZoom, MaxZoom);
- TriggerRender();
+ var previousZoom = Zoom;
+ if (previousZoom == newZoom) return;
+ OldZoom = previousZoom;
+ SetValue(ZoomProperty, value);
- base.OnScrollChanged(e);
- }*/
+ UpdateViewPort();
+ TriggerRender();
- private void FillContainerOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
- {
- e.Handled = true;
- if (Image is null) return;
- if (AllowZoom && SizeMode == SizeModes.Normal)
- {
- // The MouseWheel event can contain multiple "spins" of the wheel so we need to adjust accordingly
- //double spins = Math.Abs(e.Delta.Y);
- //Debug.WriteLine(e.GetPosition(this));
- // TODO: Really should update the source method to handle multiple increments rather than calling it multiple times
- /*for (int i = 0; i < spins; i++)
- {*/
- ProcessMouseZoom(e.Delta.Y > 0, e.GetPosition(ViewPort));
- //}
- }
+ RaisePropertyChanged(nameof(IsHorizontalBarVisible));
+ RaisePropertyChanged(nameof(IsVerticalBarVisible));
}
+ }
- protected override void OnPointerPressed(PointerPressedEventArgs e)
- {
- base.OnPointerPressed(e);
- if (e.Handled
- || _isPanning
- || _isSelecting
- || Image is null) return;
+ public bool IsActualSize => Zoom == 100;
+
+ /// <summary>
+ /// Gets the zoom factor, the zoom / 100
+ /// </summary>
+ public double ZoomFactor => Zoom / 100.0;
+
+ /// <summary>
+ /// Gets the width of the scaled image.
+ /// </summary>
+ /// <value>The width of the scaled image.</value>
+ public double ScaledImageWidth => Image?.Size.Width * ZoomFactor ?? 0;
+
+ /// <summary>
+ /// Gets the height of the scaled image.
+ /// </summary>
+ /// <value>The height of the scaled image.</value>
+ public double ScaledImageHeight => Image?.Size.Height * ZoomFactor ?? 0;
+
+ public static readonly StyledProperty<ISolidColorBrush> PixelGridColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(PixelGridColor), Brushes.DimGray);
+
+ /// <summary>
+ /// Gets or sets the color of the pixel grid.
+ /// </summary>
+ /// <value>The color of the pixel grid.</value>
+ public ISolidColorBrush PixelGridColor
+ {
+ get => GetValue(PixelGridColorProperty);
+ set => SetValue(PixelGridColorProperty, value);
+ }
- var pointer = e.GetCurrentPoint(this);
+ public static readonly StyledProperty<int> PixelGridZoomThresholdProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(PixelGridZoomThreshold), 5);
- if (SelectionMode != SelectionModes.None)
- {
- if (!(
- pointer.Properties.IsLeftButtonPressed && (SelectWithMouseButtons & MouseButtons.LeftButton) != 0 ||
- pointer.Properties.IsMiddleButtonPressed && (SelectWithMouseButtons & MouseButtons.MiddleButton) != 0 ||
- pointer.Properties.IsRightButtonPressed && (SelectWithMouseButtons & MouseButtons.RightButton) != 0
- )
- ) return;
- IsSelecting = true;
- }
- else
- {
- if (!(
- pointer.Properties.IsLeftButtonPressed && (PanWithMouseButtons & MouseButtons.LeftButton) != 0 ||
- pointer.Properties.IsMiddleButtonPressed && (PanWithMouseButtons & MouseButtons.MiddleButton) != 0 ||
- pointer.Properties.IsRightButtonPressed && (PanWithMouseButtons & MouseButtons.RightButton) != 0
- )
- || !AutoPan
- || SizeMode != SizeModes.Normal
+ /// <summary>
+ /// Gets or sets the minimum size of zoomed pixel's before the pixel grid will be drawn
+ /// </summary>
+ /// <value>The pixel grid threshold.</value>
- ) return;
+ public int PixelGridZoomThreshold
+ {
+ get => GetValue(PixelGridZoomThresholdProperty);
+ set => SetValue(PixelGridZoomThresholdProperty, value);
+ }
- IsPanning = true;
- }
+ public static readonly StyledProperty<SelectionModes> SelectionModeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, SelectionModes>(nameof(SelectionMode), SelectionModes.None);
- var location = pointer.Position;
+ public SelectionModes SelectionMode
+ {
+ get => GetValue(SelectionModeProperty);
+ set => SetValue(SelectionModeProperty, value);
+ }
- if (location.X > ViewPortSize.Width) return;
- if (location.Y > ViewPortSize.Height) return;
- _startMousePosition = location;
- }
+ public static readonly StyledProperty<ISolidColorBrush> SelectionColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(SelectionColor), new SolidColorBrush(new Color(127, 0, 128, 255)));
+
+ public ISolidColorBrush SelectionColor
+ {
+ get => GetValue(SelectionColorProperty);
+ set => SetValue(SelectionColorProperty, value);
+ }
+
+ public static readonly StyledProperty<Rect> SelectionRegionProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, Rect>(nameof(SelectionRegion), Rect.Empty);
- protected override void OnPointerReleased(PointerReleasedEventArgs e)
- {
- base.OnPointerReleased(e);
- if (e.Handled) return;
- IsPanning = false;
- IsSelecting = false;
+ public Rect SelectionRegion
+ {
+ get => GetValue(SelectionRegionProperty);
+ set
+ {
+ SetValue(SelectionRegionProperty, value);
+ //if (!RaiseAndSetIfChanged(ref _selectionRegion, value)) return;
+ TriggerRender();
+ RaisePropertyChanged(nameof(HaveSelection));
+ RaisePropertyChanged(nameof(SelectionRegionNet));
+ RaisePropertyChanged(nameof(SelectionPixelSize));
}
+ }
- protected override void OnPointerLeave(PointerEventArgs e)
+ public Rectangle SelectionRegionNet
+ {
+ get
{
- base.OnPointerLeave(e);
- PointerPosition = new Point(-1, -1);
- TriggerRender(true);
- e.Handled = true;
+ var rect = SelectionRegion;
+ return new Rectangle((int) Math.Ceiling(rect.X), (int)Math.Ceiling(rect.Y),
+ (int)rect.Width, (int)rect.Height);
}
+ }
- protected override void OnPointerMoved(PointerEventArgs e)
+ public PixelSize SelectionPixelSize
+ {
+ get
{
- base.OnPointerMoved(e);
- if (e.Handled) return;
+ var rect = SelectionRegion;
+ return new PixelSize((int) rect.Width, (int) rect.Height);
+ }
+ }
- var pointer = e.GetCurrentPoint(this);
- PointerPosition = pointer.Position;
+ public bool HaveSelection => !SelectionRegion.IsEmpty;
+ #endregion
- if (!_isPanning && !_isSelecting)
- {
- TriggerRender(true);
- return;
- }
+ #region Constructor
+ public AdvancedImageBox()
+ {
+ InitializeComponent();
- if (_isPanning)
- {
- double x;
- double y;
+ FocusableProperty.OverrideDefaultValue(typeof(AdvancedImageBox), true);
+ AffectsRender<AdvancedImageBox>(ShowGridProperty);
- if (!InvertMousePan)
- {
- x = _startScrollPosition.X + (_startMousePosition.X - _pointerPosition.X);
- y = _startScrollPosition.Y + (_startMousePosition.Y - _pointerPosition.Y);
- }
- else
- {
- x = (_startScrollPosition.X - (_startMousePosition.X - _pointerPosition.X));
- y = (_startScrollPosition.Y - (_startMousePosition.Y - _pointerPosition.Y));
- }
+ HorizontalScrollBar = this.FindControl<ScrollBar>("HorizontalScrollBar");
+ VerticalScrollBar = this.FindControl<ScrollBar>("VerticalScrollBar");
+ ViewPort = this.FindControl<ContentPresenter>("ViewPort");
- Offset = new Vector(x, y);
- }
- else if (_isSelecting)
- {
- double x;
- double y;
- double w;
- double h;
+ SizeModeChanged();
- var imageOffset = GetImageViewPort().Position;
+ HorizontalScrollBar.Scroll += ScrollBarOnScroll;
+ VerticalScrollBar.Scroll += ScrollBarOnScroll;
+ ViewPort.PointerWheelChanged += FillContainerOnPointerWheelChanged;
+ }
- if (_pointerPosition.X < _startMousePosition.X)
- {
- x = _pointerPosition.X;
- w = _startMousePosition.X - _pointerPosition.X;
- }
- else
- {
- x = _startMousePosition.X;
- w = _pointerPosition.X - _startMousePosition.X;
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ #endregion
- if (_pointerPosition.Y < _startMousePosition.Y)
- {
- y = _pointerPosition.Y;
- h = _startMousePosition.Y - _pointerPosition.Y;
- }
- else
- {
- y = _startMousePosition.Y;
- h = _pointerPosition.Y - _startMousePosition.Y;
- }
+ #region Render methods
+ public void TriggerRender(bool renderOnlyCursorTracker = false)
+ {
+ if (!_canRender) return;
+ if (renderOnlyCursorTracker && _trackerImage is null) return;
+
+ InvalidateVisual();
+ }
- x -= imageOffset.X - Offset.X;
- y -= imageOffset.Y - Offset.Y;
+ public override void Render(DrawingContext context)
+ {
+ //Debug.WriteLine($"Render: {DateTime.Now.Ticks}");
+ base.Render(context);
- var zoomFactor = ZoomFactor;
- x /= zoomFactor;
- y /= zoomFactor;
- w /= zoomFactor;
- h /= zoomFactor;
+ // Draw Grid
+ var gridCellSize = GridCellSize;
+ if (ShowGrid & gridCellSize > 0 && (!IsHorizontalBarVisible || !IsVerticalBarVisible))
+ {
+ // draw the background
+ var gridColor = GridColor;
+ var altColor = GridColorAlternate;
+ var currentColor = gridColor;
+ for (int y = 0; y < ViewPortSize.Height; y += gridCellSize)
+ {
+ var firstRowColor = currentColor;
- if (w != 0 && h != 0)
+ for (int x = 0; x < ViewPortSize.Width; x += gridCellSize)
{
- SelectionRegion = FitRectangle(new Rect(x, y, w, h));
+ context.FillRectangle(currentColor, new Rect(x, y, gridCellSize, gridCellSize));
+ currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
}
+
+ if (Equals(firstRowColor, currentColor))
+ currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
}
- e.Handled = true;
}
- #endregion
+ /*else
+ {
+ context.FillRectangle(Background, new Rect(0, 0, Viewport.Width, Viewport.Height));
+ }*/
- #region Zoom and Size modes
- private void ProcessMouseZoom(bool isZoomIn, Point cursorPosition)
- => PerformZoom(isZoomIn ? ZoomActions.ZoomIn : ZoomActions.ZoomOut, true, cursorPosition);
+ var image = Image;
+ if (image is null) return;
+ // Draw iamge
+ context.DrawImage(image,
+ GetSourceImageRegion(),
+ GetImageViewPort()
+ );
- /// <summary>
- /// Returns an appropriate zoom level based on the specified action, relative to the current zoom level.
- /// </summary>
- /// <param name="action">The action to determine the zoom level.</param>
- /// <exception cref="System.ArgumentOutOfRangeException">Thrown if an unsupported action is specified.</exception>
- private int GetZoomLevel(ZoomActions action)
- {
- var result = action switch
- {
- ZoomActions.None => Zoom,
- ZoomActions.ZoomIn => _zoomLevels.NextZoom(Zoom),
- ZoomActions.ZoomOut => _zoomLevels.PreviousZoom(Zoom),
- ZoomActions.ActualSize => 100,
- _ => throw new ArgumentOutOfRangeException(nameof(action), action, null),
- };
- return result;
- }
+ var zoomFactor = ZoomFactor;
- /// <summary>
- /// Resets the <see cref="SizeModes"/> property whilsts retaining the original <see cref="Zoom"/>.
- /// </summary>
- protected void RestoreSizeMode()
+
+ if (HaveTrackerImage && _pointerPosition.X >= 0 && _pointerPosition.Y >= 0)
{
- if (SizeMode != SizeModes.Normal)
- {
- var previousZoom = Zoom;
- SizeMode = SizeModes.Normal;
- Zoom = previousZoom; // Stop the zoom getting reset to 100% before calculating the new zoom
- }
- }
+ var destSize = TrackerImageAutoZoom
+ ? new Size(_trackerImage!.Size.Width * zoomFactor, _trackerImage.Size.Height * zoomFactor)
+ : image.Size;
- private void PerformZoom(ZoomActions action, bool preservePosition)
- => PerformZoom(action, preservePosition, CenterPoint);
+ var destPos = new Point(
+ _pointerPosition.X - destSize.Width / 2,
+ _pointerPosition.Y - destSize.Height / 2
+ );
+ context.DrawImage(_trackerImage,
+ new Rect(destPos, destSize)
+ );
+ }
- private void PerformZoom(ZoomActions action, bool preservePosition, Point relativePoint)
+ //SkiaContext.SkCanvas.dr
+ // Draw pixel grid
+ if (zoomFactor > PixelGridZoomThreshold && SizeMode == SizeModes.Normal)
{
- Point currentPixel = PointToImage(relativePoint);
- int currentZoom = Zoom;
- int newZoom = GetZoomLevel(action);
-
- /*if (preservePosition && Zoom != currentZoom)
- CanRender = false;*/
+ var viewport = GetImageViewPort();
+ var offsetX = Offset.X % zoomFactor;
+ var offsetY = Offset.Y % zoomFactor;
- RestoreSizeMode();
- Zoom = newZoom;
+ Pen pen = new(PixelGridColor);
+ for (double x = viewport.X + zoomFactor - offsetX; x < viewport.Right; x += zoomFactor)
+ {
+ context.DrawLine(pen, new Point(x, viewport.X), new Point(x, viewport.Bottom));
+ }
- if (preservePosition && Zoom != currentZoom)
+ for (double y = viewport.Y + zoomFactor - offsetY; y < viewport.Bottom; y += zoomFactor)
{
- ScrollTo(currentPixel, relativePoint);
+ context.DrawLine(pen, new Point(viewport.Y, y), new Point(viewport.Right, y));
}
+
+ context.DrawRectangle(pen, viewport);
}
- /// <summary>
- /// Zooms into the image
- /// </summary>
- public void ZoomIn()
- => ZoomIn(true);
+ if (!SelectionRegion.IsEmpty)
+ {
+ var rect = GetOffsetRectangle(SelectionRegion);
+ var selectionColor = SelectionColor;
+ context.FillRectangle(selectionColor, rect);
+ Color color = Color.FromArgb(255, selectionColor.Color.R, selectionColor.Color.G, selectionColor.Color.B);
+ context.DrawRectangle(new Pen(color.ToUint32()), rect);
+ }
+ }
- /// <summary>
- /// Zooms into the image
- /// </summary>
- /// <param name="preservePosition"><c>true</c> if the current scrolling position should be preserved relative to the new zoom level, <c>false</c> to reset.</param>
- public void ZoomIn(bool preservePosition)
+ private bool UpdateViewPort()
+ {
+ if (Image is null)
{
- PerformZoom(ZoomActions.ZoomIn, preservePosition);
+ HorizontalScrollBar.Maximum = 0;
+ VerticalScrollBar.Maximum = 0;
+ return true;
}
- /// <summary>
- /// Zooms out of the image
- /// </summary>
- public void ZoomOut()
- => ZoomOut(true);
+ var scaledImageWidth = ScaledImageWidth;
+ var scaledImageHeight = ScaledImageHeight;
+ var width = scaledImageWidth - HorizontalScrollBar.ViewportSize;
+ var height = scaledImageHeight - VerticalScrollBar.ViewportSize;
+ //var width = scaledImageWidth <= Viewport.Width ? Viewport.Width : scaledImageWidth;
+ //var height = scaledImageHeight <= Viewport.Height ? Viewport.Height : scaledImageHeight;
- /// <summary>
- /// Zooms out of the image
- /// </summary>
- /// <param name="preservePosition"><c>true</c> if the current scrolling position should be preserved relative to the new zoom level, <c>false</c> to reset.</param>
- public void ZoomOut(bool preservePosition)
+ bool changed = false;
+ if (Math.Abs(HorizontalScrollBar.Maximum - width) > 0.01)
{
- PerformZoom(ZoomActions.ZoomOut, preservePosition);
+ HorizontalScrollBar.Maximum = width;
+ changed = true;
}
- /// <summary>
- /// Zooms to the maximum size for displaying the entire image within the bounds of the control.
- /// </summary>
- public void ZoomToFit()
+ if (Math.Abs(VerticalScrollBar.Maximum - scaledImageHeight) > 0.01)
{
- var image = Image;
- if (image is null) return;
-
- double zoom;
- double aspectRatio;
-
- if (image.Size.Width > image.Size.Height)
- {
- aspectRatio = ViewPortSize.Width / image.Size.Width;
- zoom = aspectRatio * 100.0;
+ VerticalScrollBar.Maximum = height;
+ changed = true;
+ }
- if (ViewPortSize.Height < image.Size.Height * zoom / 100.0)
- {
- aspectRatio = ViewPortSize.Height / image.Size.Height;
- zoom = aspectRatio * 100.0;
- }
- }
- else
+ /*if (changed)
+ {
+ var newContainer = new ContentControl
{
- aspectRatio = ViewPortSize.Height / image.Size.Height;
- zoom = aspectRatio * 100.0;
+ Width = width,
+ Height = height
+ };
+ FillContainer.Content = SizedContainer = newContainer;
+ Debug.WriteLine($"Updated ViewPort: {DateTime.Now.Ticks}");
+ //TriggerRender();
+ }*/
- if (ViewPortSize.Width < image.Size.Width * zoom / 100.0)
- {
- aspectRatio = ViewPortSize.Width / image.Size.Width;
- zoom = aspectRatio * 100.0;
- }
- }
+ return changed;
+ }
+ #endregion
- Zoom = (int)zoom;
- }
+ #region Events and Overrides
- /// <summary>
- /// Adjusts the view port to fit the given region
- /// </summary>
- /// <param name="x">The X co-ordinate of the selection region.</param>
- /// <param name="y">The Y co-ordinate of the selection region.</param>
- /// <param name="width">The width of the selection region.</param>
- /// <param name="height">The height of the selection region.</param>
- /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public void ZoomToRegion(double x, double y, double width, double height, double margin = 0)
- {
- ZoomToRegion(new Rect(x, y, width, height), margin);
- }
+ private void ScrollBarOnScroll(object? sender, ScrollEventArgs e)
+ {
+ TriggerRender();
+ }
- /// <summary>
- /// Adjusts the view port to fit the given region
- /// </summary>
- /// <param name="x">The X co-ordinate of the selection region.</param>
- /// <param name="y">The Y co-ordinate of the selection region.</param>
- /// <param name="width">The width of the selection region.</param>
- /// <param name="height">The height of the selection region.</param>
- /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public void ZoomToRegion(int x, int y, int width, int height, double margin = 0)
+ /*protected override void OnScrollChanged(ScrollChangedEventArgs e)
+ {
+ Debug.WriteLine($"ViewportDelta: {e.ViewportDelta} | OffsetDelta: {e.OffsetDelta} | ExtentDelta: {e.ExtentDelta}");
+ if (!e.ViewportDelta.IsDefault)
{
- ZoomToRegion(new Rect(x, y, width, height), margin);
+ UpdateViewPort();
}
- /// <summary>
- /// Adjusts the view port to fit the given region
- /// </summary>
- /// <param name="rectangle">The rectangle to fit the view port to.</param>
- /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public void ZoomToRegion(Rectangle rectangle, double margin = 0) =>
- ZoomToRegion(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, margin);
+ TriggerRender();
- /// <summary>
- /// Adjusts the view port to fit the given region
- /// </summary>
- /// <param name="rectangle">The rectangle to fit the view port to.</param>
- /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public void ZoomToRegion(Rect rectangle, double margin = 0)
- {
- if (margin > 0) rectangle = rectangle.Inflate(margin);
- var ratioX = ViewPortSize.Width / rectangle.Width;
- var ratioY = ViewPortSize.Height / rectangle.Height;
- var zoomFactor = Math.Min(ratioX, ratioY);
- var cx = rectangle.X + rectangle.Width / 2;
- var cy = rectangle.Y + rectangle.Height / 2;
+ base.OnScrollChanged(e);
+ }*/
- CanRender = false;
- Zoom = (int)(zoomFactor * 100); // This function sets the zoom so viewport will change
- CenterAt(new Point(cx, cy)); // If i call this here, it will move to the wrong position due wrong viewport
+ private void FillContainerOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
+ {
+ e.Handled = true;
+ if (Image is null) return;
+ if (AllowZoom && SizeMode == SizeModes.Normal)
+ {
+ // The MouseWheel event can contain multiple "spins" of the wheel so we need to adjust accordingly
+ //double spins = Math.Abs(e.Delta.Y);
+ //Debug.WriteLine(e.GetPosition(this));
+ // TODO: Really should update the source method to handle multiple increments rather than calling it multiple times
+ /*for (int i = 0; i < spins; i++)
+ {*/
+ ProcessMouseZoom(e.Delta.Y > 0, e.GetPosition(ViewPort));
+ //}
}
+ }
- /// <summary>
- /// Zooms to current selection region
- /// </summary>
- public void ZoomToSelectionRegion(double margin = 0)
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ base.OnPointerPressed(e);
+ if (e.Handled
+ || _isPanning
+ || _isSelecting
+ || Image is null) return;
+
+ var pointer = e.GetCurrentPoint(this);
+
+ if (SelectionMode != SelectionModes.None)
{
- if (!HaveSelection) return;
- ZoomToRegion(SelectionRegion, margin);
+ if (!(
+ pointer.Properties.IsLeftButtonPressed && (SelectWithMouseButtons & MouseButtons.LeftButton) != 0 ||
+ pointer.Properties.IsMiddleButtonPressed && (SelectWithMouseButtons & MouseButtons.MiddleButton) != 0 ||
+ pointer.Properties.IsRightButtonPressed && (SelectWithMouseButtons & MouseButtons.RightButton) != 0
+ )
+ ) return;
+ IsSelecting = true;
}
-
- /// <summary>
- /// Resets the zoom to 100%.
- /// </summary>
- public void PerformActualSize()
+ else
{
- SizeMode = SizeModes.Normal;
- //SetZoom(100, ImageZoomActions.ActualSize | (Zoom < 100 ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut));
- Zoom = 100;
+ if (!(
+ pointer.Properties.IsLeftButtonPressed && (PanWithMouseButtons & MouseButtons.LeftButton) != 0 ||
+ pointer.Properties.IsMiddleButtonPressed && (PanWithMouseButtons & MouseButtons.MiddleButton) != 0 ||
+ pointer.Properties.IsRightButtonPressed && (PanWithMouseButtons & MouseButtons.RightButton) != 0
+ )
+ || !AutoPan
+ || SizeMode != SizeModes.Normal
+
+ ) return;
+
+ IsPanning = true;
}
- #endregion
- #region Utility methods
- /// <summary>
- /// Determines whether the specified point is located within the image view port
- /// </summary>
- /// <param name="point">The point.</param>
- /// <returns>
- /// <c>true</c> if the specified point is located within the image view port; otherwise, <c>false</c>.
- /// </returns>
- public bool IsPointInImage(Point point)
- => GetImageViewPort().Contains(point);
+ var location = pointer.Position;
- /// <summary>
- /// Determines whether the specified point is located within the image view port
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to check.</param>
- /// <param name="y">The Y co-ordinate of the point to check.</param>
- /// <returns>
- /// <c>true</c> if the specified point is located within the image view port; otherwise, <c>false</c>.
- /// </returns>
- public bool IsPointInImage(int x, int y)
- => IsPointInImage(new Point(x, y));
+ if (location.X > ViewPortSize.Width) return;
+ if (location.Y > ViewPortSize.Height) return;
+ _startMousePosition = location;
+ }
- /// <summary>
- /// Determines whether the specified point is located within the image view port
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to check.</param>
- /// <param name="y">The Y co-ordinate of the point to check.</param>
- /// <returns>
- /// <c>true</c> if the specified point is located within the image view port; otherwise, <c>false</c>.
- /// </returns>
- public bool IsPointInImage(double x, double y)
- => IsPointInImage(new Point(x, y));
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ base.OnPointerReleased(e);
+ if (e.Handled) return;
- /// <summary>
- /// Converts the given client size point to represent a coordinate on the source image.
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to convert.</param>
- /// <param name="y">The Y co-ordinate of the point to convert.</param>
- /// <param name="fitToBounds">
- /// if set to <c>true</c> and the point is outside the bounds of the source image, it will be mapped to the nearest edge.
- /// </param>
- /// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
- public Point PointToImage(double x, double y, bool fitToBounds = true)
- => PointToImage(new Point(x, y), fitToBounds);
+ IsPanning = false;
+ IsSelecting = false;
+ }
- /// <summary>
- /// Converts the given client size point to represent a coordinate on the source image.
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to convert.</param>
- /// <param name="y">The Y co-ordinate of the point to convert.</param>
- /// <param name="fitToBounds">
- /// if set to <c>true</c> and the point is outside the bounds of the source image, it will be mapped to the nearest edge.
- /// </param>
- /// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
- public Point PointToImage(int x, int y, bool fitToBounds = true)
+ protected override void OnPointerLeave(PointerEventArgs e)
+ {
+ base.OnPointerLeave(e);
+ PointerPosition = new Point(-1, -1);
+ TriggerRender(true);
+ e.Handled = true;
+ }
+
+ protected override void OnPointerMoved(PointerEventArgs e)
+ {
+ base.OnPointerMoved(e);
+ if (e.Handled) return;
+
+ var pointer = e.GetCurrentPoint(this);
+ PointerPosition = pointer.Position;
+
+ if (!_isPanning && !_isSelecting)
{
- return PointToImage(new Point(x, y), fitToBounds);
+ TriggerRender(true);
+ return;
}
- /// <summary>
- /// Converts the given client size point to represent a coordinate on the source image.
- /// </summary>
- /// <param name="point">The source point.</param>
- /// <param name="fitToBounds">
- /// if set to <c>true</c> and the point is outside the bounds of the source image, it will be mapped to the nearest edge.
- /// </param>
- /// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
- public Point PointToImage(Point point, bool fitToBounds = true)
+ if (_isPanning)
{
double x;
double y;
- var viewport = GetImageViewPort();
+ if (!InvertMousePan)
+ {
+ x = _startScrollPosition.X + (_startMousePosition.X - _pointerPosition.X);
+ y = _startScrollPosition.Y + (_startMousePosition.Y - _pointerPosition.Y);
+ }
+ else
+ {
+ x = (_startScrollPosition.X - (_startMousePosition.X - _pointerPosition.X));
+ y = (_startScrollPosition.Y - (_startMousePosition.Y - _pointerPosition.Y));
+ }
- if (!fitToBounds || viewport.Contains(point))
+ Offset = new Vector(x, y);
+ }
+ else if (_isSelecting)
+ {
+ double x;
+ double y;
+ double w;
+ double h;
+
+ var imageOffset = GetImageViewPort().Position;
+
+ if (_pointerPosition.X < _startMousePosition.X)
{
- x = (point.X + Offset.X - viewport.X) / ZoomFactor;
- y = (point.Y + Offset.Y - viewport.Y) / ZoomFactor;
+ x = _pointerPosition.X;
+ w = _startMousePosition.X - _pointerPosition.X;
+ }
+ else
+ {
+ x = _startMousePosition.X;
+ w = _pointerPosition.X - _startMousePosition.X;
+ }
- var image = Image;
- if (fitToBounds)
- {
- x = Math.Clamp(x, 0, image.Size.Width-1);
- y = Math.Clamp(y, 0, image.Size.Height-1);
- }
+ if (_pointerPosition.Y < _startMousePosition.Y)
+ {
+ y = _pointerPosition.Y;
+ h = _startMousePosition.Y - _pointerPosition.Y;
}
else
{
- x = 0; // Return Point.Empty if we couldn't match
- y = 0;
+ y = _startMousePosition.Y;
+ h = _pointerPosition.Y - _startMousePosition.Y;
}
- return new(x, y);
- }
+ x -= imageOffset.X - Offset.X;
+ y -= imageOffset.Y - Offset.Y;
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.Point" /> repositioned to include the current image offset and scaled by the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="Point"/> to offset.</param>
- /// <returns>A <see cref="Point"/> which has been repositioned to match the current zoom level and image offset</returns>
- public Point GetOffsetPoint(System.Drawing.Point source)
- {
- var offset = GetOffsetPoint(new Point(source.X, source.Y));
+ var zoomFactor = ZoomFactor;
+ x /= zoomFactor;
+ y /= zoomFactor;
+ w /= zoomFactor;
+ h /= zoomFactor;
- return new((int)offset.X, (int)offset.Y);
+ if (w != 0 && h != 0)
+ {
+ SelectionRegion = FitRectangle(new Rect(x, y, w, h));
+ }
}
- /// <summary>
- /// Returns the source co-ordinates repositioned to include the current image offset and scaled by the current zoom level
- /// </summary>
- /// <param name="x">The source X co-ordinate.</param>
- /// <param name="y">The source Y co-ordinate.</param>
- /// <returns>A <see cref="Point"/> which has been repositioned to match the current zoom level and image offset</returns>
- public Point GetOffsetPoint(int x, int y)
- {
- return GetOffsetPoint(new System.Drawing.Point(x, y));
- }
+ e.Handled = true;
+ }
+ #endregion
+
+ #region Zoom and Size modes
+ private void ProcessMouseZoom(bool isZoomIn, Point cursorPosition)
+ => PerformZoom(isZoomIn ? ZoomActions.ZoomIn : ZoomActions.ZoomOut, true, cursorPosition);
+
+ /// <summary>
+ /// Returns an appropriate zoom level based on the specified action, relative to the current zoom level.
+ /// </summary>
+ /// <param name="action">The action to determine the zoom level.</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">Thrown if an unsupported action is specified.</exception>
+ private int GetZoomLevel(ZoomActions action)
+ {
+ var result = action switch
+ {
+ ZoomActions.None => Zoom,
+ ZoomActions.ZoomIn => _zoomLevels.NextZoom(Zoom),
+ ZoomActions.ZoomOut => _zoomLevels.PreviousZoom(Zoom),
+ ZoomActions.ActualSize => 100,
+ _ => throw new ArgumentOutOfRangeException(nameof(action), action, null),
+ };
+ return result;
+ }
- /// <summary>
- /// Returns the source co-ordinates repositioned to include the current image offset and scaled by the current zoom level
- /// </summary>
- /// <param name="x">The source X co-ordinate.</param>
- /// <param name="y">The source Y co-ordinate.</param>
- /// <returns>A <see cref="Point"/> which has been repositioned to match the current zoom level and image offset</returns>
- public Point GetOffsetPoint(double x, double y)
+ /// <summary>
+ /// Resets the <see cref="SizeModes"/> property whilsts retaining the original <see cref="Zoom"/>.
+ /// </summary>
+ protected void RestoreSizeMode()
+ {
+ if (SizeMode != SizeModes.Normal)
{
- return GetOffsetPoint(new Point(x, y));
+ var previousZoom = Zoom;
+ SizeMode = SizeModes.Normal;
+ Zoom = previousZoom; // Stop the zoom getting reset to 100% before calculating the new zoom
}
+ }
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.PointF" /> repositioned to include the current image offset and scaled by the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="PointF"/> to offset.</param>
- /// <returns>A <see cref="PointF"/> which has been repositioned to match the current zoom level and image offset</returns>
- public Point GetOffsetPoint(Point source)
- {
- Rect viewport = GetImageViewPort();
- var scaled = GetScaledPoint(source);
- var offsetX = viewport.Left + Offset.X;
- var offsetY = viewport.Top + Offset.Y;
+ private void PerformZoom(ZoomActions action, bool preservePosition)
+ => PerformZoom(action, preservePosition, CenterPoint);
- return new(scaled.X + offsetX, scaled.Y + offsetY);
- }
+ private void PerformZoom(ZoomActions action, bool preservePosition, Point relativePoint)
+ {
+ Point currentPixel = PointToImage(relativePoint);
+ int currentZoom = Zoom;
+ int newZoom = GetZoomLevel(action);
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.RectangleF" /> scaled according to the current zoom level and repositioned to include the current image offset
- /// </summary>
- /// <param name="source">The source <see cref="RectangleF"/> to offset.</param>
- /// <returns>A <see cref="RectangleF"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
- public Rect GetOffsetRectangle(Rect source)
- {
- var viewport = GetImageViewPort();
- var scaled = GetScaledRectangle(source);
- var offsetX = viewport.Left - Offset.X;
- var offsetY = viewport.Top - Offset.Y;
+ /*if (preservePosition && Zoom != currentZoom)
+ CanRender = false;*/
- return new(new Point(scaled.Left + offsetX, scaled.Top + offsetY), scaled.Size);
- }
+ RestoreSizeMode();
+ Zoom = newZoom;
- /// <summary>
- /// Returns the source rectangle scaled according to the current zoom level and repositioned to include the current image offset
- /// </summary>
- /// <param name="x">The X co-ordinate of the source rectangle.</param>
- /// <param name="y">The Y co-ordinate of the source rectangle.</param>
- /// <param name="width">The width of the rectangle.</param>
- /// <param name="height">The height of the rectangle.</param>
- /// <returns>A <see cref="Rectangle"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
- public Rectangle GetOffsetRectangle(int x, int y, int width, int height)
+ if (preservePosition && Zoom != currentZoom)
{
- return GetOffsetRectangle(new Rectangle(x, y, width, height));
+ ScrollTo(currentPixel, relativePoint);
}
+ }
- /// <summary>
- /// Returns the source rectangle scaled according to the current zoom level and repositioned to include the current image offset
- /// </summary>
- /// <param name="x">The X co-ordinate of the source rectangle.</param>
- /// <param name="y">The Y co-ordinate of the source rectangle.</param>
- /// <param name="width">The width of the rectangle.</param>
- /// <param name="height">The height of the rectangle.</param>
- /// <returns>A <see cref="RectangleF"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
- public Rect GetOffsetRectangle(double x, double y, double width, double height)
- {
- return GetOffsetRectangle(new Rect(x, y, width, height));
- }
+ /// <summary>
+ /// Zooms into the image
+ /// </summary>
+ public void ZoomIn()
+ => ZoomIn(true);
+
+ /// <summary>
+ /// Zooms into the image
+ /// </summary>
+ /// <param name="preservePosition"><c>true</c> if the current scrolling position should be preserved relative to the new zoom level, <c>false</c> to reset.</param>
+ public void ZoomIn(bool preservePosition)
+ {
+ PerformZoom(ZoomActions.ZoomIn, preservePosition);
+ }
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.Rectangle" /> scaled according to the current zoom level and repositioned to include the current image offset
- /// </summary>
- /// <param name="source">The source <see cref="Rectangle"/> to offset.</param>
- /// <returns>A <see cref="Rectangle"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
- public Rectangle GetOffsetRectangle(Rectangle source)
- {
- var viewport = GetImageViewPort();
- var scaled = GetScaledRectangle(source);
- var offsetX = viewport.Left + Offset.X;
- var offsetY = viewport.Top + Offset.Y;
+ /// <summary>
+ /// Zooms out of the image
+ /// </summary>
+ public void ZoomOut()
+ => ZoomOut(true);
+
+ /// <summary>
+ /// Zooms out of the image
+ /// </summary>
+ /// <param name="preservePosition"><c>true</c> if the current scrolling position should be preserved relative to the new zoom level, <c>false</c> to reset.</param>
+ public void ZoomOut(bool preservePosition)
+ {
+ PerformZoom(ZoomActions.ZoomOut, preservePosition);
+ }
- return new(new System.Drawing.Point((int)(scaled.Left + offsetX), (int)(scaled.Top + offsetY)), new System.Drawing.Size((int)scaled.Size.Width, (int)scaled.Size.Height));
- }
+ /// <summary>
+ /// Zooms to the maximum size for displaying the entire image within the bounds of the control.
+ /// </summary>
+ public void ZoomToFit()
+ {
+ var image = Image;
+ if (image is null) return;
- /// <summary>
- /// Fits a given <see cref="T:System.Drawing.Rectangle" /> to match image boundaries
- /// </summary>
- /// <param name="rectangle">The rectangle.</param>
- /// <returns>
- /// A <see cref="T:System.Drawing.Rectangle" /> structure remapped to fit the image boundaries
- /// </returns>
- public Rectangle FitRectangle(Rectangle rectangle)
+ double zoom;
+ double aspectRatio;
+
+ if (image.Size.Width > image.Size.Height)
{
- var image = Image;
- if (image is null) return Rectangle.Empty;
- var x = rectangle.X;
- var y = rectangle.Y;
- var w = rectangle.Width;
- var h = rectangle.Height;
+ aspectRatio = ViewPortSize.Width / image.Size.Width;
+ zoom = aspectRatio * 100.0;
- if (x < 0)
+ if (ViewPortSize.Height < image.Size.Height * zoom / 100.0)
{
- x = 0;
+ aspectRatio = ViewPortSize.Height / image.Size.Height;
+ zoom = aspectRatio * 100.0;
}
+ }
+ else
+ {
+ aspectRatio = ViewPortSize.Height / image.Size.Height;
+ zoom = aspectRatio * 100.0;
- if (y < 0)
+ if (ViewPortSize.Width < image.Size.Width * zoom / 100.0)
{
- y = 0;
+ aspectRatio = ViewPortSize.Width / image.Size.Width;
+ zoom = aspectRatio * 100.0;
}
+ }
- if (x + w > image.Size.Width)
- {
- w = (int)(image.Size.Width - x);
- }
+ Zoom = (int)zoom;
+ }
- if (y + h > image.Size.Height)
- {
- h = (int)(image.Size.Height - y);
- }
+ /// <summary>
+ /// Adjusts the view port to fit the given region
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the selection region.</param>
+ /// <param name="y">The Y co-ordinate of the selection region.</param>
+ /// <param name="width">The width of the selection region.</param>
+ /// <param name="height">The height of the selection region.</param>
+ /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
+ public void ZoomToRegion(double x, double y, double width, double height, double margin = 0)
+ {
+ ZoomToRegion(new Rect(x, y, width, height), margin);
+ }
- return new(x, y, w, h);
- }
+ /// <summary>
+ /// Adjusts the view port to fit the given region
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the selection region.</param>
+ /// <param name="y">The Y co-ordinate of the selection region.</param>
+ /// <param name="width">The width of the selection region.</param>
+ /// <param name="height">The height of the selection region.</param>
+ /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
+ public void ZoomToRegion(int x, int y, int width, int height, double margin = 0)
+ {
+ ZoomToRegion(new Rect(x, y, width, height), margin);
+ }
- /// <summary>
- /// Fits a given <see cref="T:System.Drawing.RectangleF" /> to match image boundaries
- /// </summary>
- /// <param name="rectangle">The rectangle.</param>
- /// <returns>
- /// A <see cref="T:System.Drawing.RectangleF" /> structure remapped to fit the image boundaries
- /// </returns>
- public Rect FitRectangle(Rect rectangle)
- {
- var image = Image;
- if (image is null) return Rect.Empty;
- var x = rectangle.X;
- var y = rectangle.Y;
- var w = rectangle.Width;
- var h = rectangle.Height;
+ /// <summary>
+ /// Adjusts the view port to fit the given region
+ /// </summary>
+ /// <param name="rectangle">The rectangle to fit the view port to.</param>
+ /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
+ public void ZoomToRegion(Rectangle rectangle, double margin = 0) =>
+ ZoomToRegion(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, margin);
+
+ /// <summary>
+ /// Adjusts the view port to fit the given region
+ /// </summary>
+ /// <param name="rectangle">The rectangle to fit the view port to.</param>
+ /// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
+ public void ZoomToRegion(Rect rectangle, double margin = 0)
+ {
+ if (margin > 0) rectangle = rectangle.Inflate(margin);
+ var ratioX = ViewPortSize.Width / rectangle.Width;
+ var ratioY = ViewPortSize.Height / rectangle.Height;
+ var zoomFactor = Math.Min(ratioX, ratioY);
+ var cx = rectangle.X + rectangle.Width / 2;
+ var cy = rectangle.Y + rectangle.Height / 2;
+
+ CanRender = false;
+ Zoom = (int)(zoomFactor * 100); // This function sets the zoom so viewport will change
+ CenterAt(new Point(cx, cy)); // If i call this here, it will move to the wrong position due wrong viewport
+ }
- if (x < 0)
- {
- w -= -x;
- x = 0;
- }
+ /// <summary>
+ /// Zooms to current selection region
+ /// </summary>
+ public void ZoomToSelectionRegion(double margin = 0)
+ {
+ if (!HaveSelection) return;
+ ZoomToRegion(SelectionRegion, margin);
+ }
- if (y < 0)
- {
- h -= -y;
- y = 0;
- }
+ /// <summary>
+ /// Resets the zoom to 100%.
+ /// </summary>
+ public void PerformActualSize()
+ {
+ SizeMode = SizeModes.Normal;
+ //SetZoom(100, ImageZoomActions.ActualSize | (Zoom < 100 ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut));
+ Zoom = 100;
+ }
+ #endregion
+
+ #region Utility methods
+ /// <summary>
+ /// Determines whether the specified point is located within the image view port
+ /// </summary>
+ /// <param name="point">The point.</param>
+ /// <returns>
+ /// <c>true</c> if the specified point is located within the image view port; otherwise, <c>false</c>.
+ /// </returns>
+ public bool IsPointInImage(Point point)
+ => GetImageViewPort().Contains(point);
+
+ /// <summary>
+ /// Determines whether the specified point is located within the image view port
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to check.</param>
+ /// <param name="y">The Y co-ordinate of the point to check.</param>
+ /// <returns>
+ /// <c>true</c> if the specified point is located within the image view port; otherwise, <c>false</c>.
+ /// </returns>
+ public bool IsPointInImage(int x, int y)
+ => IsPointInImage(new Point(x, y));
+
+ /// <summary>
+ /// Determines whether the specified point is located within the image view port
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to check.</param>
+ /// <param name="y">The Y co-ordinate of the point to check.</param>
+ /// <returns>
+ /// <c>true</c> if the specified point is located within the image view port; otherwise, <c>false</c>.
+ /// </returns>
+ public bool IsPointInImage(double x, double y)
+ => IsPointInImage(new Point(x, y));
+
+ /// <summary>
+ /// Converts the given client size point to represent a coordinate on the source image.
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to convert.</param>
+ /// <param name="y">The Y co-ordinate of the point to convert.</param>
+ /// <param name="fitToBounds">
+ /// if set to <c>true</c> and the point is outside the bounds of the source image, it will be mapped to the nearest edge.
+ /// </param>
+ /// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
+ public Point PointToImage(double x, double y, bool fitToBounds = true)
+ => PointToImage(new Point(x, y), fitToBounds);
+
+ /// <summary>
+ /// Converts the given client size point to represent a coordinate on the source image.
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to convert.</param>
+ /// <param name="y">The Y co-ordinate of the point to convert.</param>
+ /// <param name="fitToBounds">
+ /// if set to <c>true</c> and the point is outside the bounds of the source image, it will be mapped to the nearest edge.
+ /// </param>
+ /// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
+ public Point PointToImage(int x, int y, bool fitToBounds = true)
+ {
+ return PointToImage(new Point(x, y), fitToBounds);
+ }
- if (x + w > image.Size.Width)
- {
- w = image.Size.Width - x;
- }
+ /// <summary>
+ /// Converts the given client size point to represent a coordinate on the source image.
+ /// </summary>
+ /// <param name="point">The source point.</param>
+ /// <param name="fitToBounds">
+ /// if set to <c>true</c> and the point is outside the bounds of the source image, it will be mapped to the nearest edge.
+ /// </param>
+ /// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
+ public Point PointToImage(Point point, bool fitToBounds = true)
+ {
+ double x;
+ double y;
+
+ var viewport = GetImageViewPort();
- if (y + h > image.Size.Height)
+ if (!fitToBounds || viewport.Contains(point))
+ {
+ x = (point.X + Offset.X - viewport.X) / ZoomFactor;
+ y = (point.Y + Offset.Y - viewport.Y) / ZoomFactor;
+
+ var image = Image;
+ if (fitToBounds)
{
- h = image.Size.Height - y;
+ x = Math.Clamp(x, 0, image!.Size.Width-1);
+ y = Math.Clamp(y, 0, image.Size.Height-1);
}
-
- return new(x, y, w, h);
}
- #endregion
+ else
+ {
+ x = 0; // Return Point.Empty if we couldn't match
+ y = 0;
+ }
- #region Navigate / Scroll methods
- /// <summary>
- /// Scrolls the control to the given point in the image, offset at the specified display point
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to scroll to.</param>
- /// <param name="y">The Y co-ordinate of the point to scroll to.</param>
- /// <param name="relativeX">The X co-ordinate relative to the <c>x</c> parameter.</param>
- /// <param name="relativeY">The Y co-ordinate relative to the <c>y</c> parameter.</param>
- public void ScrollTo(double x, double y, double relativeX, double relativeY)
- => ScrollTo(new Point(x, y), new Point(relativeX, relativeY));
+ return new(x, y);
+ }
- /// <summary>
- /// Scrolls the control to the given point in the image, offset at the specified display point
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to scroll to.</param>
- /// <param name="y">The Y co-ordinate of the point to scroll to.</param>
- /// <param name="relativeX">The X co-ordinate relative to the <c>x</c> parameter.</param>
- /// <param name="relativeY">The Y co-ordinate relative to the <c>y</c> parameter.</param>
- public void ScrollTo(int x, int y, int relativeX, int relativeY)
- => ScrollTo(new Point(x, y), new Point(relativeX, relativeY));
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.Point" /> repositioned to include the current image offset and scaled by the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="Point"/> to offset.</param>
+ /// <returns>A <see cref="Point"/> which has been repositioned to match the current zoom level and image offset</returns>
+ public Point GetOffsetPoint(System.Drawing.Point source)
+ {
+ var offset = GetOffsetPoint(new Point(source.X, source.Y));
- /// <summary>
- /// Scrolls the control to the given point in the image, offset at the specified display point
- /// </summary>
- /// <param name="imageLocation">The point of the image to attempt to scroll to.</param>
- /// <param name="relativeDisplayPoint">The relative display point to offset scrolling by.</param>
- public void ScrollTo(Point imageLocation, Point relativeDisplayPoint)
- {
- //CanRender = false;
- var zoomFactor = ZoomFactor;
- var x = imageLocation.X * zoomFactor - relativeDisplayPoint.X;
- var y = imageLocation.Y * zoomFactor - relativeDisplayPoint.Y;
+ return new((int)offset.X, (int)offset.Y);
+ }
+ /// <summary>
+ /// Returns the source co-ordinates repositioned to include the current image offset and scaled by the current zoom level
+ /// </summary>
+ /// <param name="x">The source X co-ordinate.</param>
+ /// <param name="y">The source Y co-ordinate.</param>
+ /// <returns>A <see cref="Point"/> which has been repositioned to match the current zoom level and image offset</returns>
+ public Point GetOffsetPoint(int x, int y)
+ {
+ return GetOffsetPoint(new System.Drawing.Point(x, y));
+ }
- _canRender = true;
- Offset = new Vector(x, y);
+ /// <summary>
+ /// Returns the source co-ordinates repositioned to include the current image offset and scaled by the current zoom level
+ /// </summary>
+ /// <param name="x">The source X co-ordinate.</param>
+ /// <param name="y">The source Y co-ordinate.</param>
+ /// <returns>A <see cref="Point"/> which has been repositioned to match the current zoom level and image offset</returns>
+ public Point GetOffsetPoint(double x, double y)
+ {
+ return GetOffsetPoint(new Point(x, y));
+ }
- /*Debug.WriteLine(
- $"X/Y: {x},{y} | \n" +
- $"Offset: {Offset} | \n" +
- $"ZoomFactor: {ZoomFactor} | \n" +
- $"Image Location: {imageLocation}\n" +
- $"MAX: {HorizontalScrollBar.Maximum},{VerticalScrollBar.Maximum} \n" +
- $"ViewPort: {Viewport.Width},{Viewport.Height} \n" +
- $"Container: {HorizontalScrollBar.ViewportSize},{VerticalScrollBar.ViewportSize} \n" +
- $"Relative: {relativeDisplayPoint}");*/
- }
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.PointF" /> repositioned to include the current image offset and scaled by the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="PointF"/> to offset.</param>
+ /// <returns>A <see cref="PointF"/> which has been repositioned to match the current zoom level and image offset</returns>
+ public Point GetOffsetPoint(Point source)
+ {
+ Rect viewport = GetImageViewPort();
+ var scaled = GetScaledPoint(source);
+ var offsetX = viewport.Left + Offset.X;
+ var offsetY = viewport.Top + Offset.Y;
- /// <summary>
- /// Centers the given point in the image in the center of the control
- /// </summary>
- /// <param name="imageLocation">The point of the image to attempt to center.</param>
- public void CenterAt(System.Drawing.Point imageLocation)
- => ScrollTo(new Point(imageLocation.X, imageLocation.Y), new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
+ return new(scaled.X + offsetX, scaled.Y + offsetY);
+ }
- /// <summary>
- /// Centers the given point in the image in the center of the control
- /// </summary>
- /// <param name="imageLocation">The point of the image to attempt to center.</param>
- public void CenterAt(Point imageLocation)
- => ScrollTo(imageLocation, new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.RectangleF" /> scaled according to the current zoom level and repositioned to include the current image offset
+ /// </summary>
+ /// <param name="source">The source <see cref="RectangleF"/> to offset.</param>
+ /// <returns>A <see cref="RectangleF"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
+ public Rect GetOffsetRectangle(Rect source)
+ {
+ var viewport = GetImageViewPort();
+ var scaled = GetScaledRectangle(source);
+ var offsetX = viewport.Left - Offset.X;
+ var offsetY = viewport.Top - Offset.Y;
- /// <summary>
- /// Centers the given point in the image in the center of the control
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to center.</param>
- /// <param name="y">The Y co-ordinate of the point to center.</param>
- public void CenterAt(int x, int y)
- => CenterAt(new Point(x, y));
+ return new(new Point(scaled.Left + offsetX, scaled.Top + offsetY), scaled.Size);
+ }
- /// <summary>
- /// Centers the given point in the image in the center of the control
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to center.</param>
- /// <param name="y">The Y co-ordinate of the point to center.</param>
- public void CenterAt(double x, double y)
- => CenterAt(new Point(x, y));
+ /// <summary>
+ /// Returns the source rectangle scaled according to the current zoom level and repositioned to include the current image offset
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the source rectangle.</param>
+ /// <param name="y">The Y co-ordinate of the source rectangle.</param>
+ /// <param name="width">The width of the rectangle.</param>
+ /// <param name="height">The height of the rectangle.</param>
+ /// <returns>A <see cref="Rectangle"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
+ public Rectangle GetOffsetRectangle(int x, int y, int width, int height)
+ {
+ return GetOffsetRectangle(new Rectangle(x, y, width, height));
+ }
- /// <summary>
- /// Resets the viewport to show the center of the image.
- /// </summary>
- public void CenterToImage()
- {
- Offset = new Vector(HorizontalScrollBar.Maximum / 2, VerticalScrollBar.Maximum / 2);
- }
- #endregion
+ /// <summary>
+ /// Returns the source rectangle scaled according to the current zoom level and repositioned to include the current image offset
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the source rectangle.</param>
+ /// <param name="y">The Y co-ordinate of the source rectangle.</param>
+ /// <param name="width">The width of the rectangle.</param>
+ /// <param name="height">The height of the rectangle.</param>
+ /// <returns>A <see cref="RectangleF"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
+ public Rect GetOffsetRectangle(double x, double y, double width, double height)
+ {
+ return GetOffsetRectangle(new Rect(x, y, width, height));
+ }
- #region Selection / ROI methods
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.Rectangle" /> scaled according to the current zoom level and repositioned to include the current image offset
+ /// </summary>
+ /// <param name="source">The source <see cref="Rectangle"/> to offset.</param>
+ /// <returns>A <see cref="Rectangle"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
+ public Rectangle GetOffsetRectangle(Rectangle source)
+ {
+ var viewport = GetImageViewPort();
+ var scaled = GetScaledRectangle(source);
+ var offsetX = viewport.Left + Offset.X;
+ var offsetY = viewport.Top + Offset.Y;
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.Point" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to scale.</param>
- /// <param name="y">The Y co-ordinate of the point to scale.</param>
- /// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
- public Point GetScaledPoint(int x, int y)
- {
- return GetScaledPoint(new Point(x, y));
- }
+ return new(new System.Drawing.Point((int)(scaled.Left + offsetX), (int)(scaled.Top + offsetY)), new System.Drawing.Size((int)scaled.Size.Width, (int)scaled.Size.Height));
+ }
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.Point" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="x">The X co-ordinate of the point to scale.</param>
- /// <param name="y">The Y co-ordinate of the point to scale.</param>
- /// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
- public PointF GetScaledPoint(float x, float y)
- {
- return GetScaledPoint(new PointF(x, y));
- }
+ /// <summary>
+ /// Fits a given <see cref="T:System.Drawing.Rectangle" /> to match image boundaries
+ /// </summary>
+ /// <param name="rectangle">The rectangle.</param>
+ /// <returns>
+ /// A <see cref="T:System.Drawing.Rectangle" /> structure remapped to fit the image boundaries
+ /// </returns>
+ public Rectangle FitRectangle(Rectangle rectangle)
+ {
+ var image = Image;
+ if (image is null) return Rectangle.Empty;
+ var x = rectangle.X;
+ var y = rectangle.Y;
+ var w = rectangle.Width;
+ var h = rectangle.Height;
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.Point" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="Point"/> to scale.</param>
- /// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
- public Point GetScaledPoint(Point source)
+ if (x < 0)
{
- return new(source.X * ZoomFactor, source.Y * ZoomFactor);
+ x = 0;
}
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.PointF" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="PointF"/> to scale.</param>
- /// <returns>A <see cref="PointF"/> which has been scaled to match the current zoom level</returns>
- public PointF GetScaledPoint(PointF source)
+ if (y < 0)
{
- return new((float)(source.X * ZoomFactor), (float)(source.Y * ZoomFactor));
+ y = 0;
}
- /// <summary>
- /// Returns the source rectangle scaled according to the current zoom level
- /// </summary>
- /// <param name="x">The X co-ordinate of the source rectangle.</param>
- /// <param name="y">The Y co-ordinate of the source rectangle.</param>
- /// <param name="width">The width of the rectangle.</param>
- /// <param name="height">The height of the rectangle.</param>
- /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
- public Rect GetScaledRectangle(int x, int y, int width, int height)
+ if (x + w > image.Size.Width)
{
- return GetScaledRectangle(new Rect(x, y, width, height));
+ w = (int)(image.Size.Width - x);
}
- /// <summary>
- /// Returns the source rectangle scaled according to the current zoom level
- /// </summary>
- /// <param name="x">The X co-ordinate of the source rectangle.</param>
- /// <param name="y">The Y co-ordinate of the source rectangle.</param>
- /// <param name="width">The width of the rectangle.</param>
- /// <param name="height">The height of the rectangle.</param>
- /// <returns>A <see cref="RectangleF"/> which has been scaled to match the current zoom level</returns>
- public RectangleF GetScaledRectangle(float x, float y, float width, float height)
+ if (y + h > image.Size.Height)
{
- return GetScaledRectangle(new RectangleF(x, y, width, height));
+ h = (int)(image.Size.Height - y);
}
- /// <summary>
- /// Returns the source rectangle scaled according to the current zoom level
- /// </summary>
- /// <param name="location">The location of the source rectangle.</param>
- /// <param name="size">The size of the source rectangle.</param>
- /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
- public Rect GetScaledRectangle(Point location, Size size)
- {
- return GetScaledRectangle(new Rect(location, size));
- }
+ return new(x, y, w, h);
+ }
- /// <summary>
- /// Returns the source rectangle scaled according to the current zoom level
- /// </summary>
- /// <param name="location">The location of the source rectangle.</param>
- /// <param name="size">The size of the source rectangle.</param>
- /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
- public RectangleF GetScaledRectangle(PointF location, SizeF size)
- {
- return GetScaledRectangle(new RectangleF(location, size));
- }
+ /// <summary>
+ /// Fits a given <see cref="T:System.Drawing.RectangleF" /> to match image boundaries
+ /// </summary>
+ /// <param name="rectangle">The rectangle.</param>
+ /// <returns>
+ /// A <see cref="T:System.Drawing.RectangleF" /> structure remapped to fit the image boundaries
+ /// </returns>
+ public Rect FitRectangle(Rect rectangle)
+ {
+ var image = Image;
+ if (image is null) return Rect.Empty;
+ var x = rectangle.X;
+ var y = rectangle.Y;
+ var w = rectangle.Width;
+ var h = rectangle.Height;
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.Rectangle" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="Rectangle"/> to scale.</param>
- /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
- public Rect GetScaledRectangle(Rect source)
+ if (x < 0)
{
- return new(source.Left * ZoomFactor, source.Top * ZoomFactor, source.Width * ZoomFactor, source.Height * ZoomFactor);
+ w -= -x;
+ x = 0;
}
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.RectangleF" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="RectangleF"/> to scale.</param>
- /// <returns>A <see cref="RectangleF"/> which has been scaled to match the current zoom level</returns>
- public RectangleF GetScaledRectangle(RectangleF source)
+ if (y < 0)
{
- return new((float)(source.Left * ZoomFactor), (float)(source.Top * ZoomFactor), (float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
+ h -= -y;
+ y = 0;
}
- /// <summary>
- /// Returns the source size scaled according to the current zoom level
- /// </summary>
- /// <param name="width">The width of the size to scale.</param>
- /// <param name="height">The height of the size to scale.</param>
- /// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
- public SizeF GetScaledSize(float width, float height)
+ if (x + w > image.Size.Width)
{
- return GetScaledSize(new SizeF(width, height));
+ w = image.Size.Width - x;
}
- /// <summary>
- /// Returns the source size scaled according to the current zoom level
- /// </summary>
- /// <param name="width">The width of the size to scale.</param>
- /// <param name="height">The height of the size to scale.</param>
- /// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
- public Size GetScaledSize(int width, int height)
+ if (y + h > image.Size.Height)
{
- return GetScaledSize(new Size(width, height));
+ h = image.Size.Height - y;
}
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.SizeF" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="SizeF"/> to scale.</param>
- /// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
- public SizeF GetScaledSize(SizeF source)
- {
- return new((float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
- }
+ return new(x, y, w, h);
+ }
+ #endregion
+
+ #region Navigate / Scroll methods
+ /// <summary>
+ /// Scrolls the control to the given point in the image, offset at the specified display point
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to scroll to.</param>
+ /// <param name="y">The Y co-ordinate of the point to scroll to.</param>
+ /// <param name="relativeX">The X co-ordinate relative to the <c>x</c> parameter.</param>
+ /// <param name="relativeY">The Y co-ordinate relative to the <c>y</c> parameter.</param>
+ public void ScrollTo(double x, double y, double relativeX, double relativeY)
+ => ScrollTo(new Point(x, y), new Point(relativeX, relativeY));
+
+ /// <summary>
+ /// Scrolls the control to the given point in the image, offset at the specified display point
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to scroll to.</param>
+ /// <param name="y">The Y co-ordinate of the point to scroll to.</param>
+ /// <param name="relativeX">The X co-ordinate relative to the <c>x</c> parameter.</param>
+ /// <param name="relativeY">The Y co-ordinate relative to the <c>y</c> parameter.</param>
+ public void ScrollTo(int x, int y, int relativeX, int relativeY)
+ => ScrollTo(new Point(x, y), new Point(relativeX, relativeY));
+
+ /// <summary>
+ /// Scrolls the control to the given point in the image, offset at the specified display point
+ /// </summary>
+ /// <param name="imageLocation">The point of the image to attempt to scroll to.</param>
+ /// <param name="relativeDisplayPoint">The relative display point to offset scrolling by.</param>
+ public void ScrollTo(Point imageLocation, Point relativeDisplayPoint)
+ {
+ //CanRender = false;
+ var zoomFactor = ZoomFactor;
+ var x = imageLocation.X * zoomFactor - relativeDisplayPoint.X;
+ var y = imageLocation.Y * zoomFactor - relativeDisplayPoint.Y;
+
+
+ _canRender = true;
+ Offset = new Vector(x, y);
+
+ /*Debug.WriteLine(
+ $"X/Y: {x},{y} | \n" +
+ $"Offset: {Offset} | \n" +
+ $"ZoomFactor: {ZoomFactor} | \n" +
+ $"Image Location: {imageLocation}\n" +
+ $"MAX: {HorizontalScrollBar.Maximum},{VerticalScrollBar.Maximum} \n" +
+ $"ViewPort: {Viewport.Width},{Viewport.Height} \n" +
+ $"Container: {HorizontalScrollBar.ViewportSize},{VerticalScrollBar.ViewportSize} \n" +
+ $"Relative: {relativeDisplayPoint}");*/
+ }
- /// <summary>
- /// Returns the source <see cref="T:System.Drawing.Size" /> scaled according to the current zoom level
- /// </summary>
- /// <param name="source">The source <see cref="Size"/> to scale.</param>
- /// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
- public Size GetScaledSize(Size source)
- {
- return new(source.Width * ZoomFactor, source.Height * ZoomFactor);
- }
+ /// <summary>
+ /// Centers the given point in the image in the center of the control
+ /// </summary>
+ /// <param name="imageLocation">The point of the image to attempt to center.</param>
+ public void CenterAt(System.Drawing.Point imageLocation)
+ => ScrollTo(new Point(imageLocation.X, imageLocation.Y), new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
+
+ /// <summary>
+ /// Centers the given point in the image in the center of the control
+ /// </summary>
+ /// <param name="imageLocation">The point of the image to attempt to center.</param>
+ public void CenterAt(Point imageLocation)
+ => ScrollTo(imageLocation, new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
+
+ /// <summary>
+ /// Centers the given point in the image in the center of the control
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to center.</param>
+ /// <param name="y">The Y co-ordinate of the point to center.</param>
+ public void CenterAt(int x, int y)
+ => CenterAt(new Point(x, y));
+
+ /// <summary>
+ /// Centers the given point in the image in the center of the control
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to center.</param>
+ /// <param name="y">The Y co-ordinate of the point to center.</param>
+ public void CenterAt(double x, double y)
+ => CenterAt(new Point(x, y));
+
+ /// <summary>
+ /// Resets the viewport to show the center of the image.
+ /// </summary>
+ public void CenterToImage()
+ {
+ Offset = new Vector(HorizontalScrollBar.Maximum / 2, VerticalScrollBar.Maximum / 2);
+ }
+ #endregion
- /// <summary>
- /// Creates a selection region which encompasses the entire image
- /// </summary>
- /// <exception cref="System.InvalidOperationException">Thrown if no image is currently set</exception>
- public void SelectAll()
- {
- var image = Image;
- if (image is null) return;
- SelectionRegion = new Rect(0, 0, image.Size.Width, image.Size.Height);
- }
+ #region Selection / ROI methods
- /// <summary>
- /// Clears any existing selection region
- /// </summary>
- public void SelectNone()
- {
- SelectionRegion = Rect.Empty;
- }
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.Point" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to scale.</param>
+ /// <param name="y">The Y co-ordinate of the point to scale.</param>
+ /// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
+ public Point GetScaledPoint(int x, int y)
+ {
+ return GetScaledPoint(new Point(x, y));
+ }
- #endregion
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.Point" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the point to scale.</param>
+ /// <param name="y">The Y co-ordinate of the point to scale.</param>
+ /// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
+ public PointF GetScaledPoint(float x, float y)
+ {
+ return GetScaledPoint(new PointF(x, y));
+ }
+
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.Point" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="Point"/> to scale.</param>
+ /// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
+ public Point GetScaledPoint(Point source)
+ {
+ return new(source.X * ZoomFactor, source.Y * ZoomFactor);
+ }
+
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.PointF" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="PointF"/> to scale.</param>
+ /// <returns>A <see cref="PointF"/> which has been scaled to match the current zoom level</returns>
+ public PointF GetScaledPoint(PointF source)
+ {
+ return new((float)(source.X * ZoomFactor), (float)(source.Y * ZoomFactor));
+ }
+
+ /// <summary>
+ /// Returns the source rectangle scaled according to the current zoom level
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the source rectangle.</param>
+ /// <param name="y">The Y co-ordinate of the source rectangle.</param>
+ /// <param name="width">The width of the rectangle.</param>
+ /// <param name="height">The height of the rectangle.</param>
+ /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
+ public Rect GetScaledRectangle(int x, int y, int width, int height)
+ {
+ return GetScaledRectangle(new Rect(x, y, width, height));
+ }
+
+ /// <summary>
+ /// Returns the source rectangle scaled according to the current zoom level
+ /// </summary>
+ /// <param name="x">The X co-ordinate of the source rectangle.</param>
+ /// <param name="y">The Y co-ordinate of the source rectangle.</param>
+ /// <param name="width">The width of the rectangle.</param>
+ /// <param name="height">The height of the rectangle.</param>
+ /// <returns>A <see cref="RectangleF"/> which has been scaled to match the current zoom level</returns>
+ public RectangleF GetScaledRectangle(float x, float y, float width, float height)
+ {
+ return GetScaledRectangle(new RectangleF(x, y, width, height));
+ }
+
+ /// <summary>
+ /// Returns the source rectangle scaled according to the current zoom level
+ /// </summary>
+ /// <param name="location">The location of the source rectangle.</param>
+ /// <param name="size">The size of the source rectangle.</param>
+ /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
+ public Rect GetScaledRectangle(Point location, Size size)
+ {
+ return GetScaledRectangle(new Rect(location, size));
+ }
+
+ /// <summary>
+ /// Returns the source rectangle scaled according to the current zoom level
+ /// </summary>
+ /// <param name="location">The location of the source rectangle.</param>
+ /// <param name="size">The size of the source rectangle.</param>
+ /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
+ public RectangleF GetScaledRectangle(PointF location, SizeF size)
+ {
+ return GetScaledRectangle(new RectangleF(location, size));
+ }
+
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.Rectangle" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="Rectangle"/> to scale.</param>
+ /// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
+ public Rect GetScaledRectangle(Rect source)
+ {
+ return new(source.Left * ZoomFactor, source.Top * ZoomFactor, source.Width * ZoomFactor, source.Height * ZoomFactor);
+ }
+
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.RectangleF" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="RectangleF"/> to scale.</param>
+ /// <returns>A <see cref="RectangleF"/> which has been scaled to match the current zoom level</returns>
+ public RectangleF GetScaledRectangle(RectangleF source)
+ {
+ return new((float)(source.Left * ZoomFactor), (float)(source.Top * ZoomFactor), (float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
+ }
+
+ /// <summary>
+ /// Returns the source size scaled according to the current zoom level
+ /// </summary>
+ /// <param name="width">The width of the size to scale.</param>
+ /// <param name="height">The height of the size to scale.</param>
+ /// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
+ public SizeF GetScaledSize(float width, float height)
+ {
+ return GetScaledSize(new SizeF(width, height));
+ }
+
+ /// <summary>
+ /// Returns the source size scaled according to the current zoom level
+ /// </summary>
+ /// <param name="width">The width of the size to scale.</param>
+ /// <param name="height">The height of the size to scale.</param>
+ /// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
+ public Size GetScaledSize(int width, int height)
+ {
+ return GetScaledSize(new Size(width, height));
+ }
+
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.SizeF" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="SizeF"/> to scale.</param>
+ /// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
+ public SizeF GetScaledSize(SizeF source)
+ {
+ return new((float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
+ }
+
+ /// <summary>
+ /// Returns the source <see cref="T:System.Drawing.Size" /> scaled according to the current zoom level
+ /// </summary>
+ /// <param name="source">The source <see cref="Size"/> to scale.</param>
+ /// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
+ public Size GetScaledSize(Size source)
+ {
+ return new(source.Width * ZoomFactor, source.Height * ZoomFactor);
+ }
+
+ /// <summary>
+ /// Creates a selection region which encompasses the entire image
+ /// </summary>
+ /// <exception cref="System.InvalidOperationException">Thrown if no image is currently set</exception>
+ public void SelectAll()
+ {
+ var image = Image;
+ if (image is null) return;
+ SelectionRegion = new Rect(0, 0, image.Size.Width, image.Size.Height);
+ }
+
+ /// <summary>
+ /// Clears any existing selection region
+ /// </summary>
+ public void SelectNone()
+ {
+ SelectionRegion = Rect.Empty;
+ }
+
+ #endregion
- #region Viewport and image region methods
- /// <summary>
- /// Gets the source image region.
- /// </summary>
- /// <returns></returns>
- public Rect GetSourceImageRegion()
+ #region Viewport and image region methods
+ /// <summary>
+ /// Gets the source image region.
+ /// </summary>
+ /// <returns></returns>
+ public Rect GetSourceImageRegion()
+ {
+ var image = Image;
+ if (image is null) return Rect.Empty;
+
+ switch (SizeMode)
{
- var image = Image;
- if (image is null) return Rect.Empty;
+ case SizeModes.Normal:
+ var offset = Offset;
+ var viewPort = GetImageViewPort();
+ var zoomFactor = ZoomFactor;
+ double sourceLeft = (offset.X / zoomFactor);
+ double sourceTop = (offset.Y / zoomFactor);
+ double sourceWidth = (viewPort.Width / zoomFactor);
+ double sourceHeight = (viewPort.Height / zoomFactor);
- switch (SizeMode)
- {
- case SizeModes.Normal:
- var offset = Offset;
- var viewPort = GetImageViewPort();
- var zoomFactor = ZoomFactor;
- double sourceLeft = (offset.X / zoomFactor);
- double sourceTop = (offset.Y / zoomFactor);
- double sourceWidth = (viewPort.Width / zoomFactor);
- double sourceHeight = (viewPort.Height / zoomFactor);
-
- return new(sourceLeft, sourceTop, sourceWidth, sourceHeight);
- }
+ return new(sourceLeft, sourceTop, sourceWidth, sourceHeight);
+ }
- return new(0, 0, image.Size.Width, image.Size.Height);
+ return new(0, 0, image.Size.Width, image.Size.Height);
- }
+ }
- /// <summary>
- /// Gets the image view port.
- /// </summary>
- /// <returns></returns>
- public Rect GetImageViewPort()
- {
- if (!IsImageLoaded || (ViewPortSize.Width == 0 && ViewPortSize.Height == 0)) return Rect.Empty;
+ /// <summary>
+ /// Gets the image view port.
+ /// </summary>
+ /// <returns></returns>
+ public Rect GetImageViewPort()
+ {
+ if (!IsImageLoaded || (ViewPortSize.Width == 0 && ViewPortSize.Height == 0)) return Rect.Empty;
- double xOffset = 0;
- double yOffset = 0;
- double width = 0;
- double height = 0;
+ double xOffset = 0;
+ double yOffset = 0;
+ double width = 0;
+ double height = 0;
- switch (SizeMode)
- {
- case SizeModes.Normal:
- if (AutoCenter)
- {
- xOffset = (!IsHorizontalBarVisible ? (ViewPortSize.Width - ScaledImageWidth) / 2 : 0);
- yOffset = (!IsVerticalBarVisible ? (ViewPortSize.Height - ScaledImageHeight) / 2 : 0);
- }
-
- width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), ViewPortSize.Width);
- height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), ViewPortSize.Height);
- break;
- case SizeModes.Stretch:
- width = ViewPortSize.Width;
- height = ViewPortSize.Height;
- break;
- case SizeModes.Fit:
- var image = Image;
- double scaleFactor = Math.Min(ViewPortSize.Width / image.Size.Width, ViewPortSize.Height / image.Size.Height);
+ switch (SizeMode)
+ {
+ case SizeModes.Normal:
+ if (AutoCenter)
+ {
+ xOffset = (!IsHorizontalBarVisible ? (ViewPortSize.Width - ScaledImageWidth) / 2 : 0);
+ yOffset = (!IsVerticalBarVisible ? (ViewPortSize.Height - ScaledImageHeight) / 2 : 0);
+ }
+
+ width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), ViewPortSize.Width);
+ height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), ViewPortSize.Height);
+ break;
+ case SizeModes.Stretch:
+ width = ViewPortSize.Width;
+ height = ViewPortSize.Height;
+ break;
+ case SizeModes.Fit:
+ var image = Image;
+ double scaleFactor = Math.Min(ViewPortSize.Width / image!.Size.Width, ViewPortSize.Height / image.Size.Height);
- width = Math.Floor(image.Size.Width * scaleFactor);
- height = Math.Floor(image.Size.Height * scaleFactor);
-
- if (AutoCenter)
- {
- xOffset = (ViewPortSize.Width - width) / 2;
- yOffset = (ViewPortSize.Height - height) / 2;
- }
-
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
- }
+ width = Math.Floor(image.Size.Width * scaleFactor);
+ height = Math.Floor(image.Size.Height * scaleFactor);
- return new(xOffset, yOffset, width, height);
- }
- #endregion
+ if (AutoCenter)
+ {
+ xOffset = (ViewPortSize.Width - width) / 2;
+ yOffset = (ViewPortSize.Height - height) / 2;
+ }
- #region Image methods
- public void LoadImage(string path)
- {
- Image = new Bitmap(path);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
}
- public Bitmap GetSelectedBitmap()
- {
- var image = ImageAsWriteableBitmap;
- if (image is null || !HaveSelection) return null;
- var selection = SelectionRegionNet;
- var pixelSize = SelectionPixelSize;
- using var frameBuffer = image.Lock();
+ return new(xOffset, yOffset, width, height);
+ }
+ #endregion
- var newBitmap = new WriteableBitmap(pixelSize, image.Dpi, frameBuffer.Format, AlphaFormat.Unpremul);
- using var newFrameBuffer = newBitmap.Lock();
+ #region Image methods
+ public void LoadImage(string path)
+ {
+ Image = new Bitmap(path);
+ }
- int i = 0;
+ public Bitmap? GetSelectedBitmap()
+ {
+ var image = ImageAsWriteableBitmap;
+ if (image is null || !HaveSelection) return null;
+
+ var selection = SelectionRegionNet;
+ var pixelSize = SelectionPixelSize;
+ using var frameBuffer = image.Lock();
- unsafe
- {
- var inputPixels = (uint*) (void*) frameBuffer.Address;
- var targetPixels = (uint*) (void*) newFrameBuffer.Address;
+ var newBitmap = new WriteableBitmap(pixelSize, image.Dpi, frameBuffer.Format, AlphaFormat.Unpremul);
+ using var newFrameBuffer = newBitmap.Lock();
+
+ int i = 0;
- for (int y = selection.Y; y < selection.Bottom; y++)
+ unsafe
+ {
+ var inputPixels = (uint*) (void*) frameBuffer.Address;
+ var targetPixels = (uint*) (void*) newFrameBuffer.Address;
+
+ for (int y = selection.Y; y < selection.Bottom; y++)
+ {
+ var thisY = y * frameBuffer.Size.Width;
+ for (int x = selection.X; x < selection.Right; x++)
{
- var thisY = y * frameBuffer.Size.Width;
- for (int x = selection.X; x < selection.Right; x++)
- {
- targetPixels[i++] = inputPixels[thisY + x];
- }
+ targetPixels![i++] = inputPixels![thisY + x];
}
}
-
- return newBitmap;
}
- #endregion
+ return newBitmap;
}
-}
+ #endregion
+
+} \ No newline at end of file
diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
index 7ed8446..5974565 100644
--- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
+++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<Authors>Tiago Conceição</Authors>
<Company>PTRTECH</Company>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
@@ -14,6 +14,7 @@
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Description>AvaloniaUI Controls</Description>
<Version>1.0.1</Version>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -34,7 +35,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="0.10.12" />
+ <PackageReference Include="Avalonia" Version="0.10.13" />
</ItemGroup>
<ItemGroup>
diff --git a/UVtools.Core/About.cs b/UVtools.Core/About.cs
index 1bc41d5..afde01e 100644
--- a/UVtools.Core/About.cs
+++ b/UVtools.Core/About.cs
@@ -9,24 +9,23 @@
using System;
using System.Reflection;
-namespace UVtools.Core
+namespace UVtools.Core;
+
+public static class About
{
- public static class About
- {
- public const string Software = "UVtools";
- public static string SoftwareWithVersion => $"{Software} v{VersionStr}";
- public const string Author = "Tiago Conceição";
- public const string Company = "PTRTECH";
- public const string License = "GNU Affero General Public License v3.0 (AGPL)";
- public const string LicenseUrl = "https://github.com/sn4k3/UVtools/blob/master/LICENSE";
- public const string Website = "https://github.com/sn4k3/UVtools";
- public const string Donate = "https://paypal.me/SkillTournament";
- public const string Sponsor = "https://github.com/sponsors/sn4k3";
+ public const string Software = "UVtools";
+ public static string SoftwareWithVersion => $"{Software} v{VersionStr}";
+ public const string Author = "Tiago Conceição";
+ public const string Company = "PTRTECH";
+ public const string License = "GNU Affero General Public License v3.0 (AGPL)";
+ public const string LicenseUrl = "https://github.com/sn4k3/UVtools/blob/master/LICENSE";
+ public const string Website = "https://github.com/sn4k3/UVtools";
+ public const string Donate = "https://paypal.me/SkillTournament";
+ public const string Sponsor = "https://github.com/sponsors/sn4k3";
- public const string DemoFile = "UVtools_demo_file.sl1";
+ public const string DemoFile = "UVtools_demo_file.sl1";
- public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
- public static string VersionStr => Assembly.GetExecutingAssembly().GetName().Version.ToString(3);
- public static string Arch => Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
- }
-}
+ public static Version Version => Assembly.GetExecutingAssembly().GetName().Version!;
+ public static string VersionStr => Version.ToString(3);
+ public static string Arch => Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
+} \ No newline at end of file
diff --git a/UVtools.Core/Converters/NullTerminatedConverter.cs b/UVtools.Core/Converters/NullTerminatedConverter.cs
index 077e87f..e456bd6 100644
--- a/UVtools.Core/Converters/NullTerminatedConverter.cs
+++ b/UVtools.Core/Converters/NullTerminatedConverter.cs
@@ -7,18 +7,17 @@
*/
using BinarySerialization;
-namespace UVtools.Core.Converters
+namespace UVtools.Core.Converters;
+
+public class NullTerminatedConverter : IValueConverter
{
- public class NullTerminatedConverter : IValueConverter
+ public object Convert(object value, object converterParameter, BinarySerializationContext context)
{
- public object Convert(object value, object converterParameter, BinarySerializationContext context)
- {
- return value.ToString()?.TrimEnd(char.MinValue);
- }
+ return value.ToString()!.TrimEnd(char.MinValue);
+ }
- public object ConvertBack(object value, object converterParameter, BinarySerializationContext context)
- {
- return value is null ? null : $"{value}{char.MinValue}";
- }
+ public object ConvertBack(object value, object converterParameter, BinarySerializationContext context)
+ {
+ return $"{value}{char.MinValue}";
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Converters/NullTerminatedLengthConverter.cs b/UVtools.Core/Converters/NullTerminatedLengthConverter.cs
index d097e00..1397e54 100644
--- a/UVtools.Core/Converters/NullTerminatedLengthConverter.cs
+++ b/UVtools.Core/Converters/NullTerminatedLengthConverter.cs
@@ -7,25 +7,24 @@
*/
using BinarySerialization;
-namespace UVtools.Core.Converters
+namespace UVtools.Core.Converters;
+
+public class NullTerminatedLengthConverter : IValueConverter
{
- public class NullTerminatedLengthConverter : IValueConverter
+ // Read
+ public object Convert(object value, object converterParameter, BinarySerializationContext context)
{
- // Read
- public object Convert(object value, object converterParameter, BinarySerializationContext context)
- {
- //var uintValue = System.Convert.ToUInt32(value);
- //if (uintValue == 0) return 0;
- //return uintValue - 1;
- return value;
- }
+ //var uintValue = System.Convert.ToUInt32(value);
+ //if (uintValue == 0) return 0;
+ //return uintValue - 1;
+ return value;
+ }
- // Write
- public object ConvertBack(object value, object converterParameter, BinarySerializationContext context)
- {
- var uintValue = System.Convert.ToUInt32(value);
- if (uintValue == 0) return 0;
- return uintValue + 1;
- }
+ // Write
+ public object ConvertBack(object value, object converterParameter, BinarySerializationContext context)
+ {
+ var uintValue = System.Convert.ToUInt32(value);
+ if (uintValue == 0) return 0;
+ return uintValue + 1;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/CoreSettings.cs b/UVtools.Core/CoreSettings.cs
index 1301579..e7b54fb 100644
--- a/UVtools.Core/CoreSettings.cs
+++ b/UVtools.Core/CoreSettings.cs
@@ -6,48 +6,86 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV.Cuda;
using System;
+using System.Diagnostics;
+using System.IO;
using System.Threading.Tasks;
-using Emgu.CV.Cuda;
-namespace UVtools.Core
+namespace UVtools.Core;
+
+public static class CoreSettings
{
- public static class CoreSettings
- {
- #region Members
- private static int _maxDegreeOfParallelism = -1;
+ #region Members
+ private static int _maxDegreeOfParallelism = -1;
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- /// <summary>
- /// Gets or sets the maximum number of concurrent tasks enabled by this ParallelOptions instance.
- /// Less or equal to 0 will set to auto number
- /// 1 = Single thread
- /// n = Multi threads
- /// </summary>
- public static int MaxDegreeOfParallelism
- {
- get => _maxDegreeOfParallelism;
- set => _maxDegreeOfParallelism = value > 0 ? Math.Min(value, Environment.ProcessorCount) : -1;
- }
+ /// <summary>
+ /// Gets or sets the maximum number of concurrent tasks enabled by this ParallelOptions instance.
+ /// Less or equal to 0 will set to auto number
+ /// 1 = Single thread
+ /// n = Multi threads
+ /// </summary>
+ public static int MaxDegreeOfParallelism
+ {
+ get => _maxDegreeOfParallelism;
+ set => _maxDegreeOfParallelism = value > 0 ? Math.Min(value, Environment.ProcessorCount) : -1;
+ }
- /// <summary>
- /// Gets the ParallelOptions with <see cref="MaxDegreeOfParallelism"/> set
- /// </summary>
- public static ParallelOptions ParallelOptions => new() {MaxDegreeOfParallelism = _maxDegreeOfParallelism};
+ /// <summary>
+ /// Gets the ParallelOptions with <see cref="MaxDegreeOfParallelism"/> set
+ /// </summary>
+ public static ParallelOptions ParallelOptions => new() {MaxDegreeOfParallelism = _maxDegreeOfParallelism};
- /// <summary>
- /// Gets or sets if operations run via CUDA when possible
- /// </summary>
- public static bool EnableCuda { get; set; }
+ /// <summary>
+ /// Gets or sets if operations run via CUDA when possible
+ /// </summary>
+ public static bool EnableCuda { get; set; }
- /// <summary>
- /// Gets if we can use cuda on operations
- /// </summary>
- public static bool CanUseCuda => EnableCuda && CudaInvoke.HasCuda;
+ /// <summary>
+ /// Gets if we can use cuda on operations
+ /// </summary>
+ public static bool CanUseCuda => EnableCuda && CudaInvoke.HasCuda;
- #endregion
+ /// <summary>
+ /// Gets the default folder to save the settings
+ /// </summary>
+ public static string DefaultSettingsFolder
+ {
+ get
+ {
+ var folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
+ if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
+ var path = Path.Combine(folder, About.Software);
+ return path;
+ }
+ }
+
+ /// <summary>
+ /// Gets the default folder to save the settings
+ /// </summary>
+ public static string DefaultSettingsFolderAndEnsureCreation
+ {
+ get
+ {
+ var path = DefaultSettingsFolder;
+ try
+ {
+ if (!Directory.Exists(path))
+ Directory.CreateDirectory(path);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+ return path;
+ }
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/EmguCV/EmguContour.cs b/UVtools.Core/EmguCV/EmguContour.cs
index 9b43995..4ecfecb 100644
--- a/UVtools.Core/EmguCV/EmguContour.cs
+++ b/UVtools.Core/EmguCV/EmguContour.cs
@@ -6,256 +6,255 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Drawing;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
using UVtools.Core.Extensions;
-namespace UVtools.Core.EmguCV
+namespace UVtools.Core.EmguCV;
+
+/// <summary>
+/// A contour cache for OpenCV
+/// </summary>
+public class EmguContour : IReadOnlyCollection<Point>, IDisposable
{
- /// <summary>
- /// A contour cache for OpenCV
- /// </summary>
- public class EmguContour : IReadOnlyCollection<Point>, IDisposable
- {
- #region Constants
+ #region Constants
- public const byte HierarchyNextSameLevel = 0;
- public const byte HierarchyPreviousSameLevel = 1;
- public const byte HierarchyFirstChild = 2;
- public const byte HierarchyParent = 3;
+ public const byte HierarchyNextSameLevel = 0;
+ public const byte HierarchyPreviousSameLevel = 1;
+ public const byte HierarchyFirstChild = 2;
+ public const byte HierarchyParent = 3;
- #endregion
+ #endregion
- #region Members
+ #region Members
- private VectorOfPoint _points;
- private Rectangle? _bounds;
- private RotatedRect? _boundsBestFit;
- private CircleF? _minEnclosingCircle;
- private bool? _isConvex;
- private double _area = double.NaN;
- private double _perimeter = double.NaN;
- private Moments _moments;
- private Point? _centroid;
+ private VectorOfPoint _points = null!;
+ private Rectangle? _bounds;
+ private RotatedRect? _boundsBestFit;
+ private CircleF? _minEnclosingCircle;
+ private bool? _isConvex;
+ private double _area = double.NaN;
+ private double _perimeter = double.NaN;
+ private Moments? _moments;
+ private Point? _centroid;
- #endregion
+ #endregion
- #region Properties
- public int XMin => Bounds.X;
+ #region Properties
+ public int XMin => Bounds.X;
- public int YMin => Bounds.Y;
+ public int YMin => Bounds.Y;
- public int XMax => Bounds.Right;
+ public int XMax => Bounds.Right;
- public int YMax => Bounds.Bottom;
+ public int YMax => Bounds.Bottom;
- public Rectangle Bounds => _bounds ??= CvInvoke.BoundingRectangle(_points);
+ public Rectangle Bounds => _bounds ??= CvInvoke.BoundingRectangle(_points);
- public RotatedRect BoundsBestFit => _boundsBestFit ??= CvInvoke.MinAreaRect(_points);
+ public RotatedRect BoundsBestFit => _boundsBestFit ??= CvInvoke.MinAreaRect(_points);
- public CircleF MinEnclosingCircle => _minEnclosingCircle ??= CvInvoke.MinEnclosingCircle(_points);
+ public CircleF MinEnclosingCircle => _minEnclosingCircle ??= CvInvoke.MinEnclosingCircle(_points);
- public bool IsConvex => _isConvex ??= CvInvoke.IsContourConvex(_points);
+ public bool IsConvex => _isConvex ??= CvInvoke.IsContourConvex(_points);
- /// <summary>
- /// Gets the area of the contour
- /// </summary>
- public double Area
+ /// <summary>
+ /// Gets the area of the contour
+ /// </summary>
+ public double Area
+ {
+ get
{
- get
+ if (double.IsNaN(_area))
{
- if (double.IsNaN(_area))
- {
- _area = CvInvoke.ContourArea(_points);
- }
-
- return _area;
+ _area = CvInvoke.ContourArea(_points);
}
+
+ return _area;
}
+ }
- /// <summary>
- /// Gets the perimeter of the contours
- /// </summary>
- public double Perimeter
+ /// <summary>
+ /// Gets the perimeter of the contours
+ /// </summary>
+ public double Perimeter
+ {
+ get
{
- get
+ if (double.IsNaN(_perimeter))
{
- if (double.IsNaN(_perimeter))
- {
- _perimeter = CvInvoke.ArcLength(_points, true);
- }
- return _perimeter;
+ _perimeter = CvInvoke.ArcLength(_points, true);
}
+ return _perimeter;
}
+ }
- public Moments Moments => _moments ??= CvInvoke.Moments(_points);
+ public Moments Moments => _moments ??= CvInvoke.Moments(_points);
- /// <summary>
- /// Gets the centroid of the contour
- /// </summary>
- public Point Centroid => _centroid ??= Moments.M00 == 0 ? new Point(-1,-1) :
- new Point(
- (int)Math.Round(_moments.M10 / _moments.M00),
- (int)Math.Round(_moments.M01 / _moments.M00));
+ /// <summary>
+ /// Gets the centroid of the contour
+ /// </summary>
+ public Point Centroid => _centroid ??= Moments.M00 == 0 ? new Point(-1,-1) :
+ new Point(
+ (int)Math.Round(Moments.M10 / Moments.M00),
+ (int)Math.Round(Moments.M01 / Moments.M00));
- /// <summary>
- /// Gets or sets the contour <see cref="Point"/>
- /// </summary>
- public VectorOfPoint Points
+ /// <summary>
+ /// Gets or sets the contour <see cref="Point"/>
+ /// </summary>
+ public VectorOfPoint Points
+ {
+ get => _points;
+ set
{
- get => _points;
- set
- {
- Dispose();
- _points = value ?? throw new ArgumentNullException(nameof(Points));
- }
+ Dispose();
+ _points = value ?? throw new ArgumentNullException(nameof(Points));
}
+ }
- /// <summary>
- /// Gets if this contour have any point
- /// </summary>
- public bool IsEmpty => _points.Size == 0;
- #endregion
-
- #region Constructor
- public EmguContour(VectorOfPoint points) : this(points.ToArray())
- { }
-
- public EmguContour(Point[] points)
- {
- Points = new VectorOfPoint(points);
- }
- #endregion
-
- #region Methods
-
- /// <summary>
- /// Checks if a given <see cref="Point"/> is inside the contour rectangle bounds
- /// </summary>
- /// <param name="point"></param>
- /// <returns></returns>
- public bool IsInsideBounds(Point point) => Bounds.Contains(point);
-
- /// <summary>
- /// Gets if a given <see cref="Point"/> is inside the contour
- /// </summary>
- /// <param name="point"></param>
- /// <returns></returns>
- public bool IsInside(Point point)
- {
- if (!IsInsideBounds(point)) return false;
- return CvInvoke.PointPolygonTest(_points, point, false) >= 0;
- }
+ /// <summary>
+ /// Gets if this contour have any point
+ /// </summary>
+ public bool IsEmpty => _points.Size == 0;
+ #endregion
- public double MeasureDist(Point point)
- {
- if (!IsInsideBounds(point)) return -1;
- return CvInvoke.PointPolygonTest(_points, point, true);
- }
+ #region Constructor
+ public EmguContour(VectorOfPoint points) : this(points.ToArray())
+ { }
- public IOutputArray ContourApproximation(double epsilon = 0.1)
- {
- var mat = new Mat();
- CvInvoke.ApproxPolyDP(_points, mat, epsilon*Perimeter, true);
- return mat;
- }
+ public EmguContour(Point[] points)
+ {
+ Points = new VectorOfPoint(points);
+ }
+ #endregion
- /*
- /// <summary>
- /// Calculate the X/Y min/max boundary
- /// </summary>
- private void CalculateMinMax()
- {
- Bounds = Rectangle.Empty;
+ #region Methods
- if (_contourPoints.Length == 0)
- {
- _xMin = -1;
- _yMin = -1;
- _xMax = -1;
- _yMax = -1;
- return;
- }
+ /// <summary>
+ /// Checks if a given <see cref="Point"/> is inside the contour rectangle bounds
+ /// </summary>
+ /// <param name="point"></param>
+ /// <returns></returns>
+ public bool IsInsideBounds(Point point) => Bounds.Contains(point);
- _xMin = int.MaxValue;
- _yMin = int.MaxValue;
- _xMax = int.MinValue;
- _yMax = int.MinValue;
+ /// <summary>
+ /// Gets if a given <see cref="Point"/> is inside the contour
+ /// </summary>
+ /// <param name="point"></param>
+ /// <returns></returns>
+ public bool IsInside(Point point)
+ {
+ if (!IsInsideBounds(point)) return false;
+ return CvInvoke.PointPolygonTest(_points, point, false) >= 0;
+ }
- for (int i = 0; i < _contourPoints.Length; i++)
- {
- _xMin = Math.Min(_xMin, _contourPoints[i].X);
- _yMin = Math.Min(_yMin, _contourPoints[i].Y);
+ public double MeasureDist(Point point)
+ {
+ if (!IsInsideBounds(point)) return -1;
+ return CvInvoke.PointPolygonTest(_points, point, true);
+ }
- _xMax = Math.Max(_xMax, _contourPoints[i].X);
- _yMax = Math.Max(_yMax, _contourPoints[i].Y);
- }
+ public IOutputArray ContourApproximation(double epsilon = 0.1)
+ {
+ var mat = new Mat();
+ CvInvoke.ApproxPolyDP(_points, mat, epsilon*Perimeter, true);
+ return mat;
+ }
- Bounds = new Rectangle(_xMin, _yMin, _xMax - _xMin, _yMax - _yMin);
- }
- */
+ /*
+ /// <summary>
+ /// Calculate the X/Y min/max boundary
+ /// </summary>
+ private void CalculateMinMax()
+ {
+ Bounds = Rectangle.Empty;
- public void FitCircle(Mat src, MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected, int shift = 0)
+ if (_contourPoints.Length == 0)
{
- CvInvoke.Circle(src,
- MinEnclosingCircle.Center.ToPoint(),
- (int) Math.Round(MinEnclosingCircle.Radius),
- color,
- thickness,
- lineType,
- shift);
+ _xMin = -1;
+ _yMin = -1;
+ _xMax = -1;
+ _yMax = -1;
+ return;
}
- /*public void FitEllipse(Mat src, MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected, int shift = 0)
- {
- var ellipse = CvInvoke.FitEllipse(_points);
- CvInvoke.Ellipse(src, ellipse.Center.ToPoint(), ellipse.Size.ToSize(), ellipse.Angle, 0, 0);
- }*/
- #endregion
+ _xMin = int.MaxValue;
+ _yMin = int.MaxValue;
+ _xMax = int.MinValue;
+ _yMax = int.MinValue;
- #region Static methods
- public static Point GetCentroid(VectorOfPoint points)
+ for (int i = 0; i < _contourPoints.Length; i++)
{
- if (points is null || points.Length == 0) return new Point(-1, -1);
- using var moments = CvInvoke.Moments(points);
- return moments.M00 == 0 ? new Point(-1, -1) :
- new Point(
- (int)Math.Round(moments.M10 / moments.M00),
- (int)Math.Round(moments.M01 / moments.M00));
+ _xMin = Math.Min(_xMin, _contourPoints[i].X);
+ _yMin = Math.Min(_yMin, _contourPoints[i].Y);
+
+ _xMax = Math.Max(_xMax, _contourPoints[i].X);
+ _yMax = Math.Max(_yMax, _contourPoints[i].Y);
}
- #endregion
- #region Implementations
+ Bounds = new Rectangle(_xMin, _yMin, _xMax - _xMin, _yMax - _yMin);
+ }
+ */
- public IEnumerator<Point> GetEnumerator()
- {
- return (IEnumerator<Point>) _points.ToArray().GetEnumerator();
- }
+ public void FitCircle(Mat src, MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected, int shift = 0)
+ {
+ CvInvoke.Circle(src,
+ MinEnclosingCircle.Center.ToPoint(),
+ (int) Math.Round(MinEnclosingCircle.Radius),
+ color,
+ thickness,
+ lineType,
+ shift);
+ }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ /*public void FitEllipse(Mat src, MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected, int shift = 0)
+ {
+ var ellipse = CvInvoke.FitEllipse(_points);
+ CvInvoke.Ellipse(src, ellipse.Center.ToPoint(), ellipse.Size.ToSize(), ellipse.Angle, 0, 0);
+ }*/
+ #endregion
+
+ #region Static methods
+ public static Point GetCentroid(VectorOfPoint points)
+ {
+ if (points is null || points.Length == 0) return new Point(-1, -1);
+ using var moments = CvInvoke.Moments(points);
+ return moments.M00 == 0 ? new Point(-1, -1) :
+ new Point(
+ (int)Math.Round(moments.M10 / moments.M00),
+ (int)Math.Round(moments.M01 / moments.M00));
+ }
+ #endregion
- public int Count => _points.Size;
+ #region Implementations
- public Point this[int index] => _points[index];
- public Point this[uint index] => _points[(int) index];
- public Point this[long index] => _points[(int) index];
- public Point this[ulong index] => _points[(int) index];
+ public IEnumerator<Point> GetEnumerator()
+ {
+ return (IEnumerator<Point>) _points.ToArray().GetEnumerator();
+ }
- public void Dispose()
- {
- _points?.Dispose();
- _moments?.Dispose();
- }
- #endregion
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public int Count => _points.Size;
+
+ public Point this[int index] => _points[index];
+ public Point this[uint index] => _points[(int) index];
+ public Point this[long index] => _points[(int) index];
+ public Point this[ulong index] => _points[(int) index];
+
+ public void Dispose()
+ {
+ _points?.Dispose();
+ _moments?.Dispose();
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/EmguCV/EmguContours.cs b/UVtools.Core/EmguCV/EmguContours.cs
index 3c15b56..958cfd8 100644
--- a/UVtools.Core/EmguCV/EmguContours.cs
+++ b/UVtools.Core/EmguCV/EmguContours.cs
@@ -6,246 +6,245 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.Util;
using System.Collections.Generic;
using System.Drawing;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.Util;
using UVtools.Core.Extensions;
-namespace UVtools.Core.EmguCV
+namespace UVtools.Core.EmguCV;
+
+/// <summary>
+/// Utility methods for contour handling.
+/// Use only with Tree type
+/// </summary>
+public static class EmguContours
{
/// <summary>
- /// Utility methods for contour handling.
- /// Use only with Tree type
+ /// Gets contours inside a point
/// </summary>
- public static class EmguContours
+ /// <param name="contours"></param>
+ /// <param name="hierarchy"></param>
+ /// <param name="location"></param>
+ /// <param name="includeLimitingArea">If true it will include all limiting area, otherwise only outer contour will be returned</param>
+ /// <returns></returns>
+ public static VectorOfVectorOfPoint GetContoursInside(VectorOfVectorOfPoint contours, int[,] hierarchy, Point location, bool includeLimitingArea = true)
{
- /// <summary>
- /// Gets contours inside a point
- /// </summary>
- /// <param name="contours"></param>
- /// <param name="hierarchy"></param>
- /// <param name="location"></param>
- /// <param name="includeLimitingArea">If true it will include all limiting area, otherwise only outer contour will be returned</param>
- /// <returns></returns>
- public static VectorOfVectorOfPoint GetContoursInside(VectorOfVectorOfPoint contours, int[,] hierarchy, Point location, bool includeLimitingArea = true)
+ var vector = new VectorOfVectorOfPoint();
+ var vectorSize = contours.Size;
+ for (var i = vectorSize - 1; i >= 0; i--)
{
- var vector = new VectorOfVectorOfPoint();
- var vectorSize = contours.Size;
- for (var i = vectorSize - 1; i >= 0; i--)
+ if (CvInvoke.PointPolygonTest(contours[i], location, false) < 0) continue;
+ vector.Push(contours[i]);
+ if (!includeLimitingArea) break;
+ for (int n = i + 1; n < vectorSize; n++)
{
- if (CvInvoke.PointPolygonTest(contours[i], location, false) < 0) continue;
- vector.Push(contours[i]);
- if (!includeLimitingArea) break;
- for (int n = i + 1; n < vectorSize; n++)
- {
- if (hierarchy[n, EmguContour.HierarchyParent] != i) continue;
- vector.Push(contours[n]);
- }
- break;
+ if (hierarchy[n, EmguContour.HierarchyParent] != i) continue;
+ vector.Push(contours[n]);
}
-
- return vector;
+ break;
}
- /// <summary>
- /// Gets a contour given a location.
- /// </summary>
- /// <param name="contours"></param>
- /// <param name="location"></param>
- /// <param name="index">Contour index, -1 if not exists</param>
- /// <returns>null if not exists</returns>
- public static VectorOfPoint GetContourInside(VectorOfVectorOfPoint contours, Point location, out int index)
- {
- index = -1;
- var vectorSize = contours.Size;
- for (int i = vectorSize - 1; i >= 0; i--)
- {
- if (CvInvoke.PointPolygonTest(contours[i], location, false) < 0) continue;
- index = i;
- return contours[i];
- }
-
- return null;
- }
+ return vector;
+ }
- /// <summary>
- /// Gets only the outer most external contours
- /// Only compatible with Tree type of contour detection
- /// </summary>
- /// <param name="contours"></param>
- /// <param name="hierarchy"></param>
- /// <returns></returns>
- public static VectorOfVectorOfPoint GetExternalContours(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ /// <summary>
+ /// Gets a contour given a location.
+ /// </summary>
+ /// <param name="contours"></param>
+ /// <param name="location"></param>
+ /// <param name="index">Contour index, -1 if not exists</param>
+ /// <returns>null if not exists</returns>
+ public static VectorOfPoint? GetContourInside(VectorOfVectorOfPoint contours, Point location, out int index)
+ {
+ index = -1;
+ var vectorSize = contours.Size;
+ for (int i = vectorSize - 1; i >= 0; i--)
{
- var result = new VectorOfVectorOfPoint();
- var vectorSize = contours.Size;
- for (var i = 0; i < vectorSize; i++)
- {
- if (hierarchy[i, EmguContour.HierarchyParent] != -1) continue;
- result.Push(contours[i]);
- }
-
- return result;
+ if (CvInvoke.PointPolygonTest(contours[i], location, false) < 0) continue;
+ index = i;
+ return contours[i];
}
- /// <summary>
- /// Gets contours inside contours that are black pixels
- /// </summary>
- /// <param name="contours"></param>
- /// <param name="hierarchy"></param>
- /// <returns></returns>
- public static VectorOfVectorOfPoint GetNegativeContours(VectorOfVectorOfPoint contours, int[,] hierarchy)
- {
- var result = new VectorOfVectorOfPoint();
- var vectorSize = contours.Size;
- for (var i = 0; i < vectorSize; i++)
- {
- if (hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
- result.Push(contours[i]);
- }
+ return null;
+ }
- return result;
+ /// <summary>
+ /// Gets only the outer most external contours
+ /// Only compatible with Tree type of contour detection
+ /// </summary>
+ /// <param name="contours"></param>
+ /// <param name="hierarchy"></param>
+ /// <returns></returns>
+ public static VectorOfVectorOfPoint GetExternalContours(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ {
+ var result = new VectorOfVectorOfPoint();
+ var vectorSize = contours.Size;
+ for (var i = 0; i < vectorSize; i++)
+ {
+ if (hierarchy[i, EmguContour.HierarchyParent] != -1) continue;
+ result.Push(contours[i]);
}
- /// <summary>
- /// Gets contours that are positive and negative pixels and group them by areas
- /// Only compatible with Tree type of contour detection
- /// </summary>
- /// <returns></returns>
- public static List<VectorOfVectorOfPoint>[] GetContoursInGroups(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ return result;
+ }
+
+ /// <summary>
+ /// Gets contours inside contours that are black pixels
+ /// </summary>
+ /// <param name="contours"></param>
+ /// <param name="hierarchy"></param>
+ /// <returns></returns>
+ public static VectorOfVectorOfPoint GetNegativeContours(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ {
+ var result = new VectorOfVectorOfPoint();
+ var vectorSize = contours.Size;
+ for (var i = 0; i < vectorSize; i++)
{
- return new []{GetPositiveContoursInGroups(contours, hierarchy), GetNegativeContoursInGroups(contours, hierarchy)};
+ if (hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
+ result.Push(contours[i]);
}
- /// <summary>
- /// Gets contours that are positive pixels and group them by areas
- /// Only compatible with Tree type of contour detection
- /// </summary>
- /// <returns></returns>
- public static List<VectorOfVectorOfPoint> GetPositiveContoursInGroups(VectorOfVectorOfPoint contours, int[,] hierarchy)
- {
- var result = new List<VectorOfVectorOfPoint>();
- var vectorSize = contours.Size;
- var processedContours = new bool[vectorSize];
- for (int i = 0; i < vectorSize; i++)
- {
- if (processedContours[i]) continue;
- processedContours[i] = true;
- var index = result.Count;
- result.Add(new VectorOfVectorOfPoint(contours[i]));
- for (int n = i + 1; n < vectorSize; n++)
- {
- if (processedContours[n] || hierarchy[n, EmguContour.HierarchyParent] != i) continue;
- processedContours[n] = true;
- result[index].Push(contours[n]);
- }
- }
+ return result;
+ }
- return result;
- }
+ /// <summary>
+ /// Gets contours that are positive and negative pixels and group them by areas
+ /// Only compatible with Tree type of contour detection
+ /// </summary>
+ /// <returns></returns>
+ public static List<VectorOfVectorOfPoint>[] GetContoursInGroups(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ {
+ return new []{GetPositiveContoursInGroups(contours, hierarchy), GetNegativeContoursInGroups(contours, hierarchy)};
+ }
- /// <summary>
- /// Gets contours inside contours that are black pixels and group them by areas
- /// Only compatible with Tree type of contour detection
- /// </summary>
- /// <returns></returns>
- public static List<VectorOfVectorOfPoint> GetNegativeContoursInGroups(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ /// <summary>
+ /// Gets contours that are positive pixels and group them by areas
+ /// Only compatible with Tree type of contour detection
+ /// </summary>
+ /// <returns></returns>
+ public static List<VectorOfVectorOfPoint> GetPositiveContoursInGroups(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ {
+ var result = new List<VectorOfVectorOfPoint>();
+ var vectorSize = contours.Size;
+ var processedContours = new bool[vectorSize];
+ for (int i = 0; i < vectorSize; i++)
{
- var result = new List<VectorOfVectorOfPoint>();
- var vectorSize = contours.Size;
- var processedContours = new bool[vectorSize];
- for (int i = 0; i < vectorSize; i++)
+ if (processedContours[i]) continue;
+ processedContours[i] = true;
+ var index = result.Count;
+ result.Add(new VectorOfVectorOfPoint(contours[i]));
+ for (int n = i + 1; n < vectorSize; n++)
{
- if (processedContours[i]) continue;
- processedContours[i] = true;
- if (hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
- var index = result.Count;
- result.Add(new VectorOfVectorOfPoint(contours[i]));
- for (int n = i + 1; n < vectorSize; n++)
- {
- if (processedContours[n] || hierarchy[n, EmguContour.HierarchyParent] != i) continue;
- processedContours[n] = true;
- result[index].Push(contours[n]);
- }
+ if (processedContours[n] || hierarchy[n, EmguContour.HierarchyParent] != i) continue;
+ processedContours[n] = true;
+ result[index].Push(contours[n]);
}
-
- return result;
}
- /// <summary>
- /// Gets contour real area for a limited area
- /// </summary>
- /// <param name="contours"></param>
- /// <returns></returns>
- public static double GetContourArea(VectorOfVectorOfPoint contours)
- {
- var vectorSize = contours.Size;
- if (vectorSize == 0) return 0;
+ return result;
+ }
- double result = CvInvoke.ContourArea(contours[0]);
- for (var i = 1; i < vectorSize; i++)
+ /// <summary>
+ /// Gets contours inside contours that are black pixels and group them by areas
+ /// Only compatible with Tree type of contour detection
+ /// </summary>
+ /// <returns></returns>
+ public static List<VectorOfVectorOfPoint> GetNegativeContoursInGroups(VectorOfVectorOfPoint contours, int[,] hierarchy)
+ {
+ var result = new List<VectorOfVectorOfPoint>();
+ var vectorSize = contours.Size;
+ var processedContours = new bool[vectorSize];
+ for (int i = 0; i < vectorSize; i++)
+ {
+ if (processedContours[i]) continue;
+ processedContours[i] = true;
+ if (hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
+ var index = result.Count;
+ result.Add(new VectorOfVectorOfPoint(contours[i]));
+ for (int n = i + 1; n < vectorSize; n++)
{
- result -= CvInvoke.ContourArea(contours[i]);
+ if (processedContours[n] || hierarchy[n, EmguContour.HierarchyParent] != i) continue;
+ processedContours[n] = true;
+ result[index].Push(contours[n]);
}
- return result;
}
- /// <summary>
- /// Gets contours real area for a group of contours
- /// </summary>
- /// <param name="contours">Grouped contours</param>
- /// <param name="useParallel">True to run in parallel</param>
- /// <returns>Array with same size with contours area</returns>
- public static double[] GetContoursArea(List<VectorOfVectorOfPoint> contours, bool useParallel = false)
+ return result;
+ }
+
+ /// <summary>
+ /// Gets contour real area for a limited area
+ /// </summary>
+ /// <param name="contours"></param>
+ /// <returns></returns>
+ public static double GetContourArea(VectorOfVectorOfPoint contours)
+ {
+ var vectorSize = contours.Size;
+ if (vectorSize == 0) return 0;
+
+ double result = CvInvoke.ContourArea(contours[0]);
+ for (var i = 1; i < vectorSize; i++)
{
- var result = new double[contours.Count];
+ result -= CvInvoke.ContourArea(contours[i]);
+ }
+ return result;
+ }
- if (useParallel)
+ /// <summary>
+ /// Gets contours real area for a group of contours
+ /// </summary>
+ /// <param name="contours">Grouped contours</param>
+ /// <param name="useParallel">True to run in parallel</param>
+ /// <returns>Array with same size with contours area</returns>
+ public static double[] GetContoursArea(List<VectorOfVectorOfPoint> contours, bool useParallel = false)
+ {
+ var result = new double[contours.Count];
+
+ if (useParallel)
+ {
+ Parallel.For(0, contours.Count, CoreSettings.ParallelOptions, i =>
{
- Parallel.For(0, contours.Count, CoreSettings.ParallelOptions, i =>
- {
- result[i] = GetContourArea(contours[i]);
- });
- }
- else
+ result[i] = GetContourArea(contours[i]);
+ });
+ }
+ else
+ {
+ for (var i = 0; i < contours.Count; i++)
{
- for (var i = 0; i < contours.Count; i++)
- {
- result[i] = GetContourArea(contours[i]);
- }
+ result[i] = GetContourArea(contours[i]);
}
-
- return result;
}
+
+ return result;
+ }
- /// <summary>
- /// Checks if two contours intersects
- /// </summary>
- /// <param name="contour1">Contour 1</param>
- /// <param name="contour2">Contour 2</param>
- /// <returns></returns>
- public static bool ContoursIntersect(VectorOfVectorOfPoint contour1, VectorOfVectorOfPoint contour2)
- {
- var contour1Rect = CvInvoke.BoundingRectangle(contour1[0]);
- var contour2Rect = CvInvoke.BoundingRectangle(contour2[0]);
+ /// <summary>
+ /// Checks if two contours intersects
+ /// </summary>
+ /// <param name="contour1">Contour 1</param>
+ /// <param name="contour2">Contour 2</param>
+ /// <returns></returns>
+ public static bool ContoursIntersect(VectorOfVectorOfPoint contour1, VectorOfVectorOfPoint contour2)
+ {
+ var contour1Rect = CvInvoke.BoundingRectangle(contour1[0]);
+ var contour2Rect = CvInvoke.BoundingRectangle(contour2[0]);
- /* early exit if the bounding rectangles don't intersect */
- if (!contour1Rect.IntersectsWith(contour2Rect)) return false;
- var totalRect = Rectangle.Union(contour1Rect, contour2Rect);
+ /* early exit if the bounding rectangles don't intersect */
+ if (!contour1Rect.IntersectsWith(contour2Rect)) return false;
+ var totalRect = Rectangle.Union(contour1Rect, contour2Rect);
- using var contour1Mat = EmguExtensions.InitMat(totalRect.Size);
- using var contour2Mat = EmguExtensions.InitMat(totalRect.Size);
+ using var contour1Mat = EmguExtensions.InitMat(totalRect.Size);
+ using var contour2Mat = EmguExtensions.InitMat(totalRect.Size);
- var inverseOffset = new Point(-totalRect.X, -totalRect.Y);
- CvInvoke.DrawContours(contour1Mat, contour1, -1, EmguExtensions.WhiteColor, -1, Emgu.CV.CvEnum.LineType.EightConnected, null, int.MaxValue, inverseOffset);
- CvInvoke.DrawContours(contour2Mat, contour2, -1, EmguExtensions.WhiteColor, -1, Emgu.CV.CvEnum.LineType.EightConnected, null, int.MaxValue, inverseOffset);
+ var inverseOffset = new Point(-totalRect.X, -totalRect.Y);
+ CvInvoke.DrawContours(contour1Mat, contour1, -1, EmguExtensions.WhiteColor, -1, Emgu.CV.CvEnum.LineType.EightConnected, null, int.MaxValue, inverseOffset);
+ CvInvoke.DrawContours(contour2Mat, contour2, -1, EmguExtensions.WhiteColor, -1, Emgu.CV.CvEnum.LineType.EightConnected, null, int.MaxValue, inverseOffset);
- CvInvoke.BitwiseAnd(contour1Mat, contour2Mat, contour1Mat);
+ CvInvoke.BitwiseAnd(contour1Mat, contour2Mat, contour1Mat);
- //return !contour1Mat.IsZeroed();
- return CvInvoke.CountNonZero(contour1Mat) > 0;
- }
+ //return !contour1Mat.IsZeroed();
+ return CvInvoke.CountNonZero(contour1Mat) > 0;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Enumerations.cs b/UVtools.Core/Enumerations.cs
index 98be0a5..c7b9a96 100644
--- a/UVtools.Core/Enumerations.cs
+++ b/UVtools.Core/Enumerations.cs
@@ -6,93 +6,92 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV.CvEnum;
using System;
using System.ComponentModel;
-using Emgu.CV.CvEnum;
-namespace UVtools.Core
+namespace UVtools.Core;
+
+public class Enumerations
{
- public class Enumerations
+ public enum LayerRangeSelection : byte
{
- public enum LayerRangeSelection : byte
- {
- None,
- All,
- Current,
- Bottom,
- Normal,
- First,
- Last
- }
+ None,
+ All,
+ Current,
+ Bottom,
+ Normal,
+ First,
+ Last
+ }
- public enum FlipDirection : byte
- {
- None,
- Horizontally,
- Vertically,
- Both,
- }
+ public enum FlipDirection : byte
+ {
+ None,
+ Horizontally,
+ Vertically,
+ Both,
+ }
- public enum RotateDirection : sbyte
- {
- [Description("None")]
- None = -1,
- /// <summary>Rotate 90 degrees clockwise (0)</summary>
- [Description("Rotate 90º CW")]
- Rotate90Clockwise = 0,
- /// <summary>Rotate 180 degrees clockwise (1)</summary>
- [Description("Rotate 180º")]
- Rotate180 = 1,
- /// <summary>Rotate 270 degrees clockwise (2)</summary>
- [Description("Rotate 90º CCW")]
- Rotate90CounterClockwise = 2,
- }
+ public enum RotateDirection : sbyte
+ {
+ [Description("None")]
+ None = -1,
+ /// <summary>Rotate 90 degrees clockwise (0)</summary>
+ [Description("Rotate 90º CW")]
+ Rotate90Clockwise = 0,
+ /// <summary>Rotate 180 degrees clockwise (1)</summary>
+ [Description("Rotate 180º")]
+ Rotate180 = 1,
+ /// <summary>Rotate 270 degrees clockwise (2)</summary>
+ [Description("Rotate 90º CCW")]
+ Rotate90CounterClockwise = 2,
+ }
- public enum Anchor : byte
- {
- TopLeft, TopCenter, TopRight,
- MiddleLeft, MiddleCenter, MiddleRight,
- BottomLeft, BottomCenter, BottomRight,
- None
- }
+ public enum Anchor : byte
+ {
+ TopLeft, TopCenter, TopRight,
+ MiddleLeft, MiddleCenter, MiddleRight,
+ BottomLeft, BottomCenter, BottomRight,
+ None
+ }
- public enum LightOffDelaySetMode : byte
- {
- [Description("Set the light-off with an extra delay")]
- UpdateWithExtraDelay,
+ public enum LightOffDelaySetMode : byte
+ {
+ [Description("Set the light-off with an extra delay")]
+ UpdateWithExtraDelay,
- [Description("Set the light-off without an extra delay")]
- UpdateWithoutExtraDelay,
+ [Description("Set the light-off without an extra delay")]
+ UpdateWithoutExtraDelay,
- [Description("Set the light-off to zero")]
- SetToZero,
+ [Description("Set the light-off to zero")]
+ SetToZero,
- [Description("Disabled")]
- NoAction
- }
+ [Description("Disabled")]
+ NoAction
+ }
- public static FlipType ToOpenCVFlipType(FlipDirection flip)
+ public static FlipType ToOpenCVFlipType(FlipDirection flip)
+ {
+ return flip switch
{
- return flip switch
- {
- FlipDirection.None => throw new NotSupportedException($"Flip type: {flip} is not supported by OpenCV."),
- FlipDirection.Horizontally => FlipType.Horizontal,
- FlipDirection.Vertically => FlipType.Vertical,
- FlipDirection.Both => FlipType.Both,
- _ => throw new ArgumentOutOfRangeException(nameof(flip), flip, null)
- };
- }
+ FlipDirection.None => throw new NotSupportedException($"Flip type: {flip} is not supported by OpenCV."),
+ FlipDirection.Horizontally => FlipType.Horizontal,
+ FlipDirection.Vertically => FlipType.Vertical,
+ FlipDirection.Both => FlipType.Both,
+ _ => throw new ArgumentOutOfRangeException(nameof(flip), flip, null)
+ };
+ }
- public static RotateFlags ToOpenCVRotateFlags(RotateDirection rotate)
+ public static RotateFlags ToOpenCVRotateFlags(RotateDirection rotate)
+ {
+ return rotate switch
{
- return rotate switch
- {
- RotateDirection.None => throw new NotSupportedException($"Rotate direction: {rotate} is not supported by OpenCV."),
- RotateDirection.Rotate90Clockwise => RotateFlags.Rotate90Clockwise,
- RotateDirection.Rotate90CounterClockwise => RotateFlags.Rotate90CounterClockwise,
- RotateDirection.Rotate180 => RotateFlags.Rotate180,
- _ => throw new ArgumentOutOfRangeException(nameof(rotate), rotate, null)
- };
- }
+ RotateDirection.None => throw new NotSupportedException($"Rotate direction: {rotate} is not supported by OpenCV."),
+ RotateDirection.Rotate90Clockwise => RotateFlags.Rotate90Clockwise,
+ RotateDirection.Rotate90CounterClockwise => RotateFlags.Rotate90CounterClockwise,
+ RotateDirection.Rotate180 => RotateFlags.Rotate180,
+ _ => throw new ArgumentOutOfRangeException(nameof(rotate), rotate, null)
+ };
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/BitExtensions.cs b/UVtools.Core/Extensions/BitExtensions.cs
index 8efe29c..fdbfbb4 100644
--- a/UVtools.Core/Extensions/BitExtensions.cs
+++ b/UVtools.Core/Extensions/BitExtensions.cs
@@ -5,154 +5,153 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class BitExtensions
{
- public static class BitExtensions
- {
- public static ushort ToUShortLittleEndian(byte byte1, byte byte2) => (ushort)(byte1 + (byte2 << 8));
- public static ushort ToUShortBigEndian(byte byte1, byte byte2) => (ushort)((byte1 << 8) + byte2);
-
- public static ushort ToUShortLittleEndian(byte[] buffer, int offset = 0)
- => (ushort)(buffer[offset] + (buffer[offset+1] << 8));
- public static ushort ToUShortBigEndian(byte[] buffer, int offset = 0)
- => (ushort)((buffer[offset] << 8) + buffer[offset+1]);
-
- public static uint ToUIntLittleEndian(byte byte1, byte byte2, byte byte3, byte byte4)
- => (uint)(byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24));
- public static uint ToUIntBigEndian(byte byte1, byte byte2, byte byte3, byte byte4)
- => (uint)((byte1 << 24) + (byte2 << 16) + (byte3 << 8) + byte4);
-
- public static uint ToUIntLittleEndian(byte[] buffer, int offset = 0)
- => (uint)(buffer[offset] + (buffer[offset + 1] << 8) + (buffer[offset + 2] << 16) + (buffer[offset + 3] << 24));
- public static uint ToUIntBigEndian(byte[] buffer, int offset = 0)
- => (uint)((buffer[offset] << 24) + (buffer[offset+1] << 16) + (buffer[offset+2] << 8) + buffer[offset+3]);
-
- public static byte[] ToBytesLittleEndian(ushort value)
- {
- var bytes = new byte[2];
- ToBytesLittleEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesLittleEndian(ushort value, byte[] buffer, uint offset = 0)
- {
- buffer[offset] = (byte)value;
- buffer[offset + 1] = (byte)(value >> 8);
- }
-
- public static byte[] ToBytesBigEndian(ushort value)
- {
- var bytes = new byte[2];
- ToBytesBigEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesBigEndian(ushort value, byte[] buffer, uint offset = 0)
- {
- buffer[offset] = (byte)(value >> 8);
- buffer[offset + 1] = (byte)value;
- }
-
- public static byte[] ToBytesLittleEndian(uint value)
- {
- var bytes = new byte[4];
- ToBytesLittleEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesLittleEndian(uint value, byte[] buffer, uint offset = 0)
- {
- buffer[offset] = (byte)value;
- buffer[offset + 1] = (byte)(value >> 8);
- buffer[offset + 2] = (byte)(value >> 16);
- buffer[offset + 3] = (byte)(value >> 24);
- }
-
- public static byte[] ToBytesBigEndian(uint value)
- {
- var bytes = new byte[4];
- ToBytesBigEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesBigEndian(uint value, byte[] buffer, uint offset = 0)
- {
- buffer[offset] = (byte)(value >> 24);
- buffer[offset + 1] = (byte)(value >> 16);
- buffer[offset + 2] = (byte)(value >> 8);
- buffer[offset + 3] = (byte)value;
- }
-
- public static byte[] ToBytesLittleEndian(int value)
- {
- var bytes = new byte[4];
- ToBytesLittleEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesLittleEndian(int value, byte[] buffer, uint offset = 0)
- {
- buffer[offset] = (byte)value;
- buffer[offset + 1] = (byte)(value >> 8);
- buffer[offset + 2] = (byte)(value >> 16);
- buffer[offset + 3] = (byte)(value >> 24);
- }
-
- public static byte[] ToBytesBigEndian(int value)
- {
- var bytes = new byte[4];
- ToBytesBigEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesBigEndian(int value, byte[] buffer, uint offset = 0)
- {
- buffer[offset] = (byte)(value >> 24);
- buffer[offset + 1] = (byte)(value >> 16);
- buffer[offset + 2] = (byte)(value >> 8);
- buffer[offset + 3] = (byte)value;
- }
-
-
- public static byte[] ToBytesLittleEndian(ulong value)
- {
- var bytes = new byte[8];
- ToBytesLittleEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesLittleEndian(ulong value, byte[] buffer, ulong offset = 0)
- {
- buffer[offset] = (byte)value;
- buffer[offset + 1] = (byte)(value >> 8);
- buffer[offset + 2] = (byte)(value >> 16);
- buffer[offset + 3] = (byte)(value >> 24);
-
- buffer[offset + 4] = (byte)(value >> 32);
- buffer[offset + 5] = (byte)(value >> 40);
- buffer[offset + 6] = (byte)(value >> 48);
- buffer[offset + 7] = (byte)(value >> 56);
- }
-
- public static byte[] ToBytesBigEndian(ulong value)
- {
- var bytes = new byte[8];
- ToBytesBigEndian(value, bytes);
- return bytes;
- }
-
- public static void ToBytesBigEndian(ulong value, byte[] buffer, ulong offset = 0)
- {
- buffer[offset] = (byte)(value >> 56);
- buffer[offset + 1] = (byte)(value >> 48);
- buffer[offset + 2] = (byte)(value >> 40);
- buffer[offset + 3] = (byte)(value >> 32);
- buffer[offset + 4] = (byte)(value >> 24);
- buffer[offset + 5] = (byte)(value >> 16);
- buffer[offset + 6] = (byte)(value >> 8);
- buffer[offset + 7] = (byte)value;
- }
-
-
- }
-}
+ public static ushort ToUShortLittleEndian(byte byte1, byte byte2) => (ushort)(byte1 + (byte2 << 8));
+ public static ushort ToUShortBigEndian(byte byte1, byte byte2) => (ushort)((byte1 << 8) + byte2);
+
+ public static ushort ToUShortLittleEndian(byte[] buffer, int offset = 0)
+ => (ushort)(buffer[offset] + (buffer[offset+1] << 8));
+ public static ushort ToUShortBigEndian(byte[] buffer, int offset = 0)
+ => (ushort)((buffer[offset] << 8) + buffer[offset+1]);
+
+ public static uint ToUIntLittleEndian(byte byte1, byte byte2, byte byte3, byte byte4)
+ => (uint)(byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24));
+ public static uint ToUIntBigEndian(byte byte1, byte byte2, byte byte3, byte byte4)
+ => (uint)((byte1 << 24) + (byte2 << 16) + (byte3 << 8) + byte4);
+
+ public static uint ToUIntLittleEndian(byte[] buffer, int offset = 0)
+ => (uint)(buffer[offset] + (buffer[offset + 1] << 8) + (buffer[offset + 2] << 16) + (buffer[offset + 3] << 24));
+ public static uint ToUIntBigEndian(byte[] buffer, int offset = 0)
+ => (uint)((buffer[offset] << 24) + (buffer[offset+1] << 16) + (buffer[offset+2] << 8) + buffer[offset+3]);
+
+ public static byte[] ToBytesLittleEndian(ushort value)
+ {
+ var bytes = new byte[2];
+ ToBytesLittleEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesLittleEndian(ushort value, byte[] buffer, uint offset = 0)
+ {
+ buffer[offset] = (byte)value;
+ buffer[offset + 1] = (byte)(value >> 8);
+ }
+
+ public static byte[] ToBytesBigEndian(ushort value)
+ {
+ var bytes = new byte[2];
+ ToBytesBigEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesBigEndian(ushort value, byte[] buffer, uint offset = 0)
+ {
+ buffer[offset] = (byte)(value >> 8);
+ buffer[offset + 1] = (byte)value;
+ }
+
+ public static byte[] ToBytesLittleEndian(uint value)
+ {
+ var bytes = new byte[4];
+ ToBytesLittleEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesLittleEndian(uint value, byte[] buffer, uint offset = 0)
+ {
+ buffer[offset] = (byte)value;
+ buffer[offset + 1] = (byte)(value >> 8);
+ buffer[offset + 2] = (byte)(value >> 16);
+ buffer[offset + 3] = (byte)(value >> 24);
+ }
+
+ public static byte[] ToBytesBigEndian(uint value)
+ {
+ var bytes = new byte[4];
+ ToBytesBigEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesBigEndian(uint value, byte[] buffer, uint offset = 0)
+ {
+ buffer[offset] = (byte)(value >> 24);
+ buffer[offset + 1] = (byte)(value >> 16);
+ buffer[offset + 2] = (byte)(value >> 8);
+ buffer[offset + 3] = (byte)value;
+ }
+
+ public static byte[] ToBytesLittleEndian(int value)
+ {
+ var bytes = new byte[4];
+ ToBytesLittleEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesLittleEndian(int value, byte[] buffer, uint offset = 0)
+ {
+ buffer[offset] = (byte)value;
+ buffer[offset + 1] = (byte)(value >> 8);
+ buffer[offset + 2] = (byte)(value >> 16);
+ buffer[offset + 3] = (byte)(value >> 24);
+ }
+
+ public static byte[] ToBytesBigEndian(int value)
+ {
+ var bytes = new byte[4];
+ ToBytesBigEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesBigEndian(int value, byte[] buffer, uint offset = 0)
+ {
+ buffer[offset] = (byte)(value >> 24);
+ buffer[offset + 1] = (byte)(value >> 16);
+ buffer[offset + 2] = (byte)(value >> 8);
+ buffer[offset + 3] = (byte)value;
+ }
+
+
+ public static byte[] ToBytesLittleEndian(ulong value)
+ {
+ var bytes = new byte[8];
+ ToBytesLittleEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesLittleEndian(ulong value, byte[] buffer, ulong offset = 0)
+ {
+ buffer[offset] = (byte)value;
+ buffer[offset + 1] = (byte)(value >> 8);
+ buffer[offset + 2] = (byte)(value >> 16);
+ buffer[offset + 3] = (byte)(value >> 24);
+
+ buffer[offset + 4] = (byte)(value >> 32);
+ buffer[offset + 5] = (byte)(value >> 40);
+ buffer[offset + 6] = (byte)(value >> 48);
+ buffer[offset + 7] = (byte)(value >> 56);
+ }
+
+ public static byte[] ToBytesBigEndian(ulong value)
+ {
+ var bytes = new byte[8];
+ ToBytesBigEndian(value, bytes);
+ return bytes;
+ }
+
+ public static void ToBytesBigEndian(ulong value, byte[] buffer, ulong offset = 0)
+ {
+ buffer[offset] = (byte)(value >> 56);
+ buffer[offset + 1] = (byte)(value >> 48);
+ buffer[offset + 2] = (byte)(value >> 40);
+ buffer[offset + 3] = (byte)(value >> 32);
+ buffer[offset + 4] = (byte)(value >> 24);
+ buffer[offset + 5] = (byte)(value >> 16);
+ buffer[offset + 6] = (byte)(value >> 8);
+ buffer[offset + 7] = (byte)value;
+ }
+
+
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/ClassExtensions.cs b/UVtools.Core/Extensions/ClassExtensions.cs
index 2f21c1b..222c76f 100644
--- a/UVtools.Core/Extensions/ClassExtensions.cs
+++ b/UVtools.Core/Extensions/ClassExtensions.cs
@@ -6,22 +6,21 @@
* of this license document, but changing it is not allowed.
*/
-using Newtonsoft.Json;
+using System.Text.Json;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class ClassExtensions
{
- public static class ClassExtensions
+ public static T? CloneByJsonSerialization<T>(this T classToClone) where T : class
{
- public static T CloneByJsonSerialization<T>(this T classToClone) where T : class
- {
- var clone = JsonConvert.SerializeObject (classToClone);
- return JsonConvert.DeserializeObject<T>(clone);
- }
+ var clone = JsonSerializer.SerializeToUtf8Bytes(classToClone);
+ return JsonSerializer.Deserialize<T>(clone);
+ }
- public static T CloneByXmlSerialization<T>(this T classToClone) where T : class
- {
- var clone = XmlExtensions.SerializeObject(classToClone);
- return XmlExtensions.DeserializeObject<T>(clone);
- }
+ public static T CloneByXmlSerialization<T>(this T classToClone) where T : class
+ {
+ var clone = XmlExtensions.SerializeObject(classToClone);
+ return XmlExtensions.DeserializeFromText<T>(clone);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/CryptExtensions.cs b/UVtools.Core/Extensions/CryptExtensions.cs
index 268d8e1..3962901 100644
--- a/UVtools.Core/Extensions/CryptExtensions.cs
+++ b/UVtools.Core/Extensions/CryptExtensions.cs
@@ -10,115 +10,112 @@ using System;
using System.IO;
using System.Security.Cryptography;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class CryptExtensions
{
- public static class CryptExtensions
+ public static readonly SHA1 SHA1Instance = SHA1.Create();
+ public static string ComputeSHA1Hash(byte[] input)
{
- public static readonly SHA1CryptoServiceProvider SHA1 = new();
- public static string ComputeSHA1Hash(byte[] input)
- {
- return Convert.ToBase64String(SHA1.ComputeHash(input));
- }
+ return Convert.ToBase64String(SHA1Instance.ComputeHash(input));
+ }
- public static readonly SHA256 SHA256 = SHA256.Create();
- public static byte[] ComputeSHA256Hash(byte[] input)
- {
- return SHA256.ComputeHash(input);
- }
+ public static readonly SHA256 SHA256Instance = SHA256.Create();
+ public static byte[] ComputeSHA256Hash(byte[] input)
+ {
+ return SHA256Instance.ComputeHash(input);
+ }
- public static byte[] AesCryptBytes(byte[] data, byte[] key, CipherMode mode, PaddingMode paddingMode, bool encrypt, byte[] iv = null)
+ public static byte[] AesCryptBytes(byte[] data, byte[] key, CipherMode mode, PaddingMode paddingMode, bool encrypt, byte[]? iv = null)
+ {
+ if (data.Length % 16 != 0)
{
- if (data.Length % 16 != 0)
- {
- var temp = new byte[((data.Length / 16) + 1) * 16];
- Array.Copy(data, 0, temp, 0, data.Length);
- data = temp;
- }
-
- var aes = new AesManaged
- {
- KeySize = key.Length * 8,
- Key = key,
- Padding = paddingMode,
- Mode = mode,
- };
-
- if (iv != null)
- {
- aes.IV = iv;
- }
-
- var cryptor = encrypt ? aes.CreateEncryptor() : aes.CreateDecryptor();
-
- using var msDecrypt = new MemoryStream(data);
- using var csDecrypt = new CryptoStream(msDecrypt, cryptor, CryptoStreamMode.Read);
- var outputBuffer = new byte[data.Length];
- csDecrypt.Read(outputBuffer, 0, data.Length);
-
- return outputBuffer;
+ var temp = new byte[((data.Length / 16) + 1) * 16];
+ Array.Copy(data, 0, temp, 0, data.Length);
+ data = temp;
}
- public static MemoryStream AesCryptMemoryStream(byte[] data, byte[] key, CipherMode mode, PaddingMode paddingMode, bool encrypt, byte[] iv = null)
- => new(AesCryptBytes(data, key, mode, paddingMode, encrypt, iv));
+ var aes = Aes.Create();
+ aes.KeySize = key.Length * 8;
+ aes.Key = key;
+ aes.Padding = paddingMode;
+ aes.Mode = mode;
- public static string Base64EncodeString(string plainText)
+ if (iv != null)
{
- var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
- return Convert.ToBase64String(plainTextBytes);
+ aes.IV = iv;
}
- public static string Base64DecodeString(string base64EncodedData)
- {
- var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
- return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
- }
+ var cryptor = encrypt ? aes.CreateEncryptor() : aes.CreateDecryptor();
- public static string XORCipherString(string text, string key)
- {
- var output = new char[text.Length];
+ using var msDecrypt = new MemoryStream(data);
+ using var csDecrypt = new CryptoStream(msDecrypt, cryptor, CryptoStreamMode.Read);
+ var outputBuffer = new byte[data.Length];
+ csDecrypt.Read(outputBuffer, 0, data.Length);
- for (int i = 0; i < text.Length; i++)
- {
- output[i] = (char)(text[i] ^ key[i % key.Length]);
- }
+ return outputBuffer;
+ }
- return new string(output);
- }
+ public static MemoryStream AesCryptMemoryStream(byte[] data, byte[] key, CipherMode mode, PaddingMode paddingMode, bool encrypt, byte[]? iv = null)
+ => new(AesCryptBytes(data, key, mode, paddingMode, encrypt, iv));
- public static string XORCipherString(byte[] bytes, string key)
- {
- var output = new char[bytes.Length];
+ public static string Base64EncodeString(string plainText)
+ {
+ var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
+ return Convert.ToBase64String(plainTextBytes);
+ }
+
+ public static string Base64DecodeString(string base64EncodedData)
+ {
+ var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
+ return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
+ }
- for (int i = 0; i < bytes.Length; i++)
- {
- output[i] = (char)(bytes[i] ^ key[i % key.Length]);
- }
+ public static string XORCipherString(string text, string key)
+ {
+ var output = new char[text.Length];
- return new string(output);
+ for (int i = 0; i < text.Length; i++)
+ {
+ output[i] = (char)(text[i] ^ key[i % key.Length]);
}
- public static byte[] XORCipher(string text, string key)
- {
- var output = new byte[text.Length];
+ return new string(output);
+ }
- for (int i = 0; i < text.Length; i++)
- {
- output[i] = (byte)(text[i] ^ key[i % key.Length]);
- }
+ public static string XORCipherString(byte[] bytes, string key)
+ {
+ var output = new char[bytes.Length];
- return output;
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ output[i] = (char)(bytes[i] ^ key[i % key.Length]);
}
- public static byte[] XORCipher(byte[] bytes, string key)
+ return new string(output);
+ }
+
+ public static byte[] XORCipher(string text, string key)
+ {
+ var output = new byte[text.Length];
+
+ for (int i = 0; i < text.Length; i++)
{
- var output = new byte[bytes.Length];
+ output[i] = (byte)(text[i] ^ key[i % key.Length]);
+ }
- for (int i = 0; i < bytes.Length; i++)
- {
- output[i] = (byte)(bytes[i] ^ key[i % key.Length]);
- }
+ return output;
+ }
- return output;
+ public static byte[] XORCipher(byte[] bytes, string key)
+ {
+ var output = new byte[bytes.Length];
+
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ output[i] = (byte)(bytes[i] ^ key[i % key.Length]);
}
+
+ return output;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/DateTimeExtensions.cs b/UVtools.Core/Extensions/DateTimeExtensions.cs
index f2c4b11..ebb132e 100644
--- a/UVtools.Core/Extensions/DateTimeExtensions.cs
+++ b/UVtools.Core/Extensions/DateTimeExtensions.cs
@@ -9,43 +9,42 @@
using System;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class DateTimeExtensions
{
- public static class DateTimeExtensions
+ /// <summary>
+ /// Gets the Unix timestamp since Jan 1, 1970 UTC
+ /// </summary>
+ public static TimeSpan Timestamp => DateTime.UtcNow.Subtract(DateTime.UnixEpoch);
+
+ /// <summary>
+ /// Gets the Unix timestamp in seconds since Jan 1, 1970 UTC
+ /// </summary>
+ public static double TimestampSeconds => Timestamp.TotalSeconds;
+
+ /// <summary>
+ /// Gets the Unix minutes in seconds since Jan 1, 1970 UTC
+ /// </summary>
+ public static double TimestampMinutes => Timestamp.TotalMinutes;
+
+ /// <summary>
+ /// Gets the <see cref="DateTime"/> from a unix timestamp in seconds
+ /// </summary>
+ /// <param name="seconds"></param>
+ /// <returns></returns>
+ public static DateTime GetDateTimeFromTimestampSeconds(double seconds)
+ {
+ return DateTime.UnixEpoch.AddSeconds(seconds);
+ }
+
+ /// <summary>
+ /// Gets the <see cref="DateTime"/> from a unix timestamp in minutes
+ /// </summary>
+ /// <param name="minutes"></param>
+ /// <returns></returns>
+ public static DateTime GetDateTimeFromTimestampMinutes(double minutes)
{
- /// <summary>
- /// Gets the Unix timestamp since Jan 1, 1970 UTC
- /// </summary>
- public static TimeSpan Timestamp => DateTime.UtcNow.Subtract(DateTime.UnixEpoch);
-
- /// <summary>
- /// Gets the Unix timestamp in seconds since Jan 1, 1970 UTC
- /// </summary>
- public static double TimestampSeconds => Timestamp.TotalSeconds;
-
- /// <summary>
- /// Gets the Unix minutes in seconds since Jan 1, 1970 UTC
- /// </summary>
- public static double TimestampMinutes => Timestamp.TotalMinutes;
-
- /// <summary>
- /// Gets the <see cref="DateTime"/> from a unix timestamp in seconds
- /// </summary>
- /// <param name="seconds"></param>
- /// <returns></returns>
- public static DateTime GetDateTimeFromTimestampSeconds(double seconds)
- {
- return DateTime.UnixEpoch.AddSeconds(seconds);
- }
-
- /// <summary>
- /// Gets the <see cref="DateTime"/> from a unix timestamp in minutes
- /// </summary>
- /// <param name="minutes"></param>
- /// <returns></returns>
- public static DateTime GetDateTimeFromTimestampMinutes(double minutes)
- {
- return DateTime.UnixEpoch.AddMinutes(minutes);
- }
+ return DateTime.UnixEpoch.AddMinutes(minutes);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/DrawingExtensions.cs b/UVtools.Core/Extensions/DrawingExtensions.cs
index d8f4385..f0f25bc 100644
--- a/UVtools.Core/Extensions/DrawingExtensions.cs
+++ b/UVtools.Core/Extensions/DrawingExtensions.cs
@@ -1,100 +1,99 @@
using System;
using System.Drawing;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class DrawingExtensions
{
- public static class DrawingExtensions
- {
- public static Color FactorColor(this Color color, byte pixelColor, byte min = 0, byte max = byte.MaxValue) =>
- FactorColor(color, pixelColor / 255f, min, max);
+ public static Color FactorColor(this Color color, byte pixelColor, byte min = 0, byte max = byte.MaxValue) =>
+ FactorColor(color, pixelColor / 255f, min, max);
- public static Color FactorColor(this Color color, float factor, byte min = 0, byte max = byte.MaxValue)
- {
- byte r = (byte)(color.R == 0 ? 0 :
- Math.Min(Math.Max(min, color.R * factor), max));
+ public static Color FactorColor(this Color color, float factor, byte min = 0, byte max = byte.MaxValue)
+ {
+ byte r = (byte)(color.R == 0 ? 0 :
+ Math.Min(Math.Max(min, color.R * factor), max));
- byte g = (byte)(color.G == 0 ? 0 :
- Math.Min(Math.Max(min, color.G * factor), max));
+ byte g = (byte)(color.G == 0 ? 0 :
+ Math.Min(Math.Max(min, color.G * factor), max));
- byte b = (byte)(color.B == 0 ? 0 :
- Math.Min(Math.Max(min, color.B * factor), max));
- return Color.FromArgb(r, g, b);
- }
+ byte b = (byte)(color.B == 0 ? 0 :
+ Math.Min(Math.Max(min, color.B * factor), max));
+ return Color.FromArgb(r, g, b);
+ }
- public static double CalculatePolygonSideLengthFromRadius(double radius, int sides)
- {
- return 2 * radius * Math.Sin(Math.PI / sides);
- }
+ public static double CalculatePolygonSideLengthFromRadius(double radius, int sides)
+ {
+ return 2 * radius * Math.Sin(Math.PI / sides);
+ }
- public static double CalculatePolygonVerticalLengthFromRadius(double radius, int sides)
- {
- return radius * Math.Cos(Math.PI / sides);
- }
+ public static double CalculatePolygonVerticalLengthFromRadius(double radius, int sides)
+ {
+ return radius * Math.Cos(Math.PI / sides);
+ }
- public static double CalculatePolygonRadiusFromSideLength(double length, int sides)
- {
- var theta = 360.0 / sides;
- return length / (2 * Math.Cos((90 - theta / 2) * Math.PI / 180.0));
- }
+ public static double CalculatePolygonRadiusFromSideLength(double length, int sides)
+ {
+ var theta = 360.0 / sides;
+ return length / (2 * Math.Cos((90 - theta / 2) * Math.PI / 180.0));
+ }
- public static Point[] GetPolygonVertices(int sides, int radius, Point center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false)
- {
- if (sides < 3)
- throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
+ public static Point[] GetPolygonVertices(int sides, int radius, Point center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false)
+ {
+ if (sides < 3)
+ throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
- var vertices = new Point[sides];
+ var vertices = new Point[sides];
- double deg = 360.0 / sides;//calculate the rotation angle
- var rad = Math.PI / 180.0;
+ double deg = 360.0 / sides;//calculate the rotation angle
+ var rad = Math.PI / 180.0;
- var x0 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + startingAngle) * rad);
- var y0 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + startingAngle) * rad);
+ var x0 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + startingAngle) * rad);
+ var y0 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + startingAngle) * rad);
- var x1 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + deg + startingAngle) * rad);
- var y1 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + deg + startingAngle) * rad);
+ var x1 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + deg + startingAngle) * rad);
+ var y1 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + deg + startingAngle) * rad);
- vertices[0] = new(
- (int) Math.Round(x0),
- (int) Math.Round(y0)
- );
+ vertices[0] = new(
+ (int) Math.Round(x0),
+ (int) Math.Round(y0)
+ );
- vertices[1] = new(
- (int) Math.Round(x1),
- (int) Math.Round(y1)
- );
+ vertices[1] = new(
+ (int) Math.Round(x1),
+ (int) Math.Round(y1)
+ );
- for (int i = 0; i < sides - 2; i++)
- {
- double dsinrot = Math.Sin((deg * (i + 1)) * rad);
- double dcosrot = Math.Cos((deg * (i + 1)) * rad);
+ for (int i = 0; i < sides - 2; i++)
+ {
+ double dsinrot = Math.Sin((deg * (i + 1)) * rad);
+ double dcosrot = Math.Cos((deg * (i + 1)) * rad);
- vertices[i + 2] = new(
- (int)Math.Round(center.X + dcosrot * (x1 - center.X) - dsinrot * (y1 - center.Y)),
- (int)Math.Round(center.Y + dsinrot * (x1 - center.X) + dcosrot * (y1 - center.Y))
- );
- }
+ vertices[i + 2] = new(
+ (int)Math.Round(center.X + dcosrot * (x1 - center.X) - dsinrot * (y1 - center.Y)),
+ (int)Math.Round(center.Y + dsinrot * (x1 - center.X) + dcosrot * (y1 - center.Y))
+ );
+ }
- if (flipHorizontally)
+ if (flipHorizontally)
+ {
+ var startX = center.X - radius;
+ var endX = center.X + radius;
+ for (int i = 0; i < sides; i++)
{
- var startX = center.X - radius;
- var endX = center.X + radius;
- for (int i = 0; i < sides; i++)
- {
- vertices[i].X = endX - (vertices[i].X - startX);
- }
+ vertices[i].X = endX - (vertices[i].X - startX);
}
+ }
- if (flipVertically)
+ if (flipVertically)
+ {
+ var startY = center.Y - radius;
+ var endY = center.Y + radius;
+ for (int i = 0; i < sides; i++)
{
- var startY = center.Y - radius;
- var endY = center.Y + radius;
- for (int i = 0; i < sides; i++)
- {
- vertices[i].Y = endY - (vertices[i].Y - startY);
- }
+ vertices[i].Y = endY - (vertices[i].Y - startY);
}
-
- return vertices;
}
+
+ return vertices;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 730ec12..aae8bd6 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -6,1350 +6,1349 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Drawing;
-using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.CV.Cuda;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
using UVtools.Core.EmguCV;
using UVtools.Core.Objects;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class EmguExtensions
{
- public static class EmguExtensions
+ #region Constants
+ /// <summary>
+ /// White color: 255, 255, 255, 255
+ /// </summary>
+ public static readonly MCvScalar WhiteColor = new(255, 255, 255, 255);
+
+ /// <summary>
+ /// Black color: 0, 0, 0, 255
+ /// </summary>
+ public static readonly MCvScalar BlackColor = new(0, 0, 0, 255);
+ //public static readonly MCvScalar TransparentColor = new();
+
+ public static readonly Mat Kernel3x3Rectangle = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1));
+ #endregion
+
+ #region Initializers methods
+ /// <summary>
+ /// Create a byte array of size of this <see cref="Mat"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>Blank byte array</returns>
+ public static byte[] CreateBlankByteArray(this Mat mat)
+ => new byte[mat.GetLength()];
+
+ /// <summary>
+ /// Creates a new empty <see cref="Mat"/> with same size and type of the source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns></returns>
+ public static Mat New(this Mat mat)
+ => new(mat.Size, mat.Depth, mat.NumberOfChannels);
+
+ /// <summary>
+ /// Creates a new empty <see cref="Mat"/> with same size and type of the source
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="color"></param>
+ /// <returns></returns>
+ public static Mat New(this Mat src, MCvScalar color)
+ => InitMat(src.Size, color, src.NumberOfChannels, src.Depth);
+
+ /// <summary>
+ /// Creates a new blanked (All zeros) <see cref="Mat"/> with same size and type of the source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>Blanked <see cref="Mat"/></returns>
+ public static Mat NewBlank(this Mat mat)
+ => InitMat(mat.Size, mat.NumberOfChannels, mat.Depth);
+
+ /// <summary>
+ /// Creates a new blanked (All zeros) <see cref="UMat"/> with same size and type of the source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>Blanked <see cref="Mat"/></returns>
+ public static UMat NewBlank(this UMat mat)
+ => InitUMat(mat.Size, mat.NumberOfChannels, mat.Depth);
+
+ /// <summary>
+ /// Creates a <see cref="Mat"/> with same size and type of the source and set it to a color
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="color"></param>
+ /// <returns></returns>
+ public static Mat NewSetTo(this Mat mat, MCvScalar color)
+ => InitMat(mat.Size, color, mat.NumberOfChannels, mat.Depth);
+
+
+ /// <summary>
+ /// Creates a new <see cref="Mat"/> and zero it
+ /// </summary>
+ /// <param name="size"></param>
+ /// <param name="channels"></param>
+ /// <param name="depthType"></param>
+ /// <returns></returns>
+ public static Mat InitMat(Size size, int channels = 1, DepthType depthType = DepthType.Cv8U)
+ => size.IsEmpty ? new() : Mat.Zeros(size.Height, size.Width, depthType, channels);
+
+ /// <summary>
+ /// Creates a new <see cref="UMat"/> and zero it
+ /// </summary>
+ /// <param name="size"></param>
+ /// <param name="channels"></param>
+ /// <param name="depthType"></param>
+ /// <returns></returns>
+ public static UMat InitUMat(Size size, int channels = 1, DepthType depthType = DepthType.Cv8U)
{
- #region Constants
- /// <summary>
- /// White color: 255, 255, 255, 255
- /// </summary>
- public static readonly MCvScalar WhiteColor = new(255, 255, 255, 255);
-
- /// <summary>
- /// Black color: 0, 0, 0, 255
- /// </summary>
- public static readonly MCvScalar BlackColor = new(0, 0, 0, 255);
- //public static readonly MCvScalar TransparentColor = new();
+ if (size.IsEmpty) return new();
+ var umat = new UMat(size.Height, size.Width, depthType, channels);
+ umat.SetTo(BlackColor);
+ return umat;
+ }
- public static readonly Mat Kernel3x3Rectangle = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1));
- #endregion
+ /// <summary>
+ /// Creates a new <see cref="Mat"/> and set it to a <see cref="MCvScalar"/>
+ /// </summary>
+ /// <param name="size"></param>
+ /// <param name="color"></param>
+ /// <param name="channels"></param>
+ /// <param name="depthType"></param>
+ /// <returns></returns>
+ public static Mat InitMat(Size size, MCvScalar color, int channels = 1, DepthType depthType = DepthType.Cv8U)
+ {
+ if (size.IsEmpty) return new();
+ var mat = new Mat(size, depthType, channels);
+ mat.SetTo(color);
+ return mat;
+ }
- #region Initializers methods
- /// <summary>
- /// Create a byte array of size of this <see cref="Mat"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>Blank byte array</returns>
- public static byte[] CreateBlankByteArray(this Mat mat)
- => new byte[mat.GetLength()];
+ /// <summary>
+ /// Allocates a new array of mat's
+ /// </summary>
+ /// <param name="count">Array size</param>
+ /// <returns></returns>
+ public static Mat[] InitMats(uint count) => InitMats(count, Size.Empty);
+
+ /// <summary>
+ /// Allocates a new array of mat 's
+ /// </summary>
+ /// <param name="count">Array size</param>
+ /// <param name="size">Image size to create, use <see cref="Size.Empty"/> to create a empty Mat</param>
+ /// <returns>New mat array</returns>
+ public static Mat[] InitMats(uint count, Size size)
+ {
+ var layers = new Mat[count];
+ for (var i = 0; i < layers.Length; i++)
+ {
+ layers[i] = InitMat(size);
+ }
- /// <summary>
- /// Creates a new empty <see cref="Mat"/> with same size and type of the source
- /// </summary>
- /// <param name="mat"></param>
- /// <returns></returns>
- public static Mat New(this Mat mat)
- => new(mat.Size, mat.Depth, mat.NumberOfChannels);
+ return layers;
+ }
- /// <summary>
- /// Creates a new empty <see cref="Mat"/> with same size and type of the source
- /// </summary>
- /// <param name="src"></param>
- /// <param name="color"></param>
- /// <returns></returns>
- public static Mat New(this Mat src, MCvScalar color)
- => InitMat(src.Size, color, src.NumberOfChannels, src.Depth);
+ /// <summary>
+ /// Create a new <see cref="GpuMat"/> from <see cref="Mat"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns></returns>
+ public static GpuMat ToGpuMat(this Mat mat)
+ {
+ var gpuMat = new GpuMat(mat.Rows, mat.Cols, mat.Depth, mat.NumberOfChannels);
+ gpuMat.Upload(mat);
+ return gpuMat;
+ }
+ #endregion
- /// <summary>
- /// Creates a new blanked (All zeros) <see cref="Mat"/> with same size and type of the source
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>Blanked <see cref="Mat"/></returns>
- public static Mat NewBlank(this Mat mat)
- => InitMat(mat.Size, mat.NumberOfChannels, mat.Depth);
+ #region Memory accessors
- /// <summary>
- /// Creates a new blanked (All zeros) <see cref="UMat"/> with same size and type of the source
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>Blanked <see cref="Mat"/></returns>
- public static UMat NewBlank(this UMat mat)
- => InitUMat(mat.Size, mat.NumberOfChannels, mat.Depth);
+ /// <summary>
+ /// Gets the byte pointer of this <see cref="Mat"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns></returns>
+ public static unsafe byte* GetBytePointer(this Mat mat)
+ {
+ return (byte*)mat.DataPointer.ToPointer();
+ }
- /// <summary>
- /// Creates a <see cref="Mat"/> with same size and type of the source and set it to a color
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="color"></param>
- /// <returns></returns>
- public static Mat NewSetTo(this Mat mat, MCvScalar color)
- => InitMat(mat.Size, color, mat.NumberOfChannels, mat.Depth);
+ /// <summary>
+ /// Gets the whole data span to manipulate or read pixels
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns></returns>
+ public static unsafe Span<byte> GetDataByteSpan(this Mat mat)
+ {
+ return new(mat.DataPointer.ToPointer(), mat.GetLength());
+ }
+ public static unsafe Span<byte> GetDataByteSpan(this Mat mat, int length, int offset = 0)
+ {
+ return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length);
+ }
- /// <summary>
- /// Creates a new <see cref="Mat"/> and zero it
- /// </summary>
- /// <param name="size"></param>
- /// <param name="channels"></param>
- /// <param name="depthType"></param>
- /// <returns></returns>
- public static Mat InitMat(Size size, int channels = 1, DepthType depthType = DepthType.Cv8U)
- => size.IsEmpty ? new() : Mat.Zeros(size.Height, size.Width, depthType, channels);
+ /// <summary>
+ /// Gets the data span to manipulate or read pixels given a length and offset
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="mat"></param>
+ /// <param name="length"></param>
+ /// <param name="offset"></param>
+ /// <returns></returns>
+ public static unsafe Span<T> GetDataSpan<T>(this Mat mat, int length = 0, int offset = 0)
+ {
+ return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length);
+ }
- /// <summary>
- /// Creates a new <see cref="UMat"/> and zero it
- /// </summary>
- /// <param name="size"></param>
- /// <param name="channels"></param>
- /// <param name="depthType"></param>
- /// <returns></returns>
- public static UMat InitUMat(Size size, int channels = 1, DepthType depthType = DepthType.Cv8U)
- {
- if (size.IsEmpty) return new();
- var umat = new UMat(size.Height, size.Width, depthType, channels);
- umat.SetTo(BlackColor);
- return umat;
- }
+ /// <summary>
+ /// Gets a single pixel span to manipulate or read pixels
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="mat"></param>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <returns></returns>
+ public static Span<T> GetPixelSpan<T>(this Mat mat, int x, int y)
+ {
+ return mat.GetDataSpan<T>(mat.NumberOfChannels, mat.GetPixelPos(x, y));
+ }
- /// <summary>
- /// Creates a new <see cref="Mat"/> and set it to a <see cref="MCvScalar"/>
- /// </summary>
- /// <param name="size"></param>
- /// <param name="color"></param>
- /// <param name="channels"></param>
- /// <param name="depthType"></param>
- /// <returns></returns>
- public static Mat InitMat(Size size, MCvScalar color, int channels = 1, DepthType depthType = DepthType.Cv8U)
- {
- if (size.IsEmpty) return new();
- var mat = new Mat(size, depthType, channels);
- mat.SetTo(color);
- return mat;
- }
+ /// <summary>
+ /// Gets a single pixel span to manipulate or read pixels
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="mat"></param>
+ /// <param name="pos"></param>
+ /// <returns></returns>
+ public static Span<T> GetPixelSpan<T>(this Mat mat, int pos)
+ {
+ return mat.GetDataSpan<T>(mat.NumberOfChannels, pos);
+ }
- /// <summary>
- /// Allocates a new array of mat's
- /// </summary>
- /// <param name="count">Array size</param>
- /// <returns></returns>
- public static Mat[] InitMats(uint count) => InitMats(count, Size.Empty);
+ /// <summary>
+ /// Gets a row span to manipulate or read pixels
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="mat"></param>
+ /// <param name="y"></param>
+ /// <param name="length"></param>
+ /// <param name="offset"></param>
+ /// <returns></returns>
+ public static unsafe Span<T> GetRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0)
+ {
+ return new(IntPtr.Add(mat.DataPointer, y * mat.GetRealStep() + offset).ToPointer(), length <= 0 ? mat.GetRealStep() : length);
+ }
- /// <summary>
- /// Allocates a new array of mat 's
- /// </summary>
- /// <param name="count">Array size</param>
- /// <param name="size">Image size to create, use <see cref="Size.Empty"/> to create a empty Mat</param>
- /// <returns>New mat array</returns>
- public static Mat[] InitMats(uint count, Size size)
+ /// <summary>
+ /// Gets a col span to manipulate or read pixels
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="mat"></param>
+ /// <param name="x"></param>
+ /// <param name="length"></param>
+ /// <param name="offset"></param>
+ /// <returns></returns>
+ public static unsafe Span<T> GetColSpan<T>(this Mat mat, int x, int length = 0, int offset = 0)
+ {
+ var colMat = mat.Col(x);
+ return new(IntPtr.Add(colMat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length);
+ }
+ #endregion
+
+ #region Memory Fill
+
+ /// <summary>
+ /// Fill a mat span with a color
+ /// </summary>
+ /// <param name="mat">Mat to fill</param>
+ /// <param name="startPosition">Start position, this reference will increment by the <see cref="length"/></param>
+ /// <param name="length">Length to fill</param>
+ /// <param name="color">Color to fill with</param>
+ /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param>
+ public static void FillSpan(this Mat mat, ref int startPosition, int length, byte color, byte colorFillMinThreshold = 1)
+ {
+ if (length <= 0) return;
+ if (color < colorFillMinThreshold) // Ignore threshold (mostly if blacks), spare cycles
{
- var layers = new Mat[count];
- for (var i = 0; i < layers.Length; i++)
- {
- layers[i] = InitMat(size);
- }
-
- return layers;
+ startPosition += length;
+ return;
}
- /// <summary>
- /// Create a new <see cref="GpuMat"/> from <see cref="Mat"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns></returns>
- public static GpuMat ToGpuMat(this Mat mat)
- {
- var gpuMat = new GpuMat(mat.Rows, mat.Cols, mat.Depth, mat.NumberOfChannels);
- gpuMat.Upload(mat);
- return gpuMat;
- }
- #endregion
+ mat.GetDataByteSpan(length, startPosition).Fill(color);
+ startPosition += length;
+ }
- #region Memory accessors
+ /// <summary>
+ /// Fill a mat span with a color
+ /// </summary>
+ /// <param name="mat">Mat to fill</param>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <param name="length">Length to fill</param>
+ /// <param name="color">Color to fill with</param>
+ /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param>
+ public static void FillSpan(this Mat mat, int x, int y, int length, byte color, byte colorFillMinThreshold = 1)
+ {
+ if (length <= 0 || color < colorFillMinThreshold) return; // Ignore threshold (mostly if blacks), spare cycles
+ mat.GetDataByteSpan(length, mat.GetPixelPos(x, y)).Fill(color);
+ }
- /// <summary>
- /// Gets the byte pointer of this <see cref="Mat"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns></returns>
- public static unsafe byte* GetBytePointer(this Mat mat)
- {
- return (byte*)mat.DataPointer.ToPointer();
- }
+ /// <summary>
+ /// Fill a mat span with a color
+ /// </summary>
+ /// <param name="mat">Mat to fill</param>
+ /// <param name="position"></param>
+ /// <param name="length">Length to fill</param>
+ /// <param name="color">Color to fill with</param>
+ /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param>
+ public static void FillSpan(this Mat mat, Point position, int length, byte color, byte colorFillMinThreshold = 1)
+ => mat.FillSpan(position.X, position.Y, length, color, colorFillMinThreshold);
+ #endregion
+
+ #region Get/Set methods
+
+ /// <summary>
+ /// .Step return the original Mat step, if ROI step still from original matrix which lead to errors.
+ /// Use this to get the real step size
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns></returns>
+ public static int GetRealStep(this Mat mat)
+ {
+ return mat.Width * mat.NumberOfChannels;
+ }
- /// <summary>
- /// Gets the whole data span to manipulate or read pixels
- /// </summary>
- /// <param name="mat"></param>
- /// <returns></returns>
- public static unsafe Span<byte> GetDataByteSpan(this Mat mat)
- {
- return new(mat.DataPointer.ToPointer(), mat.GetLength());
- }
+ /// <summary>
+ /// Gets the total length of this <see cref="Mat"/></param>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>The total length of this <see cref="Mat"/></returns>
+ public static int GetLength(this Mat mat)
+ {
+ return mat.Total.ToInt32() * mat.NumberOfChannels;
+ }
- public static unsafe Span<byte> GetDataByteSpan(this Mat mat, int length, int offset = 0)
- {
- return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length);
- }
+ /// <summary>
+ /// Gets a pixel index position on a span given X and Y
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="x">X coordinate</param>
+ /// <param name="y">Y coordinate</param>
+ /// <returns>The pixel index position</returns>
+ public static int GetPixelPos(this Mat mat, int x, int y)
+ {
+ return y * mat.GetRealStep() + x * mat.NumberOfChannels;
+ }
- /// <summary>
- /// Gets the data span to manipulate or read pixels given a length and offset
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="mat"></param>
- /// <param name="length"></param>
- /// <param name="offset"></param>
- /// <returns></returns>
- public static unsafe Span<T> GetDataSpan<T>(this Mat mat, int length = 0, int offset = 0)
- {
- return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length);
- }
+ /// <summary>
+ /// Gets a pixel index position on a span given X and Y
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="point">X and Y Location</param>
+ /// <returns>The pixel index position</returns>
+ public static int GetPixelPos(this Mat mat, Point point)
+ {
+ return mat.GetPixelPos(point.X, point.Y);
+ }
- /// <summary>
- /// Gets a single pixel span to manipulate or read pixels
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="mat"></param>
- /// <param name="x"></param>
- /// <param name="y"></param>
- /// <returns></returns>
- public static Span<T> GetPixelSpan<T>(this Mat mat, int x, int y)
- {
- return mat.GetDataSpan<T>(mat.NumberOfChannels, mat.GetPixelPos(x, y));
- }
+ /// <summary>
+ /// Gets a byte array copy of this <see cref="Mat"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>Byte array </returns>
+ public static byte[] GetBytes(this Mat mat)
+ {
+ var data = new byte[mat.GetLength()];
+ Marshal.Copy(mat.DataPointer, data, 0, data.Length);
+ return data;
+ }
- /// <summary>
- /// Gets a single pixel span to manipulate or read pixels
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="mat"></param>
- /// <param name="pos"></param>
- /// <returns></returns>
- public static Span<T> GetPixelSpan<T>(this Mat mat, int pos)
- {
- return mat.GetDataSpan<T>(mat.NumberOfChannels, pos);
- }
+ /// <summary>
+ /// Gets a byte pixel at a position
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="pos"></param>
+ /// <returns></returns>
+ public static byte GetByte(this Mat mat, int pos)
+ {
+ //return new Span<byte>(IntPtr.Add(mat.DataPointer, pos).ToPointer(), mat.Step)[0];
+ var value = new byte[1];
+ Marshal.Copy(mat.DataPointer + pos, value, 0, value.Length);
+ return value[0];
+ }
- /// <summary>
- /// Gets a row span to manipulate or read pixels
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="mat"></param>
- /// <param name="y"></param>
- /// <param name="length"></param>
- /// <param name="offset"></param>
- /// <returns></returns>
- public static unsafe Span<T> GetRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0)
- {
- return new(IntPtr.Add(mat.DataPointer, y * mat.GetRealStep() + offset).ToPointer(), length <= 0 ? mat.GetRealStep() : length);
- }
+ /// <summary>
+ /// Gets a byte pixel at a position
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <returns></returns>
+ public static byte GetByte(this Mat mat, int x, int y) => GetByte(mat, mat.GetPixelPos(x, y));
+
+ /// <summary>
+ /// Gets a byte pixel at a position
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="pos"></param>
+ /// <returns></returns>
+ public static byte GetByte(this Mat mat, Point pos) => GetByte(mat, mat.GetPixelPos(pos.X, pos.Y));
+
+ /// <summary>
+ /// Sets a byte pixel at a position
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="pixel"></param>
+ /// <param name="value"></param>
+ public static void SetByte(this Mat mat, int pixel, byte value) => SetByte(mat, pixel, new[] { value });
+
+ /// <summary>
+ /// Sets a byte pixel at a position
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="pixel"></param>
+ /// <param name="value"></param>
+ public static void SetByte(this Mat mat, int pixel, byte[] value) =>
+ Marshal.Copy(value, 0, mat.DataPointer + pixel, value.Length);
+
+ /// <summary>
+ /// Sets a byte pixel at a position
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <param name="value"></param>
+ public static void SetByte(this Mat mat, int x, int y, byte value) =>
+ SetByte(mat, x, y, new[] { value });
+
+ /// <summary>
+ /// Sets a byte pixel at a position
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <param name="value"></param>
+ public static void SetByte(this Mat mat, int x, int y, byte[] value) =>
+ SetByte(mat, y * mat.GetRealStep() + x * mat.NumberOfChannels, value);
+
+ /// <summary>
+ /// Sets bytes
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="value"></param>
+ public static void SetBytes(this Mat mat, byte[] value) =>
+ Marshal.Copy(value, 0, mat.DataPointer, value.Length);
+
+ /// <summary>
+ /// Gets PNG byte array
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns></returns>
+ public static byte[] GetPngByes(this IInputArray mat)
+ {
+ return CvInvoke.Imencode(".png", mat);
+ }
+ #endregion
- /// <summary>
- /// Gets a col span to manipulate or read pixels
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="mat"></param>
- /// <param name="x"></param>
- /// <param name="length"></param>
- /// <param name="offset"></param>
- /// <returns></returns>
- public static unsafe Span<T> GetColSpan<T>(this Mat mat, int x, int length = 0, int offset = 0)
- {
- var colMat = mat.Col(x);
- return new(IntPtr.Add(colMat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length);
- }
- #endregion
+ #region Create methods
- #region Memory Fill
+ public static Mat CreateMask(this Mat src, VectorOfVectorOfPoint contours)
+ {
+ var mask = src.NewBlank();
+ CvInvoke.DrawContours(mask, contours, -1, WhiteColor, -1);
+ return mask;
+ }
- /// <summary>
- /// Fill a mat span with a color
- /// </summary>
- /// <param name="mat">Mat to fill</param>
- /// <param name="startPosition">Start position, this reference will increment by the <see cref="length"/></param>
- /// <param name="length">Length to fill</param>
- /// <param name="color">Color to fill with</param>
- /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param>
- public static void FillSpan(this Mat mat, ref int startPosition, int length, byte color, byte colorFillMinThreshold = 1)
- {
- if (length <= 0) return;
- if (color < colorFillMinThreshold) // Ignore threshold (mostly if blacks), spare cycles
- {
- startPosition += length;
- return;
- }
+ public static Mat CreateMask(this Mat src, Point[][] contours)
+ {
+ using var vec = new VectorOfVectorOfPoint(contours);
+ return src.CreateMask(vec);
+ }
- mat.GetDataByteSpan(length, startPosition).Fill(color);
- startPosition += length;
- }
+ #endregion
- /// <summary>
- /// Fill a mat span with a color
- /// </summary>
- /// <param name="mat">Mat to fill</param>
- /// <param name="x"></param>
- /// <param name="y"></param>
- /// <param name="length">Length to fill</param>
- /// <param name="color">Color to fill with</param>
- /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param>
- public static void FillSpan(this Mat mat, int x, int y, int length, byte color, byte colorFillMinThreshold = 1)
- {
- if (length <= 0 || color < colorFillMinThreshold) return; // Ignore threshold (mostly if blacks), spare cycles
- mat.GetDataByteSpan(length, mat.GetPixelPos(x, y)).Fill(color);
- }
+ #region Copy methods
+ /// <summary>
+ /// Copy a region from <see cref="Mat"/> to center of other <see cref="Mat"/>
+ /// </summary>
+ /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
+ /// <param name="size">Size of the center offset</param>
+ /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
+ public static void CopyCenterToCenter(this Mat src, Size size, Mat dst)
+ {
+ var srcRoi = src.RoiFromCenter(size);
+ CopyToCenter(srcRoi, dst);
+ }
- /// <summary>
- /// Fill a mat span with a color
- /// </summary>
- /// <param name="mat">Mat to fill</param>
- /// <param name="position"></param>
- /// <param name="length">Length to fill</param>
- /// <param name="color">Color to fill with</param>
- /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param>
- public static void FillSpan(this Mat mat, Point position, int length, byte color, byte colorFillMinThreshold = 1)
- => mat.FillSpan(position.X, position.Y, length, color, colorFillMinThreshold);
- #endregion
+ /// <summary>
+ /// Copy a region from <see cref="Mat"/> to center of other <see cref="Mat"/>
+ /// </summary>
+ /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
+ /// <param name="region">Region to copy</param>
+ /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
+ public static void CopyRegionToCenter(this Mat src, Rectangle region, Mat dst)
+ {
+ var srcRoi = src.Roi(region);
+ CopyToCenter(srcRoi, dst);
+ }
- #region Get/Set methods
+ /// <summary>
+ /// Copy a <see cref="Mat"/> to center of other <see cref="Mat"/>
+ /// </summary>
+ /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
+ /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
+ public static void CopyToCenter(this Mat src, Mat dst)
+ {
+ var srcStep = src.GetRealStep();
+ var dstStep = dst.GetRealStep();
+ var dx = Math.Abs(dstStep - srcStep) / 2;
+ var dy = Math.Abs(dst.Height - src.Height) / 2;
- /// <summary>
- /// .Step return the original Mat step, if ROI step still from original matrix which lead to errors.
- /// Use this to get the real step size
- /// </summary>
- /// <param name="mat"></param>
- /// <returns></returns>
- public static int GetRealStep(this Mat mat)
+ if (src.Size == dst.Size)
{
- return mat.Width * mat.NumberOfChannels;
+ src.CopyTo(dst);
+ return;
}
- /// <summary>
- /// Gets the total length of this <see cref="Mat"/></param>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>The total length of this <see cref="Mat"/></returns>
- public static int GetLength(this Mat mat)
+ if (dstStep > srcStep && dst.Height > src.Height)
{
- return mat.Total.ToInt32() * mat.NumberOfChannels;
+ using var dstRoi = dst.Roi(new Rectangle(dx, dy, src.Width, src.Height));
+ src.CopyTo(dstRoi);
+ return;
}
-
- /// <summary>
- /// Gets a pixel index position on a span given X and Y
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="x">X coordinate</param>
- /// <param name="y">Y coordinate</param>
- /// <returns>The pixel index position</returns>
- public static int GetPixelPos(this Mat mat, int x, int y)
+
+ if (dstStep < srcStep && dst.Height < src.Height)
{
- return y * mat.GetRealStep() + x * mat.NumberOfChannels;
+ using var srcRoi = src.Roi(new Rectangle(dx, dy, dst.Width, dst.Height));
+ srcRoi.CopyTo(dst);
+ return;
}
- /// <summary>
- /// Gets a pixel index position on a span given X and Y
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="point">X and Y Location</param>
- /// <returns>The pixel index position</returns>
- public static int GetPixelPos(this Mat mat, Point point)
- {
- return mat.GetPixelPos(point.X, point.Y);
- }
+ throw new InvalidOperationException("Unable to copy, out of bounds");
+ }
- /// <summary>
- /// Gets a byte array copy of this <see cref="Mat"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>Byte array </returns>
- public static byte[] GetBytes(this Mat mat)
+ public static void CopyAreasSmallerThan(this Mat src, double threshold, Mat dst)
+ {
+ if (threshold <= 1) return;
+ using var contours = src.FindContours(out var hierarchy, RetrType.Tree);
+ var contourGroups = EmguContours.GetContoursInGroups(contours, hierarchy);
+
+ var mask = src.NewBlank();
+ uint drawContours = 0;
+ foreach (var contourGroup in contourGroups)
{
- var data = new byte[mat.GetLength()];
- Marshal.Copy(mat.DataPointer, data, 0, data.Length);
- return data;
+ using var selectedContours = new VectorOfVectorOfPoint();
+ foreach (var group in contourGroup)
+ {
+ var area = EmguContours.GetContourArea(group);
+ if (area >= threshold) continue;
+ drawContours++;
+ selectedContours.Push(group);
+ }
+
+ if (selectedContours.Size == 0) continue;
+ CvInvoke.DrawContours(mask, selectedContours, -1, WhiteColor, -1);
+
}
+ if (drawContours > 0) src.CopyTo(dst, mask);
+ }
- /// <summary>
- /// Gets a byte pixel at a position
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="pos"></param>
- /// <returns></returns>
- public static byte GetByte(this Mat mat, int pos)
+ public static void CopyAreasLargerThan(this Mat src, double threshold, Mat dst)
+ {
+ if (threshold <= 0) return;
+ using var contours = src.FindContours(out var hierarchy, RetrType.Tree);
+ var contourGroups = EmguContours.GetContoursInGroups(contours, hierarchy);
+
+ var mask = src.NewBlank();
+ uint drawContours = 0;
+ foreach (var contourGroup in contourGroups)
{
- //return new Span<byte>(IntPtr.Add(mat.DataPointer, pos).ToPointer(), mat.Step)[0];
- var value = new byte[1];
- Marshal.Copy(mat.DataPointer + pos, value, 0, value.Length);
- return value[0];
+ using var selectedContours = new VectorOfVectorOfPoint();
+ foreach (var group in contourGroup)
+ {
+ var area = EmguContours.GetContourArea(group);
+ if (area <= threshold) continue;
+ drawContours++;
+ selectedContours.Push(group);
+ }
+
+ if (selectedContours.Size == 0) continue;
+ CvInvoke.DrawContours(mask, selectedContours, -1, WhiteColor, -1);
+
}
+ if(drawContours > 0) src.CopyTo(dst, mask);
+ }
+ #endregion
- /// <summary>
- /// Gets a byte pixel at a position
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="x"></param>
- /// <param name="y"></param>
- /// <returns></returns>
- public static byte GetByte(this Mat mat, int x, int y) => GetByte(mat, mat.GetPixelPos(x, y));
+ #region Roi methods
- /// <summary>
- /// Gets a byte pixel at a position
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="pos"></param>
- /// <returns></returns>
- public static byte GetByte(this Mat mat, Point pos) => GetByte(mat, mat.GetPixelPos(pos.X, pos.Y));
+ /// <summary>
+ /// Gets a Roi, but return source when roi is empty or have same size as source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="roi"></param>
+ /// <returns></returns>
+ public static Mat Roi(this Mat mat, Rectangle roi)
+ {
+ return roi.IsEmpty || roi.Size == mat.Size ? mat : new Mat(mat, roi);
+ }
- /// <summary>
- /// Sets a byte pixel at a position
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="pixel"></param>
- /// <param name="value"></param>
- public static void SetByte(this Mat mat, int pixel, byte value) => SetByte(mat, pixel, new[] { value });
+ /// <summary>
+ /// Gets a Roi at x=0 and y=0 given a size, but return source when roi is empty or have same size as source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ public static Mat Roi(this Mat mat, Size size)
+ {
+ return size.IsEmpty || size == mat.Size ? mat : new Mat(mat, new(Point.Empty, size));
+ }
- /// <summary>
- /// Sets a byte pixel at a position
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="pixel"></param>
- /// <param name="value"></param>
- public static void SetByte(this Mat mat, int pixel, byte[] value) =>
- Marshal.Copy(value, 0, mat.DataPointer + pixel, value.Length);
+ /// <summary>
+ /// Gets a Roi from a mat size, but return source when roi is empty or have same size as source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="fromMat"></param>
+ /// <returns></returns>
+ public static Mat Roi(this Mat mat, Mat fromMat) => mat.Roi(fromMat.Size);
+
+ /// <summary>
+ /// Gets a Roi from center, but return source when have same size as source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ public static Mat RoiFromCenter(this Mat mat, Size size)
+ {
+ if(mat.Size == size) return mat;
- /// <summary>
- /// Sets a byte pixel at a position
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="x"></param>
- /// <param name="y"></param>
- /// <param name="value"></param>
- public static void SetByte(this Mat mat, int x, int y, byte value) =>
- SetByte(mat, x, y, new[] { value });
+ var newRoi = mat.Roi(new Rectangle(
+ mat.Width / 2 - size.Width / 2,
+ mat.Height / 2 - size.Height / 2,
+ size.Width,
+ size.Height
+ ));
- /// <summary>
- /// Sets a byte pixel at a position
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="x"></param>
- /// <param name="y"></param>
- /// <param name="value"></param>
- public static void SetByte(this Mat mat, int x, int y, byte[] value) =>
- SetByte(mat, y * mat.GetRealStep() + x * mat.NumberOfChannels, value);
+ return newRoi;
+ }
- /// <summary>
- /// Sets bytes
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="value"></param>
- public static void SetBytes(this Mat mat, byte[] value) =>
- Marshal.Copy(value, 0, mat.DataPointer, value.Length);
+ /// <summary>
+ /// Gets a new mat obtained from it center at a target size and roi
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="targetSize"></param>
+ /// <param name="roi"></param>
+ /// <returns></returns>
+ public static Mat NewMatFromCenterRoi(this Mat mat, Size targetSize, Rectangle roi)
+ {
+ if (targetSize == mat.Size) return mat.Clone();
+ var newMat = InitMat(targetSize);
+ var roiMat = mat.Roi(roi);
+
+ //int xStart = mat.Width / 2 - targetSize.Width / 2;
+ //int yStart = mat.Height / 2 - targetSize.Height / 2;
+ var newMatRoi = newMat.RoiFromCenter(roi.Size);
+ /*var newMatRoi = new Mat(newMat, new Rectangle(
+ targetSize.Width / 2 - roi.Width / 2,
+ targetSize.Height / 2 - roi.Height / 2,
+ roi.Width,
+ roi.Height
+ ));*/
+ roiMat.CopyTo(newMatRoi);
+ return newMat;
+ }
+ #endregion
+
+ #region Is methods
+
+ /// <summary>
+ /// Gets if a <see cref="Mat"/> is all zeroed by a threshold
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="threshold">Pixel brightness threshold</param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns></returns>
+ public static bool IsZeroed(this Mat mat, byte threshold = 0, int startPos = 0, int length = 0)
+ {
+ return mat.FindFirstPixelGreaterThan(threshold, startPos, length) == -1;
+ }
+ #endregion
+
+ #region Find methods
+
+ /// <summary>
+ /// Finds the first negative (Black) pixel
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns>Pixel position in the span, or -1 if not found</returns>
+ public static int FindFirstNegativePixel(this Mat mat, int startPos = 0, int length = 0)
+ {
+ return mat.FindFirstPixelEqualTo(0, startPos, length);
+ }
- /// <summary>
- /// Gets PNG byte array
- /// </summary>
- /// <param name="mat"></param>
- /// <returns></returns>
- public static byte[] GetPngByes(this Mat mat)
+ /// <summary>
+ /// Finds the first positive pixel
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns>Pixel position in the span, or -1 if not found</returns>
+ public static int FindFirstPositivePixel(this Mat mat, int startPos = 0, int length = 0)
+ {
+ return mat.FindFirstPixelGreaterThan(0, startPos, length);
+ }
+
+ /// <summary>
+ /// Finds the first pixel that is <see cref="value"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="value"></param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns>Pixel position in the span, or -1 if not found</returns>
+ public static int FindFirstPixelEqualTo(this Mat mat, byte value, int startPos = 0, int length = 0)
+ {
+ var span = mat.GetDataByteSpan();
+ if (length <= 0) length = span.Length;
+ for (var i = startPos; i < length; i++)
{
- return CvInvoke.Imencode(".png", mat);
+ if (span[i] == value) return i;
}
- #endregion
- #region Create methods
+ return -1;
+ }
- public static Mat CreateMask(this Mat src, VectorOfVectorOfPoint contours)
+ /// <summary>
+ /// Finds the first pixel that is at less than <see cref="value"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="value"></param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns>Pixel position in the span, or -1 if not found</returns>
+ public static int FindFirstPixelLessThan(this Mat mat, byte value, int startPos = 0, int length = 0)
+ {
+ var span = mat.GetDataByteSpan();
+ if (length <= 0) length = span.Length;
+ for (var i = startPos; i < length; i++)
{
- var mask = src.NewBlank();
- CvInvoke.DrawContours(mask, contours, -1, WhiteColor, -1);
- return mask;
+ if (span[i] < value) return i;
}
- public static Mat CreateMask(this Mat src, Point[][] contours)
+ return -1;
+ }
+
+ /// <summary>
+ /// Finds the first pixel that is at less or equal than <see cref="value"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="value"></param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns>Pixel position in the span, or -1 if not found</returns>
+ public static int FindFirstPixelEqualOrLessThan(this Mat mat, byte value, int startPos = 0, int length = 0)
+ {
+ var span = mat.GetDataByteSpan();
+ if (length <= 0) length = span.Length;
+ for (var i = startPos; i < length; i++)
{
- using var vec = new VectorOfVectorOfPoint(contours);
- return src.CreateMask(vec);
+ if (span[i] <= value) return i;
}
- #endregion
+ return -1;
+ }
- #region Copy methods
- /// <summary>
- /// Copy a region from <see cref="Mat"/> to center of other <see cref="Mat"/>
- /// </summary>
- /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
- /// <param name="size">Size of the center offset</param>
- /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
- public static void CopyCenterToCenter(this Mat src, Size size, Mat dst)
+ /// <summary>
+ /// Finds the first pixel that is at greater than <see cref="value"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="value"></param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns>Pixel position in the span, or -1 if not found</returns>
+ public static int FindFirstPixelGreaterThan(this Mat mat, byte value, int startPos = 0, int length = 0)
+ {
+ var span = mat.GetDataByteSpan();
+ if (length <= 0) length = span.Length;
+ for (var i = startPos; i < length; i++)
{
- var srcRoi = src.RoiFromCenter(size);
- CopyToCenter(srcRoi, dst);
+ if (span[i] > value) return i;
}
- /// <summary>
- /// Copy a region from <see cref="Mat"/> to center of other <see cref="Mat"/>
- /// </summary>
- /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
- /// <param name="region">Region to copy</param>
- /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
- public static void CopyRegionToCenter(this Mat src, Rectangle region, Mat dst)
- {
- var srcRoi = src.Roi(region);
- CopyToCenter(srcRoi, dst);
- }
+ return -1;
+ }
- /// <summary>
- /// Copy a <see cref="Mat"/> to center of other <see cref="Mat"/>
- /// </summary>
- /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
- /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
- public static void CopyToCenter(this Mat src, Mat dst)
+ /// <summary>
+ /// Finds the first pixel that is at equal or greater than <see cref="value"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="value"></param>
+ /// <param name="startPos">Start pixel position</param>
+ /// <param name="length">Pixel span length</param>
+ /// <returns>Pixel position in the span, or -1 if not found</returns>
+ public static int FindFirstPixelEqualOrGreaterThan(this Mat mat, byte value, int startPos = 0, int length = 0)
+ {
+ var span = mat.GetDataByteSpan();
+ if (length <= 0) length = span.Length;
+ for (var i = startPos; i < length; i++)
{
- var srcStep = src.GetRealStep();
- var dstStep = dst.GetRealStep();
- var dx = Math.Abs(dstStep - srcStep) / 2;
- var dy = Math.Abs(dst.Height - src.Height) / 2;
-
- if (src.Size == dst.Size)
- {
- src.CopyTo(dst);
- return;
- }
-
- if (dstStep > srcStep && dst.Height > src.Height)
- {
- using var dstRoi = dst.Roi(new Rectangle(dx, dy, src.Width, src.Height));
- src.CopyTo(dstRoi);
- return;
- }
-
- if (dstStep < srcStep && dst.Height < src.Height)
- {
- using var srcRoi = src.Roi(new Rectangle(dx, dy, dst.Width, dst.Height));
- srcRoi.CopyTo(dst);
- return;
- }
-
- throw new InvalidOperationException("Unable to copy, out of bounds");
+ if (span[i] >= value) return i;
}
- public static void CopyAreasSmallerThan(this Mat src, double threshold, Mat dst)
- {
- if (threshold <= 1) return;
- using var contours = src.FindContours(out var hierarchy, RetrType.Tree);
- var contourGroups = EmguContours.GetContoursInGroups(contours, hierarchy);
-
- var mask = src.NewBlank();
- uint drawContours = 0;
- foreach (var contourGroup in contourGroups)
- {
- using var selectedContours = new VectorOfVectorOfPoint();
- foreach (var group in contourGroup)
- {
- var area = EmguContours.GetContourArea(group);
- if (area >= threshold) continue;
- drawContours++;
- selectedContours.Push(group);
- }
-
- if (selectedContours.Size == 0) continue;
- CvInvoke.DrawContours(mask, selectedContours, -1, WhiteColor, -1);
-
- }
- if (drawContours > 0) src.CopyTo(dst, mask);
- }
+ return -1;
+ }
+ #endregion
- public static void CopyAreasLargerThan(this Mat src, double threshold, Mat dst)
+ #region Transform methods
+ public static void Transform(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear)
+ {
+ //var dst = new Mat(src.Size, src.Depth, src.NumberOfChannels);
+ using var translateTransform = new Matrix<double>(2, 3)
{
- if (threshold <= 0) return;
- using var contours = src.FindContours(out var hierarchy, RetrType.Tree);
- var contourGroups = EmguContours.GetContoursInGroups(contours, hierarchy);
-
- var mask = src.NewBlank();
- uint drawContours = 0;
- foreach (var contourGroup in contourGroups)
- {
- using var selectedContours = new VectorOfVectorOfPoint();
- foreach (var group in contourGroup)
- {
- var area = EmguContours.GetContourArea(group);
- if (area <= threshold) continue;
- drawContours++;
- selectedContours.Push(group);
- }
-
- if (selectedContours.Size == 0) continue;
- CvInvoke.DrawContours(mask, selectedContours, -1, WhiteColor, -1);
-
- }
- if(drawContours > 0) src.CopyTo(dst, mask);
- }
- #endregion
-
- #region Roi methods
+ [0, 0] = xScale, // xScale
+ [1, 1] = yScale, // yScale
+ [0, 2] = xTrans, //x translation + compensation of x scaling
+ [1, 2] = yTrans // y translation + compensation of y scaling
+ };
+ CvInvoke.WarpAffine(src, src, translateTransform, dstSize.IsEmpty ? src.Size : dstSize, interpolation);
+ }
- /// <summary>
- /// Gets a Roi, but return source when roi is empty or have same size as source
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="roi"></param>
- /// <returns></returns>
- public static Mat Roi(this Mat mat, Rectangle roi)
+ /// <summary>
+ /// Rotates a Mat by an angle while keeping the image size
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="angle">Angle in degrees to rotate</param>
+ /// <param name="newSize"></param>
+ /// <param name="scale"></param>
+ public static void Rotate(this Mat src, double angle, Size newSize = default, double scale = 1.0) => Rotate(src, src, angle, newSize, scale);
+
+ /// <summary>
+ /// Rotates a Mat by an angle while keeping the image size
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="dst"></param>
+ /// <param name="angle"></param>
+ /// <param name="scale"></param>
+ public static void Rotate(this Mat src, Mat dst, double angle, Size newSize = default, double scale = 1.0)
+ {
+ if (angle % 360 == 0 && scale == 1.0) return;
+ if (newSize.IsEmpty)
{
- return roi.IsEmpty || roi.Size == mat.Size ? mat : new Mat(mat, roi);
+ newSize = src.Size;
}
+
+ var halfWidth = src.Width / 2.0f;
+ var halfHeight = src.Height / 2.0f;
+ using var translateTransform = new Matrix<double>(2, 3);
+ CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform);
- /// <summary>
- /// Gets a Roi at x=0 and y=0 given a size, but return source when roi is empty or have same size as source
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- public static Mat Roi(this Mat mat, Size size)
+ if (src.Size != newSize)
{
- return size.IsEmpty || size == mat.Size ? mat : new Mat(mat, new(Point.Empty, size));
+ // adjust the rotation matrix to take into account translation
+ translateTransform[0, 2] += newSize.Width / 2.0 - halfWidth;
+ translateTransform[1, 2] += newSize.Height / 2.0 - halfHeight;
}
- /// <summary>
- /// Gets a Roi from a mat size, but return source when roi is empty or have same size as source
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="fromMat"></param>
- /// <returns></returns>
- public static Mat Roi(this Mat mat, Mat fromMat) => mat.Roi(fromMat.Size);
-
- /// <summary>
- /// Gets a Roi from center, but return source when have same size as source
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="size"></param>
- /// <returns></returns>
- public static Mat RoiFromCenter(this Mat mat, Size size)
- {
- if(mat.Size == size) return mat;
+ CvInvoke.WarpAffine(src, dst, translateTransform, newSize);
+ }
- var newRoi = mat.Roi(new Rectangle(
- mat.Width / 2 - size.Width / 2,
- mat.Height / 2 - size.Height / 2,
- size.Width,
- size.Height
- ));
+ /// <summary>
+ /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="angle"></param>
+ /// <param name="scale"></param>
+ public static void RotateAdjustBounds(this Mat src, double angle, double scale = 1.0) => RotateAdjustBounds(src, src, angle, scale);
+
+ /// <summary>
+ /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="dst"></param>
+ /// <param name="angle"></param>
+ /// <param name="scale"></param>
+ public static void RotateAdjustBounds(this Mat src, Mat dst, double angle, double scale = 1.0)
+ {
+ if (angle % 360 == 0 && scale == 1.0) return;
+ var halfWidth = src.Width / 2.0f;
+ var halfHeight = src.Height / 2.0f;
+ using var translateTransform = new Matrix<double>(2, 3);
+ CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform);
+ var cos = Math.Abs(translateTransform[0, 0]);
+ var sin = Math.Abs(translateTransform[0, 1]);
- return newRoi;
- }
+ // compute the new bounding dimensions of the image
+ int newWidth = (int) (src.Height * sin + src.Width * cos);
+ int newHeight = (int) (src.Height * cos + src.Width * sin);
- /// <summary>
- /// Gets a new mat obtained from it center at a target size and roi
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="targetSize"></param>
- /// <param name="roi"></param>
- /// <returns></returns>
- public static Mat NewMatFromCenterRoi(this Mat mat, Size targetSize, Rectangle roi)
- {
- if (targetSize == mat.Size) return mat.Clone();
- var newMat = InitMat(targetSize);
- var roiMat = mat.Roi(roi);
-
- //int xStart = mat.Width / 2 - targetSize.Width / 2;
- //int yStart = mat.Height / 2 - targetSize.Height / 2;
- var newMatRoi = newMat.RoiFromCenter(roi.Size);
- /*var newMatRoi = new Mat(newMat, new Rectangle(
- targetSize.Width / 2 - roi.Width / 2,
- targetSize.Height / 2 - roi.Height / 2,
- roi.Width,
- roi.Height
- ));*/
- roiMat.CopyTo(newMatRoi);
- return newMat;
- }
- #endregion
+ // adjust the rotation matrix to take into account translation
+ translateTransform[0, 2] += newWidth / 2.0 - halfWidth;
+ translateTransform[1, 2] += newHeight / 2.0 - halfHeight;
- #region Is methods
- /// <summary>
- /// Gets if a <see cref="Mat"/> is all zeroed by a threshold
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="threshold">Pixel brightness threshold</param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns></returns>
- public static bool IsZeroed(this Mat mat, byte threshold = 0, int startPos = 0, int length = 0)
- {
- return mat.FindFirstPixelGreaterThan(threshold, startPos, length) == -1;
- }
- #endregion
+ CvInvoke.WarpAffine(src, dst, translateTransform, new Size(newWidth, newHeight));
+ }
- #region Find methods
+ /// <summary>
+ /// Scale image from it center, preserving src bounds
+ /// https://stackoverflow.com/a/62543674/933976
+ /// </summary>
+ /// <param name="src"><see cref="Mat"/> to transform</param>
+ /// <param name="xScale">X scale factor</param>
+ /// <param name="yScale">Y scale factor</param>
+ /// <param name="xTrans">X translation</param>
+ /// <param name="yTrans">Y translation</param>
+ /// <param name="dstSize">Destination size</param>
+ /// <param name="interpolation">Interpolation mode</param>
+ public static void TransformFromCenter(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear)
+ {
+ src.Transform(xScale, yScale,
+ xTrans + (src.Width - src.Width * xScale) / 2.0,
+ yTrans + (src.Height - src.Height * yScale) / 2.0, dstSize, interpolation);
+ }
+ #endregion
+
+ #region Draw Methods
+
+ /// <summary>
+ /// Draw a rotated square around a center point
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="size"></param>
+ /// <param name="center"></param>
+ /// <param name="color"></param>
+ /// <param name="angle"></param>
+ /// <param name="thickness"></param>
+ /// <param name="lineType"></param>
+ public static void DrawRotatedSquare(this Mat src, int size, Point center, MCvScalar color, int angle = 0, int thickness = -1, LineType lineType = LineType.EightConnected)
+ => src.DrawRotatedRectangle(new(size, size), center, color, angle, thickness, lineType);
+
+ /// <summary>
+ /// Draw a rotated rectangle around a center point
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="size"></param>
+ /// <param name="center"></param>
+ /// <param name="color"></param>
+ /// <param name="angle"></param>
+ /// <param name="thickness"></param>
+ /// <param name="lineType"></param>
+ public static void DrawRotatedRectangle(this Mat src, Size size, Point center, MCvScalar color, int angle = 0, int thickness = -1, LineType lineType = LineType.EightConnected)
+ {
+ var rect = new RotatedRect(center, size, angle);
+ var vertices = rect.GetVertices();
+ var points = new Point[vertices.Length];
- /// <summary>
- /// Finds the first negative (Black) pixel
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns>Pixel position in the span, or -1 if not found</returns>
- public static int FindFirstNegativePixel(this Mat mat, int startPos = 0, int length = 0)
+ for (int i = 0; i < vertices.Length; i++)
{
- return mat.FindFirstPixelEqualTo(0, startPos, length);
+ points[i] = new(
+ (int)Math.Round(vertices[i].X),
+ (int)Math.Round(vertices[i].Y)
+ );
}
- /// <summary>
- /// Finds the first positive pixel
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns>Pixel position in the span, or -1 if not found</returns>
- public static int FindFirstPositivePixel(this Mat mat, int startPos = 0, int length = 0)
+ if (thickness <= 0)
{
- return mat.FindFirstPixelGreaterThan(0, startPos, length);
+ using var vec = new VectorOfPoint(points);
+ CvInvoke.FillConvexPoly(src, vec, color, lineType);
}
-
- /// <summary>
- /// Finds the first pixel that is <see cref="value"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="value"></param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns>Pixel position in the span, or -1 if not found</returns>
- public static int FindFirstPixelEqualTo(this Mat mat, byte value, int startPos = 0, int length = 0)
+ else
{
- var span = mat.GetDataByteSpan();
- if (length <= 0) length = span.Length;
- for (var i = startPos; i < length; i++)
- {
- if (span[i] == value) return i;
- }
-
- return -1;
+ CvInvoke.Polylines(src, points, true, color, thickness, lineType);
}
+ }
- /// <summary>
- /// Finds the first pixel that is at less than <see cref="value"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="value"></param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns>Pixel position in the span, or -1 if not found</returns>
- public static int FindFirstPixelLessThan(this Mat mat, byte value, int startPos = 0, int length = 0)
+ /// <summary>
+ /// Draw a polygon given number of sides and length
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="sides">Number of polygon sides, Special: use 1 to draw a line and >= 100 to draw a native OpenCV circle</param>
+ /// <param name="radius">Radius</param>
+ /// <param name="center">Center position</param>
+ /// <param name="color"></param>
+ /// <param name="startingAngle"></param>
+ /// <param name="thickness"></param>
+ /// <param name="lineType"></param>
+ /// <param name="flip"></param>
+ public static void DrawPolygon(this Mat src, int sides, int radius, Point center, MCvScalar color, double startingAngle = 0, int thickness = -1, LineType lineType = LineType.EightConnected, FlipType? flip = null)
+ {
+ if (sides == 1)
{
- var span = mat.GetDataByteSpan();
- if (length <= 0) length = span.Length;
- for (var i = startPos; i < length; i++)
+ var point1 = new Point(center.X - radius, center.Y);
+ var point2 = new Point(center.X + radius, center.Y);
+ point1 = point1.Rotate(startingAngle, center);
+ point2 = point2.Rotate(startingAngle, center);
+
+ if (flip is FlipType.Horizontal or FlipType.Both)
{
- if (span[i] < value) return i;
+ var newPoint1 = new Point(point2.X, point1.Y);
+ var newPoint2 = new Point(point1.X, point2.Y);
+ point1 = newPoint1;
+ point2 = newPoint2;
}
- return -1;
- }
-
- /// <summary>
- /// Finds the first pixel that is at less or equal than <see cref="value"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="value"></param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns>Pixel position in the span, or -1 if not found</returns>
- public static int FindFirstPixelEqualOrLessThan(this Mat mat, byte value, int startPos = 0, int length = 0)
- {
- var span = mat.GetDataByteSpan();
- if (length <= 0) length = span.Length;
- for (var i = startPos; i < length; i++)
+ if (flip is FlipType.Vertical or FlipType.Both)
{
- if (span[i] <= value) return i;
+ var newPoint1 = new Point(point1.X, point2.Y);
+ var newPoint2 = new Point(point2.X, point1.Y);
+ point1 = newPoint1;
+ point2 = newPoint2;
}
- return -1;
+ CvInvoke.Line(src, point1, point2, color, thickness < 1 ? 1 : thickness, lineType);
+ return;
}
-
- /// <summary>
- /// Finds the first pixel that is at greater than <see cref="value"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="value"></param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns>Pixel position in the span, or -1 if not found</returns>
- public static int FindFirstPixelGreaterThan(this Mat mat, byte value, int startPos = 0, int length = 0)
+ if (sides >= 100)
{
- var span = mat.GetDataByteSpan();
- if (length <= 0) length = span.Length;
- for (var i = startPos; i < length; i++)
- {
- if (span[i] > value) return i;
- }
-
- return -1;
+ CvInvoke.Circle(src, center, radius, color, thickness, lineType);
+ return;
}
- /// <summary>
- /// Finds the first pixel that is at equal or greater than <see cref="value"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="value"></param>
- /// <param name="startPos">Start pixel position</param>
- /// <param name="length">Pixel span length</param>
- /// <returns>Pixel position in the span, or -1 if not found</returns>
- public static int FindFirstPixelEqualOrGreaterThan(this Mat mat, byte value, int startPos = 0, int length = 0)
+ var points = DrawingExtensions.GetPolygonVertices(sides, radius, center, startingAngle,
+ flip is FlipType.Horizontal or FlipType.Both,
+ flip is FlipType.Vertical or FlipType.Both);
+
+ if (thickness <= 0)
{
- var span = mat.GetDataByteSpan();
- if (length <= 0) length = span.Length;
- for (var i = startPos; i < length; i++)
- {
- if (span[i] >= value) return i;
- }
-
- return -1;
+ using var vec = new VectorOfPoint(points);
+ CvInvoke.FillConvexPoly(src, vec, color, lineType);
}
- #endregion
-
- #region Transform methods
- public static void Transform(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear)
+ else
{
- //var dst = new Mat(src.Size, src.Depth, src.NumberOfChannels);
- using var translateTransform = new Matrix<double>(2, 3)
- {
- [0, 0] = xScale, // xScale
- [1, 1] = yScale, // yScale
- [0, 2] = xTrans, //x translation + compensation of x scaling
- [1, 2] = yTrans // y translation + compensation of y scaling
- };
- CvInvoke.WarpAffine(src, src, translateTransform, dstSize.IsEmpty ? src.Size : dstSize, interpolation);
+ CvInvoke.Polylines(src, points, true, color, thickness, lineType);
}
+
+ }
+ #endregion
+ #region Text methods
+ public enum PutTextLineAlignment : byte
+ {
/// <summary>
- /// Rotates a Mat by an angle while keeping the image size
+ /// Left aligned without trimming, openCV default call
/// </summary>
- /// <param name="src"></param>
- /// <param name="angle">Angle in degrees to rotate</param>
- /// <param name="newSize"></param>
- /// <param name="scale"></param>
- public static void Rotate(this Mat src, double angle, Size newSize = default, double scale = 1.0) => Rotate(src, src, angle, newSize, scale);
+ None,
/// <summary>
- /// Rotates a Mat by an angle while keeping the image size
+ /// Left aligned and trimmed
/// </summary>
- /// <param name="src"></param>
- /// <param name="dst"></param>
- /// <param name="angle"></param>
- /// <param name="scale"></param>
- public static void Rotate(this Mat src, Mat dst, double angle, Size newSize = default, double scale = 1.0)
- {
- if (angle % 360 == 0 && scale == 1.0) return;
- if (newSize.IsEmpty)
- {
- newSize = src.Size;
- }
-
- var halfWidth = src.Width / 2.0f;
- var halfHeight = src.Height / 2.0f;
- using var translateTransform = new Matrix<double>(2, 3);
- CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform);
-
- if (src.Size != newSize)
- {
- // adjust the rotation matrix to take into account translation
- translateTransform[0, 2] += newSize.Width / 2.0 - halfWidth;
- translateTransform[1, 2] += newSize.Height / 2.0 - halfHeight;
- }
-
- CvInvoke.WarpAffine(src, dst, translateTransform, newSize);
- }
+ Left,
/// <summary>
- /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content
+ /// Center aligned and trimmed
/// </summary>
- /// <param name="src"></param>
- /// <param name="angle"></param>
- /// <param name="scale"></param>
- public static void RotateAdjustBounds(this Mat src, double angle, double scale = 1.0) => RotateAdjustBounds(src, src, angle, scale);
+ Center,
/// <summary>
- /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content
+ /// Right aligned and trimmed
/// </summary>
- /// <param name="src"></param>
- /// <param name="dst"></param>
- /// <param name="angle"></param>
- /// <param name="scale"></param>
- public static void RotateAdjustBounds(this Mat src, Mat dst, double angle, double scale = 1.0)
+ Right
+ }
+
+ public static string PutTextLineAlignmentTrim(string line, PutTextLineAlignment lineAlignment)
+ {
+ switch (lineAlignment)
{
- if (angle % 360 == 0 && scale == 1.0) return;
- var halfWidth = src.Width / 2.0f;
- var halfHeight = src.Height / 2.0f;
- using var translateTransform = new Matrix<double>(2, 3);
- CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform);
- var cos = Math.Abs(translateTransform[0, 0]);
- var sin = Math.Abs(translateTransform[0, 1]);
-
- // compute the new bounding dimensions of the image
- int newWidth = (int) (src.Height * sin + src.Width * cos);
- int newHeight = (int) (src.Height * cos + src.Width * sin);
+ case PutTextLineAlignment.None:
+ return line.TrimEnd();
+ case PutTextLineAlignment.Left:
+ case PutTextLineAlignment.Center:
+ case PutTextLineAlignment.Right:
+ return line.Trim();
+ default:
+ throw new ArgumentOutOfRangeException(nameof(lineAlignment), lineAlignment, null);
+ }
+ }
- // adjust the rotation matrix to take into account translation
- translateTransform[0, 2] += newWidth / 2.0 - halfWidth;
- translateTransform[1, 2] += newHeight / 2.0 - halfHeight;
+ public static Size GetTextSizeExtended(string text, FontFace fontFace, double fontScale, int thickness, ref int baseLine, PutTextLineAlignment lineAlignment = default)
+ => GetTextSizeExtended(text, fontFace, fontScale, thickness, 0, ref baseLine, lineAlignment);
+ public static Size GetTextSizeExtended(string text, FontFace fontFace, double fontScale, int thickness, int lineGapOffset, ref int baseLine, PutTextLineAlignment lineAlignment = default)
+ {
+ text = text.TrimEnd('\n', '\r', ' ');
+ var lines = text.Split(StaticObjects.LineBreakCharacters, StringSplitOptions.None);
+ var textSize = CvInvoke.GetTextSize(text, fontFace, fontScale, thickness, ref baseLine);
+ if (lines.Length is 0 or 1) return textSize;
- CvInvoke.WarpAffine(src, dst, translateTransform, new Size(newWidth, newHeight));
- }
+ var lineGap = textSize.Height / 3 + lineGapOffset;
+ var width = 0;
+ var height = lines.Length * (lineGap + textSize.Height) - lineGap;
- /// <summary>
- /// Scale image from it center, preserving src bounds
- /// https://stackoverflow.com/a/62543674/933976
- /// </summary>
- /// <param name="src"><see cref="Mat"/> to transform</param>
- /// <param name="xScale">X scale factor</param>
- /// <param name="yScale">Y scale factor</param>
- /// <param name="xTrans">X translation</param>
- /// <param name="yTrans">Y translation</param>
- /// <param name="dstSize">Destination size</param>
- /// <param name="interpolation">Interpolation mode</param>
- public static void TransformFromCenter(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear)
+ for (var i = 0; i < lines.Length; i++)
{
- src.Transform(xScale, yScale,
- xTrans + (src.Width - src.Width * xScale) / 2.0,
- yTrans + (src.Height - src.Height * yScale) / 2.0, dstSize, interpolation);
- }
- #endregion
+ lines[i] = PutTextLineAlignmentTrim(lines[i], lineAlignment);
- #region Draw Methods
-
- /// <summary>
- /// Draw a rotated square around a center point
- /// </summary>
- /// <param name="src"></param>
- /// <param name="size"></param>
- /// <param name="center"></param>
- /// <param name="color"></param>
- /// <param name="angle"></param>
- /// <param name="thickness"></param>
- /// <param name="lineType"></param>
- public static void DrawRotatedSquare(this Mat src, int size, Point center, MCvScalar color, int angle = 0, int thickness = -1, LineType lineType = LineType.EightConnected)
- => src.DrawRotatedRectangle(new(size, size), center, color, angle, thickness, lineType);
+ if (string.IsNullOrWhiteSpace(lines[i])) continue;
+ int baseLineRef = 0;
+ var lineSize = CvInvoke.GetTextSize(lines[i], fontFace, fontScale, thickness, ref baseLineRef);
+ width = Math.Max(width, lineSize.Width);
+ }
- /// <summary>
- /// Draw a rotated rectangle around a center point
- /// </summary>
- /// <param name="src"></param>
- /// <param name="size"></param>
- /// <param name="center"></param>
- /// <param name="color"></param>
- /// <param name="angle"></param>
- /// <param name="thickness"></param>
- /// <param name="lineType"></param>
- public static void DrawRotatedRectangle(this Mat src, Size size, Point center, MCvScalar color, int angle = 0, int thickness = -1, LineType lineType = LineType.EightConnected)
- {
- var rect = new RotatedRect(center, size, angle);
- var vertices = rect.GetVertices();
- var points = new Point[vertices.Length];
- for (int i = 0; i < vertices.Length; i++)
- {
- points[i] = new(
- (int)Math.Round(vertices[i].X),
- (int)Math.Round(vertices[i].Y)
- );
- }
+ return new(width, height);
+ }
- if (thickness <= 0)
- {
- using var vec = new VectorOfPoint(points);
- CvInvoke.FillConvexPoly(src, vec, color, lineType);
- }
- else
- {
- CvInvoke.Polylines(src, points, true, color, thickness, lineType);
- }
- }
+ public static void PutTextExtended(this IInputOutputArray src, string text, Point org, FontFace fontFace, double fontScale,
+ MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected,
+ bool bottomLeftOrigin = false, PutTextLineAlignment lineAlignment = default)
+ => src.PutTextExtended(text, org, fontFace, fontScale, color, thickness, 0, lineType, bottomLeftOrigin, lineAlignment);
- /// <summary>
- /// Draw a polygon given number of sides and length
- /// </summary>
- /// <param name="src"></param>
- /// <param name="sides">Number of polygon sides, Special: use 1 to draw a line and >= 100 to draw a native OpenCV circle</param>
- /// <param name="radius">Radius</param>
- /// <param name="center">Center position</param>
- /// <param name="color"></param>
- /// <param name="startingAngle"></param>
- /// <param name="thickness"></param>
- /// <param name="lineType"></param>
- /// <param name="flip"></param>
- public static void DrawPolygon(this Mat src, int sides, int radius, Point center, MCvScalar color, double startingAngle = 0, int thickness = -1, LineType lineType = LineType.EightConnected, FlipType? flip = null)
+ /// <summary>
+ /// Extended OpenCV PutText to accepting line breaks and line alignment
+ /// </summary>
+ public static void PutTextExtended(this IInputOutputArray src, string text, Point org, FontFace fontFace, double fontScale,
+ MCvScalar color, int thickness = 1, int lineGapOffset = 0, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, PutTextLineAlignment lineAlignment = default)
+ {
+ text = text.TrimEnd('\n', '\r', ' ');
+ var lines = text.Split(StaticObjects.LineBreakCharacters, StringSplitOptions.None);
+
+ switch (lines.Length)
{
- if (sides == 1)
- {
- var point1 = new Point(center.X - radius, center.Y);
- var point2 = new Point(center.X + radius, center.Y);
- point1 = point1.Rotate(startingAngle, center);
- point2 = point2.Rotate(startingAngle, center);
-
- if (flip is FlipType.Horizontal or FlipType.Both)
- {
- var newPoint1 = new Point(point2.X, point1.Y);
- var newPoint2 = new Point(point1.X, point2.Y);
- point1 = newPoint1;
- point2 = newPoint2;
- }
-
- if (flip is FlipType.Vertical or FlipType.Both)
- {
- var newPoint1 = new Point(point1.X, point2.Y);
- var newPoint2 = new Point(point2.X, point1.Y);
- point1 = newPoint1;
- point2 = newPoint2;
- }
-
- CvInvoke.Line(src, point1, point2, color, thickness < 1 ? 1 : thickness, lineType);
+ case 0:
return;
- }
- if (sides >= 100)
- {
- CvInvoke.Circle(src, center, radius, color, thickness, lineType);
+ case 1:
+ CvInvoke.PutText(src, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
return;
- }
-
- var points = DrawingExtensions.GetPolygonVertices(sides, radius, center, startingAngle,
- flip is FlipType.Horizontal or FlipType.Both,
- flip is FlipType.Vertical or FlipType.Both);
-
- if (thickness <= 0)
- {
- using var vec = new VectorOfPoint(points);
- CvInvoke.FillConvexPoly(src, vec, color, lineType);
- }
- else
- {
- CvInvoke.Polylines(src, points, true, color, thickness, lineType);
- }
-
}
- #endregion
- #region Text methods
- public enum PutTextLineAlignment : byte
- {
- /// <summary>
- /// Left aligned without trimming, openCV default call
- /// </summary>
- None,
-
- /// <summary>
- /// Left aligned and trimmed
- /// </summary>
- Left,
-
- /// <summary>
- /// Center aligned and trimmed
- /// </summary>
- Center,
-
- /// <summary>
- /// Right aligned and trimmed
- /// </summary>
- Right
- }
+ // Get height of text lines in pixels (height of all lines is the same)
+ int baseLine = 0;
+ var textSize = CvInvoke.GetTextSize(text, fontFace, fontScale, thickness, ref baseLine);
+ var lineGap = textSize.Height / 3 + lineGapOffset;
+ var linesSize = new Size[lines.Length];
+ int width = 0;
- public static string PutTextLineAlignmentTrim(string line, PutTextLineAlignment lineAlignment)
+ // Sanitize lines
+ for (var i = 0; i < lines.Length; i++)
{
- switch (lineAlignment)
- {
- case PutTextLineAlignment.None:
- return line.TrimEnd();
- case PutTextLineAlignment.Left:
- case PutTextLineAlignment.Center:
- case PutTextLineAlignment.Right:
- return line.Trim();
- default:
- throw new ArgumentOutOfRangeException(nameof(lineAlignment), lineAlignment, null);
- }
+ lines[i] = PutTextLineAlignmentTrim(lines[i], lineAlignment);
}
- public static Size GetTextSizeExtended(string text, FontFace fontFace, double fontScale, int thickness, ref int baseLine, PutTextLineAlignment lineAlignment = default)
- => GetTextSizeExtended(text, fontFace, fontScale, thickness, 0, ref baseLine, lineAlignment);
- public static Size GetTextSizeExtended(string text, FontFace fontFace, double fontScale, int thickness, int lineGapOffset, ref int baseLine, PutTextLineAlignment lineAlignment = default)
+ // If line needs alignment, calculate the size for each line
+ if (lineAlignment is not PutTextLineAlignment.Left and not PutTextLineAlignment.None)
{
- text = text.TrimEnd('\n', '\r', ' ');
- var lines = text.Split(StaticObjects.LineBreakCharacters, StringSplitOptions.None);
- var textSize = CvInvoke.GetTextSize(text, fontFace, fontScale, thickness, ref baseLine);
-
- if (lines.Length is 0 or 1) return textSize;
-
- var lineGap = textSize.Height / 3 + lineGapOffset;
- var width = 0;
- var height = lines.Length * (lineGap + textSize.Height) - lineGap;
-
for (var i = 0; i < lines.Length; i++)
{
- lines[i] = PutTextLineAlignmentTrim(lines[i], lineAlignment);
-
if (string.IsNullOrWhiteSpace(lines[i])) continue;
int baseLineRef = 0;
- var lineSize = CvInvoke.GetTextSize(lines[i], fontFace, fontScale, thickness, ref baseLineRef);
- width = Math.Max(width, lineSize.Width);
+ linesSize[i] = CvInvoke.GetTextSize(lines[i], fontFace, fontScale, thickness, ref baseLineRef);
+ width = Math.Max(width, linesSize[i].Width);
}
-
-
- return new(width, height);
}
- public static void PutTextExtended(this Mat src, string text, Point org, FontFace fontFace, double fontScale,
- MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected,
- bool bottomLeftOrigin = false, PutTextLineAlignment lineAlignment = default)
- => src.PutTextExtended(text, org, fontFace, fontScale, color, thickness, 0, lineType, bottomLeftOrigin, lineAlignment);
-
- /// <summary>
- /// Extended OpenCV PutText to accepting line breaks and line alignment
- /// </summary>
- public static void PutTextExtended(this Mat src, string text, Point org, FontFace fontFace, double fontScale,
- MCvScalar color, int thickness = 1, int lineGapOffset = 0, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, PutTextLineAlignment lineAlignment = default)
+ for (var i = 0; i < lines.Length; i++)
{
- text = text.TrimEnd('\n', '\r', ' ');
- var lines = text.Split(StaticObjects.LineBreakCharacters, StringSplitOptions.None);
-
- switch (lines.Length)
- {
- case 0:
- return;
- case 1:
- CvInvoke.PutText(src, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
- return;
- }
-
- // Get height of text lines in pixels (height of all lines is the same)
- int baseLine = 0;
- var textSize = CvInvoke.GetTextSize(text, fontFace, fontScale, thickness, ref baseLine);
- var lineGap = textSize.Height / 3 + lineGapOffset;
- var linesSize = new Size[lines.Length];
- int width = 0;
-
- // Sanitize lines
- for (var i = 0; i < lines.Length; i++)
- {
- lines[i] = PutTextLineAlignmentTrim(lines[i], lineAlignment);
- }
-
- // If line needs alignment, calculate the size for each line
- if (lineAlignment is not PutTextLineAlignment.Left and not PutTextLineAlignment.None)
+ if(string.IsNullOrWhiteSpace(lines[i])) continue;
+
+ int x = lineAlignment switch
{
- for (var i = 0; i < lines.Length; i++)
- {
- if (string.IsNullOrWhiteSpace(lines[i])) continue;
- int baseLineRef = 0;
- linesSize[i] = CvInvoke.GetTextSize(lines[i], fontFace, fontScale, thickness, ref baseLineRef);
- width = Math.Max(width, linesSize[i].Width);
- }
- }
+ PutTextLineAlignment.None or PutTextLineAlignment.Left => org.X,
+ PutTextLineAlignment.Center => org.X + (width - linesSize[i].Width) / 2,
+ PutTextLineAlignment.Right => org.X + width - linesSize[i].Width,
+ _ => throw new ArgumentOutOfRangeException(nameof(lineAlignment), lineAlignment, null)
+ };
- for (var i = 0; i < lines.Length; i++)
- {
- if(string.IsNullOrWhiteSpace(lines[i])) continue;
-
- int x = lineAlignment switch
- {
- PutTextLineAlignment.None or PutTextLineAlignment.Left => org.X,
- PutTextLineAlignment.Center => org.X + (width - linesSize[i].Width) / 2,
- PutTextLineAlignment.Right => org.X + width - linesSize[i].Width,
- _ => throw new ArgumentOutOfRangeException(nameof(lineAlignment), lineAlignment, null)
- };
-
- // Find total size of text block before this line
- var lineYAdjustment = i * (lineGap + textSize.Height);
- // Move text down from original line based on line number
- int lineY = !bottomLeftOrigin ? org.Y + lineYAdjustment : org.Y - lineYAdjustment;
- CvInvoke.PutText(src, lines[i], new Point(x, lineY), fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
- }
+ // Find total size of text block before this line
+ var lineYAdjustment = i * (lineGap + textSize.Height);
+ // Move text down from original line based on line number
+ int lineY = !bottomLeftOrigin ? org.Y + lineYAdjustment : org.Y - lineYAdjustment;
+ CvInvoke.PutText(src, lines[i], new Point(x, lineY), fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
}
+ }
- /// <summary>
- /// Extended OpenCV PutText to accepting line breaks, line alignment and rotation
- /// </summary>
- public static void PutTextRotated(this Mat src, string text, Point org, FontFace fontFace, double fontScale,
- MCvScalar color,
- int thickness = 1, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false,
- PutTextLineAlignment lineAlignment = default, double angle = 0)
- => src.PutTextRotated(text, org, fontFace, fontScale, color, thickness, 0, lineType, bottomLeftOrigin,
- lineAlignment, angle);
-
- /// <summary>
- /// Extended OpenCV PutText to accepting line breaks, line alignment and rotation
- /// </summary>
- public static void PutTextRotated(this Mat src, string text, Point org, FontFace fontFace, double fontScale, MCvScalar color,
+ /// <summary>
+ /// Extended OpenCV PutText to accepting line breaks, line alignment and rotation
+ /// </summary>
+ public static void PutTextRotated(this Mat src, string text, Point org, FontFace fontFace, double fontScale,
+ MCvScalar color,
+ int thickness = 1, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false,
+ PutTextLineAlignment lineAlignment = default, double angle = 0)
+ => src.PutTextRotated(text, org, fontFace, fontScale, color, thickness, 0, lineType, bottomLeftOrigin,
+ lineAlignment, angle);
+
+ /// <summary>
+ /// Extended OpenCV PutText to accepting line breaks, line alignment and rotation
+ /// </summary>
+ public static void PutTextRotated(this Mat src, string text, Point org, FontFace fontFace, double fontScale, MCvScalar color,
int thickness = 1, int lineGapOffset = 0, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, PutTextLineAlignment lineAlignment = default, double angle = 0)
+ {
+ if (angle % 360 == 0) // No rotation needed, cheaper cycle
{
- if (angle % 360 == 0) // No rotation needed, cheaper cycle
- {
- src.PutTextExtended(text, org, fontFace, fontScale, color, thickness, lineGapOffset, lineType, bottomLeftOrigin, lineAlignment);
- return;
- }
+ src.PutTextExtended(text, org, fontFace, fontScale, color, thickness, lineGapOffset, lineType, bottomLeftOrigin, lineAlignment);
+ return;
+ }
- using var rotatedSrc = src.Clone();
- rotatedSrc.RotateAdjustBounds(-angle);
- var sizeDifference = (rotatedSrc.Size - src.Size).Half();
- org.Offset(sizeDifference.ToPoint());
- org = org.Rotate(-angle, new Point(rotatedSrc.Size.Width / 2, rotatedSrc.Size.Height / 2));
- rotatedSrc.PutTextExtended(text, org, fontFace, fontScale, color, thickness, lineGapOffset, lineType, bottomLeftOrigin, lineAlignment);
+ using var rotatedSrc = src.Clone();
+ rotatedSrc.RotateAdjustBounds(-angle);
+ var sizeDifference = (rotatedSrc.Size - src.Size).Half();
+ org.Offset(sizeDifference.ToPoint());
+ org = org.Rotate(-angle, new Point(rotatedSrc.Size.Width / 2, rotatedSrc.Size.Height / 2));
+ rotatedSrc.PutTextExtended(text, org, fontFace, fontScale, color, thickness, lineGapOffset, lineType, bottomLeftOrigin, lineAlignment);
- using var mask = rotatedSrc.NewBlank();
- mask.PutTextExtended(text, org, fontFace, fontScale, WhiteColor, thickness, lineGapOffset, lineType, bottomLeftOrigin, lineAlignment);
-
- rotatedSrc.Rotate(angle, src.Size);
- mask.Rotate(angle, src.Size);
+ using var mask = rotatedSrc.NewBlank();
+ mask.PutTextExtended(text, org, fontFace, fontScale, WhiteColor, thickness, lineGapOffset, lineType, bottomLeftOrigin, lineAlignment);
- rotatedSrc.CopyTo(src, mask);
- }
- #endregion
+ rotatedSrc.Rotate(angle, src.Size);
+ mask.Rotate(angle, src.Size);
- #region Utilities methods
+ rotatedSrc.CopyTo(src, mask);
+ }
+ #endregion
+
+ #region Utilities methods
+
+ /// <summary>
+ /// Retrieves contours from the binary image as a contour tree. The pointer firstContour is filled by the function. It is provided as a convenient way to obtain the hierarchy value as int[,].
+ /// The function modifies the source image content
+ /// </summary>
+ /// <param name="mat">The source 8-bit single channel image. Non-zero pixels are treated as 1s, zero pixels remain 0s - that is image treated as binary. To get such a binary image from grayscale, one may use cvThreshold, cvAdaptiveThreshold or cvCanny. The function modifies the source image content</param>
+ /// <param name="mode">Retrieval mode</param>
+ /// <param name="method">Approximation method (for all the modes, except CV_RETR_RUNS, which uses built-in approximation). </param>
+ /// <param name="offset">Offset, by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context</param>
+ /// <returns>The contour hierarchy</returns>
+ public static VectorOfVectorOfPoint FindContours(this IInputOutputArray mat, RetrType mode = RetrType.List, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple, Point offset = default)
+ {
+ using var hierarchy = new Mat();
+ var contours = new VectorOfVectorOfPoint();
+ CvInvoke.FindContours(mat, contours, hierarchy, mode, method, offset);
+ return contours;
+ }
- /// <summary>
- /// Retrieves contours from the binary image as a contour tree. The pointer firstContour is filled by the function. It is provided as a convenient way to obtain the hierarchy value as int[,].
- /// The function modifies the source image content
- /// </summary>
- /// <param name="mat">The source 8-bit single channel image. Non-zero pixels are treated as 1s, zero pixels remain 0s - that is image treated as binary. To get such a binary image from grayscale, one may use cvThreshold, cvAdaptiveThreshold or cvCanny. The function modifies the source image content</param>
- /// <param name="mode">Retrieval mode</param>
- /// <param name="method">Approximation method (for all the modes, except CV_RETR_RUNS, which uses built-in approximation). </param>
- /// <param name="offset">Offset, by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context</param>
- /// <returns>The contour hierarchy</returns>
- public static VectorOfVectorOfPoint FindContours(this Mat mat, RetrType mode = RetrType.List, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple, Point offset = default)
- {
- using var hierarchy = new Mat();
- var contours = new VectorOfVectorOfPoint();
- CvInvoke.FindContours(mat, contours, hierarchy, mode, method, offset);
- return contours;
- }
+ /*
+ /// <summary>
+ /// Retrieves contours from the binary image as a contour tree. The pointer firstContour is filled by the function. It is provided as a convenient way to obtain the hierarchy value as int[,].
+ /// The function modifies the source image content
+ /// </summary>
+ /// <param name="mat">The source 8-bit single channel image. Non-zero pixels are treated as 1s, zero pixels remain 0s - that is image treated as binary. To get such a binary image from grayscale, one may use cvThreshold, cvAdaptiveThreshold or cvCanny. The function modifies the source image content</param>
+ /// <param name="contours">Detected contours. Each contour is stored as a vector of points.</param>
+ /// <param name="mode">Retrieval mode</param>
+ /// <param name="method">Approximation method (for all the modes, except CV_RETR_RUNS, which uses built-in approximation). </param>
+ /// <param name="offset">Offset, by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context</param>
+ /// <returns>The contour hierarchy</returns>
+ public static int[,] FindContours(this Mat mat, IOutputArray contours, RetrType mode, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple, Point offset = default)
+ {
+ using var hierarchy = new Mat();
+ CvInvoke.FindContours(mat, contours, hierarchy, mode, method, offset);
+ var numArray = new int[hierarchy.Cols, 4];
+ var gcHandle = GCHandle.Alloc(numArray, GCHandleType.Pinned);
+ using (var mat2 = new Mat(hierarchy.Rows, hierarchy.Cols, hierarchy.Depth, 4, gcHandle.AddrOfPinnedObject(), hierarchy.Step))
+ hierarchy.CopyTo(mat2);
+ gcHandle.Free();
+ return numArray;
+ }*/
+
+ /// <summary>
+ /// Retrieves contours from the binary image as a contour tree. The pointer firstContour is filled by the function. It is provided as a convenient way to obtain the hierarchy value as int[,].
+ /// The function modifies the source image content
+ /// </summary>
+ /// <param name="mat">The source 8-bit single channel image. Non-zero pixels are treated as 1s, zero pixels remain 0s - that is image treated as binary. To get such a binary image from grayscale, one may use cvThreshold, cvAdaptiveThreshold or cvCanny. The function modifies the source image content</param>
+ /// <param name="hierarchy">The contour hierarchy</param>
+ /// <param name="mode">Retrieval mode</param>
+ /// <param name="method">Approximation method (for all the modes, except CV_RETR_RUNS, which uses built-in approximation). </param>
+ /// <param name="offset">Offset, by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context</param>
+ /// <returns>Detected contours. Each contour is stored as a vector of points.</returns>
+ public static VectorOfVectorOfPoint FindContours(this IInputOutputArray mat, out int[,] hierarchy, RetrType mode, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple, Point offset = default)
+ {
+ var contours = new VectorOfVectorOfPoint();
+ using var hierarchyMat = new Mat();
+ CvInvoke.FindContours(mat, contours, hierarchyMat, mode, method, offset);
+ hierarchy = new int[hierarchyMat.Cols, 4];
+ var gcHandle = GCHandle.Alloc(hierarchy, GCHandleType.Pinned);
+ using (var mat2 = new Mat(hierarchyMat.Rows, hierarchyMat.Cols, hierarchyMat.Depth, 4, gcHandle.AddrOfPinnedObject(), hierarchyMat.Step))
+ hierarchyMat.CopyTo(mat2);
+ gcHandle.Free();
+ return contours;
+ }
- /*
- /// <summary>
- /// Retrieves contours from the binary image as a contour tree. The pointer firstContour is filled by the function. It is provided as a convenient way to obtain the hierarchy value as int[,].
- /// The function modifies the source image content
- /// </summary>
- /// <param name="mat">The source 8-bit single channel image. Non-zero pixels are treated as 1s, zero pixels remain 0s - that is image treated as binary. To get such a binary image from grayscale, one may use cvThreshold, cvAdaptiveThreshold or cvCanny. The function modifies the source image content</param>
- /// <param name="contours">Detected contours. Each contour is stored as a vector of points.</param>
- /// <param name="mode">Retrieval mode</param>
- /// <param name="method">Approximation method (for all the modes, except CV_RETR_RUNS, which uses built-in approximation). </param>
- /// <param name="offset">Offset, by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context</param>
- /// <returns>The contour hierarchy</returns>
- public static int[,] FindContours(this Mat mat, IOutputArray contours, RetrType mode, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple, Point offset = default)
- {
- using var hierarchy = new Mat();
- CvInvoke.FindContours(mat, contours, hierarchy, mode, method, offset);
- var numArray = new int[hierarchy.Cols, 4];
- var gcHandle = GCHandle.Alloc(numArray, GCHandleType.Pinned);
- using (var mat2 = new Mat(hierarchy.Rows, hierarchy.Cols, hierarchy.Depth, 4, gcHandle.AddrOfPinnedObject(), hierarchy.Step))
- hierarchy.CopyTo(mat2);
- gcHandle.Free();
- return numArray;
- }*/
- /// <summary>
- /// Retrieves contours from the binary image as a contour tree. The pointer firstContour is filled by the function. It is provided as a convenient way to obtain the hierarchy value as int[,].
- /// The function modifies the source image content
- /// </summary>
- /// <param name="mat">The source 8-bit single channel image. Non-zero pixels are treated as 1s, zero pixels remain 0s - that is image treated as binary. To get such a binary image from grayscale, one may use cvThreshold, cvAdaptiveThreshold or cvCanny. The function modifies the source image content</param>
- /// <param name="hierarchy">The contour hierarchy</param>
- /// <param name="mode">Retrieval mode</param>
- /// <param name="method">Approximation method (for all the modes, except CV_RETR_RUNS, which uses built-in approximation). </param>
- /// <param name="offset">Offset, by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context</param>
- /// <returns>Detected contours. Each contour is stored as a vector of points.</returns>
- public static VectorOfVectorOfPoint FindContours(this Mat mat, out int[,] hierarchy, RetrType mode, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple, Point offset = default)
+ /// <summary>
+ /// Determine the area (i.e. total number of pixels in the image),
+ /// initialize the output skeletonized image, and construct the
+ /// morphological structuring element
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="iterations">Number of iterations required to perform the skeletoize</param>
+ /// <param name="ksize"></param>
+ /// <param name="elementShape"></param>
+ public static Mat Skeletonize(this Mat src, out int iterations, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle)
+ {
+ if (ksize.IsEmpty) ksize = new Size(3, 3);
+ Point anchor = new(-1, -1);
+ var skeleton = src.NewBlank();
+ var kernel = Kernel3x3Rectangle;
+
+ var image = src;
+ using var temp = new Mat();
+ iterations = 0;
+ while (true)
{
- var contours = new VectorOfVectorOfPoint();
- using var hierarchyMat = new Mat();
- CvInvoke.FindContours(mat, contours, hierarchyMat, mode, method, offset);
- hierarchy = new int[hierarchyMat.Cols, 4];
- var gcHandle = GCHandle.Alloc(hierarchy, GCHandleType.Pinned);
- using (var mat2 = new Mat(hierarchyMat.Rows, hierarchyMat.Cols, hierarchyMat.Depth, 4, gcHandle.AddrOfPinnedObject(), hierarchyMat.Step))
- hierarchyMat.CopyTo(mat2);
- gcHandle.Free();
- return contours;
+ iterations++;
+
+ // erode and dilate the image using the structuring element
+ using var eroded = new Mat();
+ CvInvoke.Erode(image, eroded, kernel, anchor, 1, BorderType.Reflect101, default);
+ CvInvoke.Dilate(eroded, temp, kernel, anchor, 1, BorderType.Reflect101, default);
+
+ // subtract the temporary image from the original, eroded
+ // image, then take the bitwise 'or' between the skeleton
+ // and the temporary image
+ CvInvoke.Subtract(image, temp, temp);
+ CvInvoke.BitwiseOr(skeleton, temp, skeleton);
+ image = eroded.Clone();
+
+ // if there are no more 'white' pixels in the image, then
+ // break from the loop
+ if (CvInvoke.CountNonZero(image) == 0) break;
}
-
- /// <summary>
- /// Determine the area (i.e. total number of pixels in the image),
- /// initialize the output skeletonized image, and construct the
- /// morphological structuring element
- /// </summary>
- /// <param name="src"></param>
- /// <param name="iterations">Number of iterations required to perform the skeletoize</param>
- /// <param name="ksize"></param>
- /// <param name="elementShape"></param>
- public static Mat Skeletonize(this Mat src, out int iterations, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle)
- {
- if (ksize.IsEmpty) ksize = new Size(3, 3);
- Point anchor = new(-1, -1);
- var skeleton = src.NewBlank();
- var kernel = Kernel3x3Rectangle;
-
- var image = src;
- using var temp = new Mat();
- iterations = 0;
- while (true)
- {
- iterations++;
-
- // erode and dilate the image using the structuring element
- using var eroded = new Mat();
- CvInvoke.Erode(image, eroded, kernel, anchor, 1, BorderType.Reflect101, default);
- CvInvoke.Dilate(eroded, temp, kernel, anchor, 1, BorderType.Reflect101, default);
-
- // subtract the temporary image from the original, eroded
- // image, then take the bitwise 'or' between the skeleton
- // and the temporary image
- CvInvoke.Subtract(image, temp, temp);
- CvInvoke.BitwiseOr(skeleton, temp, skeleton);
- image = eroded.Clone();
-
- // if there are no more 'white' pixels in the image, then
- // break from the loop
- if (CvInvoke.CountNonZero(image) == 0) break;
- }
-
- return skeleton;
- }
+ return skeleton;
+ }
- /// <summary>
- /// Determine the area (i.e. total number of pixels in the image),
- /// initialize the output skeletonized image, and construct the
- /// morphological structuring element
- /// </summary>
- /// <param name="src"></param>
- /// <param name="ksize"></param>
- /// <param name="elementShape"></param>
- public static Mat Skeletonize(this Mat src, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle)
- => src.Skeletonize(out _, ksize, elementShape);
- #endregion
-
- #region Kernel methods
-
- /// <summary>
- /// Reduces iterations to 1 and generate a kernel to match the iterations effect
- /// </summary>
- /// <param name="iterations"></param>
- /// <param name="elementShape"></param>
- /// <returns></returns>
- public static Mat GetDynamicKernel(ref int iterations, ElementShape elementShape = ElementShape.Ellipse)
- {
- var size = Math.Max(iterations, 1) * 2 + 1;
- iterations = 1;
- return CvInvoke.GetStructuringElement(elementShape, new Size(size, size), new Point(-1, -1));
- }
- #endregion
-
- #region Disposes
- /// <summary>
- /// Dispose this <see cref="Mat"/> if it's a sub matrix / roi
- /// </summary>
- /// <param name="mat">Mat to dispose</param>
- public static void DisposeIfSubMatrix(this Mat mat)
- {
- if(mat.IsSubmatrix) mat.Dispose();
- }
- #endregion
+ /// <summary>
+ /// Determine the area (i.e. total number of pixels in the image),
+ /// initialize the output skeletonized image, and construct the
+ /// morphological structuring element
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="ksize"></param>
+ /// <param name="elementShape"></param>
+ public static Mat Skeletonize(this Mat src, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle)
+ => src.Skeletonize(out _, ksize, elementShape);
+ #endregion
+
+ #region Kernel methods
+
+ /// <summary>
+ /// Reduces iterations to 1 and generate a kernel to match the iterations effect
+ /// </summary>
+ /// <param name="iterations"></param>
+ /// <param name="elementShape"></param>
+ /// <returns></returns>
+ public static Mat GetDynamicKernel(ref int iterations, ElementShape elementShape = ElementShape.Ellipse)
+ {
+ var size = Math.Max(iterations, 1) * 2 + 1;
+ iterations = 1;
+ return CvInvoke.GetStructuringElement(elementShape, new Size(size, size), new Point(-1, -1));
+ }
+ #endregion
+
+ #region Disposes
+ /// <summary>
+ /// Dispose this <see cref="Mat"/> if it's a sub matrix / roi
+ /// </summary>
+ /// <param name="mat">Mat to dispose</param>
+ public static void DisposeIfSubMatrix(this Mat mat)
+ {
+ if(mat.IsSubmatrix) mat.Dispose();
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/EnumExtensions.cs b/UVtools.Core/Extensions/EnumExtensions.cs
index 6847164..0d81996 100644
--- a/UVtools.Core/Extensions/EnumExtensions.cs
+++ b/UVtools.Core/Extensions/EnumExtensions.cs
@@ -12,28 +12,29 @@ using System.Globalization;
using System.Linq;
using UVtools.Core.Objects;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class EnumExtensions
{
- public static class EnumExtensions
+ public static string GetDescription(this Enum value)
{
- public static string GetDescription(this Enum value)
+ var attributes = value.GetType().GetField(value.ToString())?.GetCustomAttributes(typeof(DescriptionAttribute), false);
+ if (attributes is not null && attributes.Any())
{
- var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
- if (attributes.Any())
- return (attributes.First() as DescriptionAttribute)?.Description;
-
- // If no description is found, the least we can do is replace underscores with spaces
- // You can add your own custom default formatting logic here
- var ti = CultureInfo.CurrentCulture.TextInfo;
- return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
+ if (attributes.First() is DescriptionAttribute attr) return attr.Description;
}
- public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
- {
- if (!t.IsEnum)
- throw new ArgumentException($"{nameof(t)} must be an enum type");
+ // If no description is found, the least we can do is replace underscores with spaces
+ // You can add your own custom default formatting logic here
+ var ti = CultureInfo.CurrentCulture.TextInfo;
+ return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
+ }
- return Enum.GetValues(t).Cast<Enum>().OrderBy(e => e).Select(e => new ValueDescription(e, e.GetDescription())).ToList();
- }
+ public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
+ {
+ if (!t.IsEnum)
+ throw new ArgumentException($"{nameof(t)} must be an enum type");
+
+ return Enum.GetValues(t).Cast<Enum>().OrderBy(e => e).Select(e => new ValueDescription(e, e.GetDescription())).ToList();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/FileStreamExtensions.cs b/UVtools.Core/Extensions/FileStreamExtensions.cs
index 9d2a130..12fb0d8 100644
--- a/UVtools.Core/Extensions/FileStreamExtensions.cs
+++ b/UVtools.Core/Extensions/FileStreamExtensions.cs
@@ -10,164 +10,163 @@ using System;
using System.IO;
using System.Text;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class FileStreamExtensions
{
- public static class FileStreamExtensions
- {
- public static uint ReadBytes(this FileStream fs, byte[] bytes, int offset = 0)
- {
- return (uint)fs.Read(bytes, offset, bytes.Length);
- }
-
- public static byte[] ReadBytes(this FileStream fs, int length, int offset = 0)
- {
- var buffer = new byte[length];
- fs.Read(buffer, offset, length);
- return buffer;
- }
-
- public static byte[] ReadBytes(this FileStream fs, uint length, int offset = 0)
- => fs.ReadBytes((int)length, offset);
-
- /// <summary>
- /// Read from current position to the end of the file
- /// </summary>
- /// <param name="fs"></param>
- /// <returns></returns>
- 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));
- }
-
- public static uint ReadUShortBigEndian(this FileStream fs, int offset = 0)
- {
- return BitExtensions.ToUShortBigEndian(fs.ReadBytes(2, offset));
- }
-
- public static uint ReadUIntLittleEndian(this FileStream fs, int offset = 0)
- {
- return BitExtensions.ToUIntLittleEndian(fs.ReadBytes(4, offset));
- }
-
- public static uint ReadUIntBigEndian(this FileStream fs, int offset = 0)
- {
- return BitExtensions.ToUIntBigEndian(fs.ReadBytes(4, offset));
- }
-
- public static void WriteUShortLittleEndian(this FileStream fs, ushort value, int offset = 0)
- {
- fs.WriteBytes(BitExtensions.ToBytesLittleEndian(value), offset);
- }
-
- public static void WriteUShortBigEndian(this FileStream fs, ushort value, int offset = 0)
- {
- fs.WriteBytes(BitExtensions.ToBytesBigEndian(value), offset);
- }
-
- public static void WriteUIntLittleEndian(this FileStream fs, uint value, int offset = 0)
- {
- fs.WriteBytes(BitExtensions.ToBytesLittleEndian(value), offset);
- }
-
- public static void WriteUIntBigEndian(this FileStream fs, uint value, int offset = 0)
- {
- fs.WriteBytes(BitExtensions.ToBytesBigEndian(value), offset);
- }
-
- public static void WriteFloatLittleEndian(this FileStream fs, float value, int offset = 0)
- {
- var bytes = BitConverter.GetBytes(value);
- if (!BitConverter.IsLittleEndian) Array.Reverse(bytes); //reverse it so we get little endian.
- fs.WriteBytes(BitConverter.GetBytes(value), offset);
- }
- public static void WriteFloatBigEndian(this FileStream fs, float value, int offset = 0)
- {
- var bytes = BitConverter.GetBytes(value);
- if (BitConverter.IsLittleEndian) Array.Reverse(bytes); //reverse it so we get big endian.
- fs.WriteBytes(BitConverter.GetBytes(value), offset);
- }
-
- public static uint WriteStream(this FileStream fs, MemoryStream stream, int offset = 0)
- {
- return fs.WriteBytes(stream.ToArray(), offset);
- }
-
- public static uint WriteBytes(this FileStream fs, byte[] bytes, int offset = 0)
- {
- fs.Write(bytes, offset, bytes.Length);
- return (uint)bytes.Length;
- }
-
- public static uint WriteBytes(this Stream stream, byte[] bytes, int offset = 0)
- {
- stream.Write(bytes, offset, bytes.Length);
- 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.WriteBytes(bytes, offset);
- return (uint)bytes.Length;
- }
-
- public static uint WriteLine(this FileStream fs) => fs.WriteString(Environment.NewLine);
-
- public static uint WriteLine(this FileStream fs, string text, int offset = 0)
- => fs.WriteLine(text, Encoding.UTF8, offset);
-
- public static uint WriteLine(this FileStream fs, string text, Encoding encoding, int offset = 0)
- {
- var bytes = encoding.GetBytes($"{text}{Environment.NewLine}");
- fs.WriteBytes(bytes, offset);
- return (uint)bytes.Length;
- }
-
- public static uint WriteLineLF(this FileStream fs)
- {
- fs.WriteByte(0x0A);
- return 1;
- }
-
- public static uint WriteLineLF(this FileStream fs, string text, int offset = 0)
- => fs.WriteLineLF(text, Encoding.UTF8, offset);
-
- public static uint WriteLineLF(this FileStream fs, string text, Encoding encoding, int offset = 0)
- {
- var bytes = encoding.GetBytes($"{text}\n");
- fs.WriteBytes(bytes, offset);
- return (uint)bytes.Length;
- }
-
- public static uint WriteSerialize(this FileStream fs, object data, int offset = 0)
- {
- return Helpers.SerializeWriteFileStream(fs, data, offset);
- }
-
- /// <summary>
- /// Seek to a position, do work and rewind to initial position
- /// </summary>
- /// <param name="fs"></param>
- /// <param name="action"></param>
- /// <param name="offset"></param>
- /// <param name="seekOrigin"></param>
- public static void SeekDoWorkAndRewind(this FileStream fs, long offset, Action action)
- => fs.SeekDoWorkAndRewind(offset, SeekOrigin.Begin, action);
-
- public static void SeekDoWorkAndRewind(this FileStream fs, long offset, SeekOrigin seekOrigin, Action action)
- {
- var currentPos = fs.Position;
- fs.Seek(offset, seekOrigin); // Go to
- action.Invoke(); // Do work
- fs.Seek(currentPos, SeekOrigin.Begin); // Rewind
- }
- }
-}
+ public static uint ReadBytes(this FileStream fs, byte[] bytes, int offset = 0)
+ {
+ return (uint)fs.Read(bytes, offset, bytes.Length);
+ }
+
+ public static byte[] ReadBytes(this FileStream fs, int length, int offset = 0)
+ {
+ var buffer = new byte[length];
+ fs.Read(buffer, offset, length);
+ return buffer;
+ }
+
+ public static byte[] ReadBytes(this FileStream fs, uint length, int offset = 0)
+ => fs.ReadBytes((int)length, offset);
+
+ /// <summary>
+ /// Read from current position to the end of the file
+ /// </summary>
+ /// <param name="fs"></param>
+ /// <returns></returns>
+ 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));
+ }
+
+ public static uint ReadUShortBigEndian(this FileStream fs, int offset = 0)
+ {
+ return BitExtensions.ToUShortBigEndian(fs.ReadBytes(2, offset));
+ }
+
+ public static uint ReadUIntLittleEndian(this FileStream fs, int offset = 0)
+ {
+ return BitExtensions.ToUIntLittleEndian(fs.ReadBytes(4, offset));
+ }
+
+ public static uint ReadUIntBigEndian(this FileStream fs, int offset = 0)
+ {
+ return BitExtensions.ToUIntBigEndian(fs.ReadBytes(4, offset));
+ }
+
+ public static void WriteUShortLittleEndian(this FileStream fs, ushort value, int offset = 0)
+ {
+ fs.WriteBytes(BitExtensions.ToBytesLittleEndian(value), offset);
+ }
+
+ public static void WriteUShortBigEndian(this FileStream fs, ushort value, int offset = 0)
+ {
+ fs.WriteBytes(BitExtensions.ToBytesBigEndian(value), offset);
+ }
+
+ public static void WriteUIntLittleEndian(this FileStream fs, uint value, int offset = 0)
+ {
+ fs.WriteBytes(BitExtensions.ToBytesLittleEndian(value), offset);
+ }
+
+ public static void WriteUIntBigEndian(this FileStream fs, uint value, int offset = 0)
+ {
+ fs.WriteBytes(BitExtensions.ToBytesBigEndian(value), offset);
+ }
+
+ public static void WriteFloatLittleEndian(this FileStream fs, float value, int offset = 0)
+ {
+ var bytes = BitConverter.GetBytes(value);
+ if (!BitConverter.IsLittleEndian) Array.Reverse(bytes); //reverse it so we get little endian.
+ fs.WriteBytes(BitConverter.GetBytes(value), offset);
+ }
+ public static void WriteFloatBigEndian(this FileStream fs, float value, int offset = 0)
+ {
+ var bytes = BitConverter.GetBytes(value);
+ if (BitConverter.IsLittleEndian) Array.Reverse(bytes); //reverse it so we get big endian.
+ fs.WriteBytes(BitConverter.GetBytes(value), offset);
+ }
+
+ public static uint WriteStream(this FileStream fs, MemoryStream stream, int offset = 0)
+ {
+ return fs.WriteBytes(stream.ToArray(), offset);
+ }
+
+ public static uint WriteBytes(this FileStream fs, byte[] bytes, int offset = 0)
+ {
+ fs.Write(bytes, offset, bytes.Length);
+ return (uint)bytes.Length;
+ }
+
+ public static uint WriteBytes(this Stream stream, byte[] bytes, int offset = 0)
+ {
+ stream.Write(bytes, offset, bytes.Length);
+ 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.WriteBytes(bytes, offset);
+ return (uint)bytes.Length;
+ }
+
+ public static uint WriteLine(this FileStream fs) => fs.WriteString(Environment.NewLine);
+
+ public static uint WriteLine(this FileStream fs, string text, int offset = 0)
+ => fs.WriteLine(text, Encoding.UTF8, offset);
+
+ public static uint WriteLine(this FileStream fs, string text, Encoding encoding, int offset = 0)
+ {
+ var bytes = encoding.GetBytes($"{text}{Environment.NewLine}");
+ fs.WriteBytes(bytes, offset);
+ return (uint)bytes.Length;
+ }
+
+ public static uint WriteLineLF(this FileStream fs)
+ {
+ fs.WriteByte(0x0A);
+ return 1;
+ }
+
+ public static uint WriteLineLF(this FileStream fs, string text, int offset = 0)
+ => fs.WriteLineLF(text, Encoding.UTF8, offset);
+
+ public static uint WriteLineLF(this FileStream fs, string text, Encoding encoding, int offset = 0)
+ {
+ var bytes = encoding.GetBytes($"{text}\n");
+ fs.WriteBytes(bytes, offset);
+ return (uint)bytes.Length;
+ }
+
+ public static uint WriteSerialize(this FileStream fs, object data, int offset = 0)
+ {
+ return Helpers.SerializeWriteFileStream(fs, data, offset);
+ }
+
+ /// <summary>
+ /// Seek to a position, do work and rewind to initial position
+ /// </summary>
+ /// <param name="fs"></param>
+ /// <param name="action"></param>
+ /// <param name="offset"></param>
+ /// <param name="seekOrigin"></param>
+ public static void SeekDoWorkAndRewind(this FileStream fs, long offset, Action action)
+ => fs.SeekDoWorkAndRewind(offset, SeekOrigin.Begin, action);
+
+ public static void SeekDoWorkAndRewind(this FileStream fs, long offset, SeekOrigin seekOrigin, Action action)
+ {
+ var currentPos = fs.Position;
+ fs.Seek(offset, seekOrigin); // Go to
+ action.Invoke(); // Do work
+ fs.Seek(currentPos, SeekOrigin.Begin); // Rewind
+ }
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/JsonExtensions.cs b/UVtools.Core/Extensions/JsonExtensions.cs
new file mode 100644
index 0000000..e1d44cb
--- /dev/null
+++ b/UVtools.Core/Extensions/JsonExtensions.cs
@@ -0,0 +1,18 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+using System.Text.Json;
+
+namespace UVtools.Core.Extensions;
+
+public static class JsonExtensions
+{
+ public static readonly JsonSerializerOptions SettingsIndent = new()
+ {
+ WriteIndented = true
+ };
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/MathExtensions.cs b/UVtools.Core/Extensions/MathExtensions.cs
index 5916926..23ea24a 100644
--- a/UVtools.Core/Extensions/MathExtensions.cs
+++ b/UVtools.Core/Extensions/MathExtensions.cs
@@ -9,150 +9,149 @@
using System;
using System.Globalization;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class MathExtensions
{
- public static class MathExtensions
+ public static byte Clamp(this byte value, byte min, byte max)
{
- public static byte Clamp(this byte value, byte min, byte max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static sbyte Clamp(this sbyte value, sbyte min, sbyte max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static sbyte Clamp(this sbyte value, sbyte min, sbyte max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static ushort Clamp(this ushort value, ushort min, ushort max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static ushort Clamp(this ushort value, ushort min, ushort max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static short Clamp(this short value, short min, short max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static short Clamp(this short value, short min, short max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static uint Clamp(this uint value, uint min, uint max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static uint Clamp(this uint value, uint min, uint max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static int Clamp(this int value, int min, int max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static int Clamp(this int value, int min, int max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static ulong Clamp(this ulong value, ulong min, ulong max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static ulong Clamp(this ulong value, ulong min, ulong max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static long Clamp(this long value, long min, long max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static long Clamp(this long value, long min, long max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static float Clamp(this float value, float min, float max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static float Clamp(this float value, float min, float max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static double Clamp(this double value, double min, double max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static double Clamp(this double value, double min, double max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static decimal Clamp(this decimal value, decimal min, decimal max)
- {
- return value <= min ? min : value >= max ? max : value;
- }
+ public static decimal Clamp(this decimal value, decimal min, decimal max)
+ {
+ return value <= min ? min : value >= max ? max : value;
+ }
- public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
- {
- if (value.CompareTo(min) < 0)
- return min;
- if (value.CompareTo(max) > 0)
- return max;
+ public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
+ {
+ if (value.CompareTo(min) < 0)
+ return min;
+ if (value.CompareTo(max) > 0)
+ return max;
- return value;
- }
+ return value;
+ }
- public static uint DecimalDigits(this float val)
- {
- var valStr = val.ToString(CultureInfo.InvariantCulture).TrimEnd('0');
- if (string.IsNullOrEmpty(valStr)) return 0;
+ public static uint DecimalDigits(this float val)
+ {
+ var valStr = val.ToString(CultureInfo.InvariantCulture).TrimEnd('0');
+ if (string.IsNullOrEmpty(valStr)) return 0;
- var index = valStr.IndexOf('.');
- if (index < 0) return 0;
+ var index = valStr.IndexOf('.');
+ if (index < 0) return 0;
- return (uint) (valStr[index..].Length - 1);
- }
+ return (uint) (valStr[index..].Length - 1);
+ }
- public static uint DecimalDigits(this double val)
- {
- var valStr = val.ToString(CultureInfo.InvariantCulture).TrimEnd('0');
- if (string.IsNullOrEmpty(valStr)) return 0;
+ public static uint DecimalDigits(this double val)
+ {
+ var valStr = val.ToString(CultureInfo.InvariantCulture).TrimEnd('0');
+ if (string.IsNullOrEmpty(valStr)) return 0;
- var index = valStr.IndexOf('.');
- if (index < 0) return 0;
+ var index = valStr.IndexOf('.');
+ if (index < 0) return 0;
- return (uint)(valStr[index..].Length - 1);
- }
+ return (uint)(valStr[index..].Length - 1);
+ }
- public static uint DecimalDigits(this decimal val)
- {
- var valStr = val.ToString(CultureInfo.InvariantCulture).TrimEnd('0');
- if (string.IsNullOrEmpty(valStr) || valStr[^1] == '.') return 0;
+ public static uint DecimalDigits(this decimal val)
+ {
+ var valStr = val.ToString(CultureInfo.InvariantCulture).TrimEnd('0');
+ if (string.IsNullOrEmpty(valStr) || valStr[^1] == '.') return 0;
- var index = valStr.IndexOf('.');
- if (index < 0) return 0;
+ var index = valStr.IndexOf('.');
+ if (index < 0) return 0;
- return (uint)(valStr[index..].Length - 1);
- }
+ return (uint)(valStr[index..].Length - 1);
+ }
- public static bool IsInteger(this float val, float tolerance = 0.0001f) => Math.Abs(val - Math.Floor(val)) < tolerance;
- public static bool IsInteger(this double val, double tolerance = 0.0001) => Math.Abs(val - Math.Floor(val)) < tolerance;
- public static bool IsInteger(this decimal val) => val == Math.Floor(val);
+ public static bool IsInteger(this float val, float tolerance = 0.0001f) => Math.Abs(val - Math.Floor(val)) < tolerance;
+ public static bool IsInteger(this double val, double tolerance = 0.0001) => Math.Abs(val - Math.Floor(val)) < tolerance;
+ public static bool IsInteger(this decimal val) => val == Math.Floor(val);
- public static int GCD(int a, int b)
+ public static int GCD(int a, int b)
+ {
+ while (a != 0 && b != 0)
{
- while (a != 0 && b != 0)
- {
- if (a > b)
- a %= b;
- else
- b %= a;
- }
-
- return a | b;
+ if (a > b)
+ a %= b;
+ else
+ b %= a;
}
- public static uint GCD(uint a, uint b)
+ return a | b;
+ }
+
+ public static uint GCD(uint a, uint b)
+ {
+ while (a != 0 && b != 0)
{
- while (a != 0 && b != 0)
- {
- if (a > b)
- a %= b;
- else
- b %= a;
- }
-
- return a | b;
+ if (a > b)
+ a %= b;
+ else
+ b %= a;
}
- public static ulong GCD(ulong a, ulong b)
+ return a | b;
+ }
+
+ public static ulong GCD(ulong a, ulong b)
+ {
+ while (a != 0 && b != 0)
{
- while (a != 0 && b != 0)
- {
- if (a > b)
- a %= b;
- else
- b %= a;
- }
-
- return a | b;
+ if (a > b)
+ a %= b;
+ else
+ b %= a;
}
+ return a | b;
}
-}
+
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/NetworkExtensions.cs b/UVtools.Core/Extensions/NetworkExtensions.cs
index d508705..a09067e 100644
--- a/UVtools.Core/Extensions/NetworkExtensions.cs
+++ b/UVtools.Core/Extensions/NetworkExtensions.cs
@@ -13,50 +13,49 @@ using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class NetworkExtensions
{
- public static class NetworkExtensions
+ public static readonly HttpClient HttpClient = new()
{
- public static readonly HttpClient HttpClient = new()
- {
- DefaultRequestHeaders = { UserAgent = { new ProductInfoHeaderValue(About.Software, About.VersionStr) } }
- };
+ DefaultRequestHeaders = { UserAgent = { new ProductInfoHeaderValue(About.Software, About.VersionStr) } }
+ };
- public static async Task<HttpResponseMessage> DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<(long total, long bytes)> progress = null, CancellationToken cancellationToken = default)
+ public static async Task<HttpResponseMessage> DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<(long total, long bytes)>? progress = null, CancellationToken cancellationToken = default)
+ {
+ // Get the http headers first to examine the content length
+ var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
+ var contentLength = response.Content.Headers.ContentLength;
+
+ await using var download = await response.Content.ReadAsStreamAsync(cancellationToken);
+ // Ignore progress reporting when no progress reporter was
+ // passed or when the content length is unknown
+ if (progress is null || !contentLength.HasValue)
{
- // Get the http headers first to examine the content length
- var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
- var contentLength = response.Content.Headers.ContentLength;
-
- await using var download = await response.Content.ReadAsStreamAsync(cancellationToken);
- // Ignore progress reporting when no progress reporter was
- // passed or when the content length is unknown
- if (progress is null || !contentLength.HasValue)
- {
- await download.CopyToAsync(destination, cancellationToken);
- return response;
- }
-
- // Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
- var relativeProgress = new Progress<long>(downloadedBytes => progress.Report(new (contentLength.Value, downloadedBytes)));
- // Use extension method to report progress while downloading
- await download.CopyToAsync(destination, relativeProgress, cancellationToken);
- progress.Report(new(contentLength.Value, contentLength.Value));
+ await download.CopyToAsync(destination, cancellationToken);
return response;
}
+
+ // Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
+ var relativeProgress = new Progress<long>(downloadedBytes => progress.Report(new (contentLength.Value, downloadedBytes)));
+ // Use extension method to report progress while downloading
+ await download.CopyToAsync(destination, relativeProgress, cancellationToken);
+ progress.Report(new(contentLength.Value, contentLength.Value));
+ return response;
+ }
- public static async Task<HttpResponseMessage> DownloadAsync(this HttpClient client, string requestUri, string destinationFilePath, IProgress<(long total, long bytes)> progress = null, CancellationToken cancellationToken = default)
- {
- await using var stream = File.Open(destinationFilePath, FileMode.Create, FileAccess.Write);
- return await DownloadAsync(client, requestUri, stream, progress, cancellationToken);
- }
+ public static async Task<HttpResponseMessage> DownloadAsync(this HttpClient client, string requestUri, string destinationFilePath, IProgress<(long total, long bytes)>? progress = null, CancellationToken cancellationToken = default)
+ {
+ await using var stream = File.Open(destinationFilePath, FileMode.Create, FileAccess.Write);
+ return await DownloadAsync(client, requestUri, stream, progress, cancellationToken);
+ }
- public static async Task<HttpResponseMessage> DownloadAsync(string requestUri, Stream destination, IProgress<(long total, long bytes)> progress = null, CancellationToken cancellationToken = default)
- => await HttpClient.DownloadAsync(requestUri, destination, progress, cancellationToken);
+ public static async Task<HttpResponseMessage> DownloadAsync(string requestUri, Stream destination, IProgress<(long total, long bytes)>? progress = null, CancellationToken cancellationToken = default)
+ => await HttpClient.DownloadAsync(requestUri, destination, progress, cancellationToken);
- public static async Task<HttpResponseMessage> DownloadAsync(string requestUri, string destinationFilePath, IProgress<(long total, long bytes)> progress = null, CancellationToken cancellationToken = default)
- => await HttpClient.DownloadAsync(requestUri, destinationFilePath, progress, cancellationToken);
+ public static async Task<HttpResponseMessage> DownloadAsync(string requestUri, string destinationFilePath, IProgress<(long total, long bytes)>? progress = null, CancellationToken cancellationToken = default)
+ => await HttpClient.DownloadAsync(requestUri, destinationFilePath, progress, cancellationToken);
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/ParallelExtensions.cs b/UVtools.Core/Extensions/ParallelExtensions.cs
index 2f747ad..0ab9346 100644
--- a/UVtools.Core/Extensions/ParallelExtensions.cs
+++ b/UVtools.Core/Extensions/ParallelExtensions.cs
@@ -11,39 +11,38 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class ParallelExtensions
{
- public static class ParallelExtensions
+ //public static readonly ParallelOptions ParallelSingleThread = new() { MaxDegreeOfParallelism = 1 };
+ public static void ForAllInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action)
{
- //public static readonly ParallelOptions ParallelSingleThread = new() { MaxDegreeOfParallelism = 1 };
- public static void ForAllInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action)
- {
- Partitioner.Create(source)
- .AsParallel()
- .AsOrdered()
- .ForAll(action);
- }
+ Partitioner.Create(source)
+ .AsParallel()
+ .AsOrdered()
+ .ForAll(action);
+ }
- public static void ForEachInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action)
- {
+ public static void ForEachInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action)
+ {
- source = Partitioner.Create(source)
- .AsParallel()
- .AsOrdered();
+ source = Partitioner.Create(source)
+ .AsParallel()
+ .AsOrdered();
- Parallel.ForEach(source, action);
+ Parallel.ForEach(source, action);
- }
+ }
- public static IEnumerable<T1> OrderedParallel<T, T1>(this IEnumerable<T> list, Func<T, T1> action)
+ public static IEnumerable<T1> OrderedParallel<T, T1>(this IEnumerable<T> list, Func<T, T1> action)
+ {
+ var unorderedResult = new ConcurrentBag<(long, T1)>();
+ Parallel.ForEach(list, (o, state, i) =>
{
- var unorderedResult = new ConcurrentBag<(long, T1)>();
- Parallel.ForEach(list, (o, state, i) =>
- {
- unorderedResult.Add((i, action.Invoke(o)));
- });
- var ordered = unorderedResult.OrderBy(o => o.Item1);
- return ordered.Select(o => o.Item2);
- }
+ unorderedResult.Add((i, action.Invoke(o)));
+ });
+ var ordered = unorderedResult.OrderBy(o => o.Item1);
+ return ordered.Select(o => o.Item2);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/PathExtensions.cs b/UVtools.Core/Extensions/PathExtensions.cs
index 3fff87b..3a7b970 100644
--- a/UVtools.Core/Extensions/PathExtensions.cs
+++ b/UVtools.Core/Extensions/PathExtensions.cs
@@ -9,51 +9,50 @@ using System;
using System.Collections.Generic;
using System.IO;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class PathExtensions
{
- public static class PathExtensions
+ public static string GetFileNameStripAllExtensions(string path)
{
- public static string GetFileNameStripAllExtensions(string path)
- {
- path = Path.GetFileName(path);
- if(string.IsNullOrEmpty(path)) return string.Empty;
- var splitPath = path.Split('.', 2, StringSplitOptions.TrimEntries);
- return splitPath.Length == 0 ? string.Empty : splitPath[0];
- }
+ path = Path.GetFileName(path);
+ if(string.IsNullOrEmpty(path)) return string.Empty;
+ var splitPath = path.Split('.', 2, StringSplitOptions.TrimEntries);
+ return splitPath.Length == 0 ? string.Empty : splitPath[0];
+ }
- public static string GetFileNameStripExtensions(string path, List<string> extensions, out string strippedExtension)
+ public static string GetFileNameStripExtensions(string path, List<string> extensions, out string strippedExtension)
+ {
+ strippedExtension = string.Empty;
+ path = Path.GetFileName(path);
+ if (string.IsNullOrEmpty(path)) return string.Empty;
+ foreach (var extension in extensions)
{
- strippedExtension = string.Empty;
- path = Path.GetFileName(path);
- if (string.IsNullOrEmpty(path)) return string.Empty;
- foreach (var extension in extensions)
- {
- var dotExtension = $".{extension}";
- if (!path.EndsWith(dotExtension, StringComparison.OrdinalIgnoreCase)) continue;
- strippedExtension = extension;
- return path.Remove(path.Length - dotExtension.Length);
- }
-
- return path;
+ var dotExtension = $".{extension}";
+ if (!path.EndsWith(dotExtension, StringComparison.OrdinalIgnoreCase)) continue;
+ strippedExtension = extension;
+ return path.Remove(path.Length - dotExtension.Length);
}
- public static string GetTempFilePathWithFilename(string fileName)
- {
- var path = Path.GetTempPath();
- return Path.Combine(path, fileName);
- }
+ return path;
+ }
- /// <summary>
- /// Gets a temporary file with a extension and a prepend string
- /// </summary>
- /// <param name="extension">Extension name without the dot (.)</param>
- /// <param name="prepend">Optional prepend file name</param>
- /// <returns></returns>
- public static string GetTempFilePathWithExtension(string extension, string prepend = null)
- {
- var path = Path.GetTempPath();
- var fileName = $"{prepend}{Guid.NewGuid()}.{extension}";
- return Path.Combine(path, fileName);
- }
+ public static string GetTempFilePathWithFilename(string fileName)
+ {
+ var path = Path.GetTempPath();
+ return Path.Combine(path, fileName);
+ }
+
+ /// <summary>
+ /// Gets a temporary file with a extension and a prepend string
+ /// </summary>
+ /// <param name="extension">Extension name without the dot (.)</param>
+ /// <param name="prepend">Optional prepend file name</param>
+ /// <returns></returns>
+ public static string GetTempFilePathWithExtension(string extension, string? prepend = null)
+ {
+ var path = Path.GetTempPath();
+ var fileName = $"{prepend}{Guid.NewGuid()}.{extension}";
+ return Path.Combine(path, fileName);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/PointExtensions.cs b/UVtools.Core/Extensions/PointExtensions.cs
index d95572e..01c6c54 100644
--- a/UVtools.Core/Extensions/PointExtensions.cs
+++ b/UVtools.Core/Extensions/PointExtensions.cs
@@ -8,76 +8,75 @@
using System;
using System.Drawing;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class PointExtensions
{
- public static class PointExtensions
- {
- public static double FindLength(Point start, Point end) => Math.Sqrt(Math.Pow(end.Y - start.Y, 2) + Math.Pow(end.X - start.X, 2));
+ public static double FindLength(Point start, Point end) => Math.Sqrt(Math.Pow(end.Y - start.Y, 2) + Math.Pow(end.X - start.X, 2));
- public static bool IsAnyNegative(this Point point) => point.X < 0 || point.Y < 0;
- public static bool IsBothNegative(this Point point) => point.X < 0 && point.Y < 0;
- public static bool IsBothZeroOrPositive(this Point point) => point.X >= 0 && point.Y >= 0;
+ public static bool IsAnyNegative(this Point point) => point.X < 0 || point.Y < 0;
+ public static bool IsBothNegative(this Point point) => point.X < 0 && point.Y < 0;
+ public static bool IsBothZeroOrPositive(this Point point) => point.X >= 0 && point.Y >= 0;
- public static Point Rotate(this Point point, double angleDegree, Point pivot = default)
- {
- if (angleDegree % 360 == 0) return point;
- double angle = angleDegree * Math.PI / 180;
- double cos = Math.Cos(angle);
- double sin = Math.Sin(angle);
- int dx = point.X - pivot.X;
- int dy = point.Y - pivot.Y;
- double x = cos * dx - sin * dy + pivot.X;
- double y = sin * dx + cos * dy + pivot.Y;
-
- return new((int)Math.Round(x), (int)Math.Round(y));
- }
+ public static Point Rotate(this Point point, double angleDegree, Point pivot = default)
+ {
+ if (angleDegree % 360 == 0) return point;
+ double angle = angleDegree * Math.PI / 180;
+ double cos = Math.Cos(angle);
+ double sin = Math.Sin(angle);
+ int dx = point.X - pivot.X;
+ int dy = point.Y - pivot.Y;
+ double x = cos * dx - sin * dy + pivot.X;
+ double y = sin * dx + cos * dy + pivot.Y;
+
+ return new((int)Math.Round(x), (int)Math.Round(y));
+ }
- public static PointF Rotate(this PointF point, double angleDegree, PointF pivot = default)
- {
- if (angleDegree % 360 == 0) return point;
- double angle = angleDegree * Math.PI / 180;
- double cos = Math.Cos(angle);
- double sin = Math.Sin(angle);
- double dx = point.X - pivot.X;
- double dy = point.Y - pivot.Y;
- double x = cos * dx - sin * dy + pivot.X;
- double y = sin * dx + cos * dy + pivot.Y;
-
- return new((float) x, (float) y);
- }
+ public static PointF Rotate(this PointF point, double angleDegree, PointF pivot = default)
+ {
+ if (angleDegree % 360 == 0) return point;
+ double angle = angleDegree * Math.PI / 180;
+ double cos = Math.Cos(angle);
+ double sin = Math.Sin(angle);
+ double dx = point.X - pivot.X;
+ double dy = point.Y - pivot.Y;
+ double x = cos * dx - sin * dy + pivot.X;
+ double y = sin * dx + cos * dy + pivot.Y;
+
+ return new((float) x, (float) y);
+ }
- public static void Rotate(Point[] points, double angleDegree, Point pivot = default)
+ public static void Rotate(Point[] points, double angleDegree, Point pivot = default)
+ {
+ for (int i = 0; i < points.Length; i++)
{
- for (int i = 0; i < points.Length; i++)
- {
- points[i] = points[i].Rotate(angleDegree, pivot);
- }
+ points[i] = points[i].Rotate(angleDegree, pivot);
}
+ }
- public static void Rotate(PointF[] points, double angleDegree, PointF pivot = default)
+ public static void Rotate(PointF[] points, double angleDegree, PointF pivot = default)
+ {
+ for (int i = 0; i < points.Length; i++)
{
- for (int i = 0; i < points.Length; i++)
- {
- points[i] = points[i].Rotate(angleDegree, pivot);
- }
+ points[i] = points[i].Rotate(angleDegree, pivot);
}
+ }
- public static Point OffsetBy(this Point point, int value)=> new(point.X + value, point.Y + value);
- public static Point OffsetBy(this Point point, int x, int y) => new(point.X + x, point.Y + y);
- public static Point OffsetBy(this Point point, Point other) => new(point.X + other.X, point.Y + other.Y);
+ public static Point OffsetBy(this Point point, int value)=> new(point.X + value, point.Y + value);
+ public static Point OffsetBy(this Point point, int x, int y) => new(point.X + x, point.Y + y);
+ public static Point OffsetBy(this Point point, Point other) => new(point.X + other.X, point.Y + other.Y);
- public static Point Half(this Point point) => new(point.X / 2, point.Y / 2);
- public static PointF Half(this PointF point) => new(point.X / 2, point.Y / 2);
- public static Point ToPoint(this PointF point) => new((int) Math.Round(point.X), (int) Math.Round(point.Y));
+ public static Point Half(this Point point) => new(point.X / 2, point.Y / 2);
+ public static PointF Half(this PointF point) => new(point.X / 2, point.Y / 2);
+ public static Point ToPoint(this PointF point) => new((int) Math.Round(point.X), (int) Math.Round(point.Y));
- public static Size ToSize(this Point point) => new(point.X, point.Y);
+ public static Size ToSize(this Point point) => new(point.X, point.Y);
- public static SizeF ToSize(this PointF point) => new(point.X, point.Y);
+ public static SizeF ToSize(this PointF point) => new(point.X, point.Y);
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/RectangleExtensions.cs b/UVtools.Core/Extensions/RectangleExtensions.cs
index dbdd653..7678488 100644
--- a/UVtools.Core/Extensions/RectangleExtensions.cs
+++ b/UVtools.Core/Extensions/RectangleExtensions.cs
@@ -7,14 +7,13 @@
*/
using System.Drawing;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class RectangleExtensions
{
- public static class RectangleExtensions
+ public static Point Center(this Rectangle src)
{
- public static Point Center(this Rectangle src)
- {
- return new Point(src.Left + src.Width / 2, src.Top + src.Height / 2);
- }
-
+ return new Point(src.Left + src.Width / 2, src.Top + src.Height / 2);
}
-}
+
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs
index bf600ff..2429186 100644
--- a/UVtools.Core/Extensions/SizeExtensions.cs
+++ b/UVtools.Core/Extensions/SizeExtensions.cs
@@ -8,118 +8,117 @@
using System;
using System.Drawing;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class SizeExtensions
{
- public static class SizeExtensions
+ public static readonly string[] SizeSuffixes =
+ { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
+
+ public static string SizeSuffix(long value, byte decimalPlaces = 2)
{
- public static readonly string[] SizeSuffixes =
- { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
+ //if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
+ if (value < 0) { return "-" + SizeSuffix(-value); }
+ if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
+
+ // mag is 0 for bytes, 1 for KB, 2, for MB, etc.
+ int mag = (int)Math.Log(value, 1024);
- public static string SizeSuffix(long value, byte decimalPlaces = 2)
+ // 1L << (mag * 10) == 2 ^ (10 * mag)
+ // [i.e. the number of bytes in the unit corresponding to mag]
+ decimal adjustedSize = (decimal)value / (1L << (mag * 10));
+
+ // make adjustment when the value is large enough that
+ // it would round up to 1000 or more
+ if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
{
- //if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
- if (value < 0) { return "-" + SizeSuffix(-value); }
- if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
-
- // mag is 0 for bytes, 1 for KB, 2, for MB, etc.
- int mag = (int)Math.Log(value, 1024);
-
- // 1L << (mag * 10) == 2 ^ (10 * mag)
- // [i.e. the number of bytes in the unit corresponding to mag]
- decimal adjustedSize = (decimal)value / (1L << (mag * 10));
-
- // make adjustment when the value is large enough that
- // it would round up to 1000 or more
- if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
- {
- mag += 1;
- adjustedSize /= 1024;
- }
-
- return string.Format("{0:n" + decimalPlaces + "} {1}",
- adjustedSize,
- SizeSuffixes[mag]);
+ mag += 1;
+ adjustedSize /= 1024;
}
- public static Size Add(this Size size, Size otherSize) => new (size.Width + otherSize.Width, size.Height + otherSize.Height);
- public static Size Add(this Size size) => size.Add(size);
- public static Size Add(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels);
- public static Size Add(this Size size, int width, int height) => new (size.Width + width, size.Height + height);
+ return string.Format("{0:n" + decimalPlaces + "} {1}",
+ adjustedSize,
+ SizeSuffixes[mag]);
+ }
- public static Size Subtract(this Size size, Size otherSize) => new(size.Width - otherSize.Width, size.Height - otherSize.Height);
- public static Size Subtract(this Size size) => size.Subtract(size);
- public static Size Subtract(this Size size, int pixels) => new(size.Width - pixels, size.Height - pixels);
- public static Size Subtract(this Size size, int width, int height) => new(size.Width - width, size.Height - height);
+ public static Size Add(this Size size, Size otherSize) => new (size.Width + otherSize.Width, size.Height + otherSize.Height);
+ public static Size Add(this Size size) => size.Add(size);
+ public static Size Add(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels);
+ public static Size Add(this Size size, int width, int height) => new (size.Width + width, size.Height + height);
- public static Size Multiply(this Size size, SizeF otherSize) => new((int)(size.Width * otherSize.Width), (int)(size.Height * otherSize.Height));
- public static Size Multiply(this Size size) => size.Multiply(size);
- public static Size Multiply(this Size size, double dxy) => new((int)(size.Width * dxy), (int)(size.Height * dxy));
- public static Size Multiply(this Size size, double dx, double dy) => new((int)(size.Width * dx), (int)(size.Height * dy));
+ public static Size Subtract(this Size size, Size otherSize) => new(size.Width - otherSize.Width, size.Height - otherSize.Height);
+ public static Size Subtract(this Size size) => size.Subtract(size);
+ public static Size Subtract(this Size size, int pixels) => new(size.Width - pixels, size.Height - pixels);
+ public static Size Subtract(this Size size, int width, int height) => new(size.Width - width, size.Height - height);
- public static Size Divide(this Size size, SizeF otherSize) => new((int)(size.Width / otherSize.Width), (int)(size.Height / otherSize.Height));
- public static Size Divide(this Size size) => size.Divide(size);
- public static Size Divide(this Size size, double dxy) => new((int)(size.Width / dxy), (int)(size.Height / dxy));
- public static Size Divide(this Size size, double dx, double dy) => new((int)(size.Width / dx), (int)(size.Height / dy));
+ public static Size Multiply(this Size size, SizeF otherSize) => new((int)(size.Width * otherSize.Width), (int)(size.Height * otherSize.Height));
+ public static Size Multiply(this Size size) => size.Multiply(size);
+ public static Size Multiply(this Size size, double dxy) => new((int)(size.Width * dxy), (int)(size.Height * dxy));
+ public static Size Multiply(this Size size, double dx, double dy) => new((int)(size.Width * dx), (int)(size.Height * dy));
- /// <summary>
- /// Gets if this size have a zero value on width or height
- /// </summary>
- /// <param name="size"></param>
- /// <returns></returns>
- public static bool HaveZero(this Size size) => size.Width <= 0 && size.Height <= 0;
+ public static Size Divide(this Size size, SizeF otherSize) => new((int)(size.Width / otherSize.Width), (int)(size.Height / otherSize.Height));
+ public static Size Divide(this Size size) => size.Divide(size);
+ public static Size Divide(this Size size, double dxy) => new((int)(size.Width / dxy), (int)(size.Height / dxy));
+ public static Size Divide(this Size size, double dx, double dy) => new((int)(size.Width / dx), (int)(size.Height / dy));
- /// <summary>
- /// Exchange width with height
- /// </summary>
- /// <param name="size"></param>
- /// <returns></returns>
- public static Size Exchange(this Size size) => new(size.Height, size.Width);
+ /// <summary>
+ /// Gets if this size have a zero value on width or height
+ /// </summary>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ public static bool HaveZero(this Size size) => size.Width <= 0 && size.Height <= 0;
- public static int Area(this Rectangle rect) => rect.Width * rect.Height;
+ /// <summary>
+ /// Exchange width with height
+ /// </summary>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ public static Size Exchange(this Size size) => new(size.Height, size.Width);
- public static int Area(this Size size) => size.Width * size.Height;
+ public static int Area(this Rectangle rect) => rect.Width * rect.Height;
- public static int Max(this Size size) => Math.Max(size.Width, size.Height);
+ public static int Area(this Size size) => size.Width * size.Height;
+ public static int Max(this Size size) => Math.Max(size.Width, size.Height);
- public static float Area(this RectangleF rect, int round = -1) => round >= 0 ? (float) Math.Round(rect.Width * rect.Height, round) : rect.Width * rect.Height;
- /// <summary>
- /// Gets if this size have a zero value on width or height
- /// </summary>
- /// <param name="size"></param>
- /// <returns></returns>
- public static bool HaveZero(this SizeF size) => size.Width <= 0 && size.Height <= 0;
+ public static float Area(this RectangleF rect, int round = -1) => round >= 0 ? (float) Math.Round(rect.Width * rect.Height, round) : rect.Width * rect.Height;
- public static float Area(this SizeF size, int round = -1) => round >= 0 ? (float)Math.Round(size.Width * size.Height, round) : size.Width * size.Height;
+ /// <summary>
+ /// Gets if this size have a zero value on width or height
+ /// </summary>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ public static bool HaveZero(this SizeF size) => size.Width <= 0 && size.Height <= 0;
- public static float Max(this SizeF size) => Math.Max(size.Width, size.Height);
+ public static float Area(this SizeF size, int round = -1) => round >= 0 ? (float)Math.Round(size.Width * size.Height, round) : size.Width * size.Height;
- public static Size Half(this Size size) => new(size.Width / 2, size.Height / 2);
+ public static float Max(this SizeF size) => Math.Max(size.Width, size.Height);
- public static SizeF Half(this SizeF size) => new(size.Width / 2f, size.Height / 2f);
+ public static Size Half(this Size size) => new(size.Width / 2, size.Height / 2);
- public static Point ToPoint(this Size size) => new(size.Width, size.Height);
- public static PointF ToPoint(this SizeF size) => new(size.Width, size.Height);
+ public static SizeF Half(this SizeF size) => new(size.Width / 2f, size.Height / 2f);
- public static Size Rotate(this Size size, double angleDegree)
- {
- if (angleDegree % 360 == 0) return size;
- var sizeHalf = size.Half();
- double angle = angleDegree * Math.PI / 180;
- double cos = Math.Cos(angle);
- double sin = Math.Sin(angle);
- // var newImgWidth = + (float)(x0 + Math.Abs((x - x0) * Math.Cos(rad)) + Math.Abs((y - y0) * Math.Sin(rad)));
- //var newImgHeight = -(float)(y0 + Math.Abs((x - x0) * Math.Sin(rad)) + Math.Abs((y - y0) * Math.Cos(rad)));
- int dx = size.Width - sizeHalf.Width;
- int dy = size.Height - sizeHalf.Height;
- //double width = sizeHalf.Width + Math.Abs(dx * cos) + Math.Abs(dy * sin);
- //double height = sizeHalf.Height + Math.Abs(dx * sin) + Math.Abs(dy * cos);
- double width = Math.Abs(cos * dx) - Math.Abs(sin * dy) + sizeHalf.Width;
- double height = Math.Abs(sin * dx) + Math.Abs(cos * dy) + sizeHalf.Height;
-
- return new((int)Math.Round(width), (int)Math.Round(height));
- }
+ public static Point ToPoint(this Size size) => new(size.Width, size.Height);
+ public static PointF ToPoint(this SizeF size) => new(size.Width, size.Height);
+ public static Size Rotate(this Size size, double angleDegree)
+ {
+ if (angleDegree % 360 == 0) return size;
+ var sizeHalf = size.Half();
+ double angle = angleDegree * Math.PI / 180;
+ double cos = Math.Cos(angle);
+ double sin = Math.Sin(angle);
+ // var newImgWidth = + (float)(x0 + Math.Abs((x - x0) * Math.Cos(rad)) + Math.Abs((y - y0) * Math.Sin(rad)));
+ //var newImgHeight = -(float)(y0 + Math.Abs((x - x0) * Math.Sin(rad)) + Math.Abs((y - y0) * Math.Cos(rad)));
+ int dx = size.Width - sizeHalf.Width;
+ int dy = size.Height - sizeHalf.Height;
+ //double width = sizeHalf.Width + Math.Abs(dx * cos) + Math.Abs(dy * sin);
+ //double height = sizeHalf.Height + Math.Abs(dx * sin) + Math.Abs(dy * cos);
+ double width = Math.Abs(cos * dx) - Math.Abs(sin * dy) + sizeHalf.Width;
+ double height = Math.Abs(sin * dx) + Math.Abs(cos * dy) + sizeHalf.Height;
+
+ return new((int)Math.Round(width), (int)Math.Round(height));
}
-}
+
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/SpanExtensions.cs b/UVtools.Core/Extensions/SpanExtensions.cs
index 204fde1..bf4f3a1 100644
--- a/UVtools.Core/Extensions/SpanExtensions.cs
+++ b/UVtools.Core/Extensions/SpanExtensions.cs
@@ -9,33 +9,32 @@ using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class SpanExtensions
{
- public static class SpanExtensions
+ public static unsafe void Fill<T>(this Span<T> span, Func<T> provider) where T : struct
{
- public static unsafe void Fill<T>(this Span<T> span, Func<T> provider) where T : struct
+ int
+ cores = Environment.ProcessorCount,
+ batch = span.Length / cores,
+ mod = span.Length % cores,
+ size = Unsafe.SizeOf<T>();
+ ref T r0 = ref span.GetPinnableReference();
+ fixed (byte* p0 = &Unsafe.As<T, byte>(ref r0))
{
- int
- cores = Environment.ProcessorCount,
- batch = span.Length / cores,
- mod = span.Length % cores,
- size = Unsafe.SizeOf<T>();
- ref T r0 = ref span.GetPinnableReference();
- fixed (byte* p0 = &Unsafe.As<T, byte>(ref r0))
+ byte* p = p0;
+ Parallel.For(0, cores, i =>
{
- byte* p = p0;
- Parallel.For(0, cores, i =>
- {
- byte* pi = p + i * batch * size;
- for (int j = 0; j < batch; j++, pi += size)
- Unsafe.Write(pi, provider());
- });
+ byte* pi = p + i * batch * size;
+ for (int j = 0; j < batch; j++, pi += size)
+ Unsafe.Write(pi, provider());
+ });
- // Remaining values
- if (mod < 1) return;
- for (int i = span.Length - mod; i < span.Length; i++)
- Unsafe.Write(p + i * size, provider());
- }
+ // Remaining values
+ if (mod < 1) return;
+ for (int i = span.Length - mod; i < span.Length; i++)
+ Unsafe.Write(p + i * size, provider());
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/StreamExtensions.cs b/UVtools.Core/Extensions/StreamExtensions.cs
index 985c8ae..d55a6a3 100644
--- a/UVtools.Core/Extensions/StreamExtensions.cs
+++ b/UVtools.Core/Extensions/StreamExtensions.cs
@@ -11,54 +11,53 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class StreamExtensions
{
- public static class StreamExtensions
+ //public const int DefaultCopyBufferSize = 81920; // 81.92 kilobytes, .NET default
+ //public const int DefaultCopyBufferSize = 512000; // 512 kilobytes
+ public const int DefaultCopyBufferSize = 1048576; // 1 MB
+
+ /// <summary>
+ /// Converts stream into byte array
+ /// </summary>
+ /// <param name="stream">Input</param>
+ /// <returns>Byte array data</returns>
+ public static byte[] ToArray(this Stream stream)
{
- //public const int DefaultCopyBufferSize = 81920; // 81.92 kilobytes, .NET default
- //public const int DefaultCopyBufferSize = 512000; // 512 kilobytes
- public const int DefaultCopyBufferSize = 1048576; // 1 MB
+ using var memoryStream = new MemoryStream();
+ stream.CopyTo(memoryStream);
+ return memoryStream.ToArray();
+ }
- /// <summary>
- /// Converts stream into byte array
- /// </summary>
- /// <param name="stream">Input</param>
- /// <returns>Byte array data</returns>
- public static byte[] ToArray(this Stream stream)
- {
- using var memoryStream = new MemoryStream();
- stream.CopyTo(memoryStream);
- return memoryStream.ToArray();
- }
+ public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default)
+ {
+ if (source == null)
+ throw new ArgumentNullException(nameof(source));
+ if (!source.CanRead)
+ throw new ArgumentException("Has to be readable", nameof(source));
+ if (destination == null)
+ throw new ArgumentNullException(nameof(destination));
+ if (!destination.CanWrite)
+ throw new ArgumentException("Has to be writable", nameof(destination));
+ if (bufferSize < 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize));
+ if (bufferSize == 0) bufferSize = DefaultCopyBufferSize;
- public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long> progress = null, CancellationToken cancellationToken = default)
+ var buffer = new byte[bufferSize];
+ long totalBytesRead = 0;
+ int bytesRead;
+ while ((bytesRead = await source.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(false)) != 0)
{
- if (source == null)
- throw new ArgumentNullException(nameof(source));
- if (!source.CanRead)
- throw new ArgumentException("Has to be readable", nameof(source));
- if (destination == null)
- throw new ArgumentNullException(nameof(destination));
- if (!destination.CanWrite)
- throw new ArgumentException("Has to be writable", nameof(destination));
- if (bufferSize < 0)
- throw new ArgumentOutOfRangeException(nameof(bufferSize));
- if (bufferSize == 0) bufferSize = DefaultCopyBufferSize;
-
- var buffer = new byte[bufferSize];
- long totalBytesRead = 0;
- int bytesRead;
- while ((bytesRead = await source.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(false)) != 0)
- {
- await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
- totalBytesRead += bytesRead;
- progress?.Report(totalBytesRead);
- }
+ await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
+ totalBytesRead += bytesRead;
+ progress?.Report(totalBytesRead);
}
+ }
- public static async Task CopyToAsync(this Stream source, Stream destination, IProgress<long> progress = null, CancellationToken cancellationToken = default)
- {
- await CopyToAsync(source, destination, DefaultCopyBufferSize, progress, cancellationToken);
- }
+ public static async Task CopyToAsync(this Stream source, Stream destination, IProgress<long>? progress = null, CancellationToken cancellationToken = default)
+ {
+ await CopyToAsync(source, destination, DefaultCopyBufferSize, progress, cancellationToken);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/StringExtensions.cs b/UVtools.Core/Extensions/StringExtensions.cs
index 825f7aa..23943d4 100644
--- a/UVtools.Core/Extensions/StringExtensions.cs
+++ b/UVtools.Core/Extensions/StringExtensions.cs
@@ -8,51 +8,47 @@
using System;
using System.ComponentModel;
-using System.Linq;
using System.Text;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class StringExtensions
{
- public static class StringExtensions
+ public static string RemoveFromEnd(this string input, int count)
{
- public static string RemoveFromEnd(this string input, int count)
- {
- return input.Remove(input.Length - count);
- }
+ return input.Remove(input.Length - count);
+ }
- /// <summary>
- /// Upper the first character in a string
- /// </summary>
- /// <param name="input">Input string</param>
- /// <returns>Modified string with fist character upper</returns>
- public static string FirstCharToUpper(this string input)
+ /// <summary>
+ /// Upper the first character in a string
+ /// </summary>
+ /// <param name="input">Input string</param>
+ /// <returns>Modified string with fist character upper</returns>
+ public static string FirstCharToUpper(this string input)
+ {
+ return input switch
{
- switch (input)
- {
- case null: throw new ArgumentNullException(nameof(input));
- case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
- default: return $"{char.ToUpper(input[0])}{input[1..]}";
- }
- }
+ null => throw new ArgumentNullException(nameof(input)),
+ "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
+ _ => $"{char.ToUpper(input[0])}{input[1..]}"
+ };
+ }
- public static string Repeat(this string s, int n)
- => n <= 0 ? string.Empty : new StringBuilder(s.Length * n).Insert(0, s, n).ToString();
+ public static string Repeat(this string s, int n)
+ => n <= 0 ? string.Empty : new StringBuilder(s.Length * n).Insert(0, s, n).ToString();
- /// <summary>
- /// Converts a string into a target type
- /// </summary>
- /// <typeparam name="T">Target type to convert into</typeparam>
- /// <param name="input">Value</param>
- /// <returns>Converted value into target type</returns>
- public static T Convert<T>(this string input)
- {
- var converter = TypeDescriptor.GetConverter(typeof(T));
- if (converter is not null)
- {
- //Cast ConvertFromString(string text) : object to (T)
- return (T)converter.ConvertFromString(input);
- }
- return default;
- }
+ /// <summary>
+ /// Converts a string into a target type
+ /// </summary>
+ /// <typeparam name="T">Target type to convert into</typeparam>
+ /// <param name="input">Value</param>
+ /// <returns>Converted value into target type</returns>
+ public static T? Convert<T>(this string input)
+ {
+ var converter = TypeDescriptor.GetConverter(typeof(T));
+ //Cast ConvertFromString(string text) : object to (T)
+ var result = converter.ConvertFromString(input);
+ if (result is null) return default;
+ return (T) result;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/TimeExtensions.cs b/UVtools.Core/Extensions/TimeExtensions.cs
index cc90453..2988613 100644
--- a/UVtools.Core/Extensions/TimeExtensions.cs
+++ b/UVtools.Core/Extensions/TimeExtensions.cs
@@ -9,31 +9,30 @@
using System;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class TimeExtensions
{
- public static class TimeExtensions
- {
- /// <summary>
- /// Converts seconds to milliseconds
- /// </summary>
- /// <param name="value"></param>
- /// <param name="rounding"></param>
- /// <returns></returns>
- public static float SecondsToMilliseconds(float value, byte rounding = 2) => (float)Math.Round(value * 1000f, rounding);
+ /// <summary>
+ /// Converts seconds to milliseconds
+ /// </summary>
+ /// <param name="value"></param>
+ /// <param name="rounding"></param>
+ /// <returns></returns>
+ public static float SecondsToMilliseconds(float value, byte rounding = 2) => (float)Math.Round(value * 1000f, rounding);
- /// <summary>
- /// Converts seconds to milliseconds
- /// </summary>
- /// <param name="value"></param>
- /// <returns></returns>
- public static uint SecondsToMillisecondsUint(float value) => (uint)(value * 1000f);
+ /// <summary>
+ /// Converts seconds to milliseconds
+ /// </summary>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ public static uint SecondsToMillisecondsUint(float value) => (uint)(value * 1000f);
- /// <summary>
- /// Converts milliseconds to seconds
- /// </summary>
- /// <param name="value"></param>
- /// <param name="rounding"></param>
- /// <returns></returns>
- public static float MillisecondsToSeconds(float value, byte rounding = 2) => (float)Math.Round(value / 1000f, rounding);
- }
-}
+ /// <summary>
+ /// Converts milliseconds to seconds
+ /// </summary>
+ /// <param name="value"></param>
+ /// <param name="rounding"></param>
+ /// <returns></returns>
+ public static float MillisecondsToSeconds(float value, byte rounding = 2) => (float)Math.Round(value / 1000f, rounding);
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/TypeExtensions.cs b/UVtools.Core/Extensions/TypeExtensions.cs
index bc00201..99118f3 100644
--- a/UVtools.Core/Extensions/TypeExtensions.cs
+++ b/UVtools.Core/Extensions/TypeExtensions.cs
@@ -8,30 +8,32 @@
using System;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class TypeExtensions
{
- public static class TypeExtensions
+ /// <summary>
+ /// Creates a new instance of this type
+ /// </summary>
+ /// <param name="type"></param>
+ public static object? CreateInstance(this Type type, params object[]? paramArray)
{
- /// <summary>
- /// Creates a new instance of this type
- /// </summary>
- /// <param name="type"></param>
- public static object CreateInstance(this Type type, params object[]? paramArray)
- {
- return Activator.CreateInstance(type, paramArray);
- }
-
- /// <summary>
- /// Creates a new instance of this type
- /// </summary>
- /// <typeparam name="T">Target type</typeparam>
- /// <param name="type"></param>
- /// <returns>New instance of <see cref="T"/></returns>
- public static T CreateInstance<T>(this Type type, params object[]? paramArray)
- {
- return (T)Activator.CreateInstance(type, paramArray);
- }
+ return Activator.CreateInstance(type, paramArray);
+ }
- public static byte ToByte(this bool value) => (byte)(value ? 1 : 0);
+ /// <summary>
+ /// Creates a new instance of this type
+ /// </summary>
+ /// <typeparam name="T">Target type</typeparam>
+ /// <param name="type"></param>
+ /// <param name="paramArray"></param>
+ /// <returns>New instance of <see cref="T"/></returns>
+ public static T? CreateInstance<T>(this Type type, params object[]? paramArray)
+ {
+ var instance = Activator.CreateInstance(type, paramArray);
+ if (instance is null) return default;
+ return (T)instance;
}
-}
+
+ public static byte ToByte(this bool value) => (byte)(value ? 1 : 0);
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/UnitExtensions.cs b/UVtools.Core/Extensions/UnitExtensions.cs
index 765c198..9e4f355 100644
--- a/UVtools.Core/Extensions/UnitExtensions.cs
+++ b/UVtools.Core/Extensions/UnitExtensions.cs
@@ -6,15 +6,12 @@
* of this license document, but changing it is not allowed.
*/
-using System;
+namespace UVtools.Core.Extensions;
-namespace UVtools.Core.Extensions
+public static class UnitExtensions
{
- public static class UnitExtensions
- {
- /// <summary>
- /// 1 mm to 1 inch
- /// </summary>
- public const double MillimeterInInch = 0.0393700787;
- }
-}
+ /// <summary>
+ /// 1 mm to 1 inch
+ /// </summary>
+ public const double MillimeterInInch = 0.0393700787;
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/XmlExtensions.cs b/UVtools.Core/Extensions/XmlExtensions.cs
index ef24788..5caf55d 100644
--- a/UVtools.Core/Extensions/XmlExtensions.cs
+++ b/UVtools.Core/Extensions/XmlExtensions.cs
@@ -10,55 +10,122 @@ using System.Text;
using System.Xml;
using System.Xml.Serialization;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class XmlExtensions
{
- public static class XmlExtensions
+ public static readonly XmlWriterSettings SettingsIndent = new()
{
- public static string SerializeObject(object toSerialize)
- {
- var xmlSerializer = new XmlSerializer(toSerialize.GetType());
-
- using var textWriter = new StringWriter();
- xmlSerializer.Serialize(textWriter, toSerialize);
- return textWriter.ToString();
- }
+ CloseOutput = false,
+ Indent = true,
+ };
- public static string SerializeObject(object toSerialize, XmlWriterSettings settings, bool standalone = false)
+ public static void Serialize(object toSerialize, Stream stream, bool noNameSpace = false)
+ {
+ var xmlSerializer = new XmlSerializer(toSerialize.GetType());
+ XmlSerializerNamespaces? ns = null;
+ if (noNameSpace)
{
- settings.CloseOutput = false;
-
- using var ms = new MemoryStream();
- using (var xw = XmlWriter.Create(ms, settings))
- {
- xw.WriteStartDocument(standalone); // that bool parameter is called "standalone"
+ ns = new();
+ ns.Add("", "");
+ }
+ xmlSerializer.Serialize(stream, toSerialize, ns);
+ }
- var s = new XmlSerializer(toSerialize.GetType());
- s.Serialize(xw, toSerialize);
- }
+ public static void Serialize(object toSerialize, Stream stream, XmlWriterSettings settings, bool noNameSpace = false, bool standalone = false)
+ {
+ settings.CloseOutput = false;
- return settings.Encoding.GetString(ms.ToArray());
- }
+ using var xw = XmlWriter.Create(stream, settings);
+ xw.WriteStartDocument(standalone); // that bool parameter is called "standalone"
- public static string SerializeObject(object toSerialize, Encoding encoding, bool indent = true, bool omitXmlDeclaration = false, bool standalone = false)
+ var s = new XmlSerializer(toSerialize.GetType());
+ XmlSerializerNamespaces? ns = null;
+ if (noNameSpace)
{
- 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);
+ ns = new();
+ ns.Add("", "");
}
+ s.Serialize(xw, toSerialize, ns);
+ }
- public static T DeserializeObject<T>(string text)
+ public static void Serialize(object toSerialize, Stream stream, Encoding encoding, bool indent = true, bool omitXmlDeclaration = false, bool noNameSpace = false, bool standalone = false)
+ {
+ var settings = new XmlWriterSettings
{
- var serializer = new XmlSerializer(typeof(T));
- using TextReader reader = new StringReader(text);
- return (T)serializer.Deserialize(reader);
- }
+ // 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,
+ };
+ Serialize(toSerialize, stream, settings, noNameSpace, standalone);
+ }
+
+ public static void SerializeToFile(object toSerialize, string path, bool noNameSpace = false)
+ {
+ using var stream = new FileStream(path, FileMode.Create);
+ Serialize(toSerialize, stream, noNameSpace);
+ }
+
+ public static void SerializeToFile(object toSerialize, string path, XmlWriterSettings settings, bool noNameSpace = false, bool standalone = false)
+ {
+ using var stream = new FileStream(path, FileMode.Create);
+ Serialize(toSerialize, stream, settings, noNameSpace, standalone);
+ }
+
+ public static void SerializeToFile(object toSerialize, string path, Encoding encoding, bool indent = true, bool omitXmlDeclaration = false, bool noNameSpace = false, bool standalone = false)
+ {
+ using var stream = new FileStream(path, FileMode.Create);
+ Serialize(toSerialize, stream, encoding, indent, omitXmlDeclaration, noNameSpace, standalone);
+ }
+
+
+ public static string SerializeObject(object toSerialize, bool noNameSpace = false)
+ {
+ using var stream = new MemoryStream();
+ Serialize(toSerialize, stream, noNameSpace);
+ stream.Seek(0, SeekOrigin.Begin);
+ using var reader = new StreamReader(stream);
+ return reader.ReadToEnd();
+ }
+
+ public static string SerializeObject(object toSerialize, XmlWriterSettings settings, bool noNameSpace = false, bool standalone = false)
+ {
+ using var stream = new MemoryStream();
+ Serialize(toSerialize, stream, settings, noNameSpace, standalone);
+ stream.Seek(0, SeekOrigin.Begin);
+ using var reader = new StreamReader(stream);
+ return reader.ReadToEnd();
+ }
+
+ public static string SerializeObject(object toSerialize, Encoding encoding, bool indent = true, bool omitXmlDeclaration = false, bool noNameSpace = false, bool standalone = false)
+ {
+ using var stream = new MemoryStream();
+ Serialize(toSerialize, stream, encoding, indent, omitXmlDeclaration, noNameSpace, standalone);
+ stream.Seek(0, SeekOrigin.Begin);
+ using var reader = new StreamReader(stream);
+ return reader.ReadToEnd();
+ }
+
+ public static T DeserializeFromStream<T>(Stream stream)
+ {
+ var serializer = new XmlSerializer(typeof(T));
+ return (T)serializer.Deserialize(stream)!;
+ }
+ public static T DeserializeFromFile<T>(string filePath)
+ {
+ using var stream = File.OpenRead(filePath);
+ return DeserializeFromStream<T>(stream);
}
-}
+
+ public static T DeserializeFromText<T>(string text)
+ {
+ var serializer = new XmlSerializer(typeof(T));
+ using TextReader reader = new StringReader(text);
+ return (T)serializer.Deserialize(reader)!;
+ }
+
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/ZipArchiveExtensions.cs b/UVtools.Core/Extensions/ZipArchiveExtensions.cs
index 7550073..8446283 100644
--- a/UVtools.Core/Extensions/ZipArchiveExtensions.cs
+++ b/UVtools.Core/Extensions/ZipArchiveExtensions.cs
@@ -12,360 +12,361 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
-namespace UVtools.Core.Extensions
+namespace UVtools.Core.Extensions;
+
+public static class ZipArchiveExtensions
{
- public static class ZipArchiveExtensions
+ /// <summary>
+ /// Used to specify what our overwrite policy
+ /// is for files we are extracting.
+ /// </summary>
+ public enum Overwrite
{
- /// <summary>
- /// Used to specify what our overwrite policy
- /// is for files we are extracting.
- /// </summary>
- public enum Overwrite
- {
- Always,
- IfNewer,
- Never
- }
+ Always,
+ IfNewer,
+ Never
+ }
- /// <summary>
- /// Used to identify what we will do if we are
- /// trying to create a zip file and it already
- /// exists.
- /// </summary>
- public enum ArchiveAction
- {
- Merge,
- Replace,
- Error,
- Ignore
- }
+ /// <summary>
+ /// Used to identify what we will do if we are
+ /// trying to create a zip file and it already
+ /// exists.
+ /// </summary>
+ public enum ArchiveAction
+ {
+ Merge,
+ Replace,
+ Error,
+ Ignore
+ }
- /// <summary>
- /// Unzips the specified file to the given folder in a safe
- /// manner. This plans for missing paths and existing files
- /// and handles them gracefully.
- /// </summary>
- /// <param name="sourceArchiveFileName">
- /// The name of the zip file to be extracted
- /// </param>
- /// <param name="destinationDirectoryName">
- /// The directory to extract the zip file to
- /// </param>
- /// <param name="overwriteMethod">
- /// Specifies how we are going to handle an existing file.
- /// The default is IfNewer.
- /// </param>
- public static void ImprovedExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
- {
- //Opens the zip file up to be read
- using var archive = ZipFile.OpenRead(sourceArchiveFileName);
- archive.ImprovedExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, overwriteMethod);
- }
+ /// <summary>
+ /// Unzips the specified file to the given folder in a safe
+ /// manner. This plans for missing paths and existing files
+ /// and handles them gracefully.
+ /// </summary>
+ /// <param name="sourceArchiveFileName">
+ /// The name of the zip file to be extracted
+ /// </param>
+ /// <param name="destinationDirectoryName">
+ /// The directory to extract the zip file to
+ /// </param>
+ /// <param name="overwriteMethod">
+ /// Specifies how we are going to handle an existing file.
+ /// The default is IfNewer.
+ /// </param>
+ public static void ImprovedExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
+ {
+ //Opens the zip file up to be read
+ using var archive = ZipFile.OpenRead(sourceArchiveFileName);
+ archive.ImprovedExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, overwriteMethod);
+ }
- /// <summary>
- /// Unzips the specified file to the given folder in a safe
- /// manner. This plans for missing paths and existing files
- /// and handles them gracefully.
- /// </summary>
- /// <param name="sourceArchiveFileName">
- /// The name of the zip file to be extracted
- /// </param>
- /// <param name="destinationDirectoryName">
- /// The directory to extract the zip file to
- /// </param>
- /// <param name="overwriteMethod">
- /// Specifies how we are going to handle an existing file.
- /// The default is IfNewer.
- /// </param>
- public static void ImprovedExtractToDirectory(this ZipArchive archive, string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
+ /// <summary>
+ /// Unzips the specified file to the given folder in a safe
+ /// manner. This plans for missing paths and existing files
+ /// and handles them gracefully.
+ /// </summary>
+ /// <param name="sourceArchiveFileName">
+ /// The name of the zip file to be extracted
+ /// </param>
+ /// <param name="destinationDirectoryName">
+ /// The directory to extract the zip file to
+ /// </param>
+ /// <param name="overwriteMethod">
+ /// Specifies how we are going to handle an existing file.
+ /// The default is IfNewer.
+ /// </param>
+ public static void ImprovedExtractToDirectory(this ZipArchive archive, string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
+ {
+ //Loops through each file in the zip file
+ foreach (var file in archive.Entries)
{
- //Loops through each file in the zip file
- foreach (var file in archive.Entries)
- {
- file.ImprovedExtractToFile(destinationDirectoryName, overwriteMethod);
- }
+ file.ImprovedExtractToFile(destinationDirectoryName, overwriteMethod);
}
+ }
- /// <summary>
- /// Safely extracts a single file from a zip file
- /// </summary>
- /// <param name="file">
- /// The zip entry we are pulling the file from
- /// </param>
- /// <param name="destinationPath">
- /// The root of where the file is going
- /// </param>
- /// <param name="overwriteMethod">
- /// Specifies how we are going to handle an existing file.
- /// The default is Overwrite.IfNewer.
- /// </param>
- public static void ImprovedExtractToFile(this ZipArchiveEntry file, string destinationPath, Overwrite overwriteMethod = Overwrite.IfNewer)
- {
- //Gets the complete path for the destination file, including any
- //relative paths that were in the zip file
- string destinationFileName = Path.Combine(destinationPath, file.FullName);
+ /// <summary>
+ /// Safely extracts a single file from a zip file
+ /// </summary>
+ /// <param name="file">
+ /// The zip entry we are pulling the file from
+ /// </param>
+ /// <param name="destinationPath">
+ /// The root of where the file is going
+ /// </param>
+ /// <param name="overwriteMethod">
+ /// Specifies how we are going to handle an existing file.
+ /// The default is Overwrite.IfNewer.
+ /// </param>
+ public static void ImprovedExtractToFile(this ZipArchiveEntry file, string destinationPath, Overwrite overwriteMethod = Overwrite.IfNewer)
+ {
+ //Gets the complete path for the destination file, including any
+ //relative paths that were in the zip file
+ string destinationFileName = Path.Combine(destinationPath, file.FullName);
- //Gets just the new path, minus the file name so we can create the
- //directory if it does not exist
- string destinationFilePath = Path.GetDirectoryName(destinationFileName);
+ //Gets just the new path, minus the file name so we can create the
+ //directory if it does not exist
+ string? destinationFilePath = Path.GetDirectoryName(destinationFileName);
- //Creates the directory (if it doesn't exist) for the new path
- Directory.CreateDirectory(destinationFilePath);
+ destinationFilePath ??= string.Empty;
- //Determines what to do with the file based upon the
- //method of overwriting chosen
- switch (overwriteMethod)
- {
- case Overwrite.Always:
- //Just put the file in and overwrite anything that is found
- file.ExtractToFile(destinationFileName, true);
- break;
- case Overwrite.IfNewer:
- //Checks to see if the file exists, and if so, if it should
- //be overwritten
- if (!File.Exists(destinationFileName) || File.GetLastWriteTime(destinationFileName) < file.LastWriteTime)
- {
- //Either the file didn't exist or this file is newer, so
- //we will extract it and overwrite any existing file
- file.ExtractToFile(destinationFileName, true);
- }
- break;
- case Overwrite.Never:
- //Put the file in if it is new but ignores the
- //file if it already exists
- if (!File.Exists(destinationFileName))
- {
- file.ExtractToFile(destinationFileName);
- }
- break;
- }
- }
+ //Creates the directory (if it doesn't exist) for the new path
+ Directory.CreateDirectory(destinationFilePath);
- /// <summary>
- /// Allows you to add files to an archive, whether the archive
- /// already exists or not
- /// </summary>
- /// <param name="archiveFullName">
- /// The name of the archive to you want to add your files to
- /// </param>
- /// <param name="files">
- /// A set of file names that are to be added
- /// </param>
- /// <param name="action">
- /// Specifies how we are going to handle an existing archive
- /// </param>
- /// <param name="compression">
- /// Specifies what type of compression to use - defaults to Optimal
- /// </param>
- public static void AddToArchive(string archiveFullName,
- List<string> files,
- ArchiveAction action = ArchiveAction.Replace,
- Overwrite fileOverwrite = Overwrite.IfNewer,
- CompressionLevel compression = CompressionLevel.Optimal)
+ //Determines what to do with the file based upon the
+ //method of overwriting chosen
+ switch (overwriteMethod)
{
- //Identifies the mode we will be using - the default is Create
- ZipArchiveMode mode = ZipArchiveMode.Create;
+ case Overwrite.Always:
+ //Just put the file in and overwrite anything that is found
+ file.ExtractToFile(destinationFileName, true);
+ break;
+ case Overwrite.IfNewer:
+ //Checks to see if the file exists, and if so, if it should
+ //be overwritten
+ if (!File.Exists(destinationFileName) || File.GetLastWriteTime(destinationFileName) < file.LastWriteTime)
+ {
+ //Either the file didn't exist or this file is newer, so
+ //we will extract it and overwrite any existing file
+ file.ExtractToFile(destinationFileName, true);
+ }
+ break;
+ case Overwrite.Never:
+ //Put the file in if it is new but ignores the
+ //file if it already exists
+ if (!File.Exists(destinationFileName))
+ {
+ file.ExtractToFile(destinationFileName);
+ }
+ break;
+ }
+ }
- //Determines if the zip file even exists
- bool archiveExists = File.Exists(archiveFullName);
+ /// <summary>
+ /// Allows you to add files to an archive, whether the archive
+ /// already exists or not
+ /// </summary>
+ /// <param name="archiveFullName">
+ /// The name of the archive to you want to add your files to
+ /// </param>
+ /// <param name="files">
+ /// A set of file names that are to be added
+ /// </param>
+ /// <param name="action">
+ /// Specifies how we are going to handle an existing archive
+ /// </param>
+ /// <param name="compression">
+ /// Specifies what type of compression to use - defaults to Optimal
+ /// </param>
+ public static void AddToArchive(string archiveFullName,
+ List<string> files,
+ ArchiveAction action = ArchiveAction.Replace,
+ Overwrite fileOverwrite = Overwrite.IfNewer,
+ CompressionLevel compression = CompressionLevel.Optimal)
+ {
+ //Identifies the mode we will be using - the default is Create
+ ZipArchiveMode mode = ZipArchiveMode.Create;
- //Figures out what to do based upon our specified overwrite method
- switch (action)
- {
- case ArchiveAction.Merge:
- //Sets the mode to update if the file exists, otherwise
- //the default of Create is fine
- if (archiveExists)
- {
- mode = ZipArchiveMode.Update;
- }
- break;
- case ArchiveAction.Replace:
- //Deletes the file if it exists. Either way, the default
- //mode of Create is fine
- if (archiveExists)
- {
- File.Delete(archiveFullName);
- }
- break;
- case ArchiveAction.Error:
- //Throws an error if the file exists
- if (archiveExists)
- {
- throw new IOException(String.Format("The zip file {0} already exists.", archiveFullName));
- }
- break;
- case ArchiveAction.Ignore:
- //Closes the method silently and does nothing
- if (archiveExists)
- {
- return;
- }
- break;
- }
+ //Determines if the zip file even exists
+ bool archiveExists = File.Exists(archiveFullName);
- //Opens the zip file in the mode we specified
- using ZipArchive zipFile = ZipFile.Open(archiveFullName, mode);
- //This is a bit of a hack and should be refactored - I am
- //doing a similar foreach loop for both modes, but for Create
- //I am doing very little work while Update gets a lot of
- //code. This also does not handle any other mode (of
- //which there currently wouldn't be one since we don't
- //use Read here).
- if (mode == ZipArchiveMode.Create)
- {
- foreach (string file in files)
+ //Figures out what to do based upon our specified overwrite method
+ switch (action)
+ {
+ case ArchiveAction.Merge:
+ //Sets the mode to update if the file exists, otherwise
+ //the default of Create is fine
+ if (archiveExists)
+ {
+ mode = ZipArchiveMode.Update;
+ }
+ break;
+ case ArchiveAction.Replace:
+ //Deletes the file if it exists. Either way, the default
+ //mode of Create is fine
+ if (archiveExists)
{
- //Adds the file to the archive
- zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
+ File.Delete(archiveFullName);
}
+ break;
+ case ArchiveAction.Error:
+ //Throws an error if the file exists
+ if (archiveExists)
+ {
+ throw new IOException(String.Format("The zip file {0} already exists.", archiveFullName));
+ }
+ break;
+ case ArchiveAction.Ignore:
+ //Closes the method silently and does nothing
+ if (archiveExists)
+ {
+ return;
+ }
+ break;
+ }
+
+ //Opens the zip file in the mode we specified
+ using ZipArchive zipFile = ZipFile.Open(archiveFullName, mode);
+ //This is a bit of a hack and should be refactored - I am
+ //doing a similar foreach loop for both modes, but for Create
+ //I am doing very little work while Update gets a lot of
+ //code. This also does not handle any other mode (of
+ //which there currently wouldn't be one since we don't
+ //use Read here).
+ if (mode == ZipArchiveMode.Create)
+ {
+ foreach (string file in files)
+ {
+ //Adds the file to the archive
+ zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
}
- else
+ }
+ else
+ {
+ foreach (string file in files)
{
- foreach (string file in files)
- {
- var fileInZip = (from f in zipFile.Entries
- where f.Name == Path.GetFileName(file)
- select f).FirstOrDefault();
+ var fileInZip = (from f in zipFile.Entries
+ where f.Name == Path.GetFileName(file)
+ select f).FirstOrDefault();
- switch (fileOverwrite)
- {
- case Overwrite.Always:
- //Deletes the file if it is found
- if (fileInZip != null)
- {
- fileInZip.Delete();
- }
+ switch (fileOverwrite)
+ {
+ case Overwrite.Always:
+ //Deletes the file if it is found
+ if (fileInZip != null)
+ {
+ fileInZip.Delete();
+ }
- //Adds the file to the archive
- zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
+ //Adds the file to the archive
+ zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
- break;
- case Overwrite.IfNewer:
- //This is a bit trickier - we only delete the file if it is
- //newer, but if it is newer or if the file isn't already in
- //the zip file, we will write it to the zip file
- if (fileInZip != null)
+ break;
+ case Overwrite.IfNewer:
+ //This is a bit trickier - we only delete the file if it is
+ //newer, but if it is newer or if the file isn't already in
+ //the zip file, we will write it to the zip file
+ if (fileInZip != null)
+ {
+ //Deletes the file only if it is older than our file.
+ //Note that the file will be ignored if the existing file
+ //in the archive is newer.
+ if (fileInZip.LastWriteTime < File.GetLastWriteTime(file))
{
- //Deletes the file only if it is older than our file.
- //Note that the file will be ignored if the existing file
- //in the archive is newer.
- if (fileInZip.LastWriteTime < File.GetLastWriteTime(file))
- {
- fileInZip.Delete();
+ fileInZip.Delete();
- //Adds the file to the archive
- zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
- }
- }
- else
- {
- //The file wasn't already in the zip file so add it to the archive
+ //Adds the file to the archive
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
}
- break;
- case Overwrite.Never:
- //Don't do anything - this is a decision that you need to
- //consider, however, since this will mean that no file will
- //be written. You could write a second copy to the zip with
- //the same name (not sure that is wise, however).
- break;
- }
+ }
+ else
+ {
+ //The file wasn't already in the zip file so add it to the archive
+ zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
+ }
+ break;
+ case Overwrite.Never:
+ //Don't do anything - this is a decision that you need to
+ //consider, however, since this will mean that no file will
+ //be written. You could write a second copy to the zip with
+ //the same name (not sure that is wise, however).
+ break;
}
}
}
+ }
+
+ /// <summary>
+ /// Get or put a file into archive
+ /// </summary>
+ /// <param name="input"><see cref="ZipArchive"/></param>
+ /// <param name="filename">Filename to create</param>
+ /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
+ public static ZipArchiveEntry GetPutFile(this ZipArchive input, string filename)
+ {
+ return input.GetEntry(filename) ?? input.CreateEntry(filename);
+ }
- /// <summary>
- /// Get or put a file into archive
- /// </summary>
- /// <param name="input"><see cref="ZipArchive"/></param>
- /// <param name="filename">Filename to create</param>
- /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
- public static ZipArchiveEntry GetPutFile(this ZipArchive input, string filename)
+ /// <summary>
+ /// Create or update a file into archive and write content to it
+ /// </summary>
+ /// <param name="input"><see cref="ZipArchive"/></param>
+ /// <param name="filename">Filename to create</param>
+ /// <param name="content">Content to write</param>
+ /// <param name="mode"></param>
+ /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
+ public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, string? content, ZipArchiveMode mode)
+ {
+ ZipArchiveEntry entry;
+ if (mode == ZipArchiveMode.Update)
{
- return input.GetEntry(filename) ?? input.CreateEntry(filename);
+ entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
}
-
- /// <summary>
- /// Create or update a file into archive and write content to it
- /// </summary>
- /// <param name="input"><see cref="ZipArchive"/></param>
- /// <param name="filename">Filename to create</param>
- /// <param name="content">Content to write</param>
- /// <param name="checkExists"></param>
- /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
- public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, string content, ZipArchiveMode mode)
+ else
{
- ZipArchiveEntry entry;
- if (mode == ZipArchiveMode.Update)
- {
- entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
- }
- else
- {
- entry = input.CreateEntry(filename);
- }
-
- if (string.IsNullOrEmpty(content)) return entry;
- using var stream = entry.Open();
- if (mode == ZipArchiveMode.Update) stream.SetLength(0);
- using TextWriter tw = new StreamWriter(stream);
- tw.Write(content);
- return entry;
+ entry = input.CreateEntry(filename);
}
- /// <summary>
- /// Create or update a file into archive and write content to it
- /// </summary>
- /// <param name="input"><see cref="ZipArchive"/></param>
- /// <param name="filename">Filename to create</param>
- /// <param name="content">Content to write</param>
- /// <param name="mode"></param>
- /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
- public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, byte[] content, ZipArchiveMode mode)
- {
- ZipArchiveEntry entry;
- if (mode == ZipArchiveMode.Update)
- {
- entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
- }
- else
- {
- entry = input.CreateEntry(filename);
- }
+ if (string.IsNullOrEmpty(content)) return entry;
+ using var stream = entry.Open();
+ if (mode == ZipArchiveMode.Update) stream.SetLength(0);
+ using TextWriter tw = new StreamWriter(stream);
+ tw.Write(content);
+ return entry;
+ }
- if (content is null) return entry;
- using var stream = entry.Open();
- if (mode == ZipArchiveMode.Update) stream.SetLength(0);
- stream.Write(content, 0, content.Length);
- return entry;
+ /// <summary>
+ /// Create or update a file into archive and write content to it
+ /// </summary>
+ /// <param name="input"><see cref="ZipArchive"/></param>
+ /// <param name="filename">Filename to create</param>
+ /// <param name="content">Content to write</param>
+ /// <param name="mode"></param>
+ /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
+ public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, byte[]? content, ZipArchiveMode mode)
+ {
+ ZipArchiveEntry entry;
+ if (mode == ZipArchiveMode.Update)
+ {
+ entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
}
-
- /// <summary>
- /// Create or update a file into archive and write content to it
- /// </summary>
- /// <param name="input"><see cref="ZipArchive"/></param>
- /// <param name="filename">Filename to create</param>
- /// <param name="content">Content to write</param>
- /// <param name="mode"></param>
- /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
- public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, Stream content, ZipArchiveMode mode)
+ else
{
- ZipArchiveEntry entry;
- if (mode == ZipArchiveMode.Update)
- {
- entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
- }
- else
- {
- entry = input.CreateEntry(filename);
- }
+ entry = input.CreateEntry(filename);
+ }
+
+ if (content is null) return entry;
+ using var stream = entry.Open();
+ if (mode == ZipArchiveMode.Update) stream.SetLength(0);
+ stream.Write(content, 0, content.Length);
+ return entry;
+ }
- if (content is null) return entry;
- using var stream = entry.Open();
- if (mode == ZipArchiveMode.Update) stream.SetLength(0);
- content.CopyTo(stream);
- return entry;
+ /// <summary>
+ /// Create or update a file into archive and write content to it
+ /// </summary>
+ /// <param name="input"><see cref="ZipArchive"/></param>
+ /// <param name="filename">Filename to create</param>
+ /// <param name="content">Content to write</param>
+ /// <param name="mode"></param>
+ /// <returns>Created <see cref="ZipArchiveEntry"/></returns>
+ public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, Stream? content, ZipArchiveMode mode)
+ {
+ ZipArchiveEntry entry;
+ if (mode == ZipArchiveMode.Update)
+ {
+ entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
}
+ else
+ {
+ entry = input.CreateEntry(filename);
+ }
+
+ if (content is null) return entry;
+ using var stream = entry.Open();
+ if (mode == ZipArchiveMode.Update) stream.SetLength(0);
+ content.CopyTo(stream);
+ return entry;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs
index 4142cab..c1b9b2a 100644
--- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs
+++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs
@@ -15,1122 +15,1124 @@ using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class CTBEncryptedFile : FileFormat
{
- public class CTBEncryptedFile : FileFormat
- {
- #region Constants
- public const uint MAGIC_CBT_ENCRYPTED = 0x12FD0107;
- public const ushort REPEATRGB15MASK = 0x20;
- public const ushort RLE16EncodingLimit = 0xFFF;
- public const ushort RLEEncryptedMinimumLength = 512;
+ #region Constants
+ public const uint MAGIC_CBT_ENCRYPTED = 0x12FD0107;
+ public const ushort REPEATRGB15MASK = 0x20;
+ public const ushort RLE16EncodingLimit = 0xFFF;
+ public const ushort RLEEncryptedMinimumLength = 512;
- public const uint PERLAYER_SETTINGS_DISALLOW_NO_AA = 7; // 7 (This disallow per layer settings and follow global table only) No AA
- public const uint PERLAYER_SETTINGS_DISALLOW = 15; // 15 (This disallow per layer settings and follow global table only) with AA
- public const uint PERLAYER_SETTINGS_ALLOW = 0x5000000F; // 1342177295 (This allow per layer settings)
+ public const uint PERLAYER_SETTINGS_DISALLOW_NO_AA = 7; // 7 (This disallow per layer settings and follow global table only) No AA
+ public const uint PERLAYER_SETTINGS_DISALLOW = 15; // 15 (This disallow per layer settings and follow global table only) with AA
+ public const uint PERLAYER_SETTINGS_ALLOW = 0x5000000F; // 1342177295 (This allow per layer settings)
- private const string CTB_DISCLAIMER = "Layout and record format for the ctb and cbddlp file types are the copyrighted programs or codes of CBD Technology (China) Inc..The Customer or User shall not in any manner reproduce, distribute, modify, decompile, disassemble, decrypt, extract, reverse engineer, lease, assign, or sublicense the said programs or codes.";
- private const ushort CTB_DISCLAIMER_SIZE = 320;
+ private const string CTB_DISCLAIMER = "Layout and record format for the ctb and cbddlp file types are the copyrighted programs or codes of CBD Technology (China) Inc..The Customer or User shall not in any manner reproduce, distribute, modify, decompile, disassemble, decrypt, extract, reverse engineer, lease, assign, or sublicense the said programs or codes.";
+ private const ushort CTB_DISCLAIMER_SIZE = 320;
- public const byte HASH_LENGTH = 32;
- public const uint LAYER_XOR_KEY = 0xEFBEADDE;
+ public const byte HASH_LENGTH = 32;
+ public const uint LAYER_XOR_KEY = 0xEFBEADDE;
- public const string Secret0 = "XxUBHR0JHSE6DU8YCVMxORpIG0wSOTobGE8KGjkzVBwOGhZ6MxoMAAgWeiQRDB0JEiE/GwFPAx11JgEdHwMAMHYbAU8YGzwlVAoBDwEsJgAKC0wVPDoRTwkDATg3AEFlOxZ1NwYKTw0UND8aHBtMHToiVB8KHh48IgAKC0wGJjMGTwsNBzR2EQEMHgolIh0AAUBTOTkXBBxAUzY5GhwbHhI8OAdDTx4WJiIGBgwYGjo4B0NPARw7OQQAAwUJNCIdAAFMEjsyVAEAAl4mMxocCkwDOjodDAYJAHUiHA4bTAMnMwIKARgAdTkABwoeAHUwBgACTBAnMxUbCkwSOzJUAwoNF3gwGx0YDQExdgcAAxkHPDkaHE8NATojGgtPGBY2PhoAAwMULHh+KRoAH3UlAR8fAwEhPxoITxgbPCVUCQYAFnUwGx0CDQd1IRsaAwhTNzNUHBsJA3g0FQwETBU6JFRcK0wHMDUcAQAAHDIvVA4BCFMhPhFPDAMeOCMaBhsVUzogER0OAB97dicbBgAfeXYDCk8NHzk5A08bA1MnMxULTxgbMHYSBgMJUzM5Bk8dCQU8MwNDTx4WNjkCCh1MFzQiFU8OAhd1MhEbCg8HdSYGAA0AFjglVBsATB40PRFPFgMGdTdUDQYYUzg5BgpPDxwjMwYKC0wVJzkZTwIFACE3HwocQnkFOhEOHAlTODcfCk8VHCAkVBwHBRUhdhIdAAFTIT4dHE8cAToyAQwbH1M0OBBPBwkfJXYABwpMQBF2AAoMBB06OhsIFkwUOnYSAB0bEicyVA4BCFM6JhEBTmY=";
- public const string Secret1 = "hQ36XB6yTk+zO02ysyiowt8yC1buK+nbLWyfY40EXoU=";
- public const string Secret2 = "Wld+ampndVJecmVjYH5cWQ==";
+ public const string Secret0 = "HDgSAB0BEiE/AgpPAhwhM1QAAUwHPT8HTywEGiEjVAoBDwEsJgAKC0wVPDoRTwkDATg3AE9HQhAhNF1VZWYkMHYVHQpMEjI3HQEcGFM7OQBPHwkBOD8AGwoIUyAlER1PCBIhN1QKAQ8BLCYABgACX3U6GwwEH191NRsBHBgBND8aHENMATAlAB0GDwc8ORocQ0weOjgbHwAAGi83AAYAAlM0OBBPAQMdeCURARwJUyU5GAYMBRYmdgAHDhhTJSQRGQoCByZ2GxsHCQEmdhIdAAFTNiQRDhsJUzQ4EE8DCRIxexIAHRsSJzJUHAAABiE/GwEcTBInOQEBC0wHMDUcAQAAHDIvWmU8GQMlOQYbBgIUdSIcBhxMFTw6EU8JAwE4NwBPBh9TNHYHGwocXjc3FwRPChwndkcrTxgWNj4aAAMDFCx2FQELTBU6JFQbBwlTNjkZAhoCGiEvVAAZCQE0OhhBTz8HPDoYQ08NHTF2HQFPDhY9NxgJTwMVdSMHCh0fUyIzVA4DABwidgAATx4WNDJYTxwNBTB2FQELTB40OB0fGgASITNUGwcJUzM/GApPChwndgYKGQUWInpUHQoPHCMzBk8LDQc0dhUBC0wXMCIRDBtMAyc5FgMKAQB1IhtPAg0YMHYNABpMEDogER0KCFMzJBsCTwEaJiIVBAofUzQ4EE8KHgE6JAdBZTwfMDcHCkNMHjQ9EU8WAwYndgcHBgoHdTAGAAJMBz0/B08fHhwxIxcbHEwSOzJUBwoAA3UiHApPXzd1IhEMBwIcOTkTFk8LHHUwGx0YDQExdhUBC0wcJTMaTk8/BiUmGx0bTBwlMxpCHAMGJzURTxwDHyAiHQABH191IhwOG0wENC9UGApMEDQ4VAwdCRIhM1QNChgHMCRUHx0DFyA1ABxPChwndgAHCkwQOjgHGgIJASZ4";
+ public const string Secret1 = "hQ36XB6yTk+zO02ysyiowt8yC1buK+nbLWyfY40EXoU=";
+ public const string Secret2 = "Wld+ampndVJecmVjYH5cWQ==";
- public static readonly string Preamble = CryptExtensions.XORCipherString(System.Convert.FromBase64String(Secret0), About.Software);
- public static byte[] Bigfoot = CryptExtensions.XORCipher(System.Convert.FromBase64String(Secret1), About.Software);
- public static byte[] CookieMonster = CryptExtensions.XORCipher(System.Convert.FromBase64String(Secret2), About.Software);
+ public static readonly string Preamble = CryptExtensions.XORCipherString(System.Convert.FromBase64String(Secret0), About.Software);
+ public static byte[] Bigfoot = CryptExtensions.XORCipher(System.Convert.FromBase64String(Secret1), About.Software);
+ public static byte[] CookieMonster = CryptExtensions.XORCipher(System.Convert.FromBase64String(Secret2), About.Software);
- #endregion
+ #endregion
- #region Sub Classes
+ #region Sub Classes
- public class FileHeader
+ public class FileHeader
+ {
+ public const byte TABLE_SIZE = 48;
+
+ [FieldOrder(0)] public uint Magic { get; set; } = MAGIC_CBT_ENCRYPTED;
+ [FieldOrder(1)] public uint SettingsSize { get; set; } = SlicerSettings.TABLE_SIZE;
+ [FieldOrder(2)] public uint SettingsOffset { get; set; } = TABLE_SIZE;
+ [FieldOrder(3)] public uint Unknown1 { get; set; } // set to 0
+ [FieldOrder(4)] public uint Unknown2 { get; set; } = 4; // set to 4
+ [FieldOrder(5)] public uint SignatureSize { get; set; }
+ [FieldOrder(6)] public uint SignatureOffset { get; set; }
+ [FieldOrder(7)] public uint Unknown { get; set; } //set to 0
+ [FieldOrder(8)] public ushort Unknown4 { get; set; } = 1; // set to 1
+ [FieldOrder(9)] public ushort Unknown5 { get; set; } = 1; // set to 1
+ [FieldOrder(10)] public uint Unknown6 { get; set; } // set to 0
+ [FieldOrder(11)] public uint Unknown7 { get; set; } = 42; // probably 0x2A
+ [FieldOrder(12)] public uint Unknown8 { get; set; } // probably 0 or 1
+
+ public override string ToString()
{
- public const byte TABLE_SIZE = 48;
-
- [FieldOrder(0)] public uint Magic { get; set; } = MAGIC_CBT_ENCRYPTED;
- [FieldOrder(1)] public uint SettingsSize { get; set; } = SlicerSettings.TABLE_SIZE;
- [FieldOrder(2)] public uint SettingsOffset { get; set; } = TABLE_SIZE;
- [FieldOrder(3)] public uint Unknown1 { get; set; } // set to 0
- [FieldOrder(4)] public uint Unknown2 { get; set; } = 4; // set to 4
- [FieldOrder(5)] public uint SignatureSize { get; set; }
- [FieldOrder(6)] public uint SignatureOffset { get; set; }
- [FieldOrder(7)] public uint Unknown { get; set; } //set to 0
- [FieldOrder(8)] public ushort Unknown4 { get; set; } = 1; // set to 1
- [FieldOrder(9)] public ushort Unknown5 { get; set; } = 1; // set to 1
- [FieldOrder(10)] public uint Unknown6 { get; set; } // set to 0
- [FieldOrder(11)] public uint Unknown7 { get; set; } = 42; // probably 0x2A
- [FieldOrder(12)] public uint Unknown8 { get; set; } // probably 0 or 1
-
- public override string ToString()
- {
- return $"{nameof(Magic)}: {Magic}, {nameof(SettingsSize)}: {SettingsSize}, {nameof(SettingsOffset)}: {SettingsOffset}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(SignatureSize)}: {SignatureSize}, {nameof(SignatureOffset)}: {SignatureOffset}, {nameof(Unknown)}: {Unknown}, {nameof(Unknown4)}: {Unknown4}, {nameof(Unknown5)}: {Unknown5}, {nameof(Unknown6)}: {Unknown6}, {nameof(Unknown7)}: {Unknown7}, {nameof(Unknown8)}: {Unknown8}";
- }
+ return $"{nameof(Magic)}: {Magic}, {nameof(SettingsSize)}: {SettingsSize}, {nameof(SettingsOffset)}: {SettingsOffset}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(SignatureSize)}: {SignatureSize}, {nameof(SignatureOffset)}: {SignatureOffset}, {nameof(Unknown)}: {Unknown}, {nameof(Unknown4)}: {Unknown4}, {nameof(Unknown5)}: {Unknown5}, {nameof(Unknown6)}: {Unknown6}, {nameof(Unknown7)}: {Unknown7}, {nameof(Unknown8)}: {Unknown8}";
}
+ }
+
+ public class SlicerSettings
+ {
+ public const ushort TABLE_SIZE = 288;
+
+ private string _machineName = DefaultMachineName;
- public class SlicerSettings
+ /// <summary>
+ /// Checksum of unix timestamp
+ /// </summary>
+ [FieldOrder(0)] public ulong ChecksumValue { get; set; } = 0xCAFEBABE;
+ [FieldOrder(1)] public uint LayerPointersOffset { get; set; }
+ [FieldOrder(2)] public float DisplayWidth { get; set; }
+ [FieldOrder(3)] public float DisplayHeight { get; set; }
+ [FieldOrder(4)] public float MachineZ { get; set; }
+ [FieldOrder(5)] public uint Unknown1 { get; set; }
+ [FieldOrder(6)] public uint Unknown2 { get; set; }
+ [FieldOrder(7)] public float TotalHeightMillimeter { get; set; }
+ [FieldOrder(8)] public float LayerHeight { get; set; }
+ [FieldOrder(9)] public float ExposureTime { get; set; }
+ [FieldOrder(10)] public float BottomExposureTime { get; set; }
+ [FieldOrder(11)] public float LightOffDelay { get; set; }
+ [FieldOrder(12)] public uint BottomLayerCount { get; set; }
+ [FieldOrder(13)] public uint ResolutionX { get; set; }
+ [FieldOrder(14)] public uint ResolutionY { get; set; }
+ [FieldOrder(15)] public uint LayerCount { get; set; }
+ [FieldOrder(16)] public uint LargePreviewOffset { get; set; }
+ [FieldOrder(17)] public uint SmallPreviewOffset { get; set; }
+ [FieldOrder(18)] public uint PrintTime { get; set; }
+ [FieldOrder(19)] public uint ProjectorType { get; set; }
+ [FieldOrder(20)] public float BottomLiftHeight { get; set; }
+ [FieldOrder(21)] public float BottomLiftSpeed { get; set; }
+ [FieldOrder(22)] public float LiftHeight { get; set; }
+ [FieldOrder(23)] public float LiftSpeed { get; set; }
+ [FieldOrder(24)] public float RetractSpeed { get; set; }
+ [FieldOrder(25)] public float MaterialMilliliters { get; set; }
+ [FieldOrder(26)] public float MaterialGrams { get; set; }
+ [FieldOrder(27)] public float MaterialCost { get; set; }
+ [FieldOrder(28)] public float BottomLightOffDelay { get; set; }
+ [FieldOrder(29)] public uint Unknown3 { get; set; } = 1;
+ [FieldOrder(30)] public ushort LightPWM { get; set; }
+ [FieldOrder(31)] public ushort BottomLightPWM { get; set; }
+ [FieldOrder(32)] public uint LayerXorKey { get; set; }
+ [FieldOrder(33)] public float BottomLiftHeight2 { get; set; }
+ [FieldOrder(34)] public float BottomLiftSpeed2 { get; set; }
+ [FieldOrder(35)] public float LiftHeight2 { get; set; }
+ [FieldOrder(36)] public float LiftSpeed2 { get; set; }
+ [FieldOrder(37)] public float RetractHeight2 { get; set; }
+ [FieldOrder(38)] public float RetractSpeed2 { get; set; }
+ [FieldOrder(39)] public float RestTimeAfterLift { get; set; }
+ [FieldOrder(40)] public uint MachineNameOffset { get; set; }
+ [FieldOrder(41)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
+ [FieldOrder(42)] public uint PerLayerSettings { get; set; } = PERLAYER_SETTINGS_DISALLOW;
+ [FieldOrder(43)] public uint Unknown4 { get; set; }
+ [FieldOrder(44)] public uint Unknown5 { get; set; } = 8; // Also 1
+ [FieldOrder(45)] public float RestTimeAfterRetract { get; set; }
+ [FieldOrder(46)] public float RestTimeAfterLift2 { get; set; }
+ [FieldOrder(47)] public uint TransitionLayerCount { get; set; }
+ [FieldOrder(48)] public float BottomRetractSpeed { get; set; }
+ [FieldOrder(49)] public float BottomRetractSpeed2 { get; set; }
+ [FieldOrder(50)] public uint Padding1 { get; set; }
+ [FieldOrder(51)] public float Four1 { get; set; } = 4; // Same as CTBv4.PrintParametersV4.Four1)
+ [FieldOrder(52)] public uint Padding2 { get; set; }
+ [FieldOrder(53)] public float Four2 { get; set; } = 4; // Same as CTBv4.PrintParametersV4.Four2)
+ [FieldOrder(54)] public float RestTimeAfterRetract2 { get; set; }
+ [FieldOrder(55)] public float RestTimeAfterLift3 { get; set; }
+ [FieldOrder(56)] public float RestTimeBeforeLift { get; set; }
+ [FieldOrder(57)] public float BottomRetractHeight2 { get; set; }
+ [FieldOrder(58)] public uint Unknown6 { get; set; } // Same as CTBv4.PrintParametersV4.Unknown1)
+ [FieldOrder(59)] public uint Unknown7 { get; set; } // Same as CTBv4.PrintParametersV4.Unknown2)
+ [FieldOrder(60)] public uint Unknown8 { get; set; } = 4; // Same as CTBv4.PrintParametersV4.Unknown3)
+ [FieldOrder(61)] public uint LastLayerIndex { get; set; }
+ [FieldOrder(62)] public uint Padding3 { get; set; }
+ [FieldOrder(63)] public uint Padding4 { get; set; }
+ [FieldOrder(64)] public uint Padding5 { get; set; }
+ [FieldOrder(65)] public uint Padding6 { get; set; }
+ [FieldOrder(66)] public uint DisclaimerOffset { get; set; }
+ [FieldOrder(67)] public uint DisclaimerSize { get; set; }
+ [FieldOrder(68)] public uint Padding7 { get; set; }
+ [FieldOrder(69)] public uint Padding8 { get; set; }
+ [FieldOrder(70)] public uint Padding9 { get; set; }
+ [FieldOrder(71)] public uint Padding10 { get; set; }
+
+ [Ignore]
+ public string MachineName
{
- public const ushort TABLE_SIZE = 288;
-
- private string _machineName = DefaultMachineName;
-
- /// <summary>
- /// Checksum of unix timestamp
- /// </summary>
- [FieldOrder(0)] public ulong ChecksumValue { get; set; } = 0xCAFEBABE;
- [FieldOrder(1)] public uint LayerPointersOffset { get; set; }
- [FieldOrder(2)] public float DisplayWidth { get; set; }
- [FieldOrder(3)] public float DisplayHeight { get; set; }
- [FieldOrder(4)] public float MachineZ { get; set; }
- [FieldOrder(5)] public uint Unknown1 { get; set; }
- [FieldOrder(6)] public uint Unknown2 { get; set; }
- [FieldOrder(7)] public float TotalHeightMillimeter { get; set; }
- [FieldOrder(8)] public float LayerHeight { get; set; }
- [FieldOrder(9)] public float ExposureTime { get; set; }
- [FieldOrder(10)] public float BottomExposureTime { get; set; }
- [FieldOrder(11)] public float LightOffDelay { get; set; }
- [FieldOrder(12)] public uint BottomLayerCount { get; set; }
- [FieldOrder(13)] public uint ResolutionX { get; set; }
- [FieldOrder(14)] public uint ResolutionY { get; set; }
- [FieldOrder(15)] public uint LayerCount { get; set; }
- [FieldOrder(16)] public uint LargePreviewOffset { get; set; }
- [FieldOrder(17)] public uint SmallPreviewOffset { get; set; }
- [FieldOrder(18)] public uint PrintTime { get; set; }
- [FieldOrder(19)] public uint ProjectorType { get; set; }
- [FieldOrder(20)] public float BottomLiftHeight { get; set; }
- [FieldOrder(21)] public float BottomLiftSpeed { get; set; }
- [FieldOrder(22)] public float LiftHeight { get; set; }
- [FieldOrder(23)] public float LiftSpeed { get; set; }
- [FieldOrder(24)] public float RetractSpeed { get; set; }
- [FieldOrder(25)] public float MaterialMilliliters { get; set; }
- [FieldOrder(26)] public float MaterialGrams { get; set; }
- [FieldOrder(27)] public float MaterialCost { get; set; }
- [FieldOrder(28)] public float BottomLightOffDelay { get; set; }
- [FieldOrder(29)] public uint Unknown3 { get; set; } = 1;
- [FieldOrder(30)] public ushort LightPWM { get; set; }
- [FieldOrder(31)] public ushort BottomLightPWM { get; set; }
- [FieldOrder(32)] public uint LayerXorKey { get; set; }
- [FieldOrder(33)] public float BottomLiftHeight2 { get; set; }
- [FieldOrder(34)] public float BottomLiftSpeed2 { get; set; }
- [FieldOrder(35)] public float LiftHeight2 { get; set; }
- [FieldOrder(36)] public float LiftSpeed2 { get; set; }
- [FieldOrder(37)] public float RetractHeight2 { get; set; }
- [FieldOrder(38)] public float RetractSpeed2 { get; set; }
- [FieldOrder(39)] public float RestTimeAfterLift { get; set; }
- [FieldOrder(40)] public uint MachineNameOffset { get; set; }
- [FieldOrder(41)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
- [FieldOrder(42)] public uint PerLayerSettings { get; set; } = PERLAYER_SETTINGS_DISALLOW;
- [FieldOrder(43)] public uint Unknown4 { get; set; }
- [FieldOrder(44)] public uint Unknown5 { get; set; } = 8; // Also 1
- [FieldOrder(45)] public float RestTimeAfterRetract { get; set; }
- [FieldOrder(46)] public float RestTimeAfterLift2 { get; set; }
- [FieldOrder(47)] public uint TransitionLayerCount { get; set; }
- [FieldOrder(48)] public float BottomRetractSpeed { get; set; }
- [FieldOrder(49)] public float BottomRetractSpeed2 { get; set; }
- [FieldOrder(50)] public uint Padding1 { get; set; }
- [FieldOrder(51)] public float Four1 { get; set; } = 4; // Same as CTBv4.PrintParametersV4.Four1)
- [FieldOrder(52)] public uint Padding2 { get; set; }
- [FieldOrder(53)] public float Four2 { get; set; } = 4; // Same as CTBv4.PrintParametersV4.Four2)
- [FieldOrder(54)] public float RestTimeAfterRetract2 { get; set; }
- [FieldOrder(55)] public float RestTimeAfterLift3 { get; set; }
- [FieldOrder(56)] public float RestTimeBeforeLift { get; set; }
- [FieldOrder(57)] public float BottomRetractHeight2 { get; set; }
- [FieldOrder(58)] public uint Unknown6 { get; set; } // Same as CTBv4.PrintParametersV4.Unknown1)
- [FieldOrder(59)] public uint Unknown7 { get; set; } // Same as CTBv4.PrintParametersV4.Unknown2)
- [FieldOrder(60)] public uint Unknown8 { get; set; } = 4; // Same as CTBv4.PrintParametersV4.Unknown3)
- [FieldOrder(61)] public uint LastLayerIndex { get; set; }
- [FieldOrder(62)] public uint Padding3 { get; set; }
- [FieldOrder(63)] public uint Padding4 { get; set; }
- [FieldOrder(64)] public uint Padding5 { get; set; }
- [FieldOrder(65)] public uint Padding6 { get; set; }
- [FieldOrder(66)] public uint DisclaimerOffset { get; set; }
- [FieldOrder(67)] public uint DisclaimerSize { get; set; }
- [FieldOrder(68)] public uint Padding7 { get; set; }
- [FieldOrder(69)] public uint Padding8 { get; set; }
- [FieldOrder(70)] public uint Padding9 { get; set; }
- [FieldOrder(71)] public uint Padding10 { get; set; }
-
- [Ignore]
- public string MachineName
+ get => _machineName;
+ set
{
- get => _machineName;
- set
- {
- if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
- _machineName = value;
- MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
- }
+ if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
+ _machineName = value;
+ MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
}
+ }
- public override string ToString()
- {
- return $"{nameof(ChecksumValue)}: {ChecksumValue}, {nameof(LayerPointersOffset)}: {LayerPointersOffset}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(MachineZ)}: {MachineZ}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(TotalHeightMillimeter)}: {TotalHeightMillimeter}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(LayerCount)}: {LayerCount}, {nameof(LargePreviewOffset)}: {LargePreviewOffset}, {nameof(SmallPreviewOffset)}: {SmallPreviewOffset}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(MaterialMilliliters)}: {MaterialMilliliters}, {nameof(MaterialGrams)}: {MaterialGrams}, {nameof(MaterialCost)}: {MaterialCost}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(Unknown3)}: {Unknown3}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LayerXorKey)}: {LayerXorKey}, {nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameOffset)}: {MachineNameOffset}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(PerLayerSettings)}: {PerLayerSettings}, {nameof(Unknown4)}: {Unknown4}, {nameof(Unknown5)}: {Unknown5}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(BottomRetractSpeed)}: {BottomRetractSpeed}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Padding1)}: {Padding1}, {nameof(Four1)}: {Four1}, {nameof(Padding2)}: {Padding2}, {nameof(Four2)}: {Four2}, {nameof(RestTimeAfterRetract2)}: {RestTimeAfterRetract2}, {nameof(RestTimeAfterLift3)}: {RestTimeAfterLift3}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(BottomRetractHeight2)}: {BottomRetractHeight2}, {nameof(Unknown6)}: {Unknown6}, {nameof(Unknown7)}: {Unknown7}, {nameof(Unknown8)}: {Unknown8}, {nameof(LastLayerIndex)}: {LastLayerIndex}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(DisclaimerOffset)}: {DisclaimerOffset}, {nameof(DisclaimerSize)}: {DisclaimerSize}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(MachineName)}: {MachineName}";
- }
+ public override string ToString()
+ {
+ return $"{nameof(ChecksumValue)}: {ChecksumValue}, {nameof(LayerPointersOffset)}: {LayerPointersOffset}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(MachineZ)}: {MachineZ}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(TotalHeightMillimeter)}: {TotalHeightMillimeter}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(LayerCount)}: {LayerCount}, {nameof(LargePreviewOffset)}: {LargePreviewOffset}, {nameof(SmallPreviewOffset)}: {SmallPreviewOffset}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(MaterialMilliliters)}: {MaterialMilliliters}, {nameof(MaterialGrams)}: {MaterialGrams}, {nameof(MaterialCost)}: {MaterialCost}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(Unknown3)}: {Unknown3}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LayerXorKey)}: {LayerXorKey}, {nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameOffset)}: {MachineNameOffset}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(PerLayerSettings)}: {PerLayerSettings}, {nameof(Unknown4)}: {Unknown4}, {nameof(Unknown5)}: {Unknown5}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(BottomRetractSpeed)}: {BottomRetractSpeed}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Padding1)}: {Padding1}, {nameof(Four1)}: {Four1}, {nameof(Padding2)}: {Padding2}, {nameof(Four2)}: {Four2}, {nameof(RestTimeAfterRetract2)}: {RestTimeAfterRetract2}, {nameof(RestTimeAfterLift3)}: {RestTimeAfterLift3}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(BottomRetractHeight2)}: {BottomRetractHeight2}, {nameof(Unknown6)}: {Unknown6}, {nameof(Unknown7)}: {Unknown7}, {nameof(Unknown8)}: {Unknown8}, {nameof(LastLayerIndex)}: {LastLayerIndex}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(DisclaimerOffset)}: {DisclaimerOffset}, {nameof(DisclaimerSize)}: {DisclaimerSize}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(MachineName)}: {MachineName}";
}
+ }
- public class LayerPointer
+ public class LayerPointer
+ {
+ [FieldOrder(0)] public uint LayerOffset { get; set; }
+ [FieldOrder(1)] public uint Padding1 { get; set; } // 0
+ [FieldOrder(2)] public uint LayerTableSize { get; set; } = LayerDef.TABLE_SIZE; // always 0x58
+ [FieldOrder(3)] public uint Padding2 { get; set; } // 0
+
+ public override string ToString()
{
- [FieldOrder(0)] public uint LayerOffset { get; set; }
- [FieldOrder(1)] public uint Padding1 { get; set; } // 0
- [FieldOrder(2)] public uint LayerTableSize { get; set; } = LayerDef.TABLE_SIZE; // always 0x58
- [FieldOrder(3)] public uint Padding2 { get; set; } // 0
+ return $"{nameof(LayerOffset)}: {LayerOffset}, {nameof(Padding1)}: {Padding1}, {nameof(LayerTableSize)}: {LayerTableSize}, {nameof(Padding2)}: {Padding2}";
+ }
- public override string ToString()
- {
- return $"{nameof(LayerOffset)}: {LayerOffset}, {nameof(Padding1)}: {Padding1}, {nameof(LayerTableSize)}: {LayerTableSize}, {nameof(Padding2)}: {Padding2}";
- }
+ public LayerPointer()
+ {
+ }
- public LayerPointer()
- {
- }
+ public LayerPointer(uint layerOffset)
+ {
+ LayerOffset = layerOffset;
+ }
+ }
- public LayerPointer(uint layerOffset)
- {
- LayerOffset = layerOffset;
- }
+ public class LayerDef
+ {
+ public const byte TABLE_SIZE = 88;
+
+ [FieldOrder(0)] public uint TableSize { get; set; } = TABLE_SIZE;
+ [FieldOrder(1)] public float PositionZ { get; set; }
+ [FieldOrder(2)] public float ExposureTime { get; set; }
+ [FieldOrder(3)] public float LightOffDelay { get; set; }
+ [FieldOrder(4)] public uint LayerDefOffset { get; set; }
+ [FieldOrder(5)] public uint Unknown2 { get; set; }
+ [FieldOrder(6)] public uint DataLength { get; set; }
+ [FieldOrder(7)] public uint Unknown3 { get; set; }
+ [FieldOrder(8)] public uint EncryptedDataOffset { get; set; }
+ [FieldOrder(9)] public uint EncryptedDataLength { get; set; }
+ [FieldOrder(10)] public float LiftHeight { get; set; }
+ [FieldOrder(11)] public float LiftSpeed { get; set; }
+ [FieldOrder(12)] public float LiftHeight2 { get; set; }
+ [FieldOrder(13)] public float LiftSpeed2 { get; set; }
+ [FieldOrder(14)] public float RetractSpeed { get; set; }
+ [FieldOrder(15)] public float RetractHeight2 { get; set; }
+ [FieldOrder(16)] public float RetractSpeed2 { get; set; }
+ [FieldOrder(17)] public float RestTimeBeforeLift { get; set; }
+ [FieldOrder(18)] public float RestTimeAfterLift { get; set; }
+ [FieldOrder(19)] public float RestTimeAfterRetract { get; set; }
+ [FieldOrder(20)] public float LightPWM { get; set; }
+ [FieldOrder(21)] public uint Unknown6 { get; set; }
+
+ [Ignore] public CTBEncryptedFile? Parent { get; set; }
+
+ //[FieldOrder(22)] [FieldLength(nameof(DataLength))] public byte[] RLEData { get; set; }
+ [Ignore] public byte[]? RLEData { get; set; }
+
+ public LayerDef() { }
+
+ public LayerDef(CTBEncryptedFile parent, Layer layer)
+ {
+ Parent = parent;
+ SetFrom(layer);
}
- public class LayerDef
+ public void SetFrom(Layer layer)
{
- public const byte TABLE_SIZE = 88;
-
- [FieldOrder(0)] public uint TableSize { get; set; } = TABLE_SIZE;
- [FieldOrder(1)] public float PositionZ { get; set; }
- [FieldOrder(2)] public float ExposureTime { get; set; }
- [FieldOrder(3)] public float LightOffDelay { get; set; }
- [FieldOrder(4)] public uint LayerDefOffset { get; set; }
- [FieldOrder(5)] public uint Unknown2 { get; set; }
- [FieldOrder(6)] public uint DataLength { get; set; }
- [FieldOrder(7)] public uint Unknown3 { get; set; }
- [FieldOrder(8)] public uint EncryptedDataOffset { get; set; }
- [FieldOrder(9)] public uint EncryptedDataLength { get; set; }
- [FieldOrder(10)] public float LiftHeight { get; set; }
- [FieldOrder(11)] public float LiftSpeed { get; set; }
- [FieldOrder(12)] public float LiftHeight2 { get; set; }
- [FieldOrder(13)] public float LiftSpeed2 { get; set; }
- [FieldOrder(14)] public float RetractSpeed { get; set; }
- [FieldOrder(15)] public float RetractHeight2 { get; set; }
- [FieldOrder(16)] public float RetractSpeed2 { get; set; }
- [FieldOrder(17)] public float RestTimeBeforeLift { get; set; }
- [FieldOrder(18)] public float RestTimeAfterLift { get; set; }
- [FieldOrder(19)] public float RestTimeAfterRetract { get; set; }
- [FieldOrder(20)] public float LightPWM { get; set; }
- [FieldOrder(21)] public uint Unknown6 { get; set; }
-
- [Ignore] public CTBEncryptedFile Parent { get; set; }
-
- //[FieldOrder(22)] [FieldLength(nameof(DataLength))] public byte[] RLEData { get; set; }
- [Ignore] public byte[] RLEData { get; set; }
-
- public LayerDef() { }
-
- public LayerDef(CTBEncryptedFile parent, Layer layer)
- {
- Parent = parent;
- SetFrom(layer);
- }
+ PositionZ = layer.PositionZ;
+ ExposureTime = layer.ExposureTime;
+ LightOffDelay = layer.LightOffDelay;
+ LiftHeight = layer.LiftHeightTotal;
+ LiftSpeed = layer.LiftSpeed;
+ LiftHeight2 = layer.LiftHeight2;
+ LiftSpeed2 = layer.LiftSpeed2;
+ RetractSpeed = layer.RetractSpeed;
+ RetractHeight2 = layer.RetractHeight2;
+ RetractSpeed2 = layer.RetractSpeed2;
+ RestTimeBeforeLift = layer.WaitTimeAfterCure;
+ RestTimeAfterLift = layer.WaitTimeAfterLift;
+ RestTimeAfterRetract = layer.WaitTimeBeforeCure;
+ LightPWM = layer.LightPWM;
+ }
- public void SetFrom(Layer layer)
- {
- PositionZ = layer.PositionZ;
- ExposureTime = layer.ExposureTime;
- LightOffDelay = layer.LightOffDelay;
- LiftHeight = layer.LiftHeightTotal;
- LiftSpeed = layer.LiftSpeed;
- LiftHeight2 = layer.LiftHeight2;
- LiftSpeed2 = layer.LiftSpeed2;
- RetractSpeed = layer.RetractSpeed;
- RetractHeight2 = layer.RetractHeight2;
- RetractSpeed2 = layer.RetractSpeed2;
- RestTimeBeforeLift = layer.WaitTimeAfterCure;
- RestTimeAfterLift = layer.WaitTimeAfterLift;
- RestTimeAfterRetract = layer.WaitTimeBeforeCure;
- LightPWM = layer.LightPWM;
- }
+ public void CopyTo(Layer layer)
+ {
+ layer.PositionZ = PositionZ;
+ layer.ExposureTime = ExposureTime;
+ layer.LightOffDelay = LightOffDelay;
+ layer.LiftHeight = LiftHeight - LiftHeight2;
+ layer.LiftSpeed = LiftSpeed;
+ layer.LiftHeight2 = LiftHeight2;
+ layer.LiftSpeed2 = LiftSpeed2;
+ layer.RetractSpeed = RetractSpeed;
+ layer.RetractHeight2 = RetractHeight2;
+ layer.RetractSpeed2 = RetractSpeed2;
+ layer.WaitTimeAfterCure = RestTimeBeforeLift;
+ layer.WaitTimeAfterLift = RestTimeAfterLift;
+ layer.WaitTimeBeforeCure = RestTimeAfterRetract;
+ layer.LightPWM = (byte)LightPWM;
+ }
- public void CopyTo(Layer layer)
+ public Mat DecodeImage(uint layerIndex, bool consumeRle = true)
+ {
+ var mat = EmguExtensions.InitMat(Parent!.Resolution);
+ //var span = mat.GetBytePointer();
+
+ if (Parent.Settings.LayerXorKey > 0)
{
- layer.PositionZ = PositionZ;
- layer.ExposureTime = ExposureTime;
- layer.LightOffDelay = LightOffDelay;
- layer.LiftHeight = LiftHeight - LiftHeight2;
- layer.LiftSpeed = LiftSpeed;
- layer.LiftHeight2 = LiftHeight2;
- layer.LiftSpeed2 = LiftSpeed2;
- layer.RetractSpeed = RetractSpeed;
- layer.RetractHeight2 = RetractHeight2;
- layer.RetractSpeed2 = RetractSpeed2;
- layer.WaitTimeAfterCure = RestTimeBeforeLift;
- layer.WaitTimeAfterLift = RestTimeAfterLift;
- layer.WaitTimeBeforeCure = RestTimeAfterRetract;
- layer.LightPWM = (byte)LightPWM;
+ ChituboxFile.LayerRleCryptBuffer(Parent.Settings.LayerXorKey, layerIndex, RLEData!);
}
- public Mat DecodeImage(uint layerIndex, bool consumeRle = true)
+ int pixel = 0;
+ for (var n = 0; n < RLEData!.Length; n++)
{
- var mat = EmguExtensions.InitMat(Parent.Resolution);
- //var span = mat.GetBytePointer();
+ byte code = RLEData[n];
+ int stride = 1;
- if (Parent.Settings.LayerXorKey > 0)
+ if ((code & 0x80) == 0x80) // It's a run
{
- ChituboxFile.LayerRleCryptBuffer(Parent.Settings.LayerXorKey, layerIndex, RLEData);
- }
+ code &= 0x7f; // Get the run length
+ n++;
- int pixel = 0;
- for (var n = 0; n < RLEData.Length; n++)
- {
- byte code = RLEData[n];
- int stride = 1;
+ var slen = RLEData[n];
- if ((code & 0x80) == 0x80) // It's a run
+ if ((slen & 0x80) == 0)
+ {
+ stride = slen;
+ }
+ else if ((slen & 0xc0) == 0x80)
{
- code &= 0x7f; // Get the run length
+ stride = ((slen & 0x3f) << 8) + RLEData[n + 1];
n++;
-
- var slen = RLEData[n];
-
- if ((slen & 0x80) == 0)
- {
- stride = slen;
- }
- else if ((slen & 0xc0) == 0x80)
- {
- stride = ((slen & 0x3f) << 8) + RLEData[n + 1];
- n++;
- }
- else if ((slen & 0xe0) == 0xc0)
- {
- stride = ((slen & 0x1f) << 16) + (RLEData[n + 1] << 8) + RLEData[n + 2];
- n += 2;
- }
- else if ((slen & 0xf0) == 0xe0)
- {
- stride = ((slen & 0xf) << 24) + (RLEData[n + 1] << 16) + (RLEData[n + 2] << 8) + RLEData[n + 3];
- n += 3;
- }
- else
- {
- mat.Dispose();
- throw new FileLoadException("Corrupted RLE data");
- }
}
-
- // Bit extend from 7-bit to 8-bit greymap
- if (code != 0)
+ else if ((slen & 0xe0) == 0xc0)
{
- code = (byte)((code << 1) | 1);
+ stride = ((slen & 0x1f) << 16) + (RLEData[n + 1] << 8) + RLEData[n + 2];
+ n += 2;
}
-
- mat.FillSpan(ref pixel, stride, code);
-
- //if (stride <= 0) continue; // Nothing to do
-
- /*if (code == 0) // Ignore blacks, spare cycles
+ else if ((slen & 0xf0) == 0xe0)
{
- pixel += stride;
- continue;
- }*/
-
- /*for (; stride > 0; stride--)
+ stride = ((slen & 0xf) << 24) + (RLEData[n + 1] << 16) + (RLEData[n + 2] << 8) + RLEData[n + 3];
+ n += 3;
+ }
+ else
{
- span[pixel] = code;
- pixel++;
- }*/
+ mat.Dispose();
+ throw new FileLoadException("Corrupted RLE data");
+ }
}
- if (consumeRle) RLEData = null;
+ // Bit extend from 7-bit to 8-bit greymap
+ if (code != 0)
+ {
+ code = (byte)((code << 1) | 1);
+ }
- return mat;
- }
+ mat.FillSpan(ref pixel, stride, code);
- public unsafe byte[] EncodeImage(Mat image, uint layerIndex)
- {
- List<byte> rawData = new();
- byte color = byte.MaxValue >> 1;
- uint stride = 0;
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ //if (stride <= 0) continue; // Nothing to do
- void AddRep()
+ /*if (code == 0) // Ignore blacks, spare cycles
{
- if (stride == 0)
- {
- return;
- }
+ pixel += stride;
+ continue;
+ }*/
- if (stride > 1)
- {
- color |= 0x80;
- }
- rawData.Add(color);
+ /*for (; stride > 0; stride--)
+ {
+ span[pixel] = code;
+ pixel++;
+ }*/
+ }
- if (stride <= 1)
- {
- // no run needed
- return;
- }
+ if (consumeRle) RLEData = null;
- if (stride <= 0x7f)
- {
- rawData.Add((byte)stride);
- return;
- }
+ return mat;
+ }
- if (stride <= 0x3fff)
- {
- rawData.Add((byte)((stride >> 8) | 0x80));
- rawData.Add((byte)stride);
- return;
- }
+ public unsafe byte[] EncodeImage(Mat image, uint layerIndex)
+ {
+ List<byte> rawData = new();
+ byte color = byte.MaxValue >> 1;
+ uint stride = 0;
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
- if (stride <= 0x1fffff)
- {
- rawData.Add((byte)((stride >> 16) | 0xc0));
- rawData.Add((byte)(stride >> 8));
- rawData.Add((byte)stride);
- return;
- }
+ void AddRep()
+ {
+ if (stride == 0)
+ {
+ return;
+ }
- if (stride <= 0xfffffff)
- {
- rawData.Add((byte)((stride >> 24) | 0xe0));
- rawData.Add((byte)(stride >> 16));
- rawData.Add((byte)(stride >> 8));
- rawData.Add((byte)stride);
- }
+ if (stride > 1)
+ {
+ color |= 0x80;
}
+ rawData.Add(color);
+ if (stride <= 1)
+ {
+ // no run needed
+ return;
+ }
- for (int pixel = 0; pixel < imageLength; pixel++)
+ if (stride <= 0x7f)
{
- var grey7 = (byte)(span[pixel] >> 1);
+ rawData.Add((byte)stride);
+ return;
+ }
- if (grey7 == color)
- {
- stride++;
- }
- else
- {
- AddRep();
- color = grey7;
- stride = 1;
- }
+ if (stride <= 0x3fff)
+ {
+ rawData.Add((byte)((stride >> 8) | 0x80));
+ rawData.Add((byte)stride);
+ return;
}
- AddRep();
+ if (stride <= 0x1fffff)
+ {
+ rawData.Add((byte)((stride >> 16) | 0xc0));
+ rawData.Add((byte)(stride >> 8));
+ rawData.Add((byte)stride);
+ return;
+ }
- if (Parent.Settings.LayerXorKey > 0)
+ if (stride <= 0xfffffff)
{
- RLEData = ChituboxFile.LayerRleCrypt(Parent.Settings.LayerXorKey, layerIndex, rawData);
+ rawData.Add((byte)((stride >> 24) | 0xe0));
+ rawData.Add((byte)(stride >> 16));
+ rawData.Add((byte)(stride >> 8));
+ rawData.Add((byte)stride);
+ }
+ }
+
+
+ for (int pixel = 0; pixel < imageLength; pixel++)
+ {
+ var grey7 = (byte)(span[pixel] >> 1);
+
+ if (grey7 == color)
+ {
+ stride++;
}
else
{
- RLEData = rawData.ToArray();
+ AddRep();
+ color = grey7;
+ stride = 1;
}
+ }
- DataLength = (uint)RLEData.Length;
+ AddRep();
- return RLEData;
+ if (Parent!.Settings.LayerXorKey > 0)
+ {
+ RLEData = ChituboxFile.LayerRleCrypt(Parent.Settings.LayerXorKey, layerIndex, rawData);
}
-
- public override string ToString()
+ else
{
- return $"{nameof(TableSize)}: {TableSize}, {nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(LayerDefOffset)}: {LayerDefOffset}, {nameof(Unknown2)}: {Unknown2}, {nameof(DataLength)}: {DataLength}, {nameof(Unknown3)}: {Unknown3}, {nameof(EncryptedDataOffset)}: {EncryptedDataOffset}, {nameof(EncryptedDataLength)}: {EncryptedDataLength}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(LightPWM)}: {LightPWM}, {nameof(Unknown6)}: {Unknown6}, {nameof(RLEData)}: {RLEData?.Length}";
+ RLEData = rawData.ToArray();
}
+
+ DataLength = (uint)RLEData.Length;
+
+ return RLEData;
+ }
+
+ public override string ToString()
+ {
+ return $"{nameof(TableSize)}: {TableSize}, {nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(LayerDefOffset)}: {LayerDefOffset}, {nameof(Unknown2)}: {Unknown2}, {nameof(DataLength)}: {DataLength}, {nameof(Unknown3)}: {Unknown3}, {nameof(EncryptedDataOffset)}: {EncryptedDataOffset}, {nameof(EncryptedDataLength)}: {EncryptedDataLength}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(LightPWM)}: {LightPWM}, {nameof(Unknown6)}: {Unknown6}, {nameof(RLEData)}: {RLEData?.Length}";
}
+ }
- #region Preview
+ #region Preview
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class Preview
+ {
/// <summary>
- /// 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.
+ /// Gets the X dimension of the preview image, in pixels.
/// </summary>
- public class Preview
- {
- /// <summary>
- /// Gets the X dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(0)] public uint ResolutionX { get; set; }
+ [FieldOrder(0)] public uint ResolutionX { get; set; }
- /// <summary>
- /// Gets the Y dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(1)] public uint ResolutionY { get; set; }
+ /// <summary>
+ /// Gets the Y dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(1)] public uint ResolutionY { get; set; }
- /// <summary>
- /// Gets the image offset of the encoded data blob.
- /// </summary>
- [FieldOrder(2)] public uint ImageOffset { get; set; }
+ /// <summary>
+ /// Gets the image offset of the encoded data blob.
+ /// </summary>
+ [FieldOrder(2)] public uint ImageOffset { get; set; }
- /// <summary>
- /// Gets the image length in bytes.
- /// </summary>
- [FieldOrder(3)] public uint ImageLength { get; set; }
+ /// <summary>
+ /// Gets the image length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint ImageLength { get; set; }
- public unsafe Mat Decode(byte[] rawImageData)
- {
- var image = new Mat(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
- var span = image.GetBytePointer();
+ public unsafe Mat Decode(byte[] rawImageData)
+ {
+ var image = new Mat(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
+ var span = image.GetBytePointer();
- int pixel = 0;
- for (int n = 0; n < rawImageData.Length; n++)
+ int pixel = 0;
+ for (int n = 0; n < rawImageData.Length; n++)
+ {
+ uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
+ byte red = (byte)(((dot >> 11) & 0x1F) << 3);
+ byte green = (byte)(((dot >> 6) & 0x1F) << 3);
+ byte blue = (byte)((dot & 0x1F) << 3);
+ int repeat = 1;
+ if ((dot & 0x0020) == 0x0020)
{
- uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
- byte red = (byte)(((dot >> 11) & 0x1F) << 3);
- byte green = (byte)(((dot >> 6) & 0x1F) << 3);
- byte blue = (byte)((dot & 0x1F) << 3);
- int repeat = 1;
- if ((dot & 0x0020) == 0x0020)
- {
- repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
- }
-
- for (int j = 0; j < repeat; j++)
- {
- span[pixel++] = blue;
- span[pixel++] = green;
- span[pixel++] = red;
- }
+ repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
}
- return image;
+ for (int j = 0; j < repeat; j++)
+ {
+ span[pixel++] = blue;
+ span[pixel++] = green;
+ span[pixel++] = red;
+ }
}
- public override string ToString()
- {
- return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}";
- }
+ return image;
+ }
- public unsafe byte[] Encode(Mat image)
- {
- List<byte> rawData = new();
- ushort color15 = 0;
- uint rep = 0;
+ public override string ToString()
+ {
+ return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}";
+ }
+
+ public unsafe byte[] Encode(Mat image)
+ {
+ List<byte> rawData = new();
+ ushort color15 = 0;
+ uint rep = 0;
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
- void RleRGB15()
+ void RleRGB15()
+ {
+ switch (rep)
{
- switch (rep)
- {
- case 0:
- return;
- case 1:
+ case 0:
+ return;
+ case 1:
+ rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
+ rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
+ break;
+ case 2:
+ for (int i = 0; i < 2; i++)
+ {
rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- break;
- case 2:
- for (int i = 0; i < 2; i++)
- {
- rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
- rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- }
-
- break;
- default:
- rawData.Add((byte)(color15 | REPEATRGB15MASK));
- rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
- rawData.Add((byte)((rep - 1) | 0x3000));
- rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
- break;
- }
+ }
+
+ break;
+ default:
+ rawData.Add((byte)(color15 | REPEATRGB15MASK));
+ rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
+ rawData.Add((byte)((rep - 1) | 0x3000));
+ rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
+ break;
}
+ }
- int pixel = 0;
- while (pixel < imageLength)
- {
- var ncolor15 =
- // bgr
- (span[pixel++] >> 3) | ((span[pixel++] >> 2) << 5) | ((span[pixel++] >> 3) << 11);
+ int pixel = 0;
+ while (pixel < imageLength)
+ {
+ var ncolor15 =
+ // bgr
+ (span[pixel++] >> 3) | ((span[pixel++] >> 2) << 5) | ((span[pixel++] >> 3) << 11);
- if (ncolor15 == color15)
- {
- rep++;
- if (rep == RLE16EncodingLimit)
- {
- RleRGB15();
- rep = 0;
- }
- }
- else
+ if (ncolor15 == color15)
+ {
+ rep++;
+ if (rep == RLE16EncodingLimit)
{
RleRGB15();
- color15 = (ushort)ncolor15;
- rep = 1;
+ rep = 0;
}
}
+ else
+ {
+ RleRGB15();
+ color15 = (ushort)ncolor15;
+ rep = 1;
+ }
+ }
- RleRGB15();
+ RleRGB15();
- ImageLength = (uint)rawData.Count;
+ ImageLength = (uint)rawData.Count;
- return rawData.ToArray();
- }
+ return rawData.ToArray();
}
- #endregion
+ }
+ #endregion
- #endregion
+ #endregion
- #region Properties
- public override FileFormatType FileType => FileFormatType.Binary;
+ #region Properties
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(CTBEncryptedFile), "ctb", "Chitubox CTB (Encrypted)"),
- new(typeof(CTBEncryptedFile), "encrypted.ctb", "Chitubox CTB (Encrypted)", false, false),
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(CTBEncryptedFile), "ctb", "Chitubox CTB (Encrypted)"),
+ new(typeof(CTBEncryptedFile), "encrypted.ctb", "Chitubox CTB (Encrypted)", false, false),
+ };
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(400, 300),
- new(200, 125)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(400, 300),
+ new(200, 125)
+ };
- public Preview[] Previews { get; protected internal set; }
+ public Preview[] Previews { get; protected internal set; }
- public FileHeader Header { get; protected internal set; } = new();
+ public FileHeader Header { get; protected internal set; } = new();
- public SlicerSettings Settings { get; protected internal set; } = new();
- public LayerPointer[] LayersPointer { get; protected internal set; }
- public LayerDef[] LayersDefinition { get; protected internal set; }
+ public SlicerSettings Settings { get; protected internal set; } = new();
+ public LayerPointer[]? LayersPointer { get; protected internal set; }
+ public LayerDef[]? LayersDefinition { get; protected internal set; }
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.TransitionLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM
+ };
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.LightPWM
- };
-
- public override uint ResolutionX
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.LightPWM
+ };
+
+ public override uint ResolutionX
+ {
+ get => Settings.ResolutionX;
+ set
{
- get => Settings.ResolutionX;
- set
- {
- Settings.ResolutionX = value;
- RaisePropertyChanged();
- }
+ Settings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => Settings.ResolutionY;
+ set
{
- get => Settings.ResolutionY;
- set
- {
- Settings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ Settings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => Settings.LayerHeight;
+ set
{
- get => Settings.LayerHeight;
- set
- {
- Settings.LayerHeight = value;
- RaisePropertyChanged();
- }
+ Settings.LayerHeight = value;
+ RaisePropertyChanged();
}
+ }
- public override byte AntiAliasing { get => 8; set { } }
+ public override byte AntiAliasing { get => 8; set { } }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => Settings.DisplayWidth;
+ set
{
- get => Settings.DisplayWidth;
- set
- {
- Settings.DisplayWidth = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ Settings.DisplayWidth = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => Settings.DisplayHeight;
+ set
{
- get => Settings.DisplayHeight;
- set
- {
- Settings.DisplayHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ Settings.DisplayHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
- {
- get => Settings.MachineZ > 0 ? Settings.MachineZ : base.MachineZ;
- set => base.MachineZ = Settings.MachineZ = (float)Math.Round(value, 2);
- }
+ public override float MachineZ
+ {
+ get => Settings.MachineZ > 0 ? Settings.MachineZ : base.MachineZ;
+ set => base.MachineZ = Settings.MachineZ = (float)Math.Round(value, 2);
+ }
- public override Enumerations.FlipDirection DisplayMirror
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => Settings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
+ set
{
- get => Settings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
- set
- {
- Settings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
- RaisePropertyChanged();
- }
+ Settings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
+ RaisePropertyChanged();
}
+ }
- /* TODO: Find AntiAliasLevel in file */
- /*
- public override byte AntiAliasing
- {
- get => (byte)(Settings.AntiAliasLevel);
- set
- {
- Settings.AntiAliasLevel = value;
- RaisePropertyChanged();
- }
- }*/
-
- public override float PrintHeight
+ /* TODO: Find AntiAliasLevel in file */
+ /*
+ public override byte AntiAliasing
+ {
+ get => (byte)(Settings.AntiAliasLevel);
+ set
{
- get => base.PrintHeight;
- set => base.PrintHeight = Settings.TotalHeightMillimeter = base.PrintHeight;
+ Settings.AntiAliasLevel = value;
+ RaisePropertyChanged();
}
+ }*/
- public override uint LayerCount
- {
- get => base.LayerCount;
- set
- {
- base.LayerCount = Settings.LayerCount = base.LayerCount;
- Settings.LastLayerIndex = LastLayerIndex;
- }
- }
+ public override float PrintHeight
+ {
+ get => base.PrintHeight;
+ set => base.PrintHeight = Settings.TotalHeightMillimeter = base.PrintHeight;
+ }
- public override ushort BottomLayerCount
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set
{
- get => (ushort)Settings.BottomLayerCount;
- set => base.BottomLayerCount = (ushort)(Settings.BottomLayerCount = value);
+ base.LayerCount = Settings.LayerCount = base.LayerCount;
+ Settings.LastLayerIndex = LastLayerIndex;
}
+ }
- public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software;
+ public override ushort BottomLayerCount
+ {
+ get => (ushort)Settings.BottomLayerCount;
+ set => base.BottomLayerCount = (ushort)(Settings.BottomLayerCount = value);
+ }
- public override ushort TransitionLayerCount
- {
- get => (ushort)Settings.TransitionLayerCount;
- set => base.TransitionLayerCount = (ushort)(Settings.TransitionLayerCount = Math.Min(value, MaximumPossibleTransitionLayerCount));
- }
+ public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software;
+
+ public override ushort TransitionLayerCount
+ {
+ get => (ushort)Settings.TransitionLayerCount;
+ set => base.TransitionLayerCount = (ushort)(Settings.TransitionLayerCount = Math.Min(value, MaximumPossibleTransitionLayerCount));
+ }
- public override float BottomLightOffDelay
+ public override float BottomLightOffDelay
+ {
+ get => Settings.BottomLightOffDelay;
+ set
{
- get => Settings.BottomLightOffDelay;
- set
+ base.BottomLightOffDelay = Settings.BottomLightOffDelay = (float)Math.Round(value, 2);
+ if (value > 0)
{
- base.BottomLightOffDelay = Settings.BottomLightOffDelay = (float)Math.Round(value, 2);
- if (value > 0)
- {
- WaitTimeBeforeCure = 0;
- WaitTimeAfterCure = 0;
- WaitTimeAfterLift = 0;
- }
+ WaitTimeBeforeCure = 0;
+ WaitTimeAfterCure = 0;
+ WaitTimeAfterLift = 0;
}
}
+ }
- public override float LightOffDelay
+ public override float LightOffDelay
+ {
+ get => Settings.LightOffDelay;
+ set
{
- get => Settings.LightOffDelay;
- set
+ base.LightOffDelay = Settings.LightOffDelay = (float)Math.Round(value, 2);
+ if (value > 0)
{
- base.LightOffDelay = Settings.LightOffDelay = (float)Math.Round(value, 2);
- if (value > 0)
- {
- WaitTimeBeforeCure = 0;
- WaitTimeAfterCure = 0;
- WaitTimeAfterLift = 0;
- }
+ WaitTimeBeforeCure = 0;
+ WaitTimeAfterCure = 0;
+ WaitTimeAfterLift = 0;
}
}
+ }
- public override float BottomWaitTimeBeforeCure
- {
- get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : FirstLayer?.WaitTimeBeforeCure ?? 0;
- set => base.BottomWaitTimeBeforeCure = value;
- }
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : FirstLayer?.WaitTimeBeforeCure ?? 0;
+ set => base.BottomWaitTimeBeforeCure = value;
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => Settings.RestTimeAfterRetract;
+ set
{
- get => Settings.RestTimeAfterRetract;
- set
+ base.WaitTimeBeforeCure = Settings.RestTimeAfterRetract = Settings.RestTimeAfterRetract2 = (float)Math.Round(value, 2);
+ if (value > 0)
{
- base.WaitTimeBeforeCure = Settings.RestTimeAfterRetract = Settings.RestTimeAfterRetract2 = (float)Math.Round(value, 2);
- if (value > 0)
- {
- BottomLightOffDelay = 0;
- LightOffDelay = 0;
- }
+ BottomLightOffDelay = 0;
+ LightOffDelay = 0;
}
}
+ }
- public override float BottomExposureTime
- {
- get => Settings.BottomExposureTime;
- set => base.BottomExposureTime = Settings.BottomExposureTime = (float)Math.Round(value, 2);
- }
+ public override float BottomExposureTime
+ {
+ get => Settings.BottomExposureTime;
+ set => base.BottomExposureTime = Settings.BottomExposureTime = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeAfterCure
- {
- get => base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : FirstLayer?.WaitTimeAfterCure ?? 0;
- set => base.BottomWaitTimeAfterCure = value;
- }
+ public override float BottomWaitTimeAfterCure
+ {
+ get => base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : FirstLayer?.WaitTimeAfterCure ?? 0;
+ set => base.BottomWaitTimeAfterCure = value;
+ }
- public override float WaitTimeAfterCure
+ public override float WaitTimeAfterCure
+ {
+ get => Settings.RestTimeBeforeLift;
+ set
{
- get => Settings.RestTimeBeforeLift;
- set
+ base.WaitTimeAfterCure = Settings.RestTimeBeforeLift = (float)Math.Round(value, 2);
+ if (value > 0)
{
- base.WaitTimeAfterCure = Settings.RestTimeBeforeLift = (float)Math.Round(value, 2);
- if (value > 0)
- {
- BottomLightOffDelay = 0;
- LightOffDelay = 0;
- }
+ BottomLightOffDelay = 0;
+ LightOffDelay = 0;
}
}
+ }
- public override float ExposureTime
- {
- get => Settings.ExposureTime;
- set => base.ExposureTime = Settings.ExposureTime = (float)Math.Round(value, 2);
- }
+ public override float ExposureTime
+ {
+ get => Settings.ExposureTime;
+ set => base.ExposureTime = Settings.ExposureTime = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftHeight
+ public override float BottomLiftHeight
+ {
+ get => Math.Max(0,Settings.BottomLiftHeight - Settings.BottomLiftHeight2);
+ set
{
- get => Math.Max(0,Settings.BottomLiftHeight - Settings.BottomLiftHeight2);
- set
- {
- value = (float)Math.Round(value, 2);
- Settings.BottomLiftHeight = (float)Math.Round(value + Settings.BottomLiftHeight2, 2);
- base.BottomLiftHeight = value;
- }
+ value = (float)Math.Round(value, 2);
+ Settings.BottomLiftHeight = (float)Math.Round(value + Settings.BottomLiftHeight2, 2);
+ base.BottomLiftHeight = value;
}
+ }
- public override float BottomLiftSpeed
- {
- get => Settings.BottomLiftSpeed;
- set => base.BottomLiftSpeed = Settings.BottomLiftSpeed = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftSpeed
+ {
+ get => Settings.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = Settings.BottomLiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float LiftHeight
+ public override float LiftHeight
+ {
+ get => Math.Max(0,Settings.LiftHeight - Settings.LiftHeight2);
+ set
{
- get => Math.Max(0,Settings.LiftHeight - Settings.LiftHeight2);
- set
- {
- value = (float)Math.Round(value, 2);
- Settings.LiftHeight = (float)Math.Round(value + Settings.LiftHeight2, 2);
- base.LiftHeight = value;
- }
+ value = (float)Math.Round(value, 2);
+ Settings.LiftHeight = (float)Math.Round(value + Settings.LiftHeight2, 2);
+ base.LiftHeight = value;
}
+ }
- public override float LiftSpeed
- {
- get => Settings.LiftSpeed;
- set => base.LiftSpeed = Settings.LiftSpeed = (float)Math.Round(value, 2);
- }
+ public override float LiftSpeed
+ {
+ get => Settings.LiftSpeed;
+ set => base.LiftSpeed = Settings.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftHeight2
+ public override float BottomLiftHeight2
+ {
+ get => Settings.BottomLiftHeight2;
+ set
{
- get => Settings.BottomLiftHeight2;
- set
- {
- var bottomLiftHeight = BottomLiftHeight;
- Settings.BottomLiftHeight2 = (float)Math.Round(value, 2);
- BottomLiftHeight = bottomLiftHeight;
- base.BottomLiftHeight2 = Settings.BottomLiftHeight2;
- }
+ var bottomLiftHeight = BottomLiftHeight;
+ Settings.BottomLiftHeight2 = (float)Math.Round(value, 2);
+ BottomLiftHeight = bottomLiftHeight;
+ base.BottomLiftHeight2 = Settings.BottomLiftHeight2;
}
+ }
- public override float BottomLiftSpeed2
- {
- get => Settings.BottomLiftSpeed2;
- set => base.BottomLiftSpeed2 = Settings.BottomLiftSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftSpeed2
+ {
+ get => Settings.BottomLiftSpeed2;
+ set => base.BottomLiftSpeed2 = Settings.BottomLiftSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float LiftHeight2
+ public override float LiftHeight2
+ {
+ get => Settings.LiftHeight2;
+ set
{
- get => Settings.LiftHeight2;
- set
- {
- var liftHeight = LiftHeight;
- Settings.LiftHeight2 = (float)Math.Round(value, 2);
- LiftHeight = liftHeight;
- base.LiftHeight2 = Settings.LiftHeight2;
- }
+ var liftHeight = LiftHeight;
+ Settings.LiftHeight2 = (float)Math.Round(value, 2);
+ LiftHeight = liftHeight;
+ base.LiftHeight2 = Settings.LiftHeight2;
}
+ }
- public override float LiftSpeed2
- {
- get => Settings.LiftSpeed2;
- set => base.LiftSpeed2 = Settings.LiftSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float LiftSpeed2
+ {
+ get => Settings.LiftSpeed2;
+ set => base.LiftSpeed2 = Settings.LiftSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeAfterLift
- {
- get => base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : FirstLayer?.WaitTimeAfterLift ?? 0;
- set => base.BottomWaitTimeAfterLift = value;
- }
+ public override float BottomWaitTimeAfterLift
+ {
+ get => base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : FirstLayer?.WaitTimeAfterLift ?? 0;
+ set => base.BottomWaitTimeAfterLift = value;
+ }
- public override float WaitTimeAfterLift
+ public override float WaitTimeAfterLift
+ {
+ get => Settings.RestTimeAfterLift;
+ set
{
- get => Settings.RestTimeAfterLift;
- set
+ base.WaitTimeAfterLift = Settings.RestTimeAfterLift = Settings.RestTimeAfterLift2 = Settings.RestTimeAfterLift3 = (float)Math.Round(value, 2);
+ if (value > 0)
{
- base.WaitTimeAfterLift = Settings.RestTimeAfterLift = Settings.RestTimeAfterLift2 = Settings.RestTimeAfterLift3 = (float)Math.Round(value, 2);
- if (value > 0)
- {
- BottomLightOffDelay = 0;
- LightOffDelay = 0;
- }
+ BottomLightOffDelay = 0;
+ LightOffDelay = 0;
}
}
+ }
- public override float BottomRetractSpeed
- {
- get => Settings.BottomRetractSpeed;
- set => base.BottomRetractSpeed = Settings.BottomRetractSpeed = (float)Math.Round(value, 2);
- }
+ public override float BottomRetractSpeed
+ {
+ get => Settings.BottomRetractSpeed;
+ set => base.BottomRetractSpeed = Settings.BottomRetractSpeed = (float)Math.Round(value, 2);
+ }
- public override float RetractSpeed
- {
- get => Settings.RetractSpeed;
- set => base.RetractSpeed = Settings.RetractSpeed = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed
+ {
+ get => Settings.RetractSpeed;
+ set => base.RetractSpeed = Settings.RetractSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractHeight2
+ public override float BottomRetractHeight2
+ {
+ get => Settings.BottomRetractHeight2;
+ set
{
- get => Settings.BottomRetractHeight2;
- set
- {
- value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
- base.BottomRetractHeight2 = Settings.BottomRetractHeight2 = value;
- }
+ value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
+ base.BottomRetractHeight2 = Settings.BottomRetractHeight2 = value;
}
+ }
- public override float BottomRetractSpeed2
- {
- get => Settings.BottomRetractSpeed2;
- set => base.BottomRetractSpeed2 = Settings.BottomRetractSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float BottomRetractSpeed2
+ {
+ get => Settings.BottomRetractSpeed2;
+ set => base.BottomRetractSpeed2 = Settings.BottomRetractSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float RetractHeight2
+ public override float RetractHeight2
+ {
+ get => Settings.RetractHeight2;
+ set
{
- get => Settings.RetractHeight2;
- set
- {
- value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
- base.RetractHeight2 = Settings.RetractHeight2 = value;
- }
+ value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
+ base.RetractHeight2 = Settings.RetractHeight2 = value;
}
+ }
- public override float RetractSpeed2
- {
- get => Settings.RetractSpeed2;
- set => base.RetractSpeed2 = Settings.RetractSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed2
+ {
+ get => Settings.RetractSpeed2;
+ set => base.RetractSpeed2 = Settings.RetractSpeed2 = (float)Math.Round(value, 2);
+ }
- public override byte BottomLightPWM
- {
- get => (byte)Settings.BottomLightPWM;
- set => base.BottomLightPWM = (byte)(Settings.BottomLightPWM = value);
- }
+ public override byte BottomLightPWM
+ {
+ get => (byte)Settings.BottomLightPWM;
+ set => base.BottomLightPWM = (byte)(Settings.BottomLightPWM = value);
+ }
- public override byte LightPWM
- {
- get => (byte)Settings.LightPWM;
- set => base.LightPWM = (byte)(Settings.LightPWM = value);
- }
+ public override byte LightPWM
+ {
+ get => (byte)Settings.LightPWM;
+ set => base.LightPWM = (byte)(Settings.LightPWM = value);
+ }
- public override float PrintTime
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- Settings.PrintTime = (uint)base.PrintTime;
- }
+ base.PrintTime = value;
+ Settings.PrintTime = (uint)base.PrintTime;
}
+ }
- public override string MachineName
- {
- get => Settings.MachineName;
- set => base.MachineName = Settings.MachineName = value;
- }
+ public override string MachineName
+ {
+ get => Settings.MachineName;
+ set => base.MachineName = Settings.MachineName = value;
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- Settings.MaterialMilliliters = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ Settings.MaterialMilliliters = base.MaterialMilliliters;
}
+ }
- public override float MaterialGrams
- {
- get => Settings.MaterialGrams;
- set => base.MaterialGrams = Settings.MaterialGrams = (float)Math.Round(value, 3);
- }
+ public override float MaterialGrams
+ {
+ get => Settings.MaterialGrams;
+ set => base.MaterialGrams = Settings.MaterialGrams = (float)Math.Round(value, 3);
+ }
- public override float MaterialCost
- {
- get => (float)Math.Round(Settings.MaterialCost, 3);
- set => base.MaterialCost = Settings.MaterialCost = (float)Math.Round(value, 3);
- }
+ public override float MaterialCost
+ {
+ get => (float)Math.Round(Settings.MaterialCost, 3);
+ set => base.MaterialCost = Settings.MaterialCost = (float)Math.Round(value, 3);
+ }
- public override object[] Configs
+ public override object[] Configs
+ {
+ get
{
- get
- {
- return new object[] { Settings };
- }
+ return new object[] { Settings };
}
+ }
+
+ #endregion
- #endregion
+ #region Constructors
+ public CTBEncryptedFile()
+ {
+ Previews = new Preview[ThumbnailsCount];
- #region Constructors
- public CTBEncryptedFile()
+ /*if (Bigfoot is not null && Bigfoot[0] == 0 && File.Exists("MAGIC.ectb"))
{
- Previews = new Preview[ThumbnailsCount];
+ using var fs = new FileStream("MAGIC.ectb", FileMode.Open);
+ fs.ReadBytes(Bigfoot);
+ fs.ReadBytes(CookieMonster);
+ }*/
+ }
+ #endregion
- /*if (Bigfoot is not null && Bigfoot[0] == 0 && File.Exists("MAGIC.ectb"))
- {
- using var fs = new FileStream("MAGIC.ectb", FileMode.Open);
- fs.ReadBytes(Bigfoot);
- fs.ReadBytes(CookieMonster);
- }*/
- }
- #endregion
+ #region Methods
- #region Methods
+ public override void Clear()
+ {
+ base.Clear();
- public override void Clear()
+ for (byte i = 0; i < ThumbnailsCount; i++)
{
- base.Clear();
-
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- Previews[i] = new Preview();
- }
+ Previews[i] = new Preview();
}
+ }
- public override bool CanProcess(string fileFullPath)
- {
- if (!base.CanProcess(fileFullPath)) return false;
-
- try
- {
- using var fs = new BinaryReader(new FileStream(fileFullPath, FileMode.Open, FileAccess.Read));
- var magic = fs.ReadUInt32();
- return magic is MAGIC_CBT_ENCRYPTED;
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
+ public override bool CanProcess(string fileFullPath)
+ {
+ if (!base.CanProcess(fileFullPath)) return false;
- return false;
+ try
+ {
+ using var fs = new BinaryReader(new FileStream(fileFullPath, FileMode.Open, FileAccess.Read));
+ var magic = fs.ReadUInt32();
+ return magic is MAGIC_CBT_ENCRYPTED;
}
-
- private void SanitizeProperties()
+ catch (Exception e)
{
- Settings.PerLayerSettings = LayerManager.AllLayersAreUsingGlobalParameters
- ? PERLAYER_SETTINGS_DISALLOW
- : PERLAYER_SETTINGS_ALLOW;
+ Debug.WriteLine(e);
}
+ return false;
+ }
- protected override void DecodeInternally(OperationProgress progress)
- {
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- Header = Helpers.Deserialize<FileHeader>(inputFile);
- Debug.WriteLine($"Header: {Header}");
+ private void SanitizeProperties()
+ {
+ Settings.PerLayerSettings = AllLayersAreUsingGlobalParameters
+ ? PERLAYER_SETTINGS_DISALLOW
+ : PERLAYER_SETTINGS_ALLOW;
+ }
- if (Header.Magic is not MAGIC_CBT_ENCRYPTED)
- {
- throw new FileLoadException($"Not a valid CTB encrypted file! Magic Value: {Header.Magic}", FileFullPath);
- }
- inputFile.Seek(Header.SettingsOffset, SeekOrigin.Begin);
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ Header = Helpers.Deserialize<FileHeader>(inputFile);
+ Debug.WriteLine($"Header: {Header}");
+
+ if (Header.Magic is not MAGIC_CBT_ENCRYPTED)
+ {
+ throw new FileLoadException($"Not a valid CTB encrypted file! Magic Value: {Header.Magic}", FileFullPath);
+ }
+
+ inputFile.Seek(Header.SettingsOffset, SeekOrigin.Begin);
- var encryptedBlock = inputFile.ReadBytes(Header.SettingsSize);
- using (var ms = CryptExtensions.AesCryptMemoryStream(encryptedBlock, Bigfoot, CipherMode.CBC, PaddingMode.None, false, CookieMonster))
- {
- Settings = Helpers.Deserialize<SlicerSettings>(ms);
- }
+ var encryptedBlock = inputFile.ReadBytes(Header.SettingsSize);
+ using (var ms = CryptExtensions.AesCryptMemoryStream(encryptedBlock, Bigfoot, CipherMode.CBC, PaddingMode.None, false, CookieMonster))
+ {
+ Settings = Helpers.Deserialize<SlicerSettings>(ms);
+ }
- Debug.WriteLine($"Settings: {Settings}");
+ Debug.WriteLine($"Settings: {Settings}");
- /* validate hash */
- var checksumBytes = BitExtensions.ToBytesLittleEndian(Settings.ChecksumValue);
- var checksumHash = CryptExtensions.ComputeSHA256Hash(checksumBytes);
- var encryptedHash = CryptExtensions.AesCryptBytes(checksumHash, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
+ /* validate hash */
+ var checksumBytes = BitExtensions.ToBytesLittleEndian(Settings.ChecksumValue);
+ var checksumHash = CryptExtensions.ComputeSHA256Hash(checksumBytes);
+ var encryptedHash = CryptExtensions.AesCryptBytes(checksumHash, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
- inputFile.Seek(-HASH_LENGTH, SeekOrigin.End);
- var hash = inputFile.ReadBytes(HASH_LENGTH);
- if (!hash.SequenceEqual(encryptedHash))
- {
- throw new FileLoadException("The file checksum does not match, malformed file.", FileFullPath);
- }
+ inputFile.Seek(-HASH_LENGTH, SeekOrigin.End);
+ var hash = inputFile.ReadBytes(HASH_LENGTH);
+ if (!hash.SequenceEqual(encryptedHash))
+ {
+ throw new FileLoadException("The file checksum does not match, malformed file.", FileFullPath);
+ }
- progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
+ progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
+ if (Thumbnails is not null)
+ {
for (byte i = 0; i < ThumbnailsCount; i++)
{
uint offsetAddress = i == 0
@@ -1146,451 +1148,444 @@ namespace UVtools.Core.FileFormats
inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
byte[] rawImageData = new byte[Previews[i].ImageLength];
- inputFile.Read(rawImageData, 0, (int)Previews[i].ImageLength);
+ inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength);
Thumbnails[i] = Previews[i].Decode(rawImageData);
progress++;
}
+ }
- /* Read the settings and disclaimer */
- inputFile.Seek(Settings.MachineNameOffset, SeekOrigin.Begin);
- var machineNameBytes = inputFile.ReadBytes(Settings.MachineNameSize);
- Settings.MachineName = Encoding.UTF8.GetString(machineNameBytes);
+ /* Read the settings and disclaimer */
+ inputFile.Seek(Settings.MachineNameOffset, SeekOrigin.Begin);
+ var machineNameBytes = inputFile.ReadBytes(Settings.MachineNameSize);
+ Settings.MachineName = Encoding.UTF8.GetString(machineNameBytes);
- /* TODO: read the disclaimer here? we can really just ignore it though...*/
+ /* TODO: read the disclaimer here? we can really just ignore it though...*/
- /* start gathering up the layers */
- progress.Reset(OperationProgress.StatusGatherLayers, Settings.LayerCount);
- inputFile.Seek(Settings.LayerPointersOffset, SeekOrigin.Begin);
+ /* start gathering up the layers */
+ progress.Reset(OperationProgress.StatusGatherLayers, Settings.LayerCount);
+ inputFile.Seek(Settings.LayerPointersOffset, SeekOrigin.Begin);
- LayersPointer = new LayerPointer[Settings.LayerCount];
- for (uint layerIndex = 0; layerIndex < Settings.LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- LayersPointer[layerIndex] = Helpers.Deserialize<LayerPointer>(inputFile);
- Debug.WriteLine($"pointer[{layerIndex}]: {LayersPointer[layerIndex]}");
- progress++;
- }
+ LayersPointer = new LayerPointer[Settings.LayerCount];
+ for (uint layerIndex = 0; layerIndex < Settings.LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ LayersPointer[layerIndex] = Helpers.Deserialize<LayerPointer>(inputFile);
+ Debug.WriteLine($"pointer[{layerIndex}]: {LayersPointer[layerIndex]}");
+ progress++;
+ }
- progress.Reset(OperationProgress.StatusDecodeLayers, Settings.LayerCount);
- LayerManager.Init(Settings.LayerCount, DecodeType == FileDecodeType.Partial);
- LayersDefinition = new LayerDef[LayerCount];
- var buggyLayers = new ConcurrentBag<uint>();
+ progress.Reset(OperationProgress.StatusDecodeLayers, Settings.LayerCount);
+ Init(Settings.LayerCount, DecodeType == FileDecodeType.Partial);
+ LayersDefinition = new LayerDef[LayerCount];
+ var buggyLayers = new ConcurrentBag<uint>();
- foreach (var batch in BatchLayersIndexes())
+ foreach (var batch in BatchLayersIndexes())
+ {
+ foreach (var layerIndex in batch)
{
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- inputFile.Seek(LayersPointer[layerIndex].LayerOffset, SeekOrigin.Begin);
- LayersDefinition[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
- LayersDefinition[layerIndex].Parent = this;
- if (DecodeType == FileDecodeType.Full) LayersDefinition[layerIndex].RLEData = inputFile.ReadBytes(LayersDefinition[layerIndex].DataLength);
- Debug.WriteLine($"layer[{layerIndex}]: {LayersDefinition[layerIndex]}");
- }
+ inputFile.Seek(LayersPointer[layerIndex].LayerOffset, SeekOrigin.Begin);
+ LayersDefinition[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
+ LayersDefinition[layerIndex].Parent = this;
+ if (DecodeType == FileDecodeType.Full) LayersDefinition[layerIndex].RLEData = inputFile.ReadBytes(LayersDefinition[layerIndex].DataLength);
+ Debug.WriteLine($"layer[{layerIndex}]: {LayersDefinition[layerIndex]}");
+ }
- if (DecodeType == FileDecodeType.Full)
+ if (DecodeType == FileDecodeType.Full)
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
+ if (progress.Token.IsCancellationRequested) return;
- var layerDef = LayersDefinition[layerIndex];
+ var layerDef = LayersDefinition[layerIndex];
- if (layerDef.EncryptedDataLength > 0)
- {
- /* Decrypt RLE data here */
+ if (layerDef.EncryptedDataLength > 0)
+ {
+ /* Decrypt RLE data here */
- var byteBuffer = new byte[layerDef.EncryptedDataLength];
- Array.Copy(layerDef.RLEData, (int)layerDef.EncryptedDataOffset, byteBuffer, 0,
- (int)layerDef.EncryptedDataLength);
+ var byteBuffer = new byte[layerDef.EncryptedDataLength];
+ Array.Copy(layerDef.RLEData!, (int)layerDef.EncryptedDataOffset, byteBuffer, 0,
+ (int)layerDef.EncryptedDataLength);
- byteBuffer = CryptExtensions.AesCryptBytes(byteBuffer, Bigfoot, CipherMode.CBC,
- PaddingMode.None, false, CookieMonster);
- Array.Copy(byteBuffer, 0, layerDef.RLEData, layerDef.EncryptedDataOffset,
- layerDef.EncryptedDataLength);
- }
+ byteBuffer = CryptExtensions.AesCryptBytes(byteBuffer, Bigfoot, CipherMode.CBC,
+ PaddingMode.None, false, CookieMonster);
+ Array.Copy(byteBuffer, 0, layerDef.RLEData!, layerDef.EncryptedDataOffset,
+ layerDef.EncryptedDataLength);
+ }
- bool isBugged = false;
- /* bug fix for Chitubox when a small layer RLE data is encrypted */
- if (layerDef.EncryptedDataLength > 0 && layerDef.RLEData.Length < RLEEncryptedMinimumLength &&
- layerDef.RLEData.Length % 16 != 0)
- {
- buggyLayers.Add((uint)layerIndex);
- isBugged = true;
- layerDef.RLEData = null; /* clean up RLE data */
- }
+ bool isBugged = false;
+ /* bug fix for Chitubox when a small layer RLE data is encrypted */
+ if (layerDef.EncryptedDataLength > 0 && layerDef.RLEData!.Length < RLEEncryptedMinimumLength &&
+ layerDef.RLEData.Length % 16 != 0)
+ {
+ buggyLayers.Add((uint)layerIndex);
+ isBugged = true;
+ layerDef.RLEData = null; /* clean up RLE data */
+ }
- this[layerIndex] = new Layer((uint)layerIndex, isBugged ? null : layerDef.DecodeImage((uint)layerIndex), this);
+ if (isBugged)
+ {
+ this[layerIndex] = new Layer((uint)layerIndex, this);
+ }
+ else
+ {
+ this[layerIndex] = new Layer((uint) layerIndex, layerDef.DecodeImage((uint) layerIndex), this);
+ }
- progress.LockAndIncrement();
- });
- }
+ progress.LockAndIncrement();
+ });
}
+ }
- if (buggyLayers.Count == LayerCount)
- {
- throw new FileLoadException("Unable to load this file due to Chitubox bug affecting every layer." +
- "Please increase the portion of the plate in use and reslice the file.");
- }
+ if (buggyLayers.Count == LayerCount)
+ {
+ throw new FileLoadException("Unable to load this file due to Chitubox bug affecting every layer." +
+ "Please increase the portion of the plate in use and reslice the file.");
+ }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- LayersDefinition[layerIndex].CopyTo(this[layerIndex]);
- }
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ LayersDefinition[layerIndex].CopyTo(this[layerIndex]);
+ }
- if (!buggyLayers.IsEmpty)
+ if (!buggyLayers.IsEmpty)
+ {
+ var sortedLayerIndexes = buggyLayers.ToArray();
+ Array.Sort(sortedLayerIndexes);
+ uint correctedLayerCount = 0;
+
+ foreach (var layerIndex in sortedLayerIndexes)
{
- var sortedLayerIndexes = buggyLayers.ToArray();
- Array.Sort(sortedLayerIndexes);
- uint correctedLayerCount = 0;
+ int direction = layerIndex == 0 ? 1 : -1;
- foreach (var layerIndex in sortedLayerIndexes)
+ /* clone from the next one that has a mat */
+ for (int layerIndexForClone = (int)(layerIndex + direction);
+ layerIndexForClone >= 0 && layerIndexForClone < LayerCount;
+ layerIndexForClone += direction)
{
- int direction = layerIndex == 0 ? 1 : -1;
-
- /* clone from the next one that has a mat */
- for (int layerIndexForClone = (int)(layerIndex + direction);
- layerIndexForClone >= 0 && layerIndexForClone < LayerCount;
- layerIndexForClone += direction)
- {
- if (!this[layerIndexForClone].HaveImage) continue;
- this[layerIndexForClone].CopyImageTo(this[layerIndex]);
- correctedLayerCount++;
+ if (!this[layerIndexForClone].HaveImage) continue;
+ this[layerIndexForClone].CopyImageTo(this[layerIndex]);
+ correctedLayerCount++;
- /* TODO: Report to the user that a layer was cloned to work around chitubox crypto bug */
+ /* TODO: Report to the user that a layer was cloned to work around chitubox crypto bug */
- break;
- }
+ break;
}
+ }
- if (correctedLayerCount < buggyLayers.Count)
- {
- throw new FileLoadException(
- "Unable to load this file due to an Chitubox bug and the impossibility to auto correct some of these layers.\n" +
- "Please increase the portion of the plate in use and re-slice the file.");
- }
+ if (correctedLayerCount < buggyLayers.Count)
+ {
+ throw new FileLoadException(
+ "Unable to load this file due to an Chitubox bug and the impossibility to auto correct some of these layers.\n" +
+ "Please increase the portion of the plate in use and re-slice the file.");
}
- //inputFile.ReadBytes(HashLength);
}
+ //inputFile.ReadBytes(HashLength);
+ }
- protected override void EncodeInternally(OperationProgress progress)
- {
-#if !DEBUG
- if (Settings.LayerPointersOffset > 0) throw new NotSupportedException(Preamble);
-#endif
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ //uint currentOffset = 0;
+ /* Create the file header and fill out what we can. SignatureOffset will have to be populated later
+ * this will be the last thing written to file */
+ Header.SettingsSize = (uint)Helpers.Serializer.SizeOf(Settings);
+ Header.SettingsOffset = (uint)Helpers.Serializer.SizeOf(Header);
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- //uint currentOffset = 0;
- /* Create the file header and fill out what we can. SignatureOffset will have to be populated later
- * this will be the last thing written to file */
- Header.SettingsSize = (uint)Helpers.Serializer.SizeOf(Settings);
- Header.SettingsOffset = (uint)Helpers.Serializer.SizeOf(Header);
+ SanitizeProperties();
- SanitizeProperties();
+ if (Settings.LayerXorKey == 0)
+ {
+ Settings.LayerXorKey = LAYER_XOR_KEY;
+ }
- if (Settings.LayerXorKey == 0)
- {
- Settings.LayerXorKey = LAYER_XOR_KEY;
- }
+ outputFile.Seek(Header.SettingsOffset + Header.SettingsSize, SeekOrigin.Begin);
- outputFile.Seek(Header.SettingsOffset + Header.SettingsSize, SeekOrigin.Begin);
+ progress.Reset(OperationProgress.StatusEncodePreviews, 2);
- progress.Reset(OperationProgress.StatusEncodePreviews, 2);
+ Mat?[] thumbnails = { GetThumbnail(true), GetThumbnail(false) };
+ for (byte i = 0; i < thumbnails.Length; i++)
+ {
+ var image = thumbnails[i];
+ if (image is null) continue;
- Mat[] thumbnails = { GetThumbnail(true), GetThumbnail(false) };
- for (byte i = 0; i < thumbnails.Length; i++)
+ Preview preview = new()
{
- var image = thumbnails[i];
- if (image is null) continue;
-
- Preview preview = new()
- {
- ResolutionX = (uint)image.Width,
- ResolutionY = (uint)image.Height,
- };
-
- var previewBytes = preview.Encode(image);
+ ResolutionX = (uint)image.Width,
+ ResolutionY = (uint)image.Height,
+ };
-#if !DEBUG
- if (Settings.LayerPointersOffset > 0) { Bigfoot = new byte[32]; CookieMonster = new byte[16]; }
-#endif
+ var previewBytes = preview.Encode(image);
- if (previewBytes.Length == 0) continue;
+ if (previewBytes.Length == 0) continue;
- if (i == 0)
- {
- Settings.LargePreviewOffset = (uint)outputFile.Position;
- }
- else
- {
- Settings.SmallPreviewOffset = (uint)outputFile.Position;
- }
+ if (i == 0)
+ {
+ Settings.LargePreviewOffset = (uint)outputFile.Position;
+ }
+ else
+ {
+ Settings.SmallPreviewOffset = (uint)outputFile.Position;
+ }
- preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
+ preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
- Helpers.SerializeWriteFileStream(outputFile, preview);
- outputFile.WriteBytes(previewBytes);
- progress++;
- }
+ Helpers.SerializeWriteFileStream(outputFile, preview);
+ outputFile.WriteBytes(previewBytes);
+ progress++;
+ }
- Settings.MachineNameOffset = (uint)outputFile.Position;
- Settings.MachineNameSize = (uint)Settings.MachineName.Length;
- var machineNameBytes = Encoding.UTF8.GetBytes(Settings.MachineName);
+ Settings.MachineNameOffset = (uint)outputFile.Position;
+ Settings.MachineNameSize = (uint)Settings.MachineName.Length;
+ var machineNameBytes = Encoding.UTF8.GetBytes(Settings.MachineName);
- outputFile.WriteBytes(machineNameBytes);
+ outputFile.WriteBytes(machineNameBytes);
- Settings.DisclaimerOffset = (uint)outputFile.Position;
- Settings.DisclaimerSize = CTB_DISCLAIMER_SIZE;
- outputFile.WriteBytes(Encoding.UTF8.GetBytes(CTB_DISCLAIMER));
+ Settings.DisclaimerOffset = (uint)outputFile.Position;
+ Settings.DisclaimerSize = CTB_DISCLAIMER_SIZE;
+ outputFile.WriteBytes(Encoding.UTF8.GetBytes(CTB_DISCLAIMER));
- Settings.LayerPointersOffset = (uint)outputFile.Position;
+ Settings.LayerPointersOffset = (uint)outputFile.Position;
- /* we'll write this after we write out all of the layers ... */
- LayersPointer = new LayerPointer[LayerCount];
- LayersDefinition = new LayerDef[LayerCount];
+ /* we'll write this after we write out all of the layers ... */
+ LayersPointer = new LayerPointer[LayerCount];
+ LayersDefinition = new LayerDef[LayerCount];
- uint layerTableSize = (uint)Helpers.Serializer.SizeOf(new LayerPointer()) * LayerCount;
+ uint layerTableSize = (uint)Helpers.Serializer.SizeOf(new LayerPointer()) * LayerCount;
- outputFile.Seek(outputFile.Position + layerTableSize, SeekOrigin.Begin);
+ outputFile.Seek(outputFile.Position + layerTableSize, SeekOrigin.Begin);
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layerDef = new LayerDef(this, this[layerIndex]);
+ using (var mat = this[layerIndex].LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- var layerDef = new LayerDef(this, this[layerIndex]);
- using (var mat = this[layerIndex].LayerMat)
- {
- layerDef.EncodeImage(mat, (uint)layerIndex);
- LayersDefinition[layerIndex] = layerDef;
- }
-
- progress.LockAndIncrement();
- });
+ layerDef.EncodeImage(mat!, (uint)layerIndex);
+ LayersDefinition[layerIndex] = layerDef;
+ }
- /*
-#if !DEBUG
- Bigfoot = new byte[32]; CookieMonster = new byte[16];
-#endif
- */
+ progress.LockAndIncrement();
+ });
- progress.Reset(OperationProgress.StatusWritingFile, LayerCount);
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- var layerDef = LayersDefinition[layerIndex];
- LayersPointer[layerIndex] = new LayerPointer((uint)outputFile.Position);
+ progress.Reset(OperationProgress.StatusWritingFile, LayerCount);
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ var layerDef = LayersDefinition[layerIndex];
+ LayersPointer[layerIndex] = new LayerPointer((uint)outputFile.Position);
- layerDef.LayerDefOffset = LayersPointer[layerIndex].LayerOffset + LayerDef.TABLE_SIZE;
- Helpers.SerializeWriteFileStream(outputFile, layerDef);
- outputFile.WriteBytes(layerDef.RLEData);
- progress++;
- }
+ layerDef.LayerDefOffset = LayersPointer[layerIndex].LayerOffset + LayerDef.TABLE_SIZE;
+ Helpers.SerializeWriteFileStream(outputFile, layerDef);
+ outputFile.WriteBytes(layerDef.RLEData!);
+ progress++;
+ }
- /* write the final hash */
- var hash = CryptExtensions.ComputeSHA256Hash(BitExtensions.ToBytesLittleEndian(Settings.ChecksumValue));
- var encryptedHash = CryptExtensions.AesCryptBytes(hash, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
- Header.SignatureOffset = (uint)outputFile.Position;
- Header.SignatureSize = (uint)encryptedHash.Length;
- outputFile.WriteBytes(encryptedHash);
-
- // Rewind
-
- // Layer pointers
- outputFile.Seek(Settings.LayerPointersOffset, SeekOrigin.Begin);
- for (uint layerIndex = 0; layerIndex < LayersPointer.Length; layerIndex++)
- {
- Helpers.SerializeWriteFileStream(outputFile, LayersPointer[layerIndex]);
- }
+ /* write the final hash */
+ var hash = CryptExtensions.ComputeSHA256Hash(BitExtensions.ToBytesLittleEndian(Settings.ChecksumValue));
+ var encryptedHash = CryptExtensions.AesCryptBytes(hash, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
+ Header.SignatureOffset = (uint)outputFile.Position;
+ Header.SignatureSize = (uint)encryptedHash.Length;
+ outputFile.WriteBytes(encryptedHash);
+
+ // Rewind
+
+ // Layer pointers
+ outputFile.Seek(Settings.LayerPointersOffset, SeekOrigin.Begin);
+ for (uint layerIndex = 0; layerIndex < LayersPointer.Length; layerIndex++)
+ {
+ Helpers.SerializeWriteFileStream(outputFile, LayersPointer[layerIndex]);
+ }
- // Settings
- outputFile.Seek(Header.SettingsOffset, SeekOrigin.Begin);
- var settingsBytes = Helpers.Serialize(Settings).ToArray();
- var encryptedSettings = CryptExtensions.AesCryptBytes(settingsBytes, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
- outputFile.WriteBytes(encryptedSettings);
+ // Settings
+ outputFile.Seek(Header.SettingsOffset, SeekOrigin.Begin);
+ var settingsBytes = Helpers.Serialize(Settings).ToArray();
+ var encryptedSettings = CryptExtensions.AesCryptBytes(settingsBytes, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
+ outputFile.WriteBytes(encryptedSettings);
- // Header
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, Header);
- }
+ // Header
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, Header);
+ }
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- SanitizeProperties();
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ SanitizeProperties();
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
- outputFile.Seek(Header.SettingsOffset, SeekOrigin.Begin);
- var settingsBytes = Helpers.Serialize(Settings).ToArray();
- var encryptedSettings = CryptExtensions.AesCryptBytes(settingsBytes, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
- outputFile.WriteBytes(encryptedSettings);
+ outputFile.Seek(Header.SettingsOffset, SeekOrigin.Begin);
+ var settingsBytes = Helpers.Serialize(Settings).ToArray();
+ var encryptedSettings = CryptExtensions.AesCryptBytes(settingsBytes, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster);
+ outputFile.WriteBytes(encryptedSettings);
- for (uint layerIndex = 0; layerIndex < LayersPointer.Length; layerIndex++)
- {
- LayersDefinition[layerIndex].SetFrom(this[layerIndex]);
- outputFile.Seek(LayersPointer[layerIndex].LayerOffset, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, LayersDefinition[layerIndex]);
- }
+ for (uint layerIndex = 0; layerIndex < LayersPointer!.Length; layerIndex++)
+ {
+ LayersDefinition![layerIndex].SetFrom(this[layerIndex]);
+ outputFile.Seek(LayersPointer[layerIndex].LayerOffset, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, LayersDefinition[layerIndex]);
}
- #endregion
+ }
+ #endregion
- #region Static Methods
- public static void CryptFile(string filePath)
+ #region Static Methods
+ public static void CryptFile(string filePath)
+ {
+ using var msReader = new MemoryStream(File.ReadAllBytes(filePath));
+ using var msWriter = new MemoryStream();
+ msReader.CopyTo(msWriter);
+ msWriter.Position = 0;
+ msReader.Position = 0;
+ var writer = new BinaryWriter(msWriter);
+ var reader = new BinaryReader(msReader);
+
+ /* magic */
+ var magic = reader.ReadUInt32();
+ if (magic != MAGIC_CBT_ENCRYPTED)
{
- using var msReader = new MemoryStream(File.ReadAllBytes(filePath));
- using var msWriter = new MemoryStream();
- msReader.CopyTo(msWriter);
- msWriter.Position = 0;
- msReader.Position = 0;
- var writer = new BinaryWriter(msWriter);
- var reader = new BinaryReader(msReader);
-
- /* magic */
- var magic = reader.ReadUInt32();
- if (magic != MAGIC_CBT_ENCRYPTED)
- {
- Console.Write("File does not appear to be an encrypted CTB. Aborting.");
- return;
- }
+ Console.Write("File does not appear to be an encrypted CTB. Aborting.");
+ return;
+ }
- writer.Write(magic);
- var headerLength = reader.ReadUInt32();
- writer.Write(headerLength);
- var headerOffset = reader.ReadUInt32();
- writer.Write(headerOffset);
+ writer.Write(magic);
+ var headerLength = reader.ReadUInt32();
+ writer.Write(headerLength);
+ var headerOffset = reader.ReadUInt32();
+ writer.Write(headerOffset);
- var currentPos = msReader.Position;
- msReader.Seek(headerOffset + 24, SeekOrigin.Begin); // Paddings of 0
+ var currentPos = msReader.Position;
+ msReader.Seek(headerOffset + 24, SeekOrigin.Begin); // Paddings of 0
- var encrypt = reader.ReadUInt32() == 0 && reader.ReadUInt32() == 0; // Both paddings must be zero so we know we need to encrypt!
- msReader.Seek(currentPos, SeekOrigin.Begin);
+ var encrypt = reader.ReadUInt32() == 0 && reader.ReadUInt32() == 0; // Both paddings must be zero so we know we need to encrypt!
+ msReader.Seek(currentPos, SeekOrigin.Begin);
- Console.Write($"Crypt mode: {(encrypt ? "Encrypting" : "Decrypting")}");
+ Console.Write($"Crypt mode: {(encrypt ? "Encrypting" : "Decrypting")}");
- /* pass through rest of data until encrypted header */
- var bytesToPassthru = headerOffset - msReader.Position;
- var temp = reader.ReadBytes((int)bytesToPassthru);
- writer.Write(temp);
- uint printerNameLength = 0;
+ /* pass through rest of data until encrypted header */
+ var bytesToPassthru = headerOffset - msReader.Position;
+ var temp = reader.ReadBytes((int)bytesToPassthru);
+ writer.Write(temp);
+ uint printerNameLength = 0;
- var originalHeader = reader.ReadBytes((int)headerLength);
+ var originalHeader = reader.ReadBytes((int)headerLength);
- if (encrypt)
- {
- printerNameLength = BitExtensions.ToUIntLittleEndian(originalHeader, 164);
- }
+ if (encrypt)
+ {
+ printerNameLength = BitExtensions.ToUIntLittleEndian(originalHeader, 164);
+ }
- /* decrypt header with recovered keys */
- var cryptedData = CryptExtensions.AesCryptBytes(originalHeader, Bigfoot, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster);
- writer.Write(cryptedData);
+ /* decrypt header with recovered keys */
+ var cryptedData = CryptExtensions.AesCryptBytes(originalHeader, Bigfoot, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster);
+ writer.Write(cryptedData);
- /* get machine name length */
- if (!encrypt)
- {
- printerNameLength = BitExtensions.ToUIntLittleEndian(cryptedData, 164);
- }
+ /* get machine name length */
+ if (!encrypt)
+ {
+ printerNameLength = BitExtensions.ToUIntLittleEndian(cryptedData, 164);
+ }
- /* pass through the next 2 dwords */
- writer.Write(reader.ReadUInt32());
- writer.Write(reader.ReadUInt32());
+ /* pass through the next 2 dwords */
+ writer.Write(reader.ReadUInt32());
+ writer.Write(reader.ReadUInt32());
- /* get offset and length of next section (not encrypted) */
- var nextOffset = reader.ReadUInt32();
- var nextLength = reader.ReadUInt32();
+ /* get offset and length of next section (not encrypted) */
+ var nextOffset = reader.ReadUInt32();
+ var nextLength = reader.ReadUInt32();
- writer.Write(nextOffset);
- writer.Write(nextLength);
+ writer.Write(nextOffset);
+ writer.Write(nextLength);
- /* how many bytes from current position till the next offset */
- bytesToPassthru = nextOffset - msReader.Position;
- writer.Write(reader.ReadBytes((int)bytesToPassthru));
+ /* how many bytes from current position till the next offset */
+ bytesToPassthru = nextOffset - msReader.Position;
+ writer.Write(reader.ReadBytes((int)bytesToPassthru));
- /* passthrough this whole block */
- writer.Write(reader.ReadBytes((int)nextLength));
+ /* passthrough this whole block */
+ writer.Write(reader.ReadBytes((int)nextLength));
- /* pass throught the next 2 dwords */
- writer.Write(reader.ReadUInt32());
- writer.Write(reader.ReadUInt32());
+ /* pass throught the next 2 dwords */
+ writer.Write(reader.ReadUInt32());
+ writer.Write(reader.ReadUInt32());
- /* get offset and length of next section (not encrypted) */
- nextOffset = reader.ReadUInt32();
- nextLength = reader.ReadUInt32();
+ /* get offset and length of next section (not encrypted) */
+ nextOffset = reader.ReadUInt32();
+ nextLength = reader.ReadUInt32();
- writer.Write(nextOffset);
- writer.Write(nextLength);
+ writer.Write(nextOffset);
+ writer.Write(nextLength);
- /* how many bytes from current position till the next offset */
- bytesToPassthru = nextOffset - msReader.Position;
- writer.Write(reader.ReadBytes((int)bytesToPassthru));
+ /* how many bytes from current position till the next offset */
+ bytesToPassthru = nextOffset - msReader.Position;
+ writer.Write(reader.ReadBytes((int)bytesToPassthru));
- /* passthrough this whole block */
- writer.Write(reader.ReadBytes((int)nextLength));
+ /* passthrough this whole block */
+ writer.Write(reader.ReadBytes((int)nextLength));
- /* passes printer name and disclaimer */
- var x = reader.ReadBytes((int)(CTB_DISCLAIMER_SIZE + printerNameLength));
- writer.Write(x);
+ /* passes printer name and disclaimer */
+ var x = reader.ReadBytes((int)(CTB_DISCLAIMER_SIZE + printerNameLength));
+ writer.Write(x);
- /* we're at the layer offset table now */
- var layerOffsets = new List<ulong>();
- var startOfTable = msReader.Position;
- ulong layerOffset = reader.ReadUInt64();
- ulong firstLayer = layerOffset;
- while (msReader.Position < (int)firstLayer)
- {
- layerOffsets.Add(layerOffset);
- reader.ReadUInt64();
- layerOffset = reader.ReadUInt64();
- }
+ /* we're at the layer offset table now */
+ var layerOffsets = new List<ulong>();
+ var startOfTable = msReader.Position;
+ ulong layerOffset = reader.ReadUInt64();
+ ulong firstLayer = layerOffset;
+ while (msReader.Position < (int)firstLayer)
+ {
+ layerOffsets.Add(layerOffset);
+ reader.ReadUInt64();
+ layerOffset = reader.ReadUInt64();
+ }
- msReader.Position = (int)startOfTable;
+ msReader.Position = (int)startOfTable;
- /* copy the rest of the file to the output memory stream, we'll decrypt layers next */
- writer.Write(reader.ReadBytes((int)(msReader.Length - msReader.Position)));
+ /* copy the rest of the file to the output memory stream, we'll decrypt layers next */
+ writer.Write(reader.ReadBytes((int)(msReader.Length - msReader.Position)));
- byte[] cryptedFile = msWriter.ToArray();
- var layerCounter = 0;
- foreach (var offset in layerOffsets)
- {
- layerCounter++;
- msReader.Position = (int)(offset + 0x10);
+ byte[] cryptedFile = msWriter.ToArray();
+ var layerCounter = 0;
+ foreach (var offset in layerOffsets)
+ {
+ layerCounter++;
+ msReader.Position = (int)(offset + 0x10);
- msReader.Position += 0x10;
- var encryptedOffset = reader.ReadUInt32();
- var encryptedLength = reader.ReadUInt32();
- msReader.Position -= 0x18;
+ msReader.Position += 0x10;
+ var encryptedOffset = reader.ReadUInt32();
+ var encryptedLength = reader.ReadUInt32();
+ msReader.Position -= 0x18;
- if (encryptedLength > 0)
- {
- Debug.WriteLine($"Layer {layerCounter} has {encryptedLength} bytes of encrypted data. (Layer data offset: {encryptedOffset})");
- var layerDataOffset = reader.ReadUInt32();
+ if (encryptedLength > 0)
+ {
+ Debug.WriteLine($"Layer {layerCounter} has {encryptedLength} bytes of encrypted data. (Layer data offset: {encryptedOffset})");
+ var layerDataOffset = reader.ReadUInt32();
- msReader.Position = layerDataOffset + encryptedOffset;
+ msReader.Position = layerDataOffset + encryptedOffset;
- var encryptedLayerData = reader.ReadBytes((int)encryptedLength);
- var decryptedLayerData = CryptExtensions.AesCryptBytes(encryptedLayerData, Bigfoot, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster);
+ var encryptedLayerData = reader.ReadBytes((int)encryptedLength);
+ var decryptedLayerData = CryptExtensions.AesCryptBytes(encryptedLayerData, Bigfoot, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster);
- Array.Copy(decryptedLayerData, 0, cryptedFile, layerDataOffset + encryptedOffset, encryptedLength);
+ Array.Copy(decryptedLayerData, 0, cryptedFile, layerDataOffset + encryptedOffset, encryptedLength);
- /* update encrypted markers in the layer header */
- cryptedFile.AsSpan((int)(layerDataOffset - 0x38), 8).Fill(0);
- }
- else
- {
- Debug.WriteLine($"Layer {layerCounter} is not encrypted");
- }
+ /* update encrypted markers in the layer header */
+ cryptedFile.AsSpan((int)(layerDataOffset - 0x38), 8).Fill(0);
}
+ else
+ {
+ Debug.WriteLine($"Layer {layerCounter} is not encrypted");
+ }
+ }
- /* last 20 bytes are an encrypted sha256 */
- var cipheredHash = cryptedFile[^0x20..];
+ /* last 20 bytes are an encrypted sha256 */
+ var cipheredHash = cryptedFile[^0x20..];
- var plainHash = CryptExtensions.AesCryptBytes(cipheredHash, Bigfoot, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster);
- Array.Copy(plainHash, 0, cryptedFile, cryptedFile.Length - 0x20, 0x20);
+ var plainHash = CryptExtensions.AesCryptBytes(cipheredHash, Bigfoot, CipherMode.CBC, PaddingMode.None, encrypt, CookieMonster);
+ Array.Copy(plainHash, 0, cryptedFile, cryptedFile.Length - 0x20, 0x20);
- File.WriteAllBytes(filePath, cryptedFile);
- }
- #endregion
+ File.WriteAllBytes(filePath, cryptedFile);
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs
index efefca6..3bb3d99 100644
--- a/UVtools.Core/FileFormats/CWSFile.cs
+++ b/UVtools.Core/FileFormats/CWSFile.cs
@@ -6,9 +6,12 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
@@ -16,653 +19,647 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.GCode;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
-{
- #region Wanhao
-
- [Serializable]
- [XmlRoot(ElementName = "manifest")]
- public sealed class CWSManifest
- {
- public const string FileName = "manifest.xml";
-
- [XmlAttribute]
- public byte FileVersion { get; set; } = 1;
-
- public sealed class Model
- {
- [XmlElement("name")]
- public string Name { get; set; }
+namespace UVtools.Core.FileFormats;
- [XmlElement("tag")]
- public string Tag { get; set; }
- }
+#region Wanhao
- public sealed class Slice
- {
- [XmlElement("name")]
- public string Name { get; set; }
-
- public Slice()
- {
- }
-
- public Slice(string name)
- {
- Name = name;
- }
- }
+[Serializable]
+[XmlRoot(ElementName = "manifest")]
+public sealed class CWSManifest
+{
+ public const string FileName = "manifest.xml";
- [XmlArrayItem("model")]
- public Model[] Models { get; set; }
- public Slice[] Slices { get; set; }
+ [XmlAttribute]
+ public byte FileVersion { get; set; } = 1;
- public Slice SliceProfile { get; set; } = new Slice();
- public Slice GCode { get; set; } = new Slice();
- }
-
- [Serializable]
- [XmlRoot(ElementName = "SliceBuildConfig")]
- public sealed class CWSSliceBuildConfig
+ public sealed class Model
{
- public const string GCODESTART =
- ";********** Header Start ********\r\n" +
- "G21 ; Set units to be mm\r\n" +
- "G91 ; Relative Positioning\r\n" +
- "M17 ; Enable motors\r\n" +
- "; G28 Z ; Auto-home on print - commented by default";
-
- public const string GCODEEND =
- ";********** Footer ********\r\n" +
- "@cmdCloseShutter ; UV off\r\n" +
- "G4 P0 ; wait for last lift to complete\r\n" +
- "G1 Z40.0 F150.0 ; lift model clear of resin\r\n" +
- "G4 P0 ; sync\r\n" +
- "M18 ; Disable Motors\r\n" +
- ";&lt;Completed&gt;";
-
- public const string GCODELIFT =
- ";********** Lift Sequence %d$CURSLICE ********\r\n" +
- "G1 Z($ZLiftDist * $ZDir) F{$CURSLICE &lt; $NumFirstLayers?$ZBottomLiftRate:$ZLiftRate}\r\n;" +
- "&lt;Takes&gt; {$CURSLICE&lt;$NumFirstLayers?%d($ZLiftDist*1000*60/$ZBottomLiftRate):%d($ZLiftDist*1000*60/$ZLiftRate)}\r\n" +
- "G4 P0 ; Wait for lift rise to complete\r\n;" +
- "&lt;Delay&gt; %d$TopTime\r\n" +
- "G1 Z(($LayerThickness-$ZLiftDist) * $ZDir) F$ZRetractRate\r\n" +
- ";&lt;Takes&gt; %d(($ZLiftDist*1000-$LayerThickness*1000)*60/$ZRetractRate)";
-
- public const string GCODELAYER =
- ";********** Layer %d$CURSLICE ********\r\n" +
- ";&lt;Slice&gt; %d$CURSLICE\r\n" +
- "@cmdOpenShutter ; UV on\r\n" +
- ";{$CURSLICE&lt;$NumFirstLayers?&lt;Delay&gt; %d$FirstLayerTime:&lt;Delay&gt; %d$LayerTime}\r\n" +
- "@cmdCloseShutter ; UV off\r\n" +
- ";&lt;Slice&gt; Blank\r\n" +
- ";&lt;Delay&gt; %d$BlankTime";
-
- public sealed class InkConfig
- {
- public string Name { get; set; }
- public float SliceHeight { get; set; }
- public uint LayerTime { get; set; }
- public uint FirstLayerTime { get; set; }
- public ushort NumberofBottomLayers { get; set; }
- public float ResinPriceL { get; set; }
- }
+ [XmlElement("name")]
+ public string? Name { get; set; }
- [XmlAttribute]
- public byte FileVersion { get; set; } = 2;
-
- public float DotsPermmX { get; set; } = 21.164f;
- public float DotsPermmY { get; set; } = 21.164f;
-
- public ushort XResolution { get; set; }
- public ushort YResolution { get; set; }
- public uint BlankTime { get; set; }
- public uint TopTime { get; set; }
- public uint PlatformTemp { get; set; } = 75;
- public byte ExportSVG { get; set; }
- public string Export { get; set; } = "False";
- public string ExportPNG { get; set; } = "False";
- public string CalcSliceSize { get; set; } = "False";
- public uint XOffset { get; set; }
- public uint YOffset { get; set; }
- public string Direction { get; set; } = "Bottom_Up";
- public float LiftDistance { get; set; } = 6;
- public float SlideTiltValue { get; set; }
- public uint SettleTime { get; set; } = 1600;
- public string AntiAliasing { get; set; } = "True";
- public string UseMainLiftGCode { get; set; } = "False";
- public uint AntiAliasingValue { get; set; } = 4;
- public float LiftFeedRate { get; set; } = 40;
- public float BottomLiftFeedRate { get; set; } = 25;
- public float LiftRetractRate { get; set; } = 80;
- public string ExportOption { get; set; } = "ZIP";
- public string RenderOutlines { get; set; } = "False";
- public uint OutlineWidth_Inset { get; set; } = 2;
- public uint OutlineWidth_Outset { get; set; }
- public string FlipX { get; set; } = "False";
- public string FlipY { get; set; } = "False";
-
- public string Notes { get; set; } =
- "Use these as a place to start; tune them for your specific printer and resins.\r\nAlways run a calibration on your resin to dial in for your machine.\r\nProfiles created by Earl Miller for CW v1.0.0.75.";
-
- public string GCodeHeader { get; set; } = GCODESTART;
- public string GCodeFooter { get; set; } = GCODEEND;
- public string GCodeLift { get; set; } = GCODELIFT;
- public string GCodeLayer { get; set; } = GCODELAYER;
-
- [XmlElement("InkConfig")]
- public List<InkConfig> InkConfigs { get; set; }
-
- public string SelectedInk { get; set; }
- public uint MinTestExposure { get; set; } = 5000;
- public uint TestExposureStep { get; set; } = 100;
- public string ExportPreview { get; set; } = "None";
- public string UseMask { get; set; } = "False";
- public string MaskFilename { get; set; } = "False";
+ [XmlElement("tag")]
+ public string? Tag { get; set; }
}
- #endregion
-
- public class CWSFile : FileFormat
+ public sealed class Slice
{
- #region Constants
-
- public const string GCodeStart = "G28 ;Auto Home{0}" +
- "G21 ;Set units to be mm{0}" +
- "G91 ;Relative Positioning{0}" +
- "M17 ;Enable motors{0}" +
- "<Slice> Blank{0}" +
- "M106 S0{0}{0}";
-
- public const string GCodeEnd = "M106 S0 ;UV off{0}" +
- "G1 Z{1}{0}" +
- "{0}M18 ;Disable Motors{0}" +
- ";<Completed>{0}";
-
- public const string GCodeKeywordSlice = ";<Slice>";
- public const string GCodeKeywordSliceBlank = ";<Slice> Blank";
- public const string GCodeKeywordDelay = ";<Delay>";
- public const string GCodeKeywordTakes = ";<Takes>";
-
- public const string WanhaoStartGCode =
- ";********** Header Start ********{0}" +
- "G21 ; Set units to be mm{0}" +
- "G91 ; Relative Positioning{0}" +
- "M17 ; Enable motors{0}" +
- "; G28 Z ; Auto-home on print - commented by default{0}";
-
- public const string WanhaoPreSliceGCode =
- ";********** Pre-Slice {1} ********{0}" +
- "G4 P0; Make sure any previous relative moves are complete{0}" +
- ";<Delay> {2}{0}" +
- ";********** Layer {1} ********{0}";
-
- public const string WanhaoGCodeEnd =
- ";********** Footer ********{0}" +
- "M106 S0 ; UV off{0}" +
- "G4 P0 ; wait for last lift to complete{0}" +
- "G1 Z40.0 F150.0 ; lift model clear of resin{0}" +
- "G4 P0 ; sync{0}" +
- "M18 ; Disable Motors{0}" +
- ";<Completed>{0}";
-
- #endregion
-
- #region Sub Classes
-
- public class Output
+ [XmlElement("name")]
+ public string? Name { get; set; }
+
+ public Slice()
{
- // ;(****Build and Slicing Parameters****)
- [DisplayName("Pix per mm X")] public float PixPermmX { get; set; } = 19.324f;
- [DisplayName("Pix per mm Y")] public float PixPermmY { get; set; } = 19.324f;
- [DisplayName("X Resolution")] public ushort XResolution { get; set; }
- [DisplayName("Y Resolution")] public ushort YResolution { get; set; }
- [DisplayName("Layer Thickness")] public float LayerThickness { get; set; }
- [DisplayName("Layer Time")] public uint LayerTime { get; set; } = 5500;
- [DisplayName("Render Outlines")] public bool RenderOutlines { get; set; } = false;
- [DisplayName("Outline Width Inset")] public ushort OutlineWidthInset { get; set; } = 2;
- [DisplayName("Outline Width Outset")] public ushort OutlineWidthOutset { get; set; } = 0;
- [DisplayName("Bottom Layers Time")] public uint BottomLayersTime { get; set; } = 35000;
- [DisplayName("Number of Bottom Layers")] public ushort NumberBottomLayers { get; set; } = 3;
- [DisplayName("Blanking Layer Time")] public uint BlankingLayerTime { get; set; } = 1000;
- [DisplayName("Build Direction")] public string BuildDirection { get; set; } = "Bottom_Up";
- [DisplayName("Lift Distance")] public float LiftDistance { get; set; } = 4;
- [DisplayName("Slide/Tilt Value")] public byte TiltValue { get; set; }
- [DisplayName("Use Mainlift GCode Tab")] public bool UseMainliftGCodeTab { get; set; }
- [DisplayName("Settle Time")] public uint SettleTime { get; set; } = 1600;
- [DisplayName("Anti Aliasing")] public bool AntiAliasing { get; set; } = true;
- [DisplayName("Anti Aliasing Value")] public float AntiAliasingValue { get; set; } = 2;
- [DisplayName("Z Lift Feed Rate")] public float ZLiftFeedRate { get; set; } = 120;
- [DisplayName("Z Bottom Lift Feed Rate")] public float ZBottomLiftFeedRate { get; set; } = 120;
- [DisplayName("Z Lift Retract Rate")] public float ZLiftRetractRate { get; set; } = 120;
- [DisplayName("Flip X")] public bool FlipX { get; set; }
- [DisplayName("Flip Y")] public bool FlipY { get; set; }
- [DisplayName("Number of Slices")] public uint LayersNum { get; set; }
-
- // ;(****Machine Configuration ******)
- [DisplayName("Platform X Size")] public float PlatformXSize { get; set; }
- [DisplayName("Platform Y Size")] public float PlatformYSize { get; set; }
- [DisplayName("Platform Z Size")] public float PlatformZSize { get; set; }
- [DisplayName("Max X Feedrate")] public ushort MaxXFeedrate { get; set; } = 200;
- [DisplayName("Max Y Feedrate")] public ushort MaxYFeedrate { get; set; } = 200;
- [DisplayName("Max Z Feedrate")] public ushort MaxZFeedrate { get; set; } = 200;
- [DisplayName("Machine Type")] public string MachineType { get; set; } = "UV_LCD";
-
- // ;(****UVtools Configuration ******)
- [DisplayName("Bottom Layer Light PWM")] public byte BottomLightPWM { get; set; } = 255;
- [DisplayName("Layer Light PWM")] public byte LightPWM { get; set; } = 255;
}
- public class Slice
+ public Slice(string name)
{
- [DisplayName("xppm")] public float Xppm { get; set; } = 19.324f;
- [DisplayName("yppm")] public float Yppm { get; set; } = 19.324f;
- [DisplayName("xres")] public ushort Xres { get; set; }
- [DisplayName("yres")] public ushort Yres { get; set; }
- [DisplayName("thickness")] public float Thickness { get; set; }
- [DisplayName("layers_num")] public uint LayersNum { get; set; }
- [DisplayName("head_layers_num")] public ushort HeadLayersNum { get; set; } = 3;
- [DisplayName("layers_expo_ms")] public uint LayersExpoMs { get; set; } = 5500;
- [DisplayName("head_layers_expo_ms")] public uint HeadLayersExpoMs { get; set; } = 35000;
- [DisplayName("wait_before_expo_ms")] public uint WaitBeforeExpoMs { get; set; } = 2000;
- [DisplayName("lift_distance")] public float LiftDistance { get; set; } = 4;
- [DisplayName("lift_up_speed")] public float LiftUpSpeed { get; set; } = 120;
- [DisplayName("lift_down_speed")] public float LiftDownSpeed { get; set; } = 120;
- [DisplayName("lift_when_finished")] public byte LiftWhenFinished { get; set; } = 80;
+ Name = name;
}
+ }
-
-
- #endregion
-
- #region Properties
- public Slice SliceSettings { get; } = new();
- public Output OutputSettings { get; } = new();
- public CWSSliceBuildConfig SliceBuildConfig { get; set; } = new();
-
-
- public override FileFormatType FileType => FileFormatType.Archive;
+ [XmlArrayItem("model")]
+ public Model[]? Models { get; set; }
+ public Slice[]? Slices { get; set; }
- public enum PrinterType : byte
- {
- Unknown,
- Elfin,
- BeneMono,
- Wanhao,
- }
+ public Slice SliceProfile { get; set; } = new();
+ public Slice GCode { get; set; } = new();
+}
- public PrinterType Printer { get; set; } = PrinterType.Unknown;
+[Serializable]
+[XmlRoot(ElementName = "SliceBuildConfig")]
+public sealed class CWSSliceBuildConfig
+{
+ public const string GCODESTART =
+ ";********** Header Start ********\r\n" +
+ "G21 ; Set units to be mm\r\n" +
+ "G91 ; Relative Positioning\r\n" +
+ "M17 ; Enable motors\r\n" +
+ "; G28 Z ; Auto-home on print - commented by default";
+
+ public const string GCODEEND =
+ ";********** Footer ********\r\n" +
+ "@cmdCloseShutter ; UV off\r\n" +
+ "G4 P0 ; wait for last lift to complete\r\n" +
+ "G1 Z40.0 F150.0 ; lift model clear of resin\r\n" +
+ "G4 P0 ; sync\r\n" +
+ "M18 ; Disable Motors\r\n" +
+ ";&lt;Completed&gt;";
+
+ public const string GCODELIFT =
+ ";********** Lift Sequence %d$CURSLICE ********\r\n" +
+ "G1 Z($ZLiftDist * $ZDir) F{$CURSLICE &lt; $NumFirstLayers?$ZBottomLiftRate:$ZLiftRate}\r\n;" +
+ "&lt;Takes&gt; {$CURSLICE&lt;$NumFirstLayers?%d($ZLiftDist*1000*60/$ZBottomLiftRate):%d($ZLiftDist*1000*60/$ZLiftRate)}\r\n" +
+ "G4 P0 ; Wait for lift rise to complete\r\n;" +
+ "&lt;Delay&gt; %d$TopTime\r\n" +
+ "G1 Z(($LayerThickness-$ZLiftDist) * $ZDir) F$ZRetractRate\r\n" +
+ ";&lt;Takes&gt; %d(($ZLiftDist*1000-$LayerThickness*1000)*60/$ZRetractRate)";
+
+ public const string GCODELAYER =
+ ";********** Layer %d$CURSLICE ********\r\n" +
+ ";&lt;Slice&gt; %d$CURSLICE\r\n" +
+ "@cmdOpenShutter ; UV on\r\n" +
+ ";{$CURSLICE&lt;$NumFirstLayers?&lt;Delay&gt; %d$FirstLayerTime:&lt;Delay&gt; %d$LayerTime}\r\n" +
+ "@cmdCloseShutter ; UV off\r\n" +
+ ";&lt;Slice&gt; Blank\r\n" +
+ ";&lt;Delay&gt; %d$BlankTime";
+
+ public sealed class InkConfig
+ {
+ public string? Name { get; set; }
+ public float SliceHeight { get; set; }
+ public uint LayerTime { get; set; }
+ public uint FirstLayerTime { get; set; }
+ public ushort NumberofBottomLayers { get; set; }
+ public float ResinPriceL { get; set; }
+ }
- public override FileExtension[] FileExtensions { get; } = {
- new (typeof(CWSFile), "cws", "NovaMaker CWS"),
- new (typeof(CWSFile), "rgb.cws", "NovaMaker Bene4 Mono / Elfin2 Mono SE (CWS)"),
- new (typeof(CWSFile), "xml.cws", "Creation Workshop X (CWS)"),
- };
+ [XmlAttribute]
+ public byte FileVersion { get; set; } = 2;
+
+ public float DotsPermmX { get; set; } = 21.164f;
+ public float DotsPermmY { get; set; } = 21.164f;
+
+ public ushort XResolution { get; set; }
+ public ushort YResolution { get; set; }
+ public uint BlankTime { get; set; }
+ public uint TopTime { get; set; }
+ public uint PlatformTemp { get; set; } = 75;
+ public byte ExportSVG { get; set; }
+ public string Export { get; set; } = "False";
+ public string ExportPNG { get; set; } = "False";
+ public string CalcSliceSize { get; set; } = "False";
+ public uint XOffset { get; set; }
+ public uint YOffset { get; set; }
+ public string Direction { get; set; } = "Bottom_Up";
+ public float LiftDistance { get; set; } = 6;
+ public float SlideTiltValue { get; set; }
+ public uint SettleTime { get; set; } = 1600;
+ public string AntiAliasing { get; set; } = "True";
+ public string UseMainLiftGCode { get; set; } = "False";
+ public uint AntiAliasingValue { get; set; } = 4;
+ public float LiftFeedRate { get; set; } = 40;
+ public float BottomLiftFeedRate { get; set; } = 25;
+ public float LiftRetractRate { get; set; } = 80;
+ public string ExportOption { get; set; } = "ZIP";
+ public string RenderOutlines { get; set; } = "False";
+ public uint OutlineWidth_Inset { get; set; } = 2;
+ public uint OutlineWidth_Outset { get; set; }
+ public string FlipX { get; set; } = "False";
+ public string FlipY { get; set; } = "False";
+
+ public string Notes { get; set; } =
+ "Use these as a place to start; tune them for your specific printer and resins.\r\nAlways run a calibration on your resin to dial in for your machine.\r\nProfiles created by Earl Miller for CW v1.0.0.75.";
+
+ public string GCodeHeader { get; set; } = GCODESTART;
+ public string GCodeFooter { get; set; } = GCODEEND;
+ public string GCodeLift { get; set; } = GCODELIFT;
+ public string GCodeLayer { get; set; } = GCODELAYER;
+
+ [XmlElement("InkConfig")]
+ public List<InkConfig>? InkConfigs { get; set; }
+
+ public string? SelectedInk { get; set; }
+ public uint MinTestExposure { get; set; } = 5000;
+ public uint TestExposureStep { get; set; } = 100;
+ public string ExportPreview { get; set; } = "None";
+ public string UseMask { get; set; } = "False";
+ public string MaskFilename { get; set; } = "False";
+}
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
+#endregion
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
+public class CWSFile : FileFormat
+{
+ #region Constants
+
+ public const string GCodeStart = "G28 ;Auto Home{0}" +
+ "G21 ;Set units to be mm{0}" +
+ "G91 ;Relative Positioning{0}" +
+ "M17 ;Enable motors{0}" +
+ "<Slice> Blank{0}" +
+ "M106 S0{0}{0}";
+
+ public const string GCodeEnd = "M106 S0 ;UV off{0}" +
+ "G1 Z{1}{0}" +
+ "{0}M18 ;Disable Motors{0}" +
+ ";<Completed>{0}";
+
+ public const string GCodeKeywordSlice = ";<Slice>";
+ public const string GCodeKeywordSliceBlank = ";<Slice> Blank";
+ public const string GCodeKeywordDelay = ";<Delay>";
+ public const string GCodeKeywordTakes = ";<Takes>";
+
+ public const string WanhaoStartGCode =
+ ";********** Header Start ********{0}" +
+ "G21 ; Set units to be mm{0}" +
+ "G91 ; Relative Positioning{0}" +
+ "M17 ; Enable motors{0}" +
+ "; G28 Z ; Auto-home on print - commented by default{0}";
+
+ public const string WanhaoPreSliceGCode =
+ ";********** Pre-Slice {1} ********{0}" +
+ "G4 P0; Make sure any previous relative moves are complete{0}" +
+ ";<Delay> {2}{0}" +
+ ";********** Layer {1} ********{0}";
+
+ public const string WanhaoGCodeEnd =
+ ";********** Footer ********{0}" +
+ "M106 S0 ; UV off{0}" +
+ "G4 P0 ; wait for last lift to complete{0}" +
+ "G1 Z40.0 F150.0 ; lift model clear of resin{0}" +
+ "G4 P0 ; sync{0}" +
+ "M18 ; Disable Motors{0}" +
+ ";<Completed>{0}";
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ #endregion
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
+ #region Sub Classes
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
+ public class Output
+ {
+ // ;(****Build and Slicing Parameters****)
+ [DisplayName("Pix per mm X")] public float PixPermmX { get; set; } = 19.324f;
+ [DisplayName("Pix per mm Y")] public float PixPermmY { get; set; } = 19.324f;
+ [DisplayName("X Resolution")] public ushort XResolution { get; set; }
+ [DisplayName("Y Resolution")] public ushort YResolution { get; set; }
+ [DisplayName("Layer Thickness")] public float LayerThickness { get; set; }
+ [DisplayName("Layer Time")] public uint LayerTime { get; set; } = 5500;
+ [DisplayName("Render Outlines")] public bool RenderOutlines { get; set; } = false;
+ [DisplayName("Outline Width Inset")] public ushort OutlineWidthInset { get; set; } = 2;
+ [DisplayName("Outline Width Outset")] public ushort OutlineWidthOutset { get; set; } = 0;
+ [DisplayName("Bottom Layers Time")] public uint BottomLayersTime { get; set; } = 35000;
+ [DisplayName("Number of Bottom Layers")] public ushort NumberBottomLayers { get; set; } = 3;
+ [DisplayName("Blanking Layer Time")] public uint BlankingLayerTime { get; set; } = 1000;
+ [DisplayName("Build Direction")] public string BuildDirection { get; set; } = "Bottom_Up";
+ [DisplayName("Lift Distance")] public float LiftDistance { get; set; } = 4;
+ [DisplayName("Slide/Tilt Value")] public byte TiltValue { get; set; }
+ [DisplayName("Use Mainlift GCode Tab")] public bool UseMainliftGCodeTab { get; set; }
+ [DisplayName("Settle Time")] public uint SettleTime { get; set; } = 1600;
+ [DisplayName("Anti Aliasing")] public bool AntiAliasing { get; set; } = true;
+ [DisplayName("Anti Aliasing Value")] public float AntiAliasingValue { get; set; } = 2;
+ [DisplayName("Z Lift Feed Rate")] public float ZLiftFeedRate { get; set; } = 120;
+ [DisplayName("Z Bottom Lift Feed Rate")] public float ZBottomLiftFeedRate { get; set; } = 120;
+ [DisplayName("Z Lift Retract Rate")] public float ZLiftRetractRate { get; set; } = 120;
+ [DisplayName("Flip X")] public bool FlipX { get; set; }
+ [DisplayName("Flip Y")] public bool FlipY { get; set; }
+ [DisplayName("Number of Slices")] public uint LayersNum { get; set; }
+
+ // ;(****Machine Configuration ******)
+ [DisplayName("Platform X Size")] public float PlatformXSize { get; set; }
+ [DisplayName("Platform Y Size")] public float PlatformYSize { get; set; }
+ [DisplayName("Platform Z Size")] public float PlatformZSize { get; set; }
+ [DisplayName("Max X Feedrate")] public ushort MaxXFeedrate { get; set; } = 200;
+ [DisplayName("Max Y Feedrate")] public ushort MaxYFeedrate { get; set; } = 200;
+ [DisplayName("Max Z Feedrate")] public ushort MaxZFeedrate { get; set; } = 200;
+ [DisplayName("Machine Type")] public string MachineType { get; set; } = "UV_LCD";
+
+ // ;(****UVtools Configuration ******)
+ [DisplayName("Bottom Layer Light PWM")] public byte BottomLightPWM { get; set; } = 255;
+ [DisplayName("Layer Light PWM")] public byte LightPWM { get; set; } = 255;
+ }
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
+ public class Slice
+ {
+ [DisplayName("xppm")] public float Xppm { get; set; } = 19.324f;
+ [DisplayName("yppm")] public float Yppm { get; set; } = 19.324f;
+ [DisplayName("xres")] public ushort Xres { get; set; }
+ [DisplayName("yres")] public ushort Yres { get; set; }
+ [DisplayName("thickness")] public float Thickness { get; set; }
+ [DisplayName("layers_num")] public uint LayersNum { get; set; }
+ [DisplayName("head_layers_num")] public ushort HeadLayersNum { get; set; } = 3;
+ [DisplayName("layers_expo_ms")] public uint LayersExpoMs { get; set; } = 5500;
+ [DisplayName("head_layers_expo_ms")] public uint HeadLayersExpoMs { get; set; } = 35000;
+ [DisplayName("wait_before_expo_ms")] public uint WaitBeforeExpoMs { get; set; } = 2000;
+ [DisplayName("lift_distance")] public float LiftDistance { get; set; } = 4;
+ [DisplayName("lift_up_speed")] public float LiftUpSpeed { get; set; } = 120;
+ [DisplayName("lift_down_speed")] public float LiftDownSpeed { get; set; } = 120;
+ [DisplayName("lift_when_finished")] public byte LiftWhenFinished { get; set; } = 80;
+ }
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
+
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
+ #endregion
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
+ #region Properties
+ public Slice SliceSettings { get; } = new();
+ public Output OutputSettings { get; } = new();
+ public CWSSliceBuildConfig SliceBuildConfig { get; set; } = new();
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.LightPWM,
- };
+ public override FileFormatType FileType => FileFormatType.Archive;
- public override System.Drawing.Size[] ThumbnailsOriginalSize => null;
+ public enum PrinterType : byte
+ {
+ Unknown,
+ Elfin,
+ BeneMono,
+ Wanhao,
+ }
- public override uint ResolutionX
+ public PrinterType Printer { get; set; } = PrinterType.Unknown;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new (typeof(CWSFile), "cws", "NovaMaker CWS"),
+ new (typeof(CWSFile), "rgb.cws", "NovaMaker Bene4 Mono / Elfin2 Mono SE (CWS)"),
+ new (typeof(CWSFile), "xml.cws", "Creation Workshop X (CWS)"),
+ };
+
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
+
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
+
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
+
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override Size[]? ThumbnailsOriginalSize => null;
+
+ public override uint ResolutionX
+ {
+ get => SliceSettings.Xres > 0 ? SliceSettings.Xres : SliceBuildConfig.XResolution;
+ set
{
- get => SliceSettings.Xres > 0 ? SliceSettings.Xres : SliceBuildConfig.XResolution;
- set
- {
- SliceBuildConfig.XResolution =
+ SliceBuildConfig.XResolution =
OutputSettings.XResolution =
- SliceSettings.Xres = (ushort) value;
- RaisePropertyChanged();
- }
+ SliceSettings.Xres = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => SliceSettings.Yres > 0 ? SliceSettings.Yres : SliceBuildConfig.YResolution;
+ set
{
- get => SliceSettings.Yres > 0 ? SliceSettings.Yres : SliceBuildConfig.YResolution;
- set
- {
- SliceBuildConfig.YResolution =
+ SliceBuildConfig.YResolution =
OutputSettings.YResolution =
- SliceSettings.Yres = (ushort) value;
- RaisePropertyChanged();
- }
+ SliceSettings.Yres = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => OutputSettings.PlatformXSize;
+ set
{
- get => OutputSettings.PlatformXSize;
- set
- {
- OutputSettings.PlatformXSize = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ OutputSettings.PlatformXSize = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => OutputSettings.PlatformYSize;
+ set
{
- get => OutputSettings.PlatformYSize;
- set
- {
- OutputSettings.PlatformYSize = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ OutputSettings.PlatformYSize = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
- {
- get => OutputSettings.PlatformZSize > 0 ? OutputSettings.PlatformZSize : base.MachineZ;
- set => base.MachineZ = OutputSettings.PlatformZSize = (float)Math.Round(value, 2);
- }
+ public override float MachineZ
+ {
+ get => OutputSettings.PlatformZSize > 0 ? OutputSettings.PlatformZSize : base.MachineZ;
+ set => base.MachineZ = OutputSettings.PlatformZSize = (float)Math.Round(value, 2);
+ }
- public override Enumerations.FlipDirection DisplayMirror
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get
{
- 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 is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both;
- OutputSettings.FlipY = value is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both;
- RaisePropertyChanged();
- }
+ 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;
}
-
- public override byte AntiAliasing
+ set
{
- get => (byte) OutputSettings.AntiAliasingValue;
- set
- {
- base.AntiAliasing = (byte)(OutputSettings.AntiAliasingValue = value.Clamp(1, 16));
- OutputSettings.AntiAliasing = OutputSettings.AntiAliasingValue > 1;
- }
+ OutputSettings.FlipX = value is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both;
+ OutputSettings.FlipY = value is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both;
+ RaisePropertyChanged();
}
+ }
- public override float LayerHeight
+ public override byte AntiAliasing
+ {
+ get => (byte) OutputSettings.AntiAliasingValue;
+ set
{
- get => SliceSettings.Thickness > 0 ? SliceSettings.Thickness : OutputSettings.LayerThickness;
- set
- {
- OutputSettings.LayerThickness = SliceSettings.Thickness = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ base.AntiAliasing = (byte)(OutputSettings.AntiAliasingValue = value.Clamp(1, 16));
+ OutputSettings.AntiAliasing = OutputSettings.AntiAliasingValue > 1;
}
+ }
- public override uint LayerCount
+ public override float LayerHeight
+ {
+ get => SliceSettings.Thickness > 0 ? SliceSettings.Thickness : OutputSettings.LayerThickness;
+ set
{
- get => base.LayerCount;
- set => base.LayerCount = OutputSettings.LayersNum = SliceSettings.LayersNum = base.LayerCount;
+ OutputSettings.LayerThickness = SliceSettings.Thickness = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override ushort BottomLayerCount
- {
- get => SliceSettings.HeadLayersNum;
- set => base.BottomLayerCount = SliceSettings.HeadLayersNum = value;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = OutputSettings.LayersNum = SliceSettings.LayersNum = base.LayerCount;
+ }
- public override float BottomLightOffDelay
- {
- get => BottomWaitTimeBeforeCure;
- set => BottomWaitTimeBeforeCure = value;
- }
+ public override ushort BottomLayerCount
+ {
+ get => SliceSettings.HeadLayersNum;
+ set => base.BottomLayerCount = SliceSettings.HeadLayersNum = value;
+ }
- public override float LightOffDelay
- {
- get => WaitTimeBeforeCure;
- set => WaitTimeBeforeCure = value;
- }
+ public override float BottomLightOffDelay
+ {
+ get => BottomWaitTimeBeforeCure;
+ set => BottomWaitTimeBeforeCure = value;
+ }
- public override float BottomWaitTimeBeforeCure
- {
- get => base.BottomWaitTimeBeforeCure;
- set => base.BottomWaitTimeBeforeCure = base.BottomLightOffDelay = value;
- }
+ public override float LightOffDelay
+ {
+ get => WaitTimeBeforeCure;
+ set => WaitTimeBeforeCure = value;
+ }
+
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set => base.BottomWaitTimeBeforeCure = base.BottomLightOffDelay = value;
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => TimeExtensions.MillisecondsToSeconds(SliceSettings.WaitBeforeExpoMs);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(SliceSettings.WaitBeforeExpoMs);
- set
- {
- SliceSettings.WaitBeforeExpoMs = TimeExtensions.SecondsToMillisecondsUint(value);
- base.WaitTimeBeforeCure = base.LightOffDelay = value;
- }
+ SliceSettings.WaitBeforeExpoMs = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.WaitTimeBeforeCure = base.LightOffDelay = value;
}
+ }
- public override float BottomExposureTime
+ public override float BottomExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(OutputSettings.BottomLayersTime);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(OutputSettings.BottomLayersTime);
- set
- {
- OutputSettings.BottomLayersTime =
+ OutputSettings.BottomLayersTime =
SliceSettings.HeadLayersExpoMs = TimeExtensions.SecondsToMillisecondsUint(value);
- base.BottomExposureTime = value;
- }
+ base.BottomExposureTime = value;
}
+ }
- public override float ExposureTime
+ public override float ExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(OutputSettings.LayerTime);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(OutputSettings.LayerTime);
- set
- {
- OutputSettings.LayerTime =
+ OutputSettings.LayerTime =
SliceSettings.LayersExpoMs = TimeExtensions.SecondsToMillisecondsUint(value);
- base.ExposureTime = value;
- }
+ base.ExposureTime = value;
}
+ }
- public override float LiftHeight
- {
- get => OutputSettings.LiftDistance;
- set => base.LiftHeight = OutputSettings.LiftDistance = SliceSettings.LiftDistance = (float)Math.Round(value, 2);
- }
+ public override float LiftHeight
+ {
+ get => OutputSettings.LiftDistance;
+ set => base.LiftHeight = OutputSettings.LiftDistance = SliceSettings.LiftDistance = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftSpeed
- {
- get => OutputSettings.ZBottomLiftFeedRate;
- set => base.BottomLiftSpeed = OutputSettings.ZBottomLiftFeedRate = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftSpeed
+ {
+ get => OutputSettings.ZBottomLiftFeedRate;
+ set => base.BottomLiftSpeed = OutputSettings.ZBottomLiftFeedRate = (float)Math.Round(value, 2);
+ }
- public override float LiftSpeed
- {
- get => OutputSettings.ZLiftFeedRate;
- set =>
- base.LiftSpeed =
+ public override float LiftSpeed
+ {
+ get => OutputSettings.ZLiftFeedRate;
+ set =>
+ base.LiftSpeed =
OutputSettings.ZLiftFeedRate =
- SliceSettings.LiftUpSpeed = (float)Math.Round(value, 2);
- }
+ SliceSettings.LiftUpSpeed = (float)Math.Round(value, 2);
+ }
- public override float RetractSpeed
- {
- get => OutputSettings.ZLiftRetractRate;
- set => base.RetractSpeed = OutputSettings.ZLiftRetractRate = SliceSettings.LiftDownSpeed = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed
+ {
+ get => OutputSettings.ZLiftRetractRate;
+ set => base.RetractSpeed = OutputSettings.ZLiftRetractRate = SliceSettings.LiftDownSpeed = (float)Math.Round(value, 2);
+ }
- public override byte BottomLightPWM
- {
- get => OutputSettings.BottomLightPWM;
- set => base.BottomLightPWM = OutputSettings.BottomLightPWM = value;
- }
+ public override byte BottomLightPWM
+ {
+ get => OutputSettings.BottomLightPWM;
+ set => base.BottomLightPWM = OutputSettings.BottomLightPWM = value;
+ }
- public override byte LightPWM
- {
- get => OutputSettings.LightPWM;
- set => base.LightPWM = OutputSettings.LightPWM = value;
- }
+ public override byte LightPWM
+ {
+ get => OutputSettings.LightPWM;
+ set => base.LightPWM = OutputSettings.LightPWM = value;
+ }
- public override object[] Configs => Printer == PrinterType.Wanhao ? new object[] { SliceBuildConfig, OutputSettings } : new object[] { SliceSettings, OutputSettings};
- #endregion
+ public override object[] Configs => Printer == PrinterType.Wanhao ? new object[] { SliceBuildConfig, OutputSettings } : new object[] { SliceSettings, OutputSettings};
+ #endregion
- #region Constructor
- public CWSFile()
+ #region Constructor
+ public CWSFile()
+ {
+ GCode = new GCodeBuilder
{
- GCode = new GCodeBuilder
- {
- SyncMovementsWithDelay = true,
- UseComments = true,
- GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Partial,
- GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
- GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
- GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started,
- LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G1,
- EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1
- };
- GCode.CommandShowImageM6054.Set(";<Slice>", "{0}");
- GCode.CommandWaitG4.Set(";<Delay>", "{0}");
- }
- #endregion
+ SyncMovementsWithDelay = true,
+ UseComments = true,
+ GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Partial,
+ GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
+ GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
+ GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started,
+ LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G1,
+ EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1
+ };
+ GCode.CommandShowImageM6054.Set(";<Slice>", "{0}");
+ GCode.CommandWaitG4.Set(";<Delay>", "{0}");
+ }
+ #endregion
- #region Methods
+ #region Methods
- protected override void EncodeInternally(OperationProgress progress)
- {
- //var filename = fileFullPath.EndsWith(TemporaryFileAppend) ? Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(fileFullPath)) : Path.GetFileNameWithoutExtension(fileFullPath);
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ //var filename = fileFullPath.EndsWith(TemporaryFileAppend) ? Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(fileFullPath)) : Path.GetFileNameWithoutExtension(fileFullPath);
- if (Printer == PrinterType.Unknown)
+ if (Printer == PrinterType.Unknown)
+ {
+ Printer = PrinterType.Elfin;
+ for (int i = 0; i < FileExtensions.Length; i++)
{
- Printer = PrinterType.Elfin;
- for (int i = 0; i < FileExtensions.Length; i++)
- {
- if (!FileEndsWith(FileExtensions[i].Extension)) continue;
- Printer = (PrinterType) i+1;
- }
+ if (!FileEndsWith(FileExtensions[i].Extension)) continue;
+ Printer = (PrinterType) i+1;
}
+ }
- if (Printer == PrinterType.BeneMono)
+ if (Printer == PrinterType.BeneMono)
+ {
+ if (ResolutionX % 3 != 0)
{
- if (ResolutionX % 3 != 0)
- {
- throw new InvalidOperationException($"Resolution width of {ResolutionX}px is invalid. Width must be in multiples of 3.\n" +
- "Fix your printer slicing settings with the correct width that is multiple of 3.");
- }
+ throw new InvalidOperationException($"Resolution width of {ResolutionX}px is invalid. Width must be in multiples of 3.\n" +
+ "Fix your printer slicing settings with the correct width that is multiple of 3.");
}
+ }
- /*var filename = Path.GetFileNameWithoutExtension(fileFullPath);
- if (fileFullPath.EndsWith(TemporaryFileAppend))
+ /*var filename = Path.GetFileNameWithoutExtension(fileFullPath);
+ if (fileFullPath.EndsWith(TemporaryFileAppend))
+ {
+ filename = Path.GetFileNameWithoutExtension(filename);
+ }*/
+ var filename = About.Software;
+
+ if (char.IsDigit(filename[^1]))
+ {
+ throw new InvalidOperationException($"Filename for this format should not end with a digit: {filename}");
+ }
+
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ if (Printer == PrinterType.Wanhao)
+ {
+ var manifest = new CWSManifest
{
- filename = Path.GetFileNameWithoutExtension(filename);
- }*/
- var filename = About.Software;
+ GCode = {Name = $"{filename}.gcode"},
+ Slices = new CWSManifest.Slice[LayerCount],
+ SliceProfile = {Name = $"{filename}.slicing"}
+ };
- if (char.IsDigit(filename[^1]))
+ for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- throw new InvalidOperationException($"Filename for this format should not end with a digit: {filename}");
+ manifest.Slices[layerIndex] = new CWSManifest.Slice(this[layerIndex].FormatFileName(filename));
}
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- if (Printer == PrinterType.Wanhao)
+ var entry = outputFile.CreateEntry(CWSManifest.FileName);
+ using (var stream = entry.Open())
{
- var manifest = new CWSManifest
- {
- GCode = {Name = $"{filename}.gcode"},
- Slices = new CWSManifest.Slice[LayerCount],
- SliceProfile = {Name = $"{filename}.slicing"}
- };
-
- for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- manifest.Slices[layerIndex] = new CWSManifest.Slice(this[layerIndex].FormatFileName(filename));
- }
-
- XmlSerializer serializer = new(manifest.GetType());
- XmlSerializerNamespaces ns = new();
- ns.Add("", "");
- var entry = outputFile.CreateEntry(CWSManifest.FileName);
- using (var stream = entry.Open())
- {
- serializer.Serialize(stream, manifest, ns);
- }
-
- serializer = new XmlSerializer(SliceBuildConfig.GetType());
- entry = outputFile.CreateEntry($"{filename}.slicing");
- using (var stream = entry.Open())
- {
- serializer.Serialize(stream, SliceBuildConfig, ns);
- }
+ XmlExtensions.Serialize(manifest, stream, XmlExtensions.SettingsIndent, true);
}
- else
+
+ entry = outputFile.CreateEntry($"{filename}.slicing");
+ using (var stream = entry.Open())
{
- string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
- var entry = outputFile.CreateEntry("slice.conf");
+ XmlExtensions.Serialize(SliceBuildConfig, stream, XmlExtensions.SettingsIndent, true);
+ }
+ }
+ else
+ {
+ string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
+ var entry = outputFile.CreateEntry("slice.conf");
- using TextWriter tw = new StreamWriter(entry.Open());
- tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}");
- tw.WriteLine("# conf version 1.0");
- tw.WriteLine("");
+ using TextWriter tw = new StreamWriter(entry.Open());
+ tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}");
+ tw.WriteLine("# conf version 1.0");
+ tw.WriteLine("");
- foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
- if (displayNameAttribute is null) continue;
- tw.WriteLine($"{displayNameAttribute.DisplayName.PadRight(24)}= {propertyInfo.GetValue(SliceSettings)}");
- }
+ foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
+ if (displayNameAttribute is null) continue;
+ tw.WriteLine($"{displayNameAttribute.DisplayName.PadRight(24)}= {propertyInfo.GetValue(SliceSettings)}");
}
+ }
- if (Printer == PrinterType.BeneMono)
- {
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions,
+ if (Printer == PrinterType.BeneMono)
+ {
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions,
//new ParallelOptions { MaxDegreeOfParallelism = Printer == PrinterType.BeneMono ? 1 : 1 },
layerIndex =>
{
@@ -671,7 +668,7 @@ namespace UVtools.Core.FileFormats
var layer = this[layerIndex];
var layerImagePath = layer.FormatFileName(filename);
- using var mat = layer.LayerMat;
+ using var mat = layer.LayerMat!;
using var matEncode = new Mat(mat.Height, mat.GetRealStep() / 3, DepthType.Cv8U, 3);
var span = mat.GetDataByteSpan();
var spanEncode = matEncode.GetDataByteSpan();
@@ -687,357 +684,357 @@ namespace UVtools.Core.FileFormats
progress++;
}
});
+ }
+ else
+ {
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ var layer = this[layerIndex];
+ var layerImagePath = layer.FormatFileName(filename);
+ outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
}
- else
+ }
+
+ RebuildGCode();
+ outputFile.PutFileContent($"{filename}.gcode", GCodeStr, ZipArchiveMode.Create);
+ }
+
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read);
+ var entry = inputFile.GetEntry("manifest.xml");
+ if (entry is not null) // Wanhao
+ {
+ //DecodeXML(fileFullPath, inputFile, progress);
+ Printer = PrinterType.Wanhao;
+
+ try
{
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ using var stream = entry.Open();
+ var manifest = XmlExtensions.DeserializeFromStream<CWSManifest>(stream);
+ if (manifest.Slices is null)
{
- progress.Token.ThrowIfCancellationRequested();
- var layer = this[layerIndex];
- var layerImagePath = layer.FormatFileName(filename);
- outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create);
- progress++;
+ throw new NullReferenceException("Slices information are not present on the manifest file");
}
+ OutputSettings.LayersNum = (uint) manifest.Slices.Length;
}
+ catch (Exception e)
+ {
+ Clear();
+ throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
+ }
+
- RebuildGCode();
- outputFile.PutFileContent($"{filename}.gcode", GCodeStr, ZipArchiveMode.Create);
- }
+ entry = inputFile.Entries.FirstOrDefault(e => e.Name.EndsWith(".slicing"));
- protected override void DecodeInternally(OperationProgress progress)
- {
- using var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read);
- var entry = inputFile.GetEntry("manifest.xml");
- if (entry is not null) // Wanhao
+ if (entry is not null)
{
- //DecodeXML(fileFullPath, inputFile, progress);
- Printer = PrinterType.Wanhao;
-
+ //Clear();
+ //throw new FileLoadException(".slicing file not found, unable to proceed.", fileFullPath);
try
{
- var serializer = new XmlSerializer(typeof(CWSManifest));
using var stream = entry.Open();
- var manifest = (CWSManifest)serializer.Deserialize(stream);
- OutputSettings.LayersNum = (uint) manifest.Slices.Length;
+ SliceBuildConfig = XmlExtensions.DeserializeFromStream<CWSSliceBuildConfig>(stream);
}
catch (Exception e)
{
Clear();
throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
}
-
-
- entry = inputFile.Entries.FirstOrDefault(e => e.Name.EndsWith(".slicing"));
-
- if (entry is not null)
- {
- //Clear();
- //throw new FileLoadException(".slicing file not found, unable to proceed.", fileFullPath);
- try
- {
- var serializer = new XmlSerializer(typeof(CWSSliceBuildConfig));
- using var stream = entry.Open();
- SliceBuildConfig = (CWSSliceBuildConfig)serializer.Deserialize(stream);
- }
- catch (Exception e)
- {
- Clear();
- throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
- }
- }
}
- else // Novamaker
+ }
+ else // Novamaker
+ {
+ entry = inputFile.GetEntry("slice.conf");
+ if (entry is null)
{
- entry = inputFile.GetEntry("slice.conf");
- if (entry is null)
- {
- Clear();
- throw new FileLoadException("slice.conf not found", FileFullPath);
- }
+ Clear();
+ throw new FileLoadException("slice.conf not found", FileFullPath);
+ }
- using TextReader tr = new StreamReader(entry.Open());
- string line;
- while ((line = tr.ReadLine()) != null)
- {
- line = line.Replace("# ", string.Empty);
- if (string.IsNullOrEmpty(line)) continue;
- //if(line[0] == '#') continue;
+ using TextReader tr = new StreamReader(entry.Open());
+ string? line;
+ while ((line = tr.ReadLine()) != null)
+ {
+ line = line.Replace("# ", string.Empty);
+ if (string.IsNullOrEmpty(line)) continue;
+ //if(line[0] == '#') continue;
- var splitLine = line.Split('=');
- if (splitLine.Length < 2) continue;
+ var splitLine = line.Split('=');
+ if (splitLine.Length < 2) continue;
- foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
- if (displayNameAttribute is null) continue;
- if (!splitLine[0].Trim().Equals(displayNameAttribute.DisplayName)) continue;
- Helpers.SetPropertyValue(propertyInfo, SliceSettings, splitLine[1].Trim());
- }
+ foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
+ if (displayNameAttribute is null) continue;
+ if (!splitLine[0].Trim().Equals(displayNameAttribute.DisplayName)) continue;
+ Helpers.SetPropertyValue(propertyInfo, SliceSettings, splitLine[1].Trim());
}
- tr.Close();
}
+ tr.Close();
+ }
- entry = inputFile.Entries.FirstOrDefault(e => e.Name.EndsWith(".gcode"));
- if (entry is null)
- {
- Clear();
- throw new FileLoadException("Unable to find .gcode file", FileFullPath);
- }
+ entry = inputFile.Entries.FirstOrDefault(e => e.Name.EndsWith(".gcode"));
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException("Unable to find .gcode file", FileFullPath);
+ }
- using (TextReader tr = new StreamReader(entry.Open()))
+ using (TextReader tr = new StreamReader(entry.Open()))
+ {
+ string? line;
+ GCode!.Clear();
+ while ((line = tr.ReadLine()) != null)
{
- string line;
- GCode.Clear();
- while ((line = tr.ReadLine()) != null)
+ GCode.AppendLine(line);
+ if (string.IsNullOrEmpty(line)) continue;
+
+ if (line[0] != ';')
{
- GCode.AppendLine(line);
- if (string.IsNullOrEmpty(line)) continue;
+ continue;
+ }
+
+ var splitLine = line.Split('=');
+ if (splitLine.Length < 2) continue;
- if (line[0] != ';')
+ foreach (var propertyInfo in OutputSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
+ if (displayNameAttribute is null) continue;
+ if (!splitLine[0].Trim(' ', ';', '(').Equals(displayNameAttribute.DisplayName)) continue;
+ try
{
- continue;
+ Helpers.SetPropertyValue(propertyInfo, OutputSettings, splitLine[1].Trim(' ', ')', 'p', 'x', 'm', 'n', 's', '/'));
}
-
- var splitLine = line.Split('=');
- if (splitLine.Length < 2) continue;
-
- foreach (var propertyInfo in OutputSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ catch
{
- var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
- if (displayNameAttribute is null) continue;
- if (!splitLine[0].Trim(' ', ';', '(').Equals(displayNameAttribute.DisplayName)) continue;
- try
- {
- Helpers.SetPropertyValue(propertyInfo, OutputSettings, splitLine[1].Trim(' ', ')', 'p', 'x', 'm', 'n', 's', '/'));
- }
- catch
- {
- // ignored
- }
-
- //Debug.WriteLine(splitLine[1].Trim(' ', ')', 'm', 'n', '/'));
+ // ignored
}
+
+ //Debug.WriteLine(splitLine[1].Trim(' ', ')', 'm', 'n', '/'));
}
- tr.Close();
}
+ tr.Close();
+ }
- LayerManager.Init(OutputSettings.LayersNum, DecodeType == FileDecodeType.Partial);
+ Init(OutputSettings.LayersNum, DecodeType == FileDecodeType.Partial);
- progress.ItemCount = OutputSettings.LayersNum;
+ progress.ItemCount = OutputSettings.LayersNum;
- if(LayerCount > 0)
+ if(LayerCount > 0)
+ {
+ if (DecodeType == FileDecodeType.Full)
{
- if (DecodeType == FileDecodeType.Full)
+ var inputFilename = Path.GetFileNameWithoutExtension(FileFullPath)!;
+ foreach (var pngEntry in inputFile.Entries)
{
- var inputFilename = Path.GetFileNameWithoutExtension(FileFullPath);
- foreach (var pngEntry in inputFile.Entries)
- {
- if (!pngEntry.Name.EndsWith(".png")) continue;
- var filename = Path.GetFileNameWithoutExtension(pngEntry.Name)
- .Replace(inputFilename, string.Empty, StringComparison.Ordinal);
-
- var layerIndexStr = string.Empty;
- var layerStr = filename;
- for (int i = layerStr.Length - 1; i >= 0; i--)
- {
- if (layerStr[i] < '0' || layerStr[i] > '9') break;
- layerIndexStr = $"{layerStr[i]}{layerIndexStr}";
- }
+ if (!pngEntry.Name.EndsWith(".png")) continue;
+ var filename = Path.GetFileNameWithoutExtension(pngEntry.Name).Replace(inputFilename, string.Empty, StringComparison.Ordinal);
- if (string.IsNullOrEmpty(layerIndexStr)) continue;
- if (!uint.TryParse(layerIndexStr, out var layerIndex)) continue;
- using var stream = pngEntry.Open();
- this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
+ var layerIndexStr = string.Empty;
+ var layerStr = filename;
+ for (int i = layerStr.Length - 1; i >= 0; i--)
+ {
+ if (layerStr[i] < '0' || layerStr[i] > '9') break;
+ layerIndexStr = $"{layerStr[i]}{layerIndexStr}";
}
+
+ if (string.IsNullOrEmpty(layerIndexStr)) continue;
+ if (!uint.TryParse(layerIndexStr, out var layerIndex)) continue;
+ using var stream = pngEntry.Open();
+ this[layerIndex] = new Layer(layerIndex, stream, this);
}
+ }
- GCode.ParseLayersFromGCode(this);
+ GCode.ParseLayersFromGCode(this);
- var firstLayer = FirstLayer;
- if (firstLayer is not null && DecodeType == FileDecodeType.Full)
+ var firstLayer = FirstLayer;
+ if (firstLayer is not null && DecodeType == FileDecodeType.Full)
+ {
+ if (Printer == PrinterType.Unknown)
{
- if (Printer == PrinterType.Unknown)
- {
- using Mat mat = new ();
- CvInvoke.Imdecode(firstLayer.CompressedBytes, ImreadModes.AnyColor, mat);
- Printer = mat.NumberOfChannels == 1 ? PrinterType.Elfin : PrinterType.BeneMono;
- }
+ using Mat mat = new ();
+ CvInvoke.Imdecode(firstLayer.CompressedBytes, ImreadModes.AnyColor, mat);
+ Printer = mat.NumberOfChannels == 1 ? PrinterType.Elfin : PrinterType.BeneMono;
+ }
- if (Printer == PrinterType.BeneMono)
+ if (Printer == PrinterType.BeneMono)
+ {
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = this[layerIndex];
+ using Mat mat = new();
+ CvInvoke.Imdecode(layer.CompressedBytes, ImreadModes.Color, mat);
+ using Mat matDecode = new(mat.Height, mat.GetRealStep(), DepthType.Cv8U, 1);
+ var span = mat.GetDataByteSpan();
+ var spanDecode = matDecode.GetDataByteSpan();
+ for (int i = 0; i < span.Length; i++)
{
- if (progress.Token.IsCancellationRequested) return;
- var layer = this[layerIndex];
- using Mat mat = new();
- CvInvoke.Imdecode(layer.CompressedBytes, ImreadModes.Color, mat);
- using Mat matDecode = new(mat.Height, mat.GetRealStep(), DepthType.Cv8U, 1);
- var span = mat.GetDataByteSpan();
- var spanDecode = matDecode.GetDataByteSpan();
- for (int i = 0; i < span.Length; i++)
- {
- spanDecode[i] = span[i];
- }
-
- layer.LayerMat = matDecode;
- layer.IsModified = false;
- progress.LockAndIncrement();
- });
- }
+ spanDecode[i] = span[i];
+ }
+
+ layer.LayerMat = matDecode;
+ layer.IsModified = false;
+ progress.LockAndIncrement();
+ });
}
}
-
- LayerManager.GetBoundingRectangle(progress);
}
- public override void RebuildGCode()
+ GetBoundingRectangle(progress);
+ }
+
+ public override void RebuildGCode()
+ {
+ if (!SupportsGCode || SuppressRebuildGCode) return;
+ //string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
+ //GCode.Clear();
+ //GCode.AppendLine($"; {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}");
+ StringBuilder sb = new();
+ sb.AppendLine(";(**** Build and Slicing Parameters ****)");
+
+ foreach (var propertyInfo in OutputSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
- if (!SupportsGCode || SuppressRebuildGCode) return;
- //string arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
- //GCode.Clear();
- //GCode.AppendLine($"; {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}");
- StringBuilder sb = new();
- sb.AppendLine(";(**** Build and Slicing Parameters ****)");
-
- foreach (var propertyInfo in OutputSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
+ if (displayNameAttribute is null) continue;
+ if (propertyInfo.Name.Equals(nameof(OutputSettings.LayersNum)))
{
- var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
- if (displayNameAttribute is null) continue;
- if (propertyInfo.Name.Equals(nameof(OutputSettings.LayersNum)))
- {
- sb.AppendLine($";{displayNameAttribute.DisplayName} = {propertyInfo.GetValue(OutputSettings)}");
- }
- else
- {
- sb.AppendLine($";({displayNameAttribute.DisplayName} = {propertyInfo.GetValue(OutputSettings)})");
- }
+ sb.AppendLine($";{displayNameAttribute.DisplayName} = {propertyInfo.GetValue(OutputSettings)}");
+ }
+ else
+ {
+ sb.AppendLine($";({displayNameAttribute.DisplayName} = {propertyInfo.GetValue(OutputSettings)})");
}
+ }
- GCode.RebuildGCode(this, sb);
- /*GCode.AppendLine();
+ GCode?.RebuildGCode(this, sb);
+ /*GCode.AppendLine();
- GCode.AppendFormat(Printer == PrinterType.Wanhao ? WanhaoStartGCode : GCodeStart, Environment.NewLine);
+ GCode.AppendFormat(Printer == PrinterType.Wanhao ? WanhaoStartGCode : GCodeStart, Environment.NewLine);
- float lastZPosition = 0;
+ float lastZPosition = 0;
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- Layer layer = this[layerIndex];
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ Layer layer = this[layerIndex];
- if (Printer == PrinterType.Wanhao)
- {
- GCode.AppendFormat(WanhaoPreSliceGCode, Environment.NewLine, layerIndex, OutputSettings.SettleTime);
- }
+ if (Printer == PrinterType.Wanhao)
+ {
+ GCode.AppendFormat(WanhaoPreSliceGCode, Environment.NewLine, layerIndex, OutputSettings.SettleTime);
+ }
- GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}");
- if (layer.ExposureTime > 0 && layer.LightPWM > 0)
- {
- GCode.AppendLine($"M106 S{layer.LightPWM} ; UV on");
- GCode.AppendLine($"{GCodeKeywordDelay} {layer.ExposureTime * 1000}");
- GCode.AppendLine("M106 S0 ; UV off");
- }
- GCode.AppendLine(GCodeKeywordSliceBlank);
+ GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}");
+ if (layer.ExposureTime > 0 && layer.LightPWM > 0)
+ {
+ GCode.AppendLine($"M106 S{layer.LightPWM} ; UV on");
+ GCode.AppendLine($"{GCodeKeywordDelay} {layer.ExposureTime * 1000}");
+ GCode.AppendLine("M106 S0 ; UV off");
+ }
+ GCode.AppendLine(GCodeKeywordSliceBlank);
- if (Printer == PrinterType.Wanhao)
- {
- GCode.AppendLine($"{GCodeKeywordDelay} 0");
- }
+ if (Printer == PrinterType.Wanhao)
+ {
+ GCode.AppendLine($"{GCodeKeywordDelay} 0");
+ }
- if (lastZPosition != layer.PositionZ)
+ if (lastZPosition != layer.PositionZ)
+ {
+ if (layer.LiftHeight > 0)
{
- if (layer.LiftHeight > 0)
- {
- float downPos = Layer.RoundHeight(layer.LiftHeight - layer.PositionZ + lastZPosition);
- if (Printer == PrinterType.Wanhao)
- {
- GCode.AppendLine($";********** Lift Sequence {layerIndex} ********");
- }
- GCode.AppendLine($"G1 Z{layer.LiftHeight} F{layer.LiftSpeed}");
- if (Printer == PrinterType.Wanhao)
- {
- GCode.AppendLine($"{GCodeKeywordTakes} {OperationCalculator.LightOffDelayC.CalculateMillisecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed)}");
- GCode.AppendLine($"G4 P0 ; Wait for lift rise to complete");
- GCode.AppendLine($"{GCodeKeywordDelay} 0");
- }
- GCode.AppendLine($"G1 Z-{downPos} F{layer.RetractSpeed}");
- if (Printer == PrinterType.Wanhao)
- {
- GCode.AppendLine($"{GCodeKeywordTakes} {OperationCalculator.LightOffDelayC.CalculateMillisecondsLiftOnly(downPos, layer.RetractSpeed)}");
- }
- }
- else
+ float downPos = Layer.RoundHeight(layer.LiftHeight - layer.PositionZ + lastZPosition);
+ if (Printer == PrinterType.Wanhao)
{
- GCode.AppendLine($"G1 Z{Layer.RoundHeight(layer.PositionZ - lastZPosition)} F{layer.LiftSpeed}");
+ GCode.AppendLine($";********** Lift Sequence {layerIndex} ********");
}
- }
-
- if (Printer != PrinterType.Wanhao)
- {
- // delay = max(extra['wait'], 500) + int(((int(lift)/(extra['lift_feed']/60)) + (int(lift)/(extra['lift_retract']/60)))*1000)
- /*uint extraDelay =
- OperationCalculator.LightOffDelayC.CalculateMilliseconds(layer.LiftHeight, layer.LiftSpeed,
- layer.RetractSpeed);
- if (layerIndex < BottomLayerCount)
+ GCode.AppendLine($"G1 Z{layer.LiftHeight} F{layer.LiftSpeed}");
+ if (Printer == PrinterType.Wanhao)
{
- extraDelay = (uint) Math.Max(extraDelay + 10000, layer.ExposureTime * 1000);
+ GCode.AppendLine($"{GCodeKeywordTakes} {OperationCalculator.LightOffDelayC.CalculateMillisecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed)}");
+ GCode.AppendLine($"G4 P0 ; Wait for lift rise to complete");
+ GCode.AppendLine($"{GCodeKeywordDelay} 0");
}
- else
+ GCode.AppendLine($"G1 Z-{downPos} F{layer.RetractSpeed}");
+ if (Printer == PrinterType.Wanhao)
{
- extraDelay += Math.Max(OutputSettings.BlankingLayerTime, 500);
+ GCode.AppendLine($"{GCodeKeywordTakes} {OperationCalculator.LightOffDelayC.CalculateMillisecondsLiftOnly(downPos, layer.RetractSpeed)}");
}
+ }
+ else
+ {
+ GCode.AppendLine($"G1 Z{Layer.RoundHeight(layer.PositionZ - lastZPosition)} F{layer.LiftSpeed}");
+ }
+ }
- GCode.AppendLine($"{GCodeKeywordDelay} {layer.LightOffDelay * 1000}");
- GCode.AppendLine();
+ if (Printer != PrinterType.Wanhao)
+ {
+ // delay = max(extra['wait'], 500) + int(((int(lift)/(extra['lift_feed']/60)) + (int(lift)/(extra['lift_retract']/60)))*1000)
+ /*uint extraDelay =
+ OperationCalculator.LightOffDelayC.CalculateMilliseconds(layer.LiftHeight, layer.LiftSpeed,
+ layer.RetractSpeed);
+ if (layerIndex < BottomLayerCount)
+ {
+ extraDelay = (uint) Math.Max(extraDelay + 10000, layer.ExposureTime * 1000);
+ }
+ else
+ {
+ extraDelay += Math.Max(OutputSettings.BlankingLayerTime, 500);
}
- lastZPosition = layer.PositionZ;
+ GCode.AppendLine($"{GCodeKeywordDelay} {layer.LightOffDelay * 1000}");
+ GCode.AppendLine();
}
- GCode.AppendFormat(Printer == PrinterType.Wanhao ? WanhaoGCodeEnd : GCodeEnd, Environment.NewLine, SliceSettings.LiftWhenFinished);*/
- RaisePropertyChanged(nameof(GCodeStr));
+ lastZPosition = layer.PositionZ;
}
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- var arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
- var entry = outputFile.GetPutFile("slice.conf");
- var stream = entry.Open();
- stream.SetLength(0);
+ GCode.AppendFormat(Printer == PrinterType.Wanhao ? WanhaoGCodeEnd : GCodeEnd, Environment.NewLine, SliceSettings.LiftWhenFinished);*/
+ RaisePropertyChanged(nameof(GCodeStr));
+ }
- using (TextWriter tw = new StreamWriter(stream))
- {
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ var arch = Environment.Is64BitOperatingSystem ? "64-bits" : "32-bits";
+ var entry = outputFile.GetPutFile("slice.conf");
+ var stream = entry.Open();
+ stream.SetLength(0);
- tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}");
- tw.WriteLine("# conf version 1.0");
- tw.WriteLine("");
+ using (TextWriter tw = new StreamWriter(stream))
+ {
- foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
- if (displayNameAttribute is null) continue;
- tw.WriteLine($"{displayNameAttribute.DisplayName.PadRight(24)}= {propertyInfo.GetValue(SliceSettings)}");
- }
- }
+ tw.WriteLine($"# {About.Website} {About.Software} {Assembly.GetExecutingAssembly().GetName().Version} {arch} {DateTime.UtcNow}");
+ tw.WriteLine("# conf version 1.0");
+ tw.WriteLine("");
- var entriesToRemove = outputFile.Entries.Where(zipEntry => zipEntry.Name.EndsWith(".gcode")).ToArray();
- foreach (var zipEntry in entriesToRemove)
+ foreach (var propertyInfo in SliceSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
- zipEntry.Delete();
+ var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
+ if (displayNameAttribute is null) continue;
+ tw.WriteLine($"{displayNameAttribute.DisplayName.PadRight(24)}= {propertyInfo.GetValue(SliceSettings)}");
}
+ }
- outputFile.PutFileContent($"{About.Software}.gcode", GCodeStr, ZipArchiveMode.Update);
+ var entriesToRemove = outputFile.Entries.Where(zipEntry => zipEntry.Name.EndsWith(".gcode")).ToArray();
+ foreach (var zipEntry in entriesToRemove)
+ {
+ zipEntry.Delete();
+ }
- /*foreach (var layer in this)
- {
- if (!layer.IsModified) continue;
- outputFile.PutFileContent(layer.Filename, layer.CompressedBytes, ZipArchiveMode.Update);
- layer.IsModified = false;
- }*/
+ outputFile.PutFileContent($"{About.Software}.gcode", GCodeStr, ZipArchiveMode.Update);
- //Decode(FileFullPath, progress);
- }
+ /*foreach (var layer in this)
+ {
+ if (!layer.IsModified) continue;
+ outputFile.PutFileContent(layer.Filename, layer.CompressedBytes, ZipArchiveMode.Update);
+ layer.IsModified = false;
+ }*/
- #endregion
+ //Decode(FileFullPath, progress);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs
index 469a556..ecd7fd6 100644
--- a/UVtools.Core/FileFormats/CXDLPFile.cs
+++ b/UVtools.Core/FileFormats/CXDLPFile.cs
@@ -6,6 +6,9 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -15,735 +18,720 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class CXDLPFile : FileFormat
{
- public class CXDLPFile : FileFormat
+ #region Constants
+ private const byte HEADER_SIZE = 9; // CXSW3DV2
+ private const string HEADER_VALUE = "CXSW3DV2";
+ #endregion
+
+ #region Sub Classes
+ #region Header
+ public sealed class Header
{
- #region Constants
- private const byte HEADER_SIZE = 9; // CXSW3DV2
- private const string HEADER_VALUE = "CXSW3DV2";
- #endregion
-
- #region Sub Classes
- #region Header
- public sealed class Header
+ //private string _printerModel = "CL-89";
+
+ /// <summary>
+ /// Gets the size of the header
+ /// </summary>
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint HeaderSize { get; set; } = HEADER_SIZE;
+
+ /// <summary>
+ /// Gets the header name
+ /// </summary>
+ [FieldOrder(1)]
+ [FieldLength(HEADER_SIZE)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string HeaderValue { get; set; } = HEADER_VALUE;
+
+ [FieldOrder(2)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort Version { get; set; } = 2;
+
+ /// <summary>
+ /// Gets the size of the printer model
+ /// </summary>
+ [FieldOrder(3)]
+ [FieldEndianness(Endianness.Big)]
+ public uint PrinterModelSize { get; set; } = 6;
+
+ /// <summary>
+ /// Gets the printer model
+ /// </summary>
+ /*[FieldOrder(4)]
+ [FieldLength(nameof(PrinterModelSize), BindingMode = BindingMode.OneWay)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string PrinterModel
{
- //private string _printerModel = "CL-89";
-
- /// <summary>
- /// Gets the size of the header
- /// </summary>
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint HeaderSize { get; set; } = HEADER_SIZE;
-
- /// <summary>
- /// Gets the header name
- /// </summary>
- [FieldOrder(1)]
- [FieldLength(HEADER_SIZE)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string HeaderValue { get; set; } = HEADER_VALUE;
-
- [FieldOrder(2)]
- [FieldEndianness(Endianness.Big)]
- public ushort Version { get; set; } = 2;
-
- /// <summary>
- /// Gets the size of the printer model
- /// </summary>
- [FieldOrder(3)]
- [FieldEndianness(Endianness.Big)]
- public uint PrinterModelSize { get; set; } = 6;
-
- /// <summary>
- /// Gets the printer model
- /// </summary>
- /*[FieldOrder(4)]
- [FieldLength(nameof(PrinterModelSize), BindingMode = BindingMode.OneWay)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string PrinterModel
+ get => _printerModel;
+ set
{
- get => _printerModel;
- set
- {
- _printerModel = value;
- PrinterModelSize = string.IsNullOrEmpty(value) ? 0 : (uint)value.Length+1;
- }
- }*/
+ _printerModel = value;
+ PrinterModelSize = string.IsNullOrEmpty(value) ? 0 : (uint)value.Length+1;
+ }
+ }*/
- [FieldOrder(4)]
- [FieldLength(nameof(PrinterModelSize))]
- public byte[] PrinterModelArray { get; set; } = Array.Empty<byte>(); // CL-89 { 0x43, 0x4C, 0x2D, 0x38, 0x39, 0x0 }
+ [FieldOrder(4)]
+ [FieldLength(nameof(PrinterModelSize))]
+ public byte[] PrinterModelArray { get; set; } = Array.Empty<byte>(); // CL-89 { 0x43, 0x4C, 0x2D, 0x38, 0x39, 0x0 }
- [Ignore]
- public string PrinterModel
+ [Ignore]
+ public string PrinterModel
+ {
+ get => Encoding.ASCII.GetString(PrinterModelArray).TrimEnd(char.MinValue);
+ set
{
- get => Encoding.ASCII.GetString(PrinterModelArray).TrimEnd(char.MinValue);
- set
- {
- PrinterModelArray = Encoding.ASCII.GetBytes(value + char.MinValue);
- PrinterModelSize = (uint) PrinterModelArray.Length;
- }
+ PrinterModelArray = Encoding.ASCII.GetBytes(value + char.MinValue);
+ PrinterModelSize = (uint) PrinterModelArray.Length;
}
+ }
- /// <summary>
- /// Gets the number of records in the layer table
- /// </summary>
- [FieldOrder(5)]
- [FieldEndianness(Endianness.Big)]
- public ushort LayerCount { get; set; }
-
- /// <summary>
- /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(6)]
- [FieldEndianness(Endianness.Big)]
- public ushort ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(7)]
- [FieldEndianness(Endianness.Big)]
- public ushort ResolutionY { get; set; }
+ /// <summary>
+ /// Gets the number of records in the layer table
+ /// </summary>
+ [FieldOrder(5)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LayerCount { get; set; }
+
+ /// <summary>
+ /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(6)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort ResolutionX { get; set; }
+
+ /// <summary>
+ /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(7)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort ResolutionY { get; set; }
- [FieldOrder(8)]
- [FieldLength(64)]
- public byte[] Offset { get; set; } = new byte[64];
+ [FieldOrder(8)]
+ [FieldLength(64)]
+ public byte[] Offset { get; set; } = new byte[64];
- public void Validate()
+ public void Validate()
+ {
+ if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE)
{
- if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE)
- {
- throw new FileLoadException("Not a valid CXDLP file!");
- }
+ throw new FileLoadException("Not a valid CXDLP file!");
}
+ }
- public override string ToString()
- {
- return $"{nameof(HeaderSize)}: {HeaderSize}, {nameof(HeaderValue)}: {HeaderValue}, {nameof(Version)}: {Version}, {nameof(PrinterModelSize)}: {PrinterModelSize}, {nameof(PrinterModelArray)}: {PrinterModelArray}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(LayerCount)}: {LayerCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Offset)}: {Offset}";
- }
+ public override string ToString()
+ {
+ return $"{nameof(HeaderSize)}: {HeaderSize}, {nameof(HeaderValue)}: {HeaderValue}, {nameof(Version)}: {Version}, {nameof(PrinterModelSize)}: {PrinterModelSize}, {nameof(PrinterModelArray)}: {PrinterModelArray}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(LayerCount)}: {LayerCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Offset)}: {Offset}";
}
+ }
- #endregion
+ #endregion
- #region SlicerInfo
- // Address: 363407
- public sealed class SlicerInfo
- {
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint DisplayWidthDataSize { get; set; } = 20;
+ #region SlicerInfo
+ // Address: 363407
+ public sealed class SlicerInfo
+ {
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint DisplayWidthDataSize { get; set; } = 20;
- [FieldOrder(1)]
- [FieldLength(nameof(DisplayWidthDataSize))]
- public byte[] DisplayWidthBytes { get; set; }
+ [FieldOrder(1)]
+ [FieldLength(nameof(DisplayWidthDataSize))]
+ public byte[] DisplayWidthBytes { get; set; } = null!;
- [FieldOrder(2)]
- [FieldEndianness(Endianness.Big)]
- public uint DisplayHeightDataSize { get; set; } = 20;
+ [FieldOrder(2)]
+ [FieldEndianness(Endianness.Big)]
+ public uint DisplayHeightDataSize { get; set; } = 20;
- [FieldOrder(3)]
- [FieldLength(nameof(DisplayHeightDataSize))]
- public byte[] DisplayHeightBytes { get; set; }
+ [FieldOrder(3)]
+ [FieldLength(nameof(DisplayHeightDataSize))]
+ public byte[] DisplayHeightBytes { get; set; } = null!;
- [FieldOrder(4)]
- [FieldEndianness(Endianness.Big)]
- public uint LayerHeightDataSize { get; set; } = 16;
+ [FieldOrder(4)]
+ [FieldEndianness(Endianness.Big)]
+ public uint LayerHeightDataSize { get; set; } = 16;
- [FieldOrder(5)]
- [FieldLength(nameof(LayerHeightDataSize))]
- public byte[] LayerHeightBytes { get; set; }
+ [FieldOrder(5)]
+ [FieldLength(nameof(LayerHeightDataSize))]
+ public byte[] LayerHeightBytes { get; set; } = null!;
- [FieldOrder(6)]
- [FieldEndianness(Endianness.Big)]
- public ushort ExposureTime { get; set; }
+ [FieldOrder(6)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort ExposureTime { get; set; }
- [FieldOrder(7)]
- [FieldEndianness(Endianness.Big)]
- public ushort WaitTimeBeforeCure { get; set; } = 1; // 1 as minimum or it wont print!
+ [FieldOrder(7)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort WaitTimeBeforeCure { get; set; } = 1; // 1 as minimum or it wont print!
- [FieldOrder(8)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomExposureTime { get; set; }
+ [FieldOrder(8)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomExposureTime { get; set; }
- [FieldOrder(9)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLayersCount { get; set; }
+ [FieldOrder(9)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLayersCount { get; set; }
- [FieldOrder(10)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLiftHeight { get; set; }
+ [FieldOrder(10)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLiftHeight { get; set; }
- [FieldOrder(11)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLiftSpeed { get; set; }
+ [FieldOrder(11)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLiftSpeed { get; set; }
- [FieldOrder(12)]
- [FieldEndianness(Endianness.Big)]
- public ushort LiftHeight { get; set; }
+ [FieldOrder(12)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LiftHeight { get; set; }
- [FieldOrder(13)]
- [FieldEndianness(Endianness.Big)]
- public ushort LiftSpeed { get; set; }
+ [FieldOrder(13)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LiftSpeed { get; set; }
- [FieldOrder(14)]
- [FieldEndianness(Endianness.Big)]
- public ushort RetractSpeed { get; set; }
+ [FieldOrder(14)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort RetractSpeed { get; set; }
- [FieldOrder(15)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLightPWM { get; set; } = 255;
+ [FieldOrder(15)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLightPWM { get; set; } = 255;
- [FieldOrder(16)]
- [FieldEndianness(Endianness.Big)]
- public ushort LightPWM { get; set; } = 255;
+ [FieldOrder(16)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LightPWM { get; set; } = 255;
- public override string ToString()
- {
- return $"{nameof(DisplayWidthDataSize)}: {DisplayWidthDataSize}, {nameof(DisplayWidthBytes)}: {DisplayWidthBytes}, {nameof(DisplayHeightDataSize)}: {DisplayHeightDataSize}, {nameof(DisplayHeightBytes)}: {DisplayHeightBytes}, {nameof(LayerHeightDataSize)}: {LayerHeightDataSize}, {nameof(LayerHeightBytes)}: {LayerHeightBytes}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LightPWM)}: {LightPWM}";
- }
+ public override string ToString()
+ {
+ return $"{nameof(DisplayWidthDataSize)}: {DisplayWidthDataSize}, {nameof(DisplayWidthBytes)}: {DisplayWidthBytes}, {nameof(DisplayHeightDataSize)}: {DisplayHeightDataSize}, {nameof(DisplayHeightBytes)}: {DisplayHeightBytes}, {nameof(LayerHeightDataSize)}: {LayerHeightDataSize}, {nameof(LayerHeightBytes)}: {LayerHeightBytes}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LightPWM)}: {LightPWM}";
}
- #endregion
+ }
+ #endregion
- #region PreLayer
+ #region PreLayer
- public sealed class PreLayer
- {
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint LayerArea { get; set; }
+ public sealed class PreLayer
+ {
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint LayerArea { get; set; }
- public PreLayer()
- {
- }
+ public PreLayer()
+ {
+ }
- public PreLayer(uint layerArea)
- {
- LayerArea = layerArea;
- }
+ public PreLayer(uint layerArea)
+ {
+ LayerArea = layerArea;
}
+ }
- #endregion
+ #endregion
- #region SlicerInfoV3
+ #region SlicerInfoV3
- public sealed class SlicerInfoV3
- {
- //[FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint SoftwareNameSize { get; set; } = (uint)About.SoftwareWithVersion.Length + 1;
+ public sealed class SlicerInfoV3
+ {
+ //[FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint SoftwareNameSize { get; set; } = (uint)About.SoftwareWithVersion.Length + 1;
- [FieldOrder(0)] public NullTerminatedUintStringBigEndian SoftwareName { get; set; } = new(About.SoftwareWithVersion);
+ [FieldOrder(0)] public NullTerminatedUintStringBigEndian SoftwareName { get; set; } = new(About.SoftwareWithVersion);
- //[FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint MaterialNameSize { get; set; }
+ //[FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint MaterialNameSize { get; set; }
- [FieldOrder(1)] public NullTerminatedUintStringBigEndian MaterialName { get; set; } = new();
+ [FieldOrder(1)] public NullTerminatedUintStringBigEndian MaterialName { get; set; } = new();
- [FieldOrder(2)] public uint Unknown1 { get; set; }
- [FieldOrder(3)] public uint Unknown2 { get; set; }
- [FieldOrder(4)] public uint Unknown3 { get; set; }
- [FieldOrder(5)] public uint Unknown4 { get; set; }
- [FieldOrder(6)] public byte Unknown5 { get; set; } = 1;
- [FieldOrder(7)] public byte LightPWM { get; set; } = byte.MaxValue;
- [FieldOrder(8)] public ushort Unknown6 { get; set; } = 2;
- [FieldOrder(9)] public PageBreak PageBreak { get; set; } = new();
+ [FieldOrder(2)] public uint Unknown1 { get; set; }
+ [FieldOrder(3)] public uint Unknown2 { get; set; }
+ [FieldOrder(4)] public uint Unknown3 { get; set; }
+ [FieldOrder(5)] public uint Unknown4 { get; set; }
+ [FieldOrder(6)] public byte Unknown5 { get; set; } = 1;
+ [FieldOrder(7)] public byte LightPWM { get; set; } = byte.MaxValue;
+ [FieldOrder(8)] public ushort Unknown6 { get; set; } = 2;
+ [FieldOrder(9)] public PageBreak PageBreak { get; set; } = new();
- public override string ToString()
- {
- return $"{nameof(SoftwareName)}: {SoftwareName}, {nameof(MaterialName)}: {MaterialName}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}, {nameof(Unknown5)}: {Unknown5}, {nameof(LightPWM)}: {LightPWM}, {nameof(Unknown6)}: {Unknown6}, {nameof(PageBreak)}: {PageBreak}";
- }
+ public override string ToString()
+ {
+ return $"{nameof(SoftwareName)}: {SoftwareName}, {nameof(MaterialName)}: {MaterialName}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}, {nameof(Unknown5)}: {Unknown5}, {nameof(LightPWM)}: {LightPWM}, {nameof(Unknown6)}: {Unknown6}, {nameof(PageBreak)}: {PageBreak}";
}
+ }
- #endregion
+ #endregion
- #region Layer Def
+ #region Layer Def
- public sealed class LayerDef
+ public sealed class LayerDef
+ {
+ public static byte[] GetHeaderBytes(uint layerArea, uint lineCount)
{
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LayerArea { 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 static byte[] GetHeaderBytes(uint layerArea, uint lineCount)
- {
- var bytes = new byte[8];
- BitExtensions.ToBytesBigEndian(layerArea, bytes);
- BitExtensions.ToBytesBigEndian(lineCount, bytes, 4);
- return bytes;
- }
+ var bytes = new byte[8];
+ BitExtensions.ToBytesBigEndian(layerArea, bytes);
+ BitExtensions.ToBytesBigEndian(lineCount, bytes, 4);
+ return bytes;
+ }
- public LayerDef() { }
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LayerArea { get; set; }
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; }
+ [FieldOrder(2)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; } = Array.Empty<LayerLine>();
+ [FieldOrder(3)] public PageBreak PageBreak { get; set; } = new();
- public LayerDef(uint layerArea, uint lineCount, LayerLine[] lines)
- {
- LayerArea = layerArea;
- LineCount = lineCount;
- Lines = lines;
- }
- }
+ public LayerDef() { }
- public sealed class LayerLine
+ public LayerDef(uint layerArea, uint lineCount, LayerLine[] lines)
{
- 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; }
+ LayerArea = layerArea;
+ LineCount = lineCount;
+ Lines = lines;
+ }
+ }
- [Ignore] public ushort StartY => (ushort) ((((Coordinates[0] << 8) + Coordinates[1]) >> 3) & 0x1FFF); // 13 bits
+ 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 EndY => (ushort)((((Coordinates[1] << 16) + (Coordinates[2] << 8) + Coordinates[3]) >> 6) & 0x1FFF); // 13 bits
+ [Ignore] public ushort StartY => (ushort) ((((Coordinates[0] << 8) + Coordinates[1]) >> 3) & 0x1FFF); // 13 bits
- [Ignore] public ushort StartX => (ushort)(((Coordinates[3] << 8) + Coordinates[4]) & 0x3FFF); // 14 bits
- [Ignore] public ushort Length => (ushort) (EndY - StartY);
+ [Ignore] public ushort EndY => (ushort)((((Coordinates[1] << 16) + (Coordinates[2] << 8) + Coordinates[3]) >> 6) & 0x1FFF); // 13 bits
- public static byte[] GetBytes(ushort startY, ushort endY, ushort startX, byte gray)
- {
- var bytes = new byte[CoordinateCount + 1];
- bytes[0] = (byte)((startY >> 5) & 0xFF);
- bytes[1] = (byte)(((startY << 3) + (endY >> 10)) & 0xFF);
- bytes[2] = (byte)((endY >> 2) & 0xFF);
- bytes[3] = (byte)(((endY << 6) + (startX >> 8)) & 0xFF);
- bytes[4] = (byte)startX;
- bytes[5] = gray;
- return bytes;
- }
+ [Ignore] public ushort StartX => (ushort)(((Coordinates[3] << 8) + Coordinates[4]) & 0x3FFF); // 14 bits
+ [Ignore] public ushort Length => (ushort) (EndY - StartY);
- public LayerLine() { }
+ public static byte[] GetBytes(ushort startY, ushort endY, ushort startX, byte gray)
+ {
+ var bytes = new byte[CoordinateCount + 1];
+ bytes[0] = (byte)((startY >> 5) & 0xFF);
+ bytes[1] = (byte)(((startY << 3) + (endY >> 10)) & 0xFF);
+ bytes[2] = (byte)((endY >> 2) & 0xFF);
+ bytes[3] = (byte)(((endY << 6) + (startX >> 8)) & 0xFF);
+ bytes[4] = (byte)startX;
+ bytes[5] = gray;
+ return bytes;
+ }
- 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 LayerLine() { }
- public override string ToString()
- {
- return $"{nameof(Gray)}: {Gray}, {nameof(StartY)}: {StartY}, {nameof(EndY)}: {EndY}, {nameof(StartX)}: {StartX}, {nameof(Length)}: {Length}";
- }
+ 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
+ public override string ToString()
{
- public static byte[] Bytes => new byte[] {0x0D, 0x0A};
-
- [FieldOrder(0)] public byte Line { get; set; } = 0x0D;
- [FieldOrder(1)] public byte Break { get; set; } = 0x0A;
+ return $"{nameof(Gray)}: {Gray}, {nameof(StartY)}: {StartY}, {nameof(EndY)}: {EndY}, {nameof(StartX)}: {StartX}, {nameof(Length)}: {Length}";
}
+ }
+
+ public sealed class PageBreak
+ {
+ public static byte[] Bytes => new byte[] {0x0D, 0x0A};
+
+ [FieldOrder(0)] public byte Line { get; set; } = 0x0D;
+ [FieldOrder(1)] public byte Break { get; set; } = 0x0A;
+ }
- #endregion
+ #endregion
- #region Footer
- public sealed class Footer
+ #region Footer
+ public sealed class Footer
+ {
+ /// <summary>
+ /// Gets the size of the header
+ /// </summary>
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint FooterSize { get; set; } = HEADER_SIZE;
+
+ /// <summary>
+ /// Gets the header name
+ /// </summary>
+ [FieldOrder(1)]
+ [FieldLength(HEADER_SIZE)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string FooterValue { get; set; } = HEADER_VALUE;
+
+ /*[FieldOrder(2)]
+ [FieldEndianness(Endianness.Big)]
+ public uint CheckSum { get; set; }*/
+
+ public void Validate()
{
- /// <summary>
- /// Gets the size of the header
- /// </summary>
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint FooterSize { get; set; } = HEADER_SIZE;
-
- /// <summary>
- /// Gets the header name
- /// </summary>
- [FieldOrder(1)]
- [FieldLength(HEADER_SIZE)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string FooterValue { get; set; } = HEADER_VALUE;
-
- /*[FieldOrder(2)]
- [FieldEndianness(Endianness.Big)]
- public uint CheckSum { get; set; }*/
-
- public void Validate()
+ if (FooterSize != HEADER_SIZE || FooterValue != HEADER_VALUE)
{
- if (FooterSize != HEADER_SIZE || FooterValue != HEADER_VALUE)
- {
- throw new FileLoadException("Not a valid CXDLP file!");
- }
+ throw new FileLoadException("Not a valid CXDLP file!");
}
}
- #endregion
+ }
+ #endregion
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public Header HeaderSettings { get; protected internal set; } = new();
- public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
- public SlicerInfoV3 SlicerInfoV3Settings { get; protected internal set; } = new();
- public Footer FooterSettings { get; protected internal set; } = new();
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
+ public SlicerInfoV3 SlicerInfoV3Settings { get; protected internal set; } = new();
+ public Footer FooterSettings { get; protected internal set; } = new();
- public override FileFormatType FileType => FileFormatType.Binary;
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(CXDLPFile), "cxdlp", "Creality CXDLP"),
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(CXDLPFile), "cxdlp", "Creality CXDLP"),
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(116, 116),
- new(290, 290),
- new(290, 290)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(116, 116),
+ new(290, 290),
+ new(290, 290)
+ };
- public override uint[] AvailableVersions { get; } = { 2, 3 };
+ public override uint[] AvailableVersions { get; } = { 2, 3 };
- public override uint DefaultVersion => 2;
+ public override uint DefaultVersion => 2;
- public override uint Version
+ public override uint Version
+ {
+ get => HeaderSettings.Version;
+ set
{
- get => HeaderSettings.Version;
- set
- {
- base.Version = value;
- HeaderSettings.Version = (ushort)base.Version;
- }
+ base.Version = value;
+ HeaderSettings.Version = (ushort)base.Version;
}
+ }
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = (ushort) value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = (ushort) value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
- set
+ string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
+ //string str = Math.Round(value, 2).ToString("0.000000");
+ SlicerInfoSettings.DisplayWidthDataSize = (uint)(str.Length * 2);
+ var data = new byte[SlicerInfoSettings.DisplayWidthDataSize];
+ for (var i = 0; i < str.Length; i++)
{
- string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
- //string str = Math.Round(value, 2).ToString("0.000000");
- SlicerInfoSettings.DisplayWidthDataSize = (uint)(str.Length * 2);
- var data = new byte[SlicerInfoSettings.DisplayWidthDataSize];
- for (var i = 0; i < str.Length; i++)
- {
- data[i * 2 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayWidthBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayWidthBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
- set
+ string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
+ //string str = Math.Round(value, 2).ToString("0.000000");
+ SlicerInfoSettings.DisplayHeightDataSize = (uint)(str.Length * 2);
+ var data = new byte[SlicerInfoSettings.DisplayHeightDataSize];
+ for (var i = 0; i < str.Length; i++)
{
- string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
- //string str = Math.Round(value, 2).ToString("0.000000");
- SlicerInfoSettings.DisplayHeightDataSize = (uint)(str.Length * 2);
- var data = new byte[SlicerInfoSettings.DisplayHeightDataSize];
- for (var i = 0; i < str.Length; i++)
- {
- data[i * 2 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
- set
+ string str = Layer.RoundHeight(value).ToString(CultureInfo.InvariantCulture);
+ //string str = Layer.RoundHeight(value).ToString("0.000000");
+ SlicerInfoSettings.LayerHeightDataSize = (uint)(str.Length * 2);
+ var data = new byte[SlicerInfoSettings.LayerHeightDataSize];
+ for (var i = 0; i < str.Length; i++)
{
- string str = Layer.RoundHeight(value).ToString(CultureInfo.InvariantCulture);
- //string str = Layer.RoundHeight(value).ToString("0.000000");
- SlicerInfoSettings.LayerHeightDataSize = (uint)(str.Length * 2);
- var data = new byte[SlicerInfoSettings.LayerHeightDataSize];
- for (var i = 0; i < str.Length; i++)
- {
- data[i * 2 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.LayerHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
- }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = HeaderSettings.LayerCount = (ushort) base.LayerCount;
+ SlicerInfoSettings.LayerHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override ushort BottomLayerCount
- {
- get => SlicerInfoSettings.BottomLayersCount;
- set => base.BottomLayerCount = SlicerInfoSettings.BottomLayersCount = value;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = HeaderSettings.LayerCount = (ushort) base.LayerCount;
+ }
- /*public override float BottomLightOffDelay => SlicerInfoSettings.WaitTimeBeforeCure;
+ public override ushort BottomLayerCount
+ {
+ get => SlicerInfoSettings.BottomLayersCount;
+ set => base.BottomLayerCount = SlicerInfoSettings.BottomLayersCount = value;
+ }
- public override float LightOffDelay
- {
- get => SlicerInfoSettings.WaitTimeBeforeCure;
- set => base.LightOffDelay = SlicerInfoSettings.WaitTimeBeforeCure = (ushort)value;
- }*/
+ /*public override float BottomLightOffDelay => SlicerInfoSettings.WaitTimeBeforeCure;
- public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
+ public override float LightOffDelay
+ {
+ get => SlicerInfoSettings.WaitTimeBeforeCure;
+ set => base.LightOffDelay = SlicerInfoSettings.WaitTimeBeforeCure = (ushort)value;
+ }*/
- public override float WaitTimeBeforeCure
- {
- get => SlicerInfoSettings.WaitTimeBeforeCure;
- set => base.WaitTimeBeforeCure = SlicerInfoSettings.WaitTimeBeforeCure = (ushort)Math.Max(1, value);
- }
+ public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
- public override float BottomExposureTime
- {
- get => SlicerInfoSettings.BottomExposureTime;
- set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort) value;
- }
+ public override float WaitTimeBeforeCure
+ {
+ get => SlicerInfoSettings.WaitTimeBeforeCure;
+ set => base.WaitTimeBeforeCure = SlicerInfoSettings.WaitTimeBeforeCure = (ushort)Math.Max(1, value);
+ }
- public override float ExposureTime
- {
- get => SlicerInfoSettings.ExposureTime;
- set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort) value;
- }
+ public override float BottomExposureTime
+ {
+ get => SlicerInfoSettings.BottomExposureTime;
+ set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort) value;
+ }
- public override float BottomLiftHeight
- {
- get => SlicerInfoSettings.BottomLiftHeight;
- set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort) value;
- }
+ public override float ExposureTime
+ {
+ get => SlicerInfoSettings.ExposureTime;
+ set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort) value;
+ }
- public override float LiftHeight
- {
- get => SlicerInfoSettings.LiftHeight;
- set => base.LiftHeight = SlicerInfoSettings.LiftHeight = (ushort)value;
- }
+ public override float BottomLiftHeight
+ {
+ get => SlicerInfoSettings.BottomLiftHeight;
+ set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort) value;
+ }
- public override float BottomLiftSpeed
- {
- get => SlicerInfoSettings.BottomLiftSpeed;
- set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value;
- }
+ public override float LiftHeight
+ {
+ get => SlicerInfoSettings.LiftHeight;
+ set => base.LiftHeight = SlicerInfoSettings.LiftHeight = (ushort)value;
+ }
- public override float LiftSpeed
- {
- get => SlicerInfoSettings.LiftSpeed;
- set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value;
- }
+ public override float BottomLiftSpeed
+ {
+ get => SlicerInfoSettings.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value;
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ 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 BottomRetractSpeed => RetractSpeed;
- public override byte BottomLightPWM
- {
- get => (byte) SlicerInfoSettings.BottomLightPWM;
- set => base.BottomLightPWM = (byte) (SlicerInfoSettings.BottomLightPWM = value);
- }
+ public override float RetractSpeed
+ {
+ get => SlicerInfoSettings.RetractSpeed;
+ set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)value;
+ }
- public override byte LightPWM
- {
- get => (byte)SlicerInfoSettings.LightPWM;
- set => base.LightPWM = (byte) (SlicerInfoSettings.LightPWM = value);
- }
+ public override byte BottomLightPWM
+ {
+ get => (byte) SlicerInfoSettings.BottomLightPWM;
+ set => base.BottomLightPWM = (byte) (SlicerInfoSettings.BottomLightPWM = value);
+ }
- public override string MachineName
- {
- get => HeaderSettings.PrinterModel;
- set => base.MachineName = HeaderSettings.PrinterModel = value;
- }
+ public override byte LightPWM
+ {
+ get => (byte)SlicerInfoSettings.LightPWM;
+ set => base.LightPWM = (byte) (SlicerInfoSettings.LightPWM = value);
+ }
- public override string MaterialName
- {
- get => SlicerInfoV3Settings.MaterialName.Value;
- set => base.MaterialName = SlicerInfoV3Settings.MaterialName.Value = value;
- }
+ public override string MachineName
+ {
+ get => HeaderSettings.PrinterModel;
+ set => base.MachineName = HeaderSettings.PrinterModel = value;
+ }
- public override object[] Configs => new object[] { HeaderSettings, SlicerInfoSettings, SlicerInfoV3Settings, FooterSettings };
+ public override string? MaterialName
+ {
+ get => SlicerInfoV3Settings.MaterialName.Value;
+ set => base.MaterialName = SlicerInfoV3Settings.MaterialName.Value = value;
+ }
- #endregion
+ public override object[] Configs => new object[] { HeaderSettings, SlicerInfoSettings, SlicerInfoV3Settings, FooterSettings };
- #region Constructors
- #endregion
+ #endregion
- #region Methods
+ #region Constructors
+ #endregion
- private void SanitizeProperties()
- {
- SlicerInfoSettings.WaitTimeBeforeCure = (ushort)Math.Max(1, WaitTimeBeforeCure);
- }
+ #region Methods
- protected override void EncodeInternally(OperationProgress progress)
- {
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.ReadWrite);
+ private void SanitizeProperties()
+ {
+ SlicerInfoSettings.WaitTimeBeforeCure = (ushort)Math.Max(1, WaitTimeBeforeCure);
+ }
- if (!MachineName.StartsWith("CL-"))
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.ReadWrite);
+
+ if (!MachineName.StartsWith("CL-"))
+ {
+ if (ResolutionX == 1620 && ResolutionY == 2560)
{
- if (ResolutionX == 1620 && ResolutionY == 2560)
- {
- MachineName = "CL-60"; // One
- }
- else if (ResolutionX == 3840 && ResolutionY == 2400) // Sky or lite
- {
- MachineName = "CL-89"; // Sky
- }
- else if (ResolutionX == 3840 && ResolutionY == 2160)
- {
- MachineName = "CL-133"; // Max
- }
- else
- {
- throw new InvalidDataException("Unable to detect the printer model from resolution, check if resolution is well defined on slicer for your printer model.");
- }
+ MachineName = "CL-60"; // One
}
+ else if (ResolutionX == 3840 && ResolutionY == 2400) // Sky or lite
+ {
+ MachineName = "CL-89"; // Sky
+ }
+ else if (ResolutionX == 3840 && ResolutionY == 2160)
+ {
+ MachineName = "CL-133"; // Max
+ }
+ else
+ {
+ throw new InvalidDataException("Unable to detect the printer model from resolution, check if resolution is well defined on slicer for your printer model.");
+ }
+ }
- SanitizeProperties();
+ SanitizeProperties();
- var pageBreak = PageBreak.Bytes;
+ var pageBreak = PageBreak.Bytes;
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- var previews = new byte[ThumbnailsOriginalSize.Length][];
+ var previews = new byte[ThumbnailsOriginalSize!.Length][];
- // Previews
- Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ // Previews
+ Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
+ if (Thumbnails[previewIndex] is null)
{
- if (progress.Token.IsCancellationRequested) return;
- var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
- if (Thumbnails[previewIndex] is null)
- {
- previews[previewIndex] = new byte[encodeLength];
- return;
- }
-
- previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]);
+ previews[previewIndex] = new byte[encodeLength];
+ return;
+ }
- if (encodeLength != previews[previewIndex].Length)
- {
- throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
- }
- });
+ previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]!);
- for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ if (encodeLength != previews[previewIndex].Length)
{
- Helpers.SerializeWriteFileStream(outputFile, previews[i]);
- outputFile.WriteBytes(pageBreak);
- previews[i] = null;
+ throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
}
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ });
+
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ {
+ Helpers.SerializeWriteFileStream(outputFile, previews[i]);
+ outputFile.WriteBytes(pageBreak);
+ previews[i] = null!;
+ }
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- //var preLayers = new PreLayer[LayerCount];
- //var layerDefs = new LayerDef[LayerCount];
- //var layersStreams = new MemoryStream[LayerCount];
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ //var preLayers = new PreLayer[LayerCount];
+ //var layerDefs = new LayerDef[LayerCount];
+ //var layersStreams = new MemoryStream[LayerCount];
- for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- //var layer = this[layerIndex];
- outputFile.WriteBytes(BitExtensions.ToBytesBigEndian((uint)Math.Round(this[layerIndex].BoundingRectangleMillimeters.Area()*1000)));
- //preLayers[layerIndex] = new(layer.NonZeroPixelCount);
- }
- //Helpers.SerializeWriteFileStream(outputFile, preLayers);
- //Helpers.SerializeWriteFileStream(outputFile, pageBreak);
- outputFile.WriteBytes(pageBreak);
+ for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ //var layer = this[layerIndex];
+ outputFile.WriteBytes(BitExtensions.ToBytesBigEndian((uint)Math.Round(this[layerIndex].BoundingRectangleMillimeters.Area()*1000)));
+ //preLayers[layerIndex] = new(layer.NonZeroPixelCount);
+ }
+ //Helpers.SerializeWriteFileStream(outputFile, preLayers);
+ //Helpers.SerializeWriteFileStream(outputFile, pageBreak);
+ outputFile.WriteBytes(pageBreak);
- if (HeaderSettings.Version >= 3)
- {
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoV3Settings);
- }
+ if (HeaderSettings.Version >= 3)
+ {
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoV3Settings);
+ }
- var layerBytes = new List<byte>[LayerCount];
- foreach (var batch in BatchLayersIndexes())
- {
- progress.Token.ThrowIfCancellationRequested();
+ var layerBytes = new List<byte>[LayerCount];
+ foreach (var batch in BatchLayersIndexes())
+ {
+ progress.Token.ThrowIfCancellationRequested();
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = this[layerIndex];
+ using (var mat = layer.LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- var layer = this[layerIndex];
- using (var mat = layer.LayerMat)
- {
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteSpan();
- layerBytes[layerIndex] = new();
+ layerBytes[layerIndex] = new();
- uint lineCount = 0;
+ uint lineCount = 0;
- for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
+ 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 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)
- {
- layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1),
- (ushort)x, lastColor));
- lineCount++;
- }
-
- startY = color == 0 ? -1 : y;
+ int pos = mat.GetPixelPos(x, y);
+ byte color = span[pos];
- lastColor = color;
- }
+ if (lastColor == color && color != 0) continue;
if (startY >= 0)
{
@@ -751,62 +739,62 @@ namespace UVtools.Core.FileFormats
(ushort)x, lastColor));
lineCount++;
}
- }
- layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(
- (uint)Math.Round(layer.BoundingRectangleMillimeters.Area() * 1000),
- lineCount));
- layerBytes[layerIndex].AddRange(pageBreak);
- }
+ startY = color == 0 ? -1 : y;
- progress.LockAndIncrement();
- });
+ lastColor = color;
+ }
- progress.Token.ThrowIfCancellationRequested();
+ if (startY >= 0)
+ {
+ layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1),
+ (ushort)x, lastColor));
+ lineCount++;
+ }
+ }
- foreach (var layerIndex in batch)
- {
- outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
- layerBytes[layerIndex] = null;
+ layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(
+ (uint)Math.Round(layer.BoundingRectangleMillimeters.Area() * 1000),
+ lineCount));
+ layerBytes[layerIndex].AddRange(pageBreak);
}
- }
+ progress.LockAndIncrement();
+ });
- /*Parallel.For(0, LayerCount, CoreSettings.ParallelOptions,
- //new ParallelOptions{MaxDegreeOfParallelism = 1},
- layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- //List<LayerLine> layerLines = new();
- var layer = this[layerIndex];
- using var mat = layer.LayerMat;
- var span = mat.GetDataByteSpan();
+ progress.Token.ThrowIfCancellationRequested();
- layerBytes[layerIndex] = new();
-
- 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];
+ foreach (var layerIndex in batch)
+ {
+ outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
+ layerBytes[layerIndex] = null!;
+ }
+ }
- if (lastColor == color && color != 0) continue;
- if (startY >= 0)
- {
- layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor));
- //layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor));
- //Debug.WriteLine(layerLines[^1]);
- }
+ /*Parallel.For(0, LayerCount, CoreSettings.ParallelOptions,
+ //new ParallelOptions{MaxDegreeOfParallelism = 1},
+ layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ //List<LayerLine> layerLines = new();
+ var layer = this[layerIndex];
+ using var mat = layer.LayerMat;
+ var span = mat.GetDataByteSpan();
- startY = color == 0 ? -1 : y;
+ layerBytes[layerIndex] = new();
+
+ 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];
- lastColor = color;
- }
+ if (lastColor == color && color != 0) continue;
if (startY >= 0)
{
@@ -814,268 +802,279 @@ namespace UVtools.Core.FileFormats
//layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor));
//Debug.WriteLine(layerLines[^1]);
}
+
+ startY = color == 0 ? -1 : y;
+
+ lastColor = color;
}
- //layerDefs[layerIndex] = new LayerDef(layer.NonZeroPixelCount, (uint)layerLines.Count, layerLines.ToArray());
- //var layerDef = new LayerDef(layer.NonZeroPixelCount, (uint)layerLines.Count, layerLines.ToArray());
- //layersStreams[layerIndex] = new MemoryStream();
- //Helpers.Serializer.Serialize(layersStreams[layerIndex], layerDef);
+ if (startY >= 0)
+ {
+ layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor));
+ //layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor));
+ //Debug.WriteLine(layerLines[^1]);
+ }
+ }
- //layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(layer.NonZeroPixelCount, (uint) layerBytes[layerIndex].Count));
- //layerBytes[layerIndex].AddRange(PageBreak.Bytes);
+ //layerDefs[layerIndex] = new LayerDef(layer.NonZeroPixelCount, (uint)layerLines.Count, layerLines.ToArray());
+ //var layerDef = new LayerDef(layer.NonZeroPixelCount, (uint)layerLines.Count, layerLines.ToArray());
+ //layersStreams[layerIndex] = new MemoryStream();
+ //Helpers.Serializer.Serialize(layersStreams[layerIndex], layerDef);
- progress.LockAndIncrement();
- });
+ //layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(layer.NonZeroPixelCount, (uint) layerBytes[layerIndex].Count));
+ //layerBytes[layerIndex].AddRange(PageBreak.Bytes);
- progress.Reset(OperationProgress.StatusWritingFile, LayerCount);
- for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- //Helpers.SerializeWriteFileStream(outputFile, layerDefs[layerIndex]);
- //outputFile.WriteStream(layersStreams[layerIndex]);
- //layersStreams[layerIndex].Dispose();
- outputFile.WriteBytes(LayerDef.GetHeaderBytes(this[layerIndex].NonZeroPixelCount, (uint)layerBytes[layerIndex].Count));
- outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
- outputFile.WriteBytes(pageBreak);
- progress++;
- }*/
+ progress.LockAndIncrement();
+ });
- Helpers.SerializeWriteFileStream(outputFile, FooterSettings);
+ progress.Reset(OperationProgress.StatusWritingFile, LayerCount);
+ for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ //Helpers.SerializeWriteFileStream(outputFile, layerDefs[layerIndex]);
+ //outputFile.WriteStream(layersStreams[layerIndex]);
+ //layersStreams[layerIndex].Dispose();
+ outputFile.WriteBytes(LayerDef.GetHeaderBytes(this[layerIndex].NonZeroPixelCount, (uint)layerBytes[layerIndex].Count));
+ outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
+ outputFile.WriteBytes(pageBreak);
+ progress++;
+ }*/
- progress.Reset("Calculating checksum");
- uint checkSum = CalculateCheckSum(outputFile, false);
+ Helpers.SerializeWriteFileStream(outputFile, FooterSettings);
- outputFile.Write(BitExtensions.ToBytesBigEndian(checkSum));
+ progress.Reset("Calculating checksum");
+ uint checkSum = CalculateCheckSum(outputFile, false);
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine("-End-");
- }
+ outputFile.Write(BitExtensions.ToBytesBigEndian(checkSum));
- protected override void DecodeInternally(OperationProgress progress)
- {
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
- inputFile.Seek(0, SeekOrigin.Begin);
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- Debug.WriteLine(HeaderSettings);
- HeaderSettings.Validate();
+ inputFile.Seek(0, SeekOrigin.Begin);
- var position = inputFile.Position;
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ Debug.WriteLine(HeaderSettings);
+ HeaderSettings.Validate();
- progress.Reset("Validating checksum");
- var expectedCheckSum = CalculateCheckSum(inputFile, false, -4);
- uint checkSum;
- if (HeaderSettings.Version <= 2)
- {
- inputFile.Seek(3, SeekOrigin.Current);
- checkSum = (uint) inputFile.ReadByte();
- }
- else
- {
- checkSum = inputFile.ReadUIntBigEndian();
- }
+ var position = inputFile.Position;
- if (expectedCheckSum != checkSum)
- {
- throw new FileLoadException($"Checksum fails, expecting: {expectedCheckSum} but got: {checkSum}.\n" +
- $"Try to reslice the file.", FileFullPath);
- }
+ progress.Reset("Validating checksum");
+ var expectedCheckSum = CalculateCheckSum(inputFile, false, -4);
+ uint checkSum;
+ if (HeaderSettings.Version <= 2)
+ {
+ inputFile.Seek(3, SeekOrigin.Current);
+ checkSum = (uint) inputFile.ReadByte();
+ }
+ else
+ {
+ checkSum = inputFile.ReadUIntBigEndian();
+ }
- inputFile.Seek(position, SeekOrigin.Begin);
- var 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);
- }
+ if (expectedCheckSum != checkSum)
+ {
+ throw new FileLoadException($"Checksum fails, expecting: {expectedCheckSum} but got: {checkSum}.\n" +
+ $"Try to reslice the file.", FileFullPath);
+ }
- Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
- {
- Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
- previews[previewIndex] = null;
- });
+ inputFile.Seek(position, SeekOrigin.Begin);
+ var 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, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
+ previews[previewIndex] = null!;
+ });
- SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
- Debug.WriteLine(SlicerInfoSettings);
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- inputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
+ SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
+ Debug.WriteLine(SlicerInfoSettings);
+
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ inputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
+
+ if (HeaderSettings.Version >= 3) // New informative header v3
+ {
+ SlicerInfoV3Settings = Helpers.Deserialize<SlicerInfoV3>(inputFile);
+ Debug.WriteLine(SlicerInfoV3Settings);
+ }
- if (HeaderSettings.Version >= 3) // New informative header v3
- {
- SlicerInfoV3Settings = Helpers.Deserialize<SlicerInfoV3>(inputFile);
- Debug.WriteLine(SlicerInfoV3Settings);
- }
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- if (DecodeType == FileDecodeType.Full)
+ var linesBytes = new byte[LayerCount][];
+ foreach (var batch in BatchLayersIndexes())
{
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ progress.Token.ThrowIfCancellationRequested();
- var linesBytes = new byte[LayerCount][];
- foreach (var batch in BatchLayersIndexes())
+ foreach (var layerIndex in batch)
{
- progress.Token.ThrowIfCancellationRequested();
-
- foreach (var layerIndex in batch)
- {
- inputFile.Seek(4, SeekOrigin.Current);
- var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
+ inputFile.Seek(4, SeekOrigin.Current);
+ var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
- linesBytes[layerIndex] = new byte[lineCount * 6];
- inputFile.ReadBytes(linesBytes[layerIndex]);
- inputFile.Seek(2, SeekOrigin.Current);
+ linesBytes[layerIndex] = new byte[lineCount * 6];
+ inputFile.ReadBytes(linesBytes[layerIndex]);
+ inputFile.Seek(2, SeekOrigin.Current);
- progress.Token.ThrowIfCancellationRequested();
- }
+ progress.Token.ThrowIfCancellationRequested();
+ }
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = EmguExtensions.InitMat(Resolution))
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = EmguExtensions.InitMat(Resolution))
- {
- for (int i = 0; i < linesBytes[layerIndex].Length; i++)
+ for (int i = 0; i < linesBytes[layerIndex].Length; i++)
+ {
+ LayerLine line = new()
{
- LayerLine line = new()
+ Coordinates =
{
- Coordinates =
- {
- [0] = linesBytes[layerIndex][i++],
- [1] = linesBytes[layerIndex][i++],
- [2] = linesBytes[layerIndex][i++],
- [3] = linesBytes[layerIndex][i++],
- [4] = linesBytes[layerIndex][i++]
- },
- Gray = linesBytes[layerIndex][i]
- };
-
- CvInvoke.Line(mat, new Point(line.StartX, line.StartY),
- new Point(line.StartX, line.EndY),
- new MCvScalar(line.Gray));
- }
+ [0] = linesBytes[layerIndex][i++],
+ [1] = linesBytes[layerIndex][i++],
+ [2] = linesBytes[layerIndex][i++],
+ [3] = linesBytes[layerIndex][i++],
+ [4] = linesBytes[layerIndex][i++]
+ },
+ Gray = linesBytes[layerIndex][i]
+ };
+
+ CvInvoke.Line(mat, new Point(line.StartX, line.StartY),
+ new Point(line.StartX, line.EndY),
+ new MCvScalar(line.Gray));
+ }
- linesBytes[layerIndex] = null;
+ linesBytes[layerIndex] = null!;
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- }
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
- progress.LockAndIncrement();
- });
- }
- }
- else // Partial read
- {
- inputFile.Seek(-Helpers.Serializer.SizeOf(FooterSettings), SeekOrigin.End);
+ progress.LockAndIncrement();
+ });
}
+ }
+ else // Partial read
+ {
+ inputFile.Seek(-Helpers.Serializer.SizeOf(FooterSettings), SeekOrigin.End);
+ }
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- FooterSettings = Helpers.Deserialize<Footer>(inputFile);
- FooterSettings.Validate();
- }
+ FooterSettings = Helpers.Deserialize<Footer>(inputFile);
+ FooterSettings.Validate();
+ }
- protected override void PartialSaveInternally(OperationProgress progress)
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ var offset = Helpers.Serializer.SizeOf(HeaderSettings);
+ foreach (var size in ThumbnailsOriginalSize!)
{
- var offset = Helpers.Serializer.SizeOf(HeaderSettings);
- foreach (var size in ThumbnailsOriginalSize)
- {
- offset += size.Area() * 2 + 2; // + page break
- }
+ offset += size.Area() * 2 + 2; // + page break
+ }
- SanitizeProperties();
+ SanitizeProperties();
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.ReadWrite);
- outputFile.Seek(offset, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
-
- if (HeaderSettings.Version >= 3)
- {
- outputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoV3Settings);
- }
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.ReadWrite);
+ outputFile.Seek(offset, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
- uint checkSum = CalculateCheckSum(outputFile, false, -4);
- outputFile.WriteBytes(BitExtensions.ToBytesBigEndian(checkSum));
+ if (HeaderSettings.Version >= 3)
+ {
+ outputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoV3Settings);
}
- private uint CalculateCheckSum(FileStream fs, bool restorePosition = true, int offsetSize = 0)
- {
- uint checkSum = 0;
- var position = fs.Position;
- var dataSize = fs.Length + offsetSize;
- const int bufferSize = 50 * 1024 * 1024;
+ uint checkSum = CalculateCheckSum(outputFile, false, -4);
+ outputFile.WriteBytes(BitExtensions.ToBytesBigEndian(checkSum));
+ }
- fs.Seek(0, SeekOrigin.Begin);
+ private uint CalculateCheckSum(FileStream fs, bool restorePosition = true, int offsetSize = 0)
+ {
+ uint checkSum = 0;
+ var position = fs.Position;
+ var dataSize = fs.Length + offsetSize;
+ const int bufferSize = 50 * 1024 * 1024;
+
+ fs.Seek(0, SeekOrigin.Begin);
- if (HeaderSettings.Version >= 3)
+ if (HeaderSettings.Version >= 3)
+ {
+ // https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.Table.cs
+ var table = new uint[256];
+
+ for (uint i = 0; i < 256; i++)
{
- // https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.Table.cs
- var table = new uint[256];
+ uint val = i;
- for (uint i = 0; i < 256; i++)
+ for (int j = 0; j < 8; j++)
{
- uint val = i;
-
- for (int j = 0; j < 8; j++)
+ if ((val & 0b0000_0001) == 0)
{
- if ((val & 0b0000_0001) == 0)
- {
- val >>= 1;
- }
- else
- {
- val = (val >> 1) ^ 0xEDB88320u;
- }
+ val >>= 1;
+ }
+ else
+ {
+ val = (val >> 1) ^ 0xEDB88320u;
}
-
- table[i] = val;
}
- for (
- int chunkSize = (int)Math.Min(bufferSize, dataSize - fs.Position);
- chunkSize > 0;
- chunkSize = (int)Math.Min(chunkSize, dataSize - fs.Position))
+ table[i] = val;
+ }
+
+ for (
+ int chunkSize = (int)Math.Min(bufferSize, dataSize - fs.Position);
+ chunkSize > 0;
+ chunkSize = (int)Math.Min(chunkSize, dataSize - fs.Position))
+ {
+ var bytes = fs.ReadBytes(chunkSize);
+ for (int i = 0; i < bytes.Length; i++)
{
- var bytes = fs.ReadBytes(chunkSize);
- for (int i = 0; i < bytes.Length; i++)
- {
- // https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs
- byte idx = (byte)checkSum;
- idx ^= bytes[i];
- checkSum = table[idx] ^ (checkSum >> 8);
- }
+ // https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs
+ byte idx = (byte)checkSum;
+ idx ^= bytes[i];
+ checkSum = table[idx] ^ (checkSum >> 8);
}
}
- else
+ }
+ else
+ {
+ for (
+ int chunkSize = (int)Math.Min(bufferSize, dataSize - fs.Position);
+ chunkSize > 0;
+ chunkSize = (int)Math.Min(chunkSize, dataSize - fs.Position))
{
- for (
- int chunkSize = (int)Math.Min(bufferSize, dataSize - fs.Position);
- chunkSize > 0;
- chunkSize = (int)Math.Min(chunkSize, dataSize - fs.Position))
+ var bytes = fs.ReadBytes(chunkSize);
+ for (int i = 0; i < bytes.Length; i++)
{
- var bytes = fs.ReadBytes(chunkSize);
- for (int i = 0; i < bytes.Length; i++)
- {
- checkSum ^= bytes[i];
- }
+ checkSum ^= bytes[i];
}
}
+ }
- if (restorePosition) fs.Seek(position, SeekOrigin.Begin);
+ if (restorePosition) fs.Seek(position, SeekOrigin.Begin);
- return checkSum;
+ return checkSum;
- }
-
- #endregion
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/CXDLPv1File.cs b/UVtools.Core/FileFormats/CXDLPv1File.cs
index f3cc5ef..a4bc794 100644
--- a/UVtools.Core/FileFormats/CXDLPv1File.cs
+++ b/UVtools.Core/FileFormats/CXDLPv1File.cs
@@ -6,6 +6,9 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -15,585 +18,570 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class CXDLPv1File : FileFormat
{
- public class CXDLPv1File : FileFormat
+ #region Constants
+ private const byte HEADER_SIZE = 9; // CXSW3DV2
+ private const string HEADER_VALUE = "CXSW3DV2";
+ #endregion
+
+ #region Sub Classes
+ #region Header
+ public sealed class Header
{
- #region Constants
- private const byte HEADER_SIZE = 9; // CXSW3DV2
- private const string HEADER_VALUE = "CXSW3DV2";
- #endregion
-
- #region Sub Classes
- #region Header
- public sealed class Header
+ /// <summary>
+ /// Gets the size of the header
+ /// </summary>
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint HeaderSize { get; set; } = HEADER_SIZE;
+
+ /// <summary>
+ /// Gets the header name
+ /// </summary>
+ [FieldOrder(1)]
+ [FieldLength(HEADER_SIZE)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string HeaderValue { get; set; } = HEADER_VALUE;
+
+ /// <summary>
+ /// Gets the number of records in the layer table
+ /// </summary>
+ [FieldOrder(2)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LayerCount { get; set; }
+
+ /// <summary>
+ /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(3)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort ResolutionX { get; set; }
+
+ /// <summary>
+ /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(4)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort ResolutionY { get; set; }
+
+ public void Validate()
{
- /// <summary>
- /// Gets the size of the header
- /// </summary>
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint HeaderSize { get; set; } = HEADER_SIZE;
-
- /// <summary>
- /// Gets the header name
- /// </summary>
- [FieldOrder(1)]
- [FieldLength(HEADER_SIZE)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string HeaderValue { get; set; } = HEADER_VALUE;
-
- /// <summary>
- /// Gets the number of records in the layer table
- /// </summary>
- [FieldOrder(2)]
- [FieldEndianness(Endianness.Big)]
- public ushort LayerCount { get; set; }
-
- /// <summary>
- /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(3)]
- [FieldEndianness(Endianness.Big)]
- public ushort ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(4)]
- [FieldEndianness(Endianness.Big)]
- public ushort ResolutionY { get; set; }
-
- public void Validate()
- {
- if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE)
- {
- throw new FileLoadException("Not a valid CXDLP file!");
- }
- }
-
- public override string ToString()
+ if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE)
{
- return $"{nameof(HeaderSize)}: {HeaderSize}, {nameof(HeaderValue)}: {HeaderValue}, {nameof(LayerCount)}: {LayerCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}";
+ throw new FileLoadException("Not a valid CXDLP file!");
}
}
- #endregion
-
- #region SlicerInfo
- // Address: 363407
- public sealed class SlicerInfo
+ public override string ToString()
{
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint DisplayWidthDataSize { get; set; } = 20;
+ return $"{nameof(HeaderSize)}: {HeaderSize}, {nameof(HeaderValue)}: {HeaderValue}, {nameof(LayerCount)}: {LayerCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}";
+ }
+ }
- [FieldOrder(1)]
- [FieldLength(nameof(DisplayWidthDataSize))]
- public byte[] DisplayWidthBytes { get; set; }
+ #endregion
- [FieldOrder(2)]
- [FieldEndianness(Endianness.Big)]
- public uint DisplayHeightDataSize { get; set; } = 20;
+ #region SlicerInfo
+ // Address: 363407
+ public sealed class SlicerInfo
+ {
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint DisplayWidthDataSize { get; set; } = 20;
- [FieldOrder(3)]
- [FieldLength(nameof(DisplayHeightDataSize))]
- public byte[] DisplayHeightBytes { get; set; }
+ [FieldOrder(1)]
+ [FieldLength(nameof(DisplayWidthDataSize))]
+ public byte[] DisplayWidthBytes { get; set; } = null!;
- [FieldOrder(4)]
- [FieldEndianness(Endianness.Big)]
- public uint LayerHeightDataSize { get; set; } = 16;
+ [FieldOrder(2)]
+ [FieldEndianness(Endianness.Big)]
+ public uint DisplayHeightDataSize { get; set; } = 20;
- [FieldOrder(5)]
- [FieldLength(nameof(LayerHeightDataSize))]
- public byte[] LayerHeightBytes { get; set; }
+ [FieldOrder(3)]
+ [FieldLength(nameof(DisplayHeightDataSize))]
+ public byte[] DisplayHeightBytes { get; set; } = null!;
- [FieldOrder(6)]
- [FieldEndianness(Endianness.Big)]
- public ushort WaitTimeBeforeCure { get; set; }
+ [FieldOrder(4)]
+ [FieldEndianness(Endianness.Big)]
+ public uint LayerHeightDataSize { get; set; } = 16;
- [FieldOrder(7)]
- [FieldEndianness(Endianness.Big)]
- public ushort ExposureTime { get; set; }
+ [FieldOrder(5)]
+ [FieldLength(nameof(LayerHeightDataSize))]
+ public byte[] LayerHeightBytes { get; set; } = null!;
- [FieldOrder(8)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomExposureTime { get; set; }
+ [FieldOrder(6)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort WaitTimeBeforeCure { get; set; }
- [FieldOrder(9)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLayersCount { get; set; }
+ [FieldOrder(7)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort ExposureTime { get; set; }
- [FieldOrder(10)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLiftHeight { get; set; }
+ [FieldOrder(8)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomExposureTime { get; set; }
- [FieldOrder(11)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLiftSpeed { get; set; }
+ [FieldOrder(9)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLayersCount { get; set; }
- [FieldOrder(12)]
- [FieldEndianness(Endianness.Big)]
- public ushort LiftHeight { get; set; }
+ [FieldOrder(10)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLiftHeight { get; set; }
- [FieldOrder(13)]
- [FieldEndianness(Endianness.Big)]
- public ushort LiftSpeed { get; set; }
+ [FieldOrder(11)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLiftSpeed { get; set; }
- [FieldOrder(14)]
- [FieldEndianness(Endianness.Big)]
- public ushort RetractSpeed { get; set; }
+ [FieldOrder(12)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LiftHeight { get; set; }
- [FieldOrder(15)]
- [FieldEndianness(Endianness.Big)]
- public ushort BottomLightPWM { get; set; } = 255;
+ [FieldOrder(13)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LiftSpeed { get; set; }
- [FieldOrder(16)]
- [FieldEndianness(Endianness.Big)]
- public ushort LightPWM { get; set; } = 255;
+ [FieldOrder(14)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort RetractSpeed { get; set; }
- public override string ToString()
- {
- return $"{nameof(DisplayWidthDataSize)}: {DisplayWidthDataSize}, {nameof(DisplayWidthBytes)}: {DisplayWidthBytes}, {nameof(DisplayHeightDataSize)}: {DisplayHeightDataSize}, {nameof(DisplayHeightBytes)}: {DisplayHeightBytes}, {nameof(LayerHeightDataSize)}: {LayerHeightDataSize}, {nameof(LayerHeightBytes)}: {LayerHeightBytes}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LightPWM)}: {LightPWM}";
- }
- }
- #endregion
+ [FieldOrder(15)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort BottomLightPWM { get; set; } = 255;
- #region Layer Def
+ [FieldOrder(16)]
+ [FieldEndianness(Endianness.Big)]
+ public ushort LightPWM { get; set; } = 255;
- public sealed class PreLayer
+ public override string ToString()
{
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint Unknown { get; set; }
+ return $"{nameof(DisplayWidthDataSize)}: {DisplayWidthDataSize}, {nameof(DisplayWidthBytes)}: {DisplayWidthBytes}, {nameof(DisplayHeightDataSize)}: {DisplayHeightDataSize}, {nameof(DisplayHeightBytes)}: {DisplayHeightBytes}, {nameof(LayerHeightDataSize)}: {LayerHeightDataSize}, {nameof(LayerHeightBytes)}: {LayerHeightBytes}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LightPWM)}: {LightPWM}";
+ }
+ }
+ #endregion
- public PreLayer()
- {
- }
+ #region Layer Def
- public PreLayer(uint unknown)
- {
- Unknown = unknown;
- }
+ public sealed class PreLayer
+ {
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint Unknown { get; set; }
+
+ public PreLayer()
+ {
}
- public sealed class LayerDef
+ public PreLayer(uint unknown)
{
- [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();
+ Unknown = unknown;
+ }
+ }
- public static byte[] GetHeaderBytes(uint unknown, uint lineCount)
- {
- var bytes = new byte[8];
- BitExtensions.ToBytesBigEndian(unknown, bytes);
- BitExtensions.ToBytesBigEndian(lineCount, bytes, 4);
- return bytes;
- }
+ public sealed class LayerDef
+ {
+ public static byte[] GetHeaderBytes(uint unknown, uint lineCount)
+ {
+ var bytes = new byte[8];
+ BitExtensions.ToBytesBigEndian(unknown, bytes);
+ BitExtensions.ToBytesBigEndian(lineCount, bytes, 4);
+ return bytes;
+ }
- public 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; } = Array.Empty<LayerLine>();
+ [FieldOrder(3)] public PageBreak PageBreak { get; set; } = new();
- public LayerDef(uint unknown, uint lineCount, LayerLine[] lines)
- {
- Unknown = unknown;
- LineCount = lineCount;
- Lines = lines;
- }
- }
+ public LayerDef() { }
- public sealed class LayerLine
+ public LayerDef(uint unknown, uint lineCount, LayerLine[] lines)
{
- 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; }
+ Unknown = unknown;
+ LineCount = lineCount;
+ Lines = lines;
+ }
+ }
- [Ignore] public ushort StartY => (ushort)((((Coordinates[0] << 8) + Coordinates[1]) >> 3) & 0x1FFF); // 13 bits
+ 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 EndY => (ushort)((((Coordinates[1] << 16) + (Coordinates[2] << 8) + Coordinates[3]) >> 6) & 0x1FFF); // 13 bits
+ [Ignore] public ushort StartY => (ushort)((((Coordinates[0] << 8) + Coordinates[1]) >> 3) & 0x1FFF); // 13 bits
- [Ignore] public ushort StartX => (ushort)(((Coordinates[3] << 8) + Coordinates[4]) & 0x3FFF); // 14 bits
- [Ignore] public ushort Length => (ushort)(EndY - StartY);
+ [Ignore] public ushort EndY => (ushort)((((Coordinates[1] << 16) + (Coordinates[2] << 8) + Coordinates[3]) >> 6) & 0x1FFF); // 13 bits
- public static byte[] GetBytes(ushort startY, ushort endY, ushort startX, byte gray)
- {
- var bytes = new byte[CoordinateCount + 1];
- bytes[0] = (byte)((startY >> 5) & 0xFF);
- bytes[1] = (byte)(((startY << 3) + (endY >> 10)) & 0xFF);
- bytes[2] = (byte)((endY >> 2) & 0xFF);
- bytes[3] = (byte)(((endY << 6) + (startX >> 8)) & 0xFF);
- bytes[4] = (byte)startX;
- bytes[5] = gray;
- return bytes;
- }
+ [Ignore] public ushort StartX => (ushort)(((Coordinates[3] << 8) + Coordinates[4]) & 0x3FFF); // 14 bits
+ [Ignore] public ushort Length => (ushort)(EndY - StartY);
- public LayerLine() { }
+ public static byte[] GetBytes(ushort startY, ushort endY, ushort startX, byte gray)
+ {
+ var bytes = new byte[CoordinateCount + 1];
+ bytes[0] = (byte)((startY >> 5) & 0xFF);
+ bytes[1] = (byte)(((startY << 3) + (endY >> 10)) & 0xFF);
+ bytes[2] = (byte)((endY >> 2) & 0xFF);
+ bytes[3] = (byte)(((endY << 6) + (startX >> 8)) & 0xFF);
+ bytes[4] = (byte)startX;
+ bytes[5] = gray;
+ return bytes;
+ }
- 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 LayerLine() { }
- public override string ToString()
- {
- return $"{nameof(Gray)}: {Gray}, {nameof(StartY)}: {StartY}, {nameof(EndY)}: {EndY}, {nameof(StartX)}: {StartX}, {nameof(Length)}: {Length}";
- }
+ 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
+ public override string ToString()
{
- public static byte[] Bytes => new byte[] { 0x0D, 0x0A };
-
- [FieldOrder(0)] public byte Line { get; set; } = 0x0D;
- [FieldOrder(1)] public byte Break { get; set; } = 0x0A;
+ return $"{nameof(Gray)}: {Gray}, {nameof(StartY)}: {StartY}, {nameof(EndY)}: {EndY}, {nameof(StartX)}: {StartX}, {nameof(Length)}: {Length}";
}
+ }
- #endregion
+ public sealed class PageBreak
+ {
+ public static byte[] Bytes => new byte[] { 0x0D, 0x0A };
- #region Footer
- public sealed class Footer
+ [FieldOrder(0)] public byte Line { get; set; } = 0x0D;
+ [FieldOrder(1)] public byte Break { get; set; } = 0x0A;
+ }
+
+ #endregion
+
+ #region Footer
+ public sealed class Footer
+ {
+ /// <summary>
+ /// Gets the size of the header
+ /// </summary>
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint FooterSize { get; set; } = HEADER_SIZE;
+
+ /// <summary>
+ /// Gets the header name
+ /// </summary>
+ [FieldOrder(1)]
+ [FieldLength(HEADER_SIZE)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string FooterValue { get; set; } = HEADER_VALUE;
+
+ [FieldOrder(2)]
+ [FieldEndianness(Endianness.Big)]
+ public uint Unknown { get; set; } = 7;
+
+ public void Validate()
{
- /// <summary>
- /// Gets the size of the header
- /// </summary>
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint FooterSize { get; set; } = HEADER_SIZE;
-
- /// <summary>
- /// Gets the header name
- /// </summary>
- [FieldOrder(1)]
- [FieldLength(HEADER_SIZE)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string FooterValue { get; set; } = HEADER_VALUE;
-
- [FieldOrder(2)]
- [FieldEndianness(Endianness.Big)]
- public uint Unknown { get; set; } = 7;
-
- public void Validate()
+ if (FooterSize != HEADER_SIZE || FooterValue != HEADER_VALUE)
{
- if (FooterSize != HEADER_SIZE || FooterValue != HEADER_VALUE)
- {
- throw new FileLoadException("Not a valid CXDLP file!");
- }
+ throw new FileLoadException("Not a valid CXDLP file!");
}
}
- #endregion
+ }
+ #endregion
- #endregion
+ #endregion
- #region Properties
+ #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 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 FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(CXDLPv1File), "v1.cxdlp", "Creality CXDLP v1"),
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(CXDLPv1File), "v1.cxdlp", "Creality CXDLP v1"),
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(116, 116),
- new(290, 290),
- new(290, 290)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(116, 116),
+ new(290, 290),
+ new(290, 290)
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = (ushort)value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = (ushort)value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = (ushort)value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = (ushort)value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayWidthBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayWidthBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.LayerHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
- }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = HeaderSettings.LayerCount = (ushort)base.LayerCount;
+ SlicerInfoSettings.LayerHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override ushort BottomLayerCount
- {
- get => SlicerInfoSettings.BottomLayersCount;
- set => base.BottomLayerCount = SlicerInfoSettings.BottomLayersCount = value;
- }
-
- public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
-
- public override float WaitTimeBeforeCure
- {
- get => SlicerInfoSettings.WaitTimeBeforeCure;
- set => base.WaitTimeBeforeCure = SlicerInfoSettings.WaitTimeBeforeCure = (ushort)value;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = HeaderSettings.LayerCount = (ushort)base.LayerCount;
+ }
- public override float BottomExposureTime
- {
- get => SlicerInfoSettings.BottomExposureTime;
- set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value;
- }
+ public override ushort BottomLayerCount
+ {
+ get => SlicerInfoSettings.BottomLayersCount;
+ set => base.BottomLayerCount = SlicerInfoSettings.BottomLayersCount = value;
+ }
- public override float ExposureTime
- {
- get => SlicerInfoSettings.ExposureTime;
- set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value;
- }
+ public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
- public override float BottomLiftHeight
- {
- get => SlicerInfoSettings.BottomLiftHeight;
- set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value;
- }
+ public override float WaitTimeBeforeCure
+ {
+ get => SlicerInfoSettings.WaitTimeBeforeCure;
+ set => base.WaitTimeBeforeCure = SlicerInfoSettings.WaitTimeBeforeCure = (ushort)value;
+ }
- public override float LiftHeight
- {
- get => SlicerInfoSettings.LiftHeight;
- set => base.LiftHeight = SlicerInfoSettings.LiftHeight = (ushort)value;
- }
+ public override float BottomExposureTime
+ {
+ get => SlicerInfoSettings.BottomExposureTime;
+ set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value;
+ }
- public override float BottomLiftSpeed
- {
- get => SlicerInfoSettings.BottomLiftSpeed;
- set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value;
- }
+ public override float ExposureTime
+ {
+ get => SlicerInfoSettings.ExposureTime;
+ set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value;
+ }
- public override float LiftSpeed
- {
- get => SlicerInfoSettings.LiftSpeed;
- set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value;
- }
+ public override float BottomLiftHeight
+ {
+ get => SlicerInfoSettings.BottomLiftHeight;
+ set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value;
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ public override float LiftHeight
+ {
+ get => SlicerInfoSettings.LiftHeight;
+ set => base.LiftHeight = SlicerInfoSettings.LiftHeight = (ushort)value;
+ }
- public override float RetractSpeed
- {
- get => SlicerInfoSettings.RetractSpeed;
- set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)value;
- }
+ public override float BottomLiftSpeed
+ {
+ get => SlicerInfoSettings.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value;
+ }
- public override byte BottomLightPWM
- {
- get => (byte)SlicerInfoSettings.BottomLightPWM;
- set => base.BottomLightPWM = (byte)(SlicerInfoSettings.BottomLightPWM = value);
- }
+ public override float LiftSpeed
+ {
+ get => SlicerInfoSettings.LiftSpeed;
+ set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value;
+ }
- public override byte LightPWM
- {
- get => (byte)SlicerInfoSettings.LightPWM;
- set => base.LightPWM = (byte)(SlicerInfoSettings.LightPWM = value);
- }
+ public override float BottomRetractSpeed => RetractSpeed;
- public override object[] Configs => new object[] { HeaderSettings, SlicerInfoSettings, FooterSettings };
+ public override float RetractSpeed
+ {
+ get => SlicerInfoSettings.RetractSpeed;
+ set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)value;
+ }
- #endregion
+ public override byte BottomLightPWM
+ {
+ get => (byte)SlicerInfoSettings.BottomLightPWM;
+ set => base.BottomLightPWM = (byte)(SlicerInfoSettings.BottomLightPWM = value);
+ }
- #region Constructors
- #endregion
+ public override byte LightPWM
+ {
+ get => (byte)SlicerInfoSettings.LightPWM;
+ set => base.LightPWM = (byte)(SlicerInfoSettings.LightPWM = value);
+ }
- #region Methods
+ public override object[] Configs => new object[] { HeaderSettings, SlicerInfoSettings, FooterSettings };
- protected override void EncodeInternally(OperationProgress progress)
- {
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
+ #endregion
- if (ResolutionX == 2560 && ResolutionY == 1620)
- {
- MachineName = "CL-60";
- }
- else if (ResolutionX == 3840 && ResolutionY == 2400)
- {
- MachineName = "CL-89";
- }
+ #region Constructors
+ #endregion
- var pageBreak = PageBreak.Bytes;
+ #region Methods
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
- var previews = new byte[ThumbnailsOriginalSize.Length][];
+ if (ResolutionX == 2560 && ResolutionY == 1620)
+ {
+ MachineName = "CL-60";
+ }
+ else if (ResolutionX == 3840 && ResolutionY == 2400)
+ {
+ MachineName = "CL-89";
+ }
- // Previews
- Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
- if (Thumbnails[previewIndex] is null)
- {
- previews[previewIndex] = new byte[encodeLength];
- return;
- }
+ var pageBreak = PageBreak.Bytes;
- previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- if (encodeLength != previews[previewIndex].Length)
- {
- throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
- }
- });
+ var previews = new byte[ThumbnailsOriginalSize!.Length][];
- for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ // Previews
+ Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
+ if (Thumbnails[previewIndex] is null)
{
- Helpers.SerializeWriteFileStream(outputFile, previews[i]);
- outputFile.WriteBytes(pageBreak);
- previews[i] = null;
+ previews[previewIndex] = new byte[encodeLength];
+ return;
}
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
-
+ previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]!);
- for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ if (encodeLength != previews[previewIndex].Length)
{
- outputFile.WriteBytes(BitExtensions.ToBytesBigEndian(this[layerIndex].NonZeroPixelCount));
+ throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
}
- outputFile.WriteBytes(pageBreak);
+ });
- var layerBytes = new List<byte>[LayerCount];
- foreach (var batch in BatchLayersIndexes())
- {
- progress.Token.ThrowIfCancellationRequested();
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ {
+ Helpers.SerializeWriteFileStream(outputFile, previews[i]);
+ outputFile.WriteBytes(pageBreak);
+ previews[i] = null!;
+ }
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- var layer = this[layerIndex];
- using (var mat = layer.LayerMat)
- {
- var span = mat.GetDataByteSpan();
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+
- layerBytes[layerIndex] = new();
+ for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ outputFile.WriteBytes(BitExtensions.ToBytesBigEndian(this[layerIndex].NonZeroPixelCount));
+ }
+ outputFile.WriteBytes(pageBreak);
- uint lineCount = 0;
+ var layerBytes = new List<byte>[LayerCount];
+ foreach (var batch in BatchLayersIndexes())
+ {
+ progress.Token.ThrowIfCancellationRequested();
- 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];
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = this[layerIndex];
+ using (var mat = layer.LayerMat)
+ {
+ var span = mat.GetDataByteSpan();
- if (lastColor == color && color != 0) continue;
+ layerBytes[layerIndex] = new();
- if (startY >= 0)
- {
- layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1),
- (ushort)x, lastColor));
- lineCount++;
- }
+ uint lineCount = 0;
- startY = color == 0 ? -1 : y;
+ 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];
- lastColor = color;
- }
+ if (lastColor == color && color != 0) continue;
if (startY >= 0)
{
@@ -601,141 +589,152 @@ namespace UVtools.Core.FileFormats
(ushort)x, lastColor));
lineCount++;
}
+
+ startY = color == 0 ? -1 : y;
+
+ lastColor = color;
}
- layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(layer.NonZeroPixelCount, lineCount));
- layerBytes[layerIndex].AddRange(pageBreak);
+ if (startY >= 0)
+ {
+ layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1),
+ (ushort)x, lastColor));
+ lineCount++;
+ }
}
- progress.LockAndIncrement();
- });
+ layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(layer.NonZeroPixelCount, lineCount));
+ layerBytes[layerIndex].AddRange(pageBreak);
+ }
- progress.Token.ThrowIfCancellationRequested();
+ progress.LockAndIncrement();
+ });
- foreach (var layerIndex in batch)
- {
- outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
- layerBytes[layerIndex] = null;
- }
+ progress.Token.ThrowIfCancellationRequested();
+
+ foreach (var layerIndex in batch)
+ {
+ outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
+ layerBytes[layerIndex] = null!;
}
+ }
- Helpers.SerializeWriteFileStream(outputFile, FooterSettings);
+ Helpers.SerializeWriteFileStream(outputFile, FooterSettings);
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine(SlicerInfoSettings);
- Debug.WriteLine("-End-");
- }
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(SlicerInfoSettings);
+ Debug.WriteLine("-End-");
+ }
- protected override void DecodeInternally(OperationProgress progress)
- {
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- HeaderSettings.Validate();
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ HeaderSettings.Validate();
- Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(HeaderSettings);
- 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);
- }
+ 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, CoreSettings.ParallelOptions, previewIndex =>
- {
- Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
- previews[previewIndex] = null;
- });
+ Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
+ previews[previewIndex] = null!;
+ });
- SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
- Debug.WriteLine(SlicerInfoSettings);
+ SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
+ Debug.WriteLine(SlicerInfoSettings);
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- inputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ inputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
- if (DecodeType == FileDecodeType.Full)
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ var linesBytes = new byte[LayerCount][];
+ foreach (var batch in BatchLayersIndexes())
{
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- var linesBytes = new byte[LayerCount][];
- foreach (var batch in BatchLayersIndexes())
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- foreach (var layerIndex in batch)
- {
- inputFile.Seek(4, SeekOrigin.Current);
- var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
+ foreach (var layerIndex in batch)
+ {
+ inputFile.Seek(4, SeekOrigin.Current);
+ var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
- linesBytes[layerIndex] = new byte[lineCount * 6];
- inputFile.ReadBytes(linesBytes[layerIndex]);
- inputFile.Seek(2, SeekOrigin.Current);
+ linesBytes[layerIndex] = new byte[lineCount * 6];
+ inputFile.ReadBytes(linesBytes[layerIndex]);
+ inputFile.Seek(2, SeekOrigin.Current);
- progress.Token.ThrowIfCancellationRequested();
- }
+ progress.Token.ThrowIfCancellationRequested();
+ }
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = EmguExtensions.InitMat(Resolution))
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = EmguExtensions.InitMat(Resolution))
+ for (int i = 0; i < linesBytes[layerIndex].Length; i++)
{
- for (int i = 0; i < linesBytes[layerIndex].Length; i++)
+ LayerLine line = new()
{
- LayerLine line = new()
+ Coordinates =
{
- Coordinates =
- {
- [0] = linesBytes[layerIndex][i++],
- [1] = linesBytes[layerIndex][i++],
- [2] = linesBytes[layerIndex][i++],
- [3] = linesBytes[layerIndex][i++],
- [4] = linesBytes[layerIndex][i++]
- },
- Gray = linesBytes[layerIndex][i]
- };
-
- CvInvoke.Line(mat, new Point(line.StartX, line.StartY),
- new Point(line.StartX, line.EndY),
- new MCvScalar(line.Gray));
- }
+ [0] = linesBytes[layerIndex][i++],
+ [1] = linesBytes[layerIndex][i++],
+ [2] = linesBytes[layerIndex][i++],
+ [3] = linesBytes[layerIndex][i++],
+ [4] = linesBytes[layerIndex][i++]
+ },
+ Gray = linesBytes[layerIndex][i]
+ };
+
+ CvInvoke.Line(mat, new Point(line.StartX, line.StartY),
+ new Point(line.StartX, line.EndY),
+ new MCvScalar(line.Gray));
+ }
- linesBytes[layerIndex] = null;
+ linesBytes[layerIndex] = null!;
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- }
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
- progress.LockAndIncrement();
- });
- }
- }
- else // Partial read
- {
- inputFile.Seek(-Helpers.Serializer.SizeOf(FooterSettings), SeekOrigin.End);
+ progress.LockAndIncrement();
+ });
}
+ }
+ else // Partial read
+ {
+ inputFile.Seek(-Helpers.Serializer.SizeOf(FooterSettings), SeekOrigin.End);
+ }
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- FooterSettings = Helpers.Deserialize<Footer>(inputFile);
- FooterSettings.Validate();
- }
+ FooterSettings = Helpers.Deserialize<Footer>(inputFile);
+ FooterSettings.Validate();
+ }
- protected override void PartialSaveInternally(OperationProgress progress)
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ var offset = Helpers.Serializer.SizeOf(HeaderSettings);
+ foreach (var size in ThumbnailsOriginalSize!)
{
- var offset = Helpers.Serializer.SizeOf(HeaderSettings);
- foreach (var size in ThumbnailsOriginalSize)
- {
- offset += size.Area() * 2 + 2; // + page break
- }
-
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(offset, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ offset += size.Area() * 2 + 2; // + page break
}
- #endregion
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(offset, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs
index 2d0d4ba..11d52c8 100644
--- a/UVtools.Core/FileFormats/ChituboxFile.cs
+++ b/UVtools.Core/FileFormats/ChituboxFile.cs
@@ -8,6 +8,9 @@
// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -16,2233 +19,2229 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class ChituboxFile : FileFormat
{
- public class ChituboxFile : FileFormat
- {
- #region Constants
- public const byte USED_VERSION = 3; // 318570521
+ #region Constants
+ public const byte USED_VERSION = 3; // 318570521
- public const uint MAGIC_CBDDLP = 0x12FD0019; // 318570521
- public const uint MAGIC_CTB = 0x12FD0086; // 318570630
- public const uint MAGIC_CTBv4 = 0x12FD0106; // 318570758
- public const ushort REPEATRGB15MASK = 0x20;
+ public const uint MAGIC_CBDDLP = 0x12FD0019; // 318570521
+ public const uint MAGIC_CTB = 0x12FD0086; // 318570630
+ public const uint MAGIC_CTBv4 = 0x12FD0106; // 318570758
+ public const ushort REPEATRGB15MASK = 0x20;
- public const byte RLE8EncodingLimit = 0x7d; // 125;
- public const ushort RLE16EncodingLimit = 0xFFF;
+ public const byte RLE8EncodingLimit = 0x7d; // 125;
+ public const ushort RLE16EncodingLimit = 0xFFF;
- public const uint PERLAYER_SETTINGS_CBDDLP = 8; // 0 or 8 (This disallow per layer settings)
- public const uint PERLAYER_SETTINGS_DISALLOW_NO_AA = 7; // 7 (This disallow per layer settings) no AA
- public const uint PERLAYER_SETTINGS_DISALLOW = 15; // 15 (This disallow per layer settings) with AA
- public const uint PERLAYER_SETTINGS_CTBv2 = 15; // 15 for ctb v2 files and others (This disallow per layer settings)
- public const uint PERLAYER_SETTINGS_CTBv3 = 0x2000000F; // 536870927 for ctb v3 files (This allow per layer settings, while 15 don't)
- public const uint PERLAYER_SETTINGS_CTBv4 = 0x4000000F; // 1073741839 for ctb v4 files (This allow per layer settings, while 15 don't)
+ public const uint PERLAYER_SETTINGS_CBDDLP = 8; // 0 or 8 (This disallow per layer settings)
+ public const uint PERLAYER_SETTINGS_DISALLOW_NO_AA = 7; // 7 (This disallow per layer settings) no AA
+ public const uint PERLAYER_SETTINGS_DISALLOW = 15; // 15 (This disallow per layer settings) with AA
+ public const uint PERLAYER_SETTINGS_CTBv2 = 15; // 15 for ctb v2 files and others (This disallow per layer settings)
+ public const uint PERLAYER_SETTINGS_CTBv3 = 0x2000000F; // 536870927 for ctb v3 files (This allow per layer settings, while 15 don't)
+ public const uint PERLAYER_SETTINGS_CTBv4 = 0x4000000F; // 1073741839 for ctb v4 files (This allow per layer settings, while 15 don't)
- private const string CTBv4_DISCLAIMER = "Layout and record format for the ctb and cbddlp file types are the copyrighted programs or codes of CBD Technology (China) Inc..The Customer or User shall not in any manner reproduce, distribute, modify, decompile, disassemble, decrypt, extract, reverse engineer, lease, assign, or sublicense the said programs or codes.";
- private const ushort CTBv4_DISCLAIMER_SIZE = 320;
- private const ushort CTBv4_RESERVED_SIZE = 384;
-
- #endregion
-
- #region Sub Classes
- #region Header
- public class Header
- {
-
- /// <summary>
- /// Gets a magic number identifying the file type.
- /// 0x12fd_0019 for cbddlp
- /// 0x12fd_0086 for ctb
- /// </summary>
- [FieldOrder(0)] public uint Magic { get; set; }
-
- /// <summary>
- /// Gets the software version
- /// </summary>
- [FieldOrder(1)] public uint Version { get; set; } = USED_VERSION;
-
- /// <summary>
- /// Gets dimensions of the printer’s X output volume, in millimeters.
- /// </summary>
- [FieldOrder(2)] public float BedSizeX { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s Y output volume, in millimeters.
- /// </summary>
- [FieldOrder(3)] public float BedSizeY { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s Z output volume, in millimeters.
- /// </summary>
- [FieldOrder(4)] public float BedSizeZ { get; set; }
-
- [FieldOrder(5)] public uint Unknown1 { get; set; }
- [FieldOrder(6)] public uint Unknown2 { get; set; }
-
- /// <summary>
- /// Gets the height of the model described by this file, in millimeters.
- /// </summary>
- [FieldOrder(7)] public float TotalHeightMilimeter { get; set; }
-
- /// <summary>
- /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(8)] public float LayerHeightMilimeter { get; set; }
-
- /// <summary>
- /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(9)] public float LayerExposureSeconds { get; set; }
-
- /// <summary>
- /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(10)] public float BottomExposureSeconds { get; set; }
-
- /// <summary>
- /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(11)] public float LightOffDelay { get; set; }
-
- /// <summary>
- /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig..
- /// </summary>
- [FieldOrder(12)] public uint BottomLayersCount { get; set; } = DefaultBottomLayerCount;
-
- /// <summary>
- /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(13)] public uint ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(14)] public uint ResolutionY { get; set; }
-
- /// <summary>
- /// Gets the file offsets of ImageHeader records describing the larger preview images.
- /// </summary>
- [FieldOrder(15)] public uint PreviewLargeOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer.
- /// </summary>
- [FieldOrder(16)] public uint LayersDefinitionOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the number of records in the layer table for the first level set. In ctb files, that’s equivalent to the total number of records, but records may be multiplied in antialiased cbddlp files.
- /// </summary>
- [FieldOrder(17)] public uint LayerCount { get; set; }
-
- /// <summary>
- /// Gets the file offsets of ImageHeader records describing the smaller preview images.
- /// </summary>
- [FieldOrder(18)] public uint PreviewSmallOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the estimated duration of print, in seconds.
- /// </summary>
- [FieldOrder(19)] public uint PrintTime { get; set; }
-
- /// <summary>
- /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose.
- /// </summary>
- [FieldOrder(20)] public uint ProjectorType { get; set; }
-
- /// <summary>
- /// Gets the print parameters table offset
- /// </summary>
- [FieldOrder(21)] public uint PrintParametersOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the print parameters table size in bytes.
- /// </summary>
- [FieldOrder(22)] public uint PrintParametersSize { get; set; }
-
- /// <summary>
- /// Gets the number of times each layer image is repeated in the file.
- /// This is used to implement antialiasing in cbddlp files. When greater than 1,
- /// the layer table will actually contain layer_table_count * level_set_count entries.
- /// See the section on antialiasing for details.
- /// 1 for ctb
- /// </summary>
- [FieldOrder(23)] public uint AntiAliasLevel { get; set; } = 1;
-
- /// <summary>
- /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively.
- /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
- /// </summary>
- [FieldOrder(24)] public ushort LightPWM { get; set; } = DefaultLightPWM;
-
- /// <summary>
- /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively.
- /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
- /// </summary>
- [FieldOrder(25)] public ushort BottomLightPWM { get; set; } = DefaultBottomLightPWM;
-
- /// <summary>
- /// Gets the key used to encrypt layer data, or 0 if encryption is not used.
- /// </summary>
- [FieldOrder(26)] public uint EncryptionKey { get; set; }
-
- /// <summary>
- /// Gets the slicer tablet offset
- /// </summary>
- [FieldOrder(27)] public uint SlicerOffset { get; set; }
-
- /// <summary>
- /// Gets the slicer table size in bytes
- /// </summary>
- [FieldOrder(28)] public uint SlicerSize { get; set; }
-
- public override string ToString()
- {
- return $"{nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(TotalHeightMilimeter)}: {TotalHeightMilimeter}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(LayerCount)}: {LayerCount}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(PrintParametersOffsetAddress)}: {PrintParametersOffsetAddress}, {nameof(PrintParametersSize)}: {PrintParametersSize}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(SlicerOffset)}: {SlicerOffset}, {nameof(SlicerSize)}: {SlicerSize}";
- }
- }
- #endregion
-
- #region PrintParameters
- public class PrintParameters
- {
- /// <summary>
- /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters.
- /// </summary>
- [FieldOrder(0)] public float BottomLiftHeight { get; set; } = DefaultBottomLiftHeight;
-
- /// <summary>
- /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute.
- /// </summary>
- [FieldOrder(1)] public float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed;
-
- /// <summary>
- /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters.
- /// </summary>
- [FieldOrder(2)] public float LiftHeight { get; set; } = DefaultLayerHeight;
-
- /// <summary>
- /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute.
- /// </summary>
- [FieldOrder(3)] public float LiftSpeed { get; set; } = DefaultLiftSpeed;
-
- /// <summary>
- /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute.
- /// </summary>
- [FieldOrder(4)] public float RetractSpeed { get; set; } = DefaultRetractSpeed;
-
- /// <summary>
- /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model.
- /// </summary>
- [FieldOrder(5)] public float VolumeMl { get; set; }
-
- /// <summary>
- /// Gets the estimated grams, derived from volume using configured factors for density.
- /// </summary>
- [FieldOrder(6)] public float WeightG { get; set; }
-
- /// <summary>
- /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost.
- /// </summary>
- [FieldOrder(7)] public float CostDollars { get; set; }
-
- /// <summary>
- /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(8)] public float BottomLightOffDelay { get; set; }
-
- /// <summary>
- /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(9)] public float LightOffDelay { get; set; }
-
- /// <summary>
- /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(10)] public uint BottomLayerCount { get; set; } = DefaultBottomLayerCount;
- [FieldOrder(11)] public uint Padding1 { get; set; }
- [FieldOrder(12)] public uint Padding2 { get; set; }
- [FieldOrder(13)] public uint Padding3 { get; set; }
- [FieldOrder(14)] public uint Padding4 { get; set; }
-
- public override string ToString()
- {
- return $"{nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}";
- }
- }
- #endregion
-
- #region SlicerInfo
-
- public class SlicerInfo
- {
- private string _machineName = DefaultMachineName;
- [FieldOrder(0)] public float BottomLiftHeight2 { get; set; }
- [FieldOrder(1)] public float BottomLiftSpeed2 { get; set; }
- [FieldOrder(2)] public float LiftHeight2 { get; set; }
- [FieldOrder(3)] public float LiftSpeed2 { get; set; }
- [FieldOrder(4)] public float RetractHeight2 { get; set; }
- [FieldOrder(5)] public float RetractSpeed2 { get; set; }
- [FieldOrder(6)] public float RestTimeAfterLift { get; set; }
-
- /// <summary>
- /// Gets the machine name offset to a string naming the machine type, and its length in bytes.
- /// </summary>
- [FieldOrder(7)] public uint MachineNameAddress { get; set; }
-
- /// <summary>
- /// Gets the machine size in bytes
- /// </summary>
- [FieldOrder(8)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
-
- /// <summary>
- /// Gets the parameter used to control encryption.
- /// Not totally understood. 0/8 for cbddlp files, 0xF (15) for ctb files, 0x2000000F (536870927) for v3 ctb and 1073741839 for v4 ctb files to allow per layer parameters
- /// </summary>
- [FieldOrder(9)] public uint PerLayerSettings { get; set; } = PERLAYER_SETTINGS_DISALLOW;
-
- /// <summary>
- /// Gets the minutes since Jan 1, 1970 UTC
- /// </summary>
- [FieldOrder(10)] public uint ModifiedTimestampMinutes { get; set; } = (uint)DateTimeExtensions.Timestamp.TotalMinutes;
-
- [Ignore] public string ModifiedDate => DateTimeExtensions.GetDateTimeFromTimestampMinutes(ModifiedTimestampMinutes).ToString("dd/MM/yyyy HH:mm");
-
- /// <summary>
- /// Gets the user-selected antialiasing level. For cbddlp files this will match the level_set_count. For ctb files, this number is essentially arbitrary.
- /// </summary>
- [FieldOrder(11)] public uint AntiAliasLevel { get; set; } = 1;
-
- /// <summary>
- /// Gets a version of software that generated this file, encoded with major, minor, and patch release in bytes starting from the MSB down.
- /// (No provision is made to name the software being used, so this assumes that only one software package can generate the files.
- /// Probably best to hardcode it at 0x01060300.)
- /// </summary>17170480
- [FieldOrder(12)] public uint SoftwareVersion { get; set; } = 0x1090000; // ctb v3 = 0x1060300 (1.6.3) | ctb v4 = 0x1090000 (1.9.0)
- [FieldOrder(13)] public float RestTimeAfterRetract { get; set; }
- [FieldOrder(14)] public float RestTimeAfterLift2 { get; set; }
- [FieldOrder(15)] public uint TransitionLayerCount { get; set; } // CTB not all printers
- [FieldOrder(16)] public uint PrintParametersV4Address { get; set; } // V4 Only
- [FieldOrder(17)] public uint Padding2 { get; set; }
- [FieldOrder(18)] public uint Padding3 { get; set; }
-
- /// <summary>
- /// Gets the machine name. string is not nul-terminated.
- /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only.
- /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine.
- /// </summary>
- [FieldOrder(19)]
- [FieldLength(nameof(MachineNameSize))]
- public string MachineName
- {
- get => _machineName;
- set
- {
- if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
- _machineName = value;
- MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
- }
-
- }
+ private const string CTBv4_DISCLAIMER = "Layout and record format for the ctb and cbddlp file types are the copyrighted programs or codes of CBD Technology (China) Inc..The Customer or User shall not in any manner reproduce, distribute, modify, decompile, disassemble, decrypt, extract, reverse engineer, lease, assign, or sublicense the said programs or codes.";
+ private const ushort CTBv4_DISCLAIMER_SIZE = 320;
+ private const ushort CTBv4_RESERVED_SIZE = 384;
- public override string ToString()
- {
- return $"{nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(PerLayerSettings)}: {PerLayerSettings}, {nameof(ModifiedTimestampMinutes)}: {ModifiedTimestampMinutes}, {nameof(ModifiedDate)}: {ModifiedDate}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(PrintParametersV4Address)}: {PrintParametersV4Address}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(MachineName)}: {MachineName}";
- }
- }
+ #endregion
- #endregion
+ #region Sub Classes
+ #region Header
+ public class Header
+ {
- #region PrintParametersV4
- public sealed class PrintParametersV4
- {
- /*[FieldOrder(0)]
- [FieldLength(nameof(DisclaimerLength))]
- public string Disclaimer { get; set; } = CTBv4_DISCLAIMER; // 320 bytes
- */
+ /// <summary>
+ /// Gets a magic number identifying the file type.
+ /// 0x12fd_0019 for cbddlp
+ /// 0x12fd_0086 for ctb
+ /// </summary>
+ [FieldOrder(0)] public uint Magic { get; set; }
- [FieldOrder(1)]
- public float BottomRetractSpeed { get; set; }
+ /// <summary>
+ /// Gets the software version
+ /// </summary>
+ [FieldOrder(1)] public uint Version { get; set; } = USED_VERSION;
- [FieldOrder(2)]
- public float BottomRetractSpeed2 { get; set; }
+ /// <summary>
+ /// Gets dimensions of the printer’s X output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(2)] public float BedSizeX { get; set; }
- [FieldOrder(3)]
- public uint Padding1 { get; set; }
+ /// <summary>
+ /// Gets dimensions of the printer’s Y output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(3)] public float BedSizeY { get; set; }
- [FieldOrder(4)]
- public float Four1 { get; set; } = 4; // 4?
+ /// <summary>
+ /// Gets dimensions of the printer’s Z output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(4)] public float BedSizeZ { get; set; }
- [FieldOrder(5)]
- public uint Padding2 { get; set; }
+ [FieldOrder(5)] public uint Unknown1 { get; set; }
+ [FieldOrder(6)] public uint Unknown2 { get; set; }
- [FieldOrder(6)]
- public float Four2 { get; set; } = 4; // ?
+ /// <summary>
+ /// Gets the height of the model described by this file, in millimeters.
+ /// </summary>
+ [FieldOrder(7)] public float TotalHeightMilimeter { get; set; }
- [FieldOrder(7)]
- public float RestTimeAfterRetract { get; set; }
+ /// <summary>
+ /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(8)] public float LayerHeightMilimeter { get; set; }
- [FieldOrder(8)]
- public float RestTimeAfterLift { get; set; }
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(9)] public float LayerExposureSeconds { get; set; }
- [FieldOrder(9)]
- public float RestTimeBeforeLift { get; set; }
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(10)] public float BottomExposureSeconds { get; set; }
- [FieldOrder(10)]
- public float BottomRetractHeight2 { get; set; }
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(11)] public float LightOffDelay { get; set; }
- [FieldOrder(11)]
- public float Unknown1 { get; set; } // 2955.996 or uint:1161347054 but changes
+ /// <summary>
+ /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig..
+ /// </summary>
+ [FieldOrder(12)] public uint BottomLayersCount { get; set; } = DefaultBottomLayerCount;
- [FieldOrder(12)]
- public uint Unknown2 { get; set; } // 73470 but changes
+ /// <summary>
+ /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(13)] public uint ResolutionX { get; set; }
- [FieldOrder(13)]
- public uint Unknown3 { get; set; } = 5; // 5?
+ /// <summary>
+ /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(14)] public uint ResolutionY { get; set; }
- [FieldOrder(14)]
- public uint LastLayerIndex { get; set; }
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the larger preview images.
+ /// </summary>
+ [FieldOrder(15)] public uint PreviewLargeOffsetAddress { get; set; }
- [FieldOrder(15)]
- public uint Padding3 { get; set; }
+ /// <summary>
+ /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer.
+ /// </summary>
+ [FieldOrder(16)] public uint LayersDefinitionOffsetAddress { get; set; }
- [FieldOrder(16)]
- public uint Padding4 { get; set; }
+ /// <summary>
+ /// Gets the number of records in the layer table for the first level set. In ctb files, that’s equivalent to the total number of records, but records may be multiplied in antialiased cbddlp files.
+ /// </summary>
+ [FieldOrder(17)] public uint LayerCount { get; set; }
- [FieldOrder(17)]
- public uint Padding5 { get; set; }
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the smaller preview images.
+ /// </summary>
+ [FieldOrder(18)] public uint PreviewSmallOffsetAddress { get; set; }
- [FieldOrder(18)]
- public uint Padding6 { get; set; }
+ /// <summary>
+ /// Gets the estimated duration of print, in seconds.
+ /// </summary>
+ [FieldOrder(19)] public uint PrintTime { get; set; }
- [FieldOrder(19)]
- public uint DisclaimerAddress { get; set; }
+ /// <summary>
+ /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose.
+ /// </summary>
+ [FieldOrder(20)] public uint ProjectorType { get; set; }
- [FieldOrder(20)] public uint DisclaimerLength { get; set; } = CTBv4_DISCLAIMER_SIZE;
+ /// <summary>
+ /// Gets the print parameters table offset
+ /// </summary>
+ [FieldOrder(21)] public uint PrintParametersOffsetAddress { get; set; }
- [FieldOrder(21)]
- [FieldLength(CTBv4_RESERVED_SIZE)]
- public byte[] Reserved { get; set; } = new byte[CTBv4_RESERVED_SIZE]; // 384 bytes
+ /// <summary>
+ /// Gets the print parameters table size in bytes.
+ /// </summary>
+ [FieldOrder(22)] public uint PrintParametersSize { get; set; }
- public override string ToString()
- {
- return $"{nameof(BottomRetractSpeed)}: {BottomRetractSpeed}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Padding1)}: {Padding1}, {nameof(Four1)}: {Four1}, {nameof(Padding2)}: {Padding2}, {nameof(Four2)}: {Four2}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(BottomRetractHeight2)}: {BottomRetractHeight2}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(LastLayerIndex)}: {LastLayerIndex}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(DisclaimerAddress)}: {DisclaimerAddress}, {nameof(DisclaimerLength)}: {DisclaimerLength}, {nameof(Reserved)}: {Reserved}";
- }
+ /// <summary>
+ /// Gets the number of times each layer image is repeated in the file.
+ /// This is used to implement antialiasing in cbddlp files. When greater than 1,
+ /// the layer table will actually contain layer_table_count * level_set_count entries.
+ /// See the section on antialiasing for details.
+ /// 1 for ctb
+ /// </summary>
+ [FieldOrder(23)] public uint AntiAliasLevel { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(24)] public ushort LightPWM { get; set; } = DefaultLightPWM;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(25)] public ushort BottomLightPWM { get; set; } = DefaultBottomLightPWM;
+
+ /// <summary>
+ /// Gets the key used to encrypt layer data, or 0 if encryption is not used.
+ /// </summary>
+ [FieldOrder(26)] public uint EncryptionKey { get; set; }
+
+ /// <summary>
+ /// Gets the slicer tablet offset
+ /// </summary>
+ [FieldOrder(27)] public uint SlicerOffset { get; set; }
+
+ /// <summary>
+ /// Gets the slicer table size in bytes
+ /// </summary>
+ [FieldOrder(28)] public uint SlicerSize { get; set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(TotalHeightMilimeter)}: {TotalHeightMilimeter}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(LayerCount)}: {LayerCount}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(PrintParametersOffsetAddress)}: {PrintParametersOffsetAddress}, {nameof(PrintParametersSize)}: {PrintParametersSize}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(SlicerOffset)}: {SlicerOffset}, {nameof(SlicerSize)}: {SlicerSize}";
}
- #endregion
+ }
+ #endregion
- #region Preview
+ #region PrintParameters
+ public class PrintParameters
+ {
/// <summary>
- /// 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.
+ /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters.
/// </summary>
- public class Preview
- {
- /// <summary>
- /// Gets the X dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(0)] public uint ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the Y dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(1)] public uint ResolutionY { get; set; }
-
- /// <summary>
- /// Gets the image offset of the encoded data blob.
- /// </summary>
- [FieldOrder(2)] public uint ImageOffset { get; set; }
-
- /// <summary>
- /// Gets the image length in bytes.
- /// </summary>
- [FieldOrder(3)] public uint ImageLength { 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; }
-
- public unsafe Mat Decode(byte[] rawImageData)
- {
- var image = new Mat(new Size((int) ResolutionX, (int) ResolutionY), DepthType.Cv8U, 3);
- var span = image.GetBytePointer();
+ [FieldOrder(0)] public float BottomLiftHeight { get; set; } = DefaultBottomLiftHeight;
- /*var previewSize = ResolutionX * ResolutionY * 2;
- if (previewSize != rawImageData.Length)
- {
- throw new FileLoadException($"Thumbnail out of size, expecting {previewSize} bytes, got {rawImageData.Length}");
- return null;
- }*/
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(1)] public float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed;
- int pixel = 0;
- for (int n = 0; n < rawImageData.Length; n++)
- {
- uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
- //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3);
- byte red = (byte)(((dot >> 11) & 0x1F) << 3);
- byte green = (byte)(((dot >> 6) & 0x1F) << 3);
- byte blue = (byte)((dot & 0x1F) << 3);
- int repeat = 1;
- if ((dot & 0x0020) == 0x0020)
- {
- repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
- }
+ /// <summary>
+ /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters.
+ /// </summary>
+ [FieldOrder(2)] public float LiftHeight { get; set; } = DefaultLayerHeight;
- for (int j = 0; j < repeat; j++)
- {
- span[pixel++] = blue;
- span[pixel++] = green;
- span[pixel++] = red;
- //span[pixel++] = new Rgba32(red, green, blue);
- }
- }
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(3)] public float LiftSpeed { get; set; } = DefaultLiftSpeed;
- return image;
- }
+ /// <summary>
+ /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(4)] public float RetractSpeed { get; set; } = DefaultRetractSpeed;
- public override string ToString()
- {
- return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
- }
+ /// <summary>
+ /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model.
+ /// </summary>
+ [FieldOrder(5)] public float VolumeMl { get; set; }
- public unsafe byte[] Encode(Mat image)
- {
- List<byte> rawData = new();
- ushort color15 = 0;
- uint rep = 0;
+ /// <summary>
+ /// Gets the estimated grams, derived from volume using configured factors for density.
+ /// </summary>
+ [FieldOrder(6)] public float WeightG { get; set; }
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ /// <summary>
+ /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost.
+ /// </summary>
+ [FieldOrder(7)] public float CostDollars { get; set; }
- void RleRGB15()
- {
- switch (rep)
- {
- case 0:
- return;
- case 1:
- rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
- rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- break;
- case 2:
- for (int i = 0; i < 2; i++)
- {
- rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
- rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- }
-
- break;
- default:
- rawData.Add((byte)(color15 | REPEATRGB15MASK));
- rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
- rawData.Add((byte)((rep - 1) | 0x3000));
- rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
- break;
- }
- }
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(8)] public float BottomLightOffDelay { get; set; }
- int pixel = 0;
- while (pixel < imageLength)
- {
- var ncolor15 =
- // bgr
- (span[pixel++] >> 3) | ((span[pixel++] >> 2) << 5) | ((span[pixel++] >> 3) << 11);
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(9)] public float LightOffDelay { get; set; }
- if (ncolor15 == color15)
- {
- rep++;
- if (rep == RLE16EncodingLimit)
- {
- RleRGB15();
- rep = 0;
- }
- }
- else
- {
- RleRGB15();
- color15 = (ushort) ncolor15;
- rep = 1;
- }
- }
+ /// <summary>
+ /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(10)] public uint BottomLayerCount { get; set; } = DefaultBottomLayerCount;
+ [FieldOrder(11)] public uint Padding1 { get; set; }
+ [FieldOrder(12)] public uint Padding2 { get; set; }
+ [FieldOrder(13)] public uint Padding3 { get; set; }
+ [FieldOrder(14)] public uint Padding4 { get; set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}";
+ }
+ }
+ #endregion
+
+ #region SlicerInfo
+
+ public class SlicerInfo
+ {
+ private string _machineName = DefaultMachineName;
+ [FieldOrder(0)] public float BottomLiftHeight2 { get; set; }
+ [FieldOrder(1)] public float BottomLiftSpeed2 { get; set; }
+ [FieldOrder(2)] public float LiftHeight2 { get; set; }
+ [FieldOrder(3)] public float LiftSpeed2 { get; set; }
+ [FieldOrder(4)] public float RetractHeight2 { get; set; }
+ [FieldOrder(5)] public float RetractSpeed2 { get; set; }
+ [FieldOrder(6)] public float RestTimeAfterLift { get; set; }
+
+ /// <summary>
+ /// Gets the machine name offset to a string naming the machine type, and its length in bytes.
+ /// </summary>
+ [FieldOrder(7)] public uint MachineNameAddress { get; set; }
+
+ /// <summary>
+ /// Gets the machine size in bytes
+ /// </summary>
+ [FieldOrder(8)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
+
+ /// <summary>
+ /// Gets the parameter used to control encryption.
+ /// Not totally understood. 0/8 for cbddlp files, 0xF (15) for ctb files, 0x2000000F (536870927) for v3 ctb and 1073741839 for v4 ctb files to allow per layer parameters
+ /// </summary>
+ [FieldOrder(9)] public uint PerLayerSettings { get; set; } = PERLAYER_SETTINGS_DISALLOW;
- RleRGB15();
+ /// <summary>
+ /// Gets the minutes since Jan 1, 1970 UTC
+ /// </summary>
+ [FieldOrder(10)] public uint ModifiedTimestampMinutes { get; set; } = (uint)DateTimeExtensions.Timestamp.TotalMinutes;
+
+ [Ignore] public string ModifiedDate => DateTimeExtensions.GetDateTimeFromTimestampMinutes(ModifiedTimestampMinutes).ToString("dd/MM/yyyy HH:mm");
+
+ /// <summary>
+ /// Gets the user-selected antialiasing level. For cbddlp files this will match the level_set_count. For ctb files, this number is essentially arbitrary.
+ /// </summary>
+ [FieldOrder(11)] public uint AntiAliasLevel { get; set; } = 1;
- ImageLength = (uint) rawData.Count;
+ /// <summary>
+ /// Gets a version of software that generated this file, encoded with major, minor, and patch release in bytes starting from the MSB down.
+ /// (No provision is made to name the software being used, so this assumes that only one software package can generate the files.
+ /// Probably best to hardcode it at 0x01060300.)
+ /// </summary>17170480
+ [FieldOrder(12)] public uint SoftwareVersion { get; set; } = 0x1090000; // ctb v3 = 0x1060300 (1.6.3) | ctb v4 = 0x1090000 (1.9.0)
+ [FieldOrder(13)] public float RestTimeAfterRetract { get; set; }
+ [FieldOrder(14)] public float RestTimeAfterLift2 { get; set; }
+ [FieldOrder(15)] public uint TransitionLayerCount { get; set; } // CTB not all printers
+ [FieldOrder(16)] public uint PrintParametersV4Address { get; set; } // V4 Only
+ [FieldOrder(17)] public uint Padding2 { get; set; }
+ [FieldOrder(18)] public uint Padding3 { get; set; }
- return rawData.ToArray();
+ /// <summary>
+ /// Gets the machine name. string is not nul-terminated.
+ /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only.
+ /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine.
+ /// </summary>
+ [FieldOrder(19)]
+ [FieldLength(nameof(MachineNameSize))]
+ public string MachineName
+ {
+ get => _machineName;
+ set
+ {
+ if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
+ _machineName = value;
+ MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
}
+
}
- #endregion
-
- #region Layer
- public class LayerDef
+ public override string ToString()
{
- public const byte TABLE_SIZE = 36;
+ return $"{nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(PerLayerSettings)}: {PerLayerSettings}, {nameof(ModifiedTimestampMinutes)}: {ModifiedTimestampMinutes}, {nameof(ModifiedDate)}: {ModifiedDate}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(PrintParametersV4Address)}: {PrintParametersV4Address}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(MachineName)}: {MachineName}";
+ }
+ }
- /// <summary>
- /// Gets the build platform Z position for this layer, measured in millimeters.
- /// </summary>
- [FieldOrder(0)] public float PositionZ { get; set; }
+ #endregion
- /// <summary>
- /// Gets the exposure time for this layer, in seconds.
- /// </summary>
- [FieldOrder(1)] public float ExposureTime { get; set; }
+ #region PrintParametersV4
+ public sealed class PrintParametersV4
+ {
+ /*[FieldOrder(0)]
+ [FieldLength(nameof(DisclaimerLength))]
+ public string Disclaimer { get; set; } = CTBv4_DISCLAIMER; // 320 bytes
+ */
- /// <summary>
- /// Gets how long to keep the light off after exposing this layer, in seconds.
- /// </summary>
- [FieldOrder(2)] public float LightOffSeconds { get; set; }
+ [FieldOrder(1)]
+ public float BottomRetractSpeed { get; set; }
- /// <summary>
- /// Gets the layer image offset to encoded layer data, and its length in bytes.
- /// </summary>
- [FieldOrder(3)] public uint DataAddress { get; set; }
+ [FieldOrder(2)]
+ public float BottomRetractSpeed2 { get; set; }
- /// <summary>
- /// Gets the layer image length in bytes.
- /// </summary>
- [FieldOrder(4)] public uint DataSize { get; set; }
- [FieldOrder(5)] public uint Unknown1 { get; set; }
- [FieldOrder(6)] public uint TableSize { get; set; } = TABLE_SIZE;
- [FieldOrder(7)] public uint Unknown3 { get; set; }
- [FieldOrder(8)] public uint Unknown4 { get; set; }
+ [FieldOrder(3)]
+ public uint Padding1 { get; set; }
+ [FieldOrder(4)]
+ public float Four1 { get; set; } = 4; // 4?
- [Ignore] public byte[] EncodedRle { get; set; }
- [Ignore] public ChituboxFile Parent { get; set; }
+ [FieldOrder(5)]
+ public uint Padding2 { get; set; }
- public LayerDef() { }
+ [FieldOrder(6)]
+ public float Four2 { get; set; } = 4; // ?
- public LayerDef(ChituboxFile parent, Layer layer)
- {
- Parent = parent;
- SetFrom(layer);
+ [FieldOrder(7)]
+ public float RestTimeAfterRetract { get; set; }
- if (parent.HeaderSettings.Version >= 3)
- {
- TableSize += LayerDefEx.TABLE_SIZE;
- }
-
- }
+ [FieldOrder(8)]
+ public float RestTimeAfterLift { get; set; }
- public void SetFrom(Layer layer)
- {
- PositionZ = layer.PositionZ;
- ExposureTime = layer.ExposureTime;
- LightOffSeconds = layer.LightOffDelay;
- }
+ [FieldOrder(9)]
+ public float RestTimeBeforeLift { get; set; }
- public void CopyTo(Layer layer)
- {
- layer.PositionZ = PositionZ;
- layer.ExposureTime = ExposureTime;
- layer.LightOffDelay = LightOffSeconds;
- }
+ [FieldOrder(10)]
+ public float BottomRetractHeight2 { get; set; }
+ [FieldOrder(11)]
+ public float Unknown1 { get; set; } // 2955.996 or uint:1161347054 but changes
- public Mat Decode(uint layerIndex, bool consumeData = true)
- {
- var image = Parent.IsCtbFile ? DecodeCtbImage(layerIndex) : DecodeCbddlpImage(Parent, layerIndex);
+ [FieldOrder(12)]
+ public uint Unknown2 { get; set; } // 73470 but changes
- if (consumeData)
- {
- for (byte aaIndex = 0; aaIndex < Parent.HeaderSettings.AntiAliasLevel; aaIndex++)
- {
- Parent.LayerDefinitions[aaIndex, layerIndex].EncodedRle = null;
- }
- }
+ [FieldOrder(13)]
+ public uint Unknown3 { get; set; } = 5; // 5?
- return image;
- }
+ [FieldOrder(14)]
+ public uint LastLayerIndex { get; set; }
- public static unsafe Mat DecodeCbddlpImage(ChituboxFile parent, uint layerIndex)
- {
- var image = EmguExtensions.InitMat(parent.Resolution);
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ [FieldOrder(15)]
+ public uint Padding3 { get; set; }
- for (byte bit = 0; bit < parent.AntiAliasing; bit++)
- {
- var layer = parent.LayerDefinitions[bit, layerIndex];
+ [FieldOrder(16)]
+ public uint Padding4 { get; set; }
- int n = 0;
- for (int index = 0; index < layer.DataSize; index++)
- {
- // Lower 7 bits is the repeat count for the bit (0..127)
- int reps = layer.EncodedRle[index] & 0x7f;
+ [FieldOrder(17)]
+ public uint Padding5 { get; set; }
- // We only need to set the non-zero pixels
- // High bit is on for white, off for black
- if ((layer.EncodedRle[index] & 0x80) != 0)
- {
- for (int i = 0; i < reps; i++)
- {
- span[n + i]++;
- }
- }
+ [FieldOrder(18)]
+ public uint Padding6 { get; set; }
- n += reps;
+ [FieldOrder(19)]
+ public uint DisclaimerAddress { get; set; }
- if (n == imageLength)
- {
- break;
- }
+ [FieldOrder(20)] public uint DisclaimerLength { get; set; } = CTBv4_DISCLAIMER_SIZE;
- if (n > imageLength)
- {
- image.Dispose();
- throw new FileLoadException("Error image ran off the end");
- }
- }
- }
+ [FieldOrder(21)]
+ [FieldLength(CTBv4_RESERVED_SIZE)]
+ public byte[] Reserved { get; set; } = new byte[CTBv4_RESERVED_SIZE]; // 384 bytes
- for (int i = 0; i < imageLength; i++)
- {
- int newC = span[i] * (256 / parent.AntiAliasing);
+ public override string ToString()
+ {
+ return $"{nameof(BottomRetractSpeed)}: {BottomRetractSpeed}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Padding1)}: {Padding1}, {nameof(Four1)}: {Four1}, {nameof(Padding2)}: {Padding2}, {nameof(Four2)}: {Four2}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(BottomRetractHeight2)}: {BottomRetractHeight2}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(LastLayerIndex)}: {LastLayerIndex}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(DisclaimerAddress)}: {DisclaimerAddress}, {nameof(DisclaimerLength)}: {DisclaimerLength}, {nameof(Reserved)}: {Reserved}";
+ }
+ }
+ #endregion
+
+ #region Preview
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class Preview
+ {
+ /// <summary>
+ /// Gets the X dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(0)] public uint ResolutionX { get; set; }
- if (newC > 0)
- {
- newC--;
- }
+ /// <summary>
+ /// Gets the Y dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(1)] public uint ResolutionY { get; set; }
- span[i] = (byte) newC;
- }
+ /// <summary>
+ /// Gets the image offset of the encoded data blob.
+ /// </summary>
+ [FieldOrder(2)] public uint ImageOffset { get; set; }
- return image;
- }
+ /// <summary>
+ /// Gets the image length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint ImageLength { get; set; }
- private Mat DecodeCtbImage(uint layerIndex)
- {
- var mat = EmguExtensions.InitMat(Parent.Resolution);
- //var span = mat.GetBytePointer();
+ [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; }
- if (Parent.HeaderSettings.EncryptionKey > 0)
+ public unsafe Mat Decode(byte[] rawImageData)
+ {
+ var image = new Mat(new Size((int) ResolutionX, (int) ResolutionY), DepthType.Cv8U, 3);
+ var span = image.GetBytePointer();
+
+ /*var previewSize = ResolutionX * ResolutionY * 2;
+ if (previewSize != rawImageData.Length)
+ {
+ throw new FileLoadException($"Thumbnail out of size, expecting {previewSize} bytes, got {rawImageData.Length}");
+ return null;
+ }*/
+
+ int pixel = 0;
+ for (int n = 0; n < rawImageData.Length; n++)
+ {
+ uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
+ //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3);
+ byte red = (byte)(((dot >> 11) & 0x1F) << 3);
+ byte green = (byte)(((dot >> 6) & 0x1F) << 3);
+ byte blue = (byte)((dot & 0x1F) << 3);
+ int repeat = 1;
+ if ((dot & 0x0020) == 0x0020)
{
- LayerRleCryptBuffer(Parent.HeaderSettings.EncryptionKey, layerIndex, EncodedRle);
+ repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
}
- int pixel = 0;
- for (var n = 0; n < EncodedRle.Length; n++)
+ for (int j = 0; j < repeat; j++)
{
- byte code = EncodedRle[n];
- int stride = 1;
+ span[pixel++] = blue;
+ span[pixel++] = green;
+ span[pixel++] = red;
+ //span[pixel++] = new Rgba32(red, green, blue);
+ }
+ }
- if ((code & 0x80) == 0x80) // It's a run
- {
- code &= 0x7f; // Get the run length
- n++;
+ return image;
+ }
- var slen = EncodedRle[n];
+ public override string ToString()
+ {
+ return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
+ }
- if ((slen & 0x80) == 0)
- {
- stride = slen;
- }
- else if ((slen & 0xc0) == 0x80)
- {
- stride = ((slen & 0x3f) << 8) + EncodedRle[n + 1];
- n++;
- }
- else if ((slen & 0xe0) == 0xc0)
- {
- stride = ((slen & 0x1f) << 16) + (EncodedRle[n + 1] << 8) + EncodedRle[n + 2];
- n += 2;
- }
- else if ((slen & 0xf0) == 0xe0)
- {
- stride = ((slen & 0xf) << 24) + (EncodedRle[n + 1] << 16) + (EncodedRle[n + 2] << 8) + EncodedRle[n + 3];
- n += 3;
- }
- else
+ public unsafe byte[] Encode(Mat image)
+ {
+ List<byte> rawData = new();
+ ushort color15 = 0;
+ uint rep = 0;
+
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
+
+ void RleRGB15()
+ {
+ switch (rep)
+ {
+ case 0:
+ return;
+ case 1:
+ rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
+ rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
+ break;
+ case 2:
+ for (int i = 0; i < 2; i++)
{
- mat.Dispose();
- throw new FileLoadException("Corrupted RLE data");
+ rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
+ rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
}
- }
- // Bit extend from 7-bit to 8-bit greymap
- if (code != 0)
+ break;
+ default:
+ rawData.Add((byte)(color15 | REPEATRGB15MASK));
+ rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
+ rawData.Add((byte)((rep - 1) | 0x3000));
+ rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
+ break;
+ }
+ }
+
+ int pixel = 0;
+ while (pixel < imageLength)
+ {
+ var ncolor15 =
+ // bgr
+ (span[pixel++] >> 3) | ((span[pixel++] >> 2) << 5) | ((span[pixel++] >> 3) << 11);
+
+ if (ncolor15 == color15)
+ {
+ rep++;
+ if (rep == RLE16EncodingLimit)
{
- code = (byte)((code << 1) | 1);
+ RleRGB15();
+ rep = 0;
}
+ }
+ else
+ {
+ RleRGB15();
+ color15 = (ushort) ncolor15;
+ rep = 1;
+ }
+ }
- mat.FillSpan(ref pixel, stride, code);
+ RleRGB15();
- //if (stride <= 0) continue; // Nothing to do
+ ImageLength = (uint) rawData.Count;
- /*if (code == 0) // Ignore blacks, spare cycles
- {
- pixel += stride;
- continue;
- }*/
+ return rawData.ToArray();
+ }
+ }
- /*for (; stride > 0; stride--)
- {
- span[pixel] = code;
- pixel++;
- }*/
- }
+ #endregion
- return mat;
- }
+ #region Layer
+ public class LayerDef
+ {
+ public const byte TABLE_SIZE = 36;
+
+ /// <summary>
+ /// Gets the build platform Z position for this layer, measured in millimeters.
+ /// </summary>
+ [FieldOrder(0)] public float PositionZ { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time for this layer, in seconds.
+ /// </summary>
+ [FieldOrder(1)] public float ExposureTime { get; set; }
+
+ /// <summary>
+ /// Gets how long to keep the light off after exposing this layer, in seconds.
+ /// </summary>
+ [FieldOrder(2)] public float LightOffSeconds { get; set; }
+
+ /// <summary>
+ /// Gets the layer image offset to encoded layer data, and its length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint DataAddress { get; set; }
+
+ /// <summary>
+ /// Gets the layer image length in bytes.
+ /// </summary>
+ [FieldOrder(4)] public uint DataSize { get; set; }
+ [FieldOrder(5)] public uint Unknown1 { get; set; }
+ [FieldOrder(6)] public uint TableSize { get; set; } = TABLE_SIZE;
+ [FieldOrder(7)] public uint Unknown3 { get; set; }
+ [FieldOrder(8)] public uint Unknown4 { get; set; }
+
+
+ [Ignore] public byte[]? EncodedRle { get; set; }
+ [Ignore] public ChituboxFile? Parent { get; set; }
+
+ public LayerDef() { }
- public byte[] Encode(Mat image, byte aaIndex, uint layerIndex)
+ public LayerDef(ChituboxFile parent, Layer layer)
+ {
+ Parent = parent;
+ SetFrom(layer);
+
+ if (parent.HeaderSettings.Version >= 3)
{
- return Parent.IsCtbFile ? EncodeCtbImage(image, layerIndex) : EncodeCbddlpImage(image, aaIndex);
+ TableSize += LayerDefEx.TABLE_SIZE;
}
+
+ }
- public unsafe byte[] EncodeCbddlpImage(Mat image, byte bit)
+ public void SetFrom(Layer layer)
+ {
+ PositionZ = layer.PositionZ;
+ ExposureTime = layer.ExposureTime;
+ LightOffSeconds = layer.LightOffDelay;
+ }
+
+ public void CopyTo(Layer layer)
+ {
+ layer.PositionZ = PositionZ;
+ layer.ExposureTime = ExposureTime;
+ layer.LightOffDelay = LightOffSeconds;
+ }
+
+
+ public Mat Decode(uint layerIndex, bool consumeData = true)
+ {
+ var image = Parent!.IsCtbFile ? DecodeCtbImage(layerIndex) : DecodeCbddlpImage(Parent, layerIndex);
+
+ if (consumeData)
{
- List<byte> rawData = new();
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
-
- bool obit = false;
- int rep = 0;
-
- //ngrey:= uint16(r | g | b)
- // thresholds:
- // aa 1: 127
- // aa 2: 255 127
- // aa 4: 255 191 127 63
- // aa 8: 255 223 191 159 127 95 63 31
- byte threshold = (byte)(256 / Parent.AntiAliasing * bit - 1);
-
- void AddRep()
+ for (byte aaIndex = 0; aaIndex < Parent.HeaderSettings.AntiAliasLevel; aaIndex++)
{
- if (rep <= 0) return;
+ Parent.LayerDefinitions![aaIndex, layerIndex].EncodedRle = null;
+ }
+ }
- byte by = (byte)rep;
+ return image;
+ }
- if (obit)
- {
- by |= 0x80;
- //bitsOn += uint(rep)
- }
+ public static unsafe Mat DecodeCbddlpImage(ChituboxFile parent, uint layerIndex)
+ {
+ var image = EmguExtensions.InitMat(parent.Resolution);
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
- rawData.Add(by);
- }
+ for (byte bit = 0; bit < parent.AntiAliasing; bit++)
+ {
+ var layer = parent.LayerDefinitions![bit, layerIndex];
- for (int pixel = 0; pixel < imageLength; pixel++)
+ int n = 0;
+ for (int index = 0; index < layer.DataSize; index++)
{
- var nbit = span[pixel] >= threshold;
+ // Lower 7 bits is the repeat count for the bit (0..127)
+ int reps = layer.EncodedRle![index] & 0x7f;
- if (nbit == obit)
+ // We only need to set the non-zero pixels
+ // High bit is on for white, off for black
+ if ((layer.EncodedRle[index] & 0x80) != 0)
{
- rep++;
-
- if (rep == RLE8EncodingLimit)
+ for (int i = 0; i < reps; i++)
{
- AddRep();
- rep = 0;
+ span[n + i]++;
}
}
- else
+
+ n += reps;
+
+ if (n == imageLength)
{
- AddRep();
- obit = nbit;
- rep = 1;
+ break;
+ }
+
+ if (n > imageLength)
+ {
+ image.Dispose();
+ throw new FileLoadException("Error image ran off the end");
}
}
+ }
+
+ for (int i = 0; i < imageLength; i++)
+ {
+ int newC = span[i] * (256 / parent.AntiAliasing);
- // Collect stragglers
- AddRep();
+ if (newC > 0)
+ {
+ newC--;
+ }
- EncodedRle = rawData.ToArray();
- DataSize = (uint) EncodedRle.Length;
+ span[i] = (byte) newC;
+ }
+
+ return image;
+ }
+
+ private Mat DecodeCtbImage(uint layerIndex)
+ {
+ var mat = EmguExtensions.InitMat(Parent!.Resolution);
+ //var span = mat.GetBytePointer();
- return EncodedRle;
+ if (Parent.HeaderSettings.EncryptionKey > 0)
+ {
+ LayerRleCryptBuffer(Parent.HeaderSettings.EncryptionKey, layerIndex, EncodedRle!);
}
- private unsafe byte[] EncodeCtbImage(Mat image, uint layerIndex)
+ int pixel = 0;
+ for (var n = 0; n < EncodedRle!.Length; n++)
{
- List<byte> rawData = new();
- byte color = byte.MaxValue >> 1;
- uint stride = 0;
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ byte code = EncodedRle[n];
+ int stride = 1;
- void AddRep()
+ if ((code & 0x80) == 0x80) // It's a run
{
- if (stride == 0)
- {
- return;
- }
+ code &= 0x7f; // Get the run length
+ n++;
- if (stride > 1)
- {
- color |= 0x80;
- }
- rawData.Add(color);
+ var slen = EncodedRle[n];
- if (stride <= 1)
+ if ((slen & 0x80) == 0)
{
- // no run needed
- return;
+ stride = slen;
}
-
- if (stride <= 0x7f)
+ else if ((slen & 0xc0) == 0x80)
{
- rawData.Add((byte)stride);
- return;
+ stride = ((slen & 0x3f) << 8) + EncodedRle[n + 1];
+ n++;
}
-
- if (stride <= 0x3fff)
+ else if ((slen & 0xe0) == 0xc0)
{
- rawData.Add((byte)((stride >> 8) | 0x80));
- rawData.Add((byte)stride);
- return;
+ stride = ((slen & 0x1f) << 16) + (EncodedRle[n + 1] << 8) + EncodedRle[n + 2];
+ n += 2;
}
-
- if (stride <= 0x1fffff)
+ else if ((slen & 0xf0) == 0xe0)
{
- rawData.Add((byte)((stride >> 16) | 0xc0));
- rawData.Add((byte)(stride >> 8));
- rawData.Add((byte)stride);
- return;
+ stride = ((slen & 0xf) << 24) + (EncodedRle[n + 1] << 16) + (EncodedRle[n + 2] << 8) + EncodedRle[n + 3];
+ n += 3;
}
-
- if (stride <= 0xfffffff)
+ else
{
- rawData.Add((byte)((stride >> 24) | 0xe0));
- rawData.Add((byte)(stride >> 16));
- rawData.Add((byte)(stride >> 8));
- rawData.Add((byte)stride);
+ mat.Dispose();
+ throw new FileLoadException("Corrupted RLE data");
}
}
+ // Bit extend from 7-bit to 8-bit greymap
+ if (code != 0)
+ {
+ code = (byte)((code << 1) | 1);
+ }
- for (int pixel = 0; pixel < imageLength; pixel++)
+ mat.FillSpan(ref pixel, stride, code);
+
+ //if (stride <= 0) continue; // Nothing to do
+
+ /*if (code == 0) // Ignore blacks, spare cycles
{
- var grey7 = (byte) (span[pixel] >> 1);
+ pixel += stride;
+ continue;
+ }*/
- if (grey7 == color)
- {
- stride++;
- }
- else
- {
- AddRep();
- color = grey7;
- stride = 1;
- }
+ /*for (; stride > 0; stride--)
+ {
+ span[pixel] = code;
+ pixel++;
+ }*/
+ }
+
+ return mat;
+ }
+
+ public byte[] Encode(Mat image, byte aaIndex, uint layerIndex)
+ {
+ return Parent!.IsCtbFile ? EncodeCtbImage(image, layerIndex) : EncodeCbddlpImage(image, aaIndex);
+ }
+
+ public unsafe byte[] EncodeCbddlpImage(Mat image, byte bit)
+ {
+ List<byte> rawData = new();
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
+
+ bool obit = false;
+ int rep = 0;
+
+ //ngrey:= uint16(r | g | b)
+ // thresholds:
+ // aa 1: 127
+ // aa 2: 255 127
+ // aa 4: 255 191 127 63
+ // aa 8: 255 223 191 159 127 95 63 31
+ byte threshold = (byte)(256 / Parent!.AntiAliasing * bit - 1);
+
+ void AddRep()
+ {
+ if (rep <= 0) return;
+
+ byte by = (byte)rep;
+
+ if (obit)
+ {
+ by |= 0x80;
+ //bitsOn += uint(rep)
}
- AddRep();
+ rawData.Add(by);
+ }
- if (Parent.HeaderSettings.EncryptionKey > 0)
+ for (int pixel = 0; pixel < imageLength; pixel++)
+ {
+ var nbit = span[pixel] >= threshold;
+
+ if (nbit == obit)
{
- EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData);
+ rep++;
+
+ if (rep == RLE8EncodingLimit)
+ {
+ AddRep();
+ rep = 0;
+ }
}
else
{
- EncodedRle = rawData.ToArray();
+ AddRep();
+ obit = nbit;
+ rep = 1;
}
+ }
- DataSize = (uint)EncodedRle.Length;
+ // Collect stragglers
+ AddRep();
- return EncodedRle;
- }
+ EncodedRle = rawData.ToArray();
+ DataSize = (uint) EncodedRle.Length;
- public override string ToString()
- {
- return $"{nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffSeconds)}: {LightOffSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(TableSize)}: {TableSize}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
- }
+ return EncodedRle;
}
- public class LayerDefEx
- {
- public const byte TABLE_SIZE = 48;
- /// <summary>
- /// Gets a copy of layer data definition
- /// </summary>
- [FieldOrder(0)] public LayerDef LayerDef { get; set; } = new();
-
- /// <summary>
- /// Gets the total size of ctbImageInfo and Image data
- /// </summary>
- [FieldOrder(1)] public uint TotalSize { get; set; }
- [FieldOrder(2)] public float LiftHeight { get; set; }
- [FieldOrder(3)] public float LiftSpeed { get; set; }
- [FieldOrder(4)] public float LiftHeight2 { get; set; }
- [FieldOrder(5)] public float LiftSpeed2 { get; set; }
- [FieldOrder(6)] public float RetractSpeed { get; set; }
- [FieldOrder(7)] public float RetractHeight2 { get; set; }
- [FieldOrder(8)] public float RetractSpeed2 { get; set; }
- [FieldOrder(9)] public float RestTimeBeforeLift { get; set; }
- [FieldOrder(10)] public float RestTimeAfterLift { get; set; }
- [FieldOrder(11)] public float RestTimeAfterRetract { get; set; } // 28672 v3?
- [FieldOrder(12)] public float LightPWM { get; set; }
-
- public LayerDefEx() { }
-
- public LayerDefEx(LayerDef layerDef, Layer layer)
+ private unsafe byte[] EncodeCtbImage(Mat image, uint layerIndex)
+ {
+ List<byte> rawData = new();
+ byte color = byte.MaxValue >> 1;
+ uint stride = 0;
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
+
+ void AddRep()
{
- LayerDef = layerDef;
- if (layer is not null)
+ if (stride == 0)
{
- LiftHeight = layer.LiftHeight;
- LiftSpeed = layer.LiftSpeed;
- RetractSpeed = layer.RetractSpeed;
- LightPWM = layer.LightPWM;
-
- if (layerDef.Parent is not null && layerDef.Parent.HeaderSettings.Version >= 4)
- {
- LiftHeight += layer.LiftHeight2;
- LiftHeight2 = layer.LiftHeight2;
- LiftSpeed2 = layer.LiftSpeed2;
+ return;
+ }
- RetractHeight2 = layer.RetractHeight2;
- RetractSpeed2 = layer.RetractSpeed2;
+ if (stride > 1)
+ {
+ color |= 0x80;
+ }
+ rawData.Add(color);
- RestTimeAfterRetract = layer.WaitTimeBeforeCure;
- RestTimeBeforeLift = layer.WaitTimeAfterCure;
- RestTimeAfterLift = layer.WaitTimeAfterLift;
- }
+ if (stride <= 1)
+ {
+ // no run needed
+ return;
}
- if (layerDef.DataSize > 0)
+ if (stride <= 0x7f)
{
- TotalSize = (uint) (Helpers.Serializer.SizeOf(this) + layerDef.DataSize);
+ rawData.Add((byte)stride);
+ return;
}
- }
- public void CopyTo(Layer layer)
- {
- LayerDef.CopyTo(layer);
+ if (stride <= 0x3fff)
+ {
+ rawData.Add((byte)((stride >> 8) | 0x80));
+ rawData.Add((byte)stride);
+ return;
+ }
- layer.LiftHeight = LiftHeight;
- layer.LiftSpeed = LiftSpeed;
- layer.RetractSpeed = RetractSpeed;
- layer.LightPWM = (byte)LightPWM;
+ if (stride <= 0x1fffff)
+ {
+ rawData.Add((byte)((stride >> 16) | 0xc0));
+ rawData.Add((byte)(stride >> 8));
+ rawData.Add((byte)stride);
+ return;
+ }
- if (LayerDef.Parent is not null && LayerDef.Parent.HeaderSettings.Version >= 4)
+ if (stride <= 0xfffffff)
{
- layer.LiftHeight -= LiftHeight2;
- layer.LiftHeight2 = LiftHeight2;
- layer.LiftSpeed2 = LiftSpeed2;
+ rawData.Add((byte)((stride >> 24) | 0xe0));
+ rawData.Add((byte)(stride >> 16));
+ rawData.Add((byte)(stride >> 8));
+ rawData.Add((byte)stride);
+ }
+ }
+
- layer.RetractHeight2 = RetractHeight2;
- layer.RetractSpeed2 = RetractSpeed2;
+ for (int pixel = 0; pixel < imageLength; pixel++)
+ {
+ var grey7 = (byte) (span[pixel] >> 1);
- layer.WaitTimeBeforeCure = RestTimeAfterRetract;
- layer.WaitTimeAfterCure = RestTimeBeforeLift;
- layer.WaitTimeAfterLift = RestTimeAfterLift;
+ if (grey7 == color)
+ {
+ stride++;
+ }
+ else
+ {
+ AddRep();
+ color = grey7;
+ stride = 1;
}
}
- public override string ToString()
+ AddRep();
+
+ if (Parent!.HeaderSettings.EncryptionKey > 0)
+ {
+ EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData);
+ }
+ else
{
- return $"{nameof(LayerDef)}: {LayerDef}, {nameof(TotalSize)}: {TotalSize}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(LightPWM)}: {LightPWM}";
+ EncodedRle = rawData.ToArray();
}
-
- }
+ DataSize = (uint)EncodedRle.Length;
- #endregion
+ return EncodedRle;
+ }
- #endregion
+ public override string ToString()
+ {
+ return $"{nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffSeconds)}: {LightOffSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(TableSize)}: {TableSize}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
+ }
+ }
- #region Properties
+ public class LayerDefEx
+ {
+ public const byte TABLE_SIZE = 48;
+ /// <summary>
+ /// Gets a copy of layer data definition
+ /// </summary>
+ [FieldOrder(0)] public LayerDef LayerDef { get; set; } = new();
- public Header HeaderSettings { get; protected internal set; } = new();
- public PrintParameters PrintParametersSettings { get; protected internal set; } = new();
+ /// <summary>
+ /// Gets the total size of ctbImageInfo and Image data
+ /// </summary>
+ [FieldOrder(1)] public uint TotalSize { get; set; }
+ [FieldOrder(2)] public float LiftHeight { get; set; }
+ [FieldOrder(3)] public float LiftSpeed { get; set; }
+ [FieldOrder(4)] public float LiftHeight2 { get; set; }
+ [FieldOrder(5)] public float LiftSpeed2 { get; set; }
+ [FieldOrder(6)] public float RetractSpeed { get; set; }
+ [FieldOrder(7)] public float RetractHeight2 { get; set; }
+ [FieldOrder(8)] public float RetractSpeed2 { get; set; }
+ [FieldOrder(9)] public float RestTimeBeforeLift { get; set; }
+ [FieldOrder(10)] public float RestTimeAfterLift { get; set; }
+ [FieldOrder(11)] public float RestTimeAfterRetract { get; set; } // 28672 v3?
+ [FieldOrder(12)] public float LightPWM { get; set; }
+
+ public LayerDefEx() { }
+
+ public LayerDefEx(LayerDef layerDef, Layer layer)
+ {
+ LayerDef = layerDef;
+ if (layer is not null)
+ {
+ LiftHeight = layer.LiftHeight;
+ LiftSpeed = layer.LiftSpeed;
+ RetractSpeed = layer.RetractSpeed;
+ LightPWM = layer.LightPWM;
+
+ if (layerDef.Parent is not null && layerDef.Parent.HeaderSettings.Version >= 4)
+ {
+ LiftHeight += layer.LiftHeight2;
+ LiftHeight2 = layer.LiftHeight2;
+ LiftSpeed2 = layer.LiftSpeed2;
- public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
- public PrintParametersV4 PrintParametersV4Settings { get; protected internal set; } = new();
+ RetractHeight2 = layer.RetractHeight2;
+ RetractSpeed2 = layer.RetractSpeed2;
- public Preview[] Previews { get; protected internal set; }
+ RestTimeAfterRetract = layer.WaitTimeBeforeCure;
+ RestTimeBeforeLift = layer.WaitTimeAfterCure;
+ RestTimeAfterLift = layer.WaitTimeAfterLift;
+ }
+ }
- public LayerDef[,] LayerDefinitions { get; private set; }
+ if (layerDef.DataSize > 0)
+ {
+ TotalSize = (uint) (Helpers.Serializer.SizeOf(this) + layerDef.DataSize);
+ }
+ }
- public override FileFormatType FileType => FileFormatType.Binary;
+ public void CopyTo(Layer layer)
+ {
+ LayerDef.CopyTo(layer);
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(ChituboxFile), "photon", "Chitubox Photon"),
- new(typeof(ChituboxFile), "cbddlp", "Chitubox CBDDLP"),
- new(typeof(ChituboxFile), "ctb", "Chitubox CTB"),
- //new(typeof(ChituboxFile), "v2.ctb", "Chitubox CTBv2"),
- //new(typeof(ChituboxFile), "v3.ctb", "Chitubox CTBv3"),
- //new(typeof(ChituboxFile), "v4.ctb", "Chitubox CTBv4", false, false),
- //new(typeof(ChituboxFile), "encrypted.ctb", "Chitubox encrypted CTB"),
- };
+ layer.LiftHeight = LiftHeight;
+ layer.LiftSpeed = LiftSpeed;
+ layer.RetractSpeed = RetractSpeed;
+ layer.LightPWM = (byte)LightPWM;
- public override PrintParameterModifier[] PrintParameterModifiers
- {
- get
+ if (LayerDef.Parent is not null && LayerDef.Parent.HeaderSettings.Version >= 4)
{
- if (HeaderSettings.Version >= 4)
- {
- return new[]
- {
+ layer.LiftHeight -= LiftHeight2;
+ layer.LiftHeight2 = LiftHeight2;
+ layer.LiftSpeed2 = LiftSpeed2;
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.TransitionLayerCount,
+ layer.RetractHeight2 = RetractHeight2;
+ layer.RetractSpeed2 = RetractSpeed2;
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
+ layer.WaitTimeBeforeCure = RestTimeAfterRetract;
+ layer.WaitTimeAfterCure = RestTimeBeforeLift;
+ layer.WaitTimeAfterLift = RestTimeAfterLift;
+ }
+ }
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
+ public override string ToString()
+ {
+ return $"{nameof(LayerDef)}: {LayerDef}, {nameof(TotalSize)}: {TotalSize}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(LightPWM)}: {LightPWM}";
+ }
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+
+ }
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
+ #endregion
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
+ #endregion
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
+ #region Properties
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public PrintParameters PrintParametersSettings { get; protected internal set; } = new();
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
- }
+ public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
+ public PrintParametersV4 PrintParametersV4Settings { get; protected internal set; } = new();
- if (HeaderSettings.Version == 3)
- {
- return new[]
- {
+ public Preview[] Previews { get; protected internal set; }
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.TransitionLayerCount,
+ public LayerDef[,]? LayerDefinitions { get; private set; }
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ public override FileFormatType FileType => FileFormatType.Binary;
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
- };
- }
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(ChituboxFile), "photon", "Chitubox Photon"),
+ new(typeof(ChituboxFile), "cbddlp", "Chitubox CBDDLP"),
+ new(typeof(ChituboxFile), "ctb", "Chitubox CTB"),
+ //new(typeof(ChituboxFile), "v2.ctb", "Chitubox CTBv2"),
+ //new(typeof(ChituboxFile), "v3.ctb", "Chitubox CTBv3"),
+ //new(typeof(ChituboxFile), "v4.ctb", "Chitubox CTBv4", false, false),
+ //new(typeof(ChituboxFile), "encrypted.ctb", "Chitubox encrypted CTB"),
+ };
+ public override PrintParameterModifier[]? PrintParameterModifiers
+ {
+ get
+ {
+ if (HeaderSettings.Version >= 4)
+ {
return new[]
{
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomLightOffDelay,
PrintParameterModifier.LightOffDelay,
+
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
+
PrintParameterModifier.BottomExposureTime,
PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
+
PrintParameterModifier.BottomLiftHeight,
PrintParameterModifier.BottomLiftSpeed,
PrintParameterModifier.LiftHeight,
PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
PrintParameterModifier.BottomLightPWM,
PrintParameterModifier.LightPWM,
};
}
+
+ if (HeaderSettings.Version == 3)
+ {
+ return new[]
+ {
+
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
+
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+ };
+ }
+
+ return new[]
+ {
+
+ PrintParameterModifier.BottomLayerCount,
+
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
}
+ }
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers {
- get
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers {
+ get
+ {
+ if (!IsCtbFile) return null; // Only ctb files
+ /* Disable for v2 beside the fields on format they are not used
+ if (HeaderSettings.Version <= 2)
{
- if (!IsCtbFile) return null; // Only ctb files
- /* Disable for v2 beside the fields on format they are not used
- if (HeaderSettings.Version <= 2)
+ return new[]
{
- return new[]
- {
- PrintParameterModifier.ExposureSeconds,
- PrintParameterModifier.LightOffDelay,
- };
- }*/
- if (HeaderSettings.Version == 3)
+ PrintParameterModifier.ExposureSeconds,
+ PrintParameterModifier.LightOffDelay,
+ };
+ }*/
+ if (HeaderSettings.Version == 3)
+ {
+ return new[]
{
- return new[]
- {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.LightPWM,
- };
- }
- if (HeaderSettings.Version >= 4)
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.LightPWM,
+ };
+ }
+ if (HeaderSettings.Version >= 4)
+ {
+ return new[]
{
- return new[]
- {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.LightPWM,
- };
- }
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.LightPWM,
+ };
+ }
- return null;
- }
- }
+ return null;
+ }
+ }
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(400, 300),
- new(200, 125)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(400, 300),
+ new(200, 125)
+ };
- public override uint[] AvailableVersions { get; } = { 1, 2, 3, 4 };
+ public override uint[] AvailableVersions { get; } = { 1, 2, 3, 4 };
- public override uint DefaultVersion => USED_VERSION;
+ public override uint DefaultVersion => USED_VERSION;
- public override uint Version
+ public override uint Version
+ {
+ get => HeaderSettings.Version;
+ set
{
- get => HeaderSettings.Version;
- set
- {
- base.Version = value;
- HeaderSettings.Version = base.Version;
- }
+ base.Version = value;
+ HeaderSettings.Version = base.Version;
}
+ }
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => HeaderSettings.BedSizeX;
+ set
{
- get => HeaderSettings.BedSizeX;
- set
- {
- HeaderSettings.BedSizeX = (float) Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ HeaderSettings.BedSizeX = (float) Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => HeaderSettings.BedSizeY;
+ set
{
- get => HeaderSettings.BedSizeY;
- set
- {
- HeaderSettings.BedSizeY = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ HeaderSettings.BedSizeY = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
- {
- get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ;
- set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2);
- }
+ public override float MachineZ
+ {
+ get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ;
+ set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2);
+ }
- public override Enumerations.FlipDirection DisplayMirror
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
+ set
{
- get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
- set
- {
- HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
- RaisePropertyChanged();
- }
+ HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
+ RaisePropertyChanged();
}
+ }
- public override bool IsAntiAliasingEmulated => IsCbddlpFile;
+ public override bool IsAntiAliasingEmulated => IsCbddlpFile;
- public override byte AntiAliasing
+ public override byte AntiAliasing
+ {
+ get => (byte) (IsCtbFile ? SlicerInfoSettings.AntiAliasLevel : HeaderSettings.AntiAliasLevel);
+ set
{
- get => (byte) (IsCtbFile ? SlicerInfoSettings.AntiAliasLevel : HeaderSettings.AntiAliasLevel);
- set
+ if (IsCtbFile)
{
- if (IsCtbFile)
- {
- base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = value.Clamp(1, 16));
- }
- else if(IsCbddlpFile)
- {
- base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel = value.Clamp(1, 16));
- ValidateAntiAliasingLevel();
- }
+ base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = value.Clamp(1, 16));
}
- }
-
- public override float LayerHeight
- {
- get => HeaderSettings.LayerHeightMilimeter;
- set
+ else if(IsCbddlpFile)
{
- HeaderSettings.LayerHeightMilimeter = Layer.RoundHeight(value);
- RaisePropertyChanged();
+ base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel = value.Clamp(1, 16));
+ ValidateAntiAliasingLevel();
}
}
+ }
- public override float PrintHeight
+ public override float LayerHeight
+ {
+ get => HeaderSettings.LayerHeightMilimeter;
+ set
{
- get => base.PrintHeight;
- set => base.PrintHeight = HeaderSettings.TotalHeightMilimeter = base.PrintHeight;
+ HeaderSettings.LayerHeightMilimeter = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set
- {
- base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
- PrintParametersV4Settings.LastLayerIndex = LastLayerIndex;
- }
- }
+ public override float PrintHeight
+ {
+ get => base.PrintHeight;
+ set => base.PrintHeight = HeaderSettings.TotalHeightMilimeter = base.PrintHeight;
+ }
- public override ushort BottomLayerCount
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set
{
- get => (ushort) HeaderSettings.BottomLayersCount;
- set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount = PrintParametersSettings.BottomLayerCount = value);
+ base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
+ PrintParametersV4Settings.LastLayerIndex = LastLayerIndex;
}
+ }
- public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software;
+ public override ushort BottomLayerCount
+ {
+ get => (ushort) HeaderSettings.BottomLayersCount;
+ set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount = PrintParametersSettings.BottomLayerCount = value);
+ }
- public override ushort TransitionLayerCount
- {
- get => (ushort)SlicerInfoSettings.TransitionLayerCount;
- set => base.TransitionLayerCount = (ushort)(SlicerInfoSettings.TransitionLayerCount = Math.Min(value, MaximumPossibleTransitionLayerCount));
- }
+ public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software;
- public override float BottomLightOffDelay
- {
- get => PrintParametersSettings.BottomLightOffDelay;
- set
- {
- base.BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay = (float) Math.Round(value, 2);
- if (HeaderSettings.Version >= 4 && value > 0)
- {
- WaitTimeBeforeCure = 0;
- WaitTimeAfterCure = 0;
- WaitTimeAfterLift = 0;
- }
- }
- }
+ public override ushort TransitionLayerCount
+ {
+ get => (ushort)SlicerInfoSettings.TransitionLayerCount;
+ set => base.TransitionLayerCount = (ushort)(SlicerInfoSettings.TransitionLayerCount = Math.Min(value, MaximumPossibleTransitionLayerCount));
+ }
- public override float LightOffDelay
+ public override float BottomLightOffDelay
+ {
+ get => PrintParametersSettings.BottomLightOffDelay;
+ set
{
- get => HeaderSettings.LightOffDelay;
- set
+ base.BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay = (float) Math.Round(value, 2);
+ if (HeaderSettings.Version >= 4 && value > 0)
{
- base.LightOffDelay = HeaderSettings.LightOffDelay = PrintParametersSettings.LightOffDelay = (float) Math.Round(value, 2);
- if (HeaderSettings.Version >= 4 && value > 0)
- {
- WaitTimeBeforeCure = 0;
- WaitTimeAfterCure = 0;
- WaitTimeAfterLift = 0;
- }
+ WaitTimeBeforeCure = 0;
+ WaitTimeAfterCure = 0;
+ WaitTimeAfterLift = 0;
}
}
+ }
- public override float BottomWaitTimeBeforeCure
+ public override float LightOffDelay
+ {
+ get => HeaderSettings.LightOffDelay;
+ set
{
- get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : FirstLayer?.WaitTimeBeforeCure ?? 0;
- set
+ base.LightOffDelay = HeaderSettings.LightOffDelay = PrintParametersSettings.LightOffDelay = (float) Math.Round(value, 2);
+ if (HeaderSettings.Version >= 4 && value > 0)
{
- if (HeaderSettings.Version < 4)
- {
- if (value > 0)
- {
- SetBottomLightOffDelay(value);
- }
-
- return;
- }
-
- base.BottomWaitTimeBeforeCure = value;
+ WaitTimeBeforeCure = 0;
+ WaitTimeAfterCure = 0;
+ WaitTimeAfterLift = 0;
}
}
+ }
- public override float WaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : FirstLayer?.WaitTimeBeforeCure ?? 0;
+ set
{
- get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeAfterRetract : 0;
- set
+ if (HeaderSettings.Version < 4)
{
- if (HeaderSettings.Version < 4)
- {
- if (value > 0)
- {
- SetNormalLightOffDelay(value);
- }
-
- return;
- }
- base.WaitTimeBeforeCure = SlicerInfoSettings.RestTimeAfterRetract = PrintParametersV4Settings.RestTimeAfterRetract = (float)Math.Round(value, 2);
if (value > 0)
{
- BottomLightOffDelay = 0;
- LightOffDelay = 0;
+ SetBottomLightOffDelay(value);
}
- }
- }
- public override float BottomExposureTime
- {
- get => HeaderSettings.BottomExposureSeconds;
- set => base.BottomExposureTime = HeaderSettings.BottomExposureSeconds = (float) Math.Round(value, 2);
- }
-
- public override float BottomWaitTimeAfterCure
- {
- get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : FirstLayer?.WaitTimeAfterCure ?? 0) : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- base.BottomWaitTimeAfterCure = value;
+ return;
}
+
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float WaitTimeAfterCure
+ public override float WaitTimeBeforeCure
+ {
+ get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeAfterRetract : 0;
+ set
{
- get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeBeforeLift : 0;
- set
+ if (HeaderSettings.Version < 4)
{
- if (HeaderSettings.Version < 4) return;
- base.WaitTimeAfterCure = PrintParametersV4Settings.RestTimeBeforeLift = (float) Math.Round(value, 2);
if (value > 0)
{
- BottomLightOffDelay = 0;
- LightOffDelay = 0;
+ SetNormalLightOffDelay(value);
}
- }
- }
-
- public override float ExposureTime
- {
- get => HeaderSettings.LayerExposureSeconds;
- set => base.ExposureTime = HeaderSettings.LayerExposureSeconds = (float)Math.Round(value, 2);
- }
- public override float BottomLiftHeight
- {
- get
- {
- if (HeaderSettings.Version <= 3) return PrintParametersSettings.BottomLiftHeight;
- return (float)Math.Round(Math.Max(0, PrintParametersSettings.BottomLiftHeight - SlicerInfoSettings.BottomLiftHeight2), 2);
+ return;
}
- set
+ base.WaitTimeBeforeCure = SlicerInfoSettings.RestTimeAfterRetract = PrintParametersV4Settings.RestTimeAfterRetract = (float)Math.Round(value, 2);
+ if (value > 0)
{
- value = (float)Math.Round(value, 2);
- if (HeaderSettings.Version <= 3) PrintParametersSettings.BottomLiftHeight = value;
- if (HeaderSettings.Version >= 4) PrintParametersSettings.BottomLiftHeight = (float)Math.Round(value + SlicerInfoSettings.BottomLiftHeight2, 2);
- base.BottomLiftHeight = value;
+ BottomLightOffDelay = 0;
+ LightOffDelay = 0;
}
}
+ }
- public override float BottomLiftSpeed
+ public override float BottomExposureTime
+ {
+ get => HeaderSettings.BottomExposureSeconds;
+ set => base.BottomExposureTime = HeaderSettings.BottomExposureSeconds = (float) Math.Round(value, 2);
+ }
+
+ public override float BottomWaitTimeAfterCure
+ {
+ get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : FirstLayer?.WaitTimeAfterCure ?? 0) : 0;
+ set
{
- get => PrintParametersSettings.BottomLiftSpeed;
- set => base.BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed = (float)Math.Round(value, 2);
+ if (HeaderSettings.Version < 4) return;
+ base.BottomWaitTimeAfterCure = value;
}
+ }
- public override float LiftHeight
+ public override float WaitTimeAfterCure
+ {
+ get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeBeforeLift : 0;
+ set
{
- get
- {
- if(HeaderSettings.Version <= 3) return PrintParametersSettings.LiftHeight;
- return Math.Max(0, PrintParametersSettings.LiftHeight - SlicerInfoSettings.LiftHeight2);
- }
- set
+ if (HeaderSettings.Version < 4) return;
+ base.WaitTimeAfterCure = PrintParametersV4Settings.RestTimeBeforeLift = (float) Math.Round(value, 2);
+ if (value > 0)
{
- value = (float)Math.Round(value, 2);
- if (HeaderSettings.Version <= 3) PrintParametersSettings.LiftHeight = value;
- if (HeaderSettings.Version >= 4) PrintParametersSettings.LiftHeight = (float)Math.Round(value + SlicerInfoSettings.LiftHeight2, 2);
- base.LiftHeight = value;
+ BottomLightOffDelay = 0;
+ LightOffDelay = 0;
}
}
+ }
- public override float LiftSpeed
- {
- get => PrintParametersSettings.LiftSpeed;
- set => base.LiftSpeed = PrintParametersSettings.LiftSpeed = (float)Math.Round(value, 2);
- }
+ public override float ExposureTime
+ {
+ get => HeaderSettings.LayerExposureSeconds;
+ set => base.ExposureTime = HeaderSettings.LayerExposureSeconds = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftHeight2
+ public override float BottomLiftHeight
+ {
+ get
{
- get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.BottomLiftHeight2 : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- var bottomLiftHeight = BottomLiftHeight;
- SlicerInfoSettings.BottomLiftHeight2 = (float)Math.Round(value, 2);
- BottomLiftHeight = bottomLiftHeight;
- base.BottomLiftHeight2 = SlicerInfoSettings.BottomLiftHeight2;
- }
+ if (HeaderSettings.Version <= 3) return PrintParametersSettings.BottomLiftHeight;
+ return (float)Math.Round(Math.Max(0, PrintParametersSettings.BottomLiftHeight - SlicerInfoSettings.BottomLiftHeight2), 2);
}
-
- public override float BottomLiftSpeed2
+ set
{
- get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.BottomLiftSpeed2 : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- base.BottomLiftSpeed2 = SlicerInfoSettings.BottomLiftSpeed2 = (float)Math.Round(value, 2);
- }
+ value = (float)Math.Round(value, 2);
+ if (HeaderSettings.Version <= 3) PrintParametersSettings.BottomLiftHeight = value;
+ if (HeaderSettings.Version >= 4) PrintParametersSettings.BottomLiftHeight = (float)Math.Round(value + SlicerInfoSettings.BottomLiftHeight2, 2);
+ base.BottomLiftHeight = value;
}
+ }
- public override float LiftHeight2
+ public override float BottomLiftSpeed
+ {
+ get => PrintParametersSettings.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed = (float)Math.Round(value, 2);
+ }
+
+ public override float LiftHeight
+ {
+ get
{
- get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.LiftHeight2 : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- var liftHeight = LiftHeight;
- SlicerInfoSettings.LiftHeight2 = (float)Math.Round(value, 2);
- LiftHeight = liftHeight;
- base.LiftHeight2 = SlicerInfoSettings.LiftHeight2;
- }
+ if(HeaderSettings.Version <= 3) return PrintParametersSettings.LiftHeight;
+ return Math.Max(0, PrintParametersSettings.LiftHeight - SlicerInfoSettings.LiftHeight2);
}
-
- public override float LiftSpeed2
+ set
{
- get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.LiftSpeed2 : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- base.LiftSpeed2 = SlicerInfoSettings.LiftSpeed2 = (float)Math.Round(value, 2);
- }
+ value = (float)Math.Round(value, 2);
+ if (HeaderSettings.Version <= 3) PrintParametersSettings.LiftHeight = value;
+ if (HeaderSettings.Version >= 4) PrintParametersSettings.LiftHeight = (float)Math.Round(value + SlicerInfoSettings.LiftHeight2, 2);
+ base.LiftHeight = value;
}
+ }
- public override float BottomWaitTimeAfterLift
+ public override float LiftSpeed
+ {
+ get => PrintParametersSettings.LiftSpeed;
+ set => base.LiftSpeed = PrintParametersSettings.LiftSpeed = (float)Math.Round(value, 2);
+ }
+
+ public override float BottomLiftHeight2
+ {
+ get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.BottomLiftHeight2 : 0;
+ set
{
- get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : FirstLayer?.WaitTimeAfterLift ?? 0) : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- base.BottomWaitTimeAfterLift = value;
- }
+ if (HeaderSettings.Version < 4) return;
+ var bottomLiftHeight = BottomLiftHeight;
+ SlicerInfoSettings.BottomLiftHeight2 = (float)Math.Round(value, 2);
+ BottomLiftHeight = bottomLiftHeight;
+ base.BottomLiftHeight2 = SlicerInfoSettings.BottomLiftHeight2;
}
+ }
- public override float WaitTimeAfterLift
+ public override float BottomLiftSpeed2
+ {
+ get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.BottomLiftSpeed2 : 0;
+ set
{
- get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeAfterLift : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- base.WaitTimeAfterLift = SlicerInfoSettings.RestTimeAfterLift = SlicerInfoSettings.RestTimeAfterLift2 = PrintParametersV4Settings.RestTimeAfterLift = (float)Math.Round(value, 2);
- if (value > 0)
- {
- BottomLightOffDelay = 0;
- LightOffDelay = 0;
- }
- }
+ if (HeaderSettings.Version < 4) return;
+ base.BottomLiftSpeed2 = SlicerInfoSettings.BottomLiftSpeed2 = (float)Math.Round(value, 2);
}
+ }
- public override float BottomRetractSpeed
+ public override float LiftHeight2
+ {
+ get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.LiftHeight2 : 0;
+ set
{
- get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractSpeed : RetractSpeed;
- set
- {
- if (HeaderSettings.Version < 4) return;
- base.BottomRetractSpeed = PrintParametersV4Settings.BottomRetractSpeed = (float)Math.Round(value, 2);
- }
+ if (HeaderSettings.Version < 4) return;
+ var liftHeight = LiftHeight;
+ SlicerInfoSettings.LiftHeight2 = (float)Math.Round(value, 2);
+ LiftHeight = liftHeight;
+ base.LiftHeight2 = SlicerInfoSettings.LiftHeight2;
}
+ }
- public override float RetractSpeed
+ public override float LiftSpeed2
+ {
+ get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.LiftSpeed2 : 0;
+ set
{
- get => PrintParametersSettings.RetractSpeed;
- set => base.RetractSpeed = PrintParametersSettings.RetractSpeed = (float)Math.Round(value, 2);
+ if (HeaderSettings.Version < 4) return;
+ base.LiftSpeed2 = SlicerInfoSettings.LiftSpeed2 = (float)Math.Round(value, 2);
}
+ }
- public override float BottomRetractHeight2
+ public override float BottomWaitTimeAfterLift
+ {
+ get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : FirstLayer?.WaitTimeAfterLift ?? 0) : 0;
+ set
{
- get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractHeight2 : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal);
- base.BottomRetractHeight2 = PrintParametersV4Settings.BottomRetractHeight2 = value;
- }
+ if (HeaderSettings.Version < 4) return;
+ base.BottomWaitTimeAfterLift = value;
}
+ }
- public override float BottomRetractSpeed2
+ public override float WaitTimeAfterLift
+ {
+ get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeAfterLift : 0;
+ set
{
- get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractSpeed2 : 0;
- set
+ if (HeaderSettings.Version < 4) return;
+ base.WaitTimeAfterLift = SlicerInfoSettings.RestTimeAfterLift = SlicerInfoSettings.RestTimeAfterLift2 = PrintParametersV4Settings.RestTimeAfterLift = (float)Math.Round(value, 2);
+ if (value > 0)
{
- if (HeaderSettings.Version < 4) return;
- base.BottomRetractSpeed2 = PrintParametersV4Settings.BottomRetractSpeed2 = (float)Math.Round(value, 2);
+ BottomLightOffDelay = 0;
+ LightOffDelay = 0;
}
}
+ }
- public override float RetractHeight2
+ public override float BottomRetractSpeed
+ {
+ get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractSpeed : RetractSpeed;
+ set
{
- get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.RetractHeight2 : 0;
- set
- {
- if (HeaderSettings.Version < 4) return;
- value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
- base.RetractHeight2 = SlicerInfoSettings.RetractHeight2 = value;
- }
+ if (HeaderSettings.Version < 4) return;
+ base.BottomRetractSpeed = PrintParametersV4Settings.BottomRetractSpeed = (float)Math.Round(value, 2);
}
+ }
- public override float RetractSpeed2
- {
- get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.RetractSpeed2 : 0;
- set => base.RetractSpeed2 = SlicerInfoSettings.RetractSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed
+ {
+ get => PrintParametersSettings.RetractSpeed;
+ set => base.RetractSpeed = PrintParametersSettings.RetractSpeed = (float)Math.Round(value, 2);
+ }
- public override byte BottomLightPWM
+ public override float BottomRetractHeight2
+ {
+ get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractHeight2 : 0;
+ set
{
- get => (byte)HeaderSettings.BottomLightPWM;
- set => base.BottomLightPWM = (byte) (HeaderSettings.BottomLightPWM = value);
+ if (HeaderSettings.Version < 4) return;
+ value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal);
+ base.BottomRetractHeight2 = PrintParametersV4Settings.BottomRetractHeight2 = value;
}
+ }
- public override byte LightPWM
+ public override float BottomRetractSpeed2
+ {
+ get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractSpeed2 : 0;
+ set
{
- get => (byte)HeaderSettings.LightPWM;
- set => base.LightPWM = (byte) (HeaderSettings.LightPWM = value);
+ if (HeaderSettings.Version < 4) return;
+ base.BottomRetractSpeed2 = PrintParametersV4Settings.BottomRetractSpeed2 = (float)Math.Round(value, 2);
}
+ }
- public override float PrintTime
+ public override float RetractHeight2
+ {
+ get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.RetractHeight2 : 0;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- HeaderSettings.PrintTime = (uint)base.PrintTime;
- }
- }
+ if (HeaderSettings.Version < 4) return;
+ value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
+ base.RetractHeight2 = SlicerInfoSettings.RetractHeight2 = value;
+ }
+ }
- public override float MaterialMilliliters
- {
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- PrintParametersSettings.VolumeMl = base.MaterialMilliliters;
- }
- }
+ public override float RetractSpeed2
+ {
+ get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.RetractSpeed2 : 0;
+ set => base.RetractSpeed2 = SlicerInfoSettings.RetractSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float MaterialGrams
- {
- get => PrintParametersSettings.WeightG;
- set => base.MaterialGrams = PrintParametersSettings.WeightG = (float)Math.Round(value, 3);
- }
+ 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 MaterialCost
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => (float) Math.Round(PrintParametersSettings.CostDollars, 3);
- set => base.MaterialCost = PrintParametersSettings.CostDollars = (float) Math.Round(value, 3);
+ base.PrintTime = value;
+ HeaderSettings.PrintTime = (uint)base.PrintTime;
}
+ }
- public override string MachineName
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => SlicerInfoSettings.MachineName;
- set => base.MachineName = SlicerInfoSettings.MachineName = value;
+ base.MaterialMilliliters = value;
+ PrintParametersSettings.VolumeMl = base.MaterialMilliliters;
}
+ }
+
+ public override float MaterialGrams
+ {
+ get => PrintParametersSettings.WeightG;
+ set => base.MaterialGrams = PrintParametersSettings.WeightG = (float)Math.Round(value, 3);
+ }
+
+ public override float MaterialCost
+ {
+ get => (float) Math.Round(PrintParametersSettings.CostDollars, 3);
+ set => base.MaterialCost = PrintParametersSettings.CostDollars = (float) Math.Round(value, 3);
+ }
+
+ public override string MachineName
+ {
+ get => SlicerInfoSettings.MachineName;
+ set => base.MachineName = SlicerInfoSettings.MachineName = value;
+ }
- public override object[] Configs
+ public override object[] Configs
+ {
+ get
{
- get
+ return HeaderSettings.Version switch
{
- return HeaderSettings.Version switch
+ <= 1 => new object[] { HeaderSettings },
+ <= 3 => new object[] { HeaderSettings, PrintParametersSettings, SlicerInfoSettings },
+ _ => new object[] // v4
{
- <= 1 => new object[] { HeaderSettings },
- <= 3 => new object[] { HeaderSettings, PrintParametersSettings, SlicerInfoSettings },
- _ => new object[] // v4
- {
- HeaderSettings, PrintParametersSettings, SlicerInfoSettings, PrintParametersV4Settings
- }
- };
- }
+ HeaderSettings, PrintParametersSettings, SlicerInfoSettings, PrintParametersV4Settings
+ }
+ };
}
+ }
+
+ public bool IsCbddlpFile => HeaderSettings.Magic == MAGIC_CBDDLP;
+ public bool IsCtbFile => HeaderSettings.Magic is MAGIC_CTB or MAGIC_CTBv4;
- public bool IsCbddlpFile => HeaderSettings.Magic == MAGIC_CBDDLP;
- public bool IsCtbFile => HeaderSettings.Magic is MAGIC_CTB or MAGIC_CTBv4;
+ public bool CanHash => IsCbddlpFile && HeaderSettings.Version <= 2;
+ #endregion
- public bool CanHash => IsCbddlpFile && HeaderSettings.Version <= 2;
- #endregion
+ #region Constructors
+ public ChituboxFile()
+ {
+ Previews = new Preview[ThumbnailsCount];
+ }
+ #endregion
+
+ #region Methods
+ public override void Clear()
+ {
+ base.Clear();
- #region Constructors
- public ChituboxFile()
+ for (byte i = 0; i < ThumbnailsCount; i++)
{
- Previews = new Preview[ThumbnailsCount];
+ Previews[i] = new Preview();
}
- #endregion
- #region Methods
- public override void Clear()
- {
- base.Clear();
+ LayerDefinitions = null;
+ }
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- Previews[i] = new Preview();
- }
+ public override bool CanProcess(string fileFullPath)
+ {
+ if (!base.CanProcess(fileFullPath)) return false;
- LayerDefinitions = null;
+ try
+ {
+ using var fs = new BinaryReader(new FileStream(fileFullPath, FileMode.Open, FileAccess.Read));
+ var magic = fs.ReadUInt32();
+ return magic is MAGIC_CBDDLP or MAGIC_CTB or MAGIC_CTBv4;
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
}
- public override bool CanProcess(string fileFullPath)
+ return false;
+ }
+
+ private void SanitizeProperties()
+ {
+ if (IsCtbFile)
{
- if (!base.CanProcess(fileFullPath)) return false;
+ if (SlicerInfoSettings.AntiAliasLevel <= 1)
+ {
+ SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel;
+ }
+
+ HeaderSettings.AntiAliasLevel = 1;
- try
+ if (HeaderSettings.Version <= 2)
{
- using var fs = new BinaryReader(new FileStream(fileFullPath, FileMode.Open, FileAccess.Read));
- var magic = fs.ReadUInt32();
- return magic is MAGIC_CBDDLP or MAGIC_CTB or MAGIC_CTBv4;
+ SlicerInfoSettings.PerLayerSettings = PERLAYER_SETTINGS_CTBv2;
+ PrintParametersSettings.Padding4 = 0x1234; // 4660
}
- catch (Exception e)
+ else if (HeaderSettings.Version == 3)
{
- Debug.WriteLine(e);
+ SlicerInfoSettings.PerLayerSettings = AllLayersAreUsingGlobalParameters ? PERLAYER_SETTINGS_DISALLOW : PERLAYER_SETTINGS_CTBv3;
}
-
- return false;
- }
-
- private void SanitizeProperties()
- {
- if (IsCtbFile)
+ else if (HeaderSettings.Version >= 4)
{
- if (SlicerInfoSettings.AntiAliasLevel <= 1)
- {
- SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel;
- }
-
- HeaderSettings.AntiAliasLevel = 1;
-
- if (HeaderSettings.Version <= 2)
- {
- SlicerInfoSettings.PerLayerSettings = PERLAYER_SETTINGS_CTBv2;
- PrintParametersSettings.Padding4 = 0x1234; // 4660
- }
- else if (HeaderSettings.Version == 3)
- {
- SlicerInfoSettings.PerLayerSettings = LayerManager.AllLayersAreUsingGlobalParameters ? PERLAYER_SETTINGS_DISALLOW : PERLAYER_SETTINGS_CTBv3;
- }
- else if (HeaderSettings.Version >= 4)
- {
- SlicerInfoSettings.PerLayerSettings = LayerManager.AllLayersAreUsingGlobalParameters ? PERLAYER_SETTINGS_DISALLOW : PERLAYER_SETTINGS_CTBv4;
- }
+ SlicerInfoSettings.PerLayerSettings = AllLayersAreUsingGlobalParameters ? PERLAYER_SETTINGS_DISALLOW : PERLAYER_SETTINGS_CTBv4;
}
-
- SlicerInfoSettings.ModifiedTimestampMinutes = (uint)DateTimeExtensions.TimestampMinutes;
}
- private void SanitizeMagicVersion()
+ SlicerInfoSettings.ModifiedTimestampMinutes = (uint)DateTimeExtensions.TimestampMinutes;
+ }
+
+ private void SanitizeMagicVersion()
+ {
+ if (FileEndsWith(".ctb"))
{
- if (FileEndsWith(".ctb"))
+ if (HeaderSettings.Magic is not MAGIC_CTB and not MAGIC_CTBv4)
{
- if (HeaderSettings.Magic is not MAGIC_CTB and not MAGIC_CTBv4)
- {
- HeaderSettings.Magic = MAGIC_CTB;
- }
+ HeaderSettings.Magic = MAGIC_CTB;
+ }
- if (FileEndsWith(".v2.ctb"))
- {
- HeaderSettings.Magic = MAGIC_CTB;
- HeaderSettings.Version = 2;
- }
- else if (FileEndsWith(".v3.ctb"))
- {
- HeaderSettings.Magic = MAGIC_CTB;
- HeaderSettings.Version = 3;
- }
- else if (FileEndsWith(".v4.ctb"))
- {
- HeaderSettings.Magic = MAGIC_CTBv4;
- HeaderSettings.Version = 4;
- }
+ if (FileEndsWith(".v2.ctb"))
+ {
+ HeaderSettings.Magic = MAGIC_CTB;
+ HeaderSettings.Version = 2;
}
- else if (FileEndsWith(".cbddlp") || FileEndsWith(".photon"))
+ else if (FileEndsWith(".v3.ctb"))
{
- HeaderSettings.Magic = MAGIC_CBDDLP;
- if (HeaderSettings.Version > 3) HeaderSettings.Version = 3;
+ HeaderSettings.Magic = MAGIC_CTB;
+ HeaderSettings.Version = 3;
+ }
+ else if (FileEndsWith(".v4.ctb"))
+ {
+ HeaderSettings.Magic = MAGIC_CTBv4;
+ HeaderSettings.Version = 4;
}
}
-
- protected override bool OnBeforeConvertFrom(FileFormat source)
+ else if (FileEndsWith(".cbddlp") || FileEndsWith(".photon"))
{
- SanitizeMagicVersion();
- return true;
+ HeaderSettings.Magic = MAGIC_CBDDLP;
+ if (HeaderSettings.Version > 3) HeaderSettings.Version = 3;
}
+ }
- protected override void EncodeInternally(OperationProgress progress)
- {
- SanitizeMagicVersion();
- SanitizeProperties();
+ protected override bool OnBeforeConvertFrom(FileFormat source)
+ {
+ SanitizeMagicVersion();
+ return true;
+ }
- HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings);
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ SanitizeMagicVersion();
+ SanitizeProperties();
+
+ HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings);
- if (IsCtbFile)
- {
- if (HeaderSettings.EncryptionKey == 0)
- {
- var rnd = new Random();
- HeaderSettings.EncryptionKey = (uint)rnd.Next(byte.MaxValue, int.MaxValue);
- }
- }
- else
+ if (IsCtbFile)
+ {
+ if (HeaderSettings.EncryptionKey == 0)
{
- //HeaderSettings.Version = 2;
- HeaderSettings.EncryptionKey = 0; // Force disable encryption
- SlicerInfoSettings.PerLayerSettings = PERLAYER_SETTINGS_CBDDLP;
+ var rnd = new Random();
+ HeaderSettings.EncryptionKey = (uint)rnd.Next(byte.MaxValue, int.MaxValue);
}
+ }
+ else
+ {
+ //HeaderSettings.Version = 2;
+ HeaderSettings.EncryptionKey = 0; // Force disable encryption
+ SlicerInfoSettings.PerLayerSettings = PERLAYER_SETTINGS_CBDDLP;
+ }
- //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
- LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, LayerCount];
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
+ //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
+ LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, LayerCount];
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
- Mat[] thumbnails = {GetThumbnail(true), GetThumbnail(false)};
- for (byte i = 0; i < thumbnails.Length; i++)
- {
- var image = thumbnails[i];
- if(image is null) continue;
+ Mat?[] thumbnails = {GetThumbnail(true), GetThumbnail(false)};
+ for (byte i = 0; i < thumbnails.Length; i++)
+ {
+ var image = thumbnails[i];
+ if(image is null) continue;
- Preview preview = new()
- {
- ResolutionX = (uint)image.Width,
- ResolutionY = (uint)image.Height,
- };
+ Preview preview = new()
+ {
+ ResolutionX = (uint)image.Width,
+ ResolutionY = (uint)image.Height,
+ };
- var previewBytes = preview.Encode(image);
+ var previewBytes = preview.Encode(image);
- if (previewBytes.Length == 0) continue;
+ if (previewBytes.Length == 0) continue;
- if (i == 0)
- {
- HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position;
- }
- else
- {
- HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position;
- }
+ if (i == 0)
+ {
+ HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position;
+ }
+ else
+ {
+ HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position;
+ }
- preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
+ preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
- Helpers.SerializeWriteFileStream(outputFile, preview);
- outputFile.WriteBytes(previewBytes);
- }
+ Helpers.SerializeWriteFileStream(outputFile, preview);
+ outputFile.WriteBytes(previewBytes);
+ }
- if (HeaderSettings.Version >= 2)
- {
- HeaderSettings.PrintParametersOffsetAddress = (uint)outputFile.Position;
+ if (HeaderSettings.Version >= 2)
+ {
+ HeaderSettings.PrintParametersOffsetAddress = (uint)outputFile.Position;
- Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings);
+ Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings);
- HeaderSettings.SlicerOffset = (uint)outputFile.Position;
- HeaderSettings.SlicerSize = (uint) Helpers.Serializer.SizeOf(SlicerInfoSettings) - SlicerInfoSettings.MachineNameSize;
+ HeaderSettings.SlicerOffset = (uint)outputFile.Position;
+ HeaderSettings.SlicerSize = (uint) Helpers.Serializer.SizeOf(SlicerInfoSettings) - SlicerInfoSettings.MachineNameSize;
- SlicerInfoSettings.MachineNameAddress = HeaderSettings.SlicerOffset + HeaderSettings.SlicerSize;
+ SlicerInfoSettings.MachineNameAddress = HeaderSettings.SlicerOffset + HeaderSettings.SlicerSize;
- if (HeaderSettings.Version >= 4)
- {
- SlicerInfoSettings.PrintParametersV4Address = (uint)(HeaderSettings.SlicerOffset +
- Helpers.Serializer.SizeOf(SlicerInfoSettings) +
- CTBv4_DISCLAIMER_SIZE);
- }
+ if (HeaderSettings.Version >= 4)
+ {
+ SlicerInfoSettings.PrintParametersV4Address = (uint)(HeaderSettings.SlicerOffset +
+ Helpers.Serializer.SizeOf(SlicerInfoSettings) +
+ CTBv4_DISCLAIMER_SIZE);
+ }
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
- if (HeaderSettings.Version >= 4)
- {
- PrintParametersV4Settings.DisclaimerAddress = (uint)outputFile.Position;
- PrintParametersV4Settings.DisclaimerLength = (uint)CTBv4_DISCLAIMER.Length;
- outputFile.WriteBytes(Encoding.UTF8.GetBytes(CTBv4_DISCLAIMER));
- Helpers.SerializeWriteFileStream(outputFile, PrintParametersV4Settings);
- }
+ if (HeaderSettings.Version >= 4)
+ {
+ PrintParametersV4Settings.DisclaimerAddress = (uint)outputFile.Position;
+ PrintParametersV4Settings.DisclaimerLength = (uint)CTBv4_DISCLAIMER.Length;
+ outputFile.WriteBytes(Encoding.UTF8.GetBytes(CTBv4_DISCLAIMER));
+ Helpers.SerializeWriteFileStream(outputFile, PrintParametersV4Settings);
}
+ }
- HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position;
- uint layerDefSize = (uint)Helpers.Serializer.SizeOf(new LayerDef());
- //uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + layerDefSize * LayerCount * HeaderSettings.AntiAliasLevel;
+ HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position;
+ uint layerDefSize = (uint)Helpers.Serializer.SizeOf(new LayerDef());
+ //uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + layerDefSize * LayerCount * HeaderSettings.AntiAliasLevel;
- var layersHash = new Dictionary<string, LayerDef>();
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var layersHash = new Dictionary<string, LayerDef>();
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- foreach (var batch in BatchLayersIndexes())
+ foreach (var batch in BatchLayersIndexes())
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = this[layerIndex].LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = this[layerIndex].LayerMat)
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
{
- for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
- {
- var layerDef = new LayerDef(this, this[layerIndex]);
- layerDef.Encode(mat, aaIndex, (uint)layerIndex);
- LayerDefinitions[aaIndex, layerIndex] = layerDef;
- }
+ var layerDef = new LayerDef(this, this[layerIndex]);
+ layerDef.Encode(mat!, aaIndex, (uint)layerIndex);
+ LayerDefinitions[aaIndex, layerIndex] = layerDef;
}
- progress.LockAndIncrement();
- });
+ }
+ progress.LockAndIncrement();
+ });
- foreach (var layerIndex in batch)
+ foreach (var layerIndex in batch)
+ {
+ if (layerIndex == 0) layerDefSize = (uint)Helpers.Serializer.SizeOf(LayerDefinitions[0, layerIndex]);
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
{
- if (layerIndex == 0) layerDefSize = (uint)Helpers.Serializer.SizeOf(LayerDefinitions[0, layerIndex]);
- for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- var layerDef = LayerDefinitions[aaIndex, layerIndex];
- LayerDef layerDefHash = null;
+ var layerDef = LayerDefinitions[aaIndex, layerIndex];
+ LayerDef? layerDefHash = null;
- if (CanHash)
+ if (CanHash)
+ {
+ var hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle!);
+ if (layersHash.TryGetValue(hash, out layerDefHash))
{
- var hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle);
- if (layersHash.TryGetValue(hash, out layerDefHash))
- {
- layerDef.DataAddress = layerDefHash.DataAddress;
- layerDef.DataSize = layerDefHash.DataSize;
- }
- else
- {
- layersHash.Add(hash, layerDef);
- }
+ layerDef.DataAddress = layerDefHash.DataAddress;
+ layerDef.DataSize = layerDefHash.DataSize;
}
-
- if (layerDefHash is null)
+ else
{
- layerDef.DataAddress = layerDataCurrentOffset;
- outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
+ layersHash.Add(hash, layerDef);
+ }
+ }
- if (HeaderSettings.Version >= 3)
- {
- var layerDataEx = new LayerDefEx(layerDef, this[layerIndex]);
- layerDataCurrentOffset += (uint)Helpers.Serializer.SizeOf(layerDataEx);
- layerDef.DataAddress = layerDataCurrentOffset;
- Helpers.SerializeWriteFileStream(outputFile, layerDataEx);
- }
+ if (layerDefHash is null)
+ {
+ layerDef.DataAddress = layerDataCurrentOffset;
+ outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
- layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle);
+ if (HeaderSettings.Version >= 3)
+ {
+ var layerDataEx = new LayerDefEx(layerDef, this[layerIndex]);
+ layerDataCurrentOffset += (uint)Helpers.Serializer.SizeOf(layerDataEx);
+ layerDef.DataAddress = layerDataCurrentOffset;
+ Helpers.SerializeWriteFileStream(outputFile, layerDataEx);
}
- outputFile.Seek(HeaderSettings.LayersDefinitionOffsetAddress +
- aaIndex * LayerCount * layerDefSize +
- layerDefSize * layerIndex
- , SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, layerDef);
-
- layerDef.EncodedRle = null; // Free this
+ layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle!);
}
+
+ outputFile.Seek(HeaderSettings.LayersDefinitionOffsetAddress +
+ aaIndex * LayerCount * layerDefSize +
+ layerDefSize * layerIndex
+ , SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, layerDef);
+
+ layerDef.EncodedRle = null; // Free this
}
}
+ }
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(Previews[0]);
+ Debug.WriteLine(Previews[1]);
+ Debug.WriteLine(PrintParametersSettings);
+ Debug.WriteLine(SlicerInfoSettings);
+ Debug.WriteLine(PrintParametersV4Settings);
+ Debug.WriteLine("-End-");
+ }
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine(Previews[0]);
- Debug.WriteLine(Previews[1]);
- Debug.WriteLine(PrintParametersSettings);
- Debug.WriteLine(SlicerInfoSettings);
- Debug.WriteLine(PrintParametersV4Settings);
- Debug.WriteLine("-End-");
- }
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
+ //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- protected override void DecodeInternally(OperationProgress progress)
+ if (HeaderSettings.Magic is not MAGIC_CBDDLP and not MAGIC_CTB and not MAGIC_CTBv4)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
- //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
-
- if (HeaderSettings.Magic is not MAGIC_CBDDLP and not MAGIC_CTB and not MAGIC_CTBv4)
- {
- throw new FileLoadException($"Not a valid PHOTON nor CBDDLP nor CTB file! Magic Value: {HeaderSettings.Magic}", FileFullPath);
- }
+ throw new FileLoadException($"Not a valid PHOTON nor CBDDLP nor CTB file! Magic Value: {HeaderSettings.Magic}", FileFullPath);
+ }
- if (HeaderSettings.Version == 1 || HeaderSettings.AntiAliasLevel == 0)
- {
- HeaderSettings.AntiAliasLevel = 1;
- }
+ if (HeaderSettings.Version == 1 || HeaderSettings.AntiAliasLevel == 0)
+ {
+ HeaderSettings.AntiAliasLevel = 1;
+ }
- progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
+ progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
- Debug.Write("Header -> ");
- Debug.WriteLine(HeaderSettings);
+ Debug.Write("Header -> ");
+ Debug.WriteLine(HeaderSettings);
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- uint offsetAddress = i == 0
- ? HeaderSettings.PreviewLargeOffsetAddress
- : HeaderSettings.PreviewSmallOffsetAddress;
- if (offsetAddress == 0) continue;
+ for (byte i = 0; i < ThumbnailsCount; i++)
+ {
+ uint offsetAddress = i == 0
+ ? HeaderSettings.PreviewLargeOffsetAddress
+ : HeaderSettings.PreviewSmallOffsetAddress;
+ if (offsetAddress == 0) continue;
- inputFile.Seek(offsetAddress, SeekOrigin.Begin);
- Previews[i] = Helpers.Deserialize<Preview>(inputFile);
+ inputFile.Seek(offsetAddress, SeekOrigin.Begin);
+ Previews[i] = Helpers.Deserialize<Preview>(inputFile);
- Debug.Write($"Preview {i} -> ");
- Debug.WriteLine(Previews[i]);
+ Debug.Write($"Preview {i} -> ");
+ Debug.WriteLine(Previews[i]);
- inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
- byte[] rawImageData = new byte[Previews[i].ImageLength];
- inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength);
+ inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
+ byte[] rawImageData = new byte[Previews[i].ImageLength];
+ inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength);
- Thumbnails[i] = Previews[i].Decode(rawImageData);
- progress++;
- }
+ Thumbnails![i] = Previews[i].Decode(rawImageData);
+ progress++;
+ }
- if (HeaderSettings.PrintParametersOffsetAddress > 0)
- {
- inputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin);
- PrintParametersSettings = Helpers.Deserialize<PrintParameters>(inputFile);
- Debug.Write("Print Parameters -> ");
- Debug.WriteLine(PrintParametersSettings);
+ if (HeaderSettings.PrintParametersOffsetAddress > 0)
+ {
+ inputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin);
+ PrintParametersSettings = Helpers.Deserialize<PrintParameters>(inputFile);
+ Debug.Write("Print Parameters -> ");
+ Debug.WriteLine(PrintParametersSettings);
- }
+ }
- if (HeaderSettings.SlicerOffset > 0)
- {
- inputFile.Seek(HeaderSettings.SlicerOffset, SeekOrigin.Begin);
- SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
- Debug.Write("Slicer Info -> ");
- Debug.WriteLine(SlicerInfoSettings);
- }
+ if (HeaderSettings.SlicerOffset > 0)
+ {
+ inputFile.Seek(HeaderSettings.SlicerOffset, SeekOrigin.Begin);
+ SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
+ Debug.Write("Slicer Info -> ");
+ Debug.WriteLine(SlicerInfoSettings);
+ }
- /*InputFile.BaseStream.Seek(MachineInfoSettings.MachineNameAddress, SeekOrigin.Begin);
- byte[] bytes = InputFile.ReadBytes((int)MachineInfoSettings.MachineNameSize);
- MachineName = System.Text.Encoding.UTF8.GetString(bytes);
- Debug.WriteLine($"{nameof(MachineName)}: {MachineName}");*/
- //}
+ /*InputFile.BaseStream.Seek(MachineInfoSettings.MachineNameAddress, SeekOrigin.Begin);
+ byte[] bytes = InputFile.ReadBytes((int)MachineInfoSettings.MachineNameSize);
+ MachineName = System.Text.Encoding.UTF8.GetString(bytes);
+ Debug.WriteLine($"{nameof(MachineName)}: {MachineName}");*/
+ //}
- if (HeaderSettings.Version >= 4)
+ if (HeaderSettings.Version >= 4)
+ {
+ if (SlicerInfoSettings.PrintParametersV4Address == 0)
{
- if (SlicerInfoSettings.PrintParametersV4Address == 0)
- {
- throw new FileLoadException(
- $"Malformed file, PrintParametersV4Address is missing",
- FileFullPath);
- }
+ throw new FileLoadException(
+ $"Malformed file, PrintParametersV4Address is missing",
+ FileFullPath);
+ }
- inputFile.Seek(SlicerInfoSettings.PrintParametersV4Address, SeekOrigin.Begin);
- PrintParametersV4Settings = Helpers.Deserialize<PrintParametersV4>(inputFile);
- Debug.Write("Print Parameters V4 -> ");
- Debug.WriteLine(PrintParametersV4Settings);
+ inputFile.Seek(SlicerInfoSettings.PrintParametersV4Address, SeekOrigin.Begin);
+ PrintParametersV4Settings = Helpers.Deserialize<PrintParametersV4>(inputFile);
+ Debug.Write("Print Parameters V4 -> ");
+ Debug.WriteLine(PrintParametersV4Settings);
- /*if (PrintParametersV4Settings.Four1 != 4 && PrintParametersV4Settings.Four2 != 4)
- {
- throw new FileLoadException(
- $"Malformed file, PrintParametersV4 found invalid validation values, expected (4, 4) " +
- $"but got ({PrintParametersV4Settings.Four1}, {PrintParametersV4Settings.Four2})",
- FileFullPath);
- }*/
- }
+ /*if (PrintParametersV4Settings.Four1 != 4 && PrintParametersV4Settings.Four2 != 4)
+ {
+ throw new FileLoadException(
+ $"Malformed file, PrintParametersV4 found invalid validation values, expected (4, 4) " +
+ $"but got ({PrintParametersV4Settings.Four1}, {PrintParametersV4Settings.Four2})",
+ FileFullPath);
+ }*/
+ }
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, LayerCount];
- var layerDefinitionsEx = HeaderSettings.Version >= 3 ? new LayerDefEx[LayerCount] : null;
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, LayerCount];
+ var layerDefinitionsEx = HeaderSettings.Version >= 3 ? new LayerDefEx[LayerCount] : null;
- uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.AntiAliasLevel * LayerCount);
+ progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.AntiAliasLevel * LayerCount);
- for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ {
+ Debug.WriteLine($"-Image GROUP {aaIndex}-");
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- Debug.WriteLine($"-Image GROUP {aaIndex}-");
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- inputFile.Seek(layerOffset, SeekOrigin.Begin);
- var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
- layerDef.Parent = this;
- LayerDefinitions[aaIndex, layerIndex] = layerDef;
- LayerDefinitions[aaIndex, layerIndex].Parent = this;
+ progress.Token.ThrowIfCancellationRequested();
+ inputFile.Seek(layerOffset, SeekOrigin.Begin);
+ var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
+ layerDef.Parent = this;
+ LayerDefinitions[aaIndex, layerIndex] = layerDef;
+ LayerDefinitions[aaIndex, layerIndex].Parent = this;
- layerOffset += (uint) Helpers.Serializer.SizeOf(layerDef);
- Debug.Write($"LAYER {layerIndex} -> ");
- Debug.WriteLine(layerDef);
+ layerOffset += (uint) Helpers.Serializer.SizeOf(layerDef);
+ Debug.Write($"LAYER {layerIndex} -> ");
+ Debug.WriteLine(layerDef);
- //layerDef.EncodedRle = new byte[layerDef.DataSize];
+ //layerDef.EncodedRle = new byte[layerDef.DataSize];
- if (HeaderSettings.Version >= 3)
+ if (HeaderSettings.Version >= 3)
+ {
+ inputFile.SeekDoWorkAndRewind(layerDef.DataAddress - 84, () =>
{
- inputFile.SeekDoWorkAndRewind(layerDef.DataAddress - 84, () =>
- {
- layerDefinitionsEx[layerIndex] = Helpers.Deserialize<LayerDefEx>(inputFile);
- layerDefinitionsEx[layerIndex].LayerDef.Parent = this;
- Debug.Write($"LAYER {layerIndex} -> ");
- Debug.WriteLine(layerDefinitionsEx[layerIndex]);
- });
- }
-
- progress++;
+ layerDefinitionsEx![layerIndex] = Helpers.Deserialize<LayerDefEx>(inputFile);
+ layerDefinitionsEx[layerIndex].LayerDef.Parent = this;
+ Debug.Write($"LAYER {layerIndex} -> ");
+ Debug.WriteLine(layerDefinitionsEx[layerIndex]);
+ });
}
+
+ progress++;
}
+ }
- if (DecodeType == FileDecodeType.Full)
- {
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- foreach (var batch in BatchLayersIndexes())
+ foreach (var batch in BatchLayersIndexes())
+ {
+ foreach (var layerIndex in batch)
{
- foreach (var layerIndex in batch)
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
{
- for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- inputFile.Seek(LayerDefinitions[aaIndex, layerIndex].DataAddress, SeekOrigin.Begin);
- LayerDefinitions[aaIndex, layerIndex].EncodedRle = inputFile.ReadBytes(LayerDefinitions[aaIndex, layerIndex].DataSize);
- }
+ inputFile.Seek(LayerDefinitions[aaIndex, layerIndex].DataAddress, SeekOrigin.Begin);
+ LayerDefinitions[aaIndex, layerIndex].EncodedRle = inputFile.ReadBytes(LayerDefinitions[aaIndex, layerIndex].DataSize);
}
-
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = LayerDefinitions[0, layerIndex].Decode((uint)layerIndex);
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
-
- progress.LockAndIncrement();
- });
}
- }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- if (layerDefinitionsEx is not null) // CTBv4
- {
- layerDefinitionsEx[layerIndex].CopyTo(this[layerIndex]);
- }
- else // others
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
{
- LayerDefinitions[0, layerIndex].CopyTo(this[layerIndex]);
- }
- }
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = LayerDefinitions[0, layerIndex].Decode((uint)layerIndex);
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ progress.LockAndIncrement();
+ });
+ }
}
- protected override void PartialSaveInternally(OperationProgress progress)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- SanitizeProperties();
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
-
- if (HeaderSettings.Version >= 2 && HeaderSettings.PrintParametersOffsetAddress > 0)
+ if (layerDefinitionsEx is not null) // CTBv4
{
- outputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings);
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ layerDefinitionsEx[layerIndex].CopyTo(this[layerIndex]);
}
-
- uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ else // others
{
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- var layer = this[layerIndex];
- LayerDefinitions[aaIndex, layerIndex].SetFrom(layer);
-
- outputFile.Seek(layerOffset, SeekOrigin.Begin);
- layerOffset += Helpers.SerializeWriteFileStream(outputFile, LayerDefinitions[aaIndex, layerIndex]);
- }
+ LayerDefinitions[0, layerIndex].CopyTo(this[layerIndex]);
}
+ }
- if (HeaderSettings.Version >= 3)
+ }
+
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ SanitizeProperties();
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ if (HeaderSettings.Version >= 2 && HeaderSettings.PrintParametersOffsetAddress > 0)
+ {
+ outputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings);
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ }
+
+ uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
+ {
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- outputFile.Seek(LayerDefinitions[0, layerIndex].DataAddress - 84, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, new LayerDefEx(LayerDefinitions[0, layerIndex], this[layerIndex]));
- }
+ var layer = this[layerIndex];
+ LayerDefinitions![aaIndex, layerIndex].SetFrom(layer);
+
+ outputFile.Seek(layerOffset, SeekOrigin.Begin);
+ layerOffset += Helpers.SerializeWriteFileStream(outputFile, LayerDefinitions[aaIndex, layerIndex]);
}
+ }
- if (HeaderSettings.Version >= 4)
+ if (HeaderSettings.Version >= 3)
+ {
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- outputFile.Seek(SlicerInfoSettings.PrintParametersV4Address, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, PrintParametersV4Settings);
+ outputFile.Seek(LayerDefinitions![0, layerIndex].DataAddress - 84, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, new LayerDefEx(LayerDefinitions[0, layerIndex], this[layerIndex]));
}
}
- #endregion
-
- #region Static Methods
- public static byte[] LayerRleCrypt(uint seed, uint layerIndex, IEnumerable<byte> input)
+ if (HeaderSettings.Version >= 4)
{
- var result = input.ToArray();
- LayerRleCryptBuffer(seed, layerIndex, result);
- return result;
+ outputFile.Seek(SlicerInfoSettings.PrintParametersV4Address, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, PrintParametersV4Settings);
}
+ }
- public static void LayerRleCryptBuffer(uint seed, uint layerIndex, byte[] input)
- {
- if (seed == 0) return;
- var init = seed * 0x2d83cdac + 0xd8a83423;
- var key = (layerIndex * 0x1e1530cd + 0xec3d47cd) * init;
+ #endregion
- int index = 0;
- for (int i = 0; i < input.Length; i++)
- {
- var k = (byte)(key >> 8 * index);
+ #region Static Methods
+ public static byte[] LayerRleCrypt(uint seed, uint layerIndex, IEnumerable<byte> input)
+ {
+ var result = input.ToArray();
+ LayerRleCryptBuffer(seed, layerIndex, result);
+ return result;
+ }
- index++;
+ public static void LayerRleCryptBuffer(uint seed, uint layerIndex, byte[] input)
+ {
+ if (seed == 0) return;
+ var init = seed * 0x2d83cdac + 0xd8a83423;
+ var key = (layerIndex * 0x1e1530cd + 0xec3d47cd) * init;
- if ((index & 3) == 0)
- {
- key += init;
- index = 0;
- }
+ int index = 0;
+ for (int i = 0; i < input.Length; i++)
+ {
+ var k = (byte)(key >> 8 * index);
- input[i] = (byte)(input[i] ^ k);
+ index++;
+
+ if ((index & 3) == 0)
+ {
+ key += init;
+ index = 0;
}
+
+ input[i] = (byte)(input[i] ^ k);
}
- #endregion
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs
index 3155454..11dcf0b 100644
--- a/UVtools.Core/FileFormats/ChituboxZipFile.cs
+++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs
@@ -6,6 +6,9 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Util;
using System;
using System.ComponentModel;
using System.Diagnostics;
@@ -14,554 +17,549 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Util;
using UVtools.Core.Extensions;
using UVtools.Core.GCode;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class ChituboxZipFile : FileFormat
{
- public class ChituboxZipFile : FileFormat
- {
- #region Constants
+ #region Constants
- public const string GCodeFilename = "run.gcode";
- #endregion
+ public const string GCodeFilename = "run.gcode";
+ #endregion
- #region Sub Classes
+ #region Sub Classes
- public class Header
- {
- // ;(****Build and Slicing Parameters****)
- [DisplayName("fileName")] public string Filename { get; set; } = string.Empty;
- [DisplayName("machineType")] public string MachineType { get; set; } = "Default";
- [DisplayName("estimatedPrintTime")] public float EstimatedPrintTime { get; set; }
- [DisplayName("volume")] public float VolumeMl { get; set; }
- [DisplayName("resin")] public string Resin { get; set; } = "Normal";
- [DisplayName("weight")] public float WeightG { get; set; }
- [DisplayName("price")] public float Price { get; set; }
- [DisplayName("layerHeight")] public float LayerHeight { get; set; }
- [DisplayName("resolutionX")] public uint ResolutionX { get; set; }
- [DisplayName("resolutionY")] public uint ResolutionY { get; set; }
- [DisplayName("machineX")] public float MachineX { get; set; }
- [DisplayName("machineY")] public float MachineY { get; set; }
- [DisplayName("machineZ")] public float MachineZ { get; set; }
- [DisplayName("projectType")] public string ProjectType { get; set; } = "Normal";
- [DisplayName("normalExposureTime")] public float ExposureTime { get; set; } = 7; // 35s
- [DisplayName("bottomLayExposureTime")] public float BottomLayExposureTime { get; set; } = 35; // 35s
- [DisplayName("bottomLayerExposureTime")] public float BottomExposureTime { get; set; } = 35; // 35s
- [DisplayName("normalDropSpeed")] public float RetractSpeed { get; set; } = 150; // 150 mm/m
- [DisplayName("normalLayerLiftSpeed")] public float LiftSpeed { get; set; } = 60; // 60 mm/m
- [DisplayName("normalLayerLiftHeight")] public float LiftHeight { get; set; } = 5; // 5 mm
- [DisplayName("zSlowUpDistance")] public float ZSlowUpDistance { get; set; }
- [DisplayName("bottomLayCount")] public ushort BottomLayCount { get; set; } = 4;
- [DisplayName("bottomLayerCount")] public ushort BottomLayerCount { get; set; } = 4;
- [DisplayName("mirror")] public byte Mirror { get; set; } // 0/1
- [DisplayName("totalLayer")] public uint LayerCount { get; set; }
- [DisplayName("bottomLayerLiftHeight")] public float BottomLiftHeight { get; set; } = 5;
- [DisplayName("bottomLayerLiftSpeed")] public float BottomLiftSpeed { get; set; } = 60;
- [DisplayName("bottomLightOffTime")] public float BottomLightOffDelay { get; set; }
- [DisplayName("lightOffTime")] public float LightOffDelay { get; set; }
- [DisplayName("bottomPWMLight")] public byte BottomLightPWM { get; set; } = 255;
- [DisplayName("PWMLight")] public byte LightPWM { get; set; } = 255;
- [DisplayName("antiAliasLevel")] public byte AntiAliasing { get; set; } = 1;
- }
+ public class Header
+ {
+ // ;(****Build and Slicing Parameters****)
+ [DisplayName("fileName")] public string Filename { get; set; } = string.Empty;
+ [DisplayName("machineType")] public string MachineType { get; set; } = "Default";
+ [DisplayName("estimatedPrintTime")] public float EstimatedPrintTime { get; set; }
+ [DisplayName("volume")] public float VolumeMl { get; set; }
+ [DisplayName("resin")] public string? Resin { get; set; } = "Normal";
+ [DisplayName("weight")] public float WeightG { get; set; }
+ [DisplayName("price")] public float Price { get; set; }
+ [DisplayName("layerHeight")] public float LayerHeight { get; set; }
+ [DisplayName("resolutionX")] public uint ResolutionX { get; set; }
+ [DisplayName("resolutionY")] public uint ResolutionY { get; set; }
+ [DisplayName("machineX")] public float MachineX { get; set; }
+ [DisplayName("machineY")] public float MachineY { get; set; }
+ [DisplayName("machineZ")] public float MachineZ { get; set; }
+ [DisplayName("projectType")] public string ProjectType { get; set; } = "Normal";
+ [DisplayName("normalExposureTime")] public float ExposureTime { get; set; } = 7; // 35s
+ [DisplayName("bottomLayExposureTime")] public float BottomLayExposureTime { get; set; } = 35; // 35s
+ [DisplayName("bottomLayerExposureTime")] public float BottomExposureTime { get; set; } = 35; // 35s
+ [DisplayName("normalDropSpeed")] public float RetractSpeed { get; set; } = 150; // 150 mm/m
+ [DisplayName("normalLayerLiftSpeed")] public float LiftSpeed { get; set; } = 60; // 60 mm/m
+ [DisplayName("normalLayerLiftHeight")] public float LiftHeight { get; set; } = 5; // 5 mm
+ [DisplayName("zSlowUpDistance")] public float ZSlowUpDistance { get; set; }
+ [DisplayName("bottomLayCount")] public ushort BottomLayCount { get; set; } = 4;
+ [DisplayName("bottomLayerCount")] public ushort BottomLayerCount { get; set; } = 4;
+ [DisplayName("mirror")] public byte Mirror { get; set; } // 0/1
+ [DisplayName("totalLayer")] public uint LayerCount { get; set; }
+ [DisplayName("bottomLayerLiftHeight")] public float BottomLiftHeight { get; set; } = 5;
+ [DisplayName("bottomLayerLiftSpeed")] public float BottomLiftSpeed { get; set; } = 60;
+ [DisplayName("bottomLightOffTime")] public float BottomLightOffDelay { get; set; }
+ [DisplayName("lightOffTime")] public float LightOffDelay { get; set; }
+ [DisplayName("bottomPWMLight")] public byte BottomLightPWM { get; set; } = 255;
+ [DisplayName("PWMLight")] public byte LightPWM { get; set; } = 255;
+ [DisplayName("antiAliasLevel")] public byte AntiAliasing { get; set; } = 1;
+ }
- #endregion
+ #endregion
- #region Properties
- public Header HeaderSettings { get; } = new Header();
+ #region Properties
+ public Header HeaderSettings { get; } = new Header();
- public override FileFormatType FileType => FileFormatType.Archive;
+ public override FileFormatType FileType => FileFormatType.Archive;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(ChituboxZipFile), "zip", "Chitubox Zip")
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(ChituboxZipFile), "zip", "Chitubox Zip")
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
-
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
-
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
-
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
-
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
-
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
-
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
-
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
-
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.LightPWM,
- };
-
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(954, 850),
- new(168, 150)
- };
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
+
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(954, 850),
+ new(168, 150)
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => HeaderSettings.MachineX;
+ set
{
- get => HeaderSettings.MachineX;
- set
- {
- HeaderSettings.MachineX = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.MachineX = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => HeaderSettings.MachineY;
+ set
{
- get => HeaderSettings.MachineY;
- set
- {
- HeaderSettings.MachineY = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.MachineY = value;
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
- {
- get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ;
- set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2);
- }
+ public override float MachineZ
+ {
+ get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ;
+ set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2);
+ }
- public override Enumerations.FlipDirection DisplayMirror
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => HeaderSettings.Mirror == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
+ set
{
- get => HeaderSettings.Mirror == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
- set
- {
- HeaderSettings.ProjectType = value == Enumerations.FlipDirection.None ? "Normal" : "LCD_mirror";
- HeaderSettings.Mirror = (byte)(value == Enumerations.FlipDirection.None ? 0 : 1);
- RaisePropertyChanged();
- }
+ HeaderSettings.ProjectType = value == Enumerations.FlipDirection.None ? "Normal" : "LCD_mirror";
+ HeaderSettings.Mirror = (byte)(value == Enumerations.FlipDirection.None ? 0 : 1);
+ RaisePropertyChanged();
}
+ }
- public override byte AntiAliasing
- {
- get => HeaderSettings.AntiAliasing;
- set => base.AntiAliasing = HeaderSettings.AntiAliasing = value.Clamp(1, 16);
- }
+ public override byte AntiAliasing
+ {
+ get => HeaderSettings.AntiAliasing;
+ set => base.AntiAliasing = HeaderSettings.AntiAliasing = value.Clamp(1, 16);
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => HeaderSettings.LayerHeight;
+ set
{
- get => HeaderSettings.LayerHeight;
- set
- {
- HeaderSettings.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ HeaderSettings.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
+ }
- public override ushort BottomLayerCount
- {
- get => HeaderSettings.BottomLayerCount;
- set => base.BottomLayerCount = HeaderSettings.BottomLayerCount = HeaderSettings.BottomLayCount = value;
- }
+ public override ushort BottomLayerCount
+ {
+ get => HeaderSettings.BottomLayerCount;
+ set => base.BottomLayerCount = HeaderSettings.BottomLayerCount = HeaderSettings.BottomLayCount = value;
+ }
- public override float BottomLightOffDelay
- {
- get => BottomWaitTimeBeforeCure;
- set => BottomWaitTimeBeforeCure = value;
- }
+ public override float BottomLightOffDelay
+ {
+ get => BottomWaitTimeBeforeCure;
+ set => BottomWaitTimeBeforeCure = value;
+ }
- public override float LightOffDelay
- {
- get => WaitTimeBeforeCure;
- set => WaitTimeBeforeCure = value;
- }
+ public override float LightOffDelay
+ {
+ get => WaitTimeBeforeCure;
+ set => WaitTimeBeforeCure = value;
+ }
- public override float BottomWaitTimeBeforeCure
- {
- get => HeaderSettings.BottomLightOffDelay;
- set => base.BottomWaitTimeBeforeCure = HeaderSettings.BottomLightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => HeaderSettings.BottomLightOffDelay;
+ set => base.BottomWaitTimeBeforeCure = HeaderSettings.BottomLightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float WaitTimeBeforeCure
- {
- get => HeaderSettings.LightOffDelay;
- set => base.WaitTimeBeforeCure = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float WaitTimeBeforeCure
+ {
+ get => HeaderSettings.LightOffDelay;
+ set => base.WaitTimeBeforeCure = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float BottomExposureTime
- {
- get => HeaderSettings.BottomExposureTime;
- set => base.BottomExposureTime = HeaderSettings.BottomExposureTime = HeaderSettings.BottomLayExposureTime = (float)Math.Round(value, 2);
- }
+ public override float BottomExposureTime
+ {
+ get => HeaderSettings.BottomExposureTime;
+ set => base.BottomExposureTime = HeaderSettings.BottomExposureTime = HeaderSettings.BottomLayExposureTime = (float)Math.Round(value, 2);
+ }
- public override float ExposureTime
- {
- get => HeaderSettings.ExposureTime;
- set => base.ExposureTime = HeaderSettings.ExposureTime = (float)Math.Round(value, 2);
- }
+ public override float ExposureTime
+ {
+ get => HeaderSettings.ExposureTime;
+ set => base.ExposureTime = HeaderSettings.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 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 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 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 LiftSpeed
+ {
+ get => HeaderSettings.LiftSpeed;
+ set => base.LiftSpeed = HeaderSettings.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float RetractSpeed
- {
- get => HeaderSettings.RetractSpeed;
- set => base.RetractSpeed = HeaderSettings.RetractSpeed = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed
+ {
+ get => HeaderSettings.RetractSpeed;
+ set => base.RetractSpeed = HeaderSettings.RetractSpeed = (float)Math.Round(value, 2);
+ }
- public override byte BottomLightPWM
- {
- get => HeaderSettings.BottomLightPWM;
- set => base.BottomLightPWM = HeaderSettings.BottomLightPWM = value;
- }
+ public override byte BottomLightPWM
+ {
+ get => HeaderSettings.BottomLightPWM;
+ set => base.BottomLightPWM = HeaderSettings.BottomLightPWM = value;
+ }
- public override byte LightPWM
- {
- get => HeaderSettings.LightPWM;
- set => base.LightPWM = HeaderSettings.LightPWM = value;
- }
+ public override byte LightPWM
+ {
+ get => HeaderSettings.LightPWM;
+ set => base.LightPWM = HeaderSettings.LightPWM = value;
+ }
- public override float PrintTime
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- HeaderSettings.EstimatedPrintTime = base.PrintTime;
- }
+ base.PrintTime = value;
+ HeaderSettings.EstimatedPrintTime = base.PrintTime;
}
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- HeaderSettings.VolumeMl = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ HeaderSettings.VolumeMl = base.MaterialMilliliters;
}
+ }
- public override float MaterialGrams
+ public override float MaterialGrams
+ {
+ get => (float) Math.Round(HeaderSettings.WeightG, 3);
+ set
{
- get => (float) Math.Round(HeaderSettings.WeightG, 3);
- set
- {
- HeaderSettings.WeightG = (float)Math.Round(value, 3);
- RaisePropertyChanged();
- }
+ HeaderSettings.WeightG = (float)Math.Round(value, 3);
+ RaisePropertyChanged();
}
+ }
- public override float MaterialCost
+ public override float MaterialCost
+ {
+ get => (float) Math.Round(HeaderSettings.Price, 3);
+ set
{
- get => (float) Math.Round(HeaderSettings.Price, 3);
- set
- {
- HeaderSettings.Price = (float)Math.Round(value, 3);
- RaisePropertyChanged();
- }
+ HeaderSettings.Price = (float)Math.Round(value, 3);
+ RaisePropertyChanged();
}
+ }
- public override string MaterialName
+ public override string? MaterialName
+ {
+ get => HeaderSettings.Resin;
+ set
{
- get => HeaderSettings.Resin;
- set
- {
- HeaderSettings.Resin = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.Resin = value;
+ RaisePropertyChanged();
}
+ }
- public override string MachineName
- {
- get => HeaderSettings.MachineType;
- set => base.MachineName = HeaderSettings.MachineType = value;
- }
+ public override string MachineName
+ {
+ get => HeaderSettings.MachineType;
+ set => base.MachineName = HeaderSettings.MachineType = value;
+ }
- public override object[] Configs => new object[] { HeaderSettings };
+ public override object[] Configs => new object[] { HeaderSettings };
- public override bool SupportsGCode => base.SupportsGCode && !IsPHZZip;
+ public override bool SupportsGCode => base.SupportsGCode && !IsPHZZip;
- public bool IsPHZZip;
- #endregion
+ public bool IsPHZZip;
+ #endregion
- #region Constructor
- public ChituboxZipFile()
- {
- GCode = new GCodeBuilder
- {
- UseComments = true,
- GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute,
- GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
- GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
- GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenamePng1Started,
- LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
- EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1
- };
- }
- #endregion
+ #region Constructor
+ public ChituboxZipFile()
+ {
+ GCode = new GCodeBuilder
+ {
+ UseComments = true,
+ GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute,
+ GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
+ GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
+ GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenamePng1Started,
+ LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
+ EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1
+ };
+ }
+ #endregion
- #region Methods
+ #region Methods
- public override bool CanProcess(string fileFullPath)
+ public override bool CanProcess(string fileFullPath)
+ {
+ if (!base.CanProcess(fileFullPath)) return false;
+
+ try
+ {
+ var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read);
+ if (zip.Entries.Any(entry => entry.Name.EndsWith(".gcode"))) return true;
+ }
+ catch (Exception e)
{
- if (!base.CanProcess(fileFullPath)) return false;
+ Debug.WriteLine(e);
+ }
- try
- {
- var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read);
- if (zip.Entries.Any(entry => entry.Name.EndsWith(".gcode"))) return true;
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
+ return false;
+ }
- return false;
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ if (Thumbnails is not null && Thumbnails.Length > 0 && Thumbnails[0] is not null)
+ {
+ using var stream = outputFile.CreateEntry("preview.png").Open();
+ stream.WriteBytes(Thumbnails[0]!.GetPngByes());
+ stream.Close();
}
- protected override void EncodeInternally(OperationProgress progress)
+ if (Thumbnails is not null && Thumbnails.Length > 1 && Thumbnails[1] is not null)
{
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- if (Thumbnails.Length > 0 && Thumbnails[0] is not null)
- {
- using var stream = outputFile.CreateEntry("preview.png").Open();
- stream.WriteBytes(Thumbnails[0].GetPngByes());
- stream.Close();
- }
-
- if (Thumbnails.Length > 1 && Thumbnails[1] is not null)
- {
- using var stream = outputFile.CreateEntry("preview_cropping.png").Open();
- using var vec = new VectorOfByte();
- stream.WriteBytes(Thumbnails[1].GetPngByes());
- stream.Close();
- }
+ using var stream = outputFile.CreateEntry("preview_cropping.png").Open();
+ using var vec = new VectorOfByte();
+ stream.WriteBytes(Thumbnails[1]!.GetPngByes());
+ stream.Close();
+ }
- if (!IsPHZZip)
- {
- RebuildGCode();
- outputFile.PutFileContent(GCodeFilename, GCodeStr, ZipArchiveMode.Create);
- }
+ if (!IsPHZZip)
+ {
+ RebuildGCode();
+ outputFile.PutFileContent(GCodeFilename, GCodeStr, ZipArchiveMode.Create);
+ }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- var layer = this[layerIndex];
- outputFile.PutFileContent($"{layerIndex + 1}.png", layer.CompressedBytes,
- ZipArchiveMode.Create);
- progress++;
- }
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ var layer = this[layerIndex];
+ outputFile.PutFileContent($"{layerIndex + 1}.png", layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
}
+ }
- protected override void DecodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using (var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read))
{
- using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
+ var entry = inputFile.GetEntry(GCodeFilename);
+ if (entry is not null)
{
- var entry = inputFile.GetEntry(GCodeFilename);
- if (entry is not null)
- {
- //Clear();
- //throw new FileLoadException("run.gcode not found", fileFullPath);
- using TextReader tr = new StreamReader(entry.Open());
- string line;
- GCode.Clear();
- while ((line = tr.ReadLine()) != null)
- {
- GCode.AppendLine(line);
- if (string.IsNullOrEmpty(line)) continue;
-
- if (line[0] != ';')
- {
- continue;
- }
-
- var splitLine = line.Split(':');
- if (splitLine.Length < 2) continue;
-
- foreach (var propertyInfo in HeaderSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
- if (displayNameAttribute is null) continue;
- if (!splitLine[0].Trim(' ', ';').Equals(displayNameAttribute.DisplayName)) continue;
- Helpers.SetPropertyValue(propertyInfo, HeaderSettings, splitLine[1].Trim());
- }
- }
- tr.Close();
- }
- else
+ //Clear();
+ //throw new FileLoadException("run.gcode not found", fileFullPath);
+ using TextReader tr = new StreamReader(entry.Open());
+ string? line;
+ GCode!.Clear();
+ while ((line = tr.ReadLine()) != null)
{
- IsPHZZip = true;
- }
+ GCode.AppendLine(line);
+ if (string.IsNullOrEmpty(line)) continue;
- if (HeaderSettings.LayerCount == 0)
- {
- foreach (var zipEntry in inputFile.Entries)
+ if (line[0] != ';')
{
- if(!zipEntry.Name.EndsWith(".png")) continue;
- var filename = Path.GetFileNameWithoutExtension(zipEntry.Name);
- if (!filename.All(char.IsDigit)) continue;
- if (!uint.TryParse(filename, out var layerIndex)) continue;
- HeaderSettings.LayerCount = Math.Max(HeaderSettings.LayerCount, layerIndex);
+ continue;
}
- }
-
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- progress.ItemCount = LayerCount;
+ var splitLine = line.Split(':');
+ if (splitLine.Length < 2) continue;
- if (DecodeType == FileDecodeType.Full)
- {
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ foreach (var propertyInfo in HeaderSettings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
- if (progress.Token.IsCancellationRequested) break;
- entry = inputFile.GetEntry($"{layerIndex + 1}.png");
- if (entry is null)
- {
- Clear();
- throw new FileLoadException($"Layer {layerIndex + 1} not found", FileFullPath);
- }
-
- using var stream = entry.Open();
- this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
-
- progress++;
+ var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
+ if (displayNameAttribute is null) continue;
+ if (!splitLine[0].Trim(' ', ';').Equals(displayNameAttribute.DisplayName)) continue;
+ Helpers.SetPropertyValue(propertyInfo, HeaderSettings, splitLine[1].Trim());
}
}
+ tr.Close();
+ }
+ else
+ {
+ IsPHZZip = true;
+ }
- if (IsPHZZip) // PHZ file
- {
- LayerManager.RebuildLayersProperties();
- }
- else
+ if (HeaderSettings.LayerCount == 0)
+ {
+ foreach (var zipEntry in inputFile.Entries)
{
- GCode.ParseLayersFromGCode(this);
+ if(!zipEntry.Name.EndsWith(".png")) continue;
+ var filename = Path.GetFileNameWithoutExtension(zipEntry.Name);
+ if (!filename.All(char.IsDigit)) continue;
+ if (!uint.TryParse(filename, out var layerIndex)) continue;
+ HeaderSettings.LayerCount = Math.Max(HeaderSettings.LayerCount, layerIndex);
}
+ }
- entry = inputFile.GetEntry("preview.png");
- if (entry is not null)
- {
- Thumbnails[0] = new Mat();
- CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
- }
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- entry = inputFile.GetEntry("preview_cropping.png");
- if (entry is not null)
+ progress.ItemCount = LayerCount;
+
+ if (DecodeType == FileDecodeType.Full)
+ {
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- var count = CreatedThumbnailsCount;
- Thumbnails[count] = new Mat();
- CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[count]);
+ if (progress.Token.IsCancellationRequested) break;
+ entry = inputFile.GetEntry($"{layerIndex + 1}.png");
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException($"Layer {layerIndex + 1} not found", FileFullPath);
+ }
+
+ using var stream = entry.Open();
+ this[layerIndex] = new Layer(layerIndex, stream, this);
+
+ progress++;
}
}
- LayerManager.GetBoundingRectangle(progress);
- }
-
- public override void RebuildGCode()
- {
- if (!SupportsGCode || SuppressRebuildGCode) return;
- GCode.RebuildGCode(this, new object[]{ HeaderSettings });
- RaisePropertyChanged(nameof(GCodeStr));
- }
+ if (IsPHZZip) // PHZ file
+ {
+ RebuildLayersProperties();
+ }
+ else
+ {
+ GCode?.ParseLayersFromGCode(this);
+ }
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- var entriesToRemove = outputFile.Entries.Where(zipEntry => zipEntry.Name.EndsWith(".gcode")).ToArray();
- foreach (var zipEntry in entriesToRemove)
+ entry = inputFile.GetEntry("preview.png");
+ if (entry is not null)
{
- zipEntry.Delete();
+ Thumbnails![0] = new Mat();
+ CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
}
- if (!IsPHZZip)
+ entry = inputFile.GetEntry("preview_cropping.png");
+ if (entry is not null)
{
- outputFile.PutFileContent(GCodeFilename, GCodeStr, ZipArchiveMode.Update);
+ var count = CreatedThumbnailsCount;
+ Thumbnails![count] = new Mat();
+ CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[count]);
}
+ }
+
+ GetBoundingRectangle(progress);
+ }
+
+ public override void RebuildGCode()
+ {
+ if (!SupportsGCode || SuppressRebuildGCode) return;
+ GCode?.RebuildGCode(this, new object[]{ HeaderSettings });
+ RaisePropertyChanged(nameof(GCodeStr));
+ }
- //Decode(FileFullPath, progress);
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ var entriesToRemove = outputFile.Entries.Where(zipEntry => zipEntry.Name.EndsWith(".gcode")).ToArray();
+ foreach (var zipEntry in entriesToRemove)
+ {
+ zipEntry.Delete();
+ }
+
+ if (!IsPHZZip)
+ {
+ outputFile.PutFileContent(GCodeFilename, GCodeStr, ZipArchiveMode.Update);
}
- #endregion
+
+ //Decode(FileFullPath, progress);
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs
index 6b45888..6edb011 100644
--- a/UVtools.Core/FileFormats/FDGFile.cs
+++ b/UVtools.Core/FileFormats/FDGFile.cs
@@ -8,6 +8,9 @@
// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -16,486 +19,499 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class FDGFile : FileFormat
{
- public class FDGFile : FileFormat
+ #region Constants
+ private const uint MAGIC = 0xBD3C7AC8; // 3174857416
+ private const ushort REPEATRGB15MASK = 0x20;
+
+ private const ushort RLE16EncodingLimit = 0x1000;
+ #endregion
+
+ #region Sub Classes
+ #region Header
+ public class Header
{
- #region Constants
- private const uint MAGIC = 0xBD3C7AC8; // 3174857416
- private const ushort REPEATRGB15MASK = 0x20;
+ private string _machineName = DefaultMachineName;
+
+ /// <summary>
+ /// Gets a magic number identifying the file type.
+ /// 0xBD3C7AC8 for fdg
+ /// </summary>
+ [FieldOrder(0)] public uint Magic { get; set; } = MAGIC;
+
+ /// <summary>
+ /// Gets the software version
+ /// </summary>
+ [FieldOrder(1)] public uint Version { get; set; } = 2;
+
+ /// <summary>
+ /// Gets the number of records in the layer table
+ /// </summary>
+ [FieldOrder(2)] public uint LayerCount { get; set; }
+
+ /// <summary>
+ /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig..
+ /// </summary>
+ [FieldOrder(3)] public uint BottomLayersCount { get; set; } = 10;
+
+ /// <summary>
+ /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose.
+ /// </summary>
+ [FieldOrder(4)] public uint ProjectorType { get; set; }
+
+ [FieldOrder(5)] public uint BottomLayersCount2 { get; set; } = 10; // ???
+
+ /// <summary>
+ /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(6)] public uint ResolutionX { get; set; }
+
+ /// <summary>
+ /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(7)] public uint ResolutionY { get; set; }
+
+ /// <summary>
+ /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(8)] public float LayerHeightMilimeter { get; set; }
- private const ushort RLE16EncodingLimit = 0x1000;
- #endregion
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(9)] public float LayerExposureSeconds { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(10)] public float BottomExposureSeconds { get; set; }
+
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the larger preview images.
+ /// </summary>
+ [FieldOrder(11)] public uint PreviewLargeOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the smaller preview images.
+ /// </summary>
+ [FieldOrder(12)] public uint PreviewSmallOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer.
+ /// </summary>
+ [FieldOrder(13)] public uint LayersDefinitionOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the estimated duration of print, in seconds.
+ /// </summary>
+ [FieldOrder(14)] public uint PrintTime { get; set; }
+
+ /// <summary>
+ /// ?
+ /// </summary>
+ [FieldOrder(15)] public uint AntiAliasLevel { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(16)] public ushort LightPWM { get; set; } = 255;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(17)] public ushort BottomLightPWM { get; set; } = 255;
+
+ [FieldOrder(18)] public uint Padding1 { get; set; }
+ [FieldOrder(19)] public uint Padding2 { get; set; }
+
+ /// <summary>
+ /// Gets the height of the model described by this file, in millimeters.
+ /// </summary>
+ [FieldOrder(20)] public float OverallHeightMilimeter { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s X output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(21)] public float BedSizeX { get; set; }
- #region Sub Classes
- #region Header
- public class Header
+ /// <summary>
+ /// Gets dimensions of the printer’s Y output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(22)] public float BedSizeY { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s Z output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(23)] public float BedSizeZ { get; set; }
+
+ /// <summary>
+ /// Gets the key used to encrypt layer data, or 0 if encryption is not used.
+ /// </summary>
+ [FieldOrder(24)] public uint EncryptionKey { get; set; }
+
+ [FieldOrder(25)] public uint AntiAliasLevelInfo { get; set; }
+ [FieldOrder(26)] public uint EncryptionMode { get; set; } = 0x4c;
+
+ /// <summary>
+ /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model.
+ /// </summary>
+ [FieldOrder(27)] public float VolumeMl { get; set; }
+
+ /// <summary>
+ /// Gets the estimated grams, derived from volume using configured factors for density.
+ /// </summary>
+ [FieldOrder(28)] public float WeightG { get; set; }
+
+ /// <summary>
+ /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost.
+ /// </summary>
+ [FieldOrder(29)] public float CostDollars { get; set; }
+
+ /// <summary>
+ /// Gets the machine name offset to a string naming the machine type, and its length in bytes.
+ /// </summary>
+ [FieldOrder(30)] public uint MachineNameAddress { get; set; }
+
+ /// <summary>
+ /// Gets the machine size in bytes
+ /// </summary>
+ [FieldOrder(31)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
+
+ /// <summary>
+ /// Gets the machine name. string is not nul-terminated.
+ /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only.
+ /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine.
+ /// </summary>
+ [Ignore]
+ public string MachineName
{
- private string _machineName = DefaultMachineName;
-
- /// <summary>
- /// Gets a magic number identifying the file type.
- /// 0xBD3C7AC8 for fdg
- /// </summary>
- [FieldOrder(0)] public uint Magic { get; set; } = MAGIC;
-
- /// <summary>
- /// Gets the software version
- /// </summary>
- [FieldOrder(1)] public uint Version { get; set; } = 2;
-
- /// <summary>
- /// Gets the number of records in the layer table
- /// </summary>
- [FieldOrder(2)] public uint LayerCount { get; set; }
-
- /// <summary>
- /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig..
- /// </summary>
- [FieldOrder(3)] public uint BottomLayersCount { get; set; } = 10;
-
- /// <summary>
- /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose.
- /// </summary>
- [FieldOrder(4)] public uint ProjectorType { get; set; }
-
- [FieldOrder(5)] public uint BottomLayersCount2 { get; set; } = 10; // ???
-
- /// <summary>
- /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(6)] public uint ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(7)] public uint ResolutionY { get; set; }
-
- /// <summary>
- /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(8)] public float LayerHeightMilimeter { get; set; }
-
- /// <summary>
- /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(9)] public float LayerExposureSeconds { get; set; }
-
- /// <summary>
- /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(10)] public float BottomExposureSeconds { get; set; }
-
- /// <summary>
- /// Gets the file offsets of ImageHeader records describing the larger preview images.
- /// </summary>
- [FieldOrder(11)] public uint PreviewLargeOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the file offsets of ImageHeader records describing the smaller preview images.
- /// </summary>
- [FieldOrder(12)] public uint PreviewSmallOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer.
- /// </summary>
- [FieldOrder(13)] public uint LayersDefinitionOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the estimated duration of print, in seconds.
- /// </summary>
- [FieldOrder(14)] public uint PrintTime { get; set; }
-
- /// <summary>
- /// ?
- /// </summary>
- [FieldOrder(15)] public uint AntiAliasLevel { get; set; } = 1;
-
- /// <summary>
- /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively.
- /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
- /// </summary>
- [FieldOrder(16)] public ushort LightPWM { get; set; } = 255;
-
- /// <summary>
- /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively.
- /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
- /// </summary>
- [FieldOrder(17)] public ushort BottomLightPWM { get; set; } = 255;
-
- [FieldOrder(18)] public uint Padding1 { get; set; }
- [FieldOrder(19)] public uint Padding2 { get; set; }
-
- /// <summary>
- /// Gets the height of the model described by this file, in millimeters.
- /// </summary>
- [FieldOrder(20)] public float OverallHeightMilimeter { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s X output volume, in millimeters.
- /// </summary>
- [FieldOrder(21)] public float BedSizeX { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s Y output volume, in millimeters.
- /// </summary>
- [FieldOrder(22)] public float BedSizeY { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s Z output volume, in millimeters.
- /// </summary>
- [FieldOrder(23)] public float BedSizeZ { get; set; }
-
- /// <summary>
- /// Gets the key used to encrypt layer data, or 0 if encryption is not used.
- /// </summary>
- [FieldOrder(24)] public uint EncryptionKey { get; set; }
-
- [FieldOrder(25)] public uint AntiAliasLevelInfo { get; set; }
- [FieldOrder(26)] public uint EncryptionMode { get; set; } = 0x4c;
-
- /// <summary>
- /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model.
- /// </summary>
- [FieldOrder(27)] public float VolumeMl { get; set; }
-
- /// <summary>
- /// Gets the estimated grams, derived from volume using configured factors for density.
- /// </summary>
- [FieldOrder(28)] public float WeightG { get; set; }
-
- /// <summary>
- /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost.
- /// </summary>
- [FieldOrder(29)] public float CostDollars { get; set; }
-
- /// <summary>
- /// Gets the machine name offset to a string naming the machine type, and its length in bytes.
- /// </summary>
- [FieldOrder(30)] public uint MachineNameAddress { get; set; }
-
- /// <summary>
- /// Gets the machine size in bytes
- /// </summary>
- [FieldOrder(31)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
-
- /// <summary>
- /// Gets the machine name. string is not nul-terminated.
- /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only.
- /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine.
- /// </summary>
- [Ignore]
- public string MachineName
+ get => _machineName;
+ set
{
- get => _machineName;
- set
- {
- if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
- _machineName = value;
- MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
- }
+ if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
+ _machineName = value;
+ MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
}
+ }
- /// <summary>
- /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(32)] public float BottomLightOffDelay { get; set; } = 1;
-
- /// <summary>
- /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(33)] public float LightOffDelay { get; set; } = 1;
-
- [FieldOrder(34)] public uint Padding4 { get; set; }
-
- /// <summary>
- /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters.
- /// </summary>
- [FieldOrder(35)] public float BottomLiftHeight { get; set; } = 5;
-
- /// <summary>
- /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute.
- /// </summary>
- [FieldOrder(36)] public float BottomLiftSpeed { get; set; } = 300;
-
- /// <summary>
- /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters.
- /// </summary>
- [FieldOrder(37)] public float LiftHeight { get; set; } = 5;
-
- /// <summary>
- /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute.
- /// </summary>
- [FieldOrder(38)] public float LiftSpeed { get; set; } = 300;
-
- /// <summary>
- /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute.
- /// </summary>
- [FieldOrder(39)] public float RetractSpeed { get; set; } = 300;
-
- [FieldOrder(40)] public uint Padding5 { get; set; }
- [FieldOrder(41)] public uint Padding6 { get; set; }
- [FieldOrder(42)] public uint Padding7 { get; set; }
- [FieldOrder(43)] public uint Padding8 { get; set; }
- [FieldOrder(44)] public uint Padding9 { get; set; }
- [FieldOrder(45)] public uint Padding10 { get; set; }
- [FieldOrder(46)] public uint Padding11 { get; set; }
-
- /// <summary>
- /// Gets the minutes since Jan 1, 1970 UTC
- /// </summary>
- [FieldOrder(47)] public uint ModifiedTimestampMinutes { get; set; } = (uint) DateTimeExtensions.Timestamp.TotalMinutes;
-
- [Ignore] public string ModifiedDate => DateTimeExtensions.GetDateTimeFromTimestampMinutes(ModifiedTimestampMinutes).ToString("dd/MM/yyyy HH:mm");
-
- [FieldOrder(48)] public uint SoftwareVersion { get; set; } = 0x01060300;
-
- [FieldOrder(49)] public uint Padding12 { get; set; }
- [FieldOrder(50)] public uint Padding13 { get; set; }
- [FieldOrder(51)] public uint Padding14 { get; set; }
- [FieldOrder(52)] public uint Padding15 { get; set; }
- [FieldOrder(53)] public uint Padding16 { get; set; }
- [FieldOrder(54)] public uint Padding17 { get; set; }
-
- public override string ToString()
- {
- return $"{nameof(_machineName)}: {_machineName}, {nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(LayerCount)}: {LayerCount}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(BottomLayersCount2)}: {BottomLayersCount2}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(OverallHeightMilimeter)}: {OverallHeightMilimeter}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(AntiAliasLevelInfo)}: {AntiAliasLevelInfo}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(MachineName)}: {MachineName}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(Padding4)}: {Padding4}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(Padding11)}: {Padding11}, {nameof(ModifiedTimestampMinutes)}: {ModifiedTimestampMinutes}, {nameof(ModifiedDate)}: {ModifiedDate}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(Padding12)}: {Padding12}, {nameof(Padding13)}: {Padding13}, {nameof(Padding14)}: {Padding14}, {nameof(Padding15)}: {Padding15}, {nameof(Padding16)}: {Padding16}, {nameof(Padding17)}: {Padding17}";
- }
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(32)] public float BottomLightOffDelay { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(33)] public float LightOffDelay { get; set; } = 1;
+
+ [FieldOrder(34)] public uint Padding4 { get; set; }
+
+ /// <summary>
+ /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters.
+ /// </summary>
+ [FieldOrder(35)] public float BottomLiftHeight { get; set; } = 5;
+
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(36)] public float BottomLiftSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters.
+ /// </summary>
+ [FieldOrder(37)] public float LiftHeight { get; set; } = 5;
+
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(38)] public float LiftSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(39)] public float RetractSpeed { get; set; } = 300;
+
+ [FieldOrder(40)] public uint Padding5 { get; set; }
+ [FieldOrder(41)] public uint Padding6 { get; set; }
+ [FieldOrder(42)] public uint Padding7 { get; set; }
+ [FieldOrder(43)] public uint Padding8 { get; set; }
+ [FieldOrder(44)] public uint Padding9 { get; set; }
+ [FieldOrder(45)] public uint Padding10 { get; set; }
+ [FieldOrder(46)] public uint Padding11 { get; set; }
+
+ /// <summary>
+ /// Gets the minutes since Jan 1, 1970 UTC
+ /// </summary>
+ [FieldOrder(47)] public uint ModifiedTimestampMinutes { get; set; } = (uint) DateTimeExtensions.Timestamp.TotalMinutes;
+
+ [Ignore] public string ModifiedDate => DateTimeExtensions.GetDateTimeFromTimestampMinutes(ModifiedTimestampMinutes).ToString("dd/MM/yyyy HH:mm");
+
+ [FieldOrder(48)] public uint SoftwareVersion { get; set; } = 0x01060300;
+
+ [FieldOrder(49)] public uint Padding12 { get; set; }
+ [FieldOrder(50)] public uint Padding13 { get; set; }
+ [FieldOrder(51)] public uint Padding14 { get; set; }
+ [FieldOrder(52)] public uint Padding15 { get; set; }
+ [FieldOrder(53)] public uint Padding16 { get; set; }
+ [FieldOrder(54)] public uint Padding17 { get; set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(_machineName)}: {_machineName}, {nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(LayerCount)}: {LayerCount}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(BottomLayersCount2)}: {BottomLayersCount2}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(OverallHeightMilimeter)}: {OverallHeightMilimeter}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(AntiAliasLevelInfo)}: {AntiAliasLevelInfo}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(MachineName)}: {MachineName}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(Padding4)}: {Padding4}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(Padding11)}: {Padding11}, {nameof(ModifiedTimestampMinutes)}: {ModifiedTimestampMinutes}, {nameof(ModifiedDate)}: {ModifiedDate}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(Padding12)}: {Padding12}, {nameof(Padding13)}: {Padding13}, {nameof(Padding14)}: {Padding14}, {nameof(Padding15)}: {Padding15}, {nameof(Padding16)}: {Padding16}, {nameof(Padding17)}: {Padding17}";
}
- #endregion
+ }
+ #endregion
+
+ #region Preview
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class Preview
+ {
+ /// <summary>
+ /// Gets the X dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(0)] public uint ResolutionX { get; set; }
- #region Preview
/// <summary>
- /// 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.
+ /// Gets the Y dimension of the preview image, in pixels.
/// </summary>
- public class Preview
+ [FieldOrder(1)] public uint ResolutionY { get; set; }
+
+ /// <summary>
+ /// Gets the image offset of the encoded data blob.
+ /// </summary>
+ [FieldOrder(2)] public uint ImageOffset { get; set; }
+
+ /// <summary>
+ /// Gets the image length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint ImageLength { 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; }
+
+ public unsafe Mat Decode(byte[] rawImageData)
{
- /// <summary>
- /// Gets the X dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(0)] public uint ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the Y dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(1)] public uint ResolutionY { get; set; }
-
- /// <summary>
- /// Gets the image offset of the encoded data blob.
- /// </summary>
- [FieldOrder(2)] public uint ImageOffset { get; set; }
-
- /// <summary>
- /// Gets the image length in bytes.
- /// </summary>
- [FieldOrder(3)] public uint ImageLength { 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; }
-
- public unsafe Mat Decode(byte[] rawImageData)
- {
- var image = new Mat(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
- var span = image.GetBytePointer();
+ var image = new Mat(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
+ var span = image.GetBytePointer();
- int pixel = 0;
- for (uint n = 0; n < ImageLength; n++)
+ int pixel = 0;
+ for (uint n = 0; n < ImageLength; n++)
+ {
+ uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
+ //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3);
+ byte red = (byte)(((dot >> 11) & 0x1F) << 3);
+ byte green = (byte)(((dot >> 6) & 0x1F) << 3);
+ byte blue = (byte)((dot & 0x1F) << 3);
+ int repeat = 1;
+ if ((dot & 0x0020) == 0x0020)
{
- uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
- //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3);
- byte red = (byte)(((dot >> 11) & 0x1F) << 3);
- byte green = (byte)(((dot >> 6) & 0x1F) << 3);
- byte blue = (byte)((dot & 0x1F) << 3);
- int repeat = 1;
- if ((dot & 0x0020) == 0x0020)
- {
- repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
- }
+ repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
+ }
- for (int j = 0; j < repeat; j++)
- {
- span[pixel++] = blue;
- span[pixel++] = green;
- span[pixel++] = red;
- //span[pixel] = new Rgba32(red, green, blue, byte.MaxValue);
- }
+ for (int j = 0; j < repeat; j++)
+ {
+ span[pixel++] = blue;
+ span[pixel++] = green;
+ span[pixel++] = red;
+ //span[pixel] = new Rgba32(red, green, blue, byte.MaxValue);
}
-
- return image;
}
- public static unsafe byte[] Encode(Mat image)
- {
- List<byte> rawData = new();
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ return image;
+ }
+
+ public static unsafe byte[] Encode(Mat image)
+ {
+ List<byte> rawData = new();
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
- ushort color15 = 0;
- uint rep = 0;
+ ushort color15 = 0;
+ uint rep = 0;
- void RleRGB15()
+ void RleRGB15()
+ {
+ switch (rep)
{
- switch (rep)
- {
- case 0:
- return;
- case 1:
+ case 0:
+ return;
+ case 1:
+ rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
+ rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
+ break;
+ case 2:
+ for (int i = 0; i < 2; i++)
+ {
rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- break;
- case 2:
- for (int i = 0; i < 2; i++)
- {
- rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
- rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- }
-
- break;
- default:
- rawData.Add((byte)(color15 | REPEATRGB15MASK));
- rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
- rawData.Add((byte)((rep - 1) | 0x3000));
- rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
- break;
- }
+ }
+
+ break;
+ default:
+ rawData.Add((byte)(color15 | REPEATRGB15MASK));
+ rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
+ rawData.Add((byte)((rep - 1) | 0x3000));
+ rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
+ break;
}
+ }
- for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels)
- {
- var ncolor15 =
- (span[pixel] >> 3)
- | ((span[pixel+1] >> 2) << 5)
- | ((span[pixel+2] >> 3) << 11);
+ for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels)
+ {
+ var ncolor15 =
+ (span[pixel] >> 3)
+ | ((span[pixel+1] >> 2) << 5)
+ | ((span[pixel+2] >> 3) << 11);
- if (ncolor15 == color15)
- {
- rep++;
- if (rep == RLE16EncodingLimit)
- {
- RleRGB15();
- rep = 0;
- }
- }
- else
+ if (ncolor15 == color15)
+ {
+ rep++;
+ if (rep == RLE16EncodingLimit)
{
RleRGB15();
- color15 = (ushort) ncolor15;
- rep = 1;
+ rep = 0;
}
}
+ else
+ {
+ RleRGB15();
+ color15 = (ushort) ncolor15;
+ rep = 1;
+ }
+ }
- RleRGB15();
+ RleRGB15();
- return rawData.ToArray();
- }
+ return rawData.ToArray();
+ }
- public override string ToString()
- {
- return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
- }
+ public override string ToString()
+ {
+ return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
}
+ }
- #endregion
+ #endregion
+
+ #region Layer
+ public class LayerDef
+ {
+ /// <summary>
+ /// Gets the build platform Z position for this layer, measured in millimeters.
+ /// </summary>
+ [FieldOrder(0)] public float LayerPositionZ { get; set; }
- #region Layer
- public class LayerDef
+ /// <summary>
+ /// Gets the exposure time for this layer, in seconds.
+ /// </summary>
+ [FieldOrder(1)] public float LayerExposure { get; set; }
+
+ /// <summary>
+ /// Gets how long to keep the light off after exposing this layer, in seconds.
+ /// </summary>
+ [FieldOrder(2)] public float LightOffDelay { get; set; }
+
+ /// <summary>
+ /// Gets the layer image offset to encoded layer data, and its length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint DataAddress { get; set; }
+
+ /// <summary>
+ /// Gets the layer image length in bytes.
+ /// </summary>
+ [FieldOrder(4)] public uint DataSize { get; set; }
+ [FieldOrder(5)] public uint Unknown1 { get; set; }
+ [FieldOrder(6)] public uint Unknown2 { get; set; } = 84;
+ [FieldOrder(7)] public uint Unknown3 { get; set; }
+ [FieldOrder(8)] public uint Unknown4 { get; set; }
+
+ [Ignore] public byte[]? EncodedRle { get; set; }
+
+ [Ignore] public FDGFile? Parent { get; set; }
+
+ public LayerDef()
{
- /// <summary>
- /// Gets the build platform Z position for this layer, measured in millimeters.
- /// </summary>
- [FieldOrder(0)] public float LayerPositionZ { get; set; }
-
- /// <summary>
- /// Gets the exposure time for this layer, in seconds.
- /// </summary>
- [FieldOrder(1)] public float LayerExposure { get; set; }
-
- /// <summary>
- /// Gets how long to keep the light off after exposing this layer, in seconds.
- /// </summary>
- [FieldOrder(2)] public float LightOffDelay { get; set; }
-
- /// <summary>
- /// Gets the layer image offset to encoded layer data, and its length in bytes.
- /// </summary>
- [FieldOrder(3)] public uint DataAddress { get; set; }
-
- /// <summary>
- /// Gets the layer image length in bytes.
- /// </summary>
- [FieldOrder(4)] public uint DataSize { get; set; }
- [FieldOrder(5)] public uint Unknown1 { get; set; }
- [FieldOrder(6)] public uint Unknown2 { get; set; } = 84;
- [FieldOrder(7)] public uint Unknown3 { get; set; }
- [FieldOrder(8)] public uint Unknown4 { get; set; }
-
- [Ignore] public byte[] EncodedRle { get; set; }
-
- [Ignore] public FDGFile Parent { get; set; }
-
- public LayerDef()
- {
- }
+ }
- public LayerDef(FDGFile parent, Layer layer)
- {
- Parent = parent;
- SetFrom(layer);
- }
+ public LayerDef(FDGFile parent, Layer layer)
+ {
+ Parent = parent;
+ SetFrom(layer);
+ }
- public void SetFrom(Layer layer)
- {
- LayerPositionZ = layer.PositionZ;
- LayerExposure = layer.ExposureTime;
- LightOffDelay = layer.LightOffDelay;
- }
+ public void SetFrom(Layer layer)
+ {
+ LayerPositionZ = layer.PositionZ;
+ LayerExposure = layer.ExposureTime;
+ LightOffDelay = layer.LightOffDelay;
+ }
- public void CopyTo(Layer layer)
+ public void CopyTo(Layer layer)
+ {
+ layer.PositionZ = LayerPositionZ;
+ layer.ExposureTime = LayerExposure;
+ layer.LightOffDelay = LightOffDelay;
+ }
+
+ public unsafe Mat Decode(uint layerIndex, bool consumeData = true)
+ {
+ var image = EmguExtensions.InitMat(Parent!.Resolution);
+ var span = image.GetBytePointer();
+
+ if (Parent.HeaderSettings.EncryptionKey > 0)
{
- layer.PositionZ = LayerPositionZ;
- layer.ExposureTime = LayerExposure;
- layer.LightOffDelay = LightOffDelay;
+ LayerRleCryptBuffer(Parent.HeaderSettings.EncryptionKey, layerIndex, EncodedRle!);
}
- public unsafe Mat Decode(uint layerIndex, bool consumeData = true)
- {
- var image = EmguExtensions.InitMat(Parent.Resolution);
- var span = image.GetBytePointer();
+ int limit = image.Width * image.Height;
+ int index = 0;
+ byte lastColor = 0;
- if (Parent.HeaderSettings.EncryptionKey > 0)
+ foreach (var code in EncodedRle!)
+ {
+ if ((code & 0x80) == 0x80)
{
- LayerRleCryptBuffer(Parent.HeaderSettings.EncryptionKey, layerIndex, EncodedRle);
- }
+ //lastColor = (byte) (code << 1);
+ // // Convert from 7bpp to 8bpp (extending the last bit)
+ lastColor = (byte)(((code & 0x7f) << 1) | (code & 1));
+ if (lastColor >= 0xfc)
+ {
+ // Make 'white' actually white
+ lastColor = 0xff;
- int limit = image.Width * image.Height;
- int index = 0;
- byte lastColor = 0;
+ }
- foreach (var code in EncodedRle)
- {
- if ((code & 0x80) == 0x80)
+ if (index < limit)
{
- //lastColor = (byte) (code << 1);
- // // Convert from 7bpp to 8bpp (extending the last bit)
- lastColor = (byte)(((code & 0x7f) << 1) | (code & 1));
- if (lastColor >= 0xfc)
- {
- // Make 'white' actually white
- lastColor = 0xff;
-
- }
+ span[index] = lastColor;
+ }
+ else
+ {
+ image.Dispose();
+ throw new FileLoadException("Corrupted RLE data.");
+ }
+ index++;
+ }
+ else
+ {
+ for (uint i = 0; i < code; i++)
+ {
if (index < limit)
{
span[index] = lastColor;
@@ -505,684 +521,668 @@ namespace UVtools.Core.FileFormats
image.Dispose();
throw new FileLoadException("Corrupted RLE data.");
}
-
index++;
}
- else
- {
- for (uint i = 0; i < code; i++)
- {
- if (index < limit)
- {
- span[index] = lastColor;
- }
- else
- {
- image.Dispose();
- throw new FileLoadException("Corrupted RLE data.");
- }
- index++;
- }
- }
}
+ }
- if (consumeData)
- EncodedRle = null;
+ if (consumeData)
+ EncodedRle = null;
- return image;
- }
+ return image;
+ }
- public void Encode(Mat mat, uint layerIndex)
- {
- List<byte> rawData = new();
+ public void Encode(Mat mat, uint layerIndex)
+ {
+ List<byte> rawData = new();
- //byte color = byte.MaxValue >> 1;
- byte color = byte.MaxValue;
- uint stride = 0;
+ //byte color = byte.MaxValue >> 1;
+ byte color = byte.MaxValue;
+ uint stride = 0;
- void AddRep()
+ void AddRep()
+ {
+ rawData.Add((byte)(color | 0x80));
+ stride--;
+ int done = 0;
+ while (done < stride)
{
- rawData.Add((byte)(color | 0x80));
- stride--;
- int done = 0;
- while (done < stride)
- {
- int todo = 0x7d;
+ int todo = 0x7d;
- if (stride - done < todo)
- {
- todo = (int)(stride - done);
- }
+ if (stride - done < todo)
+ {
+ todo = (int)(stride - done);
+ }
- rawData.Add((byte)(todo));
+ rawData.Add((byte)(todo));
- done += todo;
- }
+ done += todo;
}
+ }
- int halfWidth = mat.Width / 2;
+ int halfWidth = mat.Width / 2;
- //int pixel = 0;
- for (int y = 0; y < mat.Height; y++)
+ //int pixel = 0;
+ for (int y = 0; y < mat.Height; y++)
+ {
+ var span = mat.GetRowSpan<byte>(y);
+ for (int x = 0; x < span.Length; x++)
{
- var span = mat.GetRowSpan<byte>(y);
- for (int x = 0; x < span.Length; x++)
- {
- var grey7 = (byte)((span[x] >> 1) & 0x7f);
- if (grey7 > 0x7c)
- {
- grey7 = 0x7c;
- }
-
- if (color == byte.MaxValue)
- {
- color = grey7;
- stride = 1;
- }
- else if (grey7 != color || x == halfWidth)
- {
- AddRep();
- color = grey7;
- stride = 1;
- }
- else
- {
- stride++;
- }
+ var grey7 = (byte)((span[x] >> 1) & 0x7f);
+ if (grey7 > 0x7c)
+ {
+ grey7 = 0x7c;
}
- AddRep();
- color = byte.MaxValue;
+ if (color == byte.MaxValue)
+ {
+ color = grey7;
+ stride = 1;
+ }
+ else if (grey7 != color || x == halfWidth)
+ {
+ AddRep();
+ color = grey7;
+ stride = 1;
+ }
+ else
+ {
+ stride++;
+ }
}
+ AddRep();
+ color = byte.MaxValue;
+ }
- if (Parent.HeaderSettings.EncryptionKey > 0)
- {
- EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData);
- }
- else
- {
- EncodedRle = rawData.ToArray();
- }
- DataSize = (uint) EncodedRle.Length;
+ if (Parent!.HeaderSettings.EncryptionKey > 0)
+ {
+ EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData);
}
-
- public override string ToString()
+ else
{
- return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
+ EncodedRle = rawData.ToArray();
}
+
+ DataSize = (uint) EncodedRle.Length;
+ }
+
+ public override string ToString()
+ {
+ return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
}
- #endregion
+ }
+ #endregion
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public Header HeaderSettings { get; protected internal set; } = new Header();
+ public Header HeaderSettings { get; protected internal set; } = new Header();
- public Preview[] Previews { get; protected internal set; }
+ public Preview[] Previews { get; protected internal set; }
- public LayerDef[] LayersDefinitions { get; private set; }
+ public LayerDef[] LayersDefinitions { get; private set; } = null!;
- public override FileFormatType FileType => FileFormatType.Binary;
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(FDGFile), "fdg", "Voxelab FDG"),
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(FDGFile), "fdg", "Voxelab FDG"),
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.ExposureTime,
- };
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.ExposureTime,
+ };
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(400, 300),
- new(200, 125)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(400, 300),
+ new(200, 125)
+ };
- public override uint[] AvailableVersions { get; } = { 2 };
+ public override uint[] AvailableVersions { get; } = { 2 };
- public override uint DefaultVersion => 2;
+ public override uint DefaultVersion => 2;
- public override uint Version
+ public override uint Version
+ {
+ get => HeaderSettings.Version;
+ set
{
- get => HeaderSettings.Version;
- set
- {
- base.Version = value;
- HeaderSettings.Version = (ushort)base.Version;
- }
+ base.Version = value;
+ HeaderSettings.Version = (ushort)base.Version;
}
+ }
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => HeaderSettings.BedSizeX;
+ set
{
- get => HeaderSettings.BedSizeX;
- set
- {
- HeaderSettings.BedSizeX = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ HeaderSettings.BedSizeX = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => HeaderSettings.BedSizeY;
+ set
{
- get => HeaderSettings.BedSizeY;
- set
- {
- HeaderSettings.BedSizeY = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ HeaderSettings.BedSizeY = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
- {
- get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ;
- set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2);
- }
+ public override float MachineZ
+ {
+ get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ;
+ set => base.MachineZ = HeaderSettings.BedSizeZ = (float)Math.Round(value, 2);
+ }
- public override Enumerations.FlipDirection DisplayMirror
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
+ set
{
- get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
- set
- {
- HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
- RaisePropertyChanged();
- }
+ HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
+ RaisePropertyChanged();
}
+ }
- public override byte AntiAliasing
- {
- get => (byte) HeaderSettings.AntiAliasLevelInfo;
- set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16));
- }
+ public override byte AntiAliasing
+ {
+ get => (byte) HeaderSettings.AntiAliasLevelInfo;
+ set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16));
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => HeaderSettings.LayerHeightMilimeter;
+ set
{
- get => HeaderSettings.LayerHeightMilimeter;
- set
- {
- HeaderSettings.LayerHeightMilimeter = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ HeaderSettings.LayerHeightMilimeter = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override float PrintHeight
- {
- get => HeaderSettings.OverallHeightMilimeter;
- set => base.PrintHeight = HeaderSettings.OverallHeightMilimeter = base.PrintHeight;
- }
+ public override float PrintHeight
+ {
+ get => HeaderSettings.OverallHeightMilimeter;
+ set => base.PrintHeight = HeaderSettings.OverallHeightMilimeter = base.PrintHeight;
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
+ }
- public override ushort BottomLayerCount
- {
- get => (ushort) HeaderSettings.BottomLayersCount;
- set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount2 = HeaderSettings.BottomLayersCount = value);
- }
+ public override ushort BottomLayerCount
+ {
+ get => (ushort) HeaderSettings.BottomLayersCount;
+ set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount2 = HeaderSettings.BottomLayersCount = value);
+ }
- public override float BottomLightOffDelay
- {
- get => HeaderSettings.BottomLightOffDelay;
- set => base.BottomLightOffDelay = HeaderSettings.BottomLightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float BottomLightOffDelay
+ {
+ get => HeaderSettings.BottomLightOffDelay;
+ set => base.BottomLightOffDelay = HeaderSettings.BottomLightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float LightOffDelay
- {
- get => HeaderSettings.LightOffDelay;
- set => base.LightOffDelay = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float LightOffDelay
+ {
+ get => HeaderSettings.LightOffDelay;
+ set => base.LightOffDelay = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
{
- get => base.BottomWaitTimeBeforeCure;
- set
- {
- SetBottomLightOffDelay(value);
- base.BottomWaitTimeBeforeCure = value;
- }
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
{
- get => base.WaitTimeBeforeCure;
- set
- {
- SetNormalLightOffDelay(value);
- base.WaitTimeBeforeCure = value;
- }
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
}
+ }
- public override float BottomExposureTime
- {
- get => HeaderSettings.BottomExposureSeconds;
- set => base.BottomExposureTime = HeaderSettings.BottomExposureSeconds = value;
- }
+ public override float BottomExposureTime
+ {
+ get => HeaderSettings.BottomExposureSeconds;
+ set => base.BottomExposureTime = HeaderSettings.BottomExposureSeconds = value;
+ }
- public override float ExposureTime
- {
- get => HeaderSettings.LayerExposureSeconds;
- set => base.ExposureTime = HeaderSettings.LayerExposureSeconds = (float)Math.Round(value, 2);
- }
+ public override float ExposureTime
+ {
+ get => HeaderSettings.LayerExposureSeconds;
+ set => base.ExposureTime = HeaderSettings.LayerExposureSeconds = (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 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 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 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 LiftSpeed
+ {
+ get => HeaderSettings.LiftSpeed;
+ set => base.LiftSpeed = HeaderSettings.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ public override float BottomRetractSpeed => RetractSpeed;
- public override float RetractSpeed
- {
- get => HeaderSettings.RetractSpeed;
- set => base.RetractSpeed = HeaderSettings.RetractSpeed = (float)Math.Round(value, 2);
- }
+ 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 BottomLightPWM
+ {
+ get => (byte) HeaderSettings.BottomLightPWM;
+ set => base.BottomLightPWM = (byte) (HeaderSettings.BottomLightPWM = value);
+ }
- public override byte LightPWM
- {
- get => (byte) HeaderSettings.BottomLightPWM;
- set => base.LightPWM = (byte) (HeaderSettings.BottomLightPWM = value);
- }
+ public override byte LightPWM
+ {
+ get => (byte) HeaderSettings.BottomLightPWM;
+ set => base.LightPWM = (byte) (HeaderSettings.BottomLightPWM = value);
+ }
- public override float PrintTime
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- HeaderSettings.PrintTime = (uint) base.PrintTime;
- }
+ base.PrintTime = value;
+ HeaderSettings.PrintTime = (uint) base.PrintTime;
}
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- HeaderSettings.VolumeMl = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ HeaderSettings.VolumeMl = base.MaterialMilliliters;
}
+ }
- public override float MaterialGrams
- {
- get => (float)Math.Round(HeaderSettings.WeightG, 3);
- set => base.MaterialGrams = HeaderSettings.WeightG = (float)Math.Round(value, 3);
- }
+ public override float MaterialGrams
+ {
+ get => (float)Math.Round(HeaderSettings.WeightG, 3);
+ set => base.MaterialGrams = HeaderSettings.WeightG = (float)Math.Round(value, 3);
+ }
- public override float MaterialCost
- {
- get => (float) Math.Round(HeaderSettings.CostDollars, 3);
- set => base.MaterialCost = HeaderSettings.CostDollars = (float)Math.Round(value, 3);
- }
+ public override float MaterialCost
+ {
+ get => (float) Math.Round(HeaderSettings.CostDollars, 3);
+ set => base.MaterialCost = HeaderSettings.CostDollars = (float)Math.Round(value, 3);
+ }
- public override string MachineName
- {
- get => HeaderSettings.MachineName;
- set => base.MachineName = HeaderSettings.MachineName = value;
- }
+ public override string MachineName
+ {
+ get => HeaderSettings.MachineName;
+ set => base.MachineName = HeaderSettings.MachineName = value;
+ }
- public override object[] Configs => new object[] { HeaderSettings };
+ public override object[] Configs => new object[] { HeaderSettings };
- #endregion
+ #endregion
- #region Constructors
- public FDGFile()
+ #region Constructors
+ public FDGFile()
+ {
+ Previews = new Preview[ThumbnailsCount];
+ }
+ #endregion
+
+ #region Methods
+ public override void Clear()
+ {
+ base.Clear();
+
+ for (byte i = 0; i < ThumbnailsCount; i++)
{
- Previews = new Preview[ThumbnailsCount];
+ Previews[i] = new Preview();
}
- #endregion
- #region Methods
- public override void Clear()
- {
- base.Clear();
+ LayersDefinitions = null!;
+ }
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- Previews[i] = new Preview();
- }
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ /*if (HeaderSettings.EncryptionKey == 0)
+ {
+ Random rnd = new Random();
+ HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue);
+ }*/
- LayersDefinitions = null;
- }
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
- protected override void EncodeInternally(OperationProgress progress)
+ for (byte i = 0; i < ThumbnailsCount; i++)
{
- /*if (HeaderSettings.EncryptionKey == 0)
- {
- Random rnd = new Random();
- HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue);
- }*/
+ var image = Thumbnails[i];
+ if(image is null) continue;
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
+ var bytes = Preview.Encode(image);
- for (byte i = 0; i < ThumbnailsCount; i++)
+ if (bytes.Length == 0) continue;
+
+ if (i == (byte) FileThumbnailSize.Small)
+ {
+ HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position;
+ }
+ else
{
- var image = Thumbnails[i];
+ HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position;
+ }
+
+ Preview preview = new()
+ {
+ ResolutionX = (uint) image.Width,
+ ResolutionY = (uint) image.Height,
+ ImageLength = (uint)bytes.Length,
+ };
- var bytes = Preview.Encode(image);
+ preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
- if (bytes.Length == 0) continue;
+ Helpers.SerializeWriteFileStream(outputFile, preview);
- if (i == (byte) FileThumbnailSize.Small)
- {
- HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position;
- }
- else
- {
- HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position;
- }
-
- Preview preview = new()
- {
- ResolutionX = (uint) image.Width,
- ResolutionY = (uint) image.Height,
- ImageLength = (uint)bytes.Length,
- };
+ outputFile.WriteBytes(bytes);
+ }
- preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
+ if (HeaderSettings.MachineNameSize > 0)
+ {
+ HeaderSettings.MachineNameAddress = (uint)outputFile.Position;
+ var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName);
+ outputFile.Write(machineBytes, 0, machineBytes.Length);
+ }
- Helpers.SerializeWriteFileStream(outputFile, preview);
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var layersHash = new Dictionary<string, LayerDef>();
+ LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
+ HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position;
+ uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + (uint)Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount;
- outputFile.WriteBytes(bytes);
- }
+ foreach (var batch in BatchLayersIndexes())
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = this[layerIndex].LayerMat)
+ {
+ LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]);
+ LayersDefinitions[layerIndex].Encode(mat, (uint)layerIndex);
+ }
+ progress.LockAndIncrement();
+ });
- if (HeaderSettings.MachineNameSize > 0)
+ foreach (var layerIndex in batch)
{
- HeaderSettings.MachineNameAddress = (uint)outputFile.Position;
- var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName);
- outputFile.Write(machineBytes, 0, machineBytes.Length);
- }
+ progress.Token.ThrowIfCancellationRequested();
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- var layersHash = new Dictionary<string, LayerDef>();
- LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
- HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position;
- uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + (uint)Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount;
+ var layerDef = LayersDefinitions[layerIndex];
+ LayerDef? layerDefHash = null;
- foreach (var batch in BatchLayersIndexes())
- {
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ if (HeaderSettings.EncryptionKey == 0)
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = this[layerIndex].LayerMat)
+ string hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle!);
+ if (layersHash.TryGetValue(hash, out layerDefHash))
{
- LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]);
- LayersDefinitions[layerIndex].Encode(mat, (uint)layerIndex);
+ layerDef.DataAddress = layerDefHash.DataAddress;
+ layerDef.DataSize = layerDefHash.DataSize;
}
- progress.LockAndIncrement();
- });
-
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
-
- var layerDef = LayersDefinitions[layerIndex];
- LayerDef layerDefHash = null;
-
- if (HeaderSettings.EncryptionKey == 0)
+ else
{
- string hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle);
- if (layersHash.TryGetValue(hash, out layerDefHash))
- {
- layerDef.DataAddress = layerDefHash.DataAddress;
- layerDef.DataSize = layerDefHash.DataSize;
- }
- else
- {
- layersHash.Add(hash, layerDef);
- }
+ layersHash.Add(hash, layerDef);
}
+ }
- if (layerDefHash is null)
- {
- layerDef.DataAddress = layerDataCurrentOffset;
+ if (layerDefHash is null)
+ {
+ layerDef.DataAddress = layerDataCurrentOffset;
- outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
- layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle);
- }
+ outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
+ layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle!);
+ }
- outputFile.Seek(layerDefCurrentOffset, SeekOrigin.Begin);
- layerDefCurrentOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef);
+ outputFile.Seek(layerDefCurrentOffset, SeekOrigin.Begin);
+ layerDefCurrentOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef);
- layerDef.EncodedRle = null; // Free
- }
+ layerDef.EncodedRle = null; // Free
}
+ }
- HeaderSettings.ModifiedTimestampMinutes = (uint)DateTimeExtensions.TimestampMinutes;
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ HeaderSettings.ModifiedTimestampMinutes = (uint)DateTimeExtensions.TimestampMinutes;
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine(Previews[0]);
- Debug.WriteLine(Previews[1]);
- Debug.WriteLine("-End-");
- }
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(Previews[0]);
+ Debug.WriteLine(Previews[1]);
+ Debug.WriteLine("-End-");
+ }
- protected override void DecodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
+ //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.Magic != MAGIC)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
- //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (HeaderSettings.Magic != MAGIC)
- {
- throw new FileLoadException("Not a valid FDG file!", FileFullPath);
- }
+ throw new FileLoadException("Not a valid FDG file!", FileFullPath);
+ }
- HeaderSettings.AntiAliasLevel = 1;
+ HeaderSettings.AntiAliasLevel = 1;
- progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
- Debug.Write("Header -> ");
- Debug.WriteLine(HeaderSettings);
+ progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
+ Debug.Write("Header -> ");
+ Debug.WriteLine(HeaderSettings);
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- uint offsetAddress = i == 0
- ? HeaderSettings.PreviewSmallOffsetAddress
- : HeaderSettings.PreviewLargeOffsetAddress;
- if (offsetAddress == 0) continue;
+ for (byte i = 0; i < ThumbnailsCount; i++)
+ {
+ uint offsetAddress = i == 0
+ ? HeaderSettings.PreviewSmallOffsetAddress
+ : HeaderSettings.PreviewLargeOffsetAddress;
+ if (offsetAddress == 0) continue;
- inputFile.Seek(offsetAddress, SeekOrigin.Begin);
- Previews[i] = Helpers.Deserialize<Preview>(inputFile);
+ inputFile.Seek(offsetAddress, SeekOrigin.Begin);
+ Previews[i] = Helpers.Deserialize<Preview>(inputFile);
- Debug.Write($"Preview {i} -> ");
- Debug.WriteLine(Previews[i]);
+ Debug.Write($"Preview {i} -> ");
+ Debug.WriteLine(Previews[i]);
- inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
- byte[] rawImageData = new byte[Previews[i].ImageLength];
- inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength);
+ inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
+ byte[] rawImageData = new byte[Previews[i].ImageLength];
+ inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength);
- Thumbnails[i] = Previews[i].Decode(rawImageData);
- progress++;
- }
+ Thumbnails[i] = Previews[i].Decode(rawImageData);
+ progress++;
+ }
- if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
- {
- inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
- var buffer = new byte[HeaderSettings.MachineNameSize];
- inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize);
- HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer);
- }
+ if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
+ {
+ inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
+ var buffer = new byte[HeaderSettings.MachineNameSize];
+ inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize);
+ HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer);
+ }
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
- progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount);
- foreach (var batch in BatchLayersIndexes())
+ progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount);
+ foreach (var batch in BatchLayersIndexes())
+ {
+ foreach (var layerIndex in batch)
{
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
- layerDef.Parent = this;
- LayersDefinitions[layerIndex] = layerDef;
+ var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
+ layerDef.Parent = this;
+ LayersDefinitions[layerIndex] = layerDef;
- Debug.Write($"LAYER {layerIndex} -> ");
- Debug.WriteLine(layerDef);
-
- if (DecodeType == FileDecodeType.Full)
- {
- inputFile.SeekDoWorkAndRewind(layerDef.DataAddress,
- () => { layerDef.EncodedRle = inputFile.ReadBytes(layerDef.DataSize); });
- }
- }
+ Debug.Write($"LAYER {layerIndex} -> ");
+ Debug.WriteLine(layerDef);
if (DecodeType == FileDecodeType.Full)
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- if (DecodeType == FileDecodeType.Full)
- {
- using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex);
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- }
-
- progress.LockAndIncrement();
- });
+ inputFile.SeekDoWorkAndRewind(layerDef.DataAddress,
+ () => { layerDef.EncodedRle = inputFile.ReadBytes(layerDef.DataSize); });
}
}
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ if (DecodeType == FileDecodeType.Full)
{
- LayersDefinitions[layerIndex].CopyTo(this[layerIndex]);
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ if (DecodeType == FileDecodeType.Full)
+ {
+ using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex);
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
+
+ progress.LockAndIncrement();
+ });
}
}
- protected override void PartialSaveInternally(OperationProgress progress)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- HeaderSettings.ModifiedTimestampMinutes = (uint)DateTimeExtensions.TimestampMinutes;
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ LayersDefinitions[layerIndex].CopyTo(this[layerIndex]);
+ }
+ }
- /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
- {
- outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
- byte[] buffer = new byte[HeaderSettings.MachineNameSize];
- outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize);
- }*/
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ HeaderSettings.ModifiedTimestampMinutes = (uint)DateTimeExtensions.TimestampMinutes;
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
{
- LayersDefinitions[layerIndex].SetFrom(this[layerIndex]);
- outputFile.Seek(layerOffset, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]);
- layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]);
- }
- }
-
- #endregion
+ outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
+ byte[] buffer = new byte[HeaderSettings.MachineNameSize];
+ outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize);
+ }*/
- #region Static Methods
- public static byte[] LayerRleCrypt(uint seed, uint layerIndex, IEnumerable<byte> input)
+ uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
{
- var result = input.ToArray();
- LayerRleCryptBuffer(seed, layerIndex, result);
- return result;
+ LayersDefinitions[layerIndex].SetFrom(this[layerIndex]);
+ outputFile.Seek(layerOffset, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]);
+ layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]);
}
+ }
- public static void LayerRleCryptBuffer(uint seed, uint layerIndex, byte[] input)
- {
- if (seed == 0) return;
+ #endregion
- var init = (seed - 0x1dcb76c3) ^ 0x257e2431;
- var key = init * 0x82391efd * (layerIndex ^ 0x110bdacd);
+ #region Static Methods
+ public static byte[] LayerRleCrypt(uint seed, uint layerIndex, IEnumerable<byte> input)
+ {
+ var result = input.ToArray();
+ LayerRleCryptBuffer(seed, layerIndex, result);
+ return result;
+ }
- int index = 0;
- for (int i = 0; i < input.Length; i++)
- {
- var k = (byte)(key >> 8 * index);
+ public static void LayerRleCryptBuffer(uint seed, uint layerIndex, byte[] input)
+ {
+ if (seed == 0) return;
- index++;
+ var init = (seed - 0x1dcb76c3) ^ 0x257e2431;
+ var key = init * 0x82391efd * (layerIndex ^ 0x110bdacd);
- if ((index & 3) == 0)
- {
- key += init;
- index = 0;
- }
+ int index = 0;
+ for (int i = 0; i < input.Length; i++)
+ {
+ var k = (byte)(key >> 8 * index);
+
+ index++;
- input[i] = (byte)(input[i] ^ k);
+ if ((index & 3) == 0)
+ {
+ key += init;
+ index = 0;
}
+
+ input[i] = (byte)(input[i] ^ k);
}
- #endregion
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/FileExtension.cs b/UVtools.Core/FileFormats/FileExtension.cs
index b83ad9f..173cc4c 100644
--- a/UVtools.Core/FileFormats/FileExtension.cs
+++ b/UVtools.Core/FileFormats/FileExtension.cs
@@ -9,128 +9,127 @@
using System;
using System.Collections.Generic;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+/// <summary>
+/// Represents a file extension for slicer file formats
+/// </summary>
+public sealed class FileExtension : IEquatable<FileExtension>, IEquatable<string>
{
+ #region Properties
/// <summary>
- /// Represents a file extension for slicer file formats
+ /// Stores a specific <see cref="FileFormat"/> type that should be used to create with this FileExtension instance
/// </summary>
- public sealed class FileExtension : IEquatable<FileExtension>, IEquatable<string>
- {
- #region Properties
- /// <summary>
- /// Stores a specific <see cref="FileFormat"/> type that should be used to create with this FileExtension instance
- /// </summary>
- public Type FileFormatType { get; }
-
- /// <summary>
- /// Gets the extension name without the dot (.)
- /// </summary>
- public string Extension { get; }
+ public Type FileFormatType { get; }
+
+ /// <summary>
+ /// Gets the extension name without the dot (.)
+ /// </summary>
+ public string Extension { get; }
- /// <summary>
- /// Gets the extension description
- /// </summary>
- public string Description { get; }
-
- /// <summary>
- /// Gets if the extension shows up on open file dialog filters
- /// </summary>
- public bool IsVisibleOnFileFilters { get; }
-
- /// <summary>
- /// Gets if the extension shows up on convert to menu
- /// </summary>
- public bool IsVisibleOnConvertMenu { get; }
-
- /// <summary>
- /// Gets a tag object
- /// </summary>
- public object Tag { get; }
-
- /// <summary>
- /// Gets the file filter for open and save dialogs
- /// </summary>
- public string Filter => $@"{Description} (*.{Extension})|*.{Extension}";
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="fileFormatType">The exact <see cref="FileFormat"/> type</param>
- /// <param name="extension">The extension name without the dot (.)</param>
- /// <param name="description">The extension description</param>
- /// <param name="isVisibleOnFileFilters">True if this extension is visible on open file dialog filters</param>
- /// <param name="isVisibleOnConvertMenu">True if this extension is visible on convert to menu</param>
- /// <param name="tag">Tag object</param>
- public FileExtension(Type fileFormatType, string extension, string description, bool isVisibleOnFileFilters = true, bool isVisibleOnConvertMenu = true, object tag = null)
- {
- FileFormatType = fileFormatType;
- Extension = extension;
- Description = description;
- IsVisibleOnFileFilters = isVisibleOnFileFilters;
- IsVisibleOnConvertMenu = isVisibleOnConvertMenu;
- Tag = tag;
- }
- #endregion
+ /// <summary>
+ /// Gets the extension description
+ /// </summary>
+ public string Description { get; }
- #region Overrides
+ /// <summary>
+ /// Gets if the extension shows up on open file dialog filters
+ /// </summary>
+ public bool IsVisibleOnFileFilters { get; }
- public override string ToString()
- {
- return $"{Description} ({Extension})";
- }
+ /// <summary>
+ /// Gets if the extension shows up on convert to menu
+ /// </summary>
+ public bool IsVisibleOnConvertMenu { get; }
- public bool Equals(FileExtension other)
- {
- return Extension.Equals(other.Extension, StringComparison.OrdinalIgnoreCase);
- }
+ /// <summary>
+ /// Gets a tag object
+ /// </summary>
+ public object? Tag { get; }
- public bool Equals(string other)
- {
- return Extension.Equals(other, StringComparison.OrdinalIgnoreCase);
- }
+ /// <summary>
+ /// Gets the file filter for open and save dialogs
+ /// </summary>
+ public string Filter => $@"{Description} (*.{Extension})|*.{Extension}";
+ #endregion
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is FileExtension other && Equals(other);
- }
+ #region Constructor
- public override int GetHashCode()
+ /// <summary>
+ /// Constructor
+ /// </summary>
+ /// <param name="fileFormatType">The exact <see cref="FileFormat"/> type</param>
+ /// <param name="extension">The extension name without the dot (.)</param>
+ /// <param name="description">The extension description</param>
+ /// <param name="isVisibleOnFileFilters">True if this extension is visible on open file dialog filters</param>
+ /// <param name="isVisibleOnConvertMenu">True if this extension is visible on convert to menu</param>
+ /// <param name="tag">Tag object</param>
+ public FileExtension(Type fileFormatType, string extension, string description, bool isVisibleOnFileFilters = true, bool isVisibleOnConvertMenu = true, object? tag = null)
+ {
+ FileFormatType = fileFormatType;
+ Extension = extension;
+ Description = description;
+ IsVisibleOnFileFilters = isVisibleOnFileFilters;
+ IsVisibleOnConvertMenu = isVisibleOnConvertMenu;
+ Tag = tag;
+ }
+ #endregion
+
+ #region Overrides
+
+ public override string ToString()
+ {
+ return $"{Description} ({Extension})";
+ }
+
+ public bool Equals(FileExtension? other)
+ {
+ return Extension.Equals(other?.Extension, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public bool Equals(string? other)
+ {
+ return Extension.Equals(other, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is FileExtension other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return (Extension != null ? Extension.GetHashCode() : 0);
+ }
+
+ private sealed class ExtensionEqualityComparer : IEqualityComparer<FileExtension>
+ {
+ public bool Equals(FileExtension? x, FileExtension? y)
{
- return (Extension != null ? Extension.GetHashCode() : 0);
+ if (ReferenceEquals(x, y)) return true;
+ if (ReferenceEquals(x, null)) return false;
+ if (ReferenceEquals(y, null)) return false;
+ if (x.GetType() != y.GetType()) return false;
+ return x.Extension == y.Extension;
}
- private sealed class ExtensionEqualityComparer : IEqualityComparer<FileExtension>
+ public int GetHashCode(FileExtension obj)
{
- public bool Equals(FileExtension x, FileExtension y)
- {
- if (ReferenceEquals(x, y)) return true;
- if (ReferenceEquals(x, null)) return false;
- if (ReferenceEquals(y, null)) return false;
- if (x.GetType() != y.GetType()) return false;
- return x.Extension == y.Extension;
- }
-
- public int GetHashCode(FileExtension obj)
- {
- return (obj.Extension != null ? obj.Extension.GetHashCode() : 0);
- }
+ return (obj.Extension != null ? obj.Extension.GetHashCode() : 0);
}
+ }
- public static IEqualityComparer<FileExtension> ExtensionComparer { get; } = new ExtensionEqualityComparer();
- #endregion
+ public static IEqualityComparer<FileExtension> ExtensionComparer { get; } = new ExtensionEqualityComparer();
+ #endregion
- #region Methods
+ #region Methods
- public FileFormat GetFileFormat(bool createNewInstance = false) =>
- FileFormatType is null
- ? FileFormat.FindByExtensionOrFilePath(Extension, createNewInstance)
- : FileFormat.FindByType(FileFormatType, createNewInstance);
+ public FileFormat? GetFileFormat(bool createNewInstance = false) =>
+ FileFormatType is null
+ ? FileFormat.FindByExtensionOrFilePath(Extension, createNewInstance)
+ : FileFormat.FindByType(FileFormatType, createNewInstance);
- public static FileExtension Find(string extension) =>
- FileFormat.FindExtension(extension);
- #endregion
- }
-}
+ public static FileExtension? Find(string extension) =>
+ FileFormat.FindExtension(extension);
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index badebc7..7ca06fd 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -6,8 +6,12 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.Collections;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
@@ -19,4250 +23,5340 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Timers;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
+using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
using UVtools.Core.GCode;
using UVtools.Core.Layers;
using UVtools.Core.Managers;
using UVtools.Core.Objects;
using UVtools.Core.Operations;
+using UVtools.Core.PixelEditor;
+using Range = System.Range;
+
+namespace UVtools.Core.FileFormats;
-namespace UVtools.Core.FileFormats
+/// <summary>
+/// Slicer <see cref="FileFormat"/> representation
+/// </summary>
+public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFormat>, IList<Layer>
{
- /// <summary>
- /// Slicer <see cref="FileFormat"/> representation
- /// </summary>
- public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFormat>, IEnumerable<Layer>
- {
- #region Constants
- public const string TemporaryFileAppend = ".tmp";
- public const ushort ExtraPrintTime = 300;
+ #region Constants
+ public const string TemporaryFileAppend = ".tmp";
+ public const ushort ExtraPrintTime = 300;
+
+ private const string ExtractConfigFileName = "Configuration";
+ private const string ExtractConfigFileExtension = "ini";
+
- private const string ExtractConfigFileName = "Configuration";
- private const string ExtractConfigFileExtension = "ini";
+ public const float DefaultLayerHeight = 0.05f;
+ public const ushort DefaultBottomLayerCount = 4;
+ public const ushort DefaultTransitionLayerCount = 0;
+ public const float DefaultBottomExposureTime = 30;
+ public const float DefaultExposureTime = 3;
- public const float DefaultLayerHeight = 0.05f;
- public const ushort DefaultBottomLayerCount = 4;
- public const ushort DefaultTransitionLayerCount = 0;
+ public const float DefaultBottomLiftHeight = 5;
+ public const float DefaultLiftHeight = 5;
+ public const float DefaultBottomLiftSpeed = 100;
+ public const float DefaultLiftSpeed = 100;
- public const float DefaultBottomExposureTime = 30;
- public const float DefaultExposureTime = 3;
+ public const float DefaultBottomLiftHeight2 = 0;
+ public const float DefaultLiftHeight2 = 0;
+ public const float DefaultBottomLiftSpeed2 = 300;
+ public const float DefaultLiftSpeed2 = 300;
- public const float DefaultBottomLiftHeight = 5;
- public const float DefaultLiftHeight = 5;
- public const float DefaultBottomLiftSpeed = 100;
- public const float DefaultLiftSpeed = 100;
- public const float DefaultBottomLiftHeight2 = 0;
- public const float DefaultLiftHeight2 = 0;
- public const float DefaultBottomLiftSpeed2 = 300;
- public const float DefaultLiftSpeed2 = 300;
+ public const float DefaultBottomRetractSpeed = 100;
+ public const float DefaultRetractSpeed = 100;
+ public const float DefaultBottomRetractHeight2 = 0;
+ public const float DefaultRetractHeight2 = 0;
+ public const float DefaultBottomRetractSpeed2 = 80;
+ public const float DefaultRetractSpeed2 = 80;
+ public const byte DefaultBottomLightPWM = 255;
+ public const byte DefaultLightPWM = 255;
- public const float DefaultBottomRetractSpeed = 100;
- public const float DefaultRetractSpeed = 100;
- public const float DefaultBottomRetractHeight2 = 0;
- public const float DefaultRetractHeight2 = 0;
- public const float DefaultBottomRetractSpeed2 = 80;
- public const float DefaultRetractSpeed2 = 80;
+ public const string DefaultMachineName = "Unknown";
- public const byte DefaultBottomLightPWM = 255;
- public const byte DefaultLightPWM = 255;
+ public const byte MaximumAntiAliasing = 16;
- public const string DefaultMachineName = "Unknown";
+ public const float MinimumLayerHeight = 0.01f;
+ public const float MaximumLayerHeight = 0.20f;
- public const byte MaximumAntiAliasing = 16;
+ private const ushort QueueTimerPrintTime = 250; // ms
- public const float MinimumLayerHeight = 0.01f;
- public const float MaximumLayerHeight = 0.20f;
+ public const string DATATYPE_PNG = "PNG";
+ public const string DATATYPE_JPG = "JPG";
+ public const string DATATYPE_JPEG = "JPEG";
+ public const string DATATYPE_JP2 = "JP2";
+ public const string DATATYPE_BMP = "BMP";
+ public const string DATATYPE_TIF = "TIF";
+ public const string DATATYPE_TIFF = "TIFF";
+ public const string DATATYPE_PPM = "PPM";
+ public const string DATATYPE_PMG = "PMG";
+ public const string DATATYPE_SR = "SR";
+ public const string DATATYPE_RAS = "RAS";
- private const ushort QueueTimerPrintTime = 250; // ms
+ public const string DATATYPE_RGB555 = "RGB555";
+ public const string DATATYPE_RGB565 = "RGB565";
+ public const string DATATYPE_RGB555_BE = "RGB555-BE";
+ public const string DATATYPE_RGB565_BE = "RGB565-BE";
+ public const string DATATYPE_RGB888 = "RGB888";
- public const string DATATYPE_PNG = "PNG";
- public const string DATATYPE_JPG = "JPG";
- public const string DATATYPE_JPEG = "JPEG";
- public const string DATATYPE_JP2 = "JP2";
- public const string DATATYPE_BMP = "BMP";
- public const string DATATYPE_TIF = "TIF";
- public const string DATATYPE_TIFF = "TIFF";
- public const string DATATYPE_PPM = "PPM";
- public const string DATATYPE_PMG = "PMG";
- public const string DATATYPE_SR = "SR";
- public const string DATATYPE_RAS = "RAS";
- public const string DATATYPE_RGB555 = "RGB555";
- public const string DATATYPE_RGB565 = "RGB565";
- public const string DATATYPE_RGB555_BE = "RGB555-BE";
- public const string DATATYPE_RGB565_BE = "RGB565-BE";
- public const string DATATYPE_RGB888 = "RGB888";
+ public const string DATATYPE_BGR555 = "BGR555";
+ public const string DATATYPE_BGR565 = "BGR565";
+ public const string DATATYPE_BGR555_BE = "BGR555-BE";
+ public const string DATATYPE_BGR565_BE = "BGR565-BE";
+ public const string DATATYPE_BGR888 = "BGR888";
+ #endregion
+ #region Enums
- public const string DATATYPE_BGR555 = "BGR555";
- public const string DATATYPE_BGR565 = "BGR565";
- public const string DATATYPE_BGR555_BE = "BGR555-BE";
- public const string DATATYPE_BGR565_BE = "BGR565-BE";
- public const string DATATYPE_BGR888 = "BGR888";
- #endregion
+ /// <summary>
+ /// Enumeration of file format types
+ /// </summary>
+ public enum FileFormatType : byte
+ {
+ Archive,
+ Binary
+ }
- #region Enums
+ /// <summary>
+ /// Enumeration of file thumbnail size types
+ /// </summary>
+ public enum FileThumbnailSize : byte
+ {
+ Small = 0,
+ Large
+ }
+ public enum TransitionLayerTypes : byte
+ {
/// <summary>
- /// Enumeration of file format types
+ /// Firmware transition layers are handled by printer firmware
/// </summary>
- public enum FileFormatType : byte
- {
- Archive,
- Binary
- }
+ Firmware,
/// <summary>
- /// Enumeration of file thumbnail size types
+ /// Software transition layers are handled by software and written on layer data
/// </summary>
- public enum FileThumbnailSize : byte
- {
- Small = 0,
- Large
- }
-
- public enum TransitionLayerTypes : byte
- {
- /// <summary>
- /// Firmware transition layers are handled by printer firmware
- /// </summary>
- Firmware,
-
- /// <summary>
- /// Software transition layers are handled by software and written on layer data
- /// </summary>
- Software
- }
-
- public enum FileDecodeType : byte
- {
- /// <summary>
- /// Decodes all the file information and caches layer images
- /// </summary>
- Full,
+ Software
+ }
- /// <summary>
- /// Decodes only the information in the file and thumbnails, no layer image is read nor cached, fast
- /// </summary>
- Partial,
- }
- #endregion
+ public enum FileDecodeType : byte
+ {
+ /// <summary>
+ /// Decodes all the file information and caches layer images
+ /// </summary>
+ Full,
- #region Sub Classes
/// <summary>
- /// Available Print Parameters to modify
+ /// Decodes only the information in the file and thumbnails, no layer image is read nor cached, fast
/// </summary>
- public class PrintParameterModifier
- {
+ Partial,
+ }
+ #endregion
+
+ #region Sub Classes
+ /// <summary>
+ /// Available Print Parameters to modify
+ /// </summary>
+ public class PrintParameterModifier
+ {
- #region Instances
- public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Absolute Z position", "mm", 0, 100000, 0.01, Layer.HeightPrecision);
- public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layer count", "Number of bottom/burn-in layers", "layers", 0, ushort.MaxValue, 1, 0);
- public static PrintParameterModifier TransitionLayerCount { get; } = new ("Transition layer count", "Number of transition layers", "layers",0, ushort.MaxValue, 1, 0);
+ #region Instances
+ public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Absolute Z position", "mm", 0, 100000, 0.01, Layer.HeightPrecision);
+ public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layer count", "Number of bottom/burn-in layers", "layers", 0, ushort.MaxValue, 1, 0);
+ public static PrintParameterModifier TransitionLayerCount { get; } = new ("Transition layer count", "Number of transition layers", "layers",0, ushort.MaxValue, 1, 0);
- public static PrintParameterModifier BottomLightOffDelay { get; } = new("Bottom light-off seconds", "Total motor movement time + rest time to wait before cure a new bottom layer", "s");
- public static PrintParameterModifier LightOffDelay { get; } = new("Light-off seconds", "Total motor movement time + rest time to wait before cure a new layer", "s");
+ public static PrintParameterModifier BottomLightOffDelay { get; } = new("Bottom light-off seconds", "Total motor movement time + rest time to wait before cure a new bottom layer", "s");
+ public static PrintParameterModifier LightOffDelay { get; } = new("Light-off seconds", "Total motor movement time + rest time to wait before cure a new layer", "s");
- public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
- public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
+ public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
+ public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
- public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers cure time", "s", 0.1M);
- public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Layers cure time", "s", 0.1M);
+ public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers cure time", "s", 0.1M);
+ public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Layers cure time", "s", 0.1M);
- public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
- public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
+ public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
+ public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
- public static PrintParameterModifier BottomLiftHeight { get; } = new ("Bottom lift height", "Bottom lift/peel height between layers", "mm");
- public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm");
+ public static PrintParameterModifier BottomLiftHeight { get; } = new ("Bottom lift height", "Bottom lift/peel height between layers", "mm");
+ public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm");
- public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height", "Bottom second lift/peel height between layers", "mm");
- public static PrintParameterModifier LiftHeight2 { get; } = new("2) Lift height", @"Second lift/peel height between layers", "mm");
+ public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height", "Bottom second lift/peel height between layers", "mm");
+ public static PrintParameterModifier LiftHeight2 { get; } = new("2) Lift height", @"Second lift/peel height between layers", "mm");
- public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
- public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
+ public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
+ public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
- public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
- public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
-
- public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", null, "mm");
- public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", null, "mm");
- public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", null, "mm/min", 10, 5000, 5);
-
- public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 5, 0);
- public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 5, 0);
-
- /*public static PrintParameterModifier[] Parameters = {
- BottomLayerCount,
-
- BottomWaitTimeBeforeCure,
- WaitTimeBeforeCure,
- BottomExposureTime,
- ExposureTime,
- BottomWaitTimeAfterCure,
- WaitTimeAfterCure,
-
- BottomLightOffDelay,
- LightOffDelay,
- BottomLiftHeight,
- BottomLiftSpeed,
- LiftHeight,
- LiftSpeed,
- BottomWaitTimeAfterLift,
- WaitTimeAfterLift,
- RetractSpeed,
-
- BottomLightPWM,
- LightPWM
- };*/
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Gets the name
- /// </summary>
- public string Name { get; }
-
- /// <summary>
- /// Gets the description
- /// </summary>
- public string Description { get; }
-
- /// <summary>
- /// Gets the value unit
- /// </summary>
- public string ValueUnit { get; }
-
- /// <summary>
- /// Gets the minimum value
- /// </summary>
- public decimal Minimum { get; }
-
- /// <summary>
- /// Gets the maximum value
- /// </summary>
- public decimal Maximum { get; }
-
- /// <summary>
- /// Gets the incrementing value for the dropdown
- /// </summary>
- public double Increment { get; set; } = 1;
-
- /// <summary>
- /// Gets the number of decimal plates
- /// </summary>
- public byte DecimalPlates { get; }
-
- /// <summary>
- /// Gets or sets the current / old value
- /// </summary>
- public decimal OldValue { get; set; }
-
- /// <summary>
- /// Gets or sets the new value
- /// </summary>
- public decimal NewValue { get; set; }
-
- public decimal Value
- {
- get => NewValue;
- set => OldValue = NewValue = value;
- }
+ public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
+
+ public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", null, "mm");
+ public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", null, "mm");
+ public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", null, "mm/min", 10, 5000, 5);
+
+ public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 5, 0);
+ public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 5, 0);
+
+ /*public static PrintParameterModifier[] Parameters = {
+ BottomLayerCount,
+
+ BottomWaitTimeBeforeCure,
+ WaitTimeBeforeCure,
+ BottomExposureTime,
+ ExposureTime,
+ BottomWaitTimeAfterCure,
+ WaitTimeAfterCure,
+
+ BottomLightOffDelay,
+ LightOffDelay,
+ BottomLiftHeight,
+ BottomLiftSpeed,
+ LiftHeight,
+ LiftSpeed,
+ BottomWaitTimeAfterLift,
+ WaitTimeAfterLift,
+ RetractSpeed,
+
+ BottomLightPWM,
+ LightPWM
+ };*/
+ #endregion
- /// <summary>
- /// Gets if the value has changed
- /// </summary>
- public bool HasChanged => OldValue != NewValue;
- #endregion
+ #region Properties
- #region Constructor
- public PrintParameterModifier(string name, string description = null, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000, double increment = 0.5, byte decimalPlates = 2)
- {
- Name = name;
- Description = description ?? $"Modify '{name}'";
- ValueUnit = valueUnit ?? string.Empty;
- Minimum = minimum;
- Maximum = maximum;
- Increment = decimalPlates == 0 ? Math.Max(1, increment) : increment;
- DecimalPlates = decimalPlates;
- }
- #endregion
+ /// <summary>
+ /// Gets the name
+ /// </summary>
+ public string Name { get; }
- #region Overrides
+ /// <summary>
+ /// Gets the description
+ /// </summary>
+ public string Description { get; }
- protected bool Equals(PrintParameterModifier other)
- {
- return Name == other.Name;
- }
+ /// <summary>
+ /// Gets the value unit
+ /// </summary>
+ public string ValueUnit { get; }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((PrintParameterModifier) obj);
- }
+ /// <summary>
+ /// Gets the minimum value
+ /// </summary>
+ public decimal Minimum { get; }
- public override int GetHashCode()
- {
- return (Name != null ? Name.GetHashCode() : 0);
- }
+ /// <summary>
+ /// Gets the maximum value
+ /// </summary>
+ public decimal Maximum { get; }
- public override string ToString()
- {
- return $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ValueUnit)}: {ValueUnit}, {nameof(Minimum)}: {Minimum}, {nameof(Maximum)}: {Maximum}, {nameof(DecimalPlates)}: {DecimalPlates}, {nameof(OldValue)}: {OldValue}, {nameof(NewValue)}: {NewValue}, {nameof(HasChanged)}: {HasChanged}";
- }
- #endregion
- }
- #endregion
+ /// <summary>
+ /// Gets the incrementing value for the dropdown
+ /// </summary>
+ public double Increment { get; set; } = 1;
- #region Static Methods
/// <summary>
- /// Gets the available formats to process
+ /// Gets the number of decimal plates
/// </summary>
- public static FileFormat[] AvailableFormats { get; } =
- {
- new SL1File(), // Prusa SL1
- new ChituboxZipFile(), // Zip
- new ChituboxFile(), // cbddlp, cbt, photon
- new CTBEncryptedFile(), // encrypted ctb
- new PhotonSFile(), // photons
- new PHZFile(), // phz
- new FDGFile(), // fdg
- new PhotonWorkshopFile(), // PSW
- new CWSFile(), // CWS
- new OSLAFile(), // OSLA
- new ZCodeFile(), // zcode
- new ZCodexFile(), // zcodex
- new MDLPFile(), // MKS v1
- new GR1File(), // GR1 Workshop
- //new CXDLPv1File(), // Creality Box v1
- new CXDLPFile(), // Creality Box
- new LGSFile(), // LGS, LGS30
- new FlashForgeSVGXFile(), // SVGX
- new GenericZIPFile(), // Generic zip files
- new VDAFile(), // VDA
- new VDTFile(), // VDT
- new UVJFile(), // UVJ
- new ImageFile(), // images
- };
-
- public static string AllSlicerFiles => AvailableFormats.Aggregate("All slicer files|",
- (current, fileFormat) => current.EndsWith("|")
- ? $"{current}{fileFormat.FileFilterExtensionsOnly}"
- : $"{current}; {fileFormat.FileFilterExtensionsOnly}");
+ public byte DecimalPlates { get; }
/// <summary>
- /// Gets all filters for open and save file dialogs
+ /// Gets or sets the current / old value
/// </summary>
- public static string AllFileFilters =>
- AllSlicerFiles
- +
- AvailableFormats.Aggregate(string.Empty,
- (current, fileFormat) => $"{current}|" + fileFormat.FileFilter);
+ public decimal OldValue { get; set; }
- public static List<KeyValuePair<string, List<string>>> AllFileFiltersAvalonia
- {
- get
- {
- var result = new List<KeyValuePair<string, List<string>>>
- {
- new("All slicer files", new List<string>())
- };
-
- for (int i = 0; i < AvailableFormats.Length; i++)
- {
- foreach (var fileExtension in AvailableFormats[i].FileExtensions)
- {
- if(!fileExtension.IsVisibleOnFileFilters) continue;
- result[0].Value.Add(fileExtension.Extension);
- result.Add(new KeyValuePair<string, List<string>>(fileExtension.Description, new List<string>
- {
- fileExtension.Extension
- }));
- }
- }
+ /// <summary>
+ /// Gets or sets the new value
+ /// </summary>
+ public decimal NewValue { get; set; }
- return result;
- }
-
+ public decimal Value
+ {
+ get => NewValue;
+ set => OldValue = NewValue = value;
}
- public static List<FileExtension> AllFileExtensions
+ /// <summary>
+ /// Gets if the value has changed
+ /// </summary>
+ public bool HasChanged => OldValue != NewValue;
+ #endregion
+
+ #region Constructor
+ public PrintParameterModifier(string name, string? description = null, string? valueUnit = null, decimal minimum = 0, decimal maximum = 1000, double increment = 0.5, byte decimalPlates = 2)
{
- get
- {
- List<FileExtension> extensions = new();
- foreach (var slicerFile in AvailableFormats)
- {
- extensions.AddRange(slicerFile.FileExtensions);
- }
- return extensions;
- }
+ Name = name;
+ Description = description ?? $"Modify '{name}'";
+ ValueUnit = valueUnit ?? string.Empty;
+ Minimum = minimum;
+ Maximum = maximum;
+ Increment = decimalPlates == 0 ? Math.Max(1, increment) : increment;
+ DecimalPlates = decimalPlates;
}
+ #endregion
- public static List<string> AllFileExtensionsString => (from slicerFile in AvailableFormats from extension in slicerFile.FileExtensions select extension.Extension).ToList();
+ #region Overrides
+ protected bool Equals(PrintParameterModifier other)
+ {
+ return Name == other.Name;
+ }
- /// <summary>
- /// Gets the count of available file extensions
- /// </summary>
- public static byte FileExtensionsCount => AvailableFormats.Aggregate<FileFormat, byte>(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length));
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((PrintParameterModifier) obj);
+ }
- /// <summary>
- /// Find <see cref="FileFormat"/> by an extension
- /// </summary>
- /// <param name="extensionOrFilePath"> name to find</param>
- /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
- /// <returns><see cref="FileFormat"/> object or null if not found</returns>
- public static FileFormat FindByExtensionOrFilePath(string extensionOrFilePath, bool createNewInstance = false)
+ public override int GetHashCode()
{
- if (string.IsNullOrWhiteSpace(extensionOrFilePath)) return null;
+ return (Name != null ? Name.GetHashCode() : 0);
+ }
- bool isFilePath = false;
- // Test for ext first
- var fileFormats = AvailableFormats.Where(fileFormat => fileFormat.IsExtensionValid(extensionOrFilePath)).ToArray();
- if (fileFormats.Length == 0) // Extension not found, can be filepath, try to find it
- {
- GetFileNameStripExtensions(extensionOrFilePath, out var extension);
- if (string.IsNullOrWhiteSpace(extension)) return null;
+ public override string ToString()
+ {
+ return $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ValueUnit)}: {ValueUnit}, {nameof(Minimum)}: {Minimum}, {nameof(Maximum)}: {Maximum}, {nameof(DecimalPlates)}: {DecimalPlates}, {nameof(OldValue)}: {OldValue}, {nameof(NewValue)}: {NewValue}, {nameof(HasChanged)}: {HasChanged}";
+ }
+ #endregion
+ }
+ #endregion
- fileFormats = AvailableFormats.Where(fileFormat => fileFormat.IsExtensionValid(extension)).ToArray();
- if (fileFormats.Length == 0) return null;
- isFilePath = true; // Was a file path
- }
+ #region Static Methods
+ /// <summary>
+ /// Gets the available formats to process
+ /// </summary>
+ public static FileFormat[] AvailableFormats { get; } =
+ {
+ new SL1File(), // Prusa SL1
+ new ChituboxZipFile(), // Zip
+ new ChituboxFile(), // cbddlp, cbt, photon
+ new CTBEncryptedFile(), // encrypted ctb
+ new PhotonSFile(), // photons
+ new PHZFile(), // phz
+ new FDGFile(), // fdg
+ new PhotonWorkshopFile(), // PSW
+ new CWSFile(), // CWS
+ new OSLAFile(), // OSLA
+ new ZCodeFile(), // zcode
+ new ZCodexFile(), // zcodex
+ new MDLPFile(), // MKS v1
+ new GR1File(), // GR1 Workshop
+ //new CXDLPv1File(), // Creality Box v1
+ new CXDLPFile(), // Creality Box
+ new LGSFile(), // LGS, LGS30
+ new FlashForgeSVGXFile(), // SVGX
+ new GenericZIPFile(), // Generic zip files
+ new VDAFile(), // VDA
+ new VDTFile(), // VDT
+ new UVJFile(), // UVJ
+ new ImageFile(), // images
+ };
+
+ public static string AllSlicerFiles => AvailableFormats.Aggregate("All slicer files|",
+ (current, fileFormat) => current.EndsWith("|")
+ ? $"{current}{fileFormat.FileFilterExtensionsOnly}"
+ : $"{current}; {fileFormat.FileFilterExtensionsOnly}");
- if (fileFormats.Length == 1 || !isFilePath)
- return createNewInstance
- ? (FileFormat)Activator.CreateInstance(fileFormats[0].GetType())
- : fileFormats[0];
+ /// <summary>
+ /// Gets all filters for open and save file dialogs
+ /// </summary>
+ public static string AllFileFilters =>
+ AllSlicerFiles
+ +
+ AvailableFormats.Aggregate(string.Empty,
+ (current, fileFormat) => $"{current}|" + fileFormat.FileFilter);
- // Multiple instances using Check for valid candidate
- foreach (var fileFormat in fileFormats)
+ public static List<KeyValuePair<string, List<string>>> AllFileFiltersAvalonia
+ {
+ get
+ {
+ var result = new List<KeyValuePair<string, List<string>>>
+ {
+ new("All slicer files", new List<string>())
+ };
+
+ for (int i = 0; i < AvailableFormats.Length; i++)
{
- if (fileFormat.CanProcess(extensionOrFilePath))
+ foreach (var fileExtension in AvailableFormats[i].FileExtensions)
{
- return createNewInstance
- ? (FileFormat)Activator.CreateInstance(fileFormat.GetType())
- : fileFormat;
+ if(!fileExtension.IsVisibleOnFileFilters) continue;
+ result[0].Value.Add(fileExtension.Extension);
+ result.Add(new KeyValuePair<string, List<string>>(fileExtension.Description, new List<string>
+ {
+ fileExtension.Extension
+ }));
}
}
- // Try this in a far and not probable attempt
- return createNewInstance
- ? (FileFormat)Activator.CreateInstance(fileFormats[0].GetType())
- : fileFormats[0];
+ return result;
}
+
+ }
- public static FileExtension FindExtension(string extension)
+ public static List<FileExtension> AllFileExtensions
+ {
+ get
{
- return AvailableFormats.SelectMany(format => format.FileExtensions).FirstOrDefault(ext => ext.Equals(extension));
+ List<FileExtension> extensions = new();
+ foreach (var slicerFile in AvailableFormats)
+ {
+ extensions.AddRange(slicerFile.FileExtensions);
+ }
+ return extensions;
}
+ }
- /// <summary>
- /// Find <see cref="FileFormat"/> by an type
- /// </summary>
- /// <param name="type">Type to find</param>
- /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
- /// <returns><see cref="FileFormat"/> object or null if not found</returns>
- public static FileFormat FindByType(Type type, bool createNewInstance = false)
- {
- var fileFormat = AvailableFormats.FirstOrDefault(format => format.GetType() == type);
- if (fileFormat is null) return null;
- return createNewInstance
- ? (FileFormat)Activator.CreateInstance(type)
- : fileFormat;
- //return (from t in AvailableFormats where type == t.GetType() select createNewInstance ? (FileFormat) Activator.CreateInstance(type) : t).FirstOrDefault();
- }
+ public static List<string> AllFileExtensionsString => (from slicerFile in AvailableFormats from extension in slicerFile.FileExtensions select extension.Extension).ToList();
+
+
+ /// <summary>
+ /// Gets the count of available file extensions
+ /// </summary>
+ public static byte FileExtensionsCount => AvailableFormats.Aggregate<FileFormat, byte>(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length));
- public static string GetFileNameStripExtensions(string filepath)
+ /// <summary>
+ /// Find <see cref="FileFormat"/> by an extension
+ /// </summary>
+ /// <param name="extensionOrFilePath"> name to find</param>
+ /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
+ /// <returns><see cref="FileFormat"/> object or null if not found</returns>
+ public static FileFormat? FindByExtensionOrFilePath(string extensionOrFilePath, bool createNewInstance = false)
+ {
+ if (string.IsNullOrWhiteSpace(extensionOrFilePath)) return null;
+
+ bool isFilePath = false;
+ // Test for ext first
+ var fileFormats = AvailableFormats.Where(fileFormat => fileFormat.IsExtensionValid(extensionOrFilePath)).ToArray();
+ if (fileFormats.Length == 0) // Extension not found, can be filepath, try to find it
{
- //if (file.EndsWith(TemporaryFileAppend)) file = Path.GetFileNameWithoutExtension(file);
- return PathExtensions.GetFileNameStripExtensions(filepath, AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out _);
+ GetFileNameStripExtensions(extensionOrFilePath, out var extension);
+ if (string.IsNullOrWhiteSpace(extension)) return null;
+
+ fileFormats = AvailableFormats.Where(fileFormat => fileFormat.IsExtensionValid(extension)).ToArray();
+ if (fileFormats.Length == 0) return null;
+ isFilePath = true; // Was a file path
}
- public static string GetFileNameStripExtensions(string filepath, out string strippedExtension)
+ if (fileFormats.Length == 1 || !isFilePath)
{
- //if (file.EndsWith(TemporaryFileAppend)) file = Path.GetFileNameWithoutExtension(file);
- return PathExtensions.GetFileNameStripExtensions(filepath, AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out strippedExtension);
+ return createNewInstance
+ ? Activator.CreateInstance(fileFormats[0].GetType()) as FileFormat
+ : fileFormats[0];
}
+
- public static FileFormat Open(string fileFullPath, FileDecodeType decodeType, OperationProgress progress = null)
+ // Multiple instances using Check for valid candidate
+ foreach (var fileFormat in fileFormats)
{
- var slicerFile = FindByExtensionOrFilePath(fileFullPath, true);
- if (slicerFile is null) return null;
- slicerFile.Decode(fileFullPath, decodeType, progress);
- return slicerFile;
+ if (fileFormat.CanProcess(extensionOrFilePath))
+ {
+ return createNewInstance
+ ? Activator.CreateInstance(fileFormat.GetType()) as FileFormat
+ : fileFormat;
+ }
}
- public static FileFormat Open(string fileFullPath, OperationProgress progress = null) =>
- Open(fileFullPath, FileDecodeType.Full, progress);
+ // Try this in a far and not probable attempt
+ return createNewInstance
+ ? Activator.CreateInstance(fileFormats[0].GetType()) as FileFormat
+ : fileFormats[0];
+ }
- /// <summary>
- /// Copy parameters from one file to another
- /// </summary>
- /// <param name="from">From source file</param>
- /// <param name="to">To target file</param>
- /// <returns>Number of affected parameters</returns>
- public static uint CopyParameters(FileFormat from, FileFormat to)
- {
- if (ReferenceEquals(from, to)) return 0;
- if (from.PrintParameterModifiers is null || to.PrintParameterModifiers is null) return 0;
+ public static FileExtension? FindExtension(string extension)
+ {
+ return AvailableFormats.SelectMany(format => format.FileExtensions).FirstOrDefault(ext => ext.Equals(extension));
+ }
- uint count = 0;
+ /// <summary>
+ /// Find <see cref="FileFormat"/> by an type
+ /// </summary>
+ /// <param name="type">Type to find</param>
+ /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
+ /// <returns><see cref="FileFormat"/> object or null if not found</returns>
+ public static FileFormat? FindByType(Type type, bool createNewInstance = false)
+ {
+ var fileFormat = AvailableFormats.FirstOrDefault(format => format.GetType() == type);
+ if (fileFormat is null) return null;
+ return createNewInstance
+ ? Activator.CreateInstance(type) as FileFormat
+ : fileFormat;
+ //return (from t in AvailableFormats where type == t.GetType() select createNewInstance ? (FileFormat) Activator.CreateInstance(type) : t).FirstOrDefault();
+ }
- to.RefreshPrintParametersModifiersValues();
- var targetPrintModifier = to.PrintParameterModifiers.ToArray();
- from.RefreshPrintParametersModifiersValues();
- foreach (var sourceModifier in from.PrintParameterModifiers)
- {
- if (!targetPrintModifier.Contains(sourceModifier)) continue;
+ public static string? GetFileNameStripExtensions(string? filepath)
+ {
+ if (filepath is null) return null;
+ //if (file.EndsWith(TemporaryFileAppend)) file = Path.GetFileNameWithoutExtension(file);
+ return PathExtensions.GetFileNameStripExtensions(filepath, AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out _);
+ }
- var fromValueStr = from.GetValueFromPrintParameterModifier(sourceModifier).ToString();
- var toValueStr = to.GetValueFromPrintParameterModifier(sourceModifier).ToString();
+ public static string GetFileNameStripExtensions(string filepath, out string strippedExtension)
+ {
+ //if (file.EndsWith(TemporaryFileAppend)) file = Path.GetFileNameWithoutExtension(file);
+ return PathExtensions.GetFileNameStripExtensions(filepath, AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out strippedExtension);
+ }
- if (decimal.TryParse(fromValueStr, out var fromValue) && decimal.TryParse(toValueStr, out var toValue) && fromValue != toValue)
- {
- to.SetValueFromPrintParameterModifier(sourceModifier, fromValue);
- count++;
- }
- }
-
- return count;
- }
+ public static FileFormat? Open(string fileFullPath, FileDecodeType decodeType, OperationProgress? progress = null)
+ {
+ var slicerFile = FindByExtensionOrFilePath(fileFullPath, true);
+ if (slicerFile is null) return null;
+ slicerFile.Decode(fileFullPath, decodeType, progress);
+ return slicerFile;
+ }
- public static byte[] EncodeImage(string dataType, Mat mat)
- {
- dataType = dataType.ToUpperInvariant();
- if (dataType
- is DATATYPE_PNG
- or DATATYPE_JPG
- or DATATYPE_JPEG
- or DATATYPE_JP2
- or DATATYPE_BMP
- or DATATYPE_TIF
- or DATATYPE_TIFF
- or DATATYPE_PPM
- or DATATYPE_PMG
- or DATATYPE_SR
- or DATATYPE_RAS
- )
- {
- return CvInvoke.Imencode($".{dataType.ToLowerInvariant()}", mat);
- }
+ public static FileFormat? Open(string fileFullPath, OperationProgress? progress = null) =>
+ Open(fileFullPath, FileDecodeType.Full, progress);
- if (dataType
- is DATATYPE_RGB555
- or DATATYPE_RGB565
- or DATATYPE_RGB555_BE
- or DATATYPE_RGB565_BE
- or DATATYPE_RGB888
-
- or DATATYPE_BGR555
- or DATATYPE_BGR565
- or DATATYPE_BGR555_BE
- or DATATYPE_BGR565_BE
- or DATATYPE_BGR888
- )
- {
- var bytesPerPixel = dataType is "RGB888" or "BGR888" ? 3 : 2;
- var bytes = new byte[mat.Width * mat.Height * bytesPerPixel];
- uint index = 0;
- var span = mat.GetDataByteSpan();
- for (int i = 0; i < span.Length;)
- {
- byte b = span[i++];
- byte g;
- byte r;
+ /// <summary>
+ /// Copy parameters from one file to another
+ /// </summary>
+ /// <param name="from">From source file</param>
+ /// <param name="to">To target file</param>
+ /// <returns>Number of affected parameters</returns>
+ public static uint CopyParameters(FileFormat from, FileFormat to)
+ {
+ if (ReferenceEquals(from, to)) return 0;
+ if (from.PrintParameterModifiers is null || to.PrintParameterModifiers is null) return 0;
- if (mat.NumberOfChannels == 1) // 8 bit safe-guard
- {
- r = g = b;
- }
- else
- {
- g = span[i++];
- r = span[i++];
- }
+ uint count = 0;
- if (mat.NumberOfChannels == 4) i++; // skip alpha
+ to.RefreshPrintParametersModifiersValues();
+ var targetPrintModifier = to.PrintParameterModifiers.ToArray();
+ from.RefreshPrintParametersModifiersValues();
+ foreach (var sourceModifier in from.PrintParameterModifiers)
+ {
+ if (!targetPrintModifier.Contains(sourceModifier)) continue;
- switch (dataType)
- {
- case DATATYPE_RGB555:
- var rgb555 = (ushort)(((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3));
- BitExtensions.ToBytesLittleEndian(rgb555, bytes, index);
- index += 2;
- break;
- case DATATYPE_RGB565:
- var rgb565 = (ushort)(((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3));
- BitExtensions.ToBytesLittleEndian(rgb565, bytes, index);
- index += 2;
- break;
- case DATATYPE_RGB555_BE:
- var rgb555Be = (ushort)(((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3));
- BitExtensions.ToBytesBigEndian(rgb555Be, bytes, index);
- index += 2;
- break;
- case DATATYPE_RGB565_BE:
- var rgb565Be = (ushort)(((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3));
- BitExtensions.ToBytesBigEndian(rgb565Be, bytes, index);
- index += 2;
- break;
- case DATATYPE_RGB888:
- bytes[index++] = r;
- bytes[index++] = g;
- bytes[index++] = b;
- break;
- case DATATYPE_BGR555:
- var bgr555 = (ushort)(((b & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (r >> 3));
- BitExtensions.ToBytesLittleEndian(bgr555, bytes, index);
- index += 2;
- break;
- case DATATYPE_BGR565:
- var bgr565 = (ushort)(((b & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (r >> 3));
- BitExtensions.ToBytesLittleEndian(bgr565, bytes, index);
- index += 2;
- break;
- case DATATYPE_BGR555_BE:
- var bgr555Be = (ushort)(((b & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (r >> 3));
- BitExtensions.ToBytesBigEndian(bgr555Be, bytes, index);
- index += 2;
- break;
- case DATATYPE_BGR565_BE:
- var bgr565Be = (ushort)(((b & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (r >> 3));
- BitExtensions.ToBytesBigEndian(bgr565Be, bytes, index);
- index += 2;
- break;
- case DATATYPE_BGR888:
- bytes[index++] = b;
- bytes[index++] = g;
- bytes[index++] = r;
- break;
- }
- }
+ var fromValueStr = from.GetValueFromPrintParameterModifier(sourceModifier)?.ToString();
+ var toValueStr = to.GetValueFromPrintParameterModifier(sourceModifier)?.ToString();
- return bytes;
- }
+ if (fromValueStr is null || toValueStr is null) continue;
- throw new NotSupportedException($"The encode type: {dataType} is not supported.");
+ if (decimal.TryParse(fromValueStr, out var fromValue) && decimal.TryParse(toValueStr, out var toValue) && fromValue != toValue)
+ {
+ to.SetValueFromPrintParameterModifier(sourceModifier, fromValue);
+ count++;
+ }
}
+
+ return count;
+ }
+
+ /// <summary>
+ /// Compress a layer from a <see cref="Stream"/>
+ /// </summary>
+ /// <param name="input"><see cref="Stream"/> to compress</param>
+ /// <returns>Compressed byte array</returns>
+ public static byte[] CompressLayer(Stream input)
+ {
+ return CompressLayer(input.ToArray());
+ }
- public static Mat DecodeImage(string dataType, byte[] bytes, Size resolution)
+ /// <summary>
+ /// Compress a layer from a byte array
+ /// </summary>
+ /// <param name="input">byte array to compress</param>
+ /// <returns>Compressed byte array</returns>
+ public static byte[] CompressLayer(byte[] input)
+ {
+ return input;
+ /*using (MemoryStream output = new MemoryStream())
{
- if (dataType
- is DATATYPE_PNG
- or DATATYPE_JPG
- or DATATYPE_JPEG
- or DATATYPE_JP2
- or DATATYPE_BMP
- or DATATYPE_TIF
- or DATATYPE_TIFF
- or DATATYPE_PPM
- or DATATYPE_PMG
- or DATATYPE_SR
- or DATATYPE_RAS
- )
+ using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal))
{
- var mat = new Mat();
- CvInvoke.Imdecode(bytes, ImreadModes.AnyColor, mat);
- return mat;
+ dstream.Write(input, 0, input.Length);
}
+ return output.ToArray();
+ }*/
+ }
- if (dataType
- is DATATYPE_RGB555
- or DATATYPE_RGB565
- or DATATYPE_RGB555_BE
- or DATATYPE_RGB565_BE
- or DATATYPE_RGB888
-
- or DATATYPE_BGR555
- or DATATYPE_BGR565
- or DATATYPE_BGR555_BE
- or DATATYPE_BGR565_BE
- or DATATYPE_BGR888
- )
+ /// <summary>
+ /// Decompress a layer from a byte array
+ /// </summary>
+ /// <param name="input">byte array to decompress</param>
+ /// <returns>Decompressed byte array</returns>
+ public static byte[] DecompressLayer(byte[] input)
+ {
+ return input;
+ /*using (MemoryStream ms = new MemoryStream(input))
+ {
+ using (MemoryStream output = new MemoryStream())
{
- var mat = new Mat(resolution, DepthType.Cv8U, 3);
- var span = mat.GetDataByteSpan();
- var pixel = 0;
- int i = 0;
- while (i < bytes.Length && pixel < span.Length)
+ using (DeflateStream dstream = new DeflateStream(ms, CompressionMode.Decompress))
{
- switch (dataType)
- {
- case DATATYPE_RGB555:
- ushort rgb555 = BitExtensions.ToUShortLittleEndian(bytes, i);
- // 0b0rrrrrgggggbbbbb
- span[pixel++] = (byte)((rgb555 & 0b00000000_00011111) << 3); // b
- span[pixel++] = (byte)((rgb555 & 0b00000011_11100000) >> 2); // g
- span[pixel++] = (byte)((rgb555 & 0b01111100_00000000) >> 7); // r
- /*span[pixel++] = (byte)((rgb555 << 3) & 0b11111000); // b
- span[pixel++] = (byte)((rgb555 >> 2) & 0b11111000); // g
- span[pixel++] = (byte)((rgb555 >> 7) & 0b11111000); // r*/
- i += 2;
- break;
- case DATATYPE_RGB565:
- // 0brrrrrggggggbbbbb
- ushort rgb565 = BitExtensions.ToUShortLittleEndian(bytes, i);
- span[pixel++] = (byte)((rgb565 & 0b00000000_00011111) << 3); // b
- span[pixel++] = (byte)((rgb565 & 0b00000111_11100000) >> 3); // g
- span[pixel++] = (byte)((rgb565 & 0b11111000_00000000) >> 8); // r
- i += 2;
- break;
- case DATATYPE_RGB555_BE:
- ushort rgb555Be = BitExtensions.ToUShortBigEndian(bytes, i);
- span[pixel++] = (byte)((rgb555Be & 0b00000000_00011111) << 3); // b
- span[pixel++] = (byte)((rgb555Be & 0b00000011_11100000) >> 2); // g
- span[pixel++] = (byte)((rgb555Be & 0b01111100_00000000) >> 7); // r
- i += 2;
- break;
- case DATATYPE_RGB565_BE:
- ushort rgb565Be = BitExtensions.ToUShortBigEndian(bytes, i);
- span[pixel++] = (byte)((rgb565Be & 0b00000000_00011111) << 3); // b
- span[pixel++] = (byte)((rgb565Be & 0b00000111_11100000) >> 3); // g
- span[pixel++] = (byte)((rgb565Be & 0b11111000_00000000) >> 8); // r
- i += 2;
- break;
- case DATATYPE_RGB888:
- span[pixel++] = bytes[i + 2]; // b
- span[pixel++] = bytes[i + 1]; // g
- span[pixel++] = bytes[i]; // r
- i += 3;
- break;
- case DATATYPE_BGR555:
- ushort bgr555 = BitExtensions.ToUShortLittleEndian(bytes, i);
- span[pixel++] = (byte)((bgr555 & 0b01111100_00000000) >> 7); // b
- span[pixel++] = (byte)((bgr555 & 0b00000011_11100000) >> 2); // g
- span[pixel++] = (byte)((bgr555 & 0b00000000_00011111) << 3); // r
- i += 2;
- break;
- case DATATYPE_BGR565:
- ushort bgr565 = BitExtensions.ToUShortLittleEndian(bytes, i);
- span[pixel++] = (byte)((bgr565 & 0b11111000_00000000) >> 8); // b
- span[pixel++] = (byte)((bgr565 & 0b00000111_11100000) >> 3); // g
- span[pixel++] = (byte)((bgr565 & 0b00000000_00011111) << 3); // r
- i += 2;
- break;
- case DATATYPE_BGR555_BE:
- ushort bgr555Be = BitExtensions.ToUShortBigEndian(bytes, i);
- span[pixel++] = (byte)((bgr555Be & 0b01111100_00000000) >> 7); // b
- span[pixel++] = (byte)((bgr555Be & 0b00000011_11100000) >> 2); // g
- span[pixel++] = (byte)((bgr555Be & 0b00000000_00011111) << 3); // r
- i += 2;
- break;
- case DATATYPE_BGR565_BE:
- ushort bgr565Be = BitExtensions.ToUShortBigEndian(bytes, i);
- span[pixel++] = (byte)((bgr565Be & 0b11111000_00000000) >> 8); // b
- span[pixel++] = (byte)((bgr565Be & 0b00000111_11100000) >> 3); // g
- span[pixel++] = (byte)((bgr565Be & 0b00000000_00011111) << 3); // r
- i += 2;
- break;
- case DATATYPE_BGR888:
- span[pixel++] = bytes[i]; // b
- span[pixel++] = bytes[i + 1]; // g
- span[pixel++] = bytes[i + 2]; // r
- i += 3;
- break;
- }
+ dstream.CopyTo(output);
}
+ return output.ToArray();
+ }
+ }*/
+ }
- for (; pixel < span.Length; pixel++) // Fill leftovers
+ public static byte[] EncodeImage(string dataType, Mat mat)
+ {
+ dataType = dataType.ToUpperInvariant();
+ if (dataType
+ is DATATYPE_PNG
+ or DATATYPE_JPG
+ or DATATYPE_JPEG
+ or DATATYPE_JP2
+ or DATATYPE_BMP
+ or DATATYPE_TIF
+ or DATATYPE_TIFF
+ or DATATYPE_PPM
+ or DATATYPE_PMG
+ or DATATYPE_SR
+ or DATATYPE_RAS
+ )
+ {
+ return CvInvoke.Imencode($".{dataType.ToLowerInvariant()}", mat);
+ }
+
+ if (dataType
+ is DATATYPE_RGB555
+ or DATATYPE_RGB565
+ or DATATYPE_RGB555_BE
+ or DATATYPE_RGB565_BE
+ or DATATYPE_RGB888
+
+ or DATATYPE_BGR555
+ or DATATYPE_BGR565
+ or DATATYPE_BGR555_BE
+ or DATATYPE_BGR565_BE
+ or DATATYPE_BGR888
+ )
+ {
+ var bytesPerPixel = dataType is "RGB888" or "BGR888" ? 3 : 2;
+ var bytes = new byte[mat.Width * mat.Height * bytesPerPixel];
+ uint index = 0;
+ var span = mat.GetDataByteSpan();
+ for (int i = 0; i < span.Length;)
+ {
+ byte b = span[i++];
+ byte g;
+ byte r;
+
+ if (mat.NumberOfChannels == 1) // 8 bit safe-guard
+ {
+ r = g = b;
+ }
+ else
{
- span[pixel] = 0;
+ g = span[i++];
+ r = span[i++];
}
- return mat;
+ if (mat.NumberOfChannels == 4) i++; // skip alpha
+
+ switch (dataType)
+ {
+ case DATATYPE_RGB555:
+ var rgb555 = (ushort)(((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3));
+ BitExtensions.ToBytesLittleEndian(rgb555, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_RGB565:
+ var rgb565 = (ushort)(((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3));
+ BitExtensions.ToBytesLittleEndian(rgb565, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_RGB555_BE:
+ var rgb555Be = (ushort)(((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3));
+ BitExtensions.ToBytesBigEndian(rgb555Be, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_RGB565_BE:
+ var rgb565Be = (ushort)(((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3));
+ BitExtensions.ToBytesBigEndian(rgb565Be, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_RGB888:
+ bytes[index++] = r;
+ bytes[index++] = g;
+ bytes[index++] = b;
+ break;
+ case DATATYPE_BGR555:
+ var bgr555 = (ushort)(((b & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (r >> 3));
+ BitExtensions.ToBytesLittleEndian(bgr555, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_BGR565:
+ var bgr565 = (ushort)(((b & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (r >> 3));
+ BitExtensions.ToBytesLittleEndian(bgr565, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_BGR555_BE:
+ var bgr555Be = (ushort)(((b & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (r >> 3));
+ BitExtensions.ToBytesBigEndian(bgr555Be, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_BGR565_BE:
+ var bgr565Be = (ushort)(((b & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (r >> 3));
+ BitExtensions.ToBytesBigEndian(bgr565Be, bytes, index);
+ index += 2;
+ break;
+ case DATATYPE_BGR888:
+ bytes[index++] = b;
+ bytes[index++] = g;
+ bytes[index++] = r;
+ break;
+ }
}
- throw new NotSupportedException($"The decode type: {dataType} is not supported.");
+ return bytes;
}
- public static Mat DecodeImage(string dataType, byte[] bytes, uint resolutionX = 0, uint resolutionY = 0)
- => DecodeImage(dataType, bytes, new Size((int)resolutionX, (int)resolutionY));
- #endregion
+ throw new NotSupportedException($"The encode type: {dataType} is not supported.");
+ }
+
+ public static Mat DecodeImage(string dataType, byte[] bytes, Size resolution)
+ {
+ if (dataType
+ is DATATYPE_PNG
+ or DATATYPE_JPG
+ or DATATYPE_JPEG
+ or DATATYPE_JP2
+ or DATATYPE_BMP
+ or DATATYPE_TIF
+ or DATATYPE_TIFF
+ or DATATYPE_PPM
+ or DATATYPE_PMG
+ or DATATYPE_SR
+ or DATATYPE_RAS
+ )
+ {
+ var mat = new Mat();
+ CvInvoke.Imdecode(bytes, ImreadModes.AnyColor, mat);
+ return mat;
+ }
+
+ if (dataType
+ is DATATYPE_RGB555
+ or DATATYPE_RGB565
+ or DATATYPE_RGB555_BE
+ or DATATYPE_RGB565_BE
+ or DATATYPE_RGB888
+
+ or DATATYPE_BGR555
+ or DATATYPE_BGR565
+ or DATATYPE_BGR555_BE
+ or DATATYPE_BGR565_BE
+ or DATATYPE_BGR888
+ )
+ {
+ var mat = new Mat(resolution, DepthType.Cv8U, 3);
+ var span = mat.GetDataByteSpan();
+ var pixel = 0;
+ int i = 0;
+ while (i < bytes.Length && pixel < span.Length)
+ {
+ switch (dataType)
+ {
+ case DATATYPE_RGB555:
+ ushort rgb555 = BitExtensions.ToUShortLittleEndian(bytes, i);
+ // 0b0rrrrrgggggbbbbb
+ span[pixel++] = (byte)((rgb555 & 0b00000000_00011111) << 3); // b
+ span[pixel++] = (byte)((rgb555 & 0b00000011_11100000) >> 2); // g
+ span[pixel++] = (byte)((rgb555 & 0b01111100_00000000) >> 7); // r
+ /*span[pixel++] = (byte)((rgb555 << 3) & 0b11111000); // b
+ span[pixel++] = (byte)((rgb555 >> 2) & 0b11111000); // g
+ span[pixel++] = (byte)((rgb555 >> 7) & 0b11111000); // r*/
+ i += 2;
+ break;
+ case DATATYPE_RGB565:
+ // 0brrrrrggggggbbbbb
+ ushort rgb565 = BitExtensions.ToUShortLittleEndian(bytes, i);
+ span[pixel++] = (byte)((rgb565 & 0b00000000_00011111) << 3); // b
+ span[pixel++] = (byte)((rgb565 & 0b00000111_11100000) >> 3); // g
+ span[pixel++] = (byte)((rgb565 & 0b11111000_00000000) >> 8); // r
+ i += 2;
+ break;
+ case DATATYPE_RGB555_BE:
+ ushort rgb555Be = BitExtensions.ToUShortBigEndian(bytes, i);
+ span[pixel++] = (byte)((rgb555Be & 0b00000000_00011111) << 3); // b
+ span[pixel++] = (byte)((rgb555Be & 0b00000011_11100000) >> 2); // g
+ span[pixel++] = (byte)((rgb555Be & 0b01111100_00000000) >> 7); // r
+ i += 2;
+ break;
+ case DATATYPE_RGB565_BE:
+ ushort rgb565Be = BitExtensions.ToUShortBigEndian(bytes, i);
+ span[pixel++] = (byte)((rgb565Be & 0b00000000_00011111) << 3); // b
+ span[pixel++] = (byte)((rgb565Be & 0b00000111_11100000) >> 3); // g
+ span[pixel++] = (byte)((rgb565Be & 0b11111000_00000000) >> 8); // r
+ i += 2;
+ break;
+ case DATATYPE_RGB888:
+ span[pixel++] = bytes[i + 2]; // b
+ span[pixel++] = bytes[i + 1]; // g
+ span[pixel++] = bytes[i]; // r
+ i += 3;
+ break;
+ case DATATYPE_BGR555:
+ ushort bgr555 = BitExtensions.ToUShortLittleEndian(bytes, i);
+ span[pixel++] = (byte)((bgr555 & 0b01111100_00000000) >> 7); // b
+ span[pixel++] = (byte)((bgr555 & 0b00000011_11100000) >> 2); // g
+ span[pixel++] = (byte)((bgr555 & 0b00000000_00011111) << 3); // r
+ i += 2;
+ break;
+ case DATATYPE_BGR565:
+ ushort bgr565 = BitExtensions.ToUShortLittleEndian(bytes, i);
+ span[pixel++] = (byte)((bgr565 & 0b11111000_00000000) >> 8); // b
+ span[pixel++] = (byte)((bgr565 & 0b00000111_11100000) >> 3); // g
+ span[pixel++] = (byte)((bgr565 & 0b00000000_00011111) << 3); // r
+ i += 2;
+ break;
+ case DATATYPE_BGR555_BE:
+ ushort bgr555Be = BitExtensions.ToUShortBigEndian(bytes, i);
+ span[pixel++] = (byte)((bgr555Be & 0b01111100_00000000) >> 7); // b
+ span[pixel++] = (byte)((bgr555Be & 0b00000011_11100000) >> 2); // g
+ span[pixel++] = (byte)((bgr555Be & 0b00000000_00011111) << 3); // r
+ i += 2;
+ break;
+ case DATATYPE_BGR565_BE:
+ ushort bgr565Be = BitExtensions.ToUShortBigEndian(bytes, i);
+ span[pixel++] = (byte)((bgr565Be & 0b11111000_00000000) >> 8); // b
+ span[pixel++] = (byte)((bgr565Be & 0b00000111_11100000) >> 3); // g
+ span[pixel++] = (byte)((bgr565Be & 0b00000000_00011111) << 3); // r
+ i += 2;
+ break;
+ case DATATYPE_BGR888:
+ span[pixel++] = bytes[i]; // b
+ span[pixel++] = bytes[i + 1]; // g
+ span[pixel++] = bytes[i + 2]; // r
+ i += 3;
+ break;
+ }
+ }
+
+ for (; pixel < span.Length; pixel++) // Fill leftovers
+ {
+ span[pixel] = 0;
+ }
+
+ return mat;
+ }
+
+ throw new NotSupportedException($"The decode type: {dataType} is not supported.");
+ }
+
+ public static Mat DecodeImage(string dataType, byte[] bytes, uint resolutionX = 0, uint resolutionY = 0)
+ => DecodeImage(dataType, bytes, new Size((int)resolutionX, (int)resolutionY));
+
+ public static void MutateGetVarsIterationChamfer(uint startLayerIndex, uint endLayerIndex, int iterationsStart, int iterationsEnd, ref bool isFade, out float iterationSteps, out int maxIteration)
+ {
+ iterationSteps = 0;
+ maxIteration = 0;
+ isFade = isFade && startLayerIndex != endLayerIndex && iterationsStart != iterationsEnd;
+ if (!isFade) return;
+ iterationSteps = Math.Abs((iterationsStart - (float)iterationsEnd) / ((float)endLayerIndex - startLayerIndex));
+ maxIteration = Math.Max(iterationsStart, iterationsEnd);
+ }
+
+ public static int MutateGetIterationVar(bool isFade, int iterationsStart, int iterationsEnd, float iterationSteps, int maxIteration, uint startLayerIndex, uint layerIndex)
+ {
+ if (!isFade) return iterationsStart;
+ // calculate iterations based on range
+ int iterations = (int)(iterationsStart < iterationsEnd
+ ? iterationsStart + (layerIndex - startLayerIndex) * iterationSteps
+ : iterationsStart - (layerIndex - startLayerIndex) * iterationSteps);
+
+ // constrain
+ return Math.Min(Math.Max(0, iterations), maxIteration);
+ }
+
+ public static int MutateGetIterationChamfer(uint layerIndex, uint startLayerIndex, uint endLayerIndex, int iterationsStart,
+ int iterationsEnd, bool isFade)
+ {
+ MutateGetVarsIterationChamfer(startLayerIndex, endLayerIndex, iterationsStart, iterationsEnd, ref isFade,
+ out float iterationSteps, out int maxIteration);
+ return MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, layerIndex);
+ }
+ #endregion
- #region Members
- public object Mutex = new();
+ #region Members
+ public object Mutex = new();
- private bool _haveModifiedLayers;
+ private Layer[] _layers = Array.Empty<Layer>();
- private uint _version;
+ private bool _haveModifiedLayers;
+ private uint _version;
- private byte _antiAliasing = 1;
+ private byte _antiAliasing = 1;
- private ushort _bottomLayerCount = DefaultBottomLayerCount;
- private ushort _transitionLayerCount = DefaultTransitionLayerCount;
+ private ushort _bottomLayerCount = DefaultBottomLayerCount;
+ private ushort _transitionLayerCount = DefaultTransitionLayerCount;
- private float _bottomLightOffDelay;
- private float _lightOffDelay;
+ private float _bottomLightOffDelay;
+ private float _lightOffDelay;
- private float _bottomWaitTimeBeforeCure;
- private float _waitTimeBeforeCure;
+ private float _bottomWaitTimeBeforeCure;
+ private float _waitTimeBeforeCure;
- private float _bottomExposureTime = DefaultBottomExposureTime;
- private float _exposureTime = DefaultExposureTime;
+ private float _bottomExposureTime = DefaultBottomExposureTime;
+ private float _exposureTime = DefaultExposureTime;
- private float _bottomWaitTimeAfterCure;
- private float _waitTimeAfterCure;
+ private float _bottomWaitTimeAfterCure;
+ private float _waitTimeAfterCure;
- private float _bottomLiftHeight = DefaultBottomLiftHeight;
- private float _liftHeight = DefaultLiftHeight;
- private float _bottomLiftSpeed = DefaultBottomLiftSpeed;
- private float _liftSpeed = DefaultLiftSpeed;
+ private float _bottomLiftHeight = DefaultBottomLiftHeight;
+ private float _liftHeight = DefaultLiftHeight;
+ private float _bottomLiftSpeed = DefaultBottomLiftSpeed;
+ private float _liftSpeed = DefaultLiftSpeed;
- private float _bottomLiftHeight2 = DefaultBottomLiftHeight2;
- private float _liftHeight2 = DefaultLiftHeight2;
- private float _bottomLiftSpeed2 = DefaultBottomLiftSpeed2;
- private float _liftSpeed2 = DefaultLiftSpeed2;
+ private float _bottomLiftHeight2 = DefaultBottomLiftHeight2;
+ private float _liftHeight2 = DefaultLiftHeight2;
+ private float _bottomLiftSpeed2 = DefaultBottomLiftSpeed2;
+ private float _liftSpeed2 = DefaultLiftSpeed2;
- private float _bottomWaitTimeAfterLift;
- private float _waitTimeAfterLift;
+ private float _bottomWaitTimeAfterLift;
+ private float _waitTimeAfterLift;
- private float _bottomRetractHeight2 = DefaultBottomRetractHeight2;
- private float _retractHeight2 = DefaultRetractHeight2;
- private float _bottomRetractSpeed2 = DefaultBottomRetractSpeed2;
- private float _retractSpeed2 = DefaultRetractSpeed2;
- private float _bottomRetractSpeed = DefaultBottomRetractSpeed;
- private float _retractSpeed = DefaultRetractSpeed;
+ private float _bottomRetractHeight2 = DefaultBottomRetractHeight2;
+ private float _retractHeight2 = DefaultRetractHeight2;
+ private float _bottomRetractSpeed2 = DefaultBottomRetractSpeed2;
+ private float _retractSpeed2 = DefaultRetractSpeed2;
+ private float _bottomRetractSpeed = DefaultBottomRetractSpeed;
+ private float _retractSpeed = DefaultRetractSpeed;
- private byte _bottomLightPwm = DefaultBottomLightPWM;
- private byte _lightPwm = DefaultLightPWM;
+ private byte _bottomLightPwm = DefaultBottomLightPWM;
+ private byte _lightPwm = DefaultLightPWM;
- private float _printTime;
- private float _materialMilliliters;
- private float _machineZ;
- private string _machineName = "Unknown";
- private string _materialName;
- private float _materialGrams;
- private float _materialCost;
- private bool _suppressRebuildGCode;
+ private float _printTime;
+ private float _materialMilliliters;
+ private float _machineZ;
+ private string _machineName = "Unknown";
+ private string? _materialName;
+ private float _materialGrams;
+ private float _materialCost;
+ private bool _suppressRebuildGCode;
- private readonly Timer _queueTimerPrintTime = new(QueueTimerPrintTime){AutoReset = false};
+ private Rectangle _boundingRectangle = Rectangle.Empty;
- #endregion
+ private readonly Timer _queueTimerPrintTime = new(QueueTimerPrintTime){AutoReset = false};
- #region Properties
+ #endregion
- /// <summary>
- /// Gets the file format type
- /// </summary>
- public abstract FileFormatType FileType { get; }
+ #region Properties
- /// <summary>
- /// Gets the valid file extensions for this <see cref="FileFormat"/>
- /// </summary>
- public abstract FileExtension[] FileExtensions { get; }
+ /// <summary>
+ /// Gets the file format type
+ /// </summary>
+ public abstract FileFormatType FileType { get; }
- public FileDecodeType DecodeType { get; private set; } = FileDecodeType.Full;
+ /// <summary>
+ /// Gets the valid file extensions for this <see cref="FileFormat"/>
+ /// </summary>
+ public abstract FileExtension[] FileExtensions { get; }
- /// <summary>
- /// Gets the available <see cref="PrintParameterModifier"/>
- /// </summary>
- public virtual PrintParameterModifier[] PrintParameterModifiers => null;
+ public FileDecodeType DecodeType { get; private set; } = FileDecodeType.Full;
- /// <summary>
- /// Gets the available <see cref="PrintParameterModifier"/> per layer
- /// </summary>
- public virtual PrintParameterModifier[] PrintParameterPerLayerModifiers => null;
+ /// <summary>
+ /// Gets the available <see cref="PrintParameterModifier"/>
+ /// </summary>
+ public virtual PrintParameterModifier[]? PrintParameterModifiers => null;
- /// <summary>
- /// Checks if a <see cref="PrintParameterModifier"/> exists on print parameters
- /// </summary>
- /// <param name="modifier"></param>
- /// <returns>True if exists, otherwise false</returns>
- public bool HavePrintParameterModifier(PrintParameterModifier modifier) =>
- PrintParameterModifiers is not null && PrintParameterModifiers.Contains(modifier);
+ /// <summary>
+ /// Gets the available <see cref="PrintParameterModifier"/> per layer
+ /// </summary>
+ public virtual PrintParameterModifier[]? PrintParameterPerLayerModifiers => null;
- /// <summary>
- /// Checks if a <see cref="PrintParameterModifier"/> exists on layer parameters
- /// </summary>
- /// <param name="modifier"></param>
- /// <returns>True if exists, otherwise false</returns>
- public bool HaveLayerParameterModifier(PrintParameterModifier modifier) =>
- SupportPerLayerSettings && PrintParameterPerLayerModifiers.Contains(modifier);
+ /// <summary>
+ /// Checks if a <see cref="PrintParameterModifier"/> exists on print parameters
+ /// </summary>
+ /// <param name="modifier"></param>
+ /// <returns>True if exists, otherwise false</returns>
+ public bool HavePrintParameterModifier(PrintParameterModifier modifier) =>
+ PrintParameterModifiers is not null && PrintParameterModifiers.Contains(modifier);
- /// <summary>
- /// Gets the file filter for open and save dialogs
- /// </summary>
- public string FileFilter {
- get
- {
- var result = string.Empty;
+ /// <summary>
+ /// Checks if a <see cref="PrintParameterModifier"/> exists on layer parameters
+ /// </summary>
+ /// <param name="modifier"></param>
+ /// <returns>True if exists, otherwise false</returns>
+ public bool HaveLayerParameterModifier(PrintParameterModifier modifier) =>
+ SupportPerLayerSettings && PrintParameterPerLayerModifiers!.Contains(modifier);
+
+ /// <summary>
+ /// Gets the file filter for open and save dialogs
+ /// </summary>
+ public string FileFilter {
+ get
+ {
+ var result = string.Empty;
- foreach (var fileExt in FileExtensions)
+ foreach (var fileExt in FileExtensions)
+ {
+ if (!ReferenceEquals(result, string.Empty))
{
- if (!ReferenceEquals(result, string.Empty))
- {
- result += '|';
- }
- result += fileExt.Filter;
+ result += '|';
}
-
- return result;
+ result += fileExt.Filter;
}
+
+ return result;
}
+ }
- /// <summary>
- /// Gets all valid file extensions for Avalonia file dialog
- /// </summary>
- public List<KeyValuePair<string, List<string>>> FileFilterAvalonia
- => FileExtensions.Select(fileExt => new KeyValuePair<string, List<string>>(fileExt.Description, new List<string> {fileExt.Extension})).ToList();
+ /// <summary>
+ /// Gets all valid file extensions for Avalonia file dialog
+ /// </summary>
+ public List<KeyValuePair<string, List<string>>> FileFilterAvalonia
+ => FileExtensions.Select(fileExt => new KeyValuePair<string, List<string>>(fileExt.Description, new List<string> {fileExt.Extension})).ToList();
- /// <summary>
- /// Gets all valid file extensions in "*.extension1;*.extension2" format
- /// </summary>
- public string FileFilterExtensionsOnly
+ /// <summary>
+ /// Gets all valid file extensions in "*.extension1;*.extension2" format
+ /// </summary>
+ public string FileFilterExtensionsOnly
+ {
+ get
{
- get
- {
- var result = string.Empty;
+ var result = string.Empty;
- foreach (var fileExt in FileExtensions)
+ foreach (var fileExt in FileExtensions)
+ {
+ if (!ReferenceEquals(result, string.Empty))
{
- if (!ReferenceEquals(result, string.Empty))
- {
- result += "; ";
- }
- result += $"*.{fileExt.Extension}";
+ result += "; ";
}
-
- return result;
+ result += $"*.{fileExt.Extension}";
}
+
+ return result;
}
+ }
- /// <summary>
- /// Gets or sets if change a global property should rebuild every layer data based on them
- /// </summary>
- public bool SuppressRebuildProperties { get; set; }
+ /// <summary>
+ /// Gets or sets if change a global property should rebuild every layer data based on them
+ /// </summary>
+ public bool SuppressRebuildProperties { get; set; }
- /// <summary>
- /// Gets the input file path loaded into this <see cref="FileFormat"/>
- /// </summary>
- public string FileFullPath { get; set; }
+ /// <summary>
+ /// Gets the input file path loaded into this <see cref="FileFormat"/>
+ /// </summary>
+ public string? FileFullPath { get; set; }
- public string DirectoryPath => Path.GetDirectoryName(FileFullPath);
- public string Filename => Path.GetFileName(FileFullPath);
- public string FileExtension => Path.GetExtension(FileFullPath);
- public string FilenameNoExt => GetFileNameStripExtensions(FileFullPath);
+ public string? DirectoryPath => Path.GetDirectoryName(FileFullPath);
+ public string? Filename => Path.GetFileName(FileFullPath);
+ public string? FileExtension => Path.GetExtension(FileFullPath);
+ public string? FilenameNoExt => GetFileNameStripExtensions(FileFullPath);
- /// <summary>
- /// Gets the available versions to set in this file format
- /// </summary>
- public virtual uint[] AvailableVersions { get; }
+ /// <summary>
+ /// Gets the available versions to set in this file format
+ /// </summary>
+ public virtual uint[]? AvailableVersions => null;
- /// <summary>
- /// Gets the amount of available versions in this file format
- /// </summary>
- public virtual byte AvailableVersionsCount => (byte)(AvailableVersions?.Length ?? 0);
+ /// <summary>
+ /// Gets the amount of available versions in this file format
+ /// </summary>
+ public virtual byte AvailableVersionsCount => (byte)(AvailableVersions?.Length ?? 0);
- /// <summary>
- /// Gets the default version to use in this file when not setting the version
- /// </summary>
- public virtual uint DefaultVersion => 0;
+ /// <summary>
+ /// Gets the default version to use in this file when not setting the version
+ /// </summary>
+ public virtual uint DefaultVersion => 0;
- /// <summary>
- /// Gets or sets the version of this file format
- /// </summary>
- public virtual uint Version
+ /// <summary>
+ /// Gets or sets the version of this file format
+ /// </summary>
+ public virtual uint Version
+ {
+ get => _version;
+ set
{
- get => _version;
- set
+ if (AvailableVersions is not null && !AvailableVersions.Contains(value))
{
- if (AvailableVersions is not null && !AvailableVersions.Contains(value))
- {
- throw new VersionNotFoundException($"Version {value} not known for this file format");
- }
-
- RequireFullEncode = true;
- RaiseAndSetIfChanged(ref _version, value);
+ throw new VersionNotFoundException($"Version {value} not known for this file format");
}
- }
- /// <summary>
- /// Gets the thumbnails count present in this file format
- /// </summary>
- public byte ThumbnailsCount => (byte)(ThumbnailsOriginalSize?.Length ?? 0);
+ RequireFullEncode = true;
+ RaiseAndSetIfChanged(ref _version, value);
+ }
+ }
- /// <summary>
- /// Gets the number of created thumbnails
- /// </summary>
- public byte CreatedThumbnailsCount {
- get
- {
- if (Thumbnails is null) return 0;
- byte count = 0;
+ /// <summary>
+ /// Gets the thumbnails count present in this file format
+ /// </summary>
+ public byte ThumbnailsCount => (byte)(ThumbnailsOriginalSize?.Length ?? 0);
- foreach (var thumbnail in Thumbnails)
- {
- if (thumbnail is null || thumbnail.IsEmpty) continue;
- count++;
- }
+ /// <summary>
+ /// Gets the number of created thumbnails
+ /// </summary>
+ public byte CreatedThumbnailsCount {
+ get
+ {
+ if (Thumbnails is null) return 0;
+ byte count = 0;
- return count;
+ foreach (var thumbnail in Thumbnails)
+ {
+ if (thumbnail is null || thumbnail.IsEmpty) continue;
+ count++;
}
+
+ return count;
}
+ }
- /// <summary>
- /// Gets the original thumbnail sizes
- /// </summary>
- public virtual Size[] ThumbnailsOriginalSize => null;
+ /// <summary>
+ /// Gets the original thumbnail sizes
+ /// </summary>
+ public virtual Size[]? ThumbnailsOriginalSize => null;
- /// <summary>
- /// Gets the thumbnails for this <see cref="FileFormat"/>
- /// </summary>
- public Mat[] Thumbnails { get; set; }
+ /// <summary>
+ /// Gets the thumbnails for this <see cref="FileFormat"/>
+ /// </summary>
+ public Mat?[] Thumbnails { get; init; }
- /// <summary>
- /// Gets the cached layers into compressed bytes
- /// </summary>
- public LayerManager LayerManager
+ public IssueManager IssueManager { get; }
+
+ /// <summary>
+ /// Layers List
+ /// </summary>
+ public Layer[] Layers
+ {
+ get => _layers;
+ set
{
- get;
- /*set
+ if (value is null)
{
- var oldLayerManager = _layerManager;
- if (!RaiseAndSetIfChanged(ref _layerManager, value) || value is null) return;
+ throw new ArgumentNullException(nameof(value));
+ }
- if(!ReferenceEquals(this, _layerManager.SlicerFile)) // Auto fix parent slicer file
- {
- _layerManager.SlicerFile = this;
- }
+ //if (ReferenceEquals(_layers, value)) return;
- // Recalculate changes
- PrintHeight = PrintHeight;
- PrintTime = PrintTimeComputed;
- MaterialMilliliters = -1;
+ var rebuildProperties = false;
+ var oldLayerCount = LayerCount;
+ var oldLayers = _layers;
+ _layers = value;
+ BoundingRectangle = Rectangle.Empty;
- if (oldLayerManager is null) return; // Init
+ if (LayerCount != oldLayerCount)
+ {
+ LayerCount = LayerCount;
+ }
- if (oldLayerManager.LayerCount != LayerCount)
+ RequireFullEncode = true;
+ PrintHeight = PrintHeight;
+ UpdatePrintTime();
+
+ if (LayerCount > 0)
+ {
+ //SetAllIsModified(true);
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) // Forced sanitize
{
- LayerCount = _layerManager.LayerCount;
- if (SuppressRebuildProperties) return;
- if (LayerCount == 0 || this[LastLayerIndex] is null) return; // Not initialized
- LayerManager.RebuildLayersProperties();
+ if (_layers[layerIndex] is null) continue;
+ _layers[layerIndex].Index = layerIndex;
+ _layers[layerIndex].SlicerFile = this;
+
+ if (layerIndex >= oldLayerCount || layerIndex < oldLayerCount && !_layers[layerIndex].Equals(oldLayers[layerIndex]))
+ {
+ // Marks as modified only if layer image changed on this index
+ _layers[layerIndex].IsModified = true;
+ }
}
- }*/
+
+ if (LayerCount != oldLayerCount && !SuppressRebuildProperties && LastLayer is not null)
+ {
+ RebuildLayersProperties();
+ rebuildProperties = true;
+ }
+ }
+
+ if (!rebuildProperties)
+ {
+ MaterialMilliliters = -1;
+ RebuildGCode();
+ }
+
+ RaisePropertyChanged();
}
+ }
- public IssueManager IssueManager { get; }
+ /// <summary>
+ /// First layer index, this is always 0
+ /// </summary>
+ public const uint FirstLayerIndex = 0;
- /// <summary>
- /// Gets the first layer
- /// </summary>
- public Layer FirstLayer => LayerManager.FirstLayer;
+ /// <summary>
+ /// Gets the last layer index
+ /// </summary>
+ public uint LastLayerIndex => LayerCount - 1;
- /// <summary>
- /// Gets the last bottom layer
- /// </summary>
- public Layer LastBottomLayer => LayerManager.LastOrDefault(layer => layer.IsBottomLayer);
+ /// <summary>
+ /// Gets the first layer
+ /// </summary>
+ public Layer? FirstLayer => LayerCount > 0 ? this[0] : null;
- /// <summary>
- /// Gets the first normal layer
- /// </summary>
- public Layer FirstNormalLayer => LayerManager.FirstOrDefault(layer => layer.IsNormalLayer);
+ /// <summary>
+ /// Gets the last bottom layer
+ /// </summary>
+ public Layer? LastBottomLayer => this.LastOrDefault(layer => layer.IsBottomLayer);
- /// <summary>
- /// Gets the last layer
- /// </summary>
- public Layer LastLayer => LayerManager.LastLayer;
+ /// <summary>
+ /// Gets the first normal layer
+ /// </summary>
+ public Layer? FirstNormalLayer => this.FirstOrDefault(layer => layer.IsNormalLayer);
- /// <summary>
- /// Gets all bottom layers
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> BottomLayers => LayerManager.BottomLayers;
+ /// <summary>
+ /// Gets the last layer
+ /// </summary>
+ public Layer? LastLayer => LayerCount > 0 ? this[^1] : null;
- /// <summary>
- /// Gets all normal layers
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> NormalLayers => LayerManager.NormalLayers;
+ /// <summary>
+ /// Gets the smallest bottom layer using the pixel count
+ /// </summary>
+ public Layer? SmallestBottomLayer => this.Where(layer => layer.IsBottomLayer && !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount);
- /// <summary>
- /// Gets all transition layers
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> TransitionLayers => LayerManager.TransitionLayers;
+ /// <summary>
+ /// Gets the largest bottom layer using the pixel count
+ /// </summary>
+ public Layer? LargestBottomLayer => this.Where(layer => layer.IsBottomLayer && !layer.IsEmpty).MaxBy(layer => layer.NonZeroPixelCount);
- /// <summary>
- /// Gets all layers that use TSMC values
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> TsmcLayers => LayerManager.TsmcLayers;
+ /// <summary>
+ /// Gets the smallest normal layer using the pixel count
+ /// </summary>
+ public Layer? SmallestNormalLayer => this.Where(layer => layer.IsNormalLayer && !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount);
- /// <summary>
- /// Gets the bounding rectangle of the object
- /// </summary>
- public Rectangle BoundingRectangle => LayerManager.BoundingRectangle;
+ /// <summary>
+ /// Gets the largest layer using the pixel count
+ /// </summary>
+ public Layer? LargestNormalLayer => this.Where(layer => layer.IsNormalLayer && !layer.IsEmpty).MaxBy(layer => layer.NonZeroPixelCount);
- /// <summary>
- /// Gets the bounding rectangle of the object in millimeters
- /// </summary>
- public RectangleF BoundingRectangleMillimeters => LayerManager.BoundingRectangleMillimeters;
+ /// <summary>
+ /// Gets the smallest normal layer using the pixel count
+ /// </summary>
+ public Layer? SmallestLayer => this.Where(layer => !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount);
- /// <summary>
- /// Gets or sets if modifications require a full encode to save
- /// </summary>
- public bool RequireFullEncode
- {
- get => _haveModifiedLayers || LayerManager.IsModified;
- set => RaiseAndSetIfChanged(ref _haveModifiedLayers, value);
- } // => LayerManager.IsModified;
+ /// <summary>
+ /// Gets the largest layer using the pixel count
+ /// </summary>
+ public Layer? LargestLayer => this.MaxBy(layer => layer.NonZeroPixelCount);
- /// <summary>
- /// Gets the image width resolution
- /// </summary>
- public Size Resolution
- {
- get => new((int)ResolutionX, (int)ResolutionY);
- set
- {
- ResolutionX = (uint) value.Width;
- ResolutionY = (uint) value.Height;
- RaisePropertyChanged();
- RaisePropertyChanged(nameof(Xppmm));
- RaisePropertyChanged(nameof(Yppmm));
- RaisePropertyChanged(nameof(Ppmm));
- }
- }
+ public Layer? GetSmallestLayerBetween(uint layerStartIndex, uint layerEndIndex)
+ {
+ return this.Where((layer, index) => !layer.IsEmpty && index >= layerStartIndex && index <= layerEndIndex).MinBy(layer => layer.NonZeroPixelCount);
+ }
- /// <summary>
- /// Gets the image width resolution
- /// </summary>
- public abstract uint ResolutionX { get; set; }
+ public Layer? GetLargestLayerBetween(uint layerStartIndex, uint layerEndIndex)
+ {
+ return this.Where((layer, index) => !layer.IsEmpty && index >= layerStartIndex && index <= layerEndIndex).MaxBy(layer => layer.NonZeroPixelCount);
+ }
- /// <summary>
- /// Gets the image height resolution
- /// </summary>
- public abstract uint ResolutionY { get; set; }
+ /// <summary>
+ /// Gets all bottom layers
+ /// </summary>
+ /// <returns></returns>
+ public IEnumerable<Layer> BottomLayers => this.Where(layer => layer.IsBottomLayer);
- /// <summary>
- /// Gets the size of display in millimeters
- /// </summary>
- public SizeF Display
+ /// <summary>
+ /// Gets all normal layers
+ /// </summary>
+ /// <returns></returns>
+ public IEnumerable<Layer> NormalLayers => this.Where(layer => layer.IsNormalLayer);
+
+ /// <summary>
+ /// Gets all transition layers
+ /// </summary>
+ /// <returns></returns>
+ public IEnumerable<Layer> TransitionLayers => this.Where(layer => layer.IsTransitionLayer);
+
+ /// <summary>
+ /// Gets all layers that use TSMC values
+ /// </summary>
+ /// <returns></returns>
+ public IEnumerable<Layer> TsmcLayers => this.Where(layer => layer.IsUsingTSMC);
+
+ /// <summary>
+ /// Gets all layers on same position but exclude the first layer on that position
+ /// </summary>
+ public IEnumerable<Layer> SamePositionedLayers
+ {
+ get
{
- get => new(DisplayWidth, DisplayHeight);
- set
+ for (int layerIndex = 1; layerIndex < LayerCount; layerIndex++)
{
- DisplayWidth = value.Width;
- DisplayHeight = value.Height;
- RaisePropertyChanged();
+ var layer = this[layerIndex];
+ if (this[layerIndex - 1].PositionZ != layer.PositionZ) continue;
+ yield return layer;
}
}
+ }
- /// <summary>
- /// Gets or sets the display width in millimeters
- /// </summary>
- public virtual float DisplayWidth { get; set; }
+ public IEnumerable<Layer> GetDistinctLayersByPositionZ(uint layerIndexStart = 0) =>
+ GetDistinctLayersByPositionZ(layerIndexStart, LastLayerIndex);
- /// <summary>
- /// Gets or sets the display height in millimeters
- /// </summary>
- public virtual float DisplayHeight { get; set; }
+ public IEnumerable<Layer> GetDistinctLayersByPositionZ(uint layerIndexStart, uint layerIndexEnd)
+ {
+ return layerIndexEnd - layerIndexStart >= LastLayerIndex
+ ? this.DistinctBy(layer => layer.PositionZ)
+ : this.Where((_, layerIndex) => layerIndex >= layerIndexStart && layerIndex <= layerIndexEnd).DistinctBy(layer => layer.PositionZ);
+ }
- /// <summary>
- /// Gets the display diagonal in millimeters
- /// </summary>
- public float DisplayDiagonal => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)), 2);
+ public IEnumerable<Layer> GetLayersFromHeightRange(float startPositionZ, float endPositionZ)
+ {
+ return this.Where(layer => layer.PositionZ >= startPositionZ && layer.PositionZ <= endPositionZ);
+ }
- /// <summary>
- /// Gets the display diagonal in inch's
- /// </summary>
- public float DisplayDiagonalInches => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)) * UnitExtensions.MillimeterInInch, 2);
+ public IEnumerable<Layer> GetLayersFromHeightRange(float endPositionZ)
+ {
+ return this.Where(layer => layer.PositionZ <= endPositionZ);
+ }
- /// <summary>
- /// Gets the display ratio
- /// </summary>
- public Size DisplayAspectRatio
+ /// <summary>
+ /// True if all layers are using same value parameters as global settings, otherwise false
+ /// </summary>
+ public bool AllLayersAreUsingGlobalParameters => this.All(layer => layer.IsUsingGlobalParameters);
+
+ /// <summary>
+ /// True if any layer is using TSMC, otherwise false when none of layers is using TSMC
+ /// </summary>
+ public bool AnyLayerIsUsingTSMC => this.Any(layer => layer.IsUsingTSMC);
+
+
+ /// <summary>
+ /// Gets if any layer got modified, otherwise false
+ /// Sets all layers `IsModified` flag
+ /// </summary>
+ public bool IsModified
+ {
+ get
{
- get
+ for (uint i = 0; i < LayerCount; i++)
{
- var gcd = MathExtensions.GCD(ResolutionX, ResolutionY);
- return new((int)(ResolutionX / gcd), (int)(ResolutionY / gcd));
+ if (this[i].IsModified) return true;
}
+ return false;
}
-
- public string DisplayAspectRatioStr
+ set
{
- get
+ for (uint i = 0; i < LayerCount; i++)
{
- var aspect = DisplayAspectRatio;
- return $"{aspect.Width}:{aspect.Height}";
+ if (this[i] is null) continue;
+ this[i].IsModified = value;
}
}
+ }
- /// <summary>
- /// Gets or sets if images need to be mirrored on lcd to print on the correct orientation
- /// </summary>
- public virtual Enumerations.FlipDirection DisplayMirror { get; set; } = Enumerations.FlipDirection.None;
+ /// <summary>
+ /// Gets the bounding rectangle of the object
+ /// </summary>
+ public Rectangle BoundingRectangle
+ {
+ get => GetBoundingRectangle();
+ set
+ {
+ RaiseAndSetIfChanged(ref _boundingRectangle, value);
+ RaisePropertyChanged(nameof(BoundingRectangleMillimeters));
+ }
+ }
- /// <summary>
- /// Gets if the display is in portrait mode
- /// </summary>
- public bool IsDisplayPortrait => ResolutionY > ResolutionX;
+ /// <summary>
+ /// Gets the bounding rectangle of the object in millimeters
+ /// </summary>
+ public RectangleF BoundingRectangleMillimeters
+ {
+ get
+ {
+ var pixelSize = PixelSize;
+ return new RectangleF(
+ (float)Math.Round(_boundingRectangle.X * pixelSize.Width, 2),
+ (float)Math.Round(_boundingRectangle.Y * pixelSize.Height, 2),
+ (float)Math.Round(_boundingRectangle.Width * pixelSize.Width, 2),
+ (float)Math.Round(_boundingRectangle.Height * pixelSize.Height, 2));
+ }
+ }
- /// <summary>
- /// Gets if the display is in landscape mode
- /// </summary>
- public bool IsDisplayLandscape => !IsDisplayPortrait;
+ /// <summary>
+ /// Gets or sets if modifications require a full encode to save
+ /// </summary>
+ public bool RequireFullEncode
+ {
+ get => _haveModifiedLayers || IsModified;
+ set => RaiseAndSetIfChanged(ref _haveModifiedLayers, value);
+ }
- /// <summary>
- /// Gets or sets the maximum printer build Z volume
- /// </summary>
- public virtual float MachineZ
+ /// <summary>
+ /// Gets the image width resolution
+ /// </summary>
+ public Size Resolution
+ {
+ get => new((int)ResolutionX, (int)ResolutionY);
+ set
{
- get => _machineZ > 0 ? _machineZ : PrintHeight;
- set => RaiseAndSetIfChanged(ref _machineZ, value);
+ ResolutionX = (uint) value.Width;
+ ResolutionY = (uint) value.Height;
+ RaisePropertyChanged();
+ RaisePropertyChanged(nameof(Xppmm));
+ RaisePropertyChanged(nameof(Yppmm));
+ RaisePropertyChanged(nameof(Ppmm));
}
+ }
- /// <summary>
- /// Gets or sets the pixels per mm on X direction
- /// </summary>
- public virtual float Xppmm
+ /// <summary>
+ /// Gets the image width resolution
+ /// </summary>
+ public abstract uint ResolutionX { get; set; }
+
+ /// <summary>
+ /// Gets the image height resolution
+ /// </summary>
+ public abstract uint ResolutionY { get; set; }
+
+ /// <summary>
+ /// Gets the size of display in millimeters
+ /// </summary>
+ public SizeF Display
+ {
+ get => new(DisplayWidth, DisplayHeight);
+ set
{
- get => DisplayWidth > 0 ? ResolutionX / DisplayWidth : 0;
- set
- {
- RaisePropertyChanged(nameof(Xppmm));
- RaisePropertyChanged(nameof(Ppmm));
- RaisePropertyChanged(nameof(PpmmMax));
- }
+ DisplayWidth = value.Width;
+ DisplayHeight = value.Height;
+ RaisePropertyChanged();
}
+ }
- /// <summary>
- /// Gets or sets the pixels per mm on Y direction
- /// </summary>
- public virtual float Yppmm
+ /// <summary>
+ /// Gets or sets the display width in millimeters
+ /// </summary>
+ public virtual float DisplayWidth { get; set; }
+
+ /// <summary>
+ /// Gets or sets the display height in millimeters
+ /// </summary>
+ public virtual float DisplayHeight { get; set; }
+
+ /// <summary>
+ /// Gets the display diagonal in millimeters
+ /// </summary>
+ public float DisplayDiagonal => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)), 2);
+
+ /// <summary>
+ /// Gets the display diagonal in inch's
+ /// </summary>
+ public float DisplayDiagonalInches => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)) * UnitExtensions.MillimeterInInch, 2);
+
+ /// <summary>
+ /// Gets the display ratio
+ /// </summary>
+ public Size DisplayAspectRatio
+ {
+ get
{
- get => DisplayHeight > 0 ? ResolutionY / DisplayHeight : 0;
- set
- {
- RaisePropertyChanged(nameof(Yppmm));
- RaisePropertyChanged(nameof(Ppmm));
- RaisePropertyChanged(nameof(PpmmMax));
- }
+ var gcd = MathExtensions.GCD(ResolutionX, ResolutionY);
+ return new((int)(ResolutionX / gcd), (int)(ResolutionY / gcd));
}
+ }
- /// <summary>
- /// Gets or sets the pixels per mm
- /// </summary>
- public SizeF Ppmm
+ public string DisplayAspectRatioStr
+ {
+ get
{
- get => new(Xppmm, Yppmm);
- set
- {
- Xppmm = value.Width;
- Yppmm = value.Height;
- }
+ var aspect = DisplayAspectRatio;
+ return $"{aspect.Width}:{aspect.Height}";
}
+ }
- /// <summary>
- /// Gets the maximum (Width or Height) pixels per mm
- /// </summary>
- public float PpmmMax => Ppmm.Max();
+ /// <summary>
+ /// Gets or sets if images need to be mirrored on lcd to print on the correct orientation
+ /// </summary>
+ public virtual Enumerations.FlipDirection DisplayMirror { get; set; } = Enumerations.FlipDirection.None;
- /// <summary>
- /// Gets the pixel width in millimeters
- /// </summary>
- public float PixelWidth => DisplayWidth > 0 && ResolutionX > 0 ? (float) Math.Round(DisplayWidth / ResolutionX, 3) : 0;
+ /// <summary>
+ /// Gets if the display is in portrait mode
+ /// </summary>
+ public bool IsDisplayPortrait => ResolutionY > ResolutionX;
- /// <summary>
- /// Gets the pixel height in millimeters
- /// </summary>
- public float PixelHeight => DisplayHeight > 0 && ResolutionY > 0 ? (float) Math.Round(DisplayHeight / ResolutionY, 3) : 0;
+ /// <summary>
+ /// Gets if the display is in landscape mode
+ /// </summary>
+ public bool IsDisplayLandscape => !IsDisplayPortrait;
- /// <summary>
- /// Gets the pixel size in millimeters
- /// </summary>
- public SizeF PixelSize => new(PixelWidth, PixelHeight);
+ /// <summary>
+ /// Gets or sets the maximum printer build Z volume
+ /// </summary>
+ public virtual float MachineZ
+ {
+ get => _machineZ > 0 ? _machineZ : PrintHeight;
+ set => RaiseAndSetIfChanged(ref _machineZ, value);
+ }
- /// <summary>
- /// Gets the maximum pixel between width and height in millimeters
- /// </summary>
- public float PixelSizeMax => PixelSize.Max();
+ /// <summary>
+ /// Gets or sets the pixels per mm on X direction
+ /// </summary>
+ public virtual float Xppmm
+ {
+ get => DisplayWidth > 0 ? ResolutionX / DisplayWidth : 0;
+ set
+ {
+ RaisePropertyChanged(nameof(Xppmm));
+ RaisePropertyChanged(nameof(Ppmm));
+ RaisePropertyChanged(nameof(PpmmMax));
+ }
+ }
- /// <summary>
- /// Gets the pixel area in millimeters
- /// </summary>
- public float PixelArea => PixelSize.Area();
+ /// <summary>
+ /// Gets or sets the pixels per mm on Y direction
+ /// </summary>
+ public virtual float Yppmm
+ {
+ get => DisplayHeight > 0 ? ResolutionY / DisplayHeight : 0;
+ set
+ {
+ RaisePropertyChanged(nameof(Yppmm));
+ RaisePropertyChanged(nameof(Ppmm));
+ RaisePropertyChanged(nameof(PpmmMax));
+ }
+ }
- /// <summary>
- /// Gets the pixel width in microns
- /// </summary>
- public float PixelWidthMicrons => DisplayWidth > 0 ? (float)Math.Round(DisplayWidth / ResolutionX * 1000, 3) : 0;
+ /// <summary>
+ /// Gets or sets the pixels per mm
+ /// </summary>
+ public SizeF Ppmm
+ {
+ get => new(Xppmm, Yppmm);
+ set
+ {
+ Xppmm = value.Width;
+ Yppmm = value.Height;
+ }
+ }
- /// <summary>
- /// Gets the pixel height in microns
- /// </summary>
- public float PixelHeightMicrons => DisplayHeight > 0 ? (float)Math.Round(DisplayHeight / ResolutionY * 1000, 3) : 0;
+ /// <summary>
+ /// Gets the maximum (Width or Height) pixels per mm
+ /// </summary>
+ public float PpmmMax => Ppmm.Max();
- /// <summary>
- /// Gets the pixel size in microns
- /// </summary>
- public SizeF PixelSizeMicrons => new(PixelWidthMicrons, PixelHeightMicrons);
+ /// <summary>
+ /// Gets the pixel width in millimeters
+ /// </summary>
+ public float PixelWidth => DisplayWidth > 0 && ResolutionX > 0 ? (float) Math.Round(DisplayWidth / ResolutionX, 3) : 0;
- /// <summary>
- /// Gets the maximum pixel between width and height in microns
- /// </summary>
- public float PixelSizeMicronsMax => PixelSizeMicrons.Max();
+ /// <summary>
+ /// Gets the pixel height in millimeters
+ /// </summary>
+ public float PixelHeight => DisplayHeight > 0 && ResolutionY > 0 ? (float) Math.Round(DisplayHeight / ResolutionY, 3) : 0;
- /// <summary>
- /// Gets the pixel area in millimeters
- /// </summary>
- public float PixelAreaMicrons => PixelSizeMicrons.Area();
+ /// <summary>
+ /// Gets the pixel size in millimeters
+ /// </summary>
+ public SizeF PixelSize => new(PixelWidth, PixelHeight);
- /// <summary>
- /// Checks if this file have AntiAliasing
- /// </summary>
- public bool HaveAntiAliasing => AntiAliasing > 1;
+ /// <summary>
+ /// Gets the maximum pixel between width and height in millimeters
+ /// </summary>
+ public float PixelSizeMax => PixelSize.Max();
- /// <summary>
- /// Gets if the AntiAliasing is emulated/fake with fractions of the time or if is real grey levels
- /// </summary>
- public virtual bool IsAntiAliasingEmulated => false;
+ /// <summary>
+ /// Gets the pixel area in millimeters
+ /// </summary>
+ public float PixelArea => PixelSize.Area();
- /// <summary>
- /// Gets or sets the AntiAliasing level
- /// </summary>
- public virtual byte AntiAliasing
- {
- get => _antiAliasing;
- set => RaiseAndSet(ref _antiAliasing, value);
- }
+ /// <summary>
+ /// Gets the pixel width in microns
+ /// </summary>
+ public float PixelWidthMicrons => DisplayWidth > 0 ? (float)Math.Round(DisplayWidth / ResolutionX * 1000, 3) : 0;
- /// <summary>
- /// Gets Layer Height in mm
- /// </summary>
- public abstract float LayerHeight { get; set; }
+ /// <summary>
+ /// Gets the pixel height in microns
+ /// </summary>
+ public float PixelHeightMicrons => DisplayHeight > 0 ? (float)Math.Round(DisplayHeight / ResolutionY * 1000, 3) : 0;
- /// <summary>
- /// Gets Layer Height in um
- /// </summary>
- public ushort LayerHeightUm => (ushort) (LayerHeight * 1000);
+ /// <summary>
+ /// Gets the pixel size in microns
+ /// </summary>
+ public SizeF PixelSizeMicrons => new(PixelWidthMicrons, PixelHeightMicrons);
+ /// <summary>
+ /// Gets the maximum pixel between width and height in microns
+ /// </summary>
+ public float PixelSizeMicronsMax => PixelSizeMicrons.Max();
- /// <summary>
- /// Gets or sets the print height in mm
- /// </summary>
- public virtual float PrintHeight
- {
- get => LayerCount == 0 ? 0 : LastLayer?.PositionZ ?? 0;
- set => RaisePropertyChanged();
- }
+ /// <summary>
+ /// Gets the pixel area in millimeters
+ /// </summary>
+ public float PixelAreaMicrons => PixelSizeMicrons.Area();
- /// <summary>
- /// First layer index, this is always 0
- /// </summary>
- public const uint FirstLayerIndex = 0;
+ /// <summary>
+ /// Gets the file volume (XYZ) in mm^3
+ /// </summary>
+ public float Volume => (float)Math.Round(this.Sum(layer => layer.GetVolume()), 3);
- /// <summary>
- /// Gets the last layer index
- /// </summary>
- public uint LastLayerIndex => LayerManager.LastLayerIndex;
+ /// <summary>
+ /// Checks if this file have AntiAliasing
+ /// </summary>
+ public bool HaveAntiAliasing => AntiAliasing > 1;
- /// <summary>
- /// Checks if this file format supports per layer settings
- /// </summary>
- public bool SupportPerLayerSettings => PrintParameterPerLayerModifiers is not null && PrintParameterPerLayerModifiers.Length > 0;
+ /// <summary>
+ /// Gets if the AntiAliasing is emulated/fake with fractions of the time or if is real grey levels
+ /// </summary>
+ public virtual bool IsAntiAliasingEmulated => false;
- /// <summary>
- /// Gets or sets the layer count
- /// </summary>
- public virtual uint LayerCount
- {
- get => LayerManager.LayerCount;
- set {
- RaisePropertyChanged();
- RaisePropertyChanged(nameof(NormalLayerCount));
- }
+ /// <summary>
+ /// Gets or sets the AntiAliasing level
+ /// </summary>
+ public virtual byte AntiAliasing
+ {
+ get => _antiAliasing;
+ set => RaiseAndSet(ref _antiAliasing, value);
+ }
+
+ /// <summary>
+ /// Gets Layer Height in mm
+ /// </summary>
+ public abstract float LayerHeight { get; set; }
+
+ /// <summary>
+ /// Gets Layer Height in um
+ /// </summary>
+ public ushort LayerHeightUm => (ushort) (LayerHeight * 1000);
+
+
+ /// <summary>
+ /// Gets or sets the print height in mm
+ /// </summary>
+ public virtual float PrintHeight
+ {
+ get => LayerCount == 0 ? 0 : LastLayer?.PositionZ ?? 0;
+ set => RaisePropertyChanged();
+ }
+
+ /// <summary>
+ /// Checks if this file format supports per layer settings
+ /// </summary>
+ public bool SupportPerLayerSettings => PrintParameterPerLayerModifiers is not null && PrintParameterPerLayerModifiers.Length > 0;
+
+ public bool IsReadOnly => false;
+
+ public int Count => _layers?.Length ?? 0;
+
+ /// <summary>
+ /// Gets or sets the layer count
+ /// </summary>
+ public virtual uint LayerCount
+ {
+ get => (uint)Count;
+ set {
+ RaisePropertyChanged();
+ RaisePropertyChanged(nameof(NormalLayerCount));
}
+ }
- /// <summary>
- /// Gets or sets the total height for the bottom layers in millimeters
- /// </summary>
- public float BottomLayersHeight
- {
- get => LastBottomLayer?.PositionZ ?? 0;
- set => BottomLayerCount = (ushort)Math.Ceiling(value / LayerHeight);
- }
+ public byte LayerDigits => (byte)LayerCount.ToString().Length;
- #region Universal Properties
+ /// <summary>
+ /// Gets or sets the total height for the bottom layers in millimeters
+ /// </summary>
+ public float BottomLayersHeight
+ {
+ get => LastBottomLayer?.PositionZ ?? 0;
+ set => BottomLayerCount = (ushort)Math.Ceiling(value / LayerHeight);
+ }
- /// <summary>
- /// Gets or sets the number of initial layer count
- /// </summary>
- public virtual ushort BottomLayerCount
+ #region Universal Properties
+
+ /// <summary>
+ /// Gets or sets the number of initial layer count
+ /// </summary>
+ public virtual ushort BottomLayerCount
+ {
+ get => _bottomLayerCount;
+ set
{
- get => _bottomLayerCount;
- set
- {
- RaiseAndSet(ref _bottomLayerCount, value);
- RaisePropertyChanged(nameof(NormalLayerCount));
- }
+ RaiseAndSet(ref _bottomLayerCount, value);
+ RaisePropertyChanged(nameof(NormalLayerCount));
}
+ }
- /// <summary>
- /// Gets the transition layer type
- /// </summary>
- public virtual TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Firmware;
+ /// <summary>
+ /// Gets the transition layer type
+ /// </summary>
+ public virtual TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Firmware;
- /// <summary>
- /// Gets or sets the number of transition layers
- /// </summary>
- public virtual ushort TransitionLayerCount
+ /// <summary>
+ /// Gets or sets the number of transition layers
+ /// </summary>
+ public virtual ushort TransitionLayerCount
+ {
+ get => _transitionLayerCount;
+ set
{
- get => _transitionLayerCount;
- set
- {
- RaiseAndSet(ref _transitionLayerCount, (ushort)Math.Min(value, MaximumPossibleTransitionLayerCount));
- RaisePropertyChanged(nameof(HaveTransitionLayers));
- }
+ RaiseAndSet(ref _transitionLayerCount, (ushort)Math.Min(value, MaximumPossibleTransitionLayerCount));
+ RaisePropertyChanged(nameof(HaveTransitionLayers));
}
+ }
- /// <summary>
- /// Gets if have transition layers
- /// </summary>
- public bool HaveTransitionLayers => _transitionLayerCount > 0;
+ /// <summary>
+ /// Gets if have transition layers
+ /// </summary>
+ public bool HaveTransitionLayers => _transitionLayerCount > 0;
- /// <summary>
- /// Gets the maximum transition layers this layer collection supports
- /// </summary>
- public uint MaximumPossibleTransitionLayerCount
+ /// <summary>
+ /// Gets the maximum transition layers this layer collection supports
+ /// </summary>
+ public uint MaximumPossibleTransitionLayerCount
+ {
+ get
{
- get
- {
- if (BottomLayerCount == 0) return 0;
- if (LayerManager.Layers is null) return uint.MaxValue;
- int layerCount = (int)LayerCount - BottomLayerCount - 1;
- if (layerCount <= 0) return 0;
- return (uint)layerCount;
- }
+ if (BottomLayerCount == 0) return 0;
+ if (_layers is null) return uint.MaxValue;
+ int layerCount = (int)LayerCount - BottomLayerCount - 1;
+ if (layerCount <= 0) return 0;
+ return (uint)layerCount;
}
+ }
- /// <summary>
- /// Gets the number of normal layer count
- /// </summary>
- public uint NormalLayerCount => LayerCount - BottomLayerCount;
+ /// <summary>
+ /// Gets the number of normal layer count
+ /// </summary>
+ public uint NormalLayerCount => LayerCount - BottomLayerCount;
- /// <summary>
- /// Gets or sets the bottom layer off time in seconds
- /// </summary>
- public virtual float BottomLightOffDelay
+ /// <summary>
+ /// Gets or sets the bottom layer off time in seconds
+ /// </summary>
+ public virtual float BottomLightOffDelay
+ {
+ get => _bottomLightOffDelay;
+ set
{
- get => _bottomLightOffDelay;
- set
- {
- RaiseAndSet(ref _bottomLightOffDelay, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LightOffDelayRepresentation));
- }
+ RaiseAndSet(ref _bottomLightOffDelay, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LightOffDelayRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the layer off time in seconds
- /// </summary>
- public virtual float LightOffDelay
+ /// <summary>
+ /// Gets or sets the layer off time in seconds
+ /// </summary>
+ public virtual float LightOffDelay
+ {
+ get => _lightOffDelay;
+ set
{
- get => _lightOffDelay;
- set
- {
- RaiseAndSet(ref _lightOffDelay, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LightOffDelayRepresentation));
- }
+ RaiseAndSet(ref _lightOffDelay, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LightOffDelayRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the bottom time in seconds to wait before cure the layer
- /// </summary>
- public virtual float BottomWaitTimeBeforeCure
+ /// <summary>
+ /// Gets or sets the bottom time in seconds to wait before cure the layer
+ /// </summary>
+ public virtual float BottomWaitTimeBeforeCure
+ {
+ get => _bottomWaitTimeBeforeCure;
+ set
{
- get => _bottomWaitTimeBeforeCure;
- set
- {
- RaiseAndSet(ref _bottomWaitTimeBeforeCure, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(WaitTimeRepresentation));
- }
+ RaiseAndSet(ref _bottomWaitTimeBeforeCure, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(WaitTimeRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the time in seconds to wait after cure the layer
- /// </summary>
- public virtual float WaitTimeBeforeCure
+ /// <summary>
+ /// Gets or sets the time in seconds to wait after cure the layer
+ /// </summary>
+ public virtual float WaitTimeBeforeCure
+ {
+ get => _waitTimeBeforeCure;
+ set
{
- get => _waitTimeBeforeCure;
- set
- {
- RaiseAndSet(ref _waitTimeBeforeCure, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(WaitTimeRepresentation));
- }
+ RaiseAndSet(ref _waitTimeBeforeCure, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(WaitTimeRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the initial exposure time for <see cref="BottomLayerCount"/> in seconds
- /// </summary>
- public virtual float BottomExposureTime
+ /// <summary>
+ /// Gets or sets the initial exposure time for <see cref="BottomLayerCount"/> in seconds
+ /// </summary>
+ public virtual float BottomExposureTime
+ {
+ get => _bottomExposureTime;
+ set
{
- get => _bottomExposureTime;
- set
- {
- RaiseAndSet(ref _bottomExposureTime, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(ExposureRepresentation));
- }
+ RaiseAndSet(ref _bottomExposureTime, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(ExposureRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the normal layer exposure time in seconds
- /// </summary>
- public virtual float ExposureTime
+ /// <summary>
+ /// Gets or sets the normal layer exposure time in seconds
+ /// </summary>
+ public virtual float ExposureTime
+ {
+ get => _exposureTime;
+ set
{
- get => _exposureTime;
- set
- {
- RaiseAndSet(ref _exposureTime, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(ExposureRepresentation));
- }
+ RaiseAndSet(ref _exposureTime, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(ExposureRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the bottom time in seconds to wait after cure the layer
- /// </summary>
- public virtual float BottomWaitTimeAfterCure
+ /// <summary>
+ /// Gets or sets the bottom time in seconds to wait after cure the layer
+ /// </summary>
+ public virtual float BottomWaitTimeAfterCure
+ {
+ get => _bottomWaitTimeAfterCure;
+ set
{
- get => _bottomWaitTimeAfterCure;
- set
- {
- RaiseAndSet(ref _bottomWaitTimeAfterCure, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(WaitTimeRepresentation));
- }
+ RaiseAndSet(ref _bottomWaitTimeAfterCure, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(WaitTimeRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the time in seconds to wait after cure the layer
- /// </summary>
- public virtual float WaitTimeAfterCure
+ /// <summary>
+ /// Gets or sets the time in seconds to wait after cure the layer
+ /// </summary>
+ public virtual float WaitTimeAfterCure
+ {
+ get => _waitTimeAfterCure;
+ set
{
- get => _waitTimeAfterCure;
- set
- {
- RaiseAndSet(ref _waitTimeAfterCure, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(WaitTimeRepresentation));
- }
+ RaiseAndSet(ref _waitTimeAfterCure, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(WaitTimeRepresentation));
}
+ }
- /// <summary>
- /// Gets: Total bottom lift height (lift1 + lift2)
- /// Sets: Bottom lift1 with value and lift2 with 0
- /// </summary>
- public float BottomLiftHeightTotal
+ /// <summary>
+ /// Gets: Total bottom lift height (lift1 + lift2)
+ /// Sets: Bottom lift1 with value and lift2 with 0
+ /// </summary>
+ public float BottomLiftHeightTotal
+ {
+ get => (float)Math.Round(BottomLiftHeight + BottomLiftHeight2, 2);
+ set
{
- get => (float)Math.Round(BottomLiftHeight + BottomLiftHeight2, 2);
- set
- {
- BottomLiftHeight = (float)Math.Round(value, 2);
- BottomLiftHeight2 = 0;
- }
+ BottomLiftHeight = (float)Math.Round(value, 2);
+ BottomLiftHeight2 = 0;
}
+ }
- /// <summary>
- /// Gets: Total lift height (lift1 + lift2)
- /// Sets: Lift1 with value and lift2 with 0
- /// </summary>
- public float LiftHeightTotal
+ /// <summary>
+ /// Gets: Total lift height (lift1 + lift2)
+ /// Sets: Lift1 with value and lift2 with 0
+ /// </summary>
+ public float LiftHeightTotal
+ {
+ get => (float)Math.Round(LiftHeight + LiftHeight2, 2);
+ set
{
- get => (float)Math.Round(LiftHeight + LiftHeight2, 2);
- set
- {
- LiftHeight = (float)Math.Round(value, 2);
- LiftHeight2 = 0;
- }
+ LiftHeight = (float)Math.Round(value, 2);
+ LiftHeight2 = 0;
}
+ }
- /// <summary>
- /// Gets or sets the bottom lift height in mm
- /// </summary>
- public virtual float BottomLiftHeight
+ /// <summary>
+ /// Gets or sets the bottom lift height in mm
+ /// </summary>
+ public virtual float BottomLiftHeight
+ {
+ get => _bottomLiftHeight;
+ set
{
- get => _bottomLiftHeight;
- set
- {
- RaiseAndSet(ref _bottomLiftHeight, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(BottomLiftHeightTotal));
- RaisePropertyChanged(nameof(LiftRepresentation));
- BottomRetractHeight2 = BottomRetractHeight2; // Sanitize
- }
+ RaiseAndSet(ref _bottomLiftHeight, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(BottomLiftHeightTotal));
+ RaisePropertyChanged(nameof(LiftRepresentation));
+ BottomRetractHeight2 = BottomRetractHeight2; // Sanitize
}
+ }
- /// <summary>
- /// Gets or sets the bottom lift speed in mm/min
- /// </summary>
- public virtual float BottomLiftSpeed
+ /// <summary>
+ /// Gets or sets the bottom lift speed in mm/min
+ /// </summary>
+ public virtual float BottomLiftSpeed
+ {
+ get => _bottomLiftSpeed;
+ set
{
- get => _bottomLiftSpeed;
- set
- {
- RaiseAndSet(ref _bottomLiftSpeed, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LiftRepresentation));
- }
+ RaiseAndSet(ref _bottomLiftSpeed, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LiftRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the lift height in mm
- /// </summary>
- public virtual float LiftHeight
+ /// <summary>
+ /// Gets or sets the lift height in mm
+ /// </summary>
+ public virtual float LiftHeight
+ {
+ get => _liftHeight;
+ set
{
- get => _liftHeight;
- set
- {
- RaiseAndSet(ref _liftHeight, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LiftHeightTotal));
- RaisePropertyChanged(nameof(LiftRepresentation));
- RetractHeight2 = RetractHeight2; // Sanitize
- }
+ RaiseAndSet(ref _liftHeight, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LiftHeightTotal));
+ RaisePropertyChanged(nameof(LiftRepresentation));
+ RetractHeight2 = RetractHeight2; // Sanitize
}
+ }
- /// <summary>
- /// Gets or sets the speed in mm/min
- /// </summary>
- public virtual float LiftSpeed
+ /// <summary>
+ /// Gets or sets the speed in mm/min
+ /// </summary>
+ public virtual float LiftSpeed
+ {
+ get => _liftSpeed;
+ set
{
- get => _liftSpeed;
- set
- {
- RaiseAndSet(ref _liftSpeed, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LiftRepresentation));
- }
+ RaiseAndSet(ref _liftSpeed, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LiftRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the second bottom lift height in mm
- /// </summary>
- public virtual float BottomLiftHeight2
+ /// <summary>
+ /// Gets or sets the second bottom lift height in mm
+ /// </summary>
+ public virtual float BottomLiftHeight2
+ {
+ get => _bottomLiftHeight2;
+ set
{
- get => _bottomLiftHeight2;
- set
- {
- RaiseAndSet(ref _bottomLiftHeight2, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(BottomLiftHeightTotal));
- RaisePropertyChanged(nameof(LiftRepresentation));
- BottomRetractHeight2 = BottomRetractHeight2; // Sanitize
- }
+ RaiseAndSet(ref _bottomLiftHeight2, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(BottomLiftHeightTotal));
+ RaisePropertyChanged(nameof(LiftRepresentation));
+ BottomRetractHeight2 = BottomRetractHeight2; // Sanitize
}
+ }
- /// <summary>
- /// Gets or sets the second bottom lift speed in mm/min
- /// </summary>
- public virtual float BottomLiftSpeed2
+ /// <summary>
+ /// Gets or sets the second bottom lift speed in mm/min
+ /// </summary>
+ public virtual float BottomLiftSpeed2
+ {
+ get => _bottomLiftSpeed2;
+ set
{
- get => _bottomLiftSpeed2;
- set
- {
- RaiseAndSet(ref _bottomLiftSpeed2, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LiftRepresentation));
- }
+ RaiseAndSet(ref _bottomLiftSpeed2, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LiftRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the second lift height in mm (This is the closer to fep retract)
- /// </summary>
- public virtual float LiftHeight2
+ /// <summary>
+ /// Gets or sets the second lift height in mm (This is the closer to fep retract)
+ /// </summary>
+ public virtual float LiftHeight2
+ {
+ get => _liftHeight2;
+ set
{
- get => _liftHeight2;
- set
- {
- RaiseAndSet(ref _liftHeight2, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LiftHeightTotal));
- RaisePropertyChanged(nameof(LiftRepresentation));
- RetractHeight2 = RetractHeight2; // Sanitize
- }
+ RaiseAndSet(ref _liftHeight2, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LiftHeightTotal));
+ RaisePropertyChanged(nameof(LiftRepresentation));
+ RetractHeight2 = RetractHeight2; // Sanitize
}
+ }
- /// <summary>
- /// Gets or sets the second speed in mm/min (This is the closer to fep retract)
- /// </summary>
- public virtual float LiftSpeed2
+ /// <summary>
+ /// Gets or sets the second speed in mm/min (This is the closer to fep retract)
+ /// </summary>
+ public virtual float LiftSpeed2
+ {
+ get => _liftSpeed2;
+ set
{
- get => _liftSpeed2;
- set
- {
- RaiseAndSet(ref _liftSpeed2, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(LiftRepresentation));
- }
+ RaiseAndSet(ref _liftSpeed2, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(LiftRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the bottom time in seconds to wait after lift / before retract
- /// </summary>
- public virtual float BottomWaitTimeAfterLift
+ /// <summary>
+ /// Gets or sets the bottom time in seconds to wait after lift / before retract
+ /// </summary>
+ public virtual float BottomWaitTimeAfterLift
+ {
+ get => _bottomWaitTimeAfterLift;
+ set
{
- get => _bottomWaitTimeAfterLift;
- set
- {
- RaiseAndSet(ref _bottomWaitTimeAfterLift, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(WaitTimeRepresentation));
- }
+ RaiseAndSet(ref _bottomWaitTimeAfterLift, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(WaitTimeRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the time in seconds to wait after lift / before retract
- /// </summary>
- public virtual float WaitTimeAfterLift
+ /// <summary>
+ /// Gets or sets the time in seconds to wait after lift / before retract
+ /// </summary>
+ public virtual float WaitTimeAfterLift
+ {
+ get => _waitTimeAfterLift;
+ set
{
- get => _waitTimeAfterLift;
- set
- {
- RaiseAndSet(ref _waitTimeAfterLift, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(WaitTimeRepresentation));
- }
+ RaiseAndSet(ref _waitTimeAfterLift, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(WaitTimeRepresentation));
}
+ }
- /// <summary>
- /// Gets: Total bottom retract height (retract1 + retract2) alias of <see cref="BottomLiftHeightTotal"/>
- /// </summary>
- public float BottomRetractHeightTotal => BottomLiftHeightTotal;
+ /// <summary>
+ /// Gets: Total bottom retract height (retract1 + retract2) alias of <see cref="BottomLiftHeightTotal"/>
+ /// </summary>
+ public float BottomRetractHeightTotal => BottomLiftHeightTotal;
- /// <summary>
- /// Gets: Total retract height (retract1 + retract2) alias of <see cref="LiftHeightTotal"/>
- /// </summary>
- public float RetractHeightTotal => LiftHeightTotal;
+ /// <summary>
+ /// Gets: Total retract height (retract1 + retract2) alias of <see cref="LiftHeightTotal"/>
+ /// </summary>
+ public float RetractHeightTotal => LiftHeightTotal;
- /// <summary>
- /// Gets the bottom retract height in mm
- /// </summary>
- public float BottomRetractHeight => (float)Math.Round(BottomLiftHeightTotal - BottomRetractHeight2, 2);
+ /// <summary>
+ /// Gets the bottom retract height in mm
+ /// </summary>
+ public float BottomRetractHeight => (float)Math.Round(BottomLiftHeightTotal - BottomRetractHeight2, 2);
- /// <summary>
- /// Gets the speed in mm/min for the bottom retracts
- /// </summary>
- public virtual float BottomRetractSpeed
+ /// <summary>
+ /// Gets the speed in mm/min for the bottom retracts
+ /// </summary>
+ public virtual float BottomRetractSpeed
+ {
+ get => _bottomRetractSpeed;
+ set
{
- get => _bottomRetractSpeed;
- set
- {
- RaiseAndSet(ref _bottomRetractSpeed, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(RetractRepresentation));
- }
+ RaiseAndSet(ref _bottomRetractSpeed, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(RetractRepresentation));
}
+ }
- /// <summary>
- /// Gets the retract height in mm
- /// </summary>
- public float RetractHeight => (float)Math.Round(LiftHeightTotal - RetractHeight2, 2);
+ /// <summary>
+ /// Gets the retract height in mm
+ /// </summary>
+ public float RetractHeight => (float)Math.Round(LiftHeightTotal - RetractHeight2, 2);
- /// <summary>
- /// Gets the speed in mm/min for the retracts
- /// </summary>
- public virtual float RetractSpeed
+ /// <summary>
+ /// Gets the speed in mm/min for the retracts
+ /// </summary>
+ public virtual float RetractSpeed
+ {
+ get => _retractSpeed;
+ set
{
- get => _retractSpeed;
- set
- {
- RaiseAndSet(ref _retractSpeed, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(RetractRepresentation));
- }
+ RaiseAndSet(ref _retractSpeed, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(RetractRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the second bottom retract height in mm
- /// </summary>
- public virtual float BottomRetractHeight2
+ /// <summary>
+ /// Gets or sets the second bottom retract height in mm
+ /// </summary>
+ public virtual float BottomRetractHeight2
+ {
+ get => _bottomRetractHeight2;
+ set
{
- get => _bottomRetractHeight2;
- set
- {
- value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal);
- RaiseAndSet(ref _bottomRetractHeight2, value);
- RaisePropertyChanged(nameof(BottomRetractHeight));
- RaisePropertyChanged(nameof(BottomRetractHeightTotal));
- RaisePropertyChanged(nameof(RetractRepresentation));
- }
+ value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal);
+ RaiseAndSet(ref _bottomRetractHeight2, value);
+ RaisePropertyChanged(nameof(BottomRetractHeight));
+ RaisePropertyChanged(nameof(BottomRetractHeightTotal));
+ RaisePropertyChanged(nameof(RetractRepresentation));
}
+ }
- /// <summary>
- /// Gets the speed in mm/min for the retracts
- /// </summary>
- public virtual float BottomRetractSpeed2
+ /// <summary>
+ /// Gets the speed in mm/min for the retracts
+ /// </summary>
+ public virtual float BottomRetractSpeed2
+ {
+ get => _bottomRetractSpeed2;
+ set
{
- get => _bottomRetractSpeed2;
- set
- {
- RaiseAndSet(ref _bottomRetractSpeed2, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(RetractRepresentation));
- }
+ RaiseAndSet(ref _bottomRetractSpeed2, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(RetractRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the second retract height in mm
- /// </summary>
- public virtual float RetractHeight2
+ /// <summary>
+ /// Gets or sets the second retract height in mm
+ /// </summary>
+ public virtual float RetractHeight2
+ {
+ get => _retractHeight2;
+ set
{
- get => _retractHeight2;
- set
- {
- value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
- RaiseAndSet(ref _retractHeight2, value);
- RaisePropertyChanged(nameof(RetractHeight));
- RaisePropertyChanged(nameof(RetractHeightTotal));
- RaisePropertyChanged(nameof(RetractRepresentation));
- }
+ value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
+ RaiseAndSet(ref _retractHeight2, value);
+ RaisePropertyChanged(nameof(RetractHeight));
+ RaisePropertyChanged(nameof(RetractHeightTotal));
+ RaisePropertyChanged(nameof(RetractRepresentation));
}
+ }
- /// <summary>
- /// Gets the speed in mm/min for the retracts
- /// </summary>
- public virtual float RetractSpeed2
+ /// <summary>
+ /// Gets the speed in mm/min for the retracts
+ /// </summary>
+ public virtual float RetractSpeed2
+ {
+ get => _retractSpeed2;
+ set
{
- get => _retractSpeed2;
- set
- {
- RaiseAndSet(ref _retractSpeed2, (float)Math.Round(value, 2));
- RaisePropertyChanged(nameof(RetractRepresentation));
- }
+ RaiseAndSet(ref _retractSpeed2, (float)Math.Round(value, 2));
+ RaisePropertyChanged(nameof(RetractRepresentation));
}
+ }
- /// <summary>
- /// Gets or sets the bottom pwm value from 0 to 255
- /// </summary>
- public virtual byte BottomLightPWM
- {
- get => _bottomLightPwm;
- set => RaiseAndSet(ref _bottomLightPwm, value);
- }
+ /// <summary>
+ /// Gets or sets the bottom pwm value from 0 to 255
+ /// </summary>
+ public virtual byte BottomLightPWM
+ {
+ get => _bottomLightPwm;
+ set => RaiseAndSet(ref _bottomLightPwm, value);
+ }
- /// <summary>
- /// Gets or sets the pwm value from 0 to 255
- /// </summary>
- public virtual byte LightPWM
+ /// <summary>
+ /// Gets or sets the pwm value from 0 to 255
+ /// </summary>
+ public virtual byte LightPWM
+ {
+ get => _lightPwm;
+ set => RaiseAndSet(ref _lightPwm, value);
+ }
+
+ /// <summary>
+ /// Gets the minimum used speed for bottom layers in mm/min
+ /// </summary>
+ public float MinimumBottomSpeed
+ {
+ get
{
- get => _lightPwm;
- set => RaiseAndSet(ref _lightPwm, value);
+ float speed = float.MaxValue;
+ if (BottomLiftSpeed > 0) speed = Math.Min(speed, BottomLiftSpeed);
+ if (CanUseBottomLiftSpeed2 && BottomLiftSpeed2 > 0) speed = Math.Min(speed, BottomLiftSpeed2);
+ if (CanUseBottomRetractSpeed && BottomRetractSpeed > 0) speed = Math.Min(speed, BottomRetractSpeed);
+ if (CanUseBottomRetractSpeed2 && BottomRetractSpeed2 > 0) speed = Math.Min(speed, BottomRetractSpeed2);
+ if (Math.Abs(speed - float.MaxValue) < 0.01) return 0;
+
+ return speed;
}
+ }
- /// <summary>
- /// Gets the minimum used speed for bottom layers in mm/min
- /// </summary>
- public float MinimumBottomSpeed
+ /// <summary>
+ /// Gets the minimum used speed for normal bottom layers in mm/min
+ /// </summary>
+ public float MinimumNormalSpeed
+ {
+ get
{
- get
- {
- float speed = float.MaxValue;
- if (BottomLiftSpeed > 0) speed = Math.Min(speed, BottomLiftSpeed);
- if (CanUseBottomLiftSpeed2 && BottomLiftSpeed2 > 0) speed = Math.Min(speed, BottomLiftSpeed2);
- if (CanUseBottomRetractSpeed && BottomRetractSpeed > 0) speed = Math.Min(speed, BottomRetractSpeed);
- if (CanUseBottomRetractSpeed2 && BottomRetractSpeed2 > 0) speed = Math.Min(speed, BottomRetractSpeed2);
- if (Math.Abs(speed - float.MaxValue) < 0.01) return 0;
-
- return speed;
- }
+ float speed = float.MaxValue;
+ if (LiftSpeed > 0) speed = Math.Min(speed, LiftSpeed);
+ if (CanUseLiftSpeed2 && LiftSpeed2 > 0) speed = Math.Min(speed, LiftSpeed2);
+ if (CanUseRetractSpeed && RetractSpeed > 0) speed = Math.Min(speed, RetractSpeed);
+ if (CanUseRetractSpeed2 && RetractSpeed2 > 0) speed = Math.Min(speed, RetractSpeed2);
+ if (Math.Abs(speed - float.MaxValue) < 0.01) return 0;
+
+ return speed;
}
+ }
- /// <summary>
- /// Gets the minimum used speed for normal bottom layers in mm/min
- /// </summary>
- public float MinimumNormalSpeed
+ /// <summary>
+ /// Gets the minimum used speed in mm/min
+ /// </summary>
+ public float MinimumSpeed
+ {
+ get
{
- get
- {
- float speed = float.MaxValue;
- if (LiftSpeed > 0) speed = Math.Min(speed, LiftSpeed);
- if (CanUseLiftSpeed2 && LiftSpeed2 > 0) speed = Math.Min(speed, LiftSpeed2);
- if (CanUseRetractSpeed && RetractSpeed > 0) speed = Math.Min(speed, RetractSpeed);
- if (CanUseRetractSpeed2 && RetractSpeed2 > 0) speed = Math.Min(speed, RetractSpeed2);
- if (Math.Abs(speed - float.MaxValue) < 0.01) return 0;
-
- return speed;
- }
+ var bottomSpeed = MinimumBottomSpeed;
+ var normalSpeed = MinimumNormalSpeed;
+ if (bottomSpeed <= 0) return normalSpeed;
+ if (normalSpeed <= 0) return bottomSpeed;
+
+ return Math.Min(bottomSpeed, normalSpeed);
}
+ }
- /// <summary>
- /// Gets the minimum used speed in mm/min
- /// </summary>
- public float MinimumSpeed
+ /// <summary>
+ /// Gets the maximum used speed for bottom layers in mm/min
+ /// </summary>
+ public float MaximumBottomSpeed
+ {
+ get
{
- get
- {
- var bottomSpeed = MinimumBottomSpeed;
- var normalSpeed = MinimumNormalSpeed;
- if (bottomSpeed <= 0) return normalSpeed;
- if (normalSpeed <= 0) return bottomSpeed;
+ float speed = BottomLiftSpeed;
+ if (CanUseBottomLiftSpeed2) speed = Math.Max(speed, BottomLiftSpeed2);
+ if (CanUseBottomRetractSpeed) speed = Math.Max(speed, BottomRetractSpeed);
+ if (CanUseBottomRetractSpeed2) speed = Math.Max(speed, BottomRetractSpeed2);
- return Math.Min(bottomSpeed, normalSpeed);
- }
+ return speed;
}
+ }
- /// <summary>
- /// Gets the maximum used speed for bottom layers in mm/min
- /// </summary>
- public float MaximumBottomSpeed
+ /// <summary>
+ /// Gets the maximum used speed for normal bottom layers in mm/min
+ /// </summary>
+ public float MaximumNormalSpeed
+ {
+ get
{
- get
- {
- float speed = BottomLiftSpeed;
- if (CanUseBottomLiftSpeed2) speed = Math.Max(speed, BottomLiftSpeed2);
- if (CanUseBottomRetractSpeed) speed = Math.Max(speed, BottomRetractSpeed);
- if (CanUseBottomRetractSpeed2) speed = Math.Max(speed, BottomRetractSpeed2);
+ float speed = LiftSpeed;
+ if (CanUseLiftSpeed2) speed = Math.Max(speed, LiftSpeed2);
+ if (CanUseRetractSpeed) speed = Math.Max(speed, RetractSpeed);
+ if (CanUseRetractSpeed2) speed = Math.Max(speed, RetractSpeed2);
- return speed;
- }
+ return speed;
}
+ }
- /// <summary>
- /// Gets the maximum used speed for normal bottom layers in mm/min
- /// </summary>
- public float MaximumNormalSpeed
+ /// <summary>
+ /// Gets the maximum used speed in mm/min
+ /// </summary>
+ public float MaximumSpeed => Math.Max(MaximumBottomSpeed, MaximumNormalSpeed);
+
+ public bool CanUseBottomLayerCount => HavePrintParameterModifier(PrintParameterModifier.BottomLayerCount);
+ public bool CanUseTransitionLayerCount => HavePrintParameterModifier(PrintParameterModifier.TransitionLayerCount);
+
+ public bool CanUseBottomLightOffDelay => HavePrintParameterModifier(PrintParameterModifier.BottomLightOffDelay);
+ public bool CanUseLightOffDelay => HavePrintParameterModifier(PrintParameterModifier.LightOffDelay);
+ public bool CanUseAnyLightOffDelay => CanUseBottomLightOffDelay || CanUseLightOffDelay;
+
+ public bool CanUseBottomWaitTimeBeforeCure => HavePrintParameterModifier(PrintParameterModifier.BottomWaitTimeBeforeCure);
+ public bool CanUseWaitTimeBeforeCure => HavePrintParameterModifier(PrintParameterModifier.WaitTimeBeforeCure);
+ public bool CanUseAnyWaitTimeBeforeCure => CanUseBottomWaitTimeBeforeCure || CanUseWaitTimeBeforeCure;
+
+ public bool CanUseBottomExposureTime => HavePrintParameterModifier(PrintParameterModifier.BottomExposureTime);
+ public bool CanUseExposureTime => HavePrintParameterModifier(PrintParameterModifier.ExposureTime);
+ public bool CanUseAnyExposureTime => CanUseBottomExposureTime || CanUseExposureTime;
+
+ public bool CanUseBottomWaitTimeAfterCure => HavePrintParameterModifier(PrintParameterModifier.BottomWaitTimeAfterCure);
+ public bool CanUseWaitTimeAfterCure => HavePrintParameterModifier(PrintParameterModifier.WaitTimeAfterCure);
+ public bool CanUseAnyWaitTimeAfterCure => CanUseBottomWaitTimeAfterCure || CanUseWaitTimeAfterCure;
+
+ public bool CanUseBottomLiftHeight => HavePrintParameterModifier(PrintParameterModifier.BottomLiftHeight);
+ public bool CanUseLiftHeight => HavePrintParameterModifier(PrintParameterModifier.LiftHeight);
+ public bool CanUseAnyLiftHeight => CanUseBottomLiftHeight || CanUseLiftHeight;
+
+ public bool CanUseBottomLiftSpeed => HavePrintParameterModifier(PrintParameterModifier.BottomLiftSpeed);
+ public bool CanUseLiftSpeed => HavePrintParameterModifier(PrintParameterModifier.LiftSpeed);
+ public bool CanUseAnyLiftSpeed => CanUseBottomLiftSpeed || CanUseLiftSpeed;
+
+ public bool CanUseBottomLiftHeight2 => HavePrintParameterModifier(PrintParameterModifier.BottomLiftHeight2);
+ public bool CanUseLiftHeight2 => HavePrintParameterModifier(PrintParameterModifier.LiftHeight2);
+ public bool CanUseAnyLiftHeight2 => CanUseBottomLiftHeight2 || CanUseLiftHeight2;
+
+ public bool CanUseBottomLiftSpeed2 => HavePrintParameterModifier(PrintParameterModifier.BottomLiftSpeed2);
+ public bool CanUseLiftSpeed2 => HavePrintParameterModifier(PrintParameterModifier.LiftSpeed2);
+ public bool CanUseAnyLiftSpeed2 => CanUseBottomLiftSpeed2 || CanUseLiftSpeed2;
+
+ public bool CanUseBottomWaitTimeAfterLift => HavePrintParameterModifier(PrintParameterModifier.BottomWaitTimeAfterLift);
+ public bool CanUseWaitTimeAfterLift => HavePrintParameterModifier(PrintParameterModifier.WaitTimeAfterLift);
+ public bool CanUseAnyWaitTimeAfterLift => CanUseBottomWaitTimeAfterLift || CanUseWaitTimeAfterLift;
+
+ public bool CanUseBottomRetractSpeed => HavePrintParameterModifier(PrintParameterModifier.BottomRetractSpeed);
+ public bool CanUseRetractSpeed => HavePrintParameterModifier(PrintParameterModifier.RetractSpeed);
+ public bool CanUseAnyRetractSpeed => CanUseBottomRetractSpeed || CanUseRetractSpeed;
+
+ public bool CanUseBottomRetractHeight2 => HavePrintParameterModifier(PrintParameterModifier.BottomRetractHeight2);
+ public bool CanUseRetractHeight2 => HavePrintParameterModifier(PrintParameterModifier.RetractHeight2);
+ public bool CanUseAnyRetractHeight2 => CanUseBottomRetractHeight2 || CanUseRetractHeight2;
+ public bool CanUseBottomRetractSpeed2 => HavePrintParameterModifier(PrintParameterModifier.BottomRetractSpeed2);
+ public bool CanUseRetractSpeed2 => HavePrintParameterModifier(PrintParameterModifier.RetractSpeed2);
+ public bool CanUseAnyRetractSpeed2 => CanUseBottomRetractSpeed2 || CanUseRetractSpeed2;
+
+ public bool CanUseAnyWaitTime => CanUseBottomWaitTimeBeforeCure || CanUseBottomWaitTimeAfterCure || CanUseBottomWaitTimeAfterLift ||
+ CanUseWaitTimeBeforeCure || CanUseWaitTimeAfterCure || CanUseWaitTimeAfterLift;
+
+ public bool CanUseBottomLightPWM => HavePrintParameterModifier(PrintParameterModifier.BottomLightPWM);
+ public bool CanUseLightPWM => HavePrintParameterModifier(PrintParameterModifier.LightPWM);
+ public bool CanUseAnyLightPWM => CanUseBottomLightPWM || CanUseLightPWM;
+
+ public bool CanUseLayerPositionZ => HaveLayerParameterModifier(PrintParameterModifier.PositionZ);
+ public bool CanUseLayerWaitTimeBeforeCure => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeBeforeCure);
+ public bool CanUseLayerExposureTime => HaveLayerParameterModifier(PrintParameterModifier.ExposureTime);
+ public bool CanUseLayerWaitTimeAfterCure => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterCure);
+ public bool CanUseLayerLiftHeight => HaveLayerParameterModifier(PrintParameterModifier.LiftHeight);
+ public bool CanUseLayerLiftSpeed => HaveLayerParameterModifier(PrintParameterModifier.LiftSpeed);
+ public bool CanUseLayerLiftHeight2 => HaveLayerParameterModifier(PrintParameterModifier.LiftHeight2);
+ public bool CanUseLayerLiftSpeed2 => HaveLayerParameterModifier(PrintParameterModifier.LiftSpeed2);
+ public bool CanUseLayerWaitTimeAfterLift => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterLift);
+ public bool CanUseLayerRetractSpeed => HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed);
+ public bool CanUseLayerRetractHeight2 => HaveLayerParameterModifier(PrintParameterModifier.RetractHeight2);
+ public bool CanUseLayerRetractSpeed2 => HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed2);
+ public bool CanUseLayerLightOffDelay => HaveLayerParameterModifier(PrintParameterModifier.LightOffDelay);
+ public bool CanUseLayerLightPWM => HaveLayerParameterModifier(PrintParameterModifier.LightPWM);
+
+ public string ExposureRepresentation
+ {
+ get
{
- get
- {
- float speed = LiftSpeed;
- if (CanUseLiftSpeed2) speed = Math.Max(speed, LiftSpeed2);
- if (CanUseRetractSpeed) speed = Math.Max(speed, RetractSpeed);
- if (CanUseRetractSpeed2) speed = Math.Max(speed, RetractSpeed2);
+ var str = string.Empty;
- return speed;
+ if (CanUseBottomExposureTime)
+ {
+ str += BottomExposureTime.ToString(CultureInfo.InvariantCulture);
}
- }
-
- /// <summary>
- /// Gets the maximum used speed in mm/min
- /// </summary>
- public float MaximumSpeed => Math.Max(MaximumBottomSpeed, MaximumNormalSpeed);
-
- public bool CanUseBottomLayerCount => HavePrintParameterModifier(PrintParameterModifier.BottomLayerCount);
- public bool CanUseTransitionLayerCount => HavePrintParameterModifier(PrintParameterModifier.TransitionLayerCount);
-
- public bool CanUseBottomLightOffDelay => HavePrintParameterModifier(PrintParameterModifier.BottomLightOffDelay);
- public bool CanUseLightOffDelay => HavePrintParameterModifier(PrintParameterModifier.LightOffDelay);
- public bool CanUseAnyLightOffDelay => CanUseBottomLightOffDelay || CanUseLightOffDelay;
-
- public bool CanUseBottomWaitTimeBeforeCure => HavePrintParameterModifier(PrintParameterModifier.BottomWaitTimeBeforeCure);
- public bool CanUseWaitTimeBeforeCure => HavePrintParameterModifier(PrintParameterModifier.WaitTimeBeforeCure);
- public bool CanUseAnyWaitTimeBeforeCure => CanUseBottomWaitTimeBeforeCure || CanUseWaitTimeBeforeCure;
-
- public bool CanUseBottomExposureTime => HavePrintParameterModifier(PrintParameterModifier.BottomExposureTime);
- public bool CanUseExposureTime => HavePrintParameterModifier(PrintParameterModifier.ExposureTime);
- public bool CanUseAnyExposureTime => CanUseBottomExposureTime || CanUseExposureTime;
-
- public bool CanUseBottomWaitTimeAfterCure => HavePrintParameterModifier(PrintParameterModifier.BottomWaitTimeAfterCure);
- public bool CanUseWaitTimeAfterCure => HavePrintParameterModifier(PrintParameterModifier.WaitTimeAfterCure);
- public bool CanUseAnyWaitTimeAfterCure => CanUseBottomWaitTimeAfterCure || CanUseWaitTimeAfterCure;
-
- public bool CanUseBottomLiftHeight => HavePrintParameterModifier(PrintParameterModifier.BottomLiftHeight);
- public bool CanUseLiftHeight => HavePrintParameterModifier(PrintParameterModifier.LiftHeight);
- public bool CanUseAnyLiftHeight => CanUseBottomLiftHeight || CanUseLiftHeight;
-
- public bool CanUseBottomLiftSpeed => HavePrintParameterModifier(PrintParameterModifier.BottomLiftSpeed);
- public bool CanUseLiftSpeed => HavePrintParameterModifier(PrintParameterModifier.LiftSpeed);
- public bool CanUseAnyLiftSpeed => CanUseBottomLiftSpeed || CanUseLiftSpeed;
-
- public bool CanUseBottomLiftHeight2 => HavePrintParameterModifier(PrintParameterModifier.BottomLiftHeight2);
- public bool CanUseLiftHeight2 => HavePrintParameterModifier(PrintParameterModifier.LiftHeight2);
- public bool CanUseAnyLiftHeight2 => CanUseBottomLiftHeight2 || CanUseLiftHeight2;
-
- public bool CanUseBottomLiftSpeed2 => HavePrintParameterModifier(PrintParameterModifier.BottomLiftSpeed2);
- public bool CanUseLiftSpeed2 => HavePrintParameterModifier(PrintParameterModifier.LiftSpeed2);
- public bool CanUseAnyLiftSpeed2 => CanUseBottomLiftSpeed2 || CanUseLiftSpeed2;
-
- public bool CanUseBottomWaitTimeAfterLift => HavePrintParameterModifier(PrintParameterModifier.BottomWaitTimeAfterLift);
- public bool CanUseWaitTimeAfterLift => HavePrintParameterModifier(PrintParameterModifier.WaitTimeAfterLift);
- public bool CanUseAnyWaitTimeAfterLift => CanUseBottomWaitTimeAfterLift || CanUseWaitTimeAfterLift;
-
- public bool CanUseBottomRetractSpeed => HavePrintParameterModifier(PrintParameterModifier.BottomRetractSpeed);
- public bool CanUseRetractSpeed => HavePrintParameterModifier(PrintParameterModifier.RetractSpeed);
- public bool CanUseAnyRetractSpeed => CanUseBottomRetractSpeed || CanUseRetractSpeed;
-
- public bool CanUseBottomRetractHeight2 => HavePrintParameterModifier(PrintParameterModifier.BottomRetractHeight2);
- public bool CanUseRetractHeight2 => HavePrintParameterModifier(PrintParameterModifier.RetractHeight2);
- public bool CanUseAnyRetractHeight2 => CanUseBottomRetractHeight2 || CanUseRetractHeight2;
- public bool CanUseBottomRetractSpeed2 => HavePrintParameterModifier(PrintParameterModifier.BottomRetractSpeed2);
- public bool CanUseRetractSpeed2 => HavePrintParameterModifier(PrintParameterModifier.RetractSpeed2);
- public bool CanUseAnyRetractSpeed2 => CanUseBottomRetractSpeed2 || CanUseRetractSpeed2;
-
- public bool CanUseAnyWaitTime => CanUseBottomWaitTimeBeforeCure || CanUseBottomWaitTimeAfterCure || CanUseBottomWaitTimeAfterLift ||
- CanUseWaitTimeBeforeCure || CanUseWaitTimeAfterCure || CanUseWaitTimeAfterLift;
-
- public bool CanUseBottomLightPWM => HavePrintParameterModifier(PrintParameterModifier.BottomLightPWM);
- public bool CanUseLightPWM => HavePrintParameterModifier(PrintParameterModifier.LightPWM);
- public bool CanUseAnyLightPWM => CanUseBottomLightPWM || CanUseLightPWM;
-
- public bool CanUseLayerPositionZ => HaveLayerParameterModifier(PrintParameterModifier.PositionZ);
- public bool CanUseLayerWaitTimeBeforeCure => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeBeforeCure);
- public bool CanUseLayerExposureTime => HaveLayerParameterModifier(PrintParameterModifier.ExposureTime);
- public bool CanUseLayerWaitTimeAfterCure => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterCure);
- public bool CanUseLayerLiftHeight => HaveLayerParameterModifier(PrintParameterModifier.LiftHeight);
- public bool CanUseLayerLiftSpeed => HaveLayerParameterModifier(PrintParameterModifier.LiftSpeed);
- public bool CanUseLayerLiftHeight2 => HaveLayerParameterModifier(PrintParameterModifier.LiftHeight2);
- public bool CanUseLayerLiftSpeed2 => HaveLayerParameterModifier(PrintParameterModifier.LiftSpeed2);
- public bool CanUseLayerWaitTimeAfterLift => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterLift);
- public bool CanUseLayerRetractSpeed => HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed);
- public bool CanUseLayerRetractHeight2 => HaveLayerParameterModifier(PrintParameterModifier.RetractHeight2);
- public bool CanUseLayerRetractSpeed2 => HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed2);
- public bool CanUseLayerLightOffDelay => HaveLayerParameterModifier(PrintParameterModifier.LightOffDelay);
- public bool CanUseLayerLightPWM => HaveLayerParameterModifier(PrintParameterModifier.LightPWM);
-
- public string ExposureRepresentation
- {
- get
+ if (CanUseExposureTime)
{
- var str = string.Empty;
-
- if (CanUseBottomExposureTime)
- {
- str += BottomExposureTime.ToString(CultureInfo.InvariantCulture);
- }
- if (CanUseExposureTime)
- {
- if (!string.IsNullOrEmpty(str)) str += '/';
- str += ExposureTime.ToString(CultureInfo.InvariantCulture);
- }
+ if (!string.IsNullOrEmpty(str)) str += '/';
+ str += ExposureTime.ToString(CultureInfo.InvariantCulture);
+ }
- if (!string.IsNullOrEmpty(str)) str += 's';
+ if (!string.IsNullOrEmpty(str)) str += 's';
- return str;
- }
+ return str;
}
+ }
- public string LiftRepresentation
+ public string LiftRepresentation
+ {
+ get
{
- get
- {
- var str = string.Empty;
+ var str = string.Empty;
- var haveBottomLiftHeight = CanUseBottomLiftHeight;
- var haveLiftHeight = CanUseLiftHeight;
- var haveBottomLiftHeight2 = CanUseBottomLiftHeight2;
- var haveLiftHeight2 = CanUseLiftHeight2;
+ var haveBottomLiftHeight = CanUseBottomLiftHeight;
+ var haveLiftHeight = CanUseLiftHeight;
+ var haveBottomLiftHeight2 = CanUseBottomLiftHeight2;
+ var haveLiftHeight2 = CanUseLiftHeight2;
- var haveBottomLiftSpeed2 = CanUseBottomLiftSpeed2;
- var haveLiftSpeed2 = CanUseLiftSpeed2;
+ var haveBottomLiftSpeed2 = CanUseBottomLiftSpeed2;
+ var haveLiftSpeed2 = CanUseLiftSpeed2;
- if (!haveBottomLiftHeight && !haveLiftHeight && !haveBottomLiftHeight2 && !haveLiftHeight2) return str;
+ if (!haveBottomLiftHeight && !haveLiftHeight && !haveBottomLiftHeight2 && !haveLiftHeight2) return str;
- // Sequence 1
- if (haveBottomLiftHeight)
+ // Sequence 1
+ if (haveBottomLiftHeight)
+ {
+ str += BottomLiftHeight.ToString(CultureInfo.InvariantCulture);
+ if (haveBottomLiftHeight2 && BottomLiftHeight2 > 0)
{
- str += BottomLiftHeight.ToString(CultureInfo.InvariantCulture);
- if (haveBottomLiftHeight2 && BottomLiftHeight2 > 0)
- {
- str += $"+{BottomLiftHeight2.ToString(CultureInfo.InvariantCulture)}";
- }
+ str += $"+{BottomLiftHeight2.ToString(CultureInfo.InvariantCulture)}";
}
+ }
- if (haveLiftHeight)
- {
- if (!string.IsNullOrEmpty(str)) str += '/';
- str += LiftHeight.ToString(CultureInfo.InvariantCulture);
+ if (haveLiftHeight)
+ {
+ if (!string.IsNullOrEmpty(str)) str += '/';
+ str += LiftHeight.ToString(CultureInfo.InvariantCulture);
- if (haveLiftHeight2 && LiftHeight2 > 0)
- {
- str += $"+{LiftHeight2.ToString(CultureInfo.InvariantCulture)}";
- }
+ if (haveLiftHeight2 && LiftHeight2 > 0)
+ {
+ str += $"+{LiftHeight2.ToString(CultureInfo.InvariantCulture)}";
}
+ }
- if (string.IsNullOrEmpty(str)) return str;
+ if (string.IsNullOrEmpty(str)) return str;
- str += "mm @ ";
+ str += "mm @ ";
- var haveBottomLiftSpeed = CanUseBottomLiftSpeed;
- var haveLiftSpeed = CanUseLiftSpeed;
- if (haveBottomLiftSpeed)
+ var haveBottomLiftSpeed = CanUseBottomLiftSpeed;
+ var haveLiftSpeed = CanUseLiftSpeed;
+ if (haveBottomLiftSpeed)
+ {
+ str += BottomLiftSpeed.ToString(CultureInfo.InvariantCulture);
+ if (haveBottomLiftSpeed2 && haveBottomLiftHeight2 && BottomLiftHeight2 > 0)
{
- str += BottomLiftSpeed.ToString(CultureInfo.InvariantCulture);
- if (haveBottomLiftSpeed2 && haveBottomLiftHeight2 && BottomLiftHeight2 > 0)
- {
- str += $"+{BottomLiftSpeed2.ToString(CultureInfo.InvariantCulture)}";
- }
+ str += $"+{BottomLiftSpeed2.ToString(CultureInfo.InvariantCulture)}";
}
- if (haveLiftSpeed)
+ }
+ if (haveLiftSpeed)
+ {
+ if (haveBottomLiftSpeed) str += '/';
+ str += LiftSpeed.ToString(CultureInfo.InvariantCulture);
+ if (haveLiftSpeed2 && haveLiftHeight2 && LiftHeight2 > 0)
{
- if (haveBottomLiftSpeed) str += '/';
- str += LiftSpeed.ToString(CultureInfo.InvariantCulture);
- if (haveLiftSpeed2 && haveLiftHeight2 && LiftHeight2 > 0)
- {
- str += $"+{LiftSpeed2.ToString(CultureInfo.InvariantCulture)}";
- }
+ str += $"+{LiftSpeed2.ToString(CultureInfo.InvariantCulture)}";
}
+ }
- str += "mm/min";
+ str += "mm/min";
- /*// Sequence 2
- if (haveBottomLiftHeight2)
- {
- str += $"\n2th: {BottomLiftHeight2.ToString(CultureInfo.InvariantCulture)}";
- }
- if (haveLiftHeight2)
- {
- str += str.EndsWith("mm/min") ? "\n2th: " : '/';
- str += LiftHeight2.ToString(CultureInfo.InvariantCulture);
- }
+ /*// Sequence 2
+ if (haveBottomLiftHeight2)
+ {
+ str += $"\n2th: {BottomLiftHeight2.ToString(CultureInfo.InvariantCulture)}";
+ }
+ if (haveLiftHeight2)
+ {
+ str += str.EndsWith("mm/min") ? "\n2th: " : '/';
+ str += LiftHeight2.ToString(CultureInfo.InvariantCulture);
+ }
- if (str.EndsWith("mm/min")) return str;
+ if (str.EndsWith("mm/min")) return str;
- str += "mm @ ";
+ str += "mm @ ";
- var haveBottomLiftSpeed2 = CanUseBottomLiftSpeed2;
- var haveLiftSpeed2 = CanUseLiftSpeed2;
- if (haveBottomLiftSpeed2)
- {
- str += BottomLiftSpeed2.ToString(CultureInfo.InvariantCulture);
- }
- if (haveLiftSpeed2)
- {
- if (haveBottomLiftSpeed2) str += '/';
- str += LiftSpeed2.ToString(CultureInfo.InvariantCulture);
- }
+ var haveBottomLiftSpeed2 = CanUseBottomLiftSpeed2;
+ var haveLiftSpeed2 = CanUseLiftSpeed2;
+ if (haveBottomLiftSpeed2)
+ {
+ str += BottomLiftSpeed2.ToString(CultureInfo.InvariantCulture);
+ }
+ if (haveLiftSpeed2)
+ {
+ if (haveBottomLiftSpeed2) str += '/';
+ str += LiftSpeed2.ToString(CultureInfo.InvariantCulture);
+ }
- str += "mm/min";*/
+ str += "mm/min";*/
- return str;
- }
+ return str;
}
+ }
- public string RetractRepresentation
+ public string RetractRepresentation
+ {
+ get
{
- get
- {
- var str = string.Empty;
+ var str = string.Empty;
- var haveBottomRetractHeight = CanUseLiftHeight;
- var haveRetractHeight = CanUseBottomLiftHeight;
- var haveBottomRetractSpeed = CanUseBottomRetractSpeed;
- var haveRetractSpeed = CanUseRetractSpeed;
- var haveBottomRetractHeight2 = CanUseBottomRetractHeight2;
- var haveRetractHeight2 = CanUseRetractHeight2;
- var haveBottomRetractSpeed2 = CanUseBottomRetractSpeed2;
- var haveRetractSpeed2 = CanUseRetractSpeed2;
+ var haveBottomRetractHeight = CanUseLiftHeight;
+ var haveRetractHeight = CanUseBottomLiftHeight;
+ var haveBottomRetractSpeed = CanUseBottomRetractSpeed;
+ var haveRetractSpeed = CanUseRetractSpeed;
+ var haveBottomRetractHeight2 = CanUseBottomRetractHeight2;
+ var haveRetractHeight2 = CanUseRetractHeight2;
+ var haveBottomRetractSpeed2 = CanUseBottomRetractSpeed2;
+ var haveRetractSpeed2 = CanUseRetractSpeed2;
- if (!haveBottomRetractSpeed && !haveRetractSpeed && !haveBottomRetractHeight2 && !haveRetractHeight2) return str;
+ if (!haveBottomRetractSpeed && !haveRetractSpeed && !haveBottomRetractHeight2 && !haveRetractHeight2) return str;
- // Sequence 1
- if (haveBottomRetractHeight)
+ // Sequence 1
+ if (haveBottomRetractHeight)
+ {
+ str += BottomRetractHeight.ToString(CultureInfo.InvariantCulture);
+ if (haveBottomRetractHeight2 && BottomRetractHeight2 > 0)
{
- str += BottomRetractHeight.ToString(CultureInfo.InvariantCulture);
- if (haveBottomRetractHeight2 && BottomRetractHeight2 > 0)
- {
- str += $"+{BottomRetractHeight2.ToString(CultureInfo.InvariantCulture)}";
- }
+ str += $"+{BottomRetractHeight2.ToString(CultureInfo.InvariantCulture)}";
}
- if (haveRetractHeight)
+ }
+ if (haveRetractHeight)
+ {
+ if (!string.IsNullOrEmpty(str)) str += '/';
+ str += RetractHeight.ToString(CultureInfo.InvariantCulture);
+ if (haveRetractHeight2 && RetractHeight2 > 0)
{
- if (!string.IsNullOrEmpty(str)) str += '/';
- str += RetractHeight.ToString(CultureInfo.InvariantCulture);
- if (haveRetractHeight2 && RetractHeight2 > 0)
- {
- str += $"+{RetractHeight2.ToString(CultureInfo.InvariantCulture)}";
- }
+ str += $"+{RetractHeight2.ToString(CultureInfo.InvariantCulture)}";
}
+ }
- if (string.IsNullOrEmpty(str)) return str;
+ if (string.IsNullOrEmpty(str)) return str;
- str += "mm @ ";
+ str += "mm @ ";
- if (haveBottomRetractSpeed)
+ if (haveBottomRetractSpeed)
+ {
+ str += BottomRetractSpeed.ToString(CultureInfo.InvariantCulture);
+ if (haveBottomRetractSpeed2 && haveBottomRetractHeight2 && BottomRetractHeight2 > 0)
{
- str += BottomRetractSpeed.ToString(CultureInfo.InvariantCulture);
- if (haveBottomRetractSpeed2 && haveBottomRetractHeight2 && BottomRetractHeight2 > 0)
- {
- str += $"+{BottomRetractSpeed2.ToString(CultureInfo.InvariantCulture)}";
- }
+ str += $"+{BottomRetractSpeed2.ToString(CultureInfo.InvariantCulture)}";
}
- if (haveRetractSpeed)
+ }
+ if (haveRetractSpeed)
+ {
+ if (haveBottomRetractSpeed) str += '/';
+ str += RetractSpeed.ToString(CultureInfo.InvariantCulture);
+ if (haveRetractSpeed2 && haveRetractHeight2 && RetractHeight2 > 0)
{
- if (haveBottomRetractSpeed) str += '/';
- str += RetractSpeed.ToString(CultureInfo.InvariantCulture);
- if (haveRetractSpeed2 && haveRetractHeight2 && RetractHeight2 > 0)
- {
- str += $"+{RetractSpeed2.ToString(CultureInfo.InvariantCulture)}";
- }
+ str += $"+{RetractSpeed2.ToString(CultureInfo.InvariantCulture)}";
}
+ }
- str += "mm/min";
+ str += "mm/min";
- // Sequence 2
- /*if (haveBottomRetractHeight2)
- {
- str += $"\n2th: {BottomRetractHeight2.ToString(CultureInfo.InvariantCulture)}";
- }
- if (haveRetractHeight2)
- {
- str += str.EndsWith("mm/min") ? "\n2th: " : '/';
- str += RetractHeight2.ToString(CultureInfo.InvariantCulture);
- }
+ // Sequence 2
+ /*if (haveBottomRetractHeight2)
+ {
+ str += $"\n2th: {BottomRetractHeight2.ToString(CultureInfo.InvariantCulture)}";
+ }
+ if (haveRetractHeight2)
+ {
+ str += str.EndsWith("mm/min") ? "\n2th: " : '/';
+ str += RetractHeight2.ToString(CultureInfo.InvariantCulture);
+ }
- if (str.EndsWith("mm/min")) return str;
+ if (str.EndsWith("mm/min")) return str;
- str += "mm @ ";
+ str += "mm @ ";
- if (haveBottomRetractSpeed2)
- {
- str += BottomRetractSpeed2.ToString(CultureInfo.InvariantCulture);
- }
- if (haveRetractSpeed2)
- {
- if (haveBottomRetractSpeed2) str += '/';
- str += RetractSpeed2.ToString(CultureInfo.InvariantCulture);
- }
+ if (haveBottomRetractSpeed2)
+ {
+ str += BottomRetractSpeed2.ToString(CultureInfo.InvariantCulture);
+ }
+ if (haveRetractSpeed2)
+ {
+ if (haveBottomRetractSpeed2) str += '/';
+ str += RetractSpeed2.ToString(CultureInfo.InvariantCulture);
+ }
- str += "mm/min";*/
+ str += "mm/min";*/
- return str;
- }
+ return str;
}
+ }
- public string LightOffDelayRepresentation
+ public string LightOffDelayRepresentation
+ {
+ get
{
- get
- {
- var str = string.Empty;
+ var str = string.Empty;
- if (CanUseBottomLightOffDelay)
- {
- str += BottomLightOffDelay.ToString(CultureInfo.InvariantCulture);
- }
- if (CanUseLightOffDelay)
- {
- if (!string.IsNullOrEmpty(str)) str += '/';
- str += LightOffDelay.ToString(CultureInfo.InvariantCulture);
- }
+ if (CanUseBottomLightOffDelay)
+ {
+ str += BottomLightOffDelay.ToString(CultureInfo.InvariantCulture);
+ }
+ if (CanUseLightOffDelay)
+ {
+ if (!string.IsNullOrEmpty(str)) str += '/';
+ str += LightOffDelay.ToString(CultureInfo.InvariantCulture);
+ }
- if (!string.IsNullOrEmpty(str)) str += 's';
+ if (!string.IsNullOrEmpty(str)) str += 's';
- return str;
- }
+ return str;
}
+ }
- public string WaitTimeRepresentation
+ public string WaitTimeRepresentation
+ {
+ get
{
- get
- {
- var str = string.Empty;
+ var str = string.Empty;
- if (CanUseBottomWaitTimeBeforeCure || CanUseBottomWaitTimeAfterCure || CanUseBottomWaitTimeAfterLift)
- {
- str += $"{BottomWaitTimeBeforeCure}/{BottomWaitTimeAfterCure}/{BottomWaitTimeAfterLift}s";
- }
- if (!string.IsNullOrEmpty(str)) str += "|";
- if (CanUseWaitTimeBeforeCure || CanUseWaitTimeAfterCure || CanUseWaitTimeAfterLift)
- {
- str += $"{WaitTimeBeforeCure}/{WaitTimeAfterCure}/{WaitTimeAfterLift}s";
- }
-
- return str;
+ if (CanUseBottomWaitTimeBeforeCure || CanUseBottomWaitTimeAfterCure || CanUseBottomWaitTimeAfterLift)
+ {
+ str += $"{BottomWaitTimeBeforeCure}/{BottomWaitTimeAfterCure}/{BottomWaitTimeAfterLift}s";
+ }
+ if (!string.IsNullOrEmpty(str)) str += "|";
+ if (CanUseWaitTimeBeforeCure || CanUseWaitTimeAfterCure || CanUseWaitTimeAfterLift)
+ {
+ str += $"{WaitTimeBeforeCure}/{WaitTimeAfterCure}/{WaitTimeAfterLift}s";
}
- }
- public IEnumerable<IEnumerable<int>> BatchLayersIndexes(int batchSize = 0)
- {
- if (batchSize <= 0) batchSize = Environment.ProcessorCount * 10;
- return MoreLinq.MoreEnumerable.Batch(Enumerable.Range(0, (int)LayerCount), batchSize);
+ return str;
}
+ }
- public IEnumerable<IEnumerable<Layer>> BatchLayers(int batchSize = 0)
- {
- if (batchSize <= 0) batchSize = Environment.ProcessorCount * 10;
- return MoreLinq.MoreEnumerable.Batch(this, batchSize);
- }
+ public IEnumerable<IEnumerable<int>> BatchLayersIndexes(int batchSize = 0)
+ {
+ if (batchSize <= 0) batchSize = Environment.ProcessorCount * 10;
+ return Enumerable.Range(0, (int) LayerCount).Chunk(batchSize);
+ }
- #endregion
+ public IEnumerable<IEnumerable<Layer>> BatchLayers(int batchSize = 0)
+ {
+ if (batchSize <= 0) batchSize = Environment.ProcessorCount * 10;
+ return this.Chunk(batchSize);
+ }
- /// <summary>
- /// Gets the estimate print time in seconds
- /// </summary>
- public virtual float PrintTime
+ #endregion
+
+ /// <summary>
+ /// Gets the estimate print time in seconds
+ /// </summary>
+ public virtual float PrintTime
+ {
+ get
{
- get
+ if (_printTime <= 0)
{
- if (_printTime <= 0)
- {
- _printTime = PrintTimeComputed;
- }
- return _printTime;
- }
- set
+ _printTime = PrintTimeComputed;
+ }
+ return _printTime;
+ }
+ set
+ {
+ if (value <= 0)
{
- if (value <= 0)
- {
- value = PrintTimeComputed;
- }
- if(!RaiseAndSetIfChanged(ref _printTime, value)) return;
- RaisePropertyChanged(nameof(PrintTimeHours));
- RaisePropertyChanged(nameof(PrintTimeString));
+ value = PrintTimeComputed;
}
+ if(!RaiseAndSetIfChanged(ref _printTime, value)) return;
+ RaisePropertyChanged(nameof(PrintTimeHours));
+ RaisePropertyChanged(nameof(PrintTimeString));
}
+ }
- /// <summary>
- /// Gets the calculated estimate print time in seconds
- /// </summary>
- public float PrintTimeComputed
+ /// <summary>
+ /// Gets the calculated estimate print time in seconds
+ /// </summary>
+ public float PrintTimeComputed
+ {
+ get
{
- get
+ if (LayerCount == 0) return 0;
+ float time = ExtraPrintTime;
+ bool computeGeneral = _layers is null;
+ if (!computeGeneral)
{
- if (LayerCount == 0) return 0;
- float time = ExtraPrintTime;
- bool computeGeneral = LayerManager is null;
- if (!computeGeneral)
+ foreach (var layer in this)
{
- foreach (var layer in this)
+ if (layer is null)
{
- if (layer is null)
- {
- computeGeneral = true;
- break;
- }
-
- var motorTime = layer.CalculateMotorMovementTime();
- time += layer.WaitTimeBeforeCure + layer.ExposureTime + layer.WaitTimeAfterCure + layer.WaitTimeAfterLift;
- if (SupportsGCode)
- {
- time += motorTime;
- if (layer.WaitTimeBeforeCure <= 0)
- {
- time += layer.LightOffDelay;
- }
- }
- else
- {
- time += motorTime > layer.LightOffDelay ? motorTime : layer.LightOffDelay;
- }
- /*if (lightOffDelay >= layer.LightOffDelay)
- time += lightOffDelay;
- else
- time += layer.LightOffDelay;*/
+ computeGeneral = true;
+ break;
}
- }
-
- if (computeGeneral)
- {
- var bottomMotorTime = CalculateMotorMovementTime(true);
- var motorTime = CalculateMotorMovementTime(false);
- time = ExtraPrintTime +
- BottomLightOffDelay * BottomLayerCount +
- LightOffDelay * NormalLayerCount +
- BottomWaitTimeBeforeCure * BottomLayerCount +
- WaitTimeBeforeCure * NormalLayerCount +
- BottomExposureTime * BottomLayerCount +
- ExposureTime * NormalLayerCount +
- BottomWaitTimeAfterCure * BottomLayerCount +
- WaitTimeAfterCure * NormalLayerCount +
- BottomWaitTimeAfterLift * BottomLayerCount +
- WaitTimeAfterLift * NormalLayerCount;
+ var motorTime = layer.CalculateMotorMovementTime();
+ time += layer.WaitTimeBeforeCure + layer.ExposureTime + layer.WaitTimeAfterCure + layer.WaitTimeAfterLift;
if (SupportsGCode)
{
- time += bottomMotorTime * BottomLayerCount + motorTime * NormalLayerCount;
-
- if (BottomWaitTimeBeforeCure <= 0)
- {
- time += BottomLightOffDelay * BottomLayerCount;
- }
- if (WaitTimeBeforeCure <= 0)
+ time += motorTime;
+ if (layer.WaitTimeBeforeCure <= 0)
{
- time += LightOffDelay * NormalLayerCount;
+ time += layer.LightOffDelay;
}
}
else
{
- time += motorTime > BottomLightOffDelay ? bottomMotorTime * BottomLayerCount : BottomLightOffDelay * BottomLayerCount;
- time += motorTime > LightOffDelay ? motorTime * NormalLayerCount : LightOffDelay * NormalLayerCount;
+ time += motorTime > layer.LightOffDelay ? motorTime : layer.LightOffDelay;
}
+ /*if (lightOffDelay >= layer.LightOffDelay)
+ time += lightOffDelay;
+ else
+ time += layer.LightOffDelay;*/
}
-
- return (float) Math.Round(time, 2);
}
- }
-
- /// <summary>
- /// Gets the estimate print time in hours
- /// </summary>
- public float PrintTimeHours => (float) Math.Round(PrintTime / 3600, 2);
- /// <summary>
- /// Gets the estimate print time in hours and minutes formatted
- /// </summary>
- public string PrintTimeString
- {
- get
+ if (computeGeneral)
{
- var printTime = PrintTime;
- return TimeSpan.FromSeconds(printTime >= float.PositiveInfinity ? 0 : printTime).ToString("hh\\hmm\\m");
- }
- }
+ var bottomMotorTime = CalculateMotorMovementTime(true);
+ var motorTime = CalculateMotorMovementTime(false);
+ time = ExtraPrintTime +
+ BottomLightOffDelay * BottomLayerCount +
+ LightOffDelay * NormalLayerCount +
+ BottomWaitTimeBeforeCure * BottomLayerCount +
+ WaitTimeBeforeCure * NormalLayerCount +
+ BottomExposureTime * BottomLayerCount +
+ ExposureTime * NormalLayerCount +
+ BottomWaitTimeAfterCure * BottomLayerCount +
+ WaitTimeAfterCure * NormalLayerCount +
+ BottomWaitTimeAfterLift * BottomLayerCount +
+ WaitTimeAfterLift * NormalLayerCount;
- /// <summary>
- /// Gets the estimate used material in ml
- /// </summary>
- public virtual float MaterialMilliliters {
- get => _materialMilliliters;
- set
- {
- if (value <= 0)
+ if (SupportsGCode)
{
- value = (float)Math.Round(this.Where(layer => layer is not null).Sum(layer => layer.MaterialMilliliters), 3);
+ time += bottomMotorTime * BottomLayerCount + motorTime * NormalLayerCount;
+
+ if (BottomWaitTimeBeforeCure <= 0)
+ {
+ time += BottomLightOffDelay * BottomLayerCount;
+ }
+ if (WaitTimeBeforeCure <= 0)
+ {
+ time += LightOffDelay * NormalLayerCount;
+ }
}
else
{
- value = (float)Math.Round(value, 3);
+ time += motorTime > BottomLightOffDelay ? bottomMotorTime * BottomLayerCount : BottomLightOffDelay * BottomLayerCount;
+ time += motorTime > LightOffDelay ? motorTime * NormalLayerCount : LightOffDelay * NormalLayerCount;
}
-
- RaiseAndSetIfChanged(ref _materialMilliliters, value);
}
- }
- //public float MaterialMillilitersComputed =>
-
-
- /// <summary>
- /// Gets the estimate material in grams
- /// </summary>
- public virtual float MaterialGrams
- {
- get => _materialGrams;
- set => RaiseAndSetIfChanged(ref _materialGrams, value);
+ return (float) Math.Round(time, 2);
}
+ }
- /// <summary>
- /// Gets the estimate material cost
- /// </summary>
- public virtual float MaterialCost
- {
- get => _materialCost;
- set => RaiseAndSetIfChanged(ref _materialCost, value);
- }
+ /// <summary>
+ /// Gets the estimate print time in hours
+ /// </summary>
+ public float PrintTimeHours => (float) Math.Round(PrintTime / 3600, 2);
- /// <summary>
- /// Gets the material name
- /// </summary>
- public virtual string MaterialName
+ /// <summary>
+ /// Gets the estimate print time in hours and minutes formatted
+ /// </summary>
+ public string PrintTimeString
+ {
+ get
{
- get => _materialName;
- set => RaiseAndSetIfChanged(ref _materialName, value);
+ var printTime = PrintTime;
+ return TimeSpan.FromSeconds(printTime >= float.PositiveInfinity ? 0 : printTime).ToString("hh\\hmm\\m");
}
+ }
- /// <summary>
- /// Gets the machine name
- /// </summary>
- public virtual string MachineName
+ /// <summary>
+ /// Gets the estimate used material in ml
+ /// </summary>
+ public virtual float MaterialMilliliters {
+ get => _materialMilliliters;
+ set
{
- get => _machineName;
- set
+ if (value <= 0)
+ {
+ value = (float)Math.Round(this.Where(layer => layer is not null).Sum(layer => layer.MaterialMilliliters), 3);
+ }
+ else
{
- if(!RaiseAndSetIfChanged(ref _machineName, value)) return;
- if(FileType == FileFormatType.Binary) RequireFullEncode = true;
+ value = (float)Math.Round(value, 3);
}
+ RaiseAndSetIfChanged(ref _materialMilliliters, value);
}
+ }
- /// <summary>
- /// Gets the GCode, returns null if not supported
- /// </summary>
- public GCodeBuilder GCode { get; set; }
+ //public float MaterialMillilitersComputed =>
- /// <summary>
- /// Gets the GCode, returns null if not supported
- /// </summary>
- public string GCodeStr
+
+ /// <summary>
+ /// Gets the estimate material in grams
+ /// </summary>
+ public virtual float MaterialGrams
+ {
+ get => _materialGrams;
+ set => RaiseAndSetIfChanged(ref _materialGrams, value);
+ }
+
+ /// <summary>
+ /// Gets the estimate material cost
+ /// </summary>
+ public virtual float MaterialCost
+ {
+ get => _materialCost;
+ set => RaiseAndSetIfChanged(ref _materialCost, value);
+ }
+
+ /// <summary>
+ /// Gets the material name
+ /// </summary>
+ public virtual string? MaterialName
+ {
+ get => _materialName;
+ set => RaiseAndSetIfChanged(ref _materialName, value);
+ }
+
+ /// <summary>
+ /// Gets the machine name
+ /// </summary>
+ public virtual string MachineName
+ {
+ get => _machineName;
+ set
{
- get => GCode?.ToString();
- set
- {
- GCode.Clear();
- if (!string.IsNullOrWhiteSpace(value))
- {
- GCode.Append(value);
- }
- RaisePropertyChanged();
-
- }
+ if(!RaiseAndSetIfChanged(ref _machineName, value)) return;
+ if(FileType == FileFormatType.Binary) RequireFullEncode = true;
}
- /// <summary>
- /// Gets if this file format supports gcode
- /// </summary>
- public virtual bool SupportsGCode => GCode is not null;
+ }
- /// <summary>
- /// Gets if this file have available gcode to read
- /// </summary>
- public bool HaveGCode => SupportsGCode && !GCode.IsEmpty;
+ /// <summary>
+ /// Gets the GCode, returns null if not supported
+ /// </summary>
+ public GCodeBuilder? GCode { get; set; }
- /// <summary>
- /// Disable or enable the gcode auto rebuild when needed, set this to false to manually write your own gcode
- /// </summary>
- public bool SuppressRebuildGCode
+ /// <summary>
+ /// Gets the GCode, returns null if not supported
+ /// </summary>
+ public string? GCodeStr
+ {
+ get => GCode?.ToString();
+ set
{
- get => _suppressRebuildGCode;
- set => RaiseAndSetIfChanged(ref _suppressRebuildGCode, value);
+ if (GCode is null) return;
+ GCode.Clear();
+ if (!string.IsNullOrWhiteSpace(value))
+ {
+ GCode.Append(value);
+ }
+ RaisePropertyChanged();
+
}
+ }
- /// <summary>
- /// Get all configuration objects with properties and values
- /// </summary>
- public abstract object[] Configs { get; }
+ /// <summary>
+ /// Gets if this file format supports gcode
+ /// </summary>
+ public virtual bool SupportsGCode => GCode is not null;
- /// <summary>
- /// Gets if this file is valid to read
- /// </summary>
- public bool IsValid => FileFullPath is not null;
- #endregion
+ /// <summary>
+ /// Gets if this file have available gcode to read
+ /// </summary>
+ public bool HaveGCode => SupportsGCode && !GCode!.IsEmpty;
- #region Constructor
- protected FileFormat()
- {
- LayerManager = new(this);
- IssueManager = new(this);
- Thumbnails = new Mat[ThumbnailsCount];
- PropertyChanged += OnPropertyChanged;
- _queueTimerPrintTime.Elapsed += (sender, e) => UpdatePrintTime();
- }
-
- private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- if (SuppressRebuildProperties) return;
- if (
- e.PropertyName
- is nameof(BottomLayerCount)
- or nameof(BottomLightOffDelay)
- or nameof(LightOffDelay)
- or nameof(BottomWaitTimeBeforeCure)
- or nameof(WaitTimeBeforeCure)
- or nameof(BottomExposureTime)
- or nameof(ExposureTime)
- or nameof(BottomWaitTimeAfterCure)
- or nameof(WaitTimeAfterCure)
- or nameof(BottomLiftHeight)
- or nameof(BottomLiftSpeed)
- or nameof(LiftHeight)
- or nameof(LiftSpeed)
- or nameof(BottomLiftHeight2)
- or nameof(BottomLiftSpeed2)
- or nameof(LiftHeight2)
- or nameof(LiftSpeed2)
- or nameof(BottomWaitTimeAfterLift)
- or nameof(WaitTimeAfterLift)
- or nameof(BottomRetractSpeed)
- or nameof(RetractSpeed)
- or nameof(BottomRetractHeight2)
- or nameof(BottomRetractSpeed2)
- or nameof(RetractHeight2)
- or nameof(RetractSpeed2)
- or nameof(BottomLightPWM)
- or nameof(LightPWM)
- )
- {
- LayerManager.RebuildLayersProperties(false, e.PropertyName);
- if(e.PropertyName
+ /// <summary>
+ /// Disable or enable the gcode auto rebuild when needed, set this to false to manually write your own gcode
+ /// </summary>
+ public bool SuppressRebuildGCode
+ {
+ get => _suppressRebuildGCode;
+ set => RaiseAndSetIfChanged(ref _suppressRebuildGCode, value);
+ }
+
+ /// <summary>
+ /// Get all configuration objects with properties and values
+ /// </summary>
+ public virtual object[] Configs => Array.Empty<object>();
+
+ /// <summary>
+ /// Gets if this file is valid to read
+ /// </summary>
+ public bool IsValid => FileFullPath is not null;
+ #endregion
+
+ #region Constructor
+ protected FileFormat()
+ {
+ IssueManager = new(this);
+ Thumbnails = new Mat[ThumbnailsCount];
+ PropertyChanged += OnPropertyChanged;
+ _queueTimerPrintTime.Elapsed += (sender, e) => UpdatePrintTime();
+ }
+
+ private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (SuppressRebuildProperties) return;
+ if (
+ e.PropertyName
+ is nameof(BottomLayerCount)
+ or nameof(BottomLightOffDelay)
+ or nameof(LightOffDelay)
+ or nameof(BottomWaitTimeBeforeCure)
+ or nameof(WaitTimeBeforeCure)
+ or nameof(BottomExposureTime)
+ or nameof(ExposureTime)
+ or nameof(BottomWaitTimeAfterCure)
+ or nameof(WaitTimeAfterCure)
+ or nameof(BottomLiftHeight)
+ or nameof(BottomLiftSpeed)
+ or nameof(LiftHeight)
+ or nameof(LiftSpeed)
+ or nameof(BottomLiftHeight2)
+ or nameof(BottomLiftSpeed2)
+ or nameof(LiftHeight2)
+ or nameof(LiftSpeed2)
+ or nameof(BottomWaitTimeAfterLift)
+ or nameof(WaitTimeAfterLift)
+ or nameof(BottomRetractSpeed)
+ or nameof(RetractSpeed)
+ or nameof(BottomRetractHeight2)
+ or nameof(BottomRetractSpeed2)
+ or nameof(RetractHeight2)
+ or nameof(RetractSpeed2)
+ or nameof(BottomLightPWM)
+ or nameof(LightPWM)
+ )
+ {
+ RebuildLayersProperties(false, e.PropertyName);
+ if(e.PropertyName
is nameof(BottomLayerCount)
or nameof(BottomExposureTime)
or nameof(ExposureTime)
- && TransitionLayerType == TransitionLayerTypes.Software
- ) ResetCurrentTransitionLayers(false);
+ && TransitionLayerType == TransitionLayerTypes.Software
+ ) ResetCurrentTransitionLayers(false);
- if(e.PropertyName
- is not nameof(BottomLightPWM)
- and not nameof(LightPWM)
- ) UpdatePrintTimeQueued();
+ if(e.PropertyName
+ is not nameof(BottomLightPWM)
+ and not nameof(LightPWM)
+ ) UpdatePrintTimeQueued();
- return;
- }
-
- // Fix transition layers times in software mode
- if (e.PropertyName is nameof(TransitionLayerCount) && TransitionLayerType == TransitionLayerTypes.Software)
- {
- ResetCurrentTransitionLayers();
- return;
- }
+ return;
}
- #endregion
-
- #region Indexers
- public Layer this[int index]
+ // Fix transition layers times in software mode
+ if (e.PropertyName is nameof(TransitionLayerCount) && TransitionLayerType == TransitionLayerTypes.Software)
{
- get => LayerManager[index];
- set => LayerManager[index] = value;
+ ResetCurrentTransitionLayers();
+ return;
}
+ }
+
+ #endregion
+
+ #region Indexers
+ public Layer this[uint index]
+ {
+ get => _layers[index];
+ set => SetLayer(index, value);
+ }
+
+ public Layer this[int index]
+ {
+ get => _layers[index];
+ set => SetLayer((uint)index, value);
+ }
+
+ public Layer this[long index]
+ {
+ get => _layers[index];
+ set => SetLayer((uint)index, value);
+ }
+
+ public Layer[] this[Range range] => _layers[range];
- public Layer this[uint index]
+ /// <summary>
+ /// Sets a layer
+ /// </summary>
+ /// <param name="index">Layer index</param>
+ /// <param name="layer">Layer to add</param>
+ /// <param name="makeClone">True to add a clone of the layer</param>
+ public void SetLayer(uint index, Layer layer, bool makeClone = false)
+ {
+ if (index >= LayerCount) return;
+ layer.IsModified = true;
+ _layers[index] = makeClone ? layer.Clone() : layer;
+ layer.Index = index;
+ layer.SlicerFile = this;
+ }
+
+ /// <summary>
+ /// Add a list of layers
+ /// </summary>
+ /// <param name="layers">Layers to add</param>
+ /// <param name="makeClone">True to add a clone of layers</param>
+ public void SetLayers(IEnumerable<Layer> layers, bool makeClone = false)
+ {
+ foreach (var layer in layers)
{
- get => LayerManager[index];
- set => LayerManager[index] = value;
+ SetLayer(layer.Index, layer, makeClone);
}
+ }
+
+ /// <summary>
+ /// Get layer given index
+ /// </summary>
+ /// <param name="index">Layer index</param>
+ /// <returns></returns>
+ public Layer GetLayer(uint index)
+ {
+ return _layers[index];
+ }
+
+ #endregion
+
+ #region Numerators
+ public IEnumerator<Layer> GetEnumerator()
+ {
+ return ((IEnumerable<Layer>)Layers).GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ #endregion
- public Layer this[long index]
+ #region Overrides
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as FileFormat);
+ }
+
+ public bool Equals(FileFormat? other)
+ {
+ if (other is null) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return FileFullPath == other.FileFullPath;
+ }
+
+ public override int GetHashCode()
+ {
+ return (FileFullPath != null ? FileFullPath.GetHashCode() : 0);
+ }
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ _queueTimerPrintTime.Dispose();
+ Clear();
+ }
+
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Clears all definitions and properties, it also dispose valid candidates
+ /// </summary>
+ public virtual void Clear()
+ {
+ FileFullPath = null;
+ _layers = Array.Empty<Layer>();
+ GCode?.Clear();
+
+ if (Thumbnails is not null)
{
- get => LayerManager[index];
- set => LayerManager[index] = value;
+ for (int i = 0; i < Thumbnails.Length; i++)
+ {
+ Thumbnails[i]?.Dispose();
+ }
}
+ }
- public Layer[] this[Range range] => LayerManager[range];
+ /// <summary>
+ /// Check if a file is valid and can be processed before read it against the <see cref="FileFormat"/> decode scheme
+ /// </summary>
+ /// <param name="fileFullPath"></param>
+ /// <returns></returns>
+ public virtual bool CanProcess(string fileFullPath)
+ {
+ if (fileFullPath is null) return false;
+ if (!File.Exists(fileFullPath)) return false;
+ //if (!IsExtensionValid(fileFullPath, true)) return false;
+ return true;
+ }
- #endregion
- #region Numerators
- public IEnumerator<Layer> GetEnumerator()
+ /// <summary>
+ /// Validate if a file is a valid <see cref="FileFormat"/>
+ /// </summary>
+ /// <param name="fileFullPath">Full file path</param>
+ public void FileValidation(string fileFullPath)
+ {
+ if (string.IsNullOrWhiteSpace(fileFullPath)) throw new ArgumentNullException(nameof(FileFullPath), "FileFullPath can't be null nor empty.");
+ if (!File.Exists(fileFullPath)) throw new FileNotFoundException("The specified file does not exists.", fileFullPath);
+
+ if (IsExtensionValid(fileFullPath, true))
{
- return LayerManager.GetEnumerator();
+ return;
}
- IEnumerator IEnumerable.GetEnumerator()
+ throw new FileLoadException($"The specified file is not valid.", fileFullPath);
+ }
+
+ /// <summary>
+ /// Checks if a extension is valid under the <see cref="FileFormat"/>
+ /// </summary>
+ /// <param name="extension">Extension to check without the dot (.)</param>
+ /// <param name="isFilePath">True if <see cref="extension"/> is a full file path, otherwise false for extension only</param>
+ /// <returns>True if valid, otherwise false</returns>
+ public bool IsExtensionValid(string extension, bool isFilePath = false)
+ {
+ if (isFilePath)
{
- return GetEnumerator();
+ GetFileNameStripExtensions(extension, out extension);
}
- #endregion
+ return !string.IsNullOrWhiteSpace(extension) && FileExtensions.Any(fileExtension => fileExtension.Equals(extension));
+ }
- #region Overrides
- public override bool Equals(object obj)
+ /// <summary>
+ /// Gets all valid file extensions in a specified format
+ /// </summary>
+ public string GetFileExtensions(string prepend = ".", string separator = ", ")
+ {
+ var result = string.Empty;
+
+ foreach (var fileExt in FileExtensions)
{
- return Equals(obj as FileFormat);
+ if (!ReferenceEquals(result, string.Empty))
+ {
+ result += separator;
+ }
+ result += $"{prepend}{fileExt.Extension}";
}
- public bool Equals(FileFormat other)
+ return result;
+ }
+
+ public bool FileEndsWith(string extension)
+ {
+ if (FileFullPath is null) return false;
+ if (extension[0] != '.') extension = $".{extension}";
+ return FileFullPath.EndsWith(extension, StringComparison.OrdinalIgnoreCase) ||
+ FileFullPath.EndsWith($"{extension}{TemporaryFileAppend}", StringComparison.OrdinalIgnoreCase);
+ }
+
+ /// <summary>
+ /// Gets a thumbnail by it height or lower
+ /// </summary>
+ /// <param name="maxHeight">Max height allowed</param>
+ /// <returns></returns>
+ public Mat? GetThumbnail(uint maxHeight = 400)
+ {
+ for (int i = 0; i < ThumbnailsCount; i++)
{
- if (other is null) return false;
- if (ReferenceEquals(this, other)) return true;
- return FileFullPath == other.FileFullPath;
+ if(Thumbnails[i] is null) continue;
+ if (Thumbnails[i]!.Height <= maxHeight) return Thumbnails[i];
}
- public override int GetHashCode()
- {
- return (FileFullPath != null ? FileFullPath.GetHashCode() : 0);
+ return null;
+ }
+
+ /// <summary>
+ /// Gets a thumbnail by the largest or smallest
+ /// </summary>
+ /// <param name="largest">True to get the largest, otherwise false</param>
+ /// <returns></returns>
+ public Mat? GetThumbnail(bool largest)
+ {
+ switch (CreatedThumbnailsCount)
+ {
+ case 0:
+ return null;
+ case 1:
+ return Thumbnails[0];
+ default:
+ if (largest)
+ {
+ return Thumbnails[0]!.Size.Area() >= Thumbnails[1]!.Size.Area() ? Thumbnails[0] : Thumbnails[1];
+ }
+ else
+ {
+ return Thumbnails[0]!.Size.Area() <= Thumbnails[1]!.Size.Area() ? Thumbnails[0] : Thumbnails[1];
+ }
}
+ }
- public void Dispose()
+ /// <summary>
+ /// Sets thumbnails from a list of thumbnails and clone them
+ /// </summary>
+ /// <param name="images"></param>
+ public void SetThumbnails(Mat?[] images)
+ {
+ if (images.Length == 0) return;
+ byte imageIndex = 0;
+ for (int i = 0; i < ThumbnailsCount; i++)
{
- GC.SuppressFinalize(this);
- _queueTimerPrintTime.Dispose();
- Clear();
+ var image = images[Math.Min(imageIndex++, images.Length - 1)];
+ if (image is null || image.IsEmpty)
+ {
+ if (imageIndex >= images.Length) break;
+ i--;
+ continue;
+ }
+
+ Thumbnails[i] = image.Clone();
+ if (Thumbnails[i]!.Size != ThumbnailsOriginalSize![i])
+ {
+ CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]);
+ }
}
- #endregion
+ RaisePropertyChanged(nameof(Thumbnails));
+ }
- #region Methods
- /// <summary>
- /// Clears all definitions and properties, it also dispose valid candidates
- /// </summary>
- public virtual void Clear()
+ /// <summary>
+ /// Sets all thumbnails the same image
+ /// </summary>
+ /// <param name="image">Image to set</param>
+ public void SetThumbnails(Mat image)
+ {
+ if (image.IsEmpty) return;
+ for (var i = 0; i < ThumbnailsCount; i++)
{
- FileFullPath = null;
- LayerManager.Clear();
- GCode?.Clear();
-
- if (Thumbnails is not null)
+ Thumbnails[i] = image.Clone();
+ if (ThumbnailsOriginalSize is null || i >= ThumbnailsOriginalSize.Length) continue;
+ if (Thumbnails[i]!.Size != ThumbnailsOriginalSize[i])
{
- for (int i = 0; i < Thumbnails.Length; i++)
- {
- Thumbnails[i]?.Dispose();
- }
+ CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]);
}
}
+ RaisePropertyChanged(nameof(Thumbnails));
+ }
- /// <summary>
- /// Check if a file is valid and can be processed before read it against the <see cref="FileFormat"/> decode scheme
- /// </summary>
- /// <param name="fileFullPath"></param>
- /// <returns></returns>
- public virtual bool CanProcess(string fileFullPath)
+ /// <summary>
+ /// Sets a thumbnail from a disk file
+ /// </summary>
+ /// <param name="index">Thumbnail index</param>
+ /// <param name="filePath"></param>
+ public void SetThumbnail(int index, string filePath)
+ {
+ if (index >= Thumbnails.Length) return;
+ Thumbnails[index] = CvInvoke.Imread(filePath, ImreadModes.AnyColor);
+ if (Thumbnails[index]!.Size != ThumbnailsOriginalSize![index])
{
- if (fileFullPath is null) return false;
- if (!File.Exists(fileFullPath)) return false;
- //if (!IsExtensionValid(fileFullPath, true)) return false;
- return true;
+ CvInvoke.Resize(Thumbnails[index], Thumbnails[index], ThumbnailsOriginalSize[index]);
}
+ RaisePropertyChanged(nameof(Thumbnails));
+ }
+ /// <summary>
+ /// Encode to an output file
+ /// </summary>
+ /// <param name="progress"></param>
+ protected abstract void EncodeInternally(OperationProgress progress);
- /// <summary>
- /// Validate if a file is a valid <see cref="FileFormat"/>
- /// </summary>
- /// <param name="fileFullPath">Full file path</param>
- public void FileValidation(string fileFullPath)
+ /// <summary>
+ /// Encode to an output file
+ /// </summary>
+ /// <param name="fileFullPath">Output file</param>
+ /// <param name="progress"></param>
+ public void Encode(string? fileFullPath, OperationProgress? progress = null)
+ {
+ if (fileFullPath is null)
{
- if (string.IsNullOrWhiteSpace(fileFullPath)) throw new ArgumentNullException(nameof(FileFullPath), "FileFullPath can't be null nor empty.");
- if (!File.Exists(fileFullPath)) throw new FileNotFoundException("The specified file does not exists.", fileFullPath);
+ throw new ArgumentNullException(nameof(fileFullPath));
+ }
- if (IsExtensionValid(fileFullPath, true))
- {
- return;
- }
+ if (DecodeType == FileDecodeType.Partial)
+ {
+ throw new InvalidOperationException("File was partial decoded, a full encode is not possible.");
+ }
- throw new FileLoadException($"The specified file is not valid.", fileFullPath);
+ progress ??= new OperationProgress();
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+
+ Sanitize();
+
+ if (File.Exists(fileFullPath)) File.Delete(fileFullPath);
+
+ FileFullPath = fileFullPath;
+
+ for (var i = 0; i < Thumbnails.Length; i++)
+ {
+ if (Thumbnails[i] is null || Thumbnails[i]!.IsEmpty) continue;
+ if(Thumbnails[i]!.Size == ThumbnailsOriginalSize![i]) continue;
+ CvInvoke.Resize(Thumbnails[i], Thumbnails[i], new Size(ThumbnailsOriginalSize[i].Width, ThumbnailsOriginalSize[i].Height));
}
- /// <summary>
- /// Checks if a extension is valid under the <see cref="FileFormat"/>
- /// </summary>
- /// <param name="extension">Extension to check without the dot (.)</param>
- /// <param name="isFilePath">True if <see cref="extension"/> is a full file path, otherwise false for extension only</param>
- /// <returns>True if valid, otherwise false</returns>
- public bool IsExtensionValid(string extension, bool isFilePath = false)
+ EncodeInternally(progress);
+
+ IsModified = false;
+ RequireFullEncode = false;
+ }
+
+ /// <summary>
+ /// Decode a slicer file
+ /// </summary>
+ /// <param name="progress"></param>
+ protected abstract void DecodeInternally(OperationProgress progress);
+
+ /// <summary>
+ /// Decode a slicer file
+ /// </summary>
+ /// <param name="fileFullPath"></param>
+ /// <param name="progress"></param>
+ public void Decode(string fileFullPath, OperationProgress? progress = null) => Decode(fileFullPath, FileDecodeType.Full, progress);
+
+ /// <summary>
+ /// Decode a slicer file
+ /// </summary>
+ /// <param name="fileFullPath"></param>
+ /// <param name="fileDecodeType"></param>
+ /// <param name="progress"></param>
+ public void Decode(string fileFullPath, FileDecodeType fileDecodeType, OperationProgress? progress = null)
+ {
+ Clear();
+ FileValidation(fileFullPath);
+ FileFullPath = fileFullPath;
+ DecodeType = fileDecodeType;
+ progress ??= new OperationProgress();
+ progress.Reset(OperationProgress.StatusGatherLayers, LayerCount);
+
+ DecodeInternally(progress);
+
+ progress.Token.ThrowIfCancellationRequested();
+
+ var layerHeightDigits = LayerHeight.DecimalDigits();
+ if (layerHeightDigits > Layer.HeightPrecision)
{
- if (isFilePath)
- {
- GetFileNameStripExtensions(extension, out extension);
- }
- return !string.IsNullOrWhiteSpace(extension) && FileExtensions.Any(fileExtension => fileExtension.Equals(extension));
+ throw new FileLoadException($"The layer height ({LayerHeight}mm) have more decimal digits than the supported ({Layer.HeightPrecision}) digits.\n" +
+ "Lower and fix your layer height on slicer to avoid precision errors.", fileFullPath);
}
- /// <summary>
- /// Gets all valid file extensions in a specified format
- /// </summary>
- public string GetFileExtensions(string prepend = ".", string separator = ", ")
+ bool reSaveFile = Sanitize();
+ if (reSaveFile)
{
- var result = string.Empty;
+ Save(progress);
+ }
+ }
- foreach (var fileExt in FileExtensions)
+ /// <summary>
+ /// Extract contents to a folder
+ /// </summary>
+ /// <param name="path">Path to folder where content will be extracted</param>
+ /// <param name="genericConfigExtract"></param>
+ /// <param name="genericLayersExtract"></param>
+ /// <param name="progress"></param>
+ public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true,
+ OperationProgress? progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.ItemName = OperationProgress.StatusExtracting;
+ /*if (emptyFirst)
+ {
+ if (Directory.Exists(path))
{
- if (!ReferenceEquals(result, string.Empty))
+ DirectoryInfo di = new DirectoryInfo(path);
+
+ foreach (FileInfo file in di.GetFiles())
{
- result += separator;
+ file.Delete();
+ }
+ foreach (DirectoryInfo dir in di.GetDirectories())
+ {
+ dir.Delete(true);
}
- result += $"{prepend}{fileExt.Extension}";
}
+ }*/
- return result;
- }
+ //if (!Directory.Exists(path))
+ //{
+ Directory.CreateDirectory(path);
+ //}
- public bool FileEndsWith(string extension)
+ if (genericConfigExtract)
{
- if (extension[0] != '.') extension = $".{extension}";
- return FileFullPath.EndsWith(extension, StringComparison.OrdinalIgnoreCase) ||
- FileFullPath.EndsWith($"{extension}{TemporaryFileAppend}", StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Gets a thumbnail by it height or lower
- /// </summary>
- /// <param name="maxHeight">Max height allowed</param>
- /// <returns></returns>
- public Mat GetThumbnail(uint maxHeight = 400)
- {
- for (int i = 0; i < ThumbnailsCount; i++)
+ if (Configs.Length > 0)
{
- if(Thumbnails[i] is null) continue;
- if (Thumbnails[i].Height <= maxHeight) return Thumbnails[i];
- }
+ using TextWriter tw = new StreamWriter(Path.Combine(path, $"{ExtractConfigFileName}.{ExtractConfigFileExtension}"), false);
+ foreach (var config in Configs)
+ {
+ var type = config.GetType();
+ tw.WriteLine($"[{type.Name}]");
+ foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ {
+ if (property.Name.Equals("Item")) continue;
+ tw.WriteLine($"{property.Name} = {property.GetValue(config)}");
+ }
- return null;
+ tw.WriteLine();
+ }
+
+ tw.Close();
+ }
}
- /// <summary>
- /// Gets a thumbnail by the largest or smallest
- /// </summary>
- /// <param name="largest">True to get the largest, otherwise false</param>
- /// <returns></returns>
- public Mat GetThumbnail(bool largest)
+ if (genericLayersExtract)
{
- switch (CreatedThumbnailsCount)
+ if (LayerCount > 0)
{
- case 0:
- return null;
- case 1:
- return Thumbnails[0];
- default:
- if (largest)
+ using TextWriter tw = new StreamWriter(Path.Combine(path, "Layers.ini"));
+ for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ var layer = this[layerIndex];
+ tw.WriteLine($"[{layerIndex}]");
+ tw.WriteLine($"{nameof(layer.NonZeroPixelCount)}: {layer.NonZeroPixelCount}");
+ tw.WriteLine($"{nameof(layer.BoundingRectangle)}: {layer.BoundingRectangle}");
+ tw.WriteLine($"{nameof(layer.IsBottomLayer)}: {layer.IsBottomLayer}");
+ tw.WriteLine($"{nameof(layer.LayerHeight)}: {layer.LayerHeight}");
+ tw.WriteLine($"{nameof(layer.PositionZ)}: {layer.PositionZ}");
+
+ if (CanUseLayerLightOffDelay)
+ tw.WriteLine($"{nameof(layer.LightOffDelay)}: {layer.LightOffDelay}");
+ if (CanUseLayerWaitTimeBeforeCure)
+ tw.WriteLine($"{nameof(layer.WaitTimeBeforeCure)}: {layer.WaitTimeBeforeCure}");
+ tw.WriteLine($"{nameof(layer.ExposureTime)}: {layer.ExposureTime}");
+ if (CanUseLayerWaitTimeAfterCure)
+ tw.WriteLine($"{nameof(layer.WaitTimeAfterCure)}: {layer.WaitTimeAfterCure}");
+
+
+ if (CanUseLayerLiftHeight)
+ tw.WriteLine($"{nameof(layer.LiftHeight)}: {layer.LiftHeight}");
+ if (CanUseLayerLiftSpeed)
+ tw.WriteLine($"{nameof(layer.LiftSpeed)}: {layer.LiftSpeed}");
+ if (CanUseLayerLiftHeight2)
+ tw.WriteLine($"{nameof(layer.LiftHeight2)}: {layer.LiftHeight2}");
+ if (CanUseLayerLiftSpeed2)
+ tw.WriteLine($"{nameof(layer.LiftSpeed2)}: {layer.LiftSpeed2}");
+ if (CanUseLayerWaitTimeAfterLift)
+ tw.WriteLine($"{nameof(layer.WaitTimeAfterLift)}: {layer.WaitTimeAfterLift}");
+ if (CanUseLayerRetractSpeed)
{
- return Thumbnails[0].Size.Area() >= Thumbnails[1].Size.Area() ? Thumbnails[0] : Thumbnails[1];
+ tw.WriteLine($"{nameof(layer.RetractHeight)}: {layer.RetractHeight}");
+ tw.WriteLine($"{nameof(layer.RetractSpeed)}: {layer.RetractSpeed}");
}
- else
+ if (CanUseLayerRetractHeight2)
+ tw.WriteLine($"{nameof(layer.RetractHeight2)}: {layer.RetractHeight2}");
+ if (CanUseLayerRetractSpeed2)
+ tw.WriteLine($"{nameof(layer.RetractSpeed2)}: {layer.RetractSpeed2}");
+
+ if (CanUseLayerLightPWM)
+ tw.WriteLine($"{nameof(layer.LightPWM)}: {layer.LightPWM}");
+
+ var materialMillilitersPercent = layer.MaterialMillilitersPercent;
+ if (!float.IsNaN(materialMillilitersPercent))
{
- return Thumbnails[0].Size.Area() <= Thumbnails[1].Size.Area() ? Thumbnails[0] : Thumbnails[1];
+ tw.WriteLine($"{nameof(layer.MaterialMilliliters)}: {layer.MaterialMilliliters}ml ({materialMillilitersPercent:F2}%)");
}
+
+ tw.WriteLine();
+ }
+ tw.Close();
}
}
- /// <summary>
- /// Sets thumbnails from a list of thumbnails and clone them
- /// </summary>
- /// <param name="images"></param>
- public void SetThumbnails(Mat[] images)
+
+ if (FileType == FileFormatType.Archive)
+ {
+ if (FileFullPath is not null)
+ {
+ progress.CanCancel = false;
+ ZipArchiveExtensions.ImprovedExtractToDirectory(FileFullPath, path, ZipArchiveExtensions.Overwrite.Always);
+ return;
+ }
+ }
+
+ progress.ItemCount = LayerCount;
+
+ if (genericLayersExtract)
{
- if (images is null || images.Length == 0) return;
- byte imageIndex = 0;
- for (int i = 0; i < ThumbnailsCount; i++)
+ uint i = 0;
+ foreach (var thumbnail in Thumbnails)
{
- var image = images[Math.Min(imageIndex++, images.Length - 1)];
- if (image is null || image.IsEmpty)
+ if (thumbnail is null)
{
- if (imageIndex >= images.Length) break;
- i--;
continue;
}
- Thumbnails[i] = image.Clone();
- if (Thumbnails[i].Size != ThumbnailsOriginalSize[i])
- {
- CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]);
- }
+ thumbnail.Save(Path.Combine(path, $"Thumbnail{i}.png"));
+ i++;
}
- RaisePropertyChanged(nameof(Thumbnails));
- }
-
- /// <summary>
- /// Sets all thumbnails the same image
- /// </summary>
- /// <param name="images">Image to set</param>
- public void SetThumbnails(Mat image)
- {
- if (image is null || image.IsEmpty) return;
- for (var i = 0; i < ThumbnailsCount; i++)
+ if (LayerCount > 0 && DecodeType == FileDecodeType.Full)
{
- Thumbnails[i] = image.Clone();
- if (ThumbnailsOriginalSize is null || i >= ThumbnailsOriginalSize.Length) continue;
- if (Thumbnails[i].Size != ThumbnailsOriginalSize[i])
+ Parallel.ForEach(this, CoreSettings.ParallelOptions, layer =>
{
- CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]);
- }
+ if (progress.Token.IsCancellationRequested) return;
+ var byteArr = layer.CompressedBytes;
+ if (byteArr is null) return;
+ using var stream = new FileStream(Path.Combine(path, layer.Filename), FileMode.Create, FileAccess.Write);
+ stream.Write(byteArr, 0, byteArr.Length);
+ progress.LockAndIncrement();
+ });
}
- RaisePropertyChanged(nameof(Thumbnails));
}
+ }
- /// <summary>
- /// Sets a thumbnail from a disk file
- /// </summary>
- /// <param name="index">Thumbnail index</param>
- /// <param name="filePath"></param>
- public void SetThumbnail(int index, string filePath)
+ /// <summary>
+ /// Re-set exposure time to the transition layers
+ /// </summary>
+ /// <param name="resetExposureTimes">True to default all the previous transition layers exposure time, otherwise false</param>
+ public void ResetCurrentTransitionLayers(bool resetExposureTimes = true)
+ {
+ if (TransitionLayerType != TransitionLayerTypes.Software) return;
+ SetTransitionLayers(TransitionLayerCount, resetExposureTimes);
+ }
+
+ /// <summary>
+ /// Set transition layers and exposure times, but do not set that count to file property <see cref="TransitionLayerCount"/>
+ /// </summary>
+ /// <param name="transitionLayerCount">Number of transition layers to set</param>
+ /// <param name="resetExposureTimes">True to default all the previous transition layers exposure time, otherwise false</param>
+ public void SetTransitionLayers(ushort transitionLayerCount, bool resetExposureTimes = true)
+ {
+ if (resetExposureTimes)
{
- Thumbnails[index] = CvInvoke.Imread(filePath, ImreadModes.AnyColor);
- if (Thumbnails[index].Size != ThumbnailsOriginalSize[index])
+ for (uint layerIndex = BottomLayerCount; layerIndex < LayerCount; layerIndex++)
{
- CvInvoke.Resize(Thumbnails[index], Thumbnails[index], ThumbnailsOriginalSize[index]);
+ var layer = this[layerIndex];
+ if (layer.ExposureTime == ExposureTime) break; // First equal layer, transition ended
+
+ layer.ExposureTime = ExposureTime;
}
- RaisePropertyChanged(nameof(Thumbnails));
}
- /// <summary>
- /// Encode to an output file
- /// </summary>
- /// <param name="progress"></param>
- protected abstract void EncodeInternally(OperationProgress progress);
+ if (transitionLayerCount == 0) return;
- /// <summary>
- /// Encode to an output file
- /// </summary>
- /// <param name="fileFullPath">Output file</param>
- /// <param name="progress"></param>
- public void Encode(string fileFullPath, OperationProgress progress = null)
+ float increment = Math.Max((BottomExposureTime - ExposureTime) / (transitionLayerCount + 1), 0f);
+ if (increment <= 0) return;
+
+ uint appliedLayers = 0;
+ for (uint layerIndex = BottomLayerCount; appliedLayers < transitionLayerCount && layerIndex < LayerCount; layerIndex++)
{
- if (DecodeType == FileDecodeType.Partial)
- {
- throw new InvalidOperationException("File was partial decoded, a full encode is not possible.");
- }
+ appliedLayers++;
+ this[layerIndex].ExposureTime = Math.Clamp(BottomExposureTime - (increment * appliedLayers), ExposureTime, BottomExposureTime);
+ }
+ }
- progress ??= new OperationProgress();
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ /// <summary>
+ /// Get height in mm from layer height
+ /// </summary>
+ /// <param name="layerIndex"></param>
+ /// <param name="realHeight"></param>
+ /// <returns>The height in mm</returns>
+ public float GetHeightFromLayer(uint layerIndex, bool realHeight = true)
+ {
+ return Layer.RoundHeight((layerIndex + (realHeight ? 1 : 0)) * LayerHeight);
+ }
-#if !DEBUG
- if (this is CTBEncryptedFile file && file.Settings.LayerPointersOffset > 0)
- {
- throw new NotSupportedException(CTBEncryptedFile.Preamble);
- }
-#endif
+ /// <summary>
+ /// Gets the global value for bottom or normal layers based on layer index
+ /// </summary>
+ /// <typeparam name="T">Type of value</typeparam>
+ /// <param name="layerIndex">Layer index</param>
+ /// <param name="bottomValue">Initial value</param>
+ /// <param name="normalValue">Normal value</param>
+ /// <returns></returns>
+ public T GetBottomOrNormalValue<T>(uint layerIndex, T bottomValue, T normalValue)
+ {
+ return layerIndex < BottomLayerCount ? bottomValue : normalValue;
+ }
- LayerManager.Sanitize();
+ /// <summary>
+ /// Gets the global value for bottom or normal layers based on layer
+ /// </summary>
+ /// <typeparam name="T">Type of value</typeparam>
+ /// <param name="layer">Layer</param>
+ /// <param name="bottomValue">Initial value</param>
+ /// <param name="normalValue">Normal value</param>
+ /// <returns></returns>
+ public T GetBottomOrNormalValue<T>(Layer layer, T bottomValue, T normalValue)
+ {
+ return layer.IsBottomLayer ? bottomValue : normalValue;
+ }
- if (File.Exists(fileFullPath)) File.Delete(fileFullPath);
+ /// <summary>
+ /// Refresh print parameters globals with this file settings
+ /// </summary>
+ public void RefreshPrintParametersModifiersValues()
+ {
+ if (PrintParameterModifiers is null) return;
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLayerCount))
+ {
+ PrintParameterModifier.BottomLayerCount.Value = BottomLayerCount;
+ }
- FileFullPath = fileFullPath;
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.TransitionLayerCount))
+ {
+ PrintParameterModifier.TransitionLayerCount.Value = TransitionLayerCount;
+ }
- for (var i = 0; i < Thumbnails.Length; i++)
- {
- if (Thumbnails[i] is null || Thumbnails[i].IsEmpty) continue;
- if(Thumbnails[i].Size == ThumbnailsOriginalSize[i]) continue;
- CvInvoke.Resize(Thumbnails[i], Thumbnails[i], new Size(ThumbnailsOriginalSize[i].Width, ThumbnailsOriginalSize[i].Height));
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLightOffDelay))
+ {
+ PrintParameterModifier.BottomLightOffDelay.Value = (decimal)BottomLightOffDelay;
+ }
- EncodeInternally(progress);
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.LightOffDelay))
+ {
+ PrintParameterModifier.LightOffDelay.Value = (decimal)LightOffDelay;
+ }
- LayerManager.SetAllIsModified(false);
- RequireFullEncode = false;
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomWaitTimeBeforeCure))
+ {
+ PrintParameterModifier.BottomWaitTimeBeforeCure.Value = (decimal)BottomWaitTimeBeforeCure;
}
- /// <summary>
- /// Decode a slicer file
- /// </summary>
- /// <param name="progress"></param>
- protected abstract void DecodeInternally(OperationProgress progress);
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.WaitTimeBeforeCure))
+ {
+ PrintParameterModifier.WaitTimeBeforeCure.Value = (decimal)WaitTimeBeforeCure;
+ }
- /// <summary>
- /// Decode a slicer file
- /// </summary>
- /// <param name="fileFullPath"></param>
- /// <param name="progress"></param>
- public void Decode(string fileFullPath, OperationProgress progress = null) => Decode(fileFullPath, FileDecodeType.Full, progress);
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomExposureTime))
+ {
+ PrintParameterModifier.BottomExposureTime.Value = (decimal) BottomExposureTime;
+ }
- /// <summary>
- /// Decode a slicer file
- /// </summary>
- /// <param name="fileFullPath"></param>
- /// <param name="fileDecodeType"></param>
- /// <param name="progress"></param>
- public void Decode(string fileFullPath, FileDecodeType fileDecodeType, OperationProgress progress = null)
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.ExposureTime))
{
- Clear();
- FileValidation(fileFullPath);
- FileFullPath = fileFullPath;
- DecodeType = fileDecodeType;
- progress ??= new OperationProgress();
- progress.Reset(OperationProgress.StatusGatherLayers, LayerCount);
+ PrintParameterModifier.ExposureTime.Value = (decimal)ExposureTime;
+ }
- DecodeInternally(progress);
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomWaitTimeAfterCure))
+ {
+ PrintParameterModifier.BottomWaitTimeAfterCure.Value = (decimal)BottomWaitTimeAfterCure;
+ }
- progress.Token.ThrowIfCancellationRequested();
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.WaitTimeAfterCure))
+ {
+ PrintParameterModifier.WaitTimeAfterCure.Value = (decimal)WaitTimeAfterCure;
+ }
- var layerHeightDigits = LayerHeight.DecimalDigits();
- if (layerHeightDigits > Layer.HeightPrecision)
- {
- throw new FileLoadException($"The layer height ({LayerHeight}mm) have more decimal digits than the supported ({Layer.HeightPrecision}) digits.\n" +
- "Lower and fix your layer height on slicer to avoid precision errors.", fileFullPath);
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftHeight))
+ {
+ PrintParameterModifier.BottomLiftHeight.Value = (decimal)BottomLiftHeight;
+ }
- bool reSaveFile = LayerManager.Sanitize();
- if (reSaveFile)
- {
- Save(progress);
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftSpeed))
+ {
+ PrintParameterModifier.BottomLiftSpeed.Value = (decimal)BottomLiftSpeed;
}
- /// <summary>
- /// Extract contents to a folder
- /// </summary>
- /// <param name="path">Path to folder where content will be extracted</param>
- /// <param name="genericConfigExtract"></param>
- /// <param name="genericLayersExtract"></param>
- /// <param name="progress"></param>
- public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true,
- OperationProgress progress = null)
- {
- progress ??= new OperationProgress();
- progress.ItemName = OperationProgress.StatusExtracting;
- /*if (emptyFirst)
- {
- if (Directory.Exists(path))
- {
- DirectoryInfo di = new DirectoryInfo(path);
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftHeight))
+ {
+ PrintParameterModifier.LiftHeight.Value = (decimal)LiftHeight;
+ }
- foreach (FileInfo file in di.GetFiles())
- {
- file.Delete();
- }
- foreach (DirectoryInfo dir in di.GetDirectories())
- {
- dir.Delete(true);
- }
- }
- }*/
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftSpeed))
+ {
+ PrintParameterModifier.LiftSpeed.Value = (decimal)LiftSpeed;
+ }
- //if (!Directory.Exists(path))
- //{
- Directory.CreateDirectory(path);
- //}
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftHeight2))
+ {
+ PrintParameterModifier.BottomLiftHeight2.Value = (decimal)BottomLiftHeight2;
+ }
- if (genericConfigExtract)
- {
- if (Configs is not null)
- {
- using TextWriter tw = new StreamWriter(Path.Combine(path, $"{ExtractConfigFileName}.{ExtractConfigFileExtension}"), false);
- foreach (var config in Configs)
- {
- var type = config.GetType();
- tw.WriteLine($"[{type.Name}]");
- foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- if (property.Name.Equals("Item")) continue;
- tw.WriteLine($"{property.Name} = {property.GetValue(config)}");
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftSpeed2))
+ {
+ PrintParameterModifier.BottomLiftSpeed2.Value = (decimal)BottomLiftSpeed2;
+ }
- tw.WriteLine();
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftHeight2))
+ {
+ PrintParameterModifier.LiftHeight2.Value = (decimal)LiftHeight2;
+ }
- tw.Close();
- }
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftSpeed2))
+ {
+ PrintParameterModifier.LiftSpeed2.Value = (decimal)LiftSpeed2;
+ }
- if (genericLayersExtract)
- {
- if (LayerCount > 0)
- {
- using TextWriter tw = new StreamWriter(Path.Combine(path, "Layers.ini"));
- for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- var layer = this[layerIndex];
- tw.WriteLine($"[{layerIndex}]");
- tw.WriteLine($"{nameof(layer.NonZeroPixelCount)}: {layer.NonZeroPixelCount}");
- tw.WriteLine($"{nameof(layer.BoundingRectangle)}: {layer.BoundingRectangle}");
- tw.WriteLine($"{nameof(layer.IsBottomLayer)}: {layer.IsBottomLayer}");
- tw.WriteLine($"{nameof(layer.LayerHeight)}: {layer.LayerHeight}");
- tw.WriteLine($"{nameof(layer.PositionZ)}: {layer.PositionZ}");
-
- if (CanUseLayerLightOffDelay)
- tw.WriteLine($"{nameof(layer.LightOffDelay)}: {layer.LightOffDelay}");
- if (CanUseLayerWaitTimeBeforeCure)
- tw.WriteLine($"{nameof(layer.WaitTimeBeforeCure)}: {layer.WaitTimeBeforeCure}");
- tw.WriteLine($"{nameof(layer.ExposureTime)}: {layer.ExposureTime}");
- if (CanUseLayerWaitTimeAfterCure)
- tw.WriteLine($"{nameof(layer.WaitTimeAfterCure)}: {layer.WaitTimeAfterCure}");
-
-
- if (CanUseLayerLiftHeight)
- tw.WriteLine($"{nameof(layer.LiftHeight)}: {layer.LiftHeight}");
- if (CanUseLayerLiftSpeed)
- tw.WriteLine($"{nameof(layer.LiftSpeed)}: {layer.LiftSpeed}");
- if (CanUseLayerLiftHeight2)
- tw.WriteLine($"{nameof(layer.LiftHeight2)}: {layer.LiftHeight2}");
- if (CanUseLayerLiftSpeed2)
- tw.WriteLine($"{nameof(layer.LiftSpeed2)}: {layer.LiftSpeed2}");
- if (CanUseLayerWaitTimeAfterLift)
- tw.WriteLine($"{nameof(layer.WaitTimeAfterLift)}: {layer.WaitTimeAfterLift}");
- if (CanUseLayerRetractSpeed)
- {
- tw.WriteLine($"{nameof(layer.RetractHeight)}: {layer.RetractHeight}");
- tw.WriteLine($"{nameof(layer.RetractSpeed)}: {layer.RetractSpeed}");
- }
- if (CanUseLayerRetractHeight2)
- tw.WriteLine($"{nameof(layer.RetractHeight2)}: {layer.RetractHeight2}");
- if (CanUseLayerRetractSpeed2)
- tw.WriteLine($"{nameof(layer.RetractSpeed2)}: {layer.RetractSpeed2}");
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomWaitTimeAfterLift))
+ {
+ PrintParameterModifier.BottomWaitTimeAfterLift.Value = (decimal)BottomWaitTimeAfterLift;
+ }
- if (CanUseLayerLightPWM)
- tw.WriteLine($"{nameof(layer.LightPWM)}: {layer.LightPWM}");
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.WaitTimeAfterLift))
+ {
+ PrintParameterModifier.WaitTimeAfterLift.Value = (decimal)WaitTimeAfterLift;
+ }
- var materialMillilitersPercent = layer.MaterialMillilitersPercent;
- if (!float.IsNaN(materialMillilitersPercent))
- {
- tw.WriteLine($"{nameof(layer.MaterialMilliliters)}: {layer.MaterialMilliliters}ml ({materialMillilitersPercent:F2}%)");
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractSpeed))
+ {
+ PrintParameterModifier.BottomRetractSpeed.Value = (decimal)BottomRetractSpeed;
+ }
- tw.WriteLine();
- }
- tw.Close();
- }
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractSpeed))
+ {
+ PrintParameterModifier.RetractSpeed.Value = (decimal)RetractSpeed;
+ }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractHeight2))
+ {
+ PrintParameterModifier.BottomRetractHeight2.Value = (decimal)BottomRetractHeight2;
+ }
- if (FileType == FileFormatType.Archive)
- {
-
- progress.CanCancel = false;
- ZipArchiveExtensions.ImprovedExtractToDirectory(FileFullPath, path, ZipArchiveExtensions.Overwrite.Always);
- return;
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractSpeed2))
+ {
+ PrintParameterModifier.BottomRetractSpeed2.Value = (decimal)BottomRetractSpeed2;
+ }
- progress.ItemCount = LayerCount;
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractHeight2))
+ {
+ PrintParameterModifier.RetractHeight2.Value = (decimal)RetractHeight2;
+ }
- if (genericLayersExtract)
- {
- uint i = 0;
- if (Thumbnails is not null)
- {
- foreach (var thumbnail in Thumbnails)
- {
- if (thumbnail is null)
- {
- continue;
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractSpeed2))
+ {
+ PrintParameterModifier.RetractSpeed2.Value = (decimal)RetractSpeed2;
+ }
- thumbnail.Save(Path.Combine(path, $"Thumbnail{i}.png"));
- i++;
- }
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLightPWM))
+ {
+ PrintParameterModifier.BottomLightPWM.Value = BottomLightPWM;
+ }
- if (LayerCount > 0 && DecodeType == FileDecodeType.Full)
- {
- Parallel.ForEach(this, CoreSettings.ParallelOptions, layer =>
- {
- if (progress.Token.IsCancellationRequested) return;
- var byteArr = layer.CompressedBytes;
- if (byteArr is null) return;
- using var stream = new FileStream(Path.Combine(path, layer.Filename), FileMode.Create, FileAccess.Write);
- stream.Write(byteArr, 0, byteArr.Length);
- progress.LockAndIncrement();
- });
- }
- }
+ if (PrintParameterModifiers.Contains(PrintParameterModifier.LightPWM))
+ {
+ PrintParameterModifier.LightPWM.Value = LightPWM;
}
+ }
- /// <summary>
- /// Re-set exposure time to the transition layers
- /// </summary>
- /// <param name="resetExposureTimes">True to default all the previous transition layers exposure time, otherwise false</param>
- public void ResetCurrentTransitionLayers(bool resetExposureTimes = true)
+ /// <summary>
+ /// Refresh print parameters per layer globals with this file settings
+ /// </summary>
+ public void RefreshPrintParametersPerLayerModifiersValues(uint layerIndex)
+ {
+ if (PrintParameterPerLayerModifiers is null) return;
+ var layer = this[layerIndex];
+
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.PositionZ))
{
- if (TransitionLayerType != TransitionLayerTypes.Software) return;
- SetTransitionLayers(TransitionLayerCount, resetExposureTimes);
+ PrintParameterModifier.PositionZ.Value = (decimal)layer.PositionZ;
}
- /// <summary>
- /// Set transition layers and exposure times, but do not set that count to file property <see cref="TransitionLayerCount"/>
- /// </summary>
- /// <param name="transitionLayerCount">Number of transition layers to set</param>
- /// <param name="resetExposureTimes">True to default all the previous transition layers exposure time, otherwise false</param>
- public void SetTransitionLayers(ushort transitionLayerCount, bool resetExposureTimes = true)
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LightOffDelay))
{
- if (resetExposureTimes)
- {
- for (uint layerIndex = BottomLayerCount; layerIndex < LayerCount; layerIndex++)
- {
- var layer = this[layerIndex];
- if (layer.ExposureTime == ExposureTime) break; // First equal layer, transition ended
+ PrintParameterModifier.LightOffDelay.Value = (decimal)layer.LightOffDelay;
+ }
- layer.ExposureTime = ExposureTime;
- }
- }
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.WaitTimeBeforeCure))
+ {
+ PrintParameterModifier.WaitTimeBeforeCure.Value = (decimal)layer.WaitTimeBeforeCure;
+ }
- if (transitionLayerCount == 0) return;
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.ExposureTime))
+ {
+ PrintParameterModifier.ExposureTime.Value = (decimal)layer.ExposureTime;
+ }
- float increment = Math.Max((BottomExposureTime - ExposureTime) / (transitionLayerCount + 1), 0f);
- if (increment <= 0) return;
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.WaitTimeAfterCure))
+ {
+ PrintParameterModifier.WaitTimeAfterCure.Value = (decimal)layer.WaitTimeAfterCure;
+ }
- uint appliedLayers = 0;
- for (uint layerIndex = BottomLayerCount; appliedLayers < transitionLayerCount && layerIndex < LayerCount; layerIndex++)
- {
- appliedLayers++;
- this[layerIndex].ExposureTime = Math.Clamp(BottomExposureTime - (increment * appliedLayers), ExposureTime, BottomExposureTime);
- }
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftHeight))
+ {
+ PrintParameterModifier.LiftHeight.Value = (decimal)layer.LiftHeight;
}
- /// <summary>
- /// Get height in mm from layer height
- /// </summary>
- /// <param name="layerIndex"></param>
- /// <param name="realHeight"></param>
- /// <returns>The height in mm</returns>
- public float GetHeightFromLayer(uint layerIndex, bool realHeight = true)
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftSpeed))
{
- return Layer.RoundHeight((layerIndex + (realHeight ? 1 : 0)) * LayerHeight);
+ PrintParameterModifier.LiftSpeed.Value = (decimal)layer.LiftSpeed;
}
- /// <summary>
- /// Gets the global value for bottom or normal layers based on layer index
- /// </summary>
- /// <typeparam name="T">Type of value</typeparam>
- /// <param name="layerIndex">Layer index</param>
- /// <param name="bottomValue">Initial value</param>
- /// <param name="normalValue">Normal value</param>
- /// <returns></returns>
- public T GetBottomOrNormalValue<T>(uint layerIndex, T bottomValue, T normalValue)
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftHeight2))
{
- return layerIndex < BottomLayerCount ? bottomValue : normalValue;
+ PrintParameterModifier.LiftHeight2.Value = (decimal)layer.LiftHeight2;
}
- /// <summary>
- /// Gets the global value for bottom or normal layers based on layer
- /// </summary>
- /// <typeparam name="T">Type of value</typeparam>
- /// <param name="layer">Layer</param>
- /// <param name="bottomValue">Initial value</param>
- /// <param name="normalValue">Normal value</param>
- /// <returns></returns>
- public T GetBottomOrNormalValue<T>(Layer layer, T bottomValue, T normalValue)
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftSpeed2))
{
- return layer.IsBottomLayer ? bottomValue : normalValue;
+ PrintParameterModifier.LiftSpeed2.Value = (decimal)layer.LiftSpeed2;
}
- /// <summary>
- /// Refresh print parameters globals with this file settings
- /// </summary>
- public void RefreshPrintParametersModifiersValues()
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.WaitTimeAfterLift))
{
- if (PrintParameterModifiers is null) return;
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLayerCount))
- {
- PrintParameterModifier.BottomLayerCount.Value = BottomLayerCount;
- }
+ PrintParameterModifier.WaitTimeAfterLift.Value = (decimal)layer.WaitTimeAfterLift;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.TransitionLayerCount))
- {
- PrintParameterModifier.TransitionLayerCount.Value = TransitionLayerCount;
- }
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractSpeed))
+ {
+ PrintParameterModifier.RetractSpeed.Value = (decimal)layer.RetractSpeed;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLightOffDelay))
- {
- PrintParameterModifier.BottomLightOffDelay.Value = (decimal)BottomLightOffDelay;
- }
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractHeight2))
+ {
+ PrintParameterModifier.RetractHeight2.Value = (decimal)layer.RetractHeight2;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.LightOffDelay))
- {
- PrintParameterModifier.LightOffDelay.Value = (decimal)LightOffDelay;
- }
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractSpeed2))
+ {
+ PrintParameterModifier.RetractSpeed2.Value = (decimal)layer.RetractSpeed2;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomWaitTimeBeforeCure))
- {
- PrintParameterModifier.BottomWaitTimeBeforeCure.Value = (decimal)BottomWaitTimeBeforeCure;
- }
+ if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LightPWM))
+ {
+ PrintParameterModifier.LightPWM.Value = layer.LightPWM;
+ }
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.WaitTimeBeforeCure))
- {
- PrintParameterModifier.WaitTimeBeforeCure.Value = (decimal)WaitTimeBeforeCure;
- }
+ /// <summary>
+ /// Gets the value attributed to <see cref="FileFormat.PrintParameterModifier"/>
+ /// </summary>
+ /// <param name="modifier">Modifier to use</param>
+ /// <returns>A value</returns>
+ public object? GetValueFromPrintParameterModifier(PrintParameterModifier modifier)
+ {
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerCount))
+ return BottomLayerCount;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.TransitionLayerCount))
+ return TransitionLayerCount;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightOffDelay))
+ return BottomLightOffDelay;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LightOffDelay))
+ return LightOffDelay;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeBeforeCure))
+ return BottomWaitTimeBeforeCure;
+ if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeBeforeCure))
+ return WaitTimeBeforeCure;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomExposureTime))
+ return BottomExposureTime;
+ if (ReferenceEquals(modifier, PrintParameterModifier.ExposureTime))
+ return ExposureTime;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterCure))
+ return BottomWaitTimeAfterCure;
+ if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterCure))
+ return WaitTimeAfterCure;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight))
+ return BottomLiftHeight;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight))
+ return LiftHeight;
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed))
+ return BottomLiftSpeed;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed))
+ return LiftSpeed;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight2))
+ return BottomLiftHeight2;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight2))
+ return LiftHeight2;
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed2))
+ return BottomLiftSpeed2;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed2))
+ return LiftSpeed2;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterLift))
+ return BottomWaitTimeAfterLift;
+ if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterLift))
+ return WaitTimeAfterLift;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed))
+ return BottomRetractSpeed;
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed))
+ return RetractSpeed;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractHeight2))
+ return BottomRetractHeight2;
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractHeight2))
+ return RetractHeight2;
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed2))
+ return BottomRetractSpeed2;
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed2))
+ return RetractSpeed2;
+
+
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM))
+ return BottomLightPWM;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM))
+ return LightPWM;
+
+ return null;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomExposureTime))
- {
- PrintParameterModifier.BottomExposureTime.Value = (decimal) BottomExposureTime;
- }
+ /// <summary>
+ /// Sets a property value attributed to <see cref="modifier"/>
+ /// </summary>
+ /// <param name="modifier">Modifier to use</param>
+ /// <param name="value">Value to set</param>
+ /// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns>
+ public bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, decimal value)
+ {
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerCount))
+ {
+ BottomLayerCount = (ushort)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.ExposureTime))
- {
- PrintParameterModifier.ExposureTime.Value = (decimal)ExposureTime;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.TransitionLayerCount))
+ {
+ TransitionLayerCount = (ushort)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomWaitTimeAfterCure))
- {
- PrintParameterModifier.BottomWaitTimeAfterCure.Value = (decimal)BottomWaitTimeAfterCure;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightOffDelay))
+ {
+ BottomLightOffDelay = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LightOffDelay))
+ {
+ LightOffDelay = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.WaitTimeAfterCure))
- {
- PrintParameterModifier.WaitTimeAfterCure.Value = (decimal)WaitTimeAfterCure;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeBeforeCure))
+ {
+ BottomWaitTimeBeforeCure = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeBeforeCure))
+ {
+ WaitTimeBeforeCure = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftHeight))
- {
- PrintParameterModifier.BottomLiftHeight.Value = (decimal)BottomLiftHeight;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomExposureTime))
+ {
+ BottomExposureTime = (float) value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.ExposureTime))
+ {
+ ExposureTime = (float) value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftSpeed))
- {
- PrintParameterModifier.BottomLiftSpeed.Value = (decimal)BottomLiftSpeed;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterCure))
+ {
+ BottomWaitTimeAfterCure = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterCure))
+ {
+ WaitTimeAfterCure = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftHeight))
- {
- PrintParameterModifier.LiftHeight.Value = (decimal)LiftHeight;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight))
+ {
+ BottomLiftHeight = (float) value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight))
+ {
+ LiftHeight = (float) value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed))
+ {
+ BottomLiftSpeed = (float) value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed))
+ {
+ LiftSpeed = (float) value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftSpeed))
- {
- PrintParameterModifier.LiftSpeed.Value = (decimal)LiftSpeed;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight2))
+ {
+ BottomLiftHeight2 = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight2))
+ {
+ LiftHeight2 = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed2))
+ {
+ BottomLiftSpeed2 = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed2))
+ {
+ LiftSpeed2 = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftHeight2))
- {
- PrintParameterModifier.BottomLiftHeight2.Value = (decimal)BottomLiftHeight2;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterLift))
+ {
+ BottomWaitTimeAfterLift = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterLift))
+ {
+ WaitTimeAfterLift = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftSpeed2))
- {
- PrintParameterModifier.BottomLiftSpeed2.Value = (decimal)BottomLiftSpeed2;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed))
+ {
+ BottomRetractSpeed = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftHeight2))
- {
- PrintParameterModifier.LiftHeight2.Value = (decimal)LiftHeight2;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed))
+ {
+ RetractSpeed = (float) value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftSpeed2))
- {
- PrintParameterModifier.LiftSpeed2.Value = (decimal)LiftSpeed2;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractHeight2))
+ {
+ BottomRetractHeight2 = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomWaitTimeAfterLift))
- {
- PrintParameterModifier.BottomWaitTimeAfterLift.Value = (decimal)BottomWaitTimeAfterLift;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractHeight2))
+ {
+ RetractHeight2 = (float)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed2))
+ {
+ BottomRetractSpeed2 = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.WaitTimeAfterLift))
- {
- PrintParameterModifier.WaitTimeAfterLift.Value = (decimal)WaitTimeAfterLift;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed2))
+ {
+ RetractSpeed2 = (float)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractSpeed))
- {
- PrintParameterModifier.BottomRetractSpeed.Value = (decimal)BottomRetractSpeed;
- }
+ if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM))
+ {
+ BottomLightPWM = (byte)value;
+ return true;
+ }
+ if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM))
+ {
+ LightPWM = (byte)value;
+ return true;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractSpeed))
- {
- PrintParameterModifier.RetractSpeed.Value = (decimal)RetractSpeed;
- }
+ return false;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractHeight2))
- {
- PrintParameterModifier.BottomRetractHeight2.Value = (decimal)BottomRetractHeight2;
- }
+ /// <summary>
+ /// Sets properties from print parameters
+ /// </summary>
+ /// <returns>Number of affected parameters</returns>
+ public byte SetValuesFromPrintParametersModifiers()
+ {
+ if (PrintParameterModifiers is null) return 0;
+ byte changed = 0;
+ foreach (var modifier in PrintParameterModifiers)
+ {
+ if(!modifier.HasChanged) continue;
+ modifier.OldValue = modifier.NewValue;
+ SetValueFromPrintParameterModifier(modifier, modifier.NewValue);
+ changed++;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractSpeed2))
- {
- PrintParameterModifier.BottomRetractSpeed2.Value = (decimal)BottomRetractSpeed2;
- }
+ return changed;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractHeight2))
- {
- PrintParameterModifier.RetractHeight2.Value = (decimal)RetractHeight2;
- }
+ public void SetNoDelays()
+ {
+ BottomLightOffDelay = 0;
+ LightOffDelay = 0;
+ BottomWaitTimeBeforeCure = 0;
+ WaitTimeBeforeCure = 0;
+ BottomWaitTimeAfterCure = 0;
+ WaitTimeAfterCure = 0;
+ BottomWaitTimeAfterLift = 0;
+ WaitTimeAfterLift = 0;
+ }
- if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractSpeed2))
- {
- PrintParameterModifier.RetractSpeed2.Value = (decimal)RetractSpeed2;
- }
+ public float CalculateBottomLightOffDelay(float extraTime = 0) => CalculateLightOffDelay(true, extraTime);
- if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLightPWM))
- {
- PrintParameterModifier.BottomLightPWM.Value = BottomLightPWM;
- }
+ public bool SetBottomLightOffDelay(float extraTime = 0) => SetLightOffDelay(true, extraTime);
- if (PrintParameterModifiers.Contains(PrintParameterModifier.LightPWM))
+ public float CalculateNormalLightOffDelay(float extraTime = 0) => CalculateLightOffDelay(false, extraTime);
+
+ public bool SetNormalLightOffDelay(float extraTime = 0) => SetLightOffDelay(false, extraTime);
+
+ public float CalculateMotorMovementTime(bool isBottomLayer, float extraTime = 0)
+ {
+ return isBottomLayer
+ ? OperationCalculator.LightOffDelayC.CalculateSeconds(BottomLiftHeight, BottomLiftSpeed, BottomRetractSpeed, extraTime, BottomLiftHeight2, BottomLiftSpeed2, BottomRetractHeight2, BottomRetractSpeed2)
+ : OperationCalculator.LightOffDelayC.CalculateSeconds(LiftHeight, LiftSpeed, RetractSpeed, extraTime, LiftHeight2, LiftSpeed2, RetractHeight2, RetractSpeed2);
+ }
+
+ public float CalculateLightOffDelay(bool isBottomLayer, float extraTime = 0)
+ {
+ extraTime = (float)Math.Round(extraTime, 2);
+ if (SupportsGCode) return extraTime;
+ return CalculateMotorMovementTime(isBottomLayer, extraTime);
+ }
+
+ public bool SetLightOffDelay(bool isBottomLayer, float extraTime = 0)
+ {
+ float lightOff = CalculateLightOffDelay(isBottomLayer, extraTime);
+ if (isBottomLayer)
+ {
+ if (BottomLightOffDelay != lightOff)
{
- PrintParameterModifier.LightPWM.Value = LightPWM;
+ BottomLightOffDelay = lightOff;
+ return true;
}
+
+ return false;
+ }
+
+ if (LightOffDelay != lightOff)
+ {
+ LightOffDelay = lightOff;
+ return true;
}
- /// <summary>
- /// Refresh print parameters per layer globals with this file settings
- /// </summary>
- public void RefreshPrintParametersPerLayerModifiersValues(uint layerIndex)
+ return false;
+ }
+
+ public float GetWaitTimeBeforeCure(bool isBottomLayer)
+ {
+ return isBottomLayer ? GetBottomWaitTimeBeforeCure() : GetNormalWaitTimeBeforeCure();
+ }
+
+ /// <summary>
+ /// Gets the bottom wait time before cure, if not available calculate it from light off delay
+ /// </summary>
+ /// <returns></returns>
+ public float GetBottomWaitTimeBeforeCure()
+ {
+ if (CanUseBottomWaitTimeBeforeCure)
{
- if (PrintParameterPerLayerModifiers is null) return;
- var layer = this[layerIndex];
+ return BottomWaitTimeBeforeCure;
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.PositionZ))
- {
- PrintParameterModifier.PositionZ.Value = (decimal)layer.PositionZ;
- }
+ if (CanUseWaitTimeBeforeCure)
+ {
+ return WaitTimeBeforeCure;
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LightOffDelay))
- {
- PrintParameterModifier.LightOffDelay.Value = (decimal)layer.LightOffDelay;
- }
+ if (CanUseBottomLightOffDelay)
+ {
+ return (float)Math.Max(0, Math.Round(BottomLightOffDelay - CalculateBottomLightOffDelay(), 2));
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.WaitTimeBeforeCure))
- {
- PrintParameterModifier.WaitTimeBeforeCure.Value = (decimal)layer.WaitTimeBeforeCure;
- }
+ if (CanUseLightOffDelay)
+ {
+ return (float)Math.Max(0, Math.Round(LightOffDelay - CalculateNormalLightOffDelay(), 2));
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.ExposureTime))
- {
- PrintParameterModifier.ExposureTime.Value = (decimal)layer.ExposureTime;
- }
+ return 0;
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.WaitTimeAfterCure))
- {
- PrintParameterModifier.WaitTimeAfterCure.Value = (decimal)layer.WaitTimeAfterCure;
- }
+ /// <summary>
+ /// Gets the wait time before cure, if not available calculate it from light off delay
+ /// </summary>
+ /// <returns></returns>
+ public float GetNormalWaitTimeBeforeCure()
+ {
+ if (CanUseWaitTimeBeforeCure)
+ {
+ return WaitTimeBeforeCure;
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftHeight))
- {
- PrintParameterModifier.LiftHeight.Value = (decimal)layer.LiftHeight;
- }
+ if (CanUseLightOffDelay)
+ {
+ return (float)Math.Max(0, Math.Round(LightOffDelay - CalculateNormalLightOffDelay(), 2));
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftSpeed))
- {
- PrintParameterModifier.LiftSpeed.Value = (decimal)layer.LiftSpeed;
- }
+ return 0;
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftHeight2))
- {
- PrintParameterModifier.LiftHeight2.Value = (decimal)layer.LiftHeight2;
- }
+ /// <summary>
+ /// Attempt to set wait time before cure if supported, otherwise fallback to light-off delay
+ /// </summary>
+ /// <param name="time">The time to set</param>
+ /// <param name="zeroLightOffDelayCalculateBase">When true and time is zero, it will calculate light-off delay without extra time, otherwise false to set light-off delay to 0 when time is 0</param>
+ public void SetWaitTimeBeforeCureOrLightOffDelay(bool isBottomLayer, float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ {
+ if (isBottomLayer)
+ {
+ SetBottomWaitTimeBeforeCureOrLightOffDelay(time, zeroLightOffDelayCalculateBase);
+ }
+ else
+ {
+ SetNormalWaitTimeBeforeCureOrLightOffDelay(time, zeroLightOffDelayCalculateBase);
+ }
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftSpeed2))
+ public void SetBottomWaitTimeBeforeCureOrLightOffDelay(float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ {
+ if (CanUseBottomWaitTimeBeforeCure)
+ {
+ BottomLightOffDelay = 0;
+ BottomWaitTimeBeforeCure = time;
+ }
+ else if (CanUseBottomLightOffDelay)
+ {
+ if (time == 0 && !zeroLightOffDelayCalculateBase)
{
- PrintParameterModifier.LiftSpeed2.Value = (decimal)layer.LiftSpeed2;
+ BottomLightOffDelay = 0;
+ return;
}
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.WaitTimeAfterLift))
- {
- PrintParameterModifier.WaitTimeAfterLift.Value = (decimal)layer.WaitTimeAfterLift;
- }
+ SetBottomLightOffDelay(time);
+ }
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractSpeed))
+ public void SetNormalWaitTimeBeforeCureOrLightOffDelay(float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ {
+ if (CanUseWaitTimeBeforeCure)
+ {
+ LightOffDelay = 0;
+ WaitTimeBeforeCure = time;
+ }
+ else if (CanUseLightOffDelay)
+ {
+ if (time == 0 && !zeroLightOffDelayCalculateBase)
{
- PrintParameterModifier.RetractSpeed.Value = (decimal)layer.RetractSpeed;
+ LightOffDelay = 0;
+ return;
}
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractHeight2))
- {
- PrintParameterModifier.RetractHeight2.Value = (decimal)layer.RetractHeight2;
- }
+ SetNormalLightOffDelay(time);
+ }
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractSpeed2))
- {
- PrintParameterModifier.RetractSpeed2.Value = (decimal)layer.RetractSpeed2;
- }
+ /// <summary>
+ /// Rebuilds GCode based on current settings
+ /// </summary>
+ public virtual void RebuildGCode()
+ {
+ if (!SupportsGCode || _suppressRebuildGCode) return;
+ GCode!.RebuildGCode(this);
+ RaisePropertyChanged(nameof(GCodeStr));
+ }
+
+ /// <summary>
+ /// Saves current configuration on input file
+ /// </summary>
+ /// <param name="progress"></param>
+ public void Save(OperationProgress? progress = null)
+ {
+ SaveAs(null, progress);
+ }
- if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LightPWM))
+ /// <summary>
+ /// Saves current configuration on a copy
+ /// </summary>
+ /// <param name="filePath">File path to save copy as, use null to overwrite active file (Same as <see cref="Save"/>)</param>
+ /// <param name="progress"></param>
+ public void SaveAs(string? filePath = null, OperationProgress? progress = null)
+ {
+ if (RequireFullEncode)
+ {
+ if (!string.IsNullOrEmpty(filePath))
{
- PrintParameterModifier.LightPWM.Value = layer.LightPWM;
+ FileFullPath = filePath;
}
+ Encode(FileFullPath, progress);
+ return;
}
- /// <summary>
- /// Gets the value attributed to <see cref="FileFormat.PrintParameterModifier"/>
- /// </summary>
- /// <param name="modifier">Modifier to use</param>
- /// <returns>A value</returns>
- public object GetValueFromPrintParameterModifier(PrintParameterModifier modifier)
- {
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerCount))
- return BottomLayerCount;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.TransitionLayerCount))
- return TransitionLayerCount;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightOffDelay))
- return BottomLightOffDelay;
- if (ReferenceEquals(modifier, PrintParameterModifier.LightOffDelay))
- return LightOffDelay;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeBeforeCure))
- return BottomWaitTimeBeforeCure;
- if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeBeforeCure))
- return WaitTimeBeforeCure;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomExposureTime))
- return BottomExposureTime;
- if (ReferenceEquals(modifier, PrintParameterModifier.ExposureTime))
- return ExposureTime;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterCure))
- return BottomWaitTimeAfterCure;
- if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterCure))
- return WaitTimeAfterCure;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight))
- return BottomLiftHeight;
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight))
- return LiftHeight;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed))
- return BottomLiftSpeed;
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed))
- return LiftSpeed;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight2))
- return BottomLiftHeight2;
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight2))
- return LiftHeight2;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed2))
- return BottomLiftSpeed2;
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed2))
- return LiftSpeed2;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterLift))
- return BottomWaitTimeAfterLift;
- if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterLift))
- return WaitTimeAfterLift;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed))
- return BottomRetractSpeed;
- if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed))
- return RetractSpeed;
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractHeight2))
- return BottomRetractHeight2;
- if (ReferenceEquals(modifier, PrintParameterModifier.RetractHeight2))
- return RetractHeight2;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed2))
- return BottomRetractSpeed2;
- if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed2))
- return RetractSpeed2;
-
-
-
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM))
- return BottomLightPWM;
- if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM))
- return LightPWM;
-
- return null;
+ if (FileFullPath is null)
+ {
+ throw new ArgumentNullException(nameof(FileFullPath));
}
- /// <summary>
- /// Sets a property value attributed to <see cref="modifier"/>
- /// </summary>
- /// <param name="modifier">Modifier to use</param>
- /// <param name="value">Value to set</param>
- /// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns>
- public bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, decimal value)
+ if (!string.IsNullOrEmpty(filePath))
{
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerCount))
- {
- BottomLayerCount = (ushort)value;
- return true;
- }
+ File.Copy(FileFullPath, filePath, true);
+ FileFullPath = filePath;
- if (ReferenceEquals(modifier, PrintParameterModifier.TransitionLayerCount))
- {
- TransitionLayerCount = (ushort)value;
- return true;
- }
+ }
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightOffDelay))
- {
- BottomLightOffDelay = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.LightOffDelay))
- {
- LightOffDelay = (float)value;
- return true;
- }
+ progress ??= new OperationProgress();
+ PartialSaveInternally(progress);
+ }
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeBeforeCure))
- {
- BottomWaitTimeBeforeCure = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeBeforeCure))
- {
- WaitTimeBeforeCure = (float)value;
- return true;
- }
+ /// <summary>
+ /// Partial save of the file, this is the file information only.
+ /// When this function is called it's already ready to save to file
+ /// </summary>
+ /// <param name="progress"></param>
+ protected abstract void PartialSaveInternally(OperationProgress progress);
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomExposureTime))
- {
- BottomExposureTime = (float) value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.ExposureTime))
- {
- ExposureTime = (float) value;
- return true;
- }
+ /// <summary>
+ /// Triggers when a conversion is valid and before start converting values
+ /// </summary>
+ /// <param name="source">Source file format</param>
+ /// <returns>True to continue the conversion, otherwise false to stop</returns>
+ protected virtual bool OnBeforeConvertFrom(FileFormat source) => true;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterCure))
- {
- BottomWaitTimeAfterCure = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterCure))
- {
- WaitTimeAfterCure = (float)value;
- return true;
- }
+ /// <summary>
+ /// Triggers when a conversion is valid and before start converting values
+ /// </summary>
+ /// <param name="output">Target file format</param>
+ /// <returns>True to continue the conversion, otherwise false to stop</returns>
+ protected virtual bool OnBeforeConvertTo(FileFormat output) => true;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight))
- {
- BottomLiftHeight = (float) value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight))
- {
- LiftHeight = (float) value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed))
- {
- BottomLiftSpeed = (float) value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed))
- {
- LiftSpeed = (float) value;
- return true;
- }
+ /// <summary>
+ /// Triggers when the conversion is made but before encoding
+ /// </summary>
+ /// <param name="source">Source file format</param>
+ /// <returns>True to continue the conversion, otherwise false to stop</returns>
+ protected virtual bool OnAfterConvertFrom(FileFormat source) => true;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight2))
- {
- BottomLiftHeight2 = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight2))
+ /// <summary>
+ /// Triggers when the conversion is made but before encoding
+ /// </summary>
+ /// <param name="output">Output file format</param>
+ /// <returns>True to continue the conversion, otherwise false to stop</returns>
+ protected virtual bool OnAfterConvertTo(FileFormat output) => true;
+
+ /// <summary>
+ /// Converts this file type to another file type
+ /// </summary>
+ /// <param name="to">Target file format</param>
+ /// <param name="fileFullPath">Output path file</param>
+ /// <param name="version">File version to use</param>
+ /// <param name="progress"></param>
+ /// <returns>The converted file if successful, otherwise null</returns>
+ public virtual FileFormat? Convert(Type to, string fileFullPath, uint version = 0, OperationProgress? progress = null)
+ {
+ if (!IsValid) return null;
+ var found = AvailableFormats.Any(format => to == format.GetType());
+ if (!found) return null;
+
+ progress ??= new OperationProgress("Converting");
+
+ if (Activator.CreateInstance(to) is not FileFormat slicerFile) return null;
+ slicerFile.FileFullPath = fileFullPath;
+
+ if (!slicerFile.OnBeforeConvertFrom(this)) return null;
+ if (!OnBeforeConvertTo(slicerFile)) return null;
+
+ if (version > 0 && version != DefaultVersion)
+ {
+ slicerFile.Version = version;
+ }
+
+ slicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ slicerFile.Init(CloneLayers());
+ slicerFile.AntiAliasing = ValidateAntiAliasingLevel();
+ slicerFile.LayerCount = LayerCount;
+ slicerFile.BottomLayerCount = BottomLayerCount;
+ slicerFile.TransitionLayerCount = TransitionLayerCount;
+ slicerFile.LayerHeight = LayerHeight;
+ slicerFile.ResolutionX = ResolutionX;
+ slicerFile.ResolutionY = ResolutionY;
+ slicerFile.DisplayWidth = DisplayWidth;
+ slicerFile.DisplayHeight = DisplayHeight;
+ slicerFile.MachineZ = MachineZ;
+ slicerFile.DisplayMirror = DisplayMirror;
+
+ // Exposure
+ slicerFile.BottomExposureTime = BottomExposureTime;
+ slicerFile.ExposureTime = ExposureTime;
+
+ // Lifts
+ slicerFile.BottomLiftHeight = BottomLiftHeight;
+ slicerFile.BottomLiftSpeed = BottomLiftSpeed;
+
+ slicerFile.LiftHeight = LiftHeight;
+ slicerFile.LiftSpeed = LiftSpeed;
+
+ slicerFile.BottomLiftSpeed2 = BottomLiftSpeed2;
+ slicerFile.LiftSpeed2 = LiftSpeed2;
+
+ slicerFile.BottomRetractSpeed = BottomRetractSpeed;
+ slicerFile.RetractSpeed = RetractSpeed;
+
+ slicerFile.BottomRetractSpeed2 = BottomRetractSpeed2;
+ slicerFile.RetractSpeed2 = RetractSpeed2;
+
+
+ if (slicerFile.CanUseAnyLiftHeight2 && (CanUseAnyLiftHeight2 || GetType() == typeof(SL1File))) // Both are TSMC compatible
{
- LiftHeight2 = (float)value;
- return true;
+ slicerFile.BottomLiftHeight2 = BottomLiftHeight2;
+ slicerFile.LiftHeight2 = LiftHeight2;
+
+ slicerFile.BottomRetractHeight2 = BottomRetractHeight2;
+ slicerFile.RetractHeight2 = RetractHeight2;
}
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed2))
+ /*else if (slicerFile.CanUseAnyLiftHeight2) // Output format is compatible with TSMC, but input isn't
{
- BottomLiftSpeed2 = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed2))
+ slicerFile.BottomLiftHeight = BottomLiftHeight;
+ slicerFile.LiftHeight = LiftHeight;
+ }*/
+ else if (CanUseAnyLiftHeight2) // Output format isn't compatible with TSMC, but input is
{
- LiftSpeed2 = (float)value;
- return true;
- }
+ slicerFile.BottomLiftHeight = BottomLiftHeightTotal;
+ slicerFile.LiftHeight = LiftHeightTotal;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterLift))
- {
- BottomWaitTimeAfterLift = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterLift))
- {
- WaitTimeAfterLift = (float)value;
- return true;
- }
+ // Set to the slowest retract speed
+ if (BottomRetractSpeed2 > 0 && BottomRetractSpeed > BottomRetractSpeed2)
+ {
+ slicerFile.BottomRetractSpeed = BottomRetractSpeed2;
+ }
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed))
- {
- BottomRetractSpeed = (float)value;
- return true;
+ // Set to the slowest retract speed
+ if (RetractSpeed2 > 0 && RetractSpeed > RetractSpeed2)
+ {
+ slicerFile.RetractSpeed = RetractSpeed2;
+ }
}
- if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed))
- {
- RetractSpeed = (float) value;
- return true;
- }
+ // Wait times
+ slicerFile.BottomLightOffDelay = BottomLightOffDelay;
+ slicerFile.LightOffDelay = LightOffDelay;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractHeight2))
- {
- BottomRetractHeight2 = (float)value;
- return true;
- }
+ slicerFile.BottomWaitTimeBeforeCure = BottomWaitTimeBeforeCure;
+ slicerFile.WaitTimeBeforeCure = WaitTimeBeforeCure;
- if (ReferenceEquals(modifier, PrintParameterModifier.RetractHeight2))
- {
- RetractHeight2 = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed2))
- {
- BottomRetractSpeed2 = (float)value;
- return true;
- }
+ slicerFile.BottomWaitTimeAfterCure = BottomWaitTimeAfterCure;
+ slicerFile.WaitTimeAfterCure = WaitTimeAfterCure;
- if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed2))
- {
- RetractSpeed2 = (float)value;
- return true;
- }
+ slicerFile.BottomWaitTimeAfterLift = BottomWaitTimeAfterLift;
+ slicerFile.WaitTimeAfterLift = WaitTimeAfterLift;
- if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM))
- {
- BottomLightPWM = (byte)value;
- return true;
- }
- if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM))
- {
- LightPWM = (byte)value;
- return true;
- }
+ slicerFile.BottomLightPWM = BottomLightPWM;
+ slicerFile.LightPWM = LightPWM;
- return false;
- }
- /// <summary>
- /// Sets properties from print parameters
- /// </summary>
- /// <returns>Number of affected parameters</returns>
- public byte SetValuesFromPrintParametersModifiers()
+ slicerFile.MachineName = MachineName;
+ slicerFile.MaterialName = MaterialName;
+ slicerFile.MaterialMilliliters = MaterialMilliliters;
+ slicerFile.MaterialGrams = MaterialGrams;
+ slicerFile.MaterialCost = MaterialCost;
+ slicerFile.Xppmm = Xppmm;
+ slicerFile.Yppmm = Yppmm;
+ slicerFile.PrintTime = PrintTime;
+ slicerFile.PrintHeight = PrintHeight;
+
+ slicerFile.SetThumbnails(Thumbnails);
+ });
+
+ if (!slicerFile.OnAfterConvertFrom(this)) return null;
+ if (!OnAfterConvertTo(slicerFile)) return null;
+
+ slicerFile.Encode(fileFullPath, progress);
+
+ return slicerFile;
+ }
+
+ /// <summary>
+ /// Converts this file type to another file type
+ /// </summary>
+ /// <param name="to">Target file format</param>
+ /// <param name="fileFullPath">Output path file</param>
+ /// <param name="version">File version</param>
+ /// <param name="progress"></param>
+ /// <returns>TThe converted file if successful, otherwise null</returns>
+ public FileFormat? Convert(FileFormat to, string fileFullPath, uint version = 0, OperationProgress? progress = null)
+ => Convert(to.GetType(), fileFullPath, version, progress);
+
+ /// <summary>
+ /// Validate AntiAlias Level
+ /// </summary>
+ public byte ValidateAntiAliasingLevel()
+ {
+ if (AntiAliasing <= 1) return 1;
+ //if(AntiAliasing % 2 != 0) throw new ArgumentException("AntiAliasing must be multiples of 2, otherwise use 0 or 1 to disable it", nameof(AntiAliasing));
+ return AntiAliasing;
+ }
+
+ /// <summary>
+ /// SuppressRebuildProperties = true, call the invoker and reset SuppressRebuildProperties = false
+ /// </summary>
+ /// <param name="action">Action work</param>
+ /// <param name="callRebuildOnEnd">True to force rebuild the layer properties after the work and before reset to false</param>
+ /// <param name="recalculateZPos">True to recalculate z position of each layer (requires <paramref name="callRebuildOnEnd"/> = true), otherwise false</param>
+ /// <param name="property">Property name to change for each layer, use null to update all properties (requires <paramref name="callRebuildOnEnd"/> = true)</param>
+ public void SuppressRebuildPropertiesWork(Action action, bool callRebuildOnEnd = false, bool recalculateZPos = true, string? property = null)
+ {
+ /*SuppressRebuildProperties = true;
+ action.Invoke();
+ if(callRebuildOnEnd) LayerManager.RebuildLayersProperties(recalculateZPos, property);
+ SuppressRebuildProperties = false;*/
+ SuppressRebuildPropertiesWork(() =>
{
- if (PrintParameterModifiers is null) return 0;
- byte changed = 0;
- foreach (var modifier in PrintParameterModifiers)
- {
- if(!modifier.HasChanged) continue;
- modifier.OldValue = modifier.NewValue;
- SetValueFromPrintParameterModifier(modifier, modifier.NewValue);
- changed++;
- }
+ action.Invoke();
+ return true;
+ }, callRebuildOnEnd, recalculateZPos, property);
+ }
- return changed;
+ /// <summary>
+ /// SuppressRebuildProperties = true, call the invoker and reset SuppressRebuildProperties = false
+ /// </summary>
+ /// <param name="action">Action work</param>
+ /// <param name="callRebuildOnEnd">True to force rebuild the layer properties after the work and before reset to false</param>
+ /// <param name="recalculateZPos">True to recalculate z position of each layer (requires <paramref name="callRebuildOnEnd"/> = true), otherwise false</param>
+ /// <param name="property">Property name to change for each layer, use null to update all properties (requires <paramref name="callRebuildOnEnd"/> = true)</param>
+ public bool SuppressRebuildPropertiesWork(Func<bool> action, bool callRebuildOnEnd = false, bool recalculateZPos = true, string? property = null)
+ {
+ bool result;
+ try
+ {
+ SuppressRebuildProperties = true;
+ result = action.Invoke();
+ if (callRebuildOnEnd && result) RebuildLayersProperties(recalculateZPos, property);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ throw;
}
+ finally
+ {
+ SuppressRebuildProperties = false;
+ }
+
+ return result;
+ }
+
+ public void UpdateGlobalPropertiesFromLayers()
+ {
+ if (LayerCount == 0) return;
+
+ SuppressRebuildPropertiesWork(() =>
+ {
+ var bottomLayer = FirstLayer;
+ if (bottomLayer is not null)
+ {
+ if (bottomLayer.LightOffDelay > 0) BottomLightOffDelay = bottomLayer.LightOffDelay;
+ if (bottomLayer.WaitTimeBeforeCure > 0) BottomWaitTimeBeforeCure = bottomLayer.WaitTimeBeforeCure;
+ if (bottomLayer.ExposureTime > 0) BottomExposureTime = bottomLayer.ExposureTime;
+ if (bottomLayer.WaitTimeAfterCure > 0) BottomWaitTimeAfterCure = bottomLayer.WaitTimeAfterCure;
+ if (bottomLayer.LiftHeight > 0) BottomLiftHeight = bottomLayer.LiftHeight;
+ if (bottomLayer.LiftSpeed > 0) BottomLiftSpeed = bottomLayer.LiftSpeed;
+ if (bottomLayer.LiftHeight2 > 0) BottomLiftHeight2 = bottomLayer.LiftHeight2;
+ if (bottomLayer.LiftSpeed2 > 0) BottomLiftSpeed2 = bottomLayer.LiftSpeed2;
+ if (bottomLayer.WaitTimeAfterLift > 0) BottomWaitTimeAfterLift = bottomLayer.WaitTimeAfterLift;
+ if (bottomLayer.RetractSpeed > 0) BottomRetractSpeed = bottomLayer.RetractSpeed;
+ if (bottomLayer.RetractHeight2 > 0) BottomRetractHeight2 = bottomLayer.RetractHeight2;
+ if (bottomLayer.RetractSpeed2 > 0) BottomRetractSpeed2 = bottomLayer.RetractSpeed2;
+ if (bottomLayer.LightPWM > 0) BottomLightPWM = bottomLayer.LightPWM;
+ }
+
+ var normalLayer = LastLayer;
+ if (normalLayer is not null)
+ {
+ if (normalLayer.LightOffDelay > 0) LightOffDelay = normalLayer.LightOffDelay;
+ if (normalLayer.WaitTimeBeforeCure > 0) WaitTimeBeforeCure = normalLayer.WaitTimeBeforeCure;
+ if (normalLayer.ExposureTime > 0) ExposureTime = normalLayer.ExposureTime;
+ if (normalLayer.WaitTimeAfterCure > 0) WaitTimeAfterCure = normalLayer.WaitTimeAfterCure;
+ if (normalLayer.LiftHeight > 0) LiftHeight = normalLayer.LiftHeight;
+ if (normalLayer.LiftSpeed > 0) LiftSpeed = normalLayer.LiftSpeed;
+ if (normalLayer.LiftHeight2 > 0) LiftHeight2 = normalLayer.LiftHeight2;
+ if (normalLayer.LiftSpeed2 > 0) LiftSpeed2 = normalLayer.LiftSpeed2;
+ if (normalLayer.WaitTimeAfterLift > 0) WaitTimeAfterLift = normalLayer.WaitTimeAfterLift;
+ if (normalLayer.RetractSpeed > 0) RetractSpeed = normalLayer.RetractSpeed;
+ if (normalLayer.RetractHeight2 > 0) RetractHeight2 = normalLayer.RetractHeight2;
+ if (normalLayer.RetractSpeed2 > 0) RetractSpeed2 = normalLayer.RetractSpeed2;
+ if (normalLayer.LightPWM > 0) LightPWM = normalLayer.LightPWM;
+ }
+ });
+ }
+
+ public void UpdatePrintTime()
+ {
+ PrintTime = PrintTimeComputed;
+ //Debug.WriteLine($"Time updated: {_printTime}s");
+ }
- public void SetNoDelays()
+ public void UpdatePrintTimeQueued()
+ {
+ lock (Mutex)
{
- BottomLightOffDelay = 0;
- LightOffDelay = 0;
- BottomWaitTimeBeforeCure = 0;
- WaitTimeBeforeCure = 0;
- BottomWaitTimeAfterCure = 0;
- WaitTimeAfterCure = 0;
- BottomWaitTimeAfterLift = 0;
- WaitTimeAfterLift = 0;
+ _queueTimerPrintTime.Stop();
+ _queueTimerPrintTime.Start();
}
+ }
- public float CalculateBottomLightOffDelay(float extraTime = 0) => CalculateLightOffDelay(true, extraTime);
+ /// <summary>
+ /// Converts millimeters to pixels given the current resolution and display size
+ /// </summary>
+ /// <param name="mm">Millimeters to convert</param>
+ /// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
+ /// <returns>Pixels</returns>
+ public uint MillimetersXToPixels(ushort mm, uint fallbackToPixels = 0)
+ {
+ var ppmm = Xppmm;
+ if (ppmm <= 0) return fallbackToPixels;
+ return (uint)(ppmm * mm);
+ }
- public bool SetBottomLightOffDelay(float extraTime = 0) => SetLightOffDelay(true, extraTime);
+ /// <summary>
+ /// Converts millimeters to pixels given the current resolution and display size
+ /// </summary>
+ /// <param name="mm">Millimeters to convert</param>
+ /// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
+ /// <returns>Pixels</returns>
+ public uint MillimetersYToPixels(ushort mm, uint fallbackToPixels = 0)
+ {
+ var ppmm = Yppmm;
+ if (ppmm <= 0) return fallbackToPixels;
+ return (uint)(ppmm * mm);
+ }
- public float CalculateNormalLightOffDelay(float extraTime = 0) => CalculateLightOffDelay(false, extraTime);
+ /// <summary>
+ /// Converts millimeters to pixels given the current resolution and display size
+ /// </summary>
+ /// <param name="mm">Millimeters to convert</param>
+ /// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
+ /// <returns>Pixels</returns>
+ public uint MillimetersToPixels(ushort mm, uint fallbackToPixels = 0)
+ {
+ var ppmm = PpmmMax;
+ if (ppmm <= 0) return fallbackToPixels;
+ return (uint)(ppmm * mm);
+ }
- public bool SetNormalLightOffDelay(float extraTime = 0) => SetLightOffDelay(false, extraTime);
+ /// <summary>
+ /// From a pixel position get the equivalent position on the display
+ /// </summary>
+ /// <param name="x">X position in pixels</param>
+ /// <param name="precision">Decimal precision</param>
+ /// <returns>Display position in millimeters</returns>
+ public float PixelToDisplayPositionX(int x, byte precision = 3) => (float)Math.Round(PixelWidth * x, precision);
- public float CalculateMotorMovementTime(bool isBottomLayer, float extraTime = 0)
- {
- return isBottomLayer
- ? OperationCalculator.LightOffDelayC.CalculateSeconds(BottomLiftHeight, BottomLiftSpeed, BottomRetractSpeed, extraTime, BottomLiftHeight2, BottomLiftSpeed2, BottomRetractHeight2, BottomRetractSpeed2)
- : OperationCalculator.LightOffDelayC.CalculateSeconds(LiftHeight, LiftSpeed, RetractSpeed, extraTime, LiftHeight2, LiftSpeed2, RetractHeight2, RetractSpeed2);
- }
+ /// <summary>
+ /// From a pixel position get the equivalent position on the display
+ /// </summary>
+ /// <param name="y">Y position in pixels</param>
+ /// <param name="precision">Decimal precision</param>
+ /// <returns>Display position in millimeters</returns>
+ public float PixelToDisplayPositionY(int y, byte precision = 3) => (float)Math.Round(PixelHeight * y, precision);
+
+ /// <summary>
+ /// From a pixel position get the equivalent position on the display
+ /// </summary>
+ /// <param name="x">X position in pixels</param>
+ /// <param name="y">Y position in pixels</param>
+ /// <param name="precision">Decimal precision</param>
+ /// <returns>Resolution position in pixels</returns>
+ public PointF PixelToDisplayPosition(int x, int y, byte precision = 3) =>new(PixelToDisplayPositionX(x, precision), PixelToDisplayPositionY(y, precision));
+ public PointF PixelToDisplayPosition(Point point, byte precision = 3) => new(PixelToDisplayPositionX(point.X, precision), PixelToDisplayPositionY(point.Y, precision));
+
+ /// <summary>
+ /// From a pixel position get the equivalent position on the display
+ /// </summary>
+ /// <param name="x">X position in millimeters</param>
+ /// <returns>Resolution position in pixels</returns>
+ public int DisplayToPixelPositionX(float x) => (int)(x * Xppmm);
+
+ /// <summary>
+ /// From a pixel position get the equivalent position on the display
+ /// </summary>
+ /// <param name="y">Y position in millimeters</param>
+ /// <returns>Resolution position in pixels</returns>
+ public int DisplayToPixelPositionY(float y) => (int)(y * Yppmm);
+
+ /// <summary>
+ /// From a pixel position get the equivalent position on the display
+ /// </summary>
+ /// <param name="x">X position in millimeters</param>
+ /// <param name="y">Y position in millimeters</param>
+ /// <returns>Resolution position in pixels</returns>
+ public Point DisplayToPixelPosition(float x, float y) => new(DisplayToPixelPositionX(x), DisplayToPixelPositionY(y));
+ public Point DisplayToPixelPosition(PointF point) => new(DisplayToPixelPositionX(point.X), DisplayToPixelPositionY(point.Y));
- public float CalculateLightOffDelay(bool isBottomLayer, float extraTime = 0)
+
+ public Rectangle GetBoundingRectangle(OperationProgress? progress = null)
+ {
+ var firstLayer = FirstLayer;
+ if (!_boundingRectangle.IsEmpty || LayerCount == 0 || firstLayer is null || !firstLayer.HaveImage) return _boundingRectangle;
+ progress ??= new OperationProgress(OperationProgress.StatusOptimizingBounds, LayerCount - 1);
+ _boundingRectangle = Rectangle.Empty;
+ uint firstValidLayerBounds = 0;
+
+ void FindFirstBoundingRectangle()
{
- extraTime = (float)Math.Round(extraTime, 2);
- if (SupportsGCode) return extraTime;
- return CalculateMotorMovementTime(isBottomLayer, extraTime);
+ for (uint layerIndex = 0; layerIndex < Count; layerIndex++)
+ {
+ firstValidLayerBounds = layerIndex;
+ if (this[layerIndex] is null || this[layerIndex].BoundingRectangle == Rectangle.Empty) continue;
+ _boundingRectangle = this[layerIndex].BoundingRectangle;
+ break;
+ }
}
- public bool SetLightOffDelay(bool isBottomLayer, float extraTime = 0)
+ FindFirstBoundingRectangle();
+ //_boundingRectangle = firstLayer.BoundingRectangle;
+
+ if (_boundingRectangle.IsEmpty) // Safe checking, all layers haven't a bounding rectangle
{
- float lightOff = CalculateLightOffDelay(isBottomLayer, extraTime);
- if (isBottomLayer)
+ progress.Reset(OperationProgress.StatusOptimizingBounds, LayerCount - 1);
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
{
- if (BottomLightOffDelay != lightOff)
- {
- BottomLightOffDelay = lightOff;
- return true;
- }
+ if (progress.Token.IsCancellationRequested) return;
- return false;
- }
-
- if (LightOffDelay != lightOff)
+ this[layerIndex].GetBoundingRectangle();
+
+ progress.LockAndIncrement();
+ });
+
+ if (progress.Token.IsCancellationRequested)
{
- LightOffDelay = lightOff;
- return true;
+ _boundingRectangle = Rectangle.Empty;
+ progress.Token.ThrowIfCancellationRequested();
}
- return false;
+ FindFirstBoundingRectangle();
}
- /// <summary>
- /// Attempt to set wait time before cure if supported, otherwise fallback to light-off delay
- /// </summary>
- /// <param name="time">The time to set</param>
- /// <param name="zeroLightOffDelayCalculateBase">When true and time is zero, it will calculate light-off delay without extra time, otherwise false to set light-off delay to 0 when time is 0</param>
- public void SetWaitTimeBeforeCureOrLightOffDelay(bool isBottomLayer, float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ if (firstValidLayerBounds + 1 < LayerCount)
{
- if (isBottomLayer)
- {
- SetBottomWaitTimeBeforeCureOrLightOffDelay(time, zeroLightOffDelayCalculateBase);
- }
- else
+ progress.Reset(OperationProgress.StatusCalculatingBounds, LayerCount - firstValidLayerBounds - 1);
+ for (var i = firstValidLayerBounds + 1; i < LayerCount; i++)
{
- SetNormalWaitTimeBeforeCureOrLightOffDelay(time, zeroLightOffDelayCalculateBase);
+ if (this[i] is null || this[i].BoundingRectangle.IsEmpty) continue;
+ _boundingRectangle = Rectangle.Union(_boundingRectangle, this[i].BoundingRectangle);
+ progress++;
}
}
- public void SetBottomWaitTimeBeforeCureOrLightOffDelay(float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ RaisePropertyChanged(nameof(BoundingRectangle));
+ return _boundingRectangle;
+ }
+
+
+ /// <summary>
+ /// Creates a empty mat of file <see cref="Resolution"/> size and create a dummy pixel to prevent a empty layer detection
+ /// </summary>
+ /// <param name="dummyPixelLocation">Location to set the dummy pixel, use a negative value (-1,-1) to set to the bounding center</param>
+ /// <param name="dummyPixelBrightness">Dummy pixel brightness</param>
+ /// <returns></returns>
+ public Mat CreateMatWithDummyPixel(Point dummyPixelLocation, byte dummyPixelBrightness)
+ {
+ var newMat = EmguExtensions.InitMat(Resolution);
+ if (dummyPixelBrightness > 0)
{
- if (CanUseBottomWaitTimeBeforeCure)
- {
- BottomLightOffDelay = 0;
- BottomWaitTimeBeforeCure = time;
- }
- else if (CanUseBottomLightOffDelay)
- {
- if (time == 0 && !zeroLightOffDelayCalculateBase)
- {
- BottomLightOffDelay = 0;
- return;
- }
+ if (dummyPixelLocation.IsAnyNegative()) dummyPixelLocation = BoundingRectangle.Center();
+ newMat.SetByte(newMat.GetPixelPos(dummyPixelLocation), dummyPixelBrightness);
+ }
- SetBottomLightOffDelay(time);
- }
+ return newMat;
+ }
+
+ /// <summary>
+ /// Creates a empty mat of file <see cref="Resolution"/> size and create a dummy pixel to prevent a empty layer detection
+ /// </summary>
+ /// <param name="dummyPixelLocation">Location to set the dummy pixel, use a negative value (-1,-1) to set to the bounding center</param>
+ /// <returns></returns>
+ public Mat CreateMatWithDummyPixel(Point dummyPixelLocation) => CreateMatWithDummyPixel(dummyPixelLocation, SupportsGCode ? (byte) 1 : (byte) 128);
+
+ /// <summary>
+ /// Creates a empty mat of file <see cref="Resolution"/> size
+ /// </summary>
+ /// <param name="dummyPixelBrightness">Dummy pixel brightness</param>
+ /// <returns></returns>
+ public Mat CreateMatWithDummyPixel(byte dummyPixelBrightness) => CreateMatWithDummyPixel(BoundingRectangle.Center(), dummyPixelBrightness);
+
+ /// <summary>
+ /// Creates a empty mat of file <see cref="Resolution"/> size
+ /// </summary>
+ /// <returns></returns>
+ public Mat CreateMatWithDummyPixel() => CreateMatWithDummyPixel(SupportsGCode ? (byte)1 : (byte)128);
+
+ /// <summary>
+ /// Creates a empty mat of file <see cref="Resolution"/> size
+ /// </summary>
+ /// <param name="initMat">True to black out the mat</param>
+ /// <returns></returns>
+ public Mat CreateMat(bool initMat = true)
+ {
+ return initMat
+ ? EmguExtensions.InitMat(Resolution)
+ : new Mat(Resolution, DepthType.Cv8U, 1);
+ }
+
+ #endregion
+
+ #region Layer collection methods
+ public void Init(Layer[] layers)
+ {
+ var oldLayerCount = LayerCount;
+ _layers = layers;
+ if (LayerCount != oldLayerCount)
+ {
+ LayerCount = LayerCount;
}
- public void SetNormalWaitTimeBeforeCureOrLightOffDelay(float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ SanitizeLayers();
+ }
+
+ public void Init(uint layerCount, bool initializeLayers = false)
+ {
+ var oldLayerCount = LayerCount;
+ _layers = new Layer[layerCount];
+ if (initializeLayers)
{
- if (CanUseWaitTimeBeforeCure)
+ for (uint layerIndex = 0; layerIndex < layerCount; layerIndex++)
{
- LightOffDelay = 0;
- WaitTimeBeforeCure = time;
+ _layers[layerIndex] = new Layer(layerIndex, this);
}
- else if (CanUseLightOffDelay)
- {
- if (time == 0 && !zeroLightOffDelayCalculateBase)
- {
- LightOffDelay = 0;
- return;
- }
+ }
- SetNormalLightOffDelay(time);
- }
+ if (LayerCount != oldLayerCount)
+ {
+ LayerCount = LayerCount;
}
+ }
- /// <summary>
- /// Rebuilds GCode based on current settings
- /// </summary>
- public virtual void RebuildGCode()
+ public void Add(Layer layer)
+ {
+ Layers = _layers.Append(layer).ToArray();
+ }
+
+ public void Add(IEnumerable<Layer> layers)
+ {
+ var list = _layers.ToList();
+ list.AddRange(layers);
+ Layers = list.ToArray();
+ }
+
+ public bool Contains(Layer layer)
+ {
+ return _layers.Contains(layer);
+ }
+
+ public void CopyTo(Layer[] array, int arrayIndex)
+ {
+ _layers.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(Layer layer)
+ {
+ var list = _layers.ToList();
+ var result = list.Remove(layer);
+ if (result)
{
- if (!SupportsGCode || _suppressRebuildGCode) return;
- GCode.RebuildGCode(this);
- RaisePropertyChanged(nameof(GCodeStr));
+ Layers = list.ToArray();
}
- /// <summary>
- /// Saves current configuration on input file
- /// </summary>
- /// <param name="progress"></param>
- public void Save(OperationProgress progress = null)
+ return result;
+ }
+
+ public int IndexOf(Layer layer)
+ {
+ for (int layerIndex = 0; layerIndex < Count; layerIndex++)
{
- SaveAs(null, progress);
+ if (_layers[layerIndex].Equals(layer)) return layerIndex;
}
- /// <summary>
- /// Saves current configuration on a copy
- /// </summary>
- /// <param name="filePath">File path to save copy as, use null to overwrite active file (Same as <see cref="Save"/>)</param>
- /// <param name="progress"></param>
- public void SaveAs(string filePath = null, OperationProgress progress = null)
+ return -1;
+ }
+
+ public void Prepend(Layer layer) => Insert(0, layer);
+ public void Prepend(IEnumerable<Layer> layers) => InsertRange(0, layers);
+ public void Append(Layer layer) => Add(layer);
+ public void AppendRange(IEnumerable<Layer> layers) => Add(layers);
+
+ public void Insert(int index, Layer layer)
+ {
+ if (index < 0) return;
+ if (index > Count)
{
- if (RequireFullEncode)
- {
- if (!string.IsNullOrEmpty(filePath))
- {
- FileFullPath = filePath;
- }
- Encode(FileFullPath, progress);
- return;
- }
+ Add(layer); // Append
+ return;
+ }
- if (!string.IsNullOrEmpty(filePath))
- {
- File.Copy(FileFullPath, filePath, true);
- FileFullPath = filePath;
+ var list = _layers.ToList();
+ list.Insert(index, layer);
+ Layers = list.ToArray();
+ }
- }
+ public void InsertRange(int index, IEnumerable<Layer> layers)
+ {
+ if (index < 0) return;
- progress ??= new OperationProgress();
- PartialSaveInternally(progress);
+ if (index > Count)
+ {
+ Add(layers);
+ return;
}
- /// <summary>
- /// Partial save of the file, this is the file information only.
- /// When this function is called it's already ready to save to file
- /// </summary>
- /// <param name="progress"></param>
- protected abstract void PartialSaveInternally(OperationProgress progress);
+ var list = _layers.ToList();
+ list.InsertRange(index, layers);
+ Layers = list.ToArray();
+ }
- /// <summary>
- /// Triggers when a conversion is valid and before start converting values
- /// </summary>
- /// <param name="source">Source file format</param>
- /// <returns>True to continue the conversion, otherwise false to stop</returns>
- protected virtual bool OnBeforeConvertFrom(FileFormat source) => true;
+ public void RemoveAt(int index)
+ {
+ if (index >= LastLayerIndex) return;
+ var list = _layers.ToList();
+ list.RemoveAt(index);
+ Layers = list.ToArray();
+ }
- /// <summary>
- /// Triggers when a conversion is valid and before start converting values
- /// </summary>
- /// <param name="output">Target file format</param>
- /// <returns>True to continue the conversion, otherwise false to stop</returns>
- protected virtual bool OnBeforeConvertTo(FileFormat output) => true;
+ public void RemoveRange(int index, int count)
+ {
+ if (count <= 0 || index >= LastLayerIndex) return;
+ var list = _layers.ToList();
+ list.RemoveRange(index, count);
+ Layers = list.ToArray();
+ }
- /// <summary>
- /// Triggers when the conversion is made but before encoding
- /// </summary>
- /// <param name="source">Source file format</param>
- /// <returns>True to continue the conversion, otherwise false to stop</returns>
- protected virtual bool OnAfterConvertFrom(FileFormat source) => true;
+ /// <summary>
+ /// Clone layers
+ /// </summary>
+ /// <returns></returns>
+ public Layer[] CloneLayers()
+ {
+ return Layer.CloneLayers(_layers);
+ }
- /// <summary>
- /// Triggers when the conversion is made but before encoding
- /// </summary>
- /// <param name="output">Output file format</param>
- /// <returns>True to continue the conversion, otherwise false to stop</returns>
- protected virtual bool OnAfterConvertTo(FileFormat output) => true;
+ /*
+ /// <summary>
+ /// Removes all null layers in the collection
+ /// </summary>
+ public void RemoveNullLayers()
+ {
+ var oldCount = LayerCount;
+ var layers = this.Where(layer => layer is not null).ToArray();
+ if (layers.Length == oldCount) return;
+ Layers = layers;
+ }*/
- /// <summary>
- /// Converts this file type to another file type
- /// </summary>
- /// <param name="to">Target file format</param>
- /// <param name="fileFullPath">Output path file</param>
- /// <param name="version">File version to use</param>
- /// <param name="progress"></param>
- /// <returns>The converted file if successful, otherwise null</returns>
- public virtual FileFormat Convert(Type to, string fileFullPath, uint version = 0, OperationProgress progress = null)
- {
- if (!IsValid) return null;
- var found = AvailableFormats.Any(format => to == format.GetType());
- if (!found) return null;
-
- progress ??= new OperationProgress("Converting");
-
- var slicerFile = (FileFormat)Activator.CreateInstance(to);
- if (slicerFile is null) return null;
- slicerFile.FileFullPath = fileFullPath;
+ /// <summary>
+ /// Reallocate with new size
+ /// </summary>
+ /// <returns></returns>
+ public Layer[] ReallocateNew(uint newLayerCount, bool makeClone = false)
+ {
+ var layers = new Layer[newLayerCount];
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ if (layerIndex >= newLayerCount) break;
+ var layer = this[layerIndex];
+ layers[layerIndex] = makeClone ? layer.Clone() : layer;
+ }
- if (!slicerFile.OnBeforeConvertFrom(this)) return null;
- if (!OnBeforeConvertTo(slicerFile)) return null;
+ return layers;
+ }
- if (version > 0 && version != DefaultVersion)
- {
- slicerFile.Version = version;
- }
+ /// <summary>
+ /// Reallocate layer count with a new size
+ /// </summary>
+ /// <param name="newLayerCount">New layer count</param>
+ /// <param name="initBlack"></param>
+ public void Reallocate(uint newLayerCount, bool initBlack = false)
+ {
+ var oldLayerCount = LayerCount;
+ int differenceLayerCount = (int)newLayerCount - Count;
+ if (differenceLayerCount == 0) return;
+ var newLayers = new Layer[newLayerCount];
- slicerFile.SuppressRebuildPropertiesWork(() =>
- {
- slicerFile.LayerManager.Init(LayerManager.CloneLayers());
- slicerFile.AntiAliasing = ValidateAntiAliasingLevel();
- slicerFile.LayerCount = LayerManager.LayerCount;
- slicerFile.BottomLayerCount = BottomLayerCount;
- slicerFile.TransitionLayerCount = TransitionLayerCount;
- slicerFile.LayerHeight = LayerHeight;
- slicerFile.ResolutionX = ResolutionX;
- slicerFile.ResolutionY = ResolutionY;
- slicerFile.DisplayWidth = DisplayWidth;
- slicerFile.DisplayHeight = DisplayHeight;
- slicerFile.MachineZ = MachineZ;
- slicerFile.DisplayMirror = DisplayMirror;
-
- // Exposure
- slicerFile.BottomExposureTime = BottomExposureTime;
- slicerFile.ExposureTime = ExposureTime;
-
- // Lifts
- slicerFile.BottomLiftHeight = BottomLiftHeight;
- slicerFile.BottomLiftSpeed = BottomLiftSpeed;
-
- slicerFile.LiftHeight = LiftHeight;
- slicerFile.LiftSpeed = LiftSpeed;
+ Array.Copy(_layers, 0, newLayers, 0, Math.Min(newLayerCount, newLayers.Length));
- slicerFile.BottomLiftSpeed2 = BottomLiftSpeed2;
- slicerFile.LiftSpeed2 = LiftSpeed2;
+ if (differenceLayerCount > 0 && initBlack)
+ {
+ using var blackMat = EmguExtensions.InitMat(Resolution);
+ var pngBytes = blackMat.GetPngByes();
+ for (var layerIndex = oldLayerCount; layerIndex < newLayerCount; layerIndex++)
+ {
+ newLayers[layerIndex] = new Layer(layerIndex, pngBytes.ToArray(), this);
+ }
+ }
- slicerFile.BottomRetractSpeed = BottomRetractSpeed;
- slicerFile.RetractSpeed = RetractSpeed;
+ SuppressRebuildPropertiesWork(() =>
+ {
+ Layers = newLayers;
+ });
+ }
- slicerFile.BottomRetractSpeed2 = BottomRetractSpeed2;
- slicerFile.RetractSpeed2 = RetractSpeed2;
+ /// <summary>
+ /// Reallocate at given index
+ /// </summary>
+ /// <returns></returns>
+ public void ReallocateInsert(uint insertAtLayerIndex, uint layerCount, bool initBlack = false)
+ {
+ if (layerCount == 0) return;
+ insertAtLayerIndex = Math.Min(insertAtLayerIndex, LayerCount);
+ var newLayers = new Layer[LayerCount + layerCount];
+ // Copy from start to insert index
+ if (insertAtLayerIndex > 0)
+ Array.Copy(_layers, 0, newLayers, 0, insertAtLayerIndex);
- if (slicerFile.CanUseAnyLiftHeight2 && (CanUseAnyLiftHeight2 || GetType() == typeof(SL1File))) // Both are TSMC compatible
- {
- slicerFile.BottomLiftHeight2 = BottomLiftHeight2;
- slicerFile.LiftHeight2 = LiftHeight2;
+ // Rearrange from last insert to end
+ if (insertAtLayerIndex < LayerCount)
+ Array.Copy(
+ _layers, insertAtLayerIndex,
+ newLayers, insertAtLayerIndex + layerCount,
+ LayerCount - insertAtLayerIndex);
+ /*for (uint layerIndex = insertAtLayerIndex; layerIndex < _layers.Length; layerIndex++)
+ {
+ newLayers[layerCount + layerIndex] = _layers[layerIndex];
+ newLayers[layerCount + layerIndex].Index = layerCount + layerIndex;
+ }*/
- slicerFile.BottomRetractHeight2 = BottomRetractHeight2;
- slicerFile.RetractHeight2 = RetractHeight2;
- }
- /*else if (slicerFile.CanUseAnyLiftHeight2) // Output format is compatible with TSMC, but input isn't
- {
- slicerFile.BottomLiftHeight = BottomLiftHeight;
- slicerFile.LiftHeight = LiftHeight;
- }*/
- else if (CanUseAnyLiftHeight2) // Output format isn't compatible with TSMC, but input is
- {
- slicerFile.BottomLiftHeight = BottomLiftHeightTotal;
- slicerFile.LiftHeight = LiftHeightTotal;
+ // Allocate new layers in between
+ if (initBlack)
+ {
+ using var blackMat = EmguExtensions.InitMat(Resolution);
+ var pngBytes = blackMat.GetPngByes();
+ for (var layerIndex = insertAtLayerIndex; layerIndex < insertAtLayerIndex + layerCount; layerIndex++)
+ {
+ newLayers[layerIndex] = new Layer(layerIndex, pngBytes.ToArray(), this);
+ }
+ }
- // Set to the slowest retract speed
- if (BottomRetractSpeed2 > 0 && BottomRetractSpeed > BottomRetractSpeed2)
- {
- slicerFile.BottomRetractSpeed = BottomRetractSpeed2;
- }
+ SuppressRebuildPropertiesWork(() =>
+ {
+ Layers = newLayers;
+ });
+ }
- // Set to the slowest retract speed
- if (RetractSpeed2 > 0 && RetractSpeed > RetractSpeed2)
- {
- slicerFile.RetractSpeed = RetractSpeed2;
- }
- }
+ /// <summary>
+ /// Reallocate at a kept range
+ /// </summary>
+ /// <param name="startLayerIndex"></param>
+ /// <param name="endLayerIndex"></param>
+ public void ReallocateKeepRange(uint startLayerIndex, uint endLayerIndex)
+ {
+ if ((int)(endLayerIndex - startLayerIndex) < 0) return;
+ var newLayers = new Layer[1 + endLayerIndex - startLayerIndex];
- // Wait times
- slicerFile.BottomLightOffDelay = BottomLightOffDelay;
- slicerFile.LightOffDelay = LightOffDelay;
+ Array.Copy(_layers, startLayerIndex, newLayers, 0, newLayers.Length);
+ /*uint currentLayerIndex = 0;
+ for (uint layerIndex = startLayerIndex; layerIndex <= endLayerIndex; layerIndex++)
+ {
+ newLayers[currentLayerIndex++] = _layers[layerIndex];
+ }*/
- slicerFile.BottomWaitTimeBeforeCure = BottomWaitTimeBeforeCure;
- slicerFile.WaitTimeBeforeCure = WaitTimeBeforeCure;
+ SuppressRebuildPropertiesWork(() =>
+ {
+ Layers = newLayers;
+ });
+ }
- slicerFile.BottomWaitTimeAfterCure = BottomWaitTimeAfterCure;
- slicerFile.WaitTimeAfterCure = WaitTimeAfterCure;
+ /// <summary>
+ /// Reallocate at start
+ /// </summary>
+ /// <returns></returns>
+ public void ReallocateStart(uint layerCount, bool initBlack = false) => ReallocateInsert(0, layerCount, initBlack);
- slicerFile.BottomWaitTimeAfterLift = BottomWaitTimeAfterLift;
- slicerFile.WaitTimeAfterLift = WaitTimeAfterLift;
+ /// <summary>
+ /// Reallocate at end
+ /// </summary>
+ /// <returns></returns>
+ public void ReallocateEnd(uint layerCount, bool initBlack = false) => ReallocateInsert(LayerCount, layerCount, initBlack);
- slicerFile.BottomLightPWM = BottomLightPWM;
- slicerFile.LightPWM = LightPWM;
+ /// <summary>
+ /// Allocate layers from a Mat array
+ /// </summary>
+ /// <param name="mats"></param>
+ /// <returns>The new Layer array</returns>
+ public Layer[] AllocateFromMat(Mat[] mats)
+ {
+ var layers = new Layer[mats.Length];
+ Parallel.For(0, mats.Length, CoreSettings.ParallelOptions, i =>
+ {
+ layers[i] = new Layer((uint)i, mats[i], this);
+ });
+ return layers;
+ }
- slicerFile.MachineName = MachineName;
- slicerFile.MaterialName = MaterialName;
- slicerFile.MaterialMilliliters = MaterialMilliliters;
- slicerFile.MaterialGrams = MaterialGrams;
- slicerFile.MaterialCost = MaterialCost;
- slicerFile.Xppmm = Xppmm;
- slicerFile.Yppmm = Yppmm;
- slicerFile.PrintTime = PrintTime;
- slicerFile.PrintHeight = PrintHeight;
+ /// <summary>
+ /// Allocate layers from a Mat array and set them to the current file
+ /// </summary>
+ /// <param name="mats"></param>
+ /// /// <returns>The new Layer array</returns>
+ public Layer[] AllocateAndSetFromMat(Mat[] mats)
+ {
+ var layers = AllocateFromMat(mats);
+ Layers = layers;
+ return layers;
+ }
- slicerFile.SetThumbnails(Thumbnails);
- });
- if (!slicerFile.OnAfterConvertFrom(this)) return null;
- if (!OnAfterConvertTo(slicerFile)) return null;
+ #endregion
- slicerFile.Encode(fileFullPath, progress);
+ #region Layer methods
- return slicerFile;
+ /// <summary>
+ /// Re-assign layer indexes and parent <see cref="FileFormat"/>
+ /// </summary>
+ public void SanitizeLayers()
+ {
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ if(this[layerIndex] is null) continue;
+ this[layerIndex].Index = layerIndex;
+ this[layerIndex].SlicerFile = this;
}
+ }
- /// <summary>
- /// Converts this file type to another file type
- /// </summary>
- /// <param name="to">Target file format</param>
- /// <param name="fileFullPath">Output path file</param>
- /// <param name="version">File version</param>
- /// <param name="progress"></param>
- /// <returns>TThe converted file if successful, otherwise null</returns>
- public FileFormat Convert(FileFormat to, string fileFullPath, uint version = 0, OperationProgress progress = null)
- => Convert(to.GetType(), fileFullPath, version, progress);
+ /// <summary>
+ /// Sanitize file and thrown exception if a severe problem is found
+ /// </summary>
+ /// <returns>True if one or more corrections has been applied, otherwise false</returns>
+ public bool Sanitize()
+ {
+ bool appliedCorrections = false;
- /// <summary>
- /// Validate AntiAlias Level
- /// </summary>
- public byte ValidateAntiAliasingLevel()
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- if (AntiAliasing <= 1) return 1;
- //if(AntiAliasing % 2 != 0) throw new ArgumentException("AntiAliasing must be multiples of 2, otherwise use 0 or 1 to disable it", nameof(AntiAliasing));
- return AntiAliasing;
+ // Check for null layers
+ if (this[layerIndex] is null) throw new InvalidDataException($"Layer {layerIndex} was defined but doesn't contain a valid image.");
+ if (layerIndex <= 0) continue;
+ // Check for bigger position z than it successor
+ if (this[layerIndex - 1].PositionZ > this[layerIndex].PositionZ && this[layerIndex - 1].NonZeroPixelCount > 1)
+ throw new InvalidDataException($"Layer {layerIndex - 1} ({this[layerIndex - 1].PositionZ}mm) have a higher Z position than the successor layer {layerIndex} ({this[layerIndex].PositionZ}mm).\n");
}
- /// <summary>
- /// SuppressRebuildProperties = true, call the invoker and reset SuppressRebuildProperties = false
- /// </summary>
- /// <param name="action">Action work</param>
- /// <param name="callRebuildOnEnd">True to force rebuild the layer properties after the work and before reset to false</param>
- /// <param name="recalculateZPos">True to recalculate z position of each layer (requires <paramref name="callRebuildOnEnd"/> = true), otherwise false</param>
- /// <param name="property">Property name to change for each layer, use null to update all properties (requires <paramref name="callRebuildOnEnd"/> = true)</param>
- public void SuppressRebuildPropertiesWork(Action action, bool callRebuildOnEnd = false, bool recalculateZPos = true, string property = null)
+ if (ResolutionX == 0 || ResolutionY == 0)
{
- /*SuppressRebuildProperties = true;
- action.Invoke();
- if(callRebuildOnEnd) LayerManager.RebuildLayersProperties(recalculateZPos, property);
- SuppressRebuildProperties = false;*/
- SuppressRebuildPropertiesWork(() =>
+ var layer = FirstLayer;
+ if (layer is not null)
{
- action.Invoke();
- return true;
- }, callRebuildOnEnd, recalculateZPos, property);
- }
+ using var mat = layer.LayerMat;
- /// <summary>
- /// SuppressRebuildProperties = true, call the invoker and reset SuppressRebuildProperties = false
- /// </summary>
- /// <param name="action">Action work</param>
- /// <param name="callRebuildOnEnd">True to force rebuild the layer properties after the work and before reset to false</param>
- /// <param name="recalculateZPos">True to recalculate z position of each layer (requires <paramref name="callRebuildOnEnd"/> = true), otherwise false</param>
- /// <param name="property">Property name to change for each layer, use null to update all properties (requires <paramref name="callRebuildOnEnd"/> = true)</param>
- public bool SuppressRebuildPropertiesWork(Func<bool> action, bool callRebuildOnEnd = false, bool recalculateZPos = true, string property = null)
- {
- bool result;
- try
- {
- SuppressRebuildProperties = true;
- result = action.Invoke();
- if (callRebuildOnEnd && result) LayerManager.RebuildLayersProperties(recalculateZPos, property);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
+ if (mat.Size.HaveZero())
+ {
+ throw new FileLoadException($"File resolution ({Resolution}) is invalid and can't be auto fixed due invalid layers with same problem ({mat.Size}).", FileFullPath);
+ }
+
+ Resolution = mat.Size;
+ appliedCorrections = true;
}
- finally
+ }
+
+ // Fix 0mm positions at layer 0
+ if (this[0].PositionZ == 0)
+ {
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- SuppressRebuildProperties = false;
+ this[layerIndex].PositionZ = Layer.RoundHeight(this[layerIndex].PositionZ + LayerHeight);
}
-
- return result;
+
+ appliedCorrections = true;
+ }
+
+ // Fix LightPWM of 0
+ if (LightPWM == 0)
+ {
+ LightPWM = DefaultLightPWM;
+ appliedCorrections = true;
+ }
+ if (BottomLightPWM == 0)
+ {
+ BottomLightPWM = DefaultBottomLightPWM;
+ appliedCorrections = true;
}
- public void UpdateGlobalPropertiesFromLayers()
+ return appliedCorrections;
+ }
+
+ /// <summary>
+ /// Rebuild layer properties based on slice settings
+ /// </summary>
+ public void RebuildLayersProperties(bool recalculateZPos = true, string? property = null)
+ {
+ //var layerHeight = SlicerFile.LayerHeight;
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- if (LayerCount == 0) return;
+ var layer = this[layerIndex];
+ layer.Index = layerIndex;
+ layer.SlicerFile = this;
+
+ if (recalculateZPos)
+ {
+ layer.PositionZ = GetHeightFromLayer(layerIndex);
+ }
- SuppressRebuildPropertiesWork(() =>
+ if (property != string.Empty)
{
- var bottomLayer = FirstLayer;
- if (bottomLayer is not null)
+ if (property is null or nameof(BottomLayerCount))
{
- if (bottomLayer.LightOffDelay > 0) BottomLightOffDelay = bottomLayer.LightOffDelay;
- if (bottomLayer.WaitTimeBeforeCure > 0) BottomWaitTimeBeforeCure = bottomLayer.WaitTimeBeforeCure;
- if (bottomLayer.ExposureTime > 0) BottomExposureTime = bottomLayer.ExposureTime;
- if (bottomLayer.WaitTimeAfterCure > 0) BottomWaitTimeAfterCure = bottomLayer.WaitTimeAfterCure;
- if (bottomLayer.LiftHeight > 0) BottomLiftHeight = bottomLayer.LiftHeight;
- if (bottomLayer.LiftSpeed > 0) BottomLiftSpeed = bottomLayer.LiftSpeed;
- if (bottomLayer.LiftHeight2 > 0) BottomLiftHeight2 = bottomLayer.LiftHeight2;
- if (bottomLayer.LiftSpeed2 > 0) BottomLiftSpeed2 = bottomLayer.LiftSpeed2;
- if (bottomLayer.WaitTimeAfterLift > 0) BottomWaitTimeAfterLift = bottomLayer.WaitTimeAfterLift;
- if (bottomLayer.RetractSpeed > 0) BottomRetractSpeed = bottomLayer.RetractSpeed;
- if (bottomLayer.RetractHeight2 > 0) BottomRetractHeight2 = bottomLayer.RetractHeight2;
- if (bottomLayer.RetractSpeed2 > 0) BottomRetractSpeed2 = bottomLayer.RetractSpeed2;
- if (bottomLayer.LightPWM > 0) BottomLightPWM = bottomLayer.LightPWM;
+ layer.LightOffDelay = GetBottomOrNormalValue(layer, BottomLightOffDelay, LightOffDelay);
+ layer.WaitTimeBeforeCure = GetBottomOrNormalValue(layer, BottomWaitTimeBeforeCure, WaitTimeBeforeCure);
+ layer.ExposureTime = GetBottomOrNormalValue(layer, BottomExposureTime, ExposureTime);
+ layer.WaitTimeAfterCure = GetBottomOrNormalValue(layer, BottomWaitTimeAfterCure, WaitTimeAfterCure);
+ layer.LiftHeight = GetBottomOrNormalValue(layer, BottomLiftHeight, LiftHeight);
+ layer.LiftSpeed = GetBottomOrNormalValue(layer, BottomLiftSpeed, LiftSpeed);
+ layer.LiftHeight2 = GetBottomOrNormalValue(layer, BottomLiftHeight2, LiftHeight2);
+ layer.LiftSpeed2 = GetBottomOrNormalValue(layer, BottomLiftSpeed2, LiftSpeed2);
+ layer.WaitTimeAfterLift = GetBottomOrNormalValue(layer, BottomWaitTimeAfterLift, WaitTimeAfterLift);
+ layer.RetractSpeed = GetBottomOrNormalValue(layer, BottomRetractSpeed, RetractSpeed);
+ layer.RetractHeight2 = GetBottomOrNormalValue(layer, BottomRetractHeight2, RetractHeight2);
+ layer.RetractSpeed2 = GetBottomOrNormalValue(layer, BottomRetractSpeed2, RetractSpeed2);
+ layer.LightPWM = GetBottomOrNormalValue(layer, BottomLightPWM, LightPWM);
}
-
- var normalLayer = LastLayer;
- if (normalLayer is not null)
+ else
{
- if (normalLayer.LightOffDelay > 0) LightOffDelay = normalLayer.LightOffDelay;
- if (normalLayer.WaitTimeBeforeCure > 0) WaitTimeBeforeCure = normalLayer.WaitTimeBeforeCure;
- if (normalLayer.ExposureTime > 0) ExposureTime = normalLayer.ExposureTime;
- if (normalLayer.WaitTimeAfterCure > 0) WaitTimeAfterCure = normalLayer.WaitTimeAfterCure;
- if (normalLayer.LiftHeight > 0) LiftHeight = normalLayer.LiftHeight;
- if (normalLayer.LiftSpeed > 0) LiftSpeed = normalLayer.LiftSpeed;
- if (normalLayer.LiftHeight2 > 0) LiftHeight2 = normalLayer.LiftHeight2;
- if (normalLayer.LiftSpeed2 > 0) LiftSpeed2 = normalLayer.LiftSpeed2;
- if (normalLayer.WaitTimeAfterLift > 0) WaitTimeAfterLift = normalLayer.WaitTimeAfterLift;
- if (normalLayer.RetractSpeed > 0) RetractSpeed = normalLayer.RetractSpeed;
- if (normalLayer.RetractHeight2 > 0) RetractHeight2 = normalLayer.RetractHeight2;
- if (normalLayer.RetractSpeed2 > 0) RetractSpeed2 = normalLayer.RetractSpeed2;
- if (normalLayer.LightPWM > 0) LightPWM = normalLayer.LightPWM;
+ if (layer.IsBottomLayer)
+ {
+ if (property == nameof(BottomLightOffDelay)) layer.LightOffDelay = BottomLightOffDelay;
+ else if (property == nameof(BottomWaitTimeBeforeCure)) layer.WaitTimeBeforeCure = BottomWaitTimeBeforeCure;
+ else if (property == nameof(BottomExposureTime)) layer.ExposureTime = BottomExposureTime;
+ else if (property == nameof(BottomWaitTimeAfterCure)) layer.WaitTimeAfterCure = BottomWaitTimeAfterCure;
+ else if (property == nameof(BottomLiftHeight)) layer.LiftHeight = BottomLiftHeight;
+ else if (property == nameof(BottomLiftSpeed)) layer.LiftSpeed = BottomLiftSpeed;
+ else if (property == nameof(BottomLiftHeight2)) layer.LiftHeight2 = BottomLiftHeight2;
+ else if (property == nameof(BottomLiftSpeed2)) layer.LiftSpeed2 = BottomLiftSpeed2;
+ else if (property == nameof(BottomWaitTimeAfterLift)) layer.WaitTimeAfterLift = BottomWaitTimeAfterLift;
+ else if (property == nameof(BottomRetractSpeed)) layer.RetractSpeed = BottomRetractSpeed;
+ else if (property == nameof(BottomRetractHeight2)) layer.RetractHeight2 = BottomRetractHeight2;
+ else if (property == nameof(BottomRetractSpeed2)) layer.RetractSpeed2 = BottomRetractSpeed2;
+ else if (property == nameof(BottomLightPWM)) layer.LightPWM = BottomLightPWM;
+
+ // Propagate value to layer when bottom property does not exists
+ else if (property == nameof(LightOffDelay) && !CanUseBottomLightOffDelay) layer.LightOffDelay = LightOffDelay;
+ else if (property == nameof(WaitTimeBeforeCure) && !CanUseBottomWaitTimeBeforeCure) layer.WaitTimeBeforeCure = WaitTimeBeforeCure;
+ else if (property == nameof(ExposureTime) && !CanUseBottomExposureTime) layer.ExposureTime = ExposureTime;
+ else if (property == nameof(WaitTimeAfterCure) && !CanUseBottomWaitTimeAfterCure) layer.WaitTimeAfterCure = WaitTimeAfterCure;
+ else if (property == nameof(LiftHeight) && !CanUseBottomLiftHeight) layer.LiftHeight = LiftHeight;
+ else if (property == nameof(LiftSpeed) && !CanUseBottomLiftSpeed) layer.LiftSpeed = LiftSpeed;
+ else if (property == nameof(LiftHeight2) && !CanUseBottomLiftHeight2) layer.LiftHeight2 = LiftHeight2;
+ else if (property == nameof(LiftSpeed2) && !CanUseBottomLiftSpeed2) layer.LiftSpeed2 = LiftSpeed2;
+ else if (property == nameof(WaitTimeAfterLift) && !CanUseBottomWaitTimeAfterLift) layer.WaitTimeAfterLift = WaitTimeAfterLift;
+ else if (property == nameof(RetractSpeed) && !CanUseBottomRetractSpeed) layer.RetractSpeed = RetractSpeed;
+ else if (property == nameof(RetractHeight2) && !CanUseBottomRetractHeight2) layer.RetractHeight2 = RetractHeight2;
+ else if (property == nameof(RetractSpeed2) && !CanUseRetractSpeed2) layer.RetractSpeed2 = RetractSpeed2;
+ else if (property == nameof(LightPWM) && !CanUseBottomLightPWM) layer.LightPWM = LightPWM;
+ }
+ else // Normal layers
+ {
+ if (property == nameof(LightOffDelay)) layer.LightOffDelay = LightOffDelay;
+ else if (property == nameof(WaitTimeBeforeCure)) layer.WaitTimeBeforeCure = WaitTimeBeforeCure;
+ else if (property == nameof(ExposureTime)) layer.ExposureTime = ExposureTime;
+ else if (property == nameof(WaitTimeAfterCure)) layer.WaitTimeAfterCure = WaitTimeAfterCure;
+ else if (property == nameof(LiftHeight)) layer.LiftHeight = LiftHeight;
+ else if (property == nameof(LiftSpeed)) layer.LiftSpeed = LiftSpeed;
+ else if (property == nameof(LiftHeight2)) layer.LiftHeight2 = LiftHeight2;
+ else if (property == nameof(LiftSpeed2)) layer.LiftSpeed2 = LiftSpeed2;
+ else if (property == nameof(WaitTimeAfterLift)) layer.WaitTimeAfterLift = WaitTimeAfterLift;
+ else if (property == nameof(RetractSpeed)) layer.RetractSpeed = RetractSpeed;
+ else if (property == nameof(RetractHeight2)) layer.RetractHeight2 = RetractHeight2;
+ else if (property == nameof(RetractSpeed2)) layer.RetractSpeed2 = RetractSpeed2;
+ else if (property == nameof(LightPWM)) layer.LightPWM = LightPWM;
+ }
}
- });
- }
+ }
- public void UpdatePrintTime()
- {
- PrintTime = PrintTimeComputed;
- //Debug.WriteLine($"Time updated: {_printTime}s");
+ layer.MaterialMilliliters = -1; // Recalculate this value to be sure
}
- public void UpdatePrintTimeQueued()
+ RebuildGCode();
+ }
+
+ /// <summary>
+ /// Set LiftHeight to 0 if previous and current have same PositionZ
+ /// <param name="zeroLightOffDelay">If true also set light off to 0, otherwise current value will be kept.</param>
+ /// </summary>
+ public void SetNoLiftForSamePositionedLayers(bool zeroLightOffDelay = false)
+ => SetLiftForSamePositionedLayers(0, zeroLightOffDelay);
+
+ public void SetLiftForSamePositionedLayers(float liftHeight = 0, bool zeroLightOffDelay = false)
+ {
+ for (int layerIndex = 1; layerIndex < LayerCount; layerIndex++)
{
- lock (Mutex)
+ var layer = this[layerIndex];
+ if (this[layerIndex - 1].PositionZ != layer.PositionZ) continue;
+ layer.LiftHeightTotal = liftHeight;
+ layer.WaitTimeAfterLift = 0;
+ if (zeroLightOffDelay)
{
- _queueTimerPrintTime.Stop();
- _queueTimerPrintTime.Start();
+ layer.LightOffDelay = 0;
+ layer.WaitTimeBeforeCure = 0;
+ layer.WaitTimeAfterCure = 0;
}
}
+ RebuildGCode();
+ }
- /// <summary>
- /// Converts millimeters to pixels given the current resolution and display size
- /// </summary>
- /// <param name="mm">Millimeters to convert</param>
- /// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
- /// <returns>Pixels</returns>
- public uint MillimetersXToPixels(ushort mm, uint fallbackToPixels = 0)
+ public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex, MatCacheManager cacheManager, out uint lastLayerIndex)
+ {
+ var startLayerPositionZ = this[layerIndex].PositionZ;
+ lastLayerIndex = layerIndex;
+ var layerMat = cacheManager.Get1(layerIndex).Clone();
+
+ for (var curIndex = layerIndex + 1; curIndex < LayerCount && this[curIndex].PositionZ == startLayerPositionZ; curIndex++)
{
- var ppmm = Xppmm;
- if (ppmm <= 0) return fallbackToPixels;
- return (uint)(ppmm * mm);
+ CvInvoke.Max(layerMat, cacheManager.Get1(curIndex), layerMat);
+ lastLayerIndex = curIndex;
}
- /// <summary>
- /// Converts millimeters to pixels given the current resolution and display size
- /// </summary>
- /// <param name="mm">Millimeters to convert</param>
- /// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
- /// <returns>Pixels</returns>
- public uint MillimetersYToPixels(ushort mm, uint fallbackToPixels = 0)
+ return layerMat;
+ }
+
+ public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex, MatCacheManager cacheManager)
+ => GetMergedMatForSequentialPositionedLayers(layerIndex, cacheManager, out _);
+
+ public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex, out uint lastLayerIndex)
+ {
+ var startLayer = this[layerIndex];
+ lastLayerIndex = layerIndex;
+ var layerMat = startLayer.LayerMat;
+
+ for (var curIndex = layerIndex + 1; curIndex < LayerCount && this[curIndex].PositionZ == startLayer.PositionZ; curIndex++)
{
- var ppmm = Yppmm;
- if (ppmm <= 0) return fallbackToPixels;
- return (uint)(ppmm * mm);
+ using var nextLayer = this[curIndex].LayerMat;
+ CvInvoke.Max(nextLayer, layerMat, layerMat);
+ lastLayerIndex = curIndex;
}
- /// <summary>
- /// Converts millimeters to pixels given the current resolution and display size
- /// </summary>
- /// <param name="mm">Millimeters to convert</param>
- /// <param name="fallbackToPixels">Fallback to this value in pixels if no ratio is available to make the convertion</param>
- /// <returns>Pixels</returns>
- public uint MillimetersToPixels(ushort mm, uint fallbackToPixels = 0)
+ return layerMat;
+ }
+
+ public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex)
+ => GetMergedMatForSequentialPositionedLayers(layerIndex, out _);
+ #endregion
+
+ #region Draw Modifications
+ public void DrawModifications(IList<PixelOperation> drawings, OperationProgress? progress = null)
+ {
+ progress ??= new OperationProgress();
+ progress.Reset("Drawings", (uint)drawings.Count);
+
+ ConcurrentDictionary<uint, Mat> modifiedLayers = new();
+ for (var i = 0; i < drawings.Count; i++)
{
- var ppmm = PpmmMax;
- if (ppmm <= 0) return fallbackToPixels;
- return (uint)(ppmm * mm);
- }
+ var operation = drawings[i];
- /// <summary>
- /// From a pixel position get the equivalent position on the display
- /// </summary>
- /// <param name="x">X position in pixels</param>
- /// <param name="precision">Decimal precision</param>
- /// <returns>Display position in millimeters</returns>
- public float PixelToDisplayPositionX(int x, byte precision = 3) => (float)Math.Round(PixelWidth * x, precision);
+ if (operation.OperationType == PixelOperation.PixelOperationType.Drawing)
+ {
+ var operationDrawing = (PixelDrawing)operation;
+ var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat);
- /// <summary>
- /// From a pixel position get the equivalent position on the display
- /// </summary>
- /// <param name="y">Y position in pixels</param>
- /// <param name="precision">Decimal precision</param>
- /// <returns>Display position in millimeters</returns>
- public float PixelToDisplayPositionY(int y, byte precision = 3) => (float)Math.Round(PixelHeight * y, precision);
+ if (operationDrawing.BrushSize == 1)
+ {
+ mat.SetByte(operation.Location.X, operation.Location.Y, operationDrawing.Brightness);
+ continue;
+ }
- /// <summary>
- /// From a pixel position get the equivalent position on the display
- /// </summary>
- /// <param name="x">X position in pixels</param>
- /// <param name="y">Y position in pixels</param>
- /// <param name="precision">Decimal precision</param>
- /// <returns>Resolution position in pixels</returns>
- public PointF PixelToDisplayPosition(int x, int y, byte precision = 3) =>new(PixelToDisplayPositionX(x, precision), PixelToDisplayPositionY(y, precision));
- public PointF PixelToDisplayPosition(Point point, byte precision = 3) => new(PixelToDisplayPositionX(point.X, precision), PixelToDisplayPositionY(point.Y, precision));
+ mat.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location,
+ new MCvScalar(operationDrawing.Brightness), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType);
+ /*switch (operationDrawing.BrushShape)
+ {
+ case PixelDrawing.BrushShapeType.Square:
+ CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
+ break;
+ case PixelDrawing.BrushShapeType.Circle:
+ CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2,
+ new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }*/
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Text)
+ {
+ var operationText = (PixelText)operation;
+ var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat);
- /// <summary>
- /// From a pixel position get the equivalent position on the display
- /// </summary>
- /// <param name="x">X position in millimeters</param>
- /// <returns>Resolution position in pixels</returns>
- public int DisplayToPixelPositionX(float x) => (int)(x * Xppmm);
+ mat.PutTextRotated(operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(operationText.Brightness), operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.LineAlignment, operationText.Angle);
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser)
+ {
+ var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat);
- /// <summary>
- /// From a pixel position get the equivalent position on the display
- /// </summary>
- /// <param name="y">Y position in millimeters</param>
- /// <returns>Resolution position in pixels</returns>
- public int DisplayToPixelPositionY(float y) => (int)(y * Yppmm);
+ using var layerContours = mat.FindContours(out var hierarchy, RetrType.Tree);
- /// <summary>
- /// From a pixel position get the equivalent position on the display
- /// </summary>
- /// <param name="x">X position in millimeters</param>
- /// <param name="y">Y position in millimeters</param>
- /// <returns>Resolution position in pixels</returns>
- public Point DisplayToPixelPosition(float x, float y) => new(DisplayToPixelPositionX(x), DisplayToPixelPositionY(y));
- public Point DisplayToPixelPosition(PointF point) => new(DisplayToPixelPositionX(point.X), DisplayToPixelPositionY(point.Y));
+ if (mat.GetByte(operation.Location) >= 10)
+ {
+ using var vec = EmguContours.GetContoursInside(layerContours, hierarchy, operation.Location);
- /// <summary>
- /// Creates a empty mat of file <see cref="Resolution"/> size and create a dummy pixel to prevent a empty layer detection
- /// </summary>
- /// <param name="dummyPixelLocation">Location to set the dummy pixel, use a negative value (-1,-1) to set to the bounding center</param>
- /// <param name="dummyPixelBrightness">Dummy pixel brightness</param>
- /// <returns></returns>
- public Mat CreateMatWithDummyPixel(Point dummyPixelLocation, byte dummyPixelBrightness)
- {
- var newMat = EmguExtensions.InitMat(Resolution);
- if (dummyPixelBrightness > 0)
+ if (vec.Size > 0)
+ {
+ CvInvoke.DrawContours(mat, vec, -1, new MCvScalar(operation.PixelBrightness), -1);
+ }
+ }
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Supports)
{
- if (dummyPixelLocation.IsAnyNegative()) dummyPixelLocation = BoundingRectangle.Center();
- newMat.SetByte(newMat.GetPixelPos(dummyPixelLocation), dummyPixelBrightness);
+ var operationSupport = (PixelSupport)operation;
+ int drawnLayers = 0;
+ for (int operationLayer = (int)operation.LayerIndex - 1; operationLayer >= 0; operationLayer--)
+ {
+ var mat = modifiedLayers.GetOrAdd((uint)operationLayer, u => this[operationLayer].LayerMat);
+ int radius = (operationLayer > 10 ? Math.Min(operationSupport.TipDiameter + drawnLayers, operationSupport.PillarDiameter) : operationSupport.BaseDiameter) / 2;
+ uint whitePixels;
+
+ int yStart = Math.Max(0, operation.Location.Y - operationSupport.TipDiameter / 2);
+ int xStart = Math.Max(0, operation.Location.X - operationSupport.TipDiameter / 2);
+
+ using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter)))
+ {
+ using var matCircleMask = matCircleRoi.NewBlank();
+ CvInvoke.Circle(matCircleMask, new Point(operationSupport.TipDiameter / 2, operationSupport.TipDiameter / 2),
+ operationSupport.TipDiameter / 2, new MCvScalar(operation.PixelBrightness), -1);
+ CvInvoke.BitwiseAnd(matCircleRoi, matCircleMask, matCircleMask);
+ whitePixels = (uint)CvInvoke.CountNonZero(matCircleMask);
+ }
+
+ if (whitePixels >= Math.Pow(operationSupport.TipDiameter, 2) / 3)
+ {
+ //CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(255), -1);
+ if (drawnLayers == 0) continue; // Supports nonexistent, keep digging
+ break; // White area end supporting
+ }
+
+ CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(operation.PixelBrightness), -1, operationSupport.LineType);
+ drawnLayers++;
+ }
}
+ else if (operation.OperationType == PixelOperation.PixelOperationType.DrainHole)
+ {
+ uint drawnLayers = 0;
+ var operationDrainHole = (PixelDrainHole)operation;
+ for (int operationLayer = (int)operation.LayerIndex; operationLayer >= 0; operationLayer--)
+ {
+ var mat = modifiedLayers.GetOrAdd((uint)operationLayer, u => this[operationLayer].LayerMat);
+ int radius = operationDrainHole.Diameter / 2;
+ uint blackPixels;
- return newMat;
- }
+ int yStart = Math.Max(0, operation.Location.Y - radius);
+ int xStart = Math.Max(0, operation.Location.X - radius);
- /// <summary>
- /// Creates a empty mat of file <see cref="Resolution"/> size and create a dummy pixel to prevent a empty layer detection
- /// </summary>
- /// <param name="dummyPixelLocation">Location to set the dummy pixel, use a negative value (-1,-1) to set to the bounding center</param>
- /// <returns></returns>
- public Mat CreateMatWithDummyPixel(Point dummyPixelLocation) => CreateMatWithDummyPixel(dummyPixelLocation, SupportsGCode ? (byte) 1 : (byte) 128);
+ using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter)))
+ {
+ using var matCircleRoiInv = new Mat();
+ CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv);
+ using var matCircleMask = matCircleRoi.NewBlank();
+ CvInvoke.Circle(matCircleMask, new Point(radius, radius), radius, EmguExtensions.WhiteColor, -1);
+ CvInvoke.BitwiseAnd(matCircleRoiInv, matCircleMask, matCircleMask);
+ blackPixels = (uint)CvInvoke.CountNonZero(matCircleMask);
+ }
- /// <summary>
- /// Creates a empty mat of file <see cref="Resolution"/> size
- /// </summary>
- /// <param name="dummyPixelBrightness">Dummy pixel brightness</param>
- /// <returns></returns>
- public Mat CreateMatWithDummyPixel(byte dummyPixelBrightness) => CreateMatWithDummyPixel(BoundingRectangle.Center(), dummyPixelBrightness);
+ if (blackPixels >= Math.Pow(operationDrainHole.Diameter, 2) / 3) // Enough area to drain?
+ {
+ if (drawnLayers == 0) continue; // Drill not found a target yet, keep digging
+ break; // Stop drill drain found!
+ }
- /// <summary>
- /// Creates a empty mat of file <see cref="Resolution"/> size
- /// </summary>
- /// <returns></returns>
- public Mat CreateMatWithDummyPixel() => CreateMatWithDummyPixel(SupportsGCode ? (byte)1 : (byte)128);
-
- /// <summary>
- /// Creates a empty mat of file <see cref="Resolution"/> size
- /// </summary>
- /// <param name="initMat">True to black out the mat</param>
- /// <returns></returns>
- public Mat CreateMat(bool initMat = true)
- {
- return initMat
- ? EmguExtensions.InitMat(Resolution)
- : new Mat(Resolution, DepthType.Cv8U, 1);
+ CvInvoke.Circle(mat, operation.Location, radius, EmguExtensions.BlackColor, -1, operationDrainHole.LineType);
+ drawnLayers++;
+ }
+ }
+
+ progress++;
}
- #endregion
+ progress.Reset("Saving", (uint)modifiedLayers.Count);
+ Parallel.ForEach(modifiedLayers, CoreSettings.ParallelOptions, modifiedLayer =>
+ {
+ this[modifiedLayer.Key].LayerMat = modifiedLayer.Value;
+ modifiedLayer.Value.Dispose();
+
+ progress.LockAndIncrement();
+ });
+
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs
index 0583caf..281935b 100644
--- a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs
+++ b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs
@@ -6,6 +6,10 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -16,735 +20,727 @@ using System.Text;
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.EmguCV;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
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";
+namespace UVtools.Core.FileFormats;
- //[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";
+[XmlRoot(ElementName = "svg", Namespace = "http://www.w3.org/2000/svg")]
- [XmlElement("printparams")] public FlashForgeSVGXSvgPrintParams PrintParameters { get; set; } = new();
+public class FlashForgeSVGXSvg
+{
+ //public string Xmlns { get; set; } = "http://www.w3.org/2000/svg";
- [XmlElement("g")] public List<FlashForgeSVGXSvgGroup> Groups { get; set; } = new();
+ //[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";
- public override string ToString()
- {
- return $"{nameof(Version)}: {Version}, {nameof(PrintParameters)}: {PrintParameters}, {nameof(Groups)}: {Groups.Count}";
- }
+ [XmlElement("printparams")] public FlashForgeSVGXSvgPrintParams PrintParameters { get; set; } = new();
- 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"
- };
+ [XmlElement("g")] public List<FlashForgeSVGXSvgGroup> Groups { get; set; } = new();
- var svg = XmlExtensions.SerializeObject(this, settings);
- return svg.Replace(" ", string.Empty)
- .Replace("<g id=\"background\" area=\"0\" perimeter=\"0\" />", "<g id=\"background\">\n</g>")
- + '\n';
- }
+ public override string ToString()
+ {
+ return $"{nameof(Version)}: {Version}, {nameof(PrintParameters)}: {PrintParameters}, {nameof(Groups)}: {Groups.Count}";
}
- [XmlRoot("printparams")]
- public class FlashForgeSVGXSvgPrintParams
+ public string SerializeToString()
{
- [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();
+ 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"
+ };
- 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}";
- }
+ var svg = XmlExtensions.SerializeObject(this, settings);
+ return svg.Replace(" ", string.Empty)
+ .Replace("<g id=\"background\" area=\"0\" perimeter=\"0\" />", "<g id=\"background\">\n</g>")
+ + '\n';
}
+}
- [XmlRoot("projectiontime")]
- public class FlashForgeSVGXSvgProjectionTime
+[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()
{
- [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}";
- }
+ 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; }
- [XmlRoot("projectionadjust")]
- public class FlashForgeSVGXSvgProjectionAdjust
+ public override string ToString()
{
- [XmlAttribute("x")] public float X { get; set; } = 100;
- [XmlAttribute("y")] public float Y { get; set; } = 100;
+ return $"{nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(ExposureTime)}: {ExposureTime}";
+ }
+}
- public override string ToString()
- {
- return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}";
- }
+[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
+[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()
{
- [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; }
+ return $"{nameof(MinX)}: {MinX}, {nameof(MinY)}: {MinY}, {nameof(MinZ)}: {MinZ}, {nameof(MaxX)}: {MaxX}, {nameof(MaxY)}: {MaxY}, {nameof(MaxZ)}: {MaxZ}";
+ }
+}
- 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; } = string.Empty;
+ [XmlAttribute("area")] public float Area { get; set; }
+ [XmlAttribute("perimeter")] public float Perimeter { get; set; }
+ [XmlElement("path")] public List<FlashForgeSVGXSvgPath> Paths { get; set; } = new();
+
+ public FlashForgeSVGXSvgGroup() { }
+
+ public FlashForgeSVGXSvgGroup(string id, float area = 0, float perimeter = 0)
+ {
+ Id = id;
+ Area = area;
+ Perimeter = perimeter;
}
- [XmlRoot("g")]
- public class FlashForgeSVGXSvgGroup
+ public override string ToString()
{
- [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<FlashForgeSVGXSvgPath> Paths { get; set; } = new();
+ return $"{nameof(Id)}: {Id}, {nameof(Area)}: {Area}, {nameof(Perimeter)}: {Perimeter}, {nameof(Paths)}: {Paths}";
+ }
+}
- public FlashForgeSVGXSvgGroup() { }
+[XmlRoot("path")]
+public class FlashForgeSVGXSvgPath
+{
+ [XmlAttribute("d")] public string Value { get; set; } = string.Empty;
+ [XmlAttribute("style")] public string Style { get; set; } = "fill:white";
+ [XmlAttribute("fill-rule")] public string FillRule { get; set; } = "evenodd";
- public FlashForgeSVGXSvgGroup(string id, float area = 0, float perimeter = 0)
- {
- Id = id;
- Area = area;
- Perimeter = perimeter;
- }
+ public FlashForgeSVGXSvgPath() { }
- public override string ToString()
- {
- return $"{nameof(Id)}: {Id}, {nameof(Area)}: {Area}, {nameof(Perimeter)}: {Perimeter}, {nameof(Paths)}: {Paths}";
- }
+ public FlashForgeSVGXSvgPath(string value)
+ {
+ Value = value;
}
- [XmlRoot("path")]
- public class FlashForgeSVGXSvgPath
+ public override string ToString()
{
- [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";
+ return $"{nameof(Value)}: {Value}, {nameof(Style)}: {Style}, {nameof(FillRule)}: {FillRule}";
+ }
+}
- public FlashForgeSVGXSvgPath() { }
- public FlashForgeSVGXSvgPath(string value)
- {
- Value = value;
- }
+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(Value)}: {Value}, {nameof(Style)}: {Style}, {nameof(FillRule)}: {FillRule}";
+ return $"{nameof(Identifier)}: {Identifier}, {nameof(Preview1Address)}: {Preview1Address}, {nameof(Preview2Address)}: {Preview2Address}, {nameof(SVGDocumentAddress)}: {SVGDocumentAddress}";
}
}
-
-
- public class FlashForgeSVGXFile : FileFormat
+ #endregion
+
+ #region Preview
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class Preview
{
- #region Constants
- #endregion
+ public const byte IdentifierLength = 2;
+ public const string IdentifierText = "BM";
- #region Sub Classes
- #region Header
- public class Header
- {
- public const byte IdentifierLength = 16;
- public const string IdentifierText = "DLP-II 1.1\n";
+ /// <summary>
+ /// Gets or sets the identifier, BM = bitmap?
+ /// </summary>
+ [FieldOrder(0)] [FieldLength(IdentifierLength)] public string Identifier { get; set; } = IdentifierText;
- [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; }
+ /// <summary>
+ /// Gets or sets the table total size
+ /// </summary>
+ [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;
- public override string ToString()
- {
- return $"{nameof(Identifier)}: {Identifier}, {nameof(Preview1Address)}: {Preview1Address}, {nameof(Preview2Address)}: {Preview2Address}, {nameof(SVGDocumentAddress)}: {SVGDocumentAddress}";
- }
- }
- #endregion
+ [FieldOrder(5)] public uint ResolutionX { get; set; }
- #region Preview
/// <summary>
- /// 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.
+ /// Gets the Y dimension of the preview image, in pixels.
/// </summary>
- public class Preview
- {
- public const byte IdentifierLength = 2;
- public const string IdentifierText = "BM";
+ [FieldOrder(6)] public uint ResolutionY { get; set; }
- /// <summary>
- /// Gets or sets the identifier, BM = bitmap?
- /// </summary>
- [FieldOrder(0)] [FieldLength(IdentifierLength)] public string Identifier { get; set; } = IdentifierText;
-
- /// <summary>
- /// Gets or sets the table total size
- /// </summary>
- [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; }
-
- /// <summary>
- /// Gets the Y dimension of the preview image, in pixels.
- /// </summary>
- [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}";
- }
+ [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; } = null!;
+
+ 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
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public Header HeaderSettings { get; protected internal set; } = new();
+ public Header HeaderSettings { get; protected internal set; } = new();
- public FlashForgeSVGXSvg SVGDocument { get; protected internal set; } = new();
+ public FlashForgeSVGXSvg SVGDocument { get; protected internal set; } = new();
- public override FileFormatType FileType => FileFormatType.Binary;
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new (typeof(FlashForgeSVGXFile), "svgx", "Flashforge SVGX"),
- };
+ 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 PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+ //PrintParameterModifier.LightPWM,
+ };
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(128, 128),
- new(200, 240)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(128, 128),
+ new(200, 240)
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => SVGDocument.PrintParameters.ResolutionX;
+ set
{
- get => SVGDocument.PrintParameters.ResolutionX;
- set
- {
- SVGDocument.PrintParameters.ResolutionX = value;
- RaisePropertyChanged();
- }
+ SVGDocument.PrintParameters.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => SVGDocument.PrintParameters.ResolutionY;
+ set
{
- get => SVGDocument.PrintParameters.ResolutionY;
- set
- {
- SVGDocument.PrintParameters.ResolutionY = value;
- RaisePropertyChanged();
- }
+ SVGDocument.PrintParameters.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => SVGDocument.PrintParameters.DisplayWidth;
+ set
{
- get => SVGDocument.PrintParameters.DisplayWidth;
- set
- {
- SVGDocument.PrintParameters.DisplayWidth = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ SVGDocument.PrintParameters.DisplayWidth = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => SVGDocument.PrintParameters.DisplayHeight;
+ set
{
- get => SVGDocument.PrintParameters.DisplayHeight;
- set
- {
- SVGDocument.PrintParameters.DisplayHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ SVGDocument.PrintParameters.DisplayHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override Enumerations.FlipDirection DisplayMirror
- {
- get => Enumerations.FlipDirection.Vertically;
- set {}
- }
+ 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 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
+ public override float LayerHeight
+ {
+ get => SVGDocument.PrintParameters.LayerHeight;
+ set
{
- get => SVGDocument.PrintParameters.LayerHeight;
- set
- {
- SVGDocument.PrintParameters.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ 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 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 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 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 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 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 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 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 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 LiftSpeed
+ {
+ get => HeaderSettings.LiftSpeed;
+ set => base.LiftSpeed = HeaderSettings.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ public override float BottomRetractSpeed => RetractSpeed;
- public override float RetractSpeed
- {
- get => HeaderSettings.RetractSpeed;
- set => base.RetractSpeed = HeaderSettings.RetractSpeed = (float)Math.Round(value, 2);
- }
+ 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 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 byte LightPWM
+ {
+ get => (byte) HeaderSettings.LightPWM;
+ set => base.LightPWM = (byte) (HeaderSettings.LightPWM = value);
+ }*/
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => SVGDocument.PrintParameters.MaterialMilliliters;
+ set
{
- get => SVGDocument.PrintParameters.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- SVGDocument.PrintParameters.MaterialMilliliters = base.MaterialMilliliters;
- }
+ 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? 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 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 };
+ public override object[] Configs => new object[] { HeaderSettings, SVGDocument.PrintParameters, SVGDocument.PrintParameters.ProjectionAdjust, SVGDocument.PrintParameters.PrintRange, SVGDocument.PrintParameters.ProjectionTime };
- #endregion
+ #endregion
- #region Constructors
- public FlashForgeSVGXFile()
+ #region Constructors
+ public FlashForgeSVGXFile()
+ {
+ }
+ #endregion
+
+ #region Methods
+ protected override void EncodeInternally(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);
}
- #endregion
- #region Methods
- protected override void EncodeInternally(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);
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
+ HeaderSettings.Preview1Address = 0;
+ HeaderSettings.Preview2Address = 0;
- HeaderSettings.Preview1Address = 0;
- HeaderSettings.Preview2Address = 0;
+ progress.Reset(OperationProgress.StatusEncodePreviews, (uint)ThumbnailsOriginalSize?.Length!);
+ foreach (var mat in Thumbnails)
+ {
+ if (HeaderSettings.Preview2Address > 0) break;
+ if(mat is null) continue;
- progress.Reset(OperationProgress.StatusEncodePreviews, (uint)ThumbnailsOriginalSize.Length);
- if (Thumbnails is not null)
+ var preview = new Preview
{
- 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
- };
+ ResolutionX = (uint)mat.Width,
+ ResolutionY = (uint)mat.Height
+ };
- using var matFlip = new Mat();
- CvInvoke.Flip(mat, matFlip, FlipType.Vertical);
+ 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);
+ 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++;
- }
+ if (HeaderSettings.Preview1Address == 0)
+ {
+ HeaderSettings.Preview1Address = (uint)outputFile.Position;
+ }
+ else
+ {
+ HeaderSettings.Preview2Address = (uint)outputFile.Position;
}
- var halfDisplay = Display.Half();
- var ppmm = Ppmm;
- var pixelUm = PixelSizeMicronsMax;
+ outputFile.WriteSerialize(preview);
+ progress++;
+ }
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- SVGDocument.Groups = new List<FlashForgeSVGXSvgGroup> { new("background") };
- var groups = new FlashForgeSVGXSvgGroup[LayerCount];
+ var halfDisplay = Display.Half();
+ var ppmm = Ppmm;
+ var pixelUm = PixelSizeMicronsMax;
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ SVGDocument.Groups = new List<FlashForgeSVGXSvgGroup> { new("background") };
+ var groups = new FlashForgeSVGXSvgGroup[LayerCount];
- groups[layerIndex] = new FlashForgeSVGXSvgGroup($"layer-{layerIndex}");
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
- using var mat = this[layerIndex].LayerMat;
- //CvInvoke.Threshold(mat, mat, 127, 255, ThresholdType.Binary); // no AA
+ groups[layerIndex] = new FlashForgeSVGXSvgGroup($"layer-{layerIndex}");
- using var contours = mat.FindContours(out var hierarchy, RetrType.Tree);
+ using var mat = this[layerIndex].LayerMat;
+ //CvInvoke.Threshold(mat, mat, 127, 255, ThresholdType.Binary); // no AA
- float minx = SVGDocument.PrintParameters.PrintRange.MinX;
- float miny = SVGDocument.PrintParameters.PrintRange.MinY;
- float maxx = SVGDocument.PrintParameters.PrintRange.MaxX;
- float maxy = SVGDocument.PrintParameters.PrintRange.MaxY;
+ using var contours = mat.FindContours(out var hierarchy, RetrType.Tree);
- var path = new StringBuilder();
- for (int i = 0; i < contours.Size; i++)
- {
- if (hierarchy[i, EmguContour.HierarchyParent] == -1) // Top hierarchy
- {
- if (path.Length > 0)
- {
- groups[layerIndex].Paths.Add(new FlashForgeSVGXSvgPath(path.ToString()));
- }
- path.Clear();
+ float minx = SVGDocument.PrintParameters.PrintRange.MinX;
+ float miny = SVGDocument.PrintParameters.PrintRange.MinY;
+ float maxx = SVGDocument.PrintParameters.PrintRange.MaxX;
+ float maxy = SVGDocument.PrintParameters.PrintRange.MaxY;
- 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
+ var path = new StringBuilder();
+ for (int i = 0; i < contours.Size; i++)
+ {
+ if (hierarchy[i, EmguContour.HierarchyParent] == -1) // Top hierarchy
+ {
+ if (path.Length > 0)
{
- path.Append(' ');
+ 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);
- 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($"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");
}
+ path.Append(" Z");
+ }
- if (path.Length > 0) // Left over
- {
- groups[layerIndex].Paths.Add(new FlashForgeSVGXSvgPath(path.ToString()));
- }
+ 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++;
- }
- });
+ 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);
+ SVGDocument.Groups.AddRange(groups);
- HeaderSettings.SVGDocumentAddress = (uint)outputFile.Position;
+ HeaderSettings.SVGDocumentAddress = (uint)outputFile.Position;
- outputFile.WriteString(SVGDocument.SerializeToString());
+ outputFile.WriteString(SVGDocument.SerializeToString());
- outputFile.Seek(0, SeekOrigin.Begin);
- outputFile.WriteSerialize(HeaderSettings);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ outputFile.WriteSerialize(HeaderSettings);
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine(SVGDocument);
- Debug.WriteLine("-End-");
- }
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(SVGDocument);
+ Debug.WriteLine("-End-");
+ }
- protected override void DecodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.Identifier != Header.IdentifierText)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (HeaderSettings.Identifier != Header.IdentifierText)
- {
- throw new FileLoadException("Not a valid Flashforge SVGX file!", FileFullPath);
- }
+ throw new FileLoadException("Not a valid Flashforge SVGX file!", FileFullPath);
+ }
- progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
- Debug.Write("Header -> ");
- Debug.WriteLine(HeaderSettings);
+ 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<Preview>(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++;
+ byte thumbnailCount = 0;
+ if (HeaderSettings.Preview1Address > 0)
+ {
+ inputFile.Seek(HeaderSettings.Preview1Address, SeekOrigin.Begin);
+ var preview = Helpers.Deserialize<Preview>(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<Preview>(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<Preview>(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++;
+ }
+ progress++;
- inputFile.Seek(HeaderSettings.SVGDocumentAddress, SeekOrigin.Begin);
- string svgDocument = Encoding.UTF8.GetString(inputFile.ReadToEnd());
- SVGDocument = XmlExtensions.DeserializeObject<FlashForgeSVGXSvg>(svgDocument);
+ inputFile.Seek(HeaderSettings.SVGDocumentAddress, SeekOrigin.Begin);
+ string svgDocument = Encoding.UTF8.GetString(inputFile.ReadToEnd());
+ SVGDocument = XmlExtensions.DeserializeFromText<FlashForgeSVGXSvg>(svgDocument);
- Debug.WriteLine(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);
- }
+ 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;
+ var halfDisplay = Display.Half();
+ var ppmm = Ppmm;
- LayerManager.Init(SVGDocument.PrintParameters.LayerCount, DecodeType == FileDecodeType.Partial);
+ Init(SVGDocument.PrintParameters.LayerCount, DecodeType == FileDecodeType.Partial);
- if (DecodeType != FileDecodeType.Full) return;
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
+ if (DecodeType != FileDecodeType.Full) return;
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
- var mat = EmguExtensions.InitMat(Resolution);
+ var mat = EmguExtensions.InitMat(Resolution);
- var group = SVGDocument.Groups.FirstOrDefault(g => g.Id == $"layer-{layerIndex}");
+ var group = SVGDocument.Groups.FirstOrDefault(g => g.Id == $"layer-{layerIndex}");
- if (@group is not null)
+ if (@group is not null)
+ {
+ var pointsOfPoints = new List<Point[]>();
+ var points = new List<Point>();
+ foreach (var path in @group.Paths)
{
- var pointsOfPoints = new List<Point[]>();
- var points = new List<Point>();
- foreach (var path in @group.Paths)
- {
- if (progress.Token.IsCancellationRequested) break;
- var spaceSplit = path.Value.Split(' ',
- StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+ if (progress.Token.IsCancellationRequested) break;
+ var spaceSplit = path.Value.Split(' ',
+ StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
- for (int i = 0; i < spaceSplit.Length; i++)
+ for (int i = 0; i < spaceSplit.Length; i++)
+ {
+ if (spaceSplit[i] == "M")
{
- if (spaceSplit[i] == "M")
+ if (points.Count > 0)
{
- if (points.Count > 0)
- {
- pointsOfPoints.Add(points.ToArray());
- points.Clear();
- }
-
- continue;
+ pointsOfPoints.Add(points.ToArray());
+ points.Clear();
}
- if (spaceSplit[i] == "Z")
- {
- if (points.Count > 0)
- {
- pointsOfPoints.Add(points.ToArray());
- points.Clear();
- }
+ continue;
+ }
- continue;
+ if (spaceSplit[i] == "Z")
+ {
+ if (points.Count > 0)
+ {
+ pointsOfPoints.Add(points.ToArray());
+ points.Clear();
}
- if (spaceSplit[i].Length == 1 && !char.IsDigit(spaceSplit[i][0]))
- continue; // Ignore any other not processed 1 char that's not a digit (L)
+ continue;
+ }
- if (i + 1 >= spaceSplit.Length) break; // No more to see
+ 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;
+ 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);
+ var mmAbsX = Math.Clamp(halfDisplay.Width + mmX, 0, DisplayWidth);
+ var mmAbsY = Math.Clamp(halfDisplay.Height + mmY, 0, DisplayHeight);
- points.Add(new Point(x, y));
- }
+ int x = (int)(mmAbsX * ppmm.Width);
+ int y = (int)(mmAbsY * ppmm.Height);
- if (points.Count > 0) // Leftovers, still this should never happen!
- {
- pointsOfPoints.Add(points.ToArray());
- points.Clear();
- }
+ points.Add(new Point(x, y));
}
- if (pointsOfPoints.Count > 0)
+ if (points.Count > 0) // Leftovers, still this should never happen!
{
- using var vecPoints = new VectorOfVectorOfPoint(pointsOfPoints.ToArray());
- CvInvoke.DrawContours(mat, vecPoints, -1, EmguExtensions.WhiteColor, -1);
+ 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();
- });
- }
+ progress.Token.ThrowIfCancellationRequested();
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(HeaderSettings.SVGDocumentAddress, SeekOrigin.Begin);
- outputFile.SetLength(outputFile.Position);
- outputFile.WriteString(SVGDocument.SerializeToString());
- }
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ progress.LockAndIncrement();
+ });
+ }
- #endregion
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ 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
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs
index c55167e..d2daf49 100644
--- a/UVtools.Core/FileFormats/GR1File.cs
+++ b/UVtools.Core/FileFormats/GR1File.cs
@@ -6,6 +6,8 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -15,546 +17,543 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class GR1File : FileFormat
{
- public class GR1File : FileFormat
- {
- #region Constants
+ #region Constants
- private const uint SlicerInfoAddress = 4 + 7 + 290 * 290 * 2 + 116 * 116 * 2 + 4;
- #endregion
+ private const uint SlicerInfoAddress = 4 + 7 + 290 * 290 * 2 + 116 * 116 * 2 + 4;
+ #endregion
- #region Sub Classes
+ #region Sub Classes
- #region Header
+ #region Header
- public sealed class Header
- {
- public const byte HEADER_SIZE = 7;
- public const string HEADER_VALUE = "MKSDLP";
-
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint HeaderSize { get; set; } = HEADER_SIZE;
-
- /// <summary>
- /// Gets the file tag = MKSDLP
- /// </summary>
- [FieldOrder(1)]
- [FieldLength(HEADER_SIZE)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string HeaderValue { get; set; } = HEADER_VALUE;
- }
- #endregion
-
- #region SlicerInfo
+ public sealed class Header
+ {
+ public const byte HEADER_SIZE = 7;
+ public const string HEADER_VALUE = "MKSDLP";
+
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint HeaderSize { get; set; } = HEADER_SIZE;
+
+ /// <summary>
+ /// Gets the file tag = MKSDLP
+ /// </summary>
+ [FieldOrder(1)]
+ [FieldLength(HEADER_SIZE)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string HeaderValue { get; set; } = HEADER_VALUE;
+ }
+ #endregion
- public sealed class SlicerInfo
- {
- // 290 * 290 * 2 + 116 * 116 * 2 + 4
- //[FieldOrder(0)] [FieldLength(195116)] public byte[] PreviewData { get; set; }
-
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort LayerCount { get; set; }
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; }
- [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; }
- [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint DisplayWidthDataSize { get; set; } = 6;
- [FieldOrder(4)] [FieldLength(nameof(DisplayWidthDataSize))] public byte[] DisplayWidthBytes { get; set; }
- [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DisplayHeightDataSize { get; set; } = 6;
- [FieldOrder(6)] [FieldLength(nameof(DisplayHeightDataSize))] public byte[] DisplayHeightBytes { get; set; }
-
- [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint LayerHeightDataSize { get; set; } = 6;
- [FieldOrder(8)] [FieldLength(nameof(LayerHeightDataSize))] public byte[] LayerHeightBytes { get; set; }
- [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public ushort ExposureTime { get; set; }
- [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LightOffDelay { get; set; }
- [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort BottomExposureTime { get; set; }
- [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort BottomLayers { get; set; }
- [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftHeight { get; set; }
- [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeed { get; set; }
- [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public ushort LiftHeight { get; set; }
- [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeed { get; set; }
- [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeed { get; set; }
- [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public ushort BottomLightPWM { get; set; }
- [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public ushort LightPWM { get; set; }
- }
- #endregion
+ #region SlicerInfo
- #region LayerDef
+ public sealed class SlicerInfo
+ {
+ // 290 * 290 * 2 + 116 * 116 * 2 + 4
+ //[FieldOrder(0)] [FieldLength(195116)] public byte[] PreviewData { get; set; }
+
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort LayerCount { get; set; }
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; }
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; }
+ [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint DisplayWidthDataSize { get; set; } = 6;
+ [FieldOrder(4)] [FieldLength(nameof(DisplayWidthDataSize))] public byte[] DisplayWidthBytes { get; set; } = null!;
+ [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DisplayHeightDataSize { get; set; } = 6;
+ [FieldOrder(6)] [FieldLength(nameof(DisplayHeightDataSize))] public byte[] DisplayHeightBytes { get; set; } = null!;
+
+ [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint LayerHeightDataSize { get; set; } = 6;
+ [FieldOrder(8)] [FieldLength(nameof(LayerHeightDataSize))] public byte[] LayerHeightBytes { get; set; } = null!;
+ [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public ushort ExposureTime { get; set; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LightOffDelay { get; set; }
+ [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort BottomExposureTime { get; set; }
+ [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort BottomLayers { get; set; }
+ [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftHeight { get; set; }
+ [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeed { get; set; }
+ [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public ushort LiftHeight { get; set; }
+ [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeed { get; set; }
+ [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeed { get; set; }
+ [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public ushort BottomLightPWM { get; set; }
+ [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public ushort LightPWM { get; set; }
+ }
+ #endregion
- public sealed class LayerDef
- {
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; }
- [FieldOrder(1)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; }
- [FieldOrder(2)] public PageBreak PageBreak { get; set; } = new();
+ #region LayerDef
- public LayerDef() { }
+ public sealed class LayerDef
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; }
+ [FieldOrder(1)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; } = Array.Empty<LayerLine>();
+ [FieldOrder(2)] public PageBreak PageBreak { get; set; } = new();
- public LayerDef(uint lineCount, LayerLine[] lines)
- {
- LineCount = lineCount;
- Lines = lines;
- }
- }
+ public LayerDef() { }
- public sealed class LayerLine
+ public LayerDef(uint lineCount, LayerLine[] lines)
{
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; }
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort EndY { get; set; }
- [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartX { get; set; }
- //[FieldOrder(3)] [FieldEndianness(Endianness.Big)] public byte Gray { get; set; }
-
+ LineCount = lineCount;
+ Lines = lines;
+ }
+ }
- public static byte[] GetBytes(ushort startY, ushort endY, ushort startX)
- {
- var bytes = new byte[6];
- BitExtensions.ToBytesBigEndian(startY, bytes);
- BitExtensions.ToBytesBigEndian(endY, bytes, 2);
- BitExtensions.ToBytesBigEndian(startX, bytes, 4);
- return bytes;
- }
+ public sealed class LayerLine
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; }
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort EndY { get; set; }
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartX { get; set; }
+ //[FieldOrder(3)] [FieldEndianness(Endianness.Big)] public byte Gray { get; set; }
- public LayerLine()
- { }
- public LayerLine(ushort startY, ushort endY, ushort startX)
- {
- StartY = startY;
- EndY = endY;
- StartX = startX;
- }
+ public static byte[] GetBytes(ushort startY, ushort endY, ushort startX)
+ {
+ var bytes = new byte[6];
+ BitExtensions.ToBytesBigEndian(startY, bytes);
+ BitExtensions.ToBytesBigEndian(endY, bytes, 2);
+ BitExtensions.ToBytesBigEndian(startX, bytes, 4);
+ return bytes;
}
- public sealed class PageBreak
+ public LayerLine()
+ { }
+
+ public LayerLine(ushort startY, ushort endY, ushort startX)
{
- public static byte[] Bytes => new byte[] {0x0D, 0x0A};
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public byte Line { get; set; } = 0x0D;
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public byte Break { get; set; } = 0x0A;
+ StartY = startY;
+ EndY = endY;
+ StartX = startX;
}
+ }
- #endregion
+ public sealed class PageBreak
+ {
+ public static byte[] Bytes => new byte[] {0x0D, 0x0A};
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public byte Line { get; set; } = 0x0D;
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public byte Break { get; set; } = 0x0A;
+ }
- #endregion
+ #endregion
- #region Properties
+ #endregion
- public Header HeaderSettings { get; protected internal set; } = new();
- public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
- public override FileFormatType FileType => FileFormatType.Binary;
+ #region Properties
- public override FileExtension[] FileExtensions { get; } = {
- new (typeof(GR1File), "gr1", "GR1 Workshop")
- };
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
-
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
-
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
-
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new (116, 116),
- new (290, 290)
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new (typeof(GR1File), "gr1", "GR1 Workshop")
+ };
+
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new (116, 116),
+ new (290, 290)
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => SlicerInfoSettings.ResolutionX;
+ set
{
- get => SlicerInfoSettings.ResolutionX;
- set
- {
- SlicerInfoSettings.ResolutionX = (ushort) value;
- RaisePropertyChanged();
- }
+ SlicerInfoSettings.ResolutionX = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => SlicerInfoSettings.ResolutionY;
+ set
{
- get => SlicerInfoSettings.ResolutionY;
- set
- {
- SlicerInfoSettings.ResolutionY = (ushort) value;
- RaisePropertyChanged();
- }
+ SlicerInfoSettings.ResolutionY = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayWidthBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayWidthBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override Enumerations.FlipDirection DisplayMirror { get; set; }
+ public override Enumerations.FlipDirection DisplayMirror { get; set; }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.LayerHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
- }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = SlicerInfoSettings.LayerCount = (ushort)base.LayerCount;
+ SlicerInfoSettings.LayerHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override ushort BottomLayerCount
- {
- get => SlicerInfoSettings.BottomLayers;
- set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = SlicerInfoSettings.LayerCount = (ushort)base.LayerCount;
+ }
- public override float BottomLightOffDelay => SlicerInfoSettings.LightOffDelay;
+ public override ushort BottomLayerCount
+ {
+ get => SlicerInfoSettings.BottomLayers;
+ set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value;
+ }
- public override float LightOffDelay
- {
- get => SlicerInfoSettings.LightOffDelay;
- set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value;
- }
+ public override float BottomLightOffDelay => SlicerInfoSettings.LightOffDelay;
- public override float BottomWaitTimeBeforeCure
- {
- get => base.BottomWaitTimeBeforeCure;
- set
- {
- SetBottomLightOffDelay(value);
- base.BottomWaitTimeBeforeCure = value;
- }
- }
+ public override float LightOffDelay
+ {
+ get => SlicerInfoSettings.LightOffDelay;
+ set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value;
+ }
- public override float WaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
{
- get => base.WaitTimeBeforeCure;
- set
- {
- SetNormalLightOffDelay(value);
- base.WaitTimeBeforeCure = value;
- }
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float BottomExposureTime
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
{
- get => SlicerInfoSettings.BottomExposureTime;
- set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value;
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
}
+ }
- public override float ExposureTime
- {
- get => SlicerInfoSettings.ExposureTime;
- set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value;
- }
+ public override float BottomExposureTime
+ {
+ get => SlicerInfoSettings.BottomExposureTime;
+ set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value;
+ }
- public override float BottomLiftHeight
- {
- get => SlicerInfoSettings.BottomLiftHeight;
- set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value;
- }
+ public override float ExposureTime
+ {
+ get => SlicerInfoSettings.ExposureTime;
+ set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value;
+ }
- public override float LiftHeight
- {
- get => SlicerInfoSettings.LiftHeight;
- set => base.LiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value;
- }
+ public override float BottomLiftHeight
+ {
+ get => SlicerInfoSettings.BottomLiftHeight;
+ set => base.BottomLiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value;
+ }
- public override float BottomLiftSpeed
- {
- get => SlicerInfoSettings.BottomLiftSpeed;
- set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value;
- }
+ public override float LiftHeight
+ {
+ get => SlicerInfoSettings.LiftHeight;
+ set => base.LiftHeight = SlicerInfoSettings.BottomLiftHeight = (ushort)value;
+ }
- public override float LiftSpeed
- {
- get => SlicerInfoSettings.LiftSpeed;
- set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value;
- }
+ public override float BottomLiftSpeed
+ {
+ get => SlicerInfoSettings.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)value;
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ 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 BottomRetractSpeed => RetractSpeed;
- public override byte BottomLightPWM
- {
- get => (byte)SlicerInfoSettings.BottomLightPWM;
- set => base.BottomLightPWM = (byte)(SlicerInfoSettings.BottomLightPWM = value);
- }
+ public override float RetractSpeed
+ {
+ get => SlicerInfoSettings.RetractSpeed;
+ set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)value;
+ }
- public override byte LightPWM
- {
- get => (byte)SlicerInfoSettings.LightPWM;
- set => base.LightPWM = (byte)(SlicerInfoSettings.LightPWM = value);
- }
+ public override byte BottomLightPWM
+ {
+ get => (byte)SlicerInfoSettings.BottomLightPWM;
+ set => base.BottomLightPWM = (byte)(SlicerInfoSettings.BottomLightPWM = value);
+ }
- public override object[] Configs => new[] { (object)HeaderSettings, SlicerInfoSettings };
+ public override byte LightPWM
+ {
+ get => (byte)SlicerInfoSettings.LightPWM;
+ set => base.LightPWM = (byte)(SlicerInfoSettings.LightPWM = value);
+ }
- #endregion
+ public override object[] Configs => new[] { (object)HeaderSettings, SlicerInfoSettings };
- #region Constructors
+ #endregion
- #endregion
+ #region Constructors
- #region Methods
- protected override void EncodeInternally(OperationProgress progress)
- {
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- var pageBreak = PageBreak.Bytes;
+ #endregion
+
+ #region Methods
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ var pageBreak = PageBreak.Bytes;
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- var previews = new byte[ThumbnailsOriginalSize.Length][];
+ var previews = new byte[ThumbnailsOriginalSize!.Length][];
- // Previews
- Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ // Previews
+ Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
+ if (Thumbnails[previewIndex] is null)
{
- if (progress.Token.IsCancellationRequested) return;
- var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
- if (Thumbnails[previewIndex] is null)
- {
- previews[previewIndex] = new byte[encodeLength];
- return;
- }
+ previews[previewIndex] = new byte[encodeLength];
+ return;
+ }
- previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]);
+ previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]!);
- if (encodeLength != previews[previewIndex].Length)
- {
- throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
- }
- });
-
- for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ if (encodeLength != previews[previewIndex].Length)
{
- Helpers.SerializeWriteFileStream(outputFile, previews[i]);
- outputFile.WriteBytes(pageBreak);
- //Helpers.SerializeWriteFileStream(outputFile, pageBreak);
- previews[i] = null;
+ throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
}
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ });
+
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ {
+ Helpers.SerializeWriteFileStream(outputFile, previews[i]);
+ outputFile.WriteBytes(pageBreak);
+ //Helpers.SerializeWriteFileStream(outputFile, pageBreak);
+ previews[i] = null!;
+ }
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- var layerBytes = new List<byte>[LayerCount];
- foreach (var batch in BatchLayersIndexes())
- {
- progress.Token.ThrowIfCancellationRequested();
+ var layerBytes = new List<byte>[LayerCount];
+ foreach (var batch in BatchLayersIndexes())
+ {
+ progress.Token.ThrowIfCancellationRequested();
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = this[layerIndex];
+ using (var mat = layer.LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- var layer = this[layerIndex];
- using (var mat = layer.LayerMat)
- {
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteSpan();
- layerBytes[layerIndex] = new();
+ layerBytes[layerIndex] = new();
- uint lineCount = 0;
+ uint lineCount = 0;
- for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
+ for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
+ {
+ int y = layer.BoundingRectangle.Y;
+ int startY = -1;
+ for (; y < layer.BoundingRectangle.Bottom; y++)
{
- int y = layer.BoundingRectangle.Y;
- int startY = -1;
- for (; y < layer.BoundingRectangle.Bottom; y++)
+ int pos = mat.GetPixelPos(x, y);
+ if (span[pos] < 128) // Black pixel
{
- int pos = mat.GetPixelPos(x, y);
- if (span[pos] < 128) // Black pixel
- {
- if (startY == -1) continue; // Keep ignoring
- layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
- startY = -1;
- lineCount++;
- }
- else
- {
- if (startY >= 0) continue; // Keep sum
- startY = y;
- }
+ if (startY == -1) continue; // Keep ignoring
+ layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
+ startY = -1;
+ lineCount++;
}
-
- if (startY >= 0)
+ else
{
- layerBytes[layerIndex]
- .AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
- lineCount++;
+ if (startY >= 0) continue; // Keep sum
+ startY = y;
}
}
- layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount));
- layerBytes[layerIndex].AddRange(pageBreak);
+ if (startY >= 0)
+ {
+ layerBytes[layerIndex]
+ .AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
+ lineCount++;
+ }
}
- progress.LockAndIncrement();
- });
+ layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount));
+ layerBytes[layerIndex].AddRange(pageBreak);
+ }
- progress.Token.ThrowIfCancellationRequested();
+ progress.LockAndIncrement();
+ });
- foreach (var layerIndex in batch)
- {
- outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
- layerBytes[layerIndex] = null;
- }
+ progress.Token.ThrowIfCancellationRequested();
+
+ foreach (var layerIndex in batch)
+ {
+ outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
+ layerBytes[layerIndex] = null!;
}
+ }
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine("-End-");
- }
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
- protected override void DecodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.HeaderValue != Header.HEADER_VALUE)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (HeaderSettings.HeaderValue != Header.HEADER_VALUE)
- {
- throw new FileLoadException("Not a valid Makerbase file!", FileFullPath);
- }
+ throw new FileLoadException("Not a valid Makerbase file!", FileFullPath);
+ }
- byte[][] previews = new byte[ThumbnailsOriginalSize.Length][];
- for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
- {
- previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2];
- inputFile.ReadBytes(previews[i]);
- inputFile.Seek(2, SeekOrigin.Current);
- }
+ 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, CoreSettings.ParallelOptions, previewIndex =>
- {
- Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
- previews[previewIndex] = null;
- });
+ Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
+ previews[previewIndex] = null!;
+ });
- SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
+ SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
- LayerManager.Init(SlicerInfoSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ Init(SlicerInfoSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- if (DecodeType == FileDecodeType.Full)
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ var linesBytes = new byte[LayerCount][];
+ foreach (var batch in BatchLayersIndexes())
{
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- var linesBytes = new byte[LayerCount][];
- foreach (var batch in BatchLayersIndexes())
+ progress.Token.ThrowIfCancellationRequested();
+
+ foreach (var layerIndex in batch)
{
- progress.Token.ThrowIfCancellationRequested();
+ var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
- foreach (var layerIndex in batch)
- {
- var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
+ linesBytes[layerIndex] = new byte[lineCount * 6];
+ inputFile.ReadBytes(linesBytes[layerIndex]);
+ inputFile.Seek(2, SeekOrigin.Current);
- linesBytes[layerIndex] = new byte[lineCount * 6];
- inputFile.ReadBytes(linesBytes[layerIndex]);
- inputFile.Seek(2, SeekOrigin.Current);
+ progress.Token.ThrowIfCancellationRequested();
+ }
- progress.Token.ThrowIfCancellationRequested();
- }
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = EmguExtensions.InitMat(Resolution);
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ for (int i = 0; i < linesBytes[layerIndex].Length; i++)
{
- if (progress.Token.IsCancellationRequested) return;
- using var mat = EmguExtensions.InitMat(Resolution);
-
- for (int i = 0; i < linesBytes[layerIndex].Length; i++)
- {
- var startY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
- var endY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
- var startX = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i]);
+ var startY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
+ var endY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
+ var startX = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i]);
- CvInvoke.Line(mat, new Point(startX, startY), new Point(startX, endY),
- EmguExtensions.WhiteColor);
- }
+ CvInvoke.Line(mat, new Point(startX, startY), new Point(startX, endY),
+ EmguExtensions.WhiteColor);
+ }
- linesBytes[layerIndex] = null;
+ linesBytes[layerIndex] = null!;
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- progress.LockAndIncrement();
- });
- }
- }
- else // Partial read
- {
- inputFile.Seek(-Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.End);
- }
-
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (HeaderSettings.HeaderValue != Header.HEADER_VALUE)
- {
- throw new FileLoadException("Not a valid Makerbase file!", FileFullPath);
+ progress.LockAndIncrement();
+ });
}
}
+ else // Partial read
+ {
+ inputFile.Seek(-Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.End);
+ }
- protected override void PartialSaveInternally(OperationProgress progress)
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.HeaderValue != Header.HEADER_VALUE)
{
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ throw new FileLoadException("Not a valid Makerbase file!", FileFullPath);
}
+ }
- #endregion
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/GenericZIPFile.cs b/UVtools.Core/FileFormats/GenericZIPFile.cs
index b90e038..8430014 100644
--- a/UVtools.Core/FileFormats/GenericZIPFile.cs
+++ b/UVtools.Core/FileFormats/GenericZIPFile.cs
@@ -6,6 +6,9 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Util;
using System;
using System.Diagnostics;
using System.Drawing;
@@ -13,179 +16,179 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Util;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+#region Sub Classes
+[Serializable]
+[XmlRoot(ElementName = "Manifest")]
+public class GenericZipManifest
{
- #region Sub Classes
- [Serializable]
- [XmlRoot(ElementName = "Manifest")]
- public class GenericZipManifest
- {
- public string CreatedBy { get; set; } = About.SoftwareWithVersion;
+ public string CreatedBy { get; set; } = About.SoftwareWithVersion;
- public string UpdatedBy { get; set; } = About.SoftwareWithVersion;
+ public string UpdatedBy { get; set; } = About.SoftwareWithVersion;
- public string CreatedDate { get; set; } = DateTime.UtcNow.ToString("u");
+ public string CreatedDate { get; set; } = DateTime.UtcNow.ToString("u");
- public string LastModifiedDate { get; set; } = DateTime.UtcNow.ToString("u");
+ public string LastModifiedDate { get; set; } = DateTime.UtcNow.ToString("u");
- public float LayerHeight { get; set; }
+ public float LayerHeight { get; set; }
- public ushort ResolutionX { get; set; }
+ public ushort ResolutionX { get; set; }
- public ushort ResolutionY { get; set; }
+ public ushort ResolutionY { get; set; }
- public float DisplayWidth { get; set; }
+ public float DisplayWidth { get; set; }
- public float DisplayHeight { get; set; }
+ public float DisplayHeight { get; set; }
- public float MachineZ { get; set; }
+ public float MachineZ { get; set; }
- public void Update()
- {
- UpdatedBy = About.SoftwareWithVersion;
- LastModifiedDate = DateTime.UtcNow.ToString("u");
- }
+ public void Update()
+ {
+ UpdatedBy = About.SoftwareWithVersion;
+ LastModifiedDate = DateTime.UtcNow.ToString("u");
}
- #endregion
+}
+#endregion
- public class GenericZIPFile : FileFormat
- {
- #region Constants
- private const string ManifestFileName = "manifest.uvtools";
- #endregion
+public class GenericZIPFile : FileFormat
+{
+ #region Constants
+ private const string ManifestFileName = "manifest.uvtools";
+ #endregion
- #region Properties
- public GenericZipManifest ManifestFile { get; set; } = new ();
+ #region Properties
+ public GenericZipManifest ManifestFile { get; set; } = new ();
- public override FileFormatType FileType => FileFormatType.Archive;
+ public override FileFormatType FileType => FileFormatType.Archive;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(GenericZIPFile), "zip", "Generic / Phrozen Zip")
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(GenericZIPFile), "zip", "Generic / Phrozen Zip")
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => ManifestFile.ResolutionX;
+ set
{
- get => ManifestFile.ResolutionX;
- set
- {
- ManifestFile.ResolutionX = (ushort) value;
- RaisePropertyChanged();
- }
+ ManifestFile.ResolutionX = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => ManifestFile.ResolutionY;
+ set
{
- get => ManifestFile.ResolutionY;
- set
- {
- ManifestFile.ResolutionY = (ushort)value;
- RaisePropertyChanged();
- }
+ ManifestFile.ResolutionY = (ushort)value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => ManifestFile.DisplayWidth;
+ set
{
- get => ManifestFile.DisplayWidth;
- set
- {
- ManifestFile.DisplayWidth = value;
- RaisePropertyChanged();
- }
+ ManifestFile.DisplayWidth = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => ManifestFile.DisplayHeight;
+ set
{
- get => ManifestFile.DisplayHeight;
- set
- {
- ManifestFile.DisplayHeight = value;
- RaisePropertyChanged();
- }
+ ManifestFile.DisplayHeight = value;
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
+ public override float MachineZ
+ {
+ get => ManifestFile.MachineZ > 0 ? ManifestFile.MachineZ : base.MachineZ;
+ set
{
- get => ManifestFile.MachineZ > 0 ? ManifestFile.MachineZ : base.MachineZ;
- set
- {
- ManifestFile.MachineZ = value;
- RaisePropertyChanged();
- }
+ ManifestFile.MachineZ = value;
+ RaisePropertyChanged();
}
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => ManifestFile.LayerHeight;
+ set
{
- get => ManifestFile.LayerHeight;
- set
- {
- ManifestFile.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ ManifestFile.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- /*public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = ManifestFile.Slices.LayerCount = base.LayerCount;
- }*/
+ /*public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = ManifestFile.Slices.LayerCount = base.LayerCount;
+ }*/
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(854, 480),
- new(472, 240)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(854, 480),
+ new(472, 240)
+ };
- public override object[] Configs => new object[] {
- ManifestFile
- };
+ public override object[] Configs => new object[] {
+ ManifestFile
+ };
- #endregion
+ #endregion
- #region Constructor
- public GenericZIPFile()
- { }
- #endregion
+ #region Constructor
+ public GenericZIPFile()
+ { }
+ #endregion
- #region Methods
+ #region Methods
- public override bool CanProcess(string fileFullPath)
- {
- if(!base.CanProcess(fileFullPath)) return false;
+ public override bool CanProcess(string fileFullPath)
+ {
+ if(!base.CanProcess(fileFullPath)) return false;
- try
- {
- using var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read);
- foreach (var entry in zip.Entries)
- {
- if (entry.Name == ManifestFileName) return true;
- if (entry.Name.EndsWith(".gcode")) return false;
- }
- }
- catch (Exception e)
+ try
+ {
+ using var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read);
+ foreach (var entry in zip.Entries)
{
- Debug.WriteLine(e);
- return false;
+ if (entry.Name == ManifestFileName) return true;
+ if (entry.Name.EndsWith(".gcode")) return false;
}
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ return false;
+ }
- return true;
- }
+ return true;
+ }
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
- protected override void EncodeInternally(OperationProgress progress)
+ if (Thumbnails is not null)
{
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
if (Thumbnails.Length > 0 && Thumbnails[0] is not null)
{
using var stream = outputFile.CreateEntry("preview.png").Open();
- stream.WriteBytes(Thumbnails[0].GetPngByes());
+ stream.WriteBytes(Thumbnails[0]!.GetPngByes());
stream.Close();
}
@@ -193,135 +196,129 @@ namespace UVtools.Core.FileFormats
{
using var stream = outputFile.CreateEntry("preview_cropping.png").Open();
using var vec = new VectorOfByte();
- stream.WriteBytes(Thumbnails[1].GetPngByes());
+ stream.WriteBytes(Thumbnails[1]!.GetPngByes());
stream.Close();
}
+ }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- var layer = this[layerIndex];
- var filename = $"{layerIndex + 1}.png";
- outputFile.PutFileContent(filename, layer.CompressedBytes, ZipArchiveMode.Create);
- progress++;
- }
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ var layer = this[layerIndex];
+ var filename = $"{layerIndex + 1}.png";
+ outputFile.PutFileContent(filename, layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
+ }
- ManifestFile.Update();
+ ManifestFile.Update();
- XmlSerializer serializer = new(ManifestFile.GetType());
- XmlSerializerNamespaces ns = new();
- ns.Add("", "");
- var entry = outputFile.CreateEntry(ManifestFileName);
- using var streamManifest = entry.Open();
- serializer.Serialize(streamManifest, ManifestFile, ns);
- }
+ var entry = outputFile.CreateEntry(ManifestFileName);
+ using var streamManifest = entry.Open();
+ XmlExtensions.Serialize(ManifestFile, streamManifest, XmlExtensions.SettingsIndent, true);
+ }
- protected override void DecodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using (var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read))
{
- using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
+ var entry = inputFile.Entries.FirstOrDefault(zipEntry => zipEntry.Name == ManifestFileName);
+ if (entry is not null)
{
- var entry = inputFile.Entries.FirstOrDefault(zipEntry => zipEntry.Name == ManifestFileName);
- if (entry is not null)
+ try
{
- try
- {
- var serializer = new XmlSerializer(ManifestFile.GetType());
- using var stream = entry.Open();
- ManifestFile = (GenericZipManifest) serializer.Deserialize(stream);
- }
- catch (Exception)
- {
- // Not required
- //Clear();
- //throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
- }
+ using var stream = entry.Open();
+ ManifestFile = XmlExtensions.DeserializeFromStream<GenericZipManifest>(stream);
}
-
- uint layerCount = 0;
- foreach (var zipEntry in inputFile.Entries)
+ catch (Exception)
{
- if (!zipEntry.Name.EndsWith(".png")) continue;
- var filename = Path.GetFileNameWithoutExtension(zipEntry.Name);
- if (!filename.All(char.IsDigit)) continue;
- if (!uint.TryParse(filename, out var layerIndex)) continue;
- layerCount = Math.Max(layerCount, layerIndex);
+ // Not required
+ //Clear();
+ //throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
}
+ }
- if (layerCount == 0)
- {
- Clear();
- throw new FileLoadException("Unable to detect layer images in the file", FileFullPath);
- }
+ uint layerCount = 0;
+ foreach (var zipEntry in inputFile.Entries)
+ {
+ if (!zipEntry.Name.EndsWith(".png")) continue;
+ var filename = Path.GetFileNameWithoutExtension(zipEntry.Name);
+ if (!filename.All(char.IsDigit)) continue;
+ if (!uint.TryParse(filename, out var layerIndex)) continue;
+ layerCount = Math.Max(layerCount, layerIndex);
+ }
+
+ if (layerCount == 0)
+ {
+ Clear();
+ throw new FileLoadException("Unable to detect layer images in the file", FileFullPath);
+ }
- LayerManager.Init(layerCount, DecodeType == FileDecodeType.Partial);
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ Init(layerCount, DecodeType == FileDecodeType.Partial);
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ if (progress.Token.IsCancellationRequested) break;
+ var filename = $"{layerIndex + 1}.png";
+ entry = inputFile.GetEntry(filename);
+ if (entry is null)
{
- if (progress.Token.IsCancellationRequested) break;
- var filename = $"{layerIndex + 1}.png";
- entry = inputFile.GetEntry(filename);
- if (entry is null)
- {
- Clear();
- throw new FileLoadException($"Layer {filename} not found", FileFullPath);
- }
-
- if (DecodeType == FileDecodeType.Full)
- {
- using var stream = entry.Open();
- this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
- }
-
- progress++;
+ Clear();
+ throw new FileLoadException($"Layer {filename} not found", FileFullPath);
}
- entry = inputFile.GetEntry("preview.png");
- if (entry is not null)
+ if (DecodeType == FileDecodeType.Full)
{
- Thumbnails[0] = new Mat();
- CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
+ using var stream = entry.Open();
+ this[layerIndex] = new Layer(layerIndex, stream, this);
}
- entry = inputFile.GetEntry("preview_cropping.png");
- if (entry is not null)
- {
- var count = CreatedThumbnailsCount;
- Thumbnails[count] = new Mat();
- CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[count]);
- }
+ progress++;
+ }
+
+ entry = inputFile.GetEntry("preview.png");
+ if (entry is not null)
+ {
+ Thumbnails[0] = new Mat();
+ CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
}
- LayerManager.GetBoundingRectangle(progress);
+ entry = inputFile.GetEntry("preview_cropping.png");
+ if (entry is not null)
+ {
+ var count = CreatedThumbnailsCount;
+ Thumbnails[count] = new Mat();
+ CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[count]);
+ }
}
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- bool deleted;
+ GetBoundingRectangle(progress);
+ }
- do
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ bool deleted;
+
+ do
+ {
+ deleted = false;
+ foreach (var zipEntry in outputFile.Entries)
{
- deleted = false;
- foreach (var zipEntry in outputFile.Entries)
- {
- if (zipEntry.Name != ManifestFileName) continue;
- zipEntry.Delete();
- deleted = true;
- break;
- }
- } while (deleted);
+ if (zipEntry.Name != ManifestFileName) continue;
+ zipEntry.Delete();
+ deleted = true;
+ break;
+ }
+ } while (deleted);
- ManifestFile.Update();
+ ManifestFile.Update();
- XmlSerializer serializer = new(ManifestFile.GetType());
- XmlSerializerNamespaces ns = new();
- ns.Add("", "");
- var entry = outputFile.CreateEntry(ManifestFileName);
- using var stream = entry.Open();
- serializer.Serialize(stream, ManifestFile, ns);
- }
- #endregion
+ var entry = outputFile.CreateEntry(ManifestFileName);
+ using var stream = entry.Open();
+
+ XmlExtensions.Serialize(ManifestFile, stream, XmlExtensions.SettingsIndent, true);
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs
index e13b738..fc1be99 100644
--- a/UVtools.Core/FileFormats/ImageFile.cs
+++ b/UVtools.Core/FileFormats/ImageFile.cs
@@ -1,117 +1,115 @@
-using System;
-using Emgu.CV;
+using Emgu.CV;
using Emgu.CV.CvEnum;
+using System;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
using Size = System.Drawing.Size;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class ImageFile : FileFormat
{
- public class ImageFile : FileFormat
+ public override FileFormatType FileType => FileFormatType.Binary;
+
+ public override FileExtension[] FileExtensions { get; } =
{
- public override FileFormatType FileType => FileFormatType.Binary;
+ new (typeof(ImageFile), "png", "PNG: Portable Network Graphics"),
+ new (typeof(ImageFile), "jpg", "JPG: Joint Photographic Experts Group"),
+ new (typeof(ImageFile), "jpeg", "JPEG: Joint Photographic Experts Group"),
+ new (typeof(ImageFile), "jp2", "JP2: Joint Photographic Experts Group (JPEG 2000)"),
+ //new (typeof(ImageFile), "tga", "TGA: Truevision"),
+ new (typeof(ImageFile), "tif", "TIF: Tag Image File Format"),
+ new (typeof(ImageFile), "tiff", "TIFF: Tag Image File Format"),
+ new (typeof(ImageFile), "bmp", "BMP: Bitmap"),
+ new (typeof(ImageFile), "pbm", "PBM: Portable Bitmap"),
+ new (typeof(ImageFile), "pgm", "PGM: Portable Greymap"),
+ //new (typeof(ImageFile), "gif", "GIF"),
+ new (typeof(ImageFile), "sr", "SR: Sun raster"),
+ new (typeof(ImageFile), "RAS", "RAS: Sun raster"),
+ };
+ public override PrintParameterModifier[]? PrintParameterModifiers => null;
- public override FileExtension[] FileExtensions { get; } =
- {
- new (typeof(ImageFile), "png", "PNG: Portable Network Graphics"),
- new (typeof(ImageFile), "jpg", "JPG: Joint Photographic Experts Group"),
- new (typeof(ImageFile), "jpeg", "JPEG: Joint Photographic Experts Group"),
- new (typeof(ImageFile), "jp2", "JP2: Joint Photographic Experts Group (JPEG 2000)"),
- //new (typeof(ImageFile), "tga", "TGA: Truevision"),
- new (typeof(ImageFile), "tif", "TIF: Tag Image File Format"),
- new (typeof(ImageFile), "tiff", "TIFF: Tag Image File Format"),
- new (typeof(ImageFile), "bmp", "BMP: Bitmap"),
- new (typeof(ImageFile), "pbm", "PBM: Portable Bitmap"),
- new (typeof(ImageFile), "pgm", "PGM: Portable Greymap"),
- //new (typeof(ImageFile), "gif", "GIF"),
- new (typeof(ImageFile), "sr", "SR: Sun raster"),
- new (typeof(ImageFile), "RAS", "RAS: Sun raster"),
- };
- public override PrintParameterModifier[] PrintParameterModifiers => null;
+ public override Size[]? ThumbnailsOriginalSize { get; } = {
+ Size.Empty,
+ Size.Empty,
+ Size.Empty,
+ Size.Empty
+ };
+ public override uint ResolutionX
+ {
+ get => (uint) ImageMat.Width;
+ set => throw new NotImplementedException();
+ }
- public override Size[] ThumbnailsOriginalSize { get; } = {
- Size.Empty,
- Size.Empty,
- Size.Empty,
- Size.Empty
- };
- public override uint ResolutionX
- {
- get => (uint) ImageMat.Width;
- set => throw new NotImplementedException();
- }
+ public override uint ResolutionY
+ {
+ get => (uint) ImageMat.Height;
+ set => throw new NotImplementedException();
+ }
- public override uint ResolutionY
+ public override float DisplayWidth
+ {
+ get => ResolutionX;
+ set
{
- get => (uint) ImageMat.Height;
- set => throw new NotImplementedException();
+ ResolutionX = (uint) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayHeight
+ {
+ get => ResolutionY;
+ set
{
- get => ResolutionX;
- set
- {
- ResolutionX = (uint) value;
- RaisePropertyChanged();
- }
+ ResolutionY = (uint) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
- {
- get => ResolutionY;
- set
- {
- ResolutionY = (uint) value;
- RaisePropertyChanged();
- }
- }
+ public override float LayerHeight { get; set; } = 0.01f;
+ /*public override float PrintTime { get; } = 0;
+ public override float UsedMaterial { get; } = 0;
+ public override float MaterialCost { get; } = 0;
+ public override string MaterialName { get; } = null;
+ public override string MachineName { get; } = null;*/
- public override float LayerHeight { get; set; } = 0.01f;
- /*public override float PrintTime { get; } = 0;
- public override float UsedMaterial { get; } = 0;
- public override float MaterialCost { get; } = 0;
- public override string MaterialName { get; } = null;
- public override string MachineName { get; } = null;*/
- public override object[] Configs { get; } = null;
+ private Mat ImageMat { get; set; } = null!;
- private Mat ImageMat { get; set; }
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ this[0].LayerMat.Save(FileFullPath);
+ }
- protected override void EncodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ ImageMat = CvInvoke.Imread(FileFullPath, ImreadModes.Grayscale);
+ const byte startDivisor = 2;
+ for (int i = 0; i < ThumbnailsCount; i++)
{
- this[0].LayerMat.Save(FileFullPath);
+ Thumbnails[i] = new Mat();
+ var divisor = (i + 1) * startDivisor;
+ CvInvoke.Resize(ImageMat, Thumbnails[i],
+ new Size(ImageMat.Width / divisor, ImageMat.Height / divisor));
}
- protected override void DecodeInternally(OperationProgress progress)
+ /*if (ImageMat.NumberOfChannels > 1)
{
- ImageMat = CvInvoke.Imread(FileFullPath, ImreadModes.Grayscale);
- const byte startDivisor = 2;
- for (int i = 0; i < ThumbnailsCount; i++)
- {
- Thumbnails[i] = new Mat();
- var divisor = (i + 1) * startDivisor;
- CvInvoke.Resize(ImageMat, Thumbnails[i],
- new Size(ImageMat.Width / divisor, ImageMat.Height / divisor));
- }
-
- /*if (ImageMat.NumberOfChannels > 1)
- {
- CvInvoke.CvtColor(ImageMat, ImageMat, ColorConversion.Bgr2Gray);
- }*/
- LayerManager.Init(1);
- this[0] = new Layer(0, ImageMat, LayerManager);
- }
+ CvInvoke.CvtColor(ImageMat, ImageMat, ColorConversion.Bgr2Gray);
+ }*/
+ Init(1);
+ this[0] = new Layer(0, ImageMat, this);
+ }
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- this[0].LayerMat.Save(FileFullPath);
- }
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ this[0].LayerMat.Save(FileFullPath);
+ }
- public override FileFormat Convert(Type to, string fileFullPath, uint version = 0, OperationProgress progress = null)
- {
- throw new NotSupportedException();
- }
+ public override FileFormat Convert(Type to, string fileFullPath, uint version = 0, OperationProgress? progress = null)
+ {
+ throw new NotSupportedException();
+ }
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs
index 492c438..13f50ac 100644
--- a/UVtools.Core/FileFormats/LGSFile.cs
+++ b/UVtools.Core/FileFormats/LGSFile.cs
@@ -6,631 +6,630 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class LGSFile : FileFormat
{
- public class LGSFile : FileFormat
- {
- #region Sub Classes
+ #region Sub Classes
- #region Header
+ #region Header
- public class Header
- {
- public const string NameValue = "Longer3D";
+ public class Header
+ {
+ public const string NameValue = "Longer3D";
- /// <summary>
- /// Gets the model name
- /// </summary>
- [FieldOrder(0)] [FieldLength(8)] public string Name { get; set; } = NameValue; // 0x00:
- [FieldOrder(1)] public uint Uint_08 { get; set; } = 1; // 0x08: 0xff000001 ?
- [FieldOrder(2)] public uint Uint_0c { get; set; } = 1; // 0x0c: 1 ?
- [FieldOrder(3)] public uint PrinterModel { get; set; } = 30; // 10, 30, 120, 4000 (4k), 4500 (4k mono)
- [FieldOrder(4)] public uint Uint_14 { get; set; } = 0; // 0x14: 0 ?
- [FieldOrder(5)] public uint MagicKey { get; set; } = 34; // 0x18: 34
- [FieldOrder(6)] public float PixelPerMmX { get; set; } = 15.404f;
- [FieldOrder(7)] public float PixelPerMmY { get; set; } = 4.866f;
- [FieldOrder(8)] public float ResolutionX { get; set; }
- [FieldOrder(9)] public float ResolutionY { get; set; }
- [FieldOrder(10)] public float LayerHeight { get; set; }
- [FieldOrder(11)] public float ExposureTimeMs { get; set; }
- [FieldOrder(12)] public float BottomExposureTimeMs { get; set; }
- [FieldOrder(13)] public float Float_38 { get; set; } = 10; // 0x38: 10
- [FieldOrder(14)] public float LightOffDelayMs { get; set; } = 2000;
- [FieldOrder(15)] public float BottomLightOffDelayMs { get; set; }
- [FieldOrder(16)] public float BottomHeight { get; set; }
- [FieldOrder(17)] public float Float_48 { get; set; } = 0.6f; // 0x48: 0.6
- [FieldOrder(18)] public float BottomLiftHeight { get; set; } = 4;
- [FieldOrder(19)] public float LiftHeight { get; set; }
- [FieldOrder(20)] public float LiftSpeed { get; set; } = 150;
- [FieldOrder(21)] public float LiftSpeed_ { get; set; } = 150;
- [FieldOrder(22)] public float BottomLiftSpeed { get; set; } = 90;
- [FieldOrder(23)] public float BottomLiftSpeed_ { get; set; } = 90;
- [FieldOrder(24)] public float Float_64 { get; set; } = 5; // 0x64: 5?
- [FieldOrder(25)] public float Float_68 { get; set; } = 60; // 0x68: 60?
- [FieldOrder(26)] public float Float_6c { get; set; } = 10; // 0x6c: 10?
- [FieldOrder(27)] public float Float_70 { get; set; } = 600; // 0x70: 600?
- [FieldOrder(28)] public float Float_74 { get; set; } = 600; // 0x70: 600?
- [FieldOrder(29)] public float Float_78 { get; set; } = 2; // 0x78: 2?
- [FieldOrder(30)] public float Float_7c { get; set; } = 0.2f; // 0x7c: 0.2?
- [FieldOrder(31)] public float Float_80 { get; set; } = 60; // 0x80: 60?
- [FieldOrder(32)] public float Float_84 { get; set; } = 1; // 0x84: 1?
- [FieldOrder(33)] public float Float_88 { get; set; } = 6; // 0x88: 6?
- [FieldOrder(34)] public float Float_8c { get; set; } = 150; // 0x8c: 150 ?
- [FieldOrder(35)] public float Float_90 { get; set; } = 1001; // 0x90: 1001 ?
- [FieldOrder(36)] public float MachineZ { get; set; } = 140;// 0x94: 140 for lgs10, 170 for lgs30, 150 for lgs120, 190 for lgs4k
- [FieldOrder(37)] public uint Uint_98 { get; set; } // 0x98: 0 ?
- [FieldOrder(38)] public uint Uint_9c { get; set; } // 0x9c: 0 ?
- [FieldOrder(39)] public uint Uint_a0 { get; set; } // 0xa0: 0 ?
- [FieldOrder(40)] public uint LayerCount { get; set; }
- [FieldOrder(41)] public uint Uint_a8 { get; set; } = 4; // 0xa8: 4 ?
- [FieldOrder(42)] public uint PreviewSizeX { get; set; } = 120;
- [FieldOrder(43)] public uint PreviewSizeY { get; set; } = 150;
- }
+ /// <summary>
+ /// Gets the model name
+ /// </summary>
+ [FieldOrder(0)] [FieldLength(8)] public string Name { get; set; } = NameValue; // 0x00:
+ [FieldOrder(1)] public uint Uint_08 { get; set; } = 1; // 0x08: 0xff000001 ?
+ [FieldOrder(2)] public uint Uint_0c { get; set; } = 1; // 0x0c: 1 ?
+ [FieldOrder(3)] public uint PrinterModel { get; set; } = 30; // 10, 30, 120, 4000 (4k), 4500 (4k mono)
+ [FieldOrder(4)] public uint Uint_14 { get; set; } = 0; // 0x14: 0 ?
+ [FieldOrder(5)] public uint MagicKey { get; set; } = 34; // 0x18: 34
+ [FieldOrder(6)] public float PixelPerMmX { get; set; } = 15.404f;
+ [FieldOrder(7)] public float PixelPerMmY { get; set; } = 4.866f;
+ [FieldOrder(8)] public float ResolutionX { get; set; }
+ [FieldOrder(9)] public float ResolutionY { get; set; }
+ [FieldOrder(10)] public float LayerHeight { get; set; }
+ [FieldOrder(11)] public float ExposureTimeMs { get; set; }
+ [FieldOrder(12)] public float BottomExposureTimeMs { get; set; }
+ [FieldOrder(13)] public float Float_38 { get; set; } = 10; // 0x38: 10
+ [FieldOrder(14)] public float LightOffDelayMs { get; set; } = 2000;
+ [FieldOrder(15)] public float BottomLightOffDelayMs { get; set; }
+ [FieldOrder(16)] public float BottomHeight { get; set; }
+ [FieldOrder(17)] public float Float_48 { get; set; } = 0.6f; // 0x48: 0.6
+ [FieldOrder(18)] public float BottomLiftHeight { get; set; } = 4;
+ [FieldOrder(19)] public float LiftHeight { get; set; }
+ [FieldOrder(20)] public float LiftSpeed { get; set; } = 150;
+ [FieldOrder(21)] public float LiftSpeed_ { get; set; } = 150;
+ [FieldOrder(22)] public float BottomLiftSpeed { get; set; } = 90;
+ [FieldOrder(23)] public float BottomLiftSpeed_ { get; set; } = 90;
+ [FieldOrder(24)] public float Float_64 { get; set; } = 5; // 0x64: 5?
+ [FieldOrder(25)] public float Float_68 { get; set; } = 60; // 0x68: 60?
+ [FieldOrder(26)] public float Float_6c { get; set; } = 10; // 0x6c: 10?
+ [FieldOrder(27)] public float Float_70 { get; set; } = 600; // 0x70: 600?
+ [FieldOrder(28)] public float Float_74 { get; set; } = 600; // 0x70: 600?
+ [FieldOrder(29)] public float Float_78 { get; set; } = 2; // 0x78: 2?
+ [FieldOrder(30)] public float Float_7c { get; set; } = 0.2f; // 0x7c: 0.2?
+ [FieldOrder(31)] public float Float_80 { get; set; } = 60; // 0x80: 60?
+ [FieldOrder(32)] public float Float_84 { get; set; } = 1; // 0x84: 1?
+ [FieldOrder(33)] public float Float_88 { get; set; } = 6; // 0x88: 6?
+ [FieldOrder(34)] public float Float_8c { get; set; } = 150; // 0x8c: 150 ?
+ [FieldOrder(35)] public float Float_90 { get; set; } = 1001; // 0x90: 1001 ?
+ [FieldOrder(36)] public float MachineZ { get; set; } = 140;// 0x94: 140 for lgs10, 170 for lgs30, 150 for lgs120, 190 for lgs4k
+ [FieldOrder(37)] public uint Uint_98 { get; set; } // 0x98: 0 ?
+ [FieldOrder(38)] public uint Uint_9c { get; set; } // 0x9c: 0 ?
+ [FieldOrder(39)] public uint Uint_a0 { get; set; } // 0xa0: 0 ?
+ [FieldOrder(40)] public uint LayerCount { get; set; }
+ [FieldOrder(41)] public uint Uint_a8 { get; set; } = 4; // 0xa8: 4 ?
+ [FieldOrder(42)] public uint PreviewSizeX { get; set; } = 120;
+ [FieldOrder(43)] public uint PreviewSizeY { get; set; } = 150;
+ }
- #endregion
+ #endregion
- #region LGS120PngPreview
+ #region LGS120PngPreview
- public class LGS120PngPreview
- {
- public const ushort ResolutionX = 1200;
- public const ushort ResolutionY = 1600;
+ public class LGS120PngPreview
+ {
+ public const ushort ResolutionX = 1200;
+ public const ushort ResolutionY = 1600;
- [FieldOrder(0)] public uint DataSize { get; set; }
+ [FieldOrder(0)] public uint DataSize { get; set; }
- [FieldOrder(1)]
- [FieldLength(nameof(DataSize))]
- public byte[] PngBytes { get; set; }
+ [FieldOrder(1)]
+ [FieldLength(nameof(DataSize))]
+ public byte[] PngBytes { get; set; } = null!;
- [FieldOrder(2)] public ushort Padding { get; set; }
+ [FieldOrder(2)] public ushort Padding { get; set; }
- public void Encode(Mat mat)
- {
- mat ??= EmguExtensions.InitMat(new Size(ResolutionX, ResolutionY), 3);
+ public void Encode(Mat mat)
+ {
+ mat ??= EmguExtensions.InitMat(new Size(ResolutionX, ResolutionY), 3);
- if (mat.Width != ResolutionX || mat.Height != ResolutionY)
- {
- using var resizeMat = new Mat();
- CvInvoke.Resize(mat, resizeMat, new Size(ResolutionX, ResolutionY));
- PngBytes = resizeMat.GetPngByes();
- }
- else
- {
- PngBytes = mat.GetPngByes();
- }
+ if (mat.Width != ResolutionX || mat.Height != ResolutionY)
+ {
+ using var resizeMat = new Mat();
+ CvInvoke.Resize(mat, resizeMat, new Size(ResolutionX, ResolutionY));
+ PngBytes = resizeMat.GetPngByes();
}
-
- public Mat Decode(bool consumeRle = true)
+ else
{
- var mat = new Mat();
- CvInvoke.Imdecode(PngBytes, ImreadModes.AnyColor, mat);
- if (consumeRle)
- PngBytes = null;
- return mat;
+ PngBytes = mat.GetPngByes();
}
}
- #endregion
+ public Mat Decode(bool consumeRle = true)
+ {
+ var mat = new Mat();
+ CvInvoke.Imdecode(PngBytes, ImreadModes.AnyColor, mat);
+ if (consumeRle)
+ PngBytes = null!;
+ return mat;
+ }
+ }
- #region LayerData
+ #endregion
- public class LayerDef
- {
- [Ignore] public LGSFile Parent { get; set; }
+ #region LayerData
- [FieldOrder(0)]
- public uint DataSize { get; set; }
+ public class LayerDef
+ {
+ [Ignore] public LGSFile Parent { get; set; } = null!;
- [FieldOrder(1)]
- [FieldLength(nameof(DataSize))]
- public byte[] EncodedRle { get; set; }
+ [FieldOrder(0)]
+ public uint DataSize { get; set; }
- public LayerDef() { }
+ [FieldOrder(1)]
+ [FieldLength(nameof(DataSize))]
+ public byte[] EncodedRle { get; set; } = null!;
- public LayerDef(LGSFile parent)
- {
- Parent = parent;
- }
+ public LayerDef() { }
- public unsafe byte[] Encode(Mat mat)
- {
- List<byte> rawData = new();
- List<byte> chunk = new();
+ public LayerDef(LGSFile parent)
+ {
+ Parent = parent;
+ }
- if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
- {
- CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90Clockwise);
- }
+ public unsafe byte[] Encode(Mat mat)
+ {
+ List<byte> rawData = new();
+ List<byte> chunk = new();
- var spanMat = mat.GetBytePointer();
- var imageLength = mat.GetLength();
+ if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ {
+ CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90Clockwise);
+ }
- uint span = 0;
- byte lc = 0;
+ var spanMat = mat.GetBytePointer();
+ var imageLength = mat.GetLength();
- void addSpan(){
- chunk.Clear();
- for (; span > 0; span >>= 4) {
- chunk.Insert(0, (byte)((byte)(span & 0xf) | (lc & 0xf0)));
- }
- rawData.AddRange(chunk.ToArray());
+ uint span = 0;
+ byte lc = 0;
+
+ void addSpan(){
+ chunk.Clear();
+ for (; span > 0; span >>= 4) {
+ chunk.Insert(0, (byte)((byte)(span & 0xf) | (lc & 0xf0)));
}
+ rawData.AddRange(chunk.ToArray());
+ }
- for (int i = 0; i < imageLength; i++)
- {
- byte c = (byte) (spanMat[i] & 0xf0);
+ for (int i = 0; i < imageLength; i++)
+ {
+ byte c = (byte) (spanMat[i] & 0xf0);
- if (c == lc)
- {
- span++;
- }
- else
- {
- addSpan();
- span = 1;
- }
-
- lc = c;
+ if (c == lc)
+ {
+ span++;
}
-
- addSpan();
- EncodedRle = rawData.ToArray();
- DataSize = (uint) EncodedRle.Length;
-
- if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ else
{
- CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
+ addSpan();
+ span = 1;
}
- return EncodedRle;
+ lc = c;
}
- public Mat Decode(bool consumeRle = true)
- {
- // lgs10/30 -------->
- // lgs120/4k From Y bottom to top Y
- var mat = EmguExtensions.InitMat(Parent.HeaderSettings.PrinterModel is 4000 or 4500 ? Parent.Resolution.Exchange() : Parent.Resolution);
- //var matSpan = mat.GetBytePointer();
- var imageLength = mat.GetLength();
-
- int pixelPos = 0;
+ addSpan();
+ EncodedRle = rawData.ToArray();
+ DataSize = (uint) EncodedRle.Length;
- for (var i = 0; i < EncodedRle.Length; i++)
- {
- var b = EncodedRle[i];
- byte colorNibble = (byte)(b >> 4);
- byte color = (byte)(colorNibble << 4 | colorNibble);
- int repeat = b & 0xf;
-
- while (i + 1 < EncodedRle.Length && (EncodedRle[i + 1] >> 4) == colorNibble)
- {
- i++;
- repeat = (repeat << 4) | (EncodedRle[i] & 0xf);
- }
+ if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ {
+ CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
+ }
- if (pixelPos >= imageLength)
- {
- throw new FileLoadException($"Too much buffer, expected: {imageLength}, got: {pixelPos}");
- }
+ return EncodedRle;
+ }
- mat.FillSpan(ref pixelPos, repeat, color);
+ public Mat Decode(bool consumeRle = true)
+ {
+ // lgs10/30 -------->
+ // lgs120/4k From Y bottom to top Y
+ var mat = EmguExtensions.InitMat(Parent.HeaderSettings.PrinterModel is 4000 or 4500 ? Parent.Resolution.Exchange() : Parent.Resolution);
+ //var matSpan = mat.GetBytePointer();
+ var imageLength = mat.GetLength();
+
+ int pixelPos = 0;
- //if (repeat <= 0) continue;
- /*while (repeat-- > 0)
- {
- matSpan[pixel++] = color;
- }*/
+ for (var i = 0; i < EncodedRle.Length; i++)
+ {
+ var b = EncodedRle[i];
+ byte colorNibble = (byte)(b >> 4);
+ byte color = (byte)(colorNibble << 4 | colorNibble);
+ int repeat = b & 0xf;
+ while (i + 1 < EncodedRle.Length && (EncodedRle[i + 1] >> 4) == colorNibble)
+ {
+ i++;
+ repeat = (repeat << 4) | (EncodedRle[i] & 0xf);
}
- if (pixelPos != imageLength)
+ if (pixelPos >= imageLength)
{
- throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {pixelPos}");
+ throw new FileLoadException($"Too much buffer, expected: {imageLength}, got: {pixelPos}");
}
- if (consumeRle)
- EncodedRle = null;
+ mat.FillSpan(ref pixelPos, repeat, color);
- if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ //if (repeat <= 0) continue;
+ /*while (repeat-- > 0)
{
- CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
- }
+ matSpan[pixel++] = color;
+ }*/
- return mat;
}
+
+ if (pixelPos != imageLength)
+ {
+ throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {pixelPos}");
+ }
+
+ if (consumeRle)
+ EncodedRle = null!;
+
+ if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ {
+ CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
+ }
+
+ return mat;
}
- #endregion
+ }
+ #endregion
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public Header HeaderSettings { get; protected internal set; } = new();
- public override FileFormatType FileType => FileFormatType.Binary;
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new (typeof(LGSFile), "lgs", "Longer Orange 10"),
- new (typeof(LGSFile), "lgs30", "Longer Orange 30"),
- new (typeof(LGSFile), "lgs120", "Longer Orange 120"),
- new (typeof(LGSFile), "lgs4k", "Longer Orange 4k"),
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new (typeof(LGSFile), "lgs", "Longer Orange 10"),
+ new (typeof(LGSFile), "lgs30", "Longer Orange 30"),
+ new (typeof(LGSFile), "lgs120", "Longer Orange 120"),
+ new (typeof(LGSFile), "lgs4k", "Longer Orange 4k"),
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
- };
+ };
- public override Size[] ThumbnailsOriginalSize { get; } = {new(120, 150)};
+ public override Size[]? ThumbnailsOriginalSize { get; } = {new(120, 150)};
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => (uint) HeaderSettings.ResolutionX;
+ set
{
- get => (uint) HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => (uint)HeaderSettings.ResolutionY;
+ set
{
- get => (uint)HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float Xppmm
+ public override float Xppmm
+ {
+ get => HeaderSettings.PixelPerMmX > 0 ? HeaderSettings.PixelPerMmX : base.Xppmm;
+ set
{
- get => HeaderSettings.PixelPerMmX > 0 ? HeaderSettings.PixelPerMmX : base.Xppmm;
- set
- {
- HeaderSettings.PixelPerMmX = value;
- base.Xppmm = value;
- }
+ HeaderSettings.PixelPerMmX = value;
+ base.Xppmm = value;
}
+ }
- public override float Yppmm
+ public override float Yppmm
+ {
+ get => HeaderSettings.PixelPerMmY > 0 ? HeaderSettings.PixelPerMmY : base.Yppmm;
+ set
{
- get => HeaderSettings.PixelPerMmY > 0 ? HeaderSettings.PixelPerMmY : base.Yppmm;
- set
- {
- HeaderSettings.PixelPerMmY = value;
- base.Yppmm = value;
- }
+ HeaderSettings.PixelPerMmY = value;
+ base.Yppmm = value;
}
+ }
- public override float DisplayWidth
- {
- get => ResolutionX / HeaderSettings.PixelPerMmX;
- set { }
- }
+ public override float DisplayWidth
+ {
+ get => ResolutionX / HeaderSettings.PixelPerMmX;
+ set { }
+ }
- public override float DisplayHeight
- {
- get => ResolutionY / HeaderSettings.PixelPerMmY;
- set { }
- }
+ public override float DisplayHeight
+ {
+ get => ResolutionY / HeaderSettings.PixelPerMmY;
+ set { }
+ }
- public override float MachineZ
- {
- get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ;
- set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2);
- }
+ public override float MachineZ
+ {
+ get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ;
+ set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2);
+ }
- public override Enumerations.FlipDirection DisplayMirror
- {
- get => Enumerations.FlipDirection.Horizontally;
- set { }
- }
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => Enumerations.FlipDirection.Horizontally;
+ set { }
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => HeaderSettings.LayerHeight;
+ set
{
- get => HeaderSettings.LayerHeight;
- set
- {
- HeaderSettings.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ HeaderSettings.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
+ }
- public override ushort BottomLayerCount
+ public override ushort BottomLayerCount
+ {
+ get => (ushort) (HeaderSettings.BottomHeight / LayerHeight);
+ set
{
- get => (ushort) (HeaderSettings.BottomHeight / LayerHeight);
- set
- {
- HeaderSettings.BottomHeight = value * LayerHeight;
- base.BottomLayerCount = value;
- }
+ HeaderSettings.BottomHeight = value * LayerHeight;
+ base.BottomLayerCount = value;
}
+ }
- public override float BottomLightOffDelay
+ public override float BottomLightOffDelay
+ {
+ get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.BottomLightOffDelayMs);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.BottomLightOffDelayMs);
- set
- {
- HeaderSettings.BottomLightOffDelayMs = TimeExtensions.SecondsToMilliseconds(value);
- base.BottomLightOffDelay = value;
- }
+ HeaderSettings.BottomLightOffDelayMs = TimeExtensions.SecondsToMilliseconds(value);
+ base.BottomLightOffDelay = value;
}
+ }
- public override float LightOffDelay
+ public override float LightOffDelay
+ {
+ get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.LightOffDelayMs);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.LightOffDelayMs);
- set
- {
- HeaderSettings.LightOffDelayMs = TimeExtensions.SecondsToMilliseconds(value);
- base.LightOffDelay = value;
- }
+ HeaderSettings.LightOffDelayMs = TimeExtensions.SecondsToMilliseconds(value);
+ base.LightOffDelay = value;
}
+ }
- public override float BottomWaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
{
- get => base.BottomWaitTimeBeforeCure;
- set
- {
- SetBottomLightOffDelay(value);
- base.BottomWaitTimeBeforeCure = value;
- }
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
{
- get => base.WaitTimeBeforeCure;
- set
- {
- SetNormalLightOffDelay(value);
- base.WaitTimeBeforeCure = value;
- }
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
}
+ }
- public override float BottomExposureTime
+ public override float BottomExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.BottomExposureTimeMs);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.BottomExposureTimeMs);
- set
- {
- HeaderSettings.BottomExposureTimeMs = TimeExtensions.SecondsToMilliseconds(value);
- base.BottomExposureTime = value;
- }
+ HeaderSettings.BottomExposureTimeMs = TimeExtensions.SecondsToMilliseconds(value);
+ base.BottomExposureTime = value;
}
+ }
- public override float ExposureTime
+ public override float ExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.ExposureTimeMs);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.ExposureTimeMs);
- set
- {
- HeaderSettings.ExposureTimeMs = TimeExtensions.SecondsToMilliseconds(value);
- base.ExposureTime = value;
- }
+ HeaderSettings.ExposureTimeMs = TimeExtensions.SecondsToMilliseconds(value);
+ base.ExposureTime = value;
}
+ }
- public override float BottomLiftHeight
- {
- get => HeaderSettings.BottomLiftHeight;
- set => base.BottomLiftHeight = HeaderSettings.BottomLiftHeight = value;
- }
+ public override float BottomLiftHeight
+ {
+ get => HeaderSettings.BottomLiftHeight;
+ set => base.BottomLiftHeight = HeaderSettings.BottomLiftHeight = value;
+ }
- public override float LiftHeight
- {
- get => HeaderSettings.LiftHeight;
- set => base.LiftHeight = HeaderSettings.LiftHeight = value;
- }
+ public override float LiftHeight
+ {
+ get => HeaderSettings.LiftHeight;
+ set => base.LiftHeight = HeaderSettings.LiftHeight = value;
+ }
- public override float BottomLiftSpeed
- {
- get => HeaderSettings.BottomLiftSpeed;
- set => base.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed_ = value;
- }
+ public override float BottomLiftSpeed
+ {
+ get => HeaderSettings.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed_ = value;
+ }
- public override float LiftSpeed
- {
- get => HeaderSettings.LiftSpeed;
- set => base.LiftSpeed = HeaderSettings.LiftSpeed = HeaderSettings.LiftSpeed_ = value;
- }
+ public override float LiftSpeed
+ {
+ get => HeaderSettings.LiftSpeed;
+ set => base.LiftSpeed = HeaderSettings.LiftSpeed = HeaderSettings.LiftSpeed_ = value;
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ public override float BottomRetractSpeed => RetractSpeed;
- public override float RetractSpeed => LiftSpeed;
+ public override float RetractSpeed => LiftSpeed;
- /*public override float PrintTime => 0;
+ /*public override float PrintTime => 0;
- public override float UsedMaterial => 0;
+ public override float UsedMaterial => 0;
- public override float MaterialCost => 0;
+ public override float MaterialCost => 0;
- public override string MaterialName => "Unknown";
- public override string MachineName => null;*/
+ public override string MaterialName => "Unknown";
+ public override string MachineName => null;*/
- public override object[] Configs => new object[] { HeaderSettings };
+ public override object[] Configs => new object[] { HeaderSettings };
+
+ #endregion
+
+ #region Constructors
+ public LGSFile()
+ {
+ }
+ #endregion
- #endregion
+ #region Methods
- #region Constructors
- public LGSFile()
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ if (FileEndsWith(".lgs")) // Longer Orange 10
{
+ MachineZ = 140;
+ HeaderSettings.PrinterModel = 10;
+ }
+ else if (FileEndsWith(".lgs30")) // Longer Orange 30
+ {
+ MachineZ = 170;
+ HeaderSettings.PrinterModel = 30;
+ }
+ else if (FileEndsWith(".lgs120")) // Longer Orange 120
+ {
+ MachineZ = 150;
+ HeaderSettings.PrinterModel = 120;
+ }
+ else if (FileEndsWith(".lgs4k")) // Longer Orange 4K & Mono
+ {
+ MachineZ = 190;
+ if(HeaderSettings.PrinterModel is not 4000 and not 4500) HeaderSettings.PrinterModel = 4500;
}
- #endregion
-
- #region Methods
- protected override void EncodeInternally(OperationProgress progress)
+ //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
+ using (var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write))
{
- if (FileEndsWith(".lgs")) // Longer Orange 10
- {
- MachineZ = 140;
- HeaderSettings.PrinterModel = 10;
- }
- else if (FileEndsWith(".lgs30")) // Longer Orange 30
- {
- MachineZ = 170;
- HeaderSettings.PrinterModel = 30;
- }
- else if (FileEndsWith(".lgs120")) // Longer Orange 120
- {
- MachineZ = 150;
- HeaderSettings.PrinterModel = 120;
- }
- else if (FileEndsWith(".lgs4k")) // Longer Orange 4K & Mono
- {
- MachineZ = 190;
- if(HeaderSettings.PrinterModel is not 4000 and not 4500) HeaderSettings.PrinterModel = 4500;
- }
+ outputFile.WriteSerialize(HeaderSettings);
+ outputFile.WriteBytes(EncodeImage(DATATYPE_RGB565_BE, Thumbnails[0]!));
- //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
- using (var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write))
+ if (HeaderSettings.PrinterModel == 120)
{
- outputFile.WriteSerialize(HeaderSettings);
- outputFile.WriteBytes(EncodeImage(DATATYPE_RGB565_BE, Thumbnails[0]));
-
- if (HeaderSettings.PrinterModel == 120)
- {
- // Insert PNG here
- var mat = GetThumbnail(true);
- var pngPreview = new LGS120PngPreview();
- pngPreview.Encode(mat);
- outputFile.WriteSerialize(pngPreview);
- }
+ // Insert PNG here
+ var mat = GetThumbnail(true);
+ var pngPreview = new LGS120PngPreview();
+ pngPreview.Encode(mat!);
+ outputFile.WriteSerialize(pngPreview);
+ }
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- var layerData = new LayerDef[LayerCount];
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var layerData = new LayerDef[LayerCount];
- foreach (var batch in BatchLayersIndexes())
+ foreach (var batch in BatchLayersIndexes())
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = this[layerIndex].LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = this[layerIndex].LayerMat)
- {
- layerData[layerIndex] = new LayerDef(this);
- layerData[layerIndex].Encode(mat);
- }
- progress.LockAndIncrement();
- });
-
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
- outputFile.WriteSerialize(layerData[layerIndex]);
- layerData[layerIndex].EncodedRle = null; // Free this
+ layerData[layerIndex] = new LayerDef(this);
+ layerData[layerIndex].Encode(mat);
}
- }
+ progress.LockAndIncrement();
+ });
-
- progress.ItemName = "Saving layers";
- progress.ProcessedItems = 0;
-
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ foreach (var layerIndex in batch)
{
progress.Token.ThrowIfCancellationRequested();
outputFile.WriteSerialize(layerData[layerIndex]);
- progress++;
+ layerData[layerIndex].EncodedRle = null!; // Free this
}
}
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine("-End-");
- }
- protected override void DecodeInternally(OperationProgress progress)
- {
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (HeaderSettings.Name != Header.NameValue)
+ progress.ItemName = "Saving layers";
+ progress.ProcessedItems = 0;
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- throw new FileLoadException("Not a valid LGS file!", FileFullPath);
+ progress.Token.ThrowIfCancellationRequested();
+ outputFile.WriteSerialize(layerData[layerIndex]);
+ progress++;
}
+ }
- //if (HeaderSettings.PrinterModel is 10 or 30 or 120)
- //{
- // Fix inconsistencies found of different version of plugin and slicers
- if (ResolutionX > ResolutionY)
- {
- (ResolutionX, ResolutionY) = (ResolutionY, ResolutionX);
- }
- //}
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
- var previewSize = HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2;
- var previewData = inputFile.ReadBytes(previewSize);
- Thumbnails[0] = DecodeImage(DATATYPE_RGB565_BE, previewData, HeaderSettings.PreviewSizeX, HeaderSettings.PreviewSizeY);
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.Name != Header.NameValue)
+ {
+ throw new FileLoadException("Not a valid LGS file!", FileFullPath);
+ }
- if (HeaderSettings.PrinterModel == 120)
- {
- var pngPreview = Helpers.Deserialize<LGS120PngPreview>(inputFile);
- }
+ //if (HeaderSettings.PrinterModel is 10 or 30 or 120)
+ //{
+ // Fix inconsistencies found of different version of plugin and slicers
+ if (ResolutionX > ResolutionY)
+ {
+ (ResolutionX, ResolutionY) = (ResolutionY, ResolutionX);
+ }
+ //}
+ var previewSize = HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2;
+ var previewData = inputFile.ReadBytes(previewSize);
+ Thumbnails[0] = DecodeImage(DATATYPE_RGB565_BE, previewData, HeaderSettings.PreviewSizeX, HeaderSettings.PreviewSizeY);
+
+ if (HeaderSettings.PrinterModel == 120)
+ {
+ var pngPreview = Helpers.Deserialize<LGS120PngPreview>(inputFile);
+ }
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- var layerData = new LayerDef[LayerCount];
- if (DecodeType == FileDecodeType.Full)
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ var layerData = new LayerDef[LayerCount];
+
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ foreach (var batch in BatchLayersIndexes())
{
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- foreach (var batch in BatchLayersIndexes())
+ foreach (var layerIndex in batch)
{
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- layerData[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
- layerData[layerIndex].Parent = this;
- }
+ layerData[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
+ layerData[layerIndex].Parent = this;
+ }
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = layerData[layerIndex].Decode();
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = layerData[layerIndex].Decode();
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- progress.LockAndIncrement();
- });
- }
+ progress.LockAndIncrement();
+ });
}
-
- LayerManager.RebuildLayersProperties();
}
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- }
-
- #endregion
+ RebuildLayersProperties();
}
-}
+
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs
index fe6c7b2..de64fa3 100644
--- a/UVtools.Core/FileFormats/MDLPFile.cs
+++ b/UVtools.Core/FileFormats/MDLPFile.cs
@@ -6,6 +6,8 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -15,500 +17,497 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class MDLPFile : FileFormat
{
- public class MDLPFile : FileFormat
- {
- #region Constants
+ #region Constants
- private const uint SlicerInfoAddress = 4 + 7 + 290 * 290 * 2 + 116 * 116 * 2 + 4;
- #endregion
+ private const uint SlicerInfoAddress = 4 + 7 + 290 * 290 * 2 + 116 * 116 * 2 + 4;
+ #endregion
- #region Sub Classes
+ #region Sub Classes
- #region Header
+ #region Header
- public sealed class Header
+ public sealed class Header
+ {
+ public const byte HEADER_SIZE = 7;
+ public const string HEADER_VALUE = "MKSDLP";
+
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint HeaderSize { get; set; } = HEADER_SIZE;
+
+ /// <summary>
+ /// Gets the file tag = MKSDLP
+ /// </summary>
+ [FieldOrder(1)]
+ [FieldLength(HEADER_SIZE)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string HeaderValue { get; set; } = HEADER_VALUE;
+
+ public void Validate()
{
- public const byte HEADER_SIZE = 7;
- public const string HEADER_VALUE = "MKSDLP";
-
- [FieldOrder(0)]
- [FieldEndianness(Endianness.Big)]
- public uint HeaderSize { get; set; } = HEADER_SIZE;
-
- /// <summary>
- /// Gets the file tag = MKSDLP
- /// </summary>
- [FieldOrder(1)]
- [FieldLength(HEADER_SIZE)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string HeaderValue { get; set; } = HEADER_VALUE;
-
- public void Validate()
+ if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE)
{
- if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE)
- {
- throw new FileLoadException("Not a valid Makerbase mdlp file!");
- }
+ throw new FileLoadException("Not a valid Makerbase mdlp file!");
}
}
+ }
- public sealed class SlicerInfo
- {
- // 290 * 290 * 2 + 116 * 116 * 2 + 4
- //[FieldOrder(0)] [FieldLength(195116)] public byte[] PreviewData { get; set; }
-
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort LayerCount { get; set; }
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; }
- [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; }
- [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint DisplayWidthDataSize { get; set; } = 6;
- [FieldOrder(4)] [FieldLength(nameof(DisplayWidthDataSize))] public byte[] DisplayWidthBytes { get; set; }
- [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DisplayHeightDataSize { get; set; } = 6;
- [FieldOrder(6)] [FieldLength(nameof(DisplayHeightDataSize))] public byte[] DisplayHeightBytes { get; set; }
-
- [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint LayerHeightDataSize { get; set; } = 6;
- [FieldOrder(8)] [FieldLength(nameof(LayerHeightDataSize))] public byte[] LayerHeightBytes { get; set; }
- [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public ushort ExposureTime { get; set; }
- [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LightOffDelay { get; set; }
- [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort BottomExposureTime { get; set; }
- [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort BottomLayers { get; set; }
- /* [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftHeight { get; set; }
- [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeed { get; set; }
- [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public ushort LiftHeight { get; set; }
- [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeed { get; set; }
- [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeed { get; set; }
- [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public ushort BottomLightPWM { get; set; }
- [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public ushort LightPWM { get; set; }*/
- }
- #endregion
-
- #region LayerDef
+ public sealed class SlicerInfo
+ {
+ // 290 * 290 * 2 + 116 * 116 * 2 + 4
+ //[FieldOrder(0)] [FieldLength(195116)] public byte[] PreviewData { get; set; }
+
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort LayerCount { get; set; }
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; }
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; }
+ [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint DisplayWidthDataSize { get; set; } = 6;
+ [FieldOrder(4)] [FieldLength(nameof(DisplayWidthDataSize))] public byte[] DisplayWidthBytes { get; set; } = null!;
+ [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DisplayHeightDataSize { get; set; } = 6;
+ [FieldOrder(6)] [FieldLength(nameof(DisplayHeightDataSize))] public byte[] DisplayHeightBytes { get; set; } = null!;
+
+ [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint LayerHeightDataSize { get; set; } = 6;
+ [FieldOrder(8)] [FieldLength(nameof(LayerHeightDataSize))] public byte[] LayerHeightBytes { get; set; } = null!;
+ [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public ushort ExposureTime { get; set; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LightOffDelay { get; set; }
+ [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort BottomExposureTime { get; set; }
+ [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort BottomLayers { get; set; }
+ /* [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftHeight { get; set; }
+ [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeed { get; set; }
+ [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public ushort LiftHeight { get; set; }
+ [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeed { get; set; }
+ [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeed { get; set; }
+ [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public ushort BottomLightPWM { get; set; }
+ [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public ushort LightPWM { get; set; }*/
+ }
+ #endregion
- public sealed class LayerDef
- {
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; }
- [FieldOrder(1)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; }
- [FieldOrder(2)] public PageBreak PageBreak { get; set; } = new();
+ #region LayerDef
+ public sealed class LayerDef
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; }
+ [FieldOrder(1)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; }
+ [FieldOrder(2)] public PageBreak PageBreak { get; set; } = new();
- public LayerDef(uint lineCount, LayerLine[] lines)
- {
- LineCount = lineCount;
- Lines = lines;
- }
- }
- public sealed class LayerLine
+ public LayerDef(uint lineCount, LayerLine[] lines)
{
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; }
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort EndY { get; set; }
- [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartX { get; set; }
+ LineCount = lineCount;
+ Lines = lines;
+ }
+ }
- public static byte[] GetBytes(ushort StartY, ushort EndY, ushort StartX)
- {
- var bytes = new byte[6];
- BitExtensions.ToBytesBigEndian(StartY, bytes);
- BitExtensions.ToBytesBigEndian(EndY, bytes, 2);
- BitExtensions.ToBytesBigEndian(StartX, bytes, 4);
- return bytes;
- }
+ public sealed class LayerLine
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; }
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort EndY { get; set; }
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartX { get; set; }
+ public static byte[] GetBytes(ushort StartY, ushort EndY, ushort StartX)
+ {
+ var bytes = new byte[6];
+ BitExtensions.ToBytesBigEndian(StartY, bytes);
+ BitExtensions.ToBytesBigEndian(EndY, bytes, 2);
+ BitExtensions.ToBytesBigEndian(StartX, bytes, 4);
+ return bytes;
+ }
- public LayerLine()
- { }
- public LayerLine(ushort startY, ushort endY, ushort startX)
- {
- StartY = startY;
- EndY = endY;
- StartX = startX;
- }
- }
+ public LayerLine()
+ { }
- public sealed class PageBreak
+ public LayerLine(ushort startY, ushort endY, ushort startX)
{
- public static byte[] Bytes => new byte[] {0x0D, 0x0A};
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public byte Line { get; set; } = 0x0D;
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public byte Break { get; set; } = 0x0A;
+ StartY = startY;
+ EndY = endY;
+ StartX = startX;
}
+ }
+
+ public sealed class PageBreak
+ {
+ public static byte[] Bytes => new byte[] {0x0D, 0x0A};
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public byte Line { get; set; } = 0x0D;
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public byte Break { get; set; } = 0x0A;
+ }
- #endregion
+ #endregion
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public Header HeaderSettings { get; protected internal set; } = new();
- public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
- public override FileFormatType FileType => FileFormatType.Binary;
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new (typeof(MDLPFile), "mdlp", "Makerbase MDLP v1"),
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new (typeof(MDLPFile), "mdlp", "Makerbase MDLP v1"),
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
-
- //PrintParameterModifier.BottomLightOffDelay,
- /*PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
-
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,*/
- };
-
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new (116, 116),
- new (290, 290)
- };
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ //PrintParameterModifier.BottomLightOffDelay,
+ /*PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,*/
+ };
+
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new (116, 116),
+ new (290, 290)
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => SlicerInfoSettings.ResolutionX;
+ set
{
- get => SlicerInfoSettings.ResolutionX;
- set
- {
- SlicerInfoSettings.ResolutionX = (ushort) value;
- RaisePropertyChanged();
- }
+ SlicerInfoSettings.ResolutionX = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => SlicerInfoSettings.ResolutionY;
+ set
{
- get => SlicerInfoSettings.ResolutionY;
- set
- {
- SlicerInfoSettings.ResolutionY = (ushort) value;
- RaisePropertyChanged();
- }
+ SlicerInfoSettings.ResolutionY = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayWidthBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayWidthBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayWidthBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.DisplayHeightBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.DisplayHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
+
+ SlicerInfoSettings.DisplayHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override Enumerations.FlipDirection DisplayMirror { get; set; }
- public override float LayerHeight
+ public override Enumerations.FlipDirection DisplayMirror { get; set; }
+ public override float LayerHeight
+ {
+ get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
+ set
{
- get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray()));
- 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++)
{
- 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 + 1] = System.Convert.ToByte(str[i]);
- }
-
- SlicerInfoSettings.LayerHeightBytes = data;
- RaisePropertyChanged();
+ data[i * 2 + 1] = System.Convert.ToByte(str[i]);
}
- }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = SlicerInfoSettings.LayerCount = (ushort)base.LayerCount;
+ SlicerInfoSettings.LayerHeightBytes = data;
+ RaisePropertyChanged();
}
+ }
- public override ushort BottomLayerCount
- {
- get => SlicerInfoSettings.BottomLayers;
- set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = SlicerInfoSettings.LayerCount = (ushort)base.LayerCount;
+ }
- public override float BottomLightOffDelay => LightOffDelay;
+ public override ushort BottomLayerCount
+ {
+ get => SlicerInfoSettings.BottomLayers;
+ set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value;
+ }
- public override float LightOffDelay
- {
- get => SlicerInfoSettings.LightOffDelay;
- set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value;
- }
+ public override float BottomLightOffDelay => LightOffDelay;
- public override float BottomWaitTimeBeforeCure
- {
- get => base.BottomWaitTimeBeforeCure;
- set
- {
- SetBottomLightOffDelay(value);
- base.BottomWaitTimeBeforeCure = value;
- }
- }
+ public override float LightOffDelay
+ {
+ get => SlicerInfoSettings.LightOffDelay;
+ set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value;
+ }
- public override float WaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
{
- get => base.WaitTimeBeforeCure;
- set
- {
- SetNormalLightOffDelay(value);
- base.WaitTimeBeforeCure = value;
- }
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float BottomExposureTime
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
{
- get => SlicerInfoSettings.BottomExposureTime;
- set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value;
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
}
+ }
- public override float ExposureTime
- {
- get => SlicerInfoSettings.ExposureTime;
- set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value;
- }
+ public override float BottomExposureTime
+ {
+ get => SlicerInfoSettings.BottomExposureTime;
+ set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value;
+ }
- public override object[] Configs => new[] { (object)HeaderSettings, SlicerInfoSettings };
+ public override float ExposureTime
+ {
+ get => SlicerInfoSettings.ExposureTime;
+ set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value;
+ }
- #endregion
+ public override object[] Configs => new[] { (object)HeaderSettings, SlicerInfoSettings };
- #region Constructors
+ #endregion
- #endregion
+ #region Constructors
- #region Methods
- protected override void EncodeInternally(OperationProgress progress)
- {
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- var pageBreak = PageBreak.Bytes;
+ #endregion
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ #region Methods
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ var pageBreak = PageBreak.Bytes;
- var previews = new byte[ThumbnailsOriginalSize.Length][];
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- // Previews
- Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
- if (Thumbnails[previewIndex] is null)
- {
- previews[previewIndex] = new byte[encodeLength];
- return;
- }
+ var previews = new byte[ThumbnailsOriginalSize!.Length][];
- previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]);
+ // Previews
+ Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2;
+ if (Thumbnails[previewIndex] is null)
+ {
+ previews[previewIndex] = new byte[encodeLength];
+ return;
+ }
- if (encodeLength != previews[previewIndex].Length)
- {
- throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
- }
- });
+ previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]!);
- for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ if (encodeLength != previews[previewIndex].Length)
{
- Helpers.SerializeWriteFileStream(outputFile, previews[i]);
- outputFile.WriteBytes(pageBreak);
- previews[i] = null;
+ throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}");
}
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ });
+
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ {
+ Helpers.SerializeWriteFileStream(outputFile, previews[i]);
+ outputFile.WriteBytes(pageBreak);
+ previews[i] = null!;
+ }
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- var range = Enumerable.Range(0, (int)LayerCount);
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var range = Enumerable.Range(0, (int)LayerCount);
- var layerBytes = new List<byte>[LayerCount];
- foreach (var batch in BatchLayersIndexes())
- {
- progress.Token.ThrowIfCancellationRequested();
+ var layerBytes = new List<byte>[LayerCount];
+ foreach (var batch in BatchLayersIndexes())
+ {
+ progress.Token.ThrowIfCancellationRequested();
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = this[layerIndex];
+ using (var mat = layer.LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- var layer = this[layerIndex];
- using (var mat = layer.LayerMat)
- {
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteSpan();
- layerBytes[layerIndex] = new();
+ layerBytes[layerIndex] = new();
- uint lineCount = 0;
+ uint lineCount = 0;
- for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
+ for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
+ {
+ int y = layer.BoundingRectangle.Y;
+ int startY = -1;
+ for (; y < layer.BoundingRectangle.Bottom; y++)
{
- int y = layer.BoundingRectangle.Y;
- int startY = -1;
- for (; y < layer.BoundingRectangle.Bottom; y++)
+ int pos = mat.GetPixelPos(x, y);
+ if (span[pos] < 128) // Black pixel
{
- int pos = mat.GetPixelPos(x, y);
- if (span[pos] < 128) // Black pixel
- {
- if (startY == -1) continue; // Keep ignoring
- layerBytes[layerIndex]
- .AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
- startY = -1;
- lineCount++;
- }
- else
- {
- if (startY >= 0) continue; // Keep sum
- startY = y;
- }
+ if (startY == -1) continue; // Keep ignoring
+ layerBytes[layerIndex]
+ .AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
+ startY = -1;
+ lineCount++;
}
-
- if (startY >= 0)
+ else
{
- layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
- lineCount++;
+ if (startY >= 0) continue; // Keep sum
+ startY = y;
}
}
- layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount));
- layerBytes[layerIndex].AddRange(pageBreak);
+ if (startY >= 0)
+ {
+ layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
+ lineCount++;
+ }
}
- progress.LockAndIncrement();
- });
+ layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount));
+ layerBytes[layerIndex].AddRange(pageBreak);
+ }
- progress.Token.ThrowIfCancellationRequested();
+ progress.LockAndIncrement();
+ });
- foreach (var layerIndex in batch)
- {
- outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
- layerBytes[layerIndex] = null;
- }
+ progress.Token.ThrowIfCancellationRequested();
+
+ foreach (var layerIndex in batch)
+ {
+ outputFile.WriteBytes(layerBytes[layerIndex].ToArray());
+ layerBytes[layerIndex] = null!;
}
+ }
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine("-End-");
- }
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
- protected override void DecodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
+ //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ HeaderSettings.Validate();
+
+ byte[][] previews = new byte[ThumbnailsOriginalSize!.Length][];
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
- //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- HeaderSettings.Validate();
-
- byte[][] previews = new byte[ThumbnailsOriginalSize.Length][];
- for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
- {
- previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2];
- inputFile.ReadBytes(previews[i]);
- inputFile.Seek(2, SeekOrigin.Current);
- }
+ previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2];
+ inputFile.ReadBytes(previews[i]);
+ inputFile.Seek(2, SeekOrigin.Current);
+ }
- Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
- {
- Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
- previews[previewIndex] = null;
- });
+ Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex =>
+ {
+ Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]);
+ previews[previewIndex] = null!;
+ });
- SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
+ SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
- LayerManager.Init(SlicerInfoSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ Init(SlicerInfoSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- if (DecodeType == FileDecodeType.Full)
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ var linesBytes = new byte[LayerCount][];
+ foreach (var batch in BatchLayersIndexes())
{
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- var linesBytes = new byte[LayerCount][];
- foreach (var batch in BatchLayersIndexes())
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- foreach (var layerIndex in batch)
- {
- var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
+ foreach (var layerIndex in batch)
+ {
+ var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4));
- linesBytes[layerIndex] = new byte[lineCount * 6];
- inputFile.ReadBytes(linesBytes[layerIndex]);
- inputFile.Seek(2, SeekOrigin.Current);
+ linesBytes[layerIndex] = new byte[lineCount * 6];
+ inputFile.ReadBytes(linesBytes[layerIndex]);
+ inputFile.Seek(2, SeekOrigin.Current);
- progress.Token.ThrowIfCancellationRequested();
- }
+ progress.Token.ThrowIfCancellationRequested();
+ }
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = EmguExtensions.InitMat(Resolution))
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = EmguExtensions.InitMat(Resolution))
- {
- for (int i = 0; i < linesBytes[layerIndex].Length; i++)
- {
- var startY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
- var endY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
- var startX = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i]);
+ for (int i = 0; i < linesBytes[layerIndex].Length; i++)
+ {
+ var startY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
+ var endY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]);
+ var startX = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i]);
- CvInvoke.Line(mat, new Point(startX, startY), new Point(startX, endY), EmguExtensions.WhiteColor);
- }
+ CvInvoke.Line(mat, new Point(startX, startY), new Point(startX, endY), EmguExtensions.WhiteColor);
+ }
- linesBytes[layerIndex] = null;
+ linesBytes[layerIndex] = null!;
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- }
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
- progress.LockAndIncrement();
- });
- }
- }
- else // Partial read
- {
- inputFile.Seek(-Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.End);
+ progress.LockAndIncrement();
+ });
}
-
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- HeaderSettings.Validate();
}
-
- protected override void PartialSaveInternally(OperationProgress progress)
+ else // Partial read
{
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ inputFile.Seek(-Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.End);
}
- #endregion
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ HeaderSettings.Validate();
}
-}
+
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs
index ae44a71..4b364bf 100644
--- a/UVtools.Core/FileFormats/OSLAFile.cs
+++ b/UVtools.Core/FileFormats/OSLAFile.cs
@@ -8,746 +8,733 @@
// https://github.com/sn4k3/UVtools/blob/master/Documentation/osla.md
+using BinarySerialization;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
-using BinarySerialization;
using UVtools.Core.Extensions;
using UVtools.Core.GCode;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class OSLAFile : FileFormat
{
- public class OSLAFile : FileFormat
- {
- #region Constants
+ #region Constants
- public const string MARKER = "OSLATiCo";
+ public const string MARKER = "OSLATiCo";
- #endregion
+ #endregion
- #region Sub Classes
- #region Header
- public class FileDef
- {
- [FieldOrder(0)]
- [FieldLength(8)]
- public string Marker { get; set; } = MARKER;
-
- [FieldOrder(1)]
- public ushort Version { get; set; } = 0;
-
- [FieldOrder(2)]
- [FieldLength(20)]
- public string CreatedDateTime { get; set; } = DateTime.UtcNow.ToString("u");
+ #region Sub Classes
+ #region Header
+ public class FileDef
+ {
+ [FieldOrder(0)]
+ [FieldLength(8)]
+ public string Marker { get; set; } = MARKER;
- [FieldOrder(3)]
- [FieldLength(50)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string CreatedBy { get; set; } = About.SoftwareWithVersion;
+ [FieldOrder(1)]
+ public ushort Version { get; set; } = 0;
- [FieldOrder(4)]
- [FieldLength(20)]
- public string ModifiedDateTime { get; set; } = DateTime.UtcNow.ToString("u");
+ [FieldOrder(2)]
+ [FieldLength(20)]
+ public string CreatedDateTime { get; set; } = DateTime.UtcNow.ToString("u");
- [FieldOrder(5)]
- [FieldLength(50)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string ModifiedBy { get; set; } = About.SoftwareWithVersion;
+ [FieldOrder(3)]
+ [FieldLength(50)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string CreatedBy { get; set; } = About.SoftwareWithVersion;
- public override string ToString()
- {
- return $"{nameof(Marker)}: {Marker}, {nameof(Version)}: {Version}, {nameof(CreatedDateTime)}: {CreatedDateTime}, {nameof(CreatedBy)}: {CreatedBy}, {nameof(ModifiedDateTime)}: {ModifiedDateTime}, {nameof(ModifiedBy)}: {ModifiedBy}";
- }
+ [FieldOrder(4)]
+ [FieldLength(20)]
+ public string ModifiedDateTime { get; set; } = DateTime.UtcNow.ToString("u");
- public void Update()
- {
- ModifiedDateTime= DateTime.UtcNow.ToString("u");
- ModifiedBy = About.SoftwareWithVersion;;
- }
+ [FieldOrder(5)]
+ [FieldLength(50)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string ModifiedBy { get; set; } = About.SoftwareWithVersion;
- public void Validate()
- {
- if (Marker != MARKER)
- {
- throw new FileLoadException($"Invalid marker: {Marker}, not a valid OSLA file.");
- }
- }
- }
-
-
- public class Header
+ public override string ToString()
{
- [FieldOrder(0)] public uint TableSize { get; set; }
- [FieldOrder(1)] public uint ResolutionX { get; set; }
- [FieldOrder(2)] public uint ResolutionY { get; set; }
- [FieldOrder(3)] public float MachineZ { get; set; }
- [FieldOrder(4)] public float DisplayWidth { get; set; }
- [FieldOrder(5)] public float DisplayHeight { get; set; }
- [FieldOrder(6)] public byte DisplayMirror { get; set; } // 0 = No mirror | 1 = Horizontally | 2 = Vertically | 3 = Horizontally+Vertically | >3 = No mirror
- [FieldOrder(7)] [FieldLength(16)] [SerializeAs(SerializedType.TerminatedString)] public string PreviewDataType { get; set; } = "RGB565";
- [FieldOrder(8)] [FieldLength(16)] [SerializeAs(SerializedType.TerminatedString)] public string LayerDataType { get; set; } = "PNG";
- [FieldOrder(9)] public uint PreviewTableSize { get; set; } = 8;
- [FieldOrder(10)] public uint PreviewCount { get; set; }
- [FieldOrder(11)] public float LayerHeight { get; set; } = 0.05f;
- [FieldOrder(12)] public ushort BottomLayersCount { get; set; } = 4;
- [FieldOrder(13)] public uint LayerCount { get; set; }
- [FieldOrder(14)] public uint LayerTableSize { get; set; } = 69;
- [FieldOrder(15)] public uint LayerDefinitionsAddress { get; set; }
- [FieldOrder(16)] public uint GCodeAddress { get; set; }
- [FieldOrder(17)] public uint PrintTime { get; set; }
- [FieldOrder(18)] public float MaterialMilliliters { get; set; }
- [FieldOrder(19)] public float MaterialCost { get; set; }
- [FieldOrder(20)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string MaterialName { get; set; }
- [FieldOrder(21)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; } = "Unknown";
-
-
- public override string ToString()
- {
- return $"{nameof(TableSize)}: {TableSize}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(MachineZ)}: {MachineZ}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(DisplayMirror)}: {DisplayMirror}, {nameof(PreviewDataType)}: {PreviewDataType}, {nameof(LayerDataType)}: {LayerDataType}, {nameof(PreviewTableSize)}: {PreviewTableSize}, {nameof(PreviewCount)}: {PreviewCount}, {nameof(LayerTableSize)}: {LayerTableSize}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LayerCount)}: {LayerCount}, {nameof(LayerDefinitionsAddress)}: {LayerDefinitionsAddress}, {nameof(GCodeAddress)}: {GCodeAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(MaterialMilliliters)}: {MaterialMilliliters}, {nameof(MaterialCost)}: {MaterialCost}, {nameof(MaterialName)}: {MaterialName}, {nameof(MachineName)}: {MachineName}";
- }
+ return $"{nameof(Marker)}: {Marker}, {nameof(Version)}: {Version}, {nameof(CreatedDateTime)}: {CreatedDateTime}, {nameof(CreatedBy)}: {CreatedBy}, {nameof(ModifiedDateTime)}: {ModifiedDateTime}, {nameof(ModifiedBy)}: {ModifiedBy}";
}
- #endregion
-
- #region Custom Table
- public class CustomTable
+ public void Update()
{
- [FieldOrder(0)] public uint TableSize { get; set; }
-
- [FieldOrder(1)] [FieldCount(nameof(TableSize))] public byte[] Bytes { get; set; } = Array.Empty<byte>();
-
- public override string ToString()
- {
- return $"{nameof(TableSize)}: {TableSize}, {nameof(Bytes)}: {Bytes.Length}";
- }
+ ModifiedDateTime= DateTime.UtcNow.ToString("u");
+ ModifiedBy = About.SoftwareWithVersion;;
}
- #endregion
-
- #region Preview
- public class Preview
+ public void Validate()
{
- /// <summary>
- /// Gets the X dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(0)] public ushort ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the Y dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(1)] public ushort ResolutionY { get; set; }
-
- /// <summary>
- /// Gets the image length in bytes.
- /// </summary>
- [FieldOrder(2)] public uint ImageLength { get; set; }
- //[FieldOrder(3)] [FieldCount(nameof(ImageLength))] public byte[] ImageData { get; set; }
-
-
- public override string ToString()
+ if (Marker != MARKER)
{
- return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageLength)}: {ImageLength}";
+ throw new FileLoadException($"Invalid marker: {Marker}, not a valid OSLA file.");
}
}
+ }
- #endregion
- #region Layer
- public class LayerDef
+ public class Header
+ {
+ [FieldOrder(0)] public uint TableSize { get; set; }
+ [FieldOrder(1)] public uint ResolutionX { get; set; }
+ [FieldOrder(2)] public uint ResolutionY { get; set; }
+ [FieldOrder(3)] public float MachineZ { get; set; }
+ [FieldOrder(4)] public float DisplayWidth { get; set; }
+ [FieldOrder(5)] public float DisplayHeight { get; set; }
+ [FieldOrder(6)] public byte DisplayMirror { get; set; } // 0 = No mirror | 1 = Horizontally | 2 = Vertically | 3 = Horizontally+Vertically | >3 = No mirror
+ [FieldOrder(7)] [FieldLength(16)] [SerializeAs(SerializedType.TerminatedString)] public string PreviewDataType { get; set; } = "RGB565";
+ [FieldOrder(8)] [FieldLength(16)] [SerializeAs(SerializedType.TerminatedString)] public string LayerDataType { get; set; } = "PNG";
+ [FieldOrder(9)] public uint PreviewTableSize { get; set; } = 8;
+ [FieldOrder(10)] public uint PreviewCount { get; set; }
+ [FieldOrder(11)] public float LayerHeight { get; set; } = 0.05f;
+ [FieldOrder(12)] public ushort BottomLayersCount { get; set; } = 4;
+ [FieldOrder(13)] public uint LayerCount { get; set; }
+ [FieldOrder(14)] public uint LayerTableSize { get; set; } = 69;
+ [FieldOrder(15)] public uint LayerDefinitionsAddress { get; set; }
+ [FieldOrder(16)] public uint GCodeAddress { get; set; }
+ [FieldOrder(17)] public uint PrintTime { get; set; }
+ [FieldOrder(18)] public float MaterialMilliliters { get; set; }
+ [FieldOrder(19)] public float MaterialCost { get; set; }
+ [FieldOrder(20)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string? MaterialName { get; set; } = string.Empty;
+ [FieldOrder(21)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; } = "Unknown";
+
+
+ public override string ToString()
{
- //[FieldOrder(0)] public uint DataAddress { get; set; }
-
- [FieldOrder(1)] public float PositionZ { get; set; }
- [FieldOrder(2)] public float LiftHeight { get; set; }
- [FieldOrder(3)] public float LiftSpeed { get; set; }
- [FieldOrder(4)] public float LiftHeight2 { get; set; }
- [FieldOrder(5)] public float LiftSpeed2 { get; set; }
- [FieldOrder(6)] public float WaitTimeAfterLift { get; set; }
- [FieldOrder(7)] public float RetractSpeed { get; set; }
- [FieldOrder(8)] public float RetractHeight2 { get; set; }
- [FieldOrder(9)] public float RetractSpeed2 { get; set; }
- [FieldOrder(10)] public float WaitTimeBeforeCure { get; set; }
- [FieldOrder(11)] public float ExposureTime { get; set; }
- [FieldOrder(12)] public float WaitTimeAfterCure { get; set; }
- [FieldOrder(13)] public byte LightPWM { get; set; }
- [FieldOrder(14)] public uint BoundingRectangleX { get; set; }
- [FieldOrder(15)] public uint BoundingRectangleY { get; set; }
- [FieldOrder(16)] public uint BoundingRectangleWidth { get; set; }
- [FieldOrder(17)] public uint BoundingRectangleHeight { get; set; }
-
- //[Ignore] public byte[] ImageData { get; set; }
-
- public LayerDef()
- {
- }
+ return $"{nameof(TableSize)}: {TableSize}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(MachineZ)}: {MachineZ}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(DisplayMirror)}: {DisplayMirror}, {nameof(PreviewDataType)}: {PreviewDataType}, {nameof(LayerDataType)}: {LayerDataType}, {nameof(PreviewTableSize)}: {PreviewTableSize}, {nameof(PreviewCount)}: {PreviewCount}, {nameof(LayerTableSize)}: {LayerTableSize}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LayerCount)}: {LayerCount}, {nameof(LayerDefinitionsAddress)}: {LayerDefinitionsAddress}, {nameof(GCodeAddress)}: {GCodeAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(MaterialMilliliters)}: {MaterialMilliliters}, {nameof(MaterialCost)}: {MaterialCost}, {nameof(MaterialName)}: {MaterialName}, {nameof(MachineName)}: {MachineName}";
+ }
+ }
+ #endregion
- public LayerDef(Layer layer)
- {
- PositionZ = layer.PositionZ;
- LiftHeight = layer.LiftHeight;
- LiftSpeed = layer.LiftSpeed;
- LiftHeight2 = layer.LiftHeight2;
- LiftSpeed2 = layer.LiftSpeed2;
- WaitTimeAfterLift = layer.WaitTimeAfterLift;
- RetractSpeed = layer.RetractSpeed;
- RetractHeight2 = layer.RetractHeight2;
- RetractSpeed2 = layer.RetractSpeed2;
- WaitTimeBeforeCure = layer.WaitTimeBeforeCure;
- ExposureTime = layer.ExposureTime;
- WaitTimeAfterCure = layer.WaitTimeAfterCure;
- LightPWM = layer.LightPWM;
- BoundingRectangleX = (uint)layer.BoundingRectangle.X;
- BoundingRectangleY = (uint)layer.BoundingRectangle.Y;
- BoundingRectangleWidth = (uint)layer.BoundingRectangle.Width;
- BoundingRectangleHeight = (uint)layer.BoundingRectangle.Height;
- }
+ #region Custom Table
- public void CopyTo(Layer layer)
- {
- layer.PositionZ = PositionZ;
- layer.LiftHeight = LiftHeight;
- layer.LiftSpeed = LiftSpeed;
- layer.LiftHeight2 = LiftHeight2;
- layer.LiftSpeed2 = LiftSpeed2;
- layer.WaitTimeAfterLift = WaitTimeAfterLift;
- layer.RetractSpeed = RetractSpeed;
- layer.RetractHeight2 = RetractHeight2;
- layer.RetractSpeed2 = RetractSpeed2;
- layer.WaitTimeBeforeCure = WaitTimeBeforeCure;
- layer.ExposureTime = ExposureTime;
- layer.WaitTimeAfterCure = WaitTimeAfterCure;
- layer.LightPWM = LightPWM;
- }
- }
- #endregion
+ public class CustomTable
+ {
+ [FieldOrder(0)] public uint TableSize { get; set; }
- #region GCode
+ [FieldOrder(1)] [FieldCount(nameof(TableSize))] public byte[] Bytes { get; set; } = Array.Empty<byte>();
- public class GCodeDef
+ public override string ToString()
{
- [FieldOrder(0)]
- public uint GCodeSize { get; set; }
-
- [FieldOrder(1)] [FieldLength(nameof(GCodeSize))]
- public string GCodeText { get; set; }
+ return $"{nameof(TableSize)}: {TableSize}, {nameof(Bytes)}: {Bytes.Length}";
}
- #endregion
-
- #endregion
-
- #region Properties
-
- public FileDef FileSettings { get; protected internal set; } = new();
- public Header HeaderSettings { get; protected internal set; } = new();
- public CustomTable CustomTableSettings { get; protected internal set; } = new();
- public Preview[] Previews { get; protected internal set; }
-
- public override FileFormatType FileType => FileFormatType.Binary;
-
- public override FileExtension[] FileExtensions { get; } = {
- new (typeof(OSLAFile), "osla", "Open SLA universal binary file"),
- //new ("omsla", "Open mSLA universal binary file"),
- //new ("odlp", "Open DLP universal binary file"),
- };
-
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
-
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
-
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
-
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
-
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
-
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
+ }
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
+ #endregion
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
+ #region Preview
+ public class Preview
+ {
+ /// <summary>
+ /// Gets the X dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(0)] public ushort ResolutionX { get; set; }
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
+ /// <summary>
+ /// Gets the Y dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(1)] public ushort ResolutionY { get; set; }
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ /// <summary>
+ /// Gets the image length in bytes.
+ /// </summary>
+ [FieldOrder(2)] public uint ImageLength { get; set; }
+ //[FieldOrder(3)] [FieldCount(nameof(ImageLength))] public byte[] ImageData { get; set; }
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.LightPWM,
- };
- public override Size[] ThumbnailsOriginalSize { get; } =
+ public override string ToString()
{
- new(400, 400),
- new(200, 200)
- };
+ return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageLength)}: {ImageLength}";
+ }
+ }
+
+ #endregion
- public override uint ResolutionX
+ #region Layer
+ public class LayerDef
+ {
+ //[FieldOrder(0)] public uint DataAddress { get; set; }
+
+ [FieldOrder(1)] public float PositionZ { get; set; }
+ [FieldOrder(2)] public float LiftHeight { get; set; }
+ [FieldOrder(3)] public float LiftSpeed { get; set; }
+ [FieldOrder(4)] public float LiftHeight2 { get; set; }
+ [FieldOrder(5)] public float LiftSpeed2 { get; set; }
+ [FieldOrder(6)] public float WaitTimeAfterLift { get; set; }
+ [FieldOrder(7)] public float RetractSpeed { get; set; }
+ [FieldOrder(8)] public float RetractHeight2 { get; set; }
+ [FieldOrder(9)] public float RetractSpeed2 { get; set; }
+ [FieldOrder(10)] public float WaitTimeBeforeCure { get; set; }
+ [FieldOrder(11)] public float ExposureTime { get; set; }
+ [FieldOrder(12)] public float WaitTimeAfterCure { get; set; }
+ [FieldOrder(13)] public byte LightPWM { get; set; }
+ [FieldOrder(14)] public uint BoundingRectangleX { get; set; }
+ [FieldOrder(15)] public uint BoundingRectangleY { get; set; }
+ [FieldOrder(16)] public uint BoundingRectangleWidth { get; set; }
+ [FieldOrder(17)] public uint BoundingRectangleHeight { get; set; }
+
+ //[Ignore] public byte[] ImageData { get; set; }
+
+ public LayerDef()
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = value;
- RaisePropertyChanged();
- }
}
- public override uint ResolutionY
+ public LayerDef(Layer layer)
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ PositionZ = layer.PositionZ;
+ LiftHeight = layer.LiftHeight;
+ LiftSpeed = layer.LiftSpeed;
+ LiftHeight2 = layer.LiftHeight2;
+ LiftSpeed2 = layer.LiftSpeed2;
+ WaitTimeAfterLift = layer.WaitTimeAfterLift;
+ RetractSpeed = layer.RetractSpeed;
+ RetractHeight2 = layer.RetractHeight2;
+ RetractSpeed2 = layer.RetractSpeed2;
+ WaitTimeBeforeCure = layer.WaitTimeBeforeCure;
+ ExposureTime = layer.ExposureTime;
+ WaitTimeAfterCure = layer.WaitTimeAfterCure;
+ LightPWM = layer.LightPWM;
+ BoundingRectangleX = (uint)layer.BoundingRectangle.X;
+ BoundingRectangleY = (uint)layer.BoundingRectangle.Y;
+ BoundingRectangleWidth = (uint)layer.BoundingRectangle.Width;
+ BoundingRectangleHeight = (uint)layer.BoundingRectangle.Height;
}
- public override float DisplayWidth
+ public void CopyTo(Layer layer)
{
- get => HeaderSettings.DisplayWidth;
- set
- {
- HeaderSettings.DisplayWidth = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ layer.PositionZ = PositionZ;
+ layer.LiftHeight = LiftHeight;
+ layer.LiftSpeed = LiftSpeed;
+ layer.LiftHeight2 = LiftHeight2;
+ layer.LiftSpeed2 = LiftSpeed2;
+ layer.WaitTimeAfterLift = WaitTimeAfterLift;
+ layer.RetractSpeed = RetractSpeed;
+ layer.RetractHeight2 = RetractHeight2;
+ layer.RetractSpeed2 = RetractSpeed2;
+ layer.WaitTimeBeforeCure = WaitTimeBeforeCure;
+ layer.ExposureTime = ExposureTime;
+ layer.WaitTimeAfterCure = WaitTimeAfterCure;
+ layer.LightPWM = LightPWM;
}
+ }
+ #endregion
+ #region GCode
- public override float DisplayHeight
- {
- get => HeaderSettings.DisplayHeight;
- set
- {
- HeaderSettings.DisplayHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
- }
+ public class GCodeDef
+ {
+ [FieldOrder(0)]
+ public uint GCodeSize { get; set; }
- public override float MachineZ
- {
- get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ;
- set
- {
- HeaderSettings.MachineZ = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
- }
+ [FieldOrder(1)] [FieldLength(nameof(GCodeSize))]
+ public string? GCodeText { get; set; }
+ }
+ #endregion
+
+ #endregion
+
+ #region Properties
+
+ public FileDef FileSettings { get; protected internal set; } = new();
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public CustomTable CustomTableSettings { get; protected internal set; } = new();
+ public override FileFormatType FileType => FileFormatType.Binary;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new (typeof(OSLAFile), "osla", "Open SLA universal binary file"),
+ //new ("omsla", "Open mSLA universal binary file"),
+ //new ("odlp", "Open DLP universal binary file"),
+ };
+
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
+
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
+
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
+
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(400, 400),
+ new(200, 200)
+ };
- public override Enumerations.FlipDirection DisplayMirror
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => (Enumerations.FlipDirection)HeaderSettings.DisplayMirror;
- set
- {
- HeaderSettings.DisplayMirror = (byte)value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override float LayerHeight
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.LayerHeight;
- set
- {
- HeaderSettings.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
+ public override float DisplayWidth
+ {
+ get => HeaderSettings.DisplayWidth;
+ set
{
- get => base.LayerCount;
- set => HeaderSettings.LayerCount = base.LayerCount;
+ HeaderSettings.DisplayWidth = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override ushort BottomLayerCount
+
+ public override float DisplayHeight
+ {
+ get => HeaderSettings.DisplayHeight;
+ set
{
- get => HeaderSettings.BottomLayersCount;
- set => base.BottomLayerCount = HeaderSettings.BottomLayersCount = value;
+ HeaderSettings.DisplayHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float PrintTime
+ public override float MachineZ
+ {
+ get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- HeaderSettings.PrintTime = (uint)base.PrintTime;
- }
+ HeaderSettings.MachineZ = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float MaterialMilliliters
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => (Enumerations.FlipDirection)HeaderSettings.DisplayMirror;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- HeaderSettings.MaterialMilliliters = base.MaterialMilliliters;
- }
+ HeaderSettings.DisplayMirror = (byte)value;
+ RaisePropertyChanged();
}
+ }
- public override float MaterialCost
+ public override float LayerHeight
+ {
+ get => HeaderSettings.LayerHeight;
+ set
{
- get => (float) Math.Round(HeaderSettings.MaterialCost, 3);
- set => base.MaterialCost = HeaderSettings.MaterialCost = (float)Math.Round(value, 3);
+ HeaderSettings.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
+
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => HeaderSettings.LayerCount = base.LayerCount;
+ }
+
+ public override ushort BottomLayerCount
+ {
+ get => HeaderSettings.BottomLayersCount;
+ set => base.BottomLayerCount = HeaderSettings.BottomLayersCount = value;
+ }
- public override string MaterialName
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => HeaderSettings.MaterialName;
- set => base.MaterialName = HeaderSettings.MaterialName = value;
+ base.PrintTime = value;
+ HeaderSettings.PrintTime = (uint)base.PrintTime;
}
+ }
- public override string MachineName
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => HeaderSettings.MachineName;
- set => base.MachineName = HeaderSettings.MachineName = value;
+ base.MaterialMilliliters = value;
+ HeaderSettings.MaterialMilliliters = base.MaterialMilliliters;
}
+ }
- public override object[] Configs => new object[] { FileSettings, HeaderSettings };
+ public override float MaterialCost
+ {
+ get => (float) Math.Round(HeaderSettings.MaterialCost, 3);
+ set => base.MaterialCost = HeaderSettings.MaterialCost = (float)Math.Round(value, 3);
+ }
- #endregion
+ public override string? MaterialName
+ {
+ get => HeaderSettings.MaterialName;
+ set => base.MaterialName = HeaderSettings.MaterialName = value;
+ }
- #region Constructors
- public OSLAFile()
- {
- //Previews = new Preview[ThumbnailsCount];
- GCode = new GCodeBuilder
- {
- UseTailComma = true,
- UseComments = true,
- GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute,
- GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
- GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
- GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started,
- LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
- EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
- CommandShowImageM6054 = {Arguments = "{0}"},
- };
- }
- #endregion
+ public override string MachineName
+ {
+ get => HeaderSettings.MachineName;
+ set => base.MachineName = HeaderSettings.MachineName = value;
+ }
- #region Methods
- public override void Clear()
- {
- base.Clear();
+ public override object[] Configs => new object[] { FileSettings, HeaderSettings };
- Previews = null;
- }
+ #endregion
- protected override void EncodeInternally(OperationProgress progress)
+ #region Constructors
+ public OSLAFile()
+ {
+ GCode = new GCodeBuilder
{
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- FileSettings.Update();
- var fileDefSize = Helpers.SerializeWriteFileStream(outputFile, FileSettings);
- HeaderSettings.TableSize = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
-
- outputFile.Seek((int)HeaderSettings.TableSize, SeekOrigin.Current);
+ UseTailComma = true,
+ UseComments = true,
+ GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute,
+ GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
+ GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
+ GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started,
+ LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
+ EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
+ CommandShowImageM6054 = {Arguments = "{0}"},
+ };
+ }
+ #endregion
- Helpers.SerializeWriteFileStream(outputFile, CustomTableSettings); // Custom table
+ #region Methods
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ FileSettings.Update();
+ var fileDefSize = Helpers.SerializeWriteFileStream(outputFile, FileSettings);
+ HeaderSettings.TableSize = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
- // Previews
- progress.Reset(OperationProgress.StatusEncodePreviews, ThumbnailsCount);
- HeaderSettings.PreviewCount = 0;
- uint sizeofPreview = 0;
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- var image = Thumbnails[i];
- if(image is null) continue;
+ outputFile.Seek((int)HeaderSettings.TableSize, SeekOrigin.Current);
- progress.Token.ThrowIfCancellationRequested();
+ Helpers.SerializeWriteFileStream(outputFile, CustomTableSettings); // Custom table
- var bytes = EncodeImage(HeaderSettings.PreviewDataType, image);
- if (bytes.Length == 0) continue;
- var preview = new Preview
- {
- ResolutionX = (ushort) image.Width,
- ResolutionY = (ushort) image.Height,
- ImageLength = (uint) bytes.Length,
- };
+ // Previews
+ progress.Reset(OperationProgress.StatusEncodePreviews, ThumbnailsCount);
+ HeaderSettings.PreviewCount = 0;
+ uint sizeofPreview = 0;
+ for (byte i = 0; i < ThumbnailsCount; i++)
+ {
+ var image = Thumbnails[i];
+ if(image is null) continue;
- if (sizeofPreview == 0)
- {
- sizeofPreview = (uint) Helpers.Serializer.SizeOf(preview);
- }
+ progress.Token.ThrowIfCancellationRequested();
- HeaderSettings.PreviewCount++;
+ var bytes = EncodeImage(HeaderSettings.PreviewDataType, image);
+ if (bytes.Length == 0) continue;
+ var preview = new Preview
+ {
+ ResolutionX = (ushort) image.Width,
+ ResolutionY = (ushort) image.Height,
+ ImageLength = (uint) bytes.Length,
+ };
- Helpers.SerializeWriteFileStream(outputFile, preview);
- // Need to fill what we don't know
- if (HeaderSettings.PreviewTableSize > sizeofPreview)
- {
- outputFile.Seek(HeaderSettings.LayerTableSize - sizeofPreview, SeekOrigin.Current);
- }
- outputFile.WriteBytes(bytes);
-
- progress++;
+ if (sizeofPreview == 0)
+ {
+ sizeofPreview = (uint) Helpers.Serializer.SizeOf(preview);
}
- uint[] layerDataAddresses = new uint[LayerCount];
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- HeaderSettings.LayerDefinitionsAddress = (uint) outputFile.Position;
-
- outputFile.Seek(HeaderSettings.LayerTableSize * LayerCount, SeekOrigin.Current); // Start of layer data
+ HeaderSettings.PreviewCount++;
- var layersHash = new Dictionary<string, uint>();
-
- foreach (var batch in BatchLayersIndexes())
+ Helpers.SerializeWriteFileStream(outputFile, preview);
+ // Need to fill what we don't know
+ if (HeaderSettings.PreviewTableSize > sizeofPreview)
{
- var layerBytes = new byte[LayerCount][];
+ outputFile.Seek(HeaderSettings.LayerTableSize - sizeofPreview, SeekOrigin.Current);
+ }
+ outputFile.WriteBytes(bytes);
+
+ progress++;
+ }
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = this[layerIndex].LayerMat)
- {
- layerBytes[layerIndex] = EncodeImage(HeaderSettings.LayerDataType, mat);
- }
+ uint[] layerDataAddresses = new uint[LayerCount];
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ HeaderSettings.LayerDefinitionsAddress = (uint) outputFile.Position;
+
+ outputFile.Seek(HeaderSettings.LayerTableSize * LayerCount, SeekOrigin.Current); // Start of layer data
- progress.LockAndIncrement();
- });
+ var layersHash = new Dictionary<string, uint>();
+
+ foreach (var batch in BatchLayersIndexes())
+ {
+ var layerBytes = new byte[LayerCount][];
- foreach (var layerIndex in batch)
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = this[layerIndex].LayerMat)
{
- progress.Token.ThrowIfCancellationRequested();
-
- // Try to reuse layers
- var hash = CryptExtensions.ComputeSHA1Hash(layerBytes[layerIndex]);
- if (layersHash.TryGetValue(hash, out var address))
- {
- layerDataAddresses[layerIndex] = address;
- }
- else
- {
- layerDataAddresses[layerIndex] = (uint)outputFile.Position;
- outputFile.WriteUIntLittleEndian((uint)layerBytes[layerIndex].Length);
- outputFile.WriteBytes(layerBytes[layerIndex]);
- layersHash.Add(hash, layerDataAddresses[layerIndex]);
- }
-
- layerBytes[layerIndex] = null; // Free this
+ layerBytes[layerIndex] = EncodeImage(HeaderSettings.LayerDataType, mat);
}
- }
- HeaderSettings.GCodeAddress = (uint)outputFile.Position;
+ progress.LockAndIncrement();
+ });
- uint layerTableSize = 0;
- outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin);
- for (int layerIndex = 0; layerIndex < layerDataAddresses.Length; layerIndex++)
+ foreach (var layerIndex in batch)
{
progress.Token.ThrowIfCancellationRequested();
- var layer = this[layerIndex];
- var layerdef = new LayerDef(layer);
-
- outputFile.WriteUIntLittleEndian(layerDataAddresses[layerIndex]);
- Helpers.SerializeWriteFileStream(outputFile, layerdef);
- if (layerTableSize == 0)
+ // Try to reuse layers
+ var hash = CryptExtensions.ComputeSHA1Hash(layerBytes[layerIndex]);
+ if (layersHash.TryGetValue(hash, out var address))
{
- layerTableSize = 4 + (uint)Helpers.Serializer.SizeOf(layerdef);
+ layerDataAddresses[layerIndex] = address;
}
- if (HeaderSettings.LayerTableSize > layerTableSize)
+ else
{
- outputFile.Seek(HeaderSettings.LayerTableSize - layerTableSize, SeekOrigin.Current);
+ layerDataAddresses[layerIndex] = (uint)outputFile.Position;
+ outputFile.WriteUIntLittleEndian((uint)layerBytes[layerIndex].Length);
+ outputFile.WriteBytes(layerBytes[layerIndex]);
+ layersHash.Add(hash, layerDataAddresses[layerIndex]);
}
- }
-
- progress.Reset(OperationProgress.StatusEncodeGcode);
- outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin);
- RebuildGCode();
- var gcodeSettings = new GCodeDef { GCodeText = GCodeStr };
- gcodeSettings.GCodeSize = (uint)gcodeSettings.GCodeText.Length;
- Helpers.SerializeWriteFileStream(outputFile, gcodeSettings);
- outputFile.Seek(fileDefSize, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
-
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(FileSettings);
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine("-End-");
+ layerBytes[layerIndex] = null!; // Free this
+ }
}
- protected override void DecodeInternally(OperationProgress progress)
+ HeaderSettings.GCodeAddress = (uint)outputFile.Position;
+
+ uint layerTableSize = 0;
+ outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin);
+ for (int layerIndex = 0; layerIndex < layerDataAddresses.Length; layerIndex++)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- FileSettings = Helpers.Deserialize<FileDef>(inputFile);
- Debug.Write("File -> ");
- Debug.WriteLine(FileSettings);
- FileSettings.Validate();
-
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- Debug.Write("Header -> ");
- Debug.WriteLine(HeaderSettings);
-
- var headerSize = Helpers.Serializer.SizeOf(HeaderSettings);
- if (HeaderSettings.TableSize > headerSize) // By pass what we dont know
+ progress.Token.ThrowIfCancellationRequested();
+
+ var layer = this[layerIndex];
+ var layerdef = new LayerDef(layer);
+
+ outputFile.WriteUIntLittleEndian(layerDataAddresses[layerIndex]);
+ Helpers.SerializeWriteFileStream(outputFile, layerdef);
+ if (layerTableSize == 0)
+ {
+ layerTableSize = 4 + (uint)Helpers.Serializer.SizeOf(layerdef);
+ }
+ if (HeaderSettings.LayerTableSize > layerTableSize)
{
- inputFile.Seek(HeaderSettings.TableSize - headerSize, SeekOrigin.Current);
+ outputFile.Seek(HeaderSettings.LayerTableSize - layerTableSize, SeekOrigin.Current);
}
+ }
- CustomTableSettings = Helpers.Deserialize<CustomTable>(inputFile);
- Debug.Write("Custom table -> ");
- Debug.WriteLine(CustomTableSettings);
+ progress.Reset(OperationProgress.StatusEncodeGcode);
+ outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin);
+ RebuildGCode();
+ var gcodeSettings = new GCodeDef { GCodeText = GCodeStr };
+ gcodeSettings.GCodeSize = (uint)(gcodeSettings.GCodeText?.Length ?? 0);
+ Helpers.SerializeWriteFileStream(outputFile, gcodeSettings);
- progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
+ outputFile.Seek(fileDefSize, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- Previews = new Preview[HeaderSettings.PreviewCount];
- for (byte i = 0; i < HeaderSettings.PreviewCount; i++)
- {
- progress.Token.ThrowIfCancellationRequested();
- Previews[i] = Helpers.Deserialize<Preview>(inputFile);
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(FileSettings);
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
- Debug.Write($"Preview {i} -> ");
- Debug.WriteLine(Previews[i]);
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ FileSettings = Helpers.Deserialize<FileDef>(inputFile);
+ Debug.Write("File -> ");
+ Debug.WriteLine(FileSettings);
+ FileSettings.Validate();
+
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ Debug.Write("Header -> ");
+ Debug.WriteLine(HeaderSettings);
+
+ var headerSize = Helpers.Serializer.SizeOf(HeaderSettings);
+ if (HeaderSettings.TableSize > headerSize) // By pass what we dont know
+ {
+ inputFile.Seek(HeaderSettings.TableSize - headerSize, SeekOrigin.Current);
+ }
- // Need to fill what we don't know
- if (HeaderSettings.PreviewTableSize > 8)
- {
- inputFile.Seek(HeaderSettings.LayerTableSize - 8, SeekOrigin.Current);
- }
+ CustomTableSettings = Helpers.Deserialize<CustomTable>(inputFile);
+ Debug.Write("Custom table -> ");
+ Debug.WriteLine(CustomTableSettings);
+
+ progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
+ for (byte i = 0; i < HeaderSettings.PreviewCount; i++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ var preview = Helpers.Deserialize<Preview>(inputFile);
- var bytes = inputFile.ReadBytes((int)Previews[i].ImageLength);
+ Debug.Write($"Preview {i} -> ");
+ Debug.WriteLine(preview);
- Thumbnails[i] = DecodeImage(HeaderSettings.PreviewDataType, bytes, Previews[i].ResolutionX, Previews[i].ResolutionY);
- progress++;
+ // Need to fill what we don't know
+ if (HeaderSettings.PreviewTableSize > 8)
+ {
+ inputFile.Seek(HeaderSettings.LayerTableSize - 8, SeekOrigin.Current);
}
- inputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin);
+ var bytes = inputFile.ReadBytes((int)preview.ImageLength);
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- var layerDef = new LayerDef[LayerCount];
+ Thumbnails[i] = DecodeImage(HeaderSettings.PreviewDataType, bytes, preview.ResolutionX, preview.ResolutionY);
+ progress++;
+ }
+ inputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin);
- progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount);
- uint[] layerDataAddresses = new uint[LayerCount];
- uint layerTableSize = 0;
- for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- layerDataAddresses[layerIndex] = inputFile.ReadUIntLittleEndian();
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ var layerDef = new LayerDef[LayerCount];
- layerDef[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
- if (layerTableSize == 0)
- {
- layerTableSize = 4 + (uint)Helpers.Serializer.SizeOf(layerDef[layerIndex]);
- }
- if (HeaderSettings.LayerTableSize > layerTableSize)
- {
- inputFile.Seek(HeaderSettings.LayerTableSize - layerTableSize, SeekOrigin.Current);
- }
- progress++;
+ progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount);
+ uint[] layerDataAddresses = new uint[LayerCount];
+ uint layerTableSize = 0;
+ for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ layerDataAddresses[layerIndex] = inputFile.ReadUIntLittleEndian();
+
+ layerDef[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
+ if (layerTableSize == 0)
+ {
+ layerTableSize = 4 + (uint)Helpers.Serializer.SizeOf(layerDef[layerIndex]);
}
+ if (HeaderSettings.LayerTableSize > layerTableSize)
+ {
+ inputFile.Seek(HeaderSettings.LayerTableSize - layerTableSize, SeekOrigin.Current);
+ }
+
+ progress++;
+ }
- if (DecodeType == FileDecodeType.Full)
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount);
+ foreach (var batch in BatchLayersIndexes())
{
- progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount);
- foreach (var batch in BatchLayersIndexes())
- {
- var layerBytes = new byte[LayerCount][];
+ var layerBytes = new byte[LayerCount][];
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
+ foreach (var layerIndex in batch)
+ {
+ progress.Token.ThrowIfCancellationRequested();
- inputFile.Seek(layerDataAddresses[layerIndex], SeekOrigin.Begin);
- layerBytes[layerIndex] = inputFile.ReadBytes(inputFile.ReadUIntLittleEndian());
- }
+ inputFile.Seek(layerDataAddresses[layerIndex], SeekOrigin.Begin);
+ layerBytes[layerIndex] = inputFile.ReadBytes(inputFile.ReadUIntLittleEndian());
+ }
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = DecodeImage(HeaderSettings.LayerDataType, layerBytes[layerIndex], Resolution);
- layerBytes[layerIndex] = null; // Clean
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = DecodeImage(HeaderSettings.LayerDataType, layerBytes[layerIndex], Resolution);
+ layerBytes[layerIndex] = null!; // Clean
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- progress.LockAndIncrement();
- });
- }
+ progress.LockAndIncrement();
+ });
}
+ }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- layerDef[layerIndex].CopyTo(this[layerIndex]);
- }
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ layerDef[layerIndex].CopyTo(this[layerIndex]);
+ }
- progress.Reset(OperationProgress.StatusDecodeGcode);
- inputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin);
- var gcodeDef = Helpers.Deserialize<GCodeDef>(inputFile);
- GCodeStr = gcodeDef.GCodeText;
- //GCode.ParseLayersFromGCode(this);
+ progress.Reset(OperationProgress.StatusDecodeGcode);
+ inputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin);
+ var gcodeDef = Helpers.Deserialize<GCodeDef>(inputFile);
+ GCodeStr = gcodeDef.GCodeText;
+ //GCode.ParseLayersFromGCode(this);
- UpdateGlobalPropertiesFromLayers();
- }
+ UpdateGlobalPropertiesFromLayers();
+ }
- protected override void PartialSaveInternally(OperationProgress progress)
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ FileSettings.Update();
+ Helpers.SerializeWriteFileStream(outputFile, FileSettings);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin);
+ foreach (var layer in this)
{
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(0, SeekOrigin.Begin);
- FileSettings.Update();
- Helpers.SerializeWriteFileStream(outputFile, FileSettings);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
-
- outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin);
- foreach (var layer in this)
- {
- outputFile.Seek(4, SeekOrigin.Current); // skip address
- Helpers.SerializeWriteFileStream(outputFile, new LayerDef(layer)); // Update layer values
- }
+ outputFile.Seek(4, SeekOrigin.Current); // skip address
+ Helpers.SerializeWriteFileStream(outputFile, new LayerDef(layer)); // Update layer values
+ }
- if (HeaderSettings.GCodeAddress > 0)
- {
- outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin);
- outputFile.SetLength(HeaderSettings.GCodeAddress);
+ if (HeaderSettings.GCodeAddress > 0)
+ {
+ outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin);
+ outputFile.SetLength(HeaderSettings.GCodeAddress);
- RebuildGCode();
- var gcodeSettings = new GCodeDef { GCodeText = GCodeStr };
- gcodeSettings.GCodeSize = (uint)gcodeSettings.GCodeText.Length;
- Helpers.SerializeWriteFileStream(outputFile, gcodeSettings);
- }
+ RebuildGCode();
+ var gcodeSettings = new GCodeDef { GCodeText = GCodeStr };
+ gcodeSettings.GCodeSize = (uint)(gcodeSettings.GCodeText?.Length ?? 0);
+ Helpers.SerializeWriteFileStream(outputFile, gcodeSettings);
}
- #endregion
+ }
+ #endregion
- #region Static Methods
+ #region Static Methods
- #endregion
- }
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs
index 9ad433f..2e220af 100644
--- a/UVtools.Core/FileFormats/PHZFile.cs
+++ b/UVtools.Core/FileFormats/PHZFile.cs
@@ -8,6 +8,9 @@
// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -16,500 +19,513 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class PHZFile : FileFormat
{
- public class PHZFile : FileFormat
+ #region Constants
+ private const uint MAGIC_PHZ = 0x9FDA83AE;
+ private const ushort REPEATRGB15MASK = 0x20;
+
+ private const ushort RLE16EncodingLimit = 0x1000;
+ #endregion
+
+ #region Sub Classes
+ #region Header
+ public class Header
{
- #region Constants
- private const uint MAGIC_PHZ = 0x9FDA83AE;
- private const ushort REPEATRGB15MASK = 0x20;
+ private string _machineName = DefaultMachineName;
+
+ /// <summary>
+ /// Gets a magic number identifying the file type.
+ /// 0x12fd_0019 for cbddlp
+ /// 0x12fd_0086 for ctb
+ /// 0x9FDA83AE for phz
+ /// </summary>
+ [FieldOrder(0)] public uint Magic { get; set; } = MAGIC_PHZ;
+
+ /// <summary>
+ /// Gets the software version
+ /// </summary>
+ [FieldOrder(1)] public uint Version { get; set; } = 2;
+
+ /// <summary>
+ /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(2)] public float LayerHeightMilimeter { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(3)] public float LayerExposureSeconds { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table.
+ /// </summary>
+ [FieldOrder(4)] public float BottomExposureSeconds { get; set; }
+
+ /// <summary>
+ /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig..
+ /// </summary>
+ [FieldOrder(5)] public uint BottomLayersCount { get; set; } = 10;
+
+ /// <summary>
+ /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(6)] public uint ResolutionX { get; set; }
+
+ /// <summary>
+ /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
+ /// </summary>
+ [FieldOrder(7)] public uint ResolutionY { get; set; }
+
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the larger preview images.
+ /// </summary>
+ [FieldOrder(8)] public uint PreviewLargeOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer.
+ /// </summary>
+ [FieldOrder(9)] public uint LayersDefinitionOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the number of records in the layer table for the first level set. In ctb files, that’s equivalent to the total number of records, but records may be multiplied in antialiased cbddlp files.
+ /// </summary>
+ [FieldOrder(10)] public uint LayerCount { get; set; }
+
+ /// <summary>
+ /// Gets the file offsets of ImageHeader records describing the smaller preview images.
+ /// </summary>
+ [FieldOrder(11)] public uint PreviewSmallOffsetAddress { get; set; }
+
+ /// <summary>
+ /// Gets the estimated duration of print, in seconds.
+ /// </summary>
+ [FieldOrder(12)] public uint PrintTime { get; set; }
+
+ /// <summary>
+ /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose.
+ /// </summary>
+ [FieldOrder(13)] public uint ProjectorType { get; set; }
+
+ /// <summary>
+ /// Gets the number of times each layer image is repeated in the file.
+ /// This is used to implement antialiasing in cbddlp files. When greater than 1,
+ /// the layer table will actually contain layer_table_count * level_set_count entries.
+ /// See the section on antialiasing for details.
+ /// </summary>
+ [FieldOrder(14)] public uint AntiAliasLevel { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(15)] public ushort LightPWM { get; set; } = 255;
+
+ /// <summary>
+ /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively.
+ /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
+ /// </summary>
+ [FieldOrder(16)] public ushort BottomLightPWM { get; set; } = 255;
+
+ [FieldOrder(17)] public uint Padding1 { get; set; }
+ [FieldOrder(18)] public uint Padding2 { get; set; }
+
+ /// <summary>
+ /// Gets the height of the model described by this file, in millimeters.
+ /// </summary>
+ [FieldOrder(19)] public float OverallHeightMilimeter { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s X output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(20)] public float BedSizeX { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s Y output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(21)] public float BedSizeY { get; set; }
+
+ /// <summary>
+ /// Gets dimensions of the printer’s Z output volume, in millimeters.
+ /// </summary>
+ [FieldOrder(22)] public float BedSizeZ { get; set; }
+
+ /// <summary>
+ /// Gets the key used to encrypt layer data, or 0 if encryption is not used.
+ /// </summary>
+ [FieldOrder(23)] public uint EncryptionKey { get; set; }
+
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(24)] public float BottomLightOffDelay { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(25)] public float LightOffDelay { get; set; } = 1;
+
+ /// <summary>
+ /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig.
+ /// </summary>
+ [FieldOrder(26)] public uint BottomLayersCount2 { get; set; } = 10;
+
+ [FieldOrder(27)] public uint Padding3 { get; set; }
+
+ /// <summary>
+ /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters.
+ /// </summary>
+ [FieldOrder(28)] public float BottomLiftHeight { get; set; } = 5;
+
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(29)] public float BottomLiftSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters.
+ /// </summary>
+ [FieldOrder(30)] public float LiftHeight { get; set; } = 5;
+
+ /// <summary>
+ /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(31)] public float LiftSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute.
+ /// </summary>
+ [FieldOrder(32)] public float RetractSpeed { get; set; } = 300;
+
+ /// <summary>
+ /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model.
+ /// </summary>
+ [FieldOrder(33)] public float VolumeMl { get; set; }
- private const ushort RLE16EncodingLimit = 0x1000;
- #endregion
+ /// <summary>
+ /// Gets the estimated grams, derived from volume using configured factors for density.
+ /// </summary>
+ [FieldOrder(34)] public float WeightG { get; set; }
+
+ /// <summary>
+ /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost.
+ /// </summary>
+ [FieldOrder(35)] public float CostDollars { get; set; }
+
+ [FieldOrder(36)] public uint Padding4 { get; set; }
+
+ /// <summary>
+ /// Gets the machine name offset to a string naming the machine type, and its length in bytes.
+ /// </summary>
+ [FieldOrder(37)] public uint MachineNameAddress { get; set; }
+
+ /// <summary>
+ /// Gets the machine size in bytes
+ /// </summary>
+ [FieldOrder(38)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
- #region Sub Classes
- #region Header
- public class Header
+ /// <summary>
+ /// Gets the machine name. string is not nul-terminated.
+ /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only.
+ /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine.
+ /// </summary>
+ [Ignore]
+ public string MachineName
{
- private string _machineName = DefaultMachineName;
-
- /// <summary>
- /// Gets a magic number identifying the file type.
- /// 0x12fd_0019 for cbddlp
- /// 0x12fd_0086 for ctb
- /// 0x9FDA83AE for phz
- /// </summary>
- [FieldOrder(0)] public uint Magic { get; set; } = MAGIC_PHZ;
-
- /// <summary>
- /// Gets the software version
- /// </summary>
- [FieldOrder(1)] public uint Version { get; set; } = 2;
-
- /// <summary>
- /// Gets the layer height setting used at slicing, in millimeters. Actual height used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(2)] public float LayerHeightMilimeter { get; set; }
-
- /// <summary>
- /// Gets the exposure time setting used at slicing, in seconds, for normal (non-bottom) layers, respectively. Actual time used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(3)] public float LayerExposureSeconds { get; set; }
-
- /// <summary>
- /// Gets the exposure time setting used at slicing, in seconds, for bottom layers. Actual time used by the machine is in the layer table.
- /// </summary>
- [FieldOrder(4)] public float BottomExposureSeconds { get; set; }
-
- /// <summary>
- /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig..
- /// </summary>
- [FieldOrder(5)] public uint BottomLayersCount { get; set; } = 10;
-
- /// <summary>
- /// Gets the printer resolution along X axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(6)] public uint ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the printer resolution along Y axis, in pixels. This information is critical to correctly decoding layer images.
- /// </summary>
- [FieldOrder(7)] public uint ResolutionY { get; set; }
-
- /// <summary>
- /// Gets the file offsets of ImageHeader records describing the larger preview images.
- /// </summary>
- [FieldOrder(8)] public uint PreviewLargeOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the file offset of a table of LayerHeader records giving parameters for each printed layer.
- /// </summary>
- [FieldOrder(9)] public uint LayersDefinitionOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the number of records in the layer table for the first level set. In ctb files, that’s equivalent to the total number of records, but records may be multiplied in antialiased cbddlp files.
- /// </summary>
- [FieldOrder(10)] public uint LayerCount { get; set; }
-
- /// <summary>
- /// Gets the file offsets of ImageHeader records describing the smaller preview images.
- /// </summary>
- [FieldOrder(11)] public uint PreviewSmallOffsetAddress { get; set; }
-
- /// <summary>
- /// Gets the estimated duration of print, in seconds.
- /// </summary>
- [FieldOrder(12)] public uint PrintTime { get; set; }
-
- /// <summary>
- /// Gets the records whether this file was generated assuming normal (0) or mirrored (1) image projection. LCD printers are "mirrored" for this purpose.
- /// </summary>
- [FieldOrder(13)] public uint ProjectorType { get; set; }
-
- /// <summary>
- /// Gets the number of times each layer image is repeated in the file.
- /// This is used to implement antialiasing in cbddlp files. When greater than 1,
- /// the layer table will actually contain layer_table_count * level_set_count entries.
- /// See the section on antialiasing for details.
- /// </summary>
- [FieldOrder(14)] public uint AntiAliasLevel { get; set; } = 1;
-
- /// <summary>
- /// Gets the PWM duty cycle for the UV illumination source on normal levels, respectively.
- /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
- /// </summary>
- [FieldOrder(15)] public ushort LightPWM { get; set; } = 255;
-
- /// <summary>
- /// Gets the PWM duty cycle for the UV illumination source on bottom levels, respectively.
- /// This appears to be an 8-bit quantity where 0xFF is fully on and 0x00 is fully off.
- /// </summary>
- [FieldOrder(16)] public ushort BottomLightPWM { get; set; } = 255;
-
- [FieldOrder(17)] public uint Padding1 { get; set; }
- [FieldOrder(18)] public uint Padding2 { get; set; }
-
- /// <summary>
- /// Gets the height of the model described by this file, in millimeters.
- /// </summary>
- [FieldOrder(19)] public float OverallHeightMilimeter { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s X output volume, in millimeters.
- /// </summary>
- [FieldOrder(20)] public float BedSizeX { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s Y output volume, in millimeters.
- /// </summary>
- [FieldOrder(21)] public float BedSizeY { get; set; }
-
- /// <summary>
- /// Gets dimensions of the printer’s Z output volume, in millimeters.
- /// </summary>
- [FieldOrder(22)] public float BedSizeZ { get; set; }
-
- /// <summary>
- /// Gets the key used to encrypt layer data, or 0 if encryption is not used.
- /// </summary>
- [FieldOrder(23)] public uint EncryptionKey { get; set; }
-
- /// <summary>
- /// Gets the light off time setting used at slicing, for bottom layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(24)] public float BottomLightOffDelay { get; set; } = 1;
-
- /// <summary>
- /// Gets the light off time setting used at slicing, for normal layers, in seconds. Actual time used by the machine is in the layer table. Note that light_off_time_s appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(25)] public float LightOffDelay { get; set; } = 1;
-
- /// <summary>
- /// Gets number of layers configured as "bottom." Note that this field appears in both the file header and ExtConfig.
- /// </summary>
- [FieldOrder(26)] public uint BottomLayersCount2 { get; set; } = 10;
-
- [FieldOrder(27)] public uint Padding3 { get; set; }
-
- /// <summary>
- /// Gets the distance to lift the build platform away from the vat after bottom layers, in millimeters.
- /// </summary>
- [FieldOrder(28)] public float BottomLiftHeight { get; set; } = 5;
-
- /// <summary>
- /// Gets the speed at which to lift the build platform away from the vat after bottom layers, in millimeters per minute.
- /// </summary>
- [FieldOrder(29)] public float BottomLiftSpeed { get; set; } = 300;
-
- /// <summary>
- /// Gets the distance to lift the build platform away from the vat after normal layers, in millimeters.
- /// </summary>
- [FieldOrder(30)] public float LiftHeight { get; set; } = 5;
-
- /// <summary>
- /// Gets the speed at which to lift the build platform away from the vat after normal layers, in millimeters per minute.
- /// </summary>
- [FieldOrder(31)] public float LiftSpeed { get; set; } = 300;
-
- /// <summary>
- /// Gets the speed to use when the build platform re-approaches the vat after lift, in millimeters per minute.
- /// </summary>
- [FieldOrder(32)] public float RetractSpeed { get; set; } = 300;
-
- /// <summary>
- /// Gets the estimated required resin, measured in milliliters. The volume number is derived from the model.
- /// </summary>
- [FieldOrder(33)] public float VolumeMl { get; set; }
-
- /// <summary>
- /// Gets the estimated grams, derived from volume using configured factors for density.
- /// </summary>
- [FieldOrder(34)] public float WeightG { get; set; }
-
- /// <summary>
- /// Gets the estimated cost based on currency unit the user had configured. Derived from volume using configured factors for density and cost.
- /// </summary>
- [FieldOrder(35)] public float CostDollars { get; set; }
-
- [FieldOrder(36)] public uint Padding4 { get; set; }
-
- /// <summary>
- /// Gets the machine name offset to a string naming the machine type, and its length in bytes.
- /// </summary>
- [FieldOrder(37)] public uint MachineNameAddress { get; set; }
-
- /// <summary>
- /// Gets the machine size in bytes
- /// </summary>
- [FieldOrder(38)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length);
-
- /// <summary>
- /// Gets the machine name. string is not nul-terminated.
- /// The character encoding is currently unknown — all observed files in the wild use 7-bit ASCII characters only.
- /// Note that the machine type here is set in the software profile, and is not the name the user assigned to the machine.
- /// </summary>
- [Ignore]
- public string MachineName
+ get => _machineName;
+ set
{
- get => _machineName;
- set
- {
- if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
- _machineName = value;
- MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
- }
+ if (string.IsNullOrEmpty(value)) value = DefaultMachineName;
+ _machineName = value;
+ MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length;
}
+ }
- [FieldOrder(39)] public uint Padding5 { get; set; }
- [FieldOrder(40)] public uint Padding6 { get; set; }
- [FieldOrder(41)] public uint Padding7 { get; set; }
- [FieldOrder(42)] public uint Padding8 { get; set; }
- [FieldOrder(43)] public uint Padding9 { get; set; }
- [FieldOrder(44)] public uint Padding10 { get; set; }
+ [FieldOrder(39)] public uint Padding5 { get; set; }
+ [FieldOrder(40)] public uint Padding6 { get; set; }
+ [FieldOrder(41)] public uint Padding7 { get; set; }
+ [FieldOrder(42)] public uint Padding8 { get; set; }
+ [FieldOrder(43)] public uint Padding9 { get; set; }
+ [FieldOrder(44)] public uint Padding10 { get; set; }
- /// <summary>
- /// Gets the parameter used to control encryption.
- /// Not totally understood. 0 for cbddlp files, 0xF for ctb files, 0x1c (28) for phz
- /// </summary>
- [FieldOrder(45)] public uint EncryptionMode { get; set; } = 28;
+ /// <summary>
+ /// Gets the parameter used to control encryption.
+ /// Not totally understood. 0 for cbddlp files, 0xF for ctb files, 0x1c (28) for phz
+ /// </summary>
+ [FieldOrder(45)] public uint EncryptionMode { get; set; } = 28;
- /// <summary>
- /// Gets the minutes since Jan 1, 1970 UTC
- /// </summary>
- [FieldOrder(46)] public uint ModifiedTimestampMinutes { get; set; } = (uint)DateTimeExtensions.Timestamp.TotalMinutes;
+ /// <summary>
+ /// Gets the minutes since Jan 1, 1970 UTC
+ /// </summary>
+ [FieldOrder(46)] public uint ModifiedTimestampMinutes { get; set; } = (uint)DateTimeExtensions.Timestamp.TotalMinutes;
- [Ignore] public string ModifiedDate => DateTimeExtensions.GetDateTimeFromTimestampMinutes(ModifiedTimestampMinutes).ToString("dd/MM/yyyy HH:mm");
+ [Ignore] public string ModifiedDate => DateTimeExtensions.GetDateTimeFromTimestampMinutes(ModifiedTimestampMinutes).ToString("dd/MM/yyyy HH:mm");
- [FieldOrder(47)] public uint AntiAliasLevelInfo { get; set; }
+ [FieldOrder(47)] public uint AntiAliasLevelInfo { get; set; }
- [FieldOrder(48)] public uint SoftwareVersion { get; set; } = 0x01060300;
+ [FieldOrder(48)] public uint SoftwareVersion { get; set; } = 0x01060300;
- [FieldOrder(49)] public uint Padding11 { get; set; }
- [FieldOrder(50)] public uint Padding12 { get; set; }
- [FieldOrder(51)] public uint Padding13 { get; set; }
- [FieldOrder(52)] public uint Padding14 { get; set; }
- [FieldOrder(53)] public uint Padding15 { get; set; }
- [FieldOrder(54)] public uint Padding16{ get; set; }
+ [FieldOrder(49)] public uint Padding11 { get; set; }
+ [FieldOrder(50)] public uint Padding12 { get; set; }
+ [FieldOrder(51)] public uint Padding13 { get; set; }
+ [FieldOrder(52)] public uint Padding14 { get; set; }
+ [FieldOrder(53)] public uint Padding15 { get; set; }
+ [FieldOrder(54)] public uint Padding16{ get; set; }
- public override string ToString()
- {
- return $"{nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(LayerCount)}: {LayerCount}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(OverallHeightMilimeter)}: {OverallHeightMilimeter}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayersCount2)}: {BottomLayersCount2}, {nameof(Padding3)}: {Padding3}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(Padding4)}: {Padding4}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(MachineName)}: {MachineName}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(ModifiedTimestampMinutes)}: {ModifiedTimestampMinutes}, {nameof(ModifiedDate)}: {ModifiedDate}, {nameof(AntiAliasLevelInfo)}: {AntiAliasLevelInfo}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(Padding11)}: {Padding11}, {nameof(Padding12)}: {Padding12}, {nameof(Padding13)}: {Padding13}, {nameof(Padding14)}: {Padding14}, {nameof(Padding15)}: {Padding15}, {nameof(Padding16)}: {Padding16}";
- }
+ public override string ToString()
+ {
+ return $"{nameof(Magic)}: {Magic}, {nameof(Version)}: {Version}, {nameof(LayerHeightMilimeter)}: {LayerHeightMilimeter}, {nameof(LayerExposureSeconds)}: {LayerExposureSeconds}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PreviewLargeOffsetAddress)}: {PreviewLargeOffsetAddress}, {nameof(LayersDefinitionOffsetAddress)}: {LayersDefinitionOffsetAddress}, {nameof(LayerCount)}: {LayerCount}, {nameof(PreviewSmallOffsetAddress)}: {PreviewSmallOffsetAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(ProjectorType)}: {ProjectorType}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(LightPWM)}: {LightPWM}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(OverallHeightMilimeter)}: {OverallHeightMilimeter}, {nameof(BedSizeX)}: {BedSizeX}, {nameof(BedSizeY)}: {BedSizeY}, {nameof(BedSizeZ)}: {BedSizeZ}, {nameof(EncryptionKey)}: {EncryptionKey}, {nameof(BottomLightOffDelay)}: {BottomLightOffDelay}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomLayersCount2)}: {BottomLayersCount2}, {nameof(Padding3)}: {Padding3}, {nameof(BottomLiftHeight)}: {BottomLiftHeight}, {nameof(BottomLiftSpeed)}: {BottomLiftSpeed}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(WeightG)}: {WeightG}, {nameof(CostDollars)}: {CostDollars}, {nameof(Padding4)}: {Padding4}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(MachineName)}: {MachineName}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Padding7)}: {Padding7}, {nameof(Padding8)}: {Padding8}, {nameof(Padding9)}: {Padding9}, {nameof(Padding10)}: {Padding10}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(ModifiedTimestampMinutes)}: {ModifiedTimestampMinutes}, {nameof(ModifiedDate)}: {ModifiedDate}, {nameof(AntiAliasLevelInfo)}: {AntiAliasLevelInfo}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(Padding11)}: {Padding11}, {nameof(Padding12)}: {Padding12}, {nameof(Padding13)}: {Padding13}, {nameof(Padding14)}: {Padding14}, {nameof(Padding15)}: {Padding15}, {nameof(Padding16)}: {Padding16}";
}
- #endregion
+ }
+ #endregion
+
+ #region Preview
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class Preview
+ {
+ /// <summary>
+ /// Gets the X dimension of the preview image, in pixels.
+ /// </summary>
+ [FieldOrder(0)] public uint ResolutionX { get; set; }
- #region Preview
/// <summary>
- /// 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.
+ /// Gets the Y dimension of the preview image, in pixels.
/// </summary>
- public class Preview
+ [FieldOrder(1)] public uint ResolutionY { get; set; }
+
+ /// <summary>
+ /// Gets the image offset of the encoded data blob.
+ /// </summary>
+ [FieldOrder(2)] public uint ImageOffset { get; set; }
+
+ /// <summary>
+ /// Gets the image length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint ImageLength { 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; }
+
+ public unsafe Mat Decode(byte[] rawImageData)
{
- /// <summary>
- /// Gets the X dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(0)] public uint ResolutionX { get; set; }
-
- /// <summary>
- /// Gets the Y dimension of the preview image, in pixels.
- /// </summary>
- [FieldOrder(1)] public uint ResolutionY { get; set; }
-
- /// <summary>
- /// Gets the image offset of the encoded data blob.
- /// </summary>
- [FieldOrder(2)] public uint ImageOffset { get; set; }
-
- /// <summary>
- /// Gets the image length in bytes.
- /// </summary>
- [FieldOrder(3)] public uint ImageLength { 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; }
-
- public unsafe Mat Decode(byte[] rawImageData)
- {
- var image = new Mat(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
- var span = image.GetBytePointer();
+ var image = new Mat(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
+ var span = image.GetBytePointer();
- int pixel = 0;
- for (uint n = 0; n < ImageLength; n++)
+ int pixel = 0;
+ for (uint n = 0; n < ImageLength; n++)
+ {
+ uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
+ //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3);
+ byte red = (byte)(((dot >> 11) & 0x1F) << 3);
+ byte green = (byte)(((dot >> 6) & 0x1F) << 3);
+ byte blue = (byte)((dot & 0x1F) << 3);
+ int repeat = 1;
+ if ((dot & 0x0020) == 0x0020)
{
- uint dot = (uint)(rawImageData[n] & 0xFF | ((rawImageData[++n] & 0xFF) << 8));
- //uint color = ((dot & 0xF800) << 8) | ((dot & 0x07C0) << 5) | ((dot & 0x001F) << 3);
- byte red = (byte)(((dot >> 11) & 0x1F) << 3);
- byte green = (byte)(((dot >> 6) & 0x1F) << 3);
- byte blue = (byte)((dot & 0x1F) << 3);
- int repeat = 1;
- if ((dot & 0x0020) == 0x0020)
- {
- repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
- }
+ repeat += rawImageData[++n] & 0xFF | ((rawImageData[++n] & 0x0F) << 8);
+ }
- for (int j = 0; j < repeat; j++)
- {
- span[pixel++] = blue;
- span[pixel++] = green;
- span[pixel++] = red;
- //span[pixel] = new Rgba32(red, green, blue, byte.MaxValue);
- }
+ for (int j = 0; j < repeat; j++)
+ {
+ span[pixel++] = blue;
+ span[pixel++] = green;
+ span[pixel++] = red;
+ //span[pixel] = new Rgba32(red, green, blue, byte.MaxValue);
}
-
- return image;
}
- public static unsafe byte[] Encode(Mat image)
- {
- List<byte> rawData = new();
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ return image;
+ }
+
+ public static unsafe byte[] Encode(Mat image)
+ {
+ List<byte> rawData = new();
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
- ushort color15 = 0;
- uint rep = 0;
+ ushort color15 = 0;
+ uint rep = 0;
- void RleRGB15()
+ void RleRGB15()
+ {
+ switch (rep)
{
- switch (rep)
- {
- case 0:
- return;
- case 1:
+ case 0:
+ return;
+ case 1:
+ rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
+ rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
+ break;
+ case 2:
+ for (int i = 0; i < 2; i++)
+ {
rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- break;
- case 2:
- for (int i = 0; i < 2; i++)
- {
- rawData.Add((byte)(color15 & ~REPEATRGB15MASK));
- rawData.Add((byte)((color15 & ~REPEATRGB15MASK) >> 8));
- }
-
- break;
- default:
- rawData.Add((byte)(color15 | REPEATRGB15MASK));
- rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
- rawData.Add((byte)((rep - 1) | 0x3000));
- rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
- break;
- }
+ }
+
+ break;
+ default:
+ rawData.Add((byte)(color15 | REPEATRGB15MASK));
+ rawData.Add((byte)((color15 | REPEATRGB15MASK) >> 8));
+ rawData.Add((byte)((rep - 1) | 0x3000));
+ rawData.Add((byte)(((rep - 1) | 0x3000) >> 8));
+ break;
}
+ }
- for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels)
- {
- var ncolor15 =
- (span[pixel] >> 3)
- | ((span[pixel+1] >> 2) << 5)
- | ((span[pixel+2] >> 3) << 11);
+ for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels)
+ {
+ var ncolor15 =
+ (span[pixel] >> 3)
+ | ((span[pixel+1] >> 2) << 5)
+ | ((span[pixel+2] >> 3) << 11);
- if (ncolor15 == color15)
- {
- rep++;
- if (rep == RLE16EncodingLimit)
- {
- RleRGB15();
- rep = 0;
- }
- }
- else
+ if (ncolor15 == color15)
+ {
+ rep++;
+ if (rep == RLE16EncodingLimit)
{
RleRGB15();
- color15 = (ushort) ncolor15;
- rep = 1;
+ rep = 0;
}
}
+ else
+ {
+ RleRGB15();
+ color15 = (ushort) ncolor15;
+ rep = 1;
+ }
+ }
- RleRGB15();
+ RleRGB15();
- return rawData.ToArray();
- }
+ return rawData.ToArray();
+ }
- public override string ToString()
- {
- return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
- }
+ public override string ToString()
+ {
+ return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
}
+ }
- #endregion
+ #endregion
- #region Layer
- public class LayerDef
+ #region Layer
+ public class LayerDef
+ {
+ /// <summary>
+ /// Gets the build platform Z position for this layer, measured in millimeters.
+ /// </summary>
+ [FieldOrder(0)] public float PositionZ { get; set; }
+
+ /// <summary>
+ /// Gets the exposure time for this layer, in seconds.
+ /// </summary>
+ [FieldOrder(1)] public float ExposureTime { get; set; }
+
+ /// <summary>
+ /// Gets how long to keep the light off after exposing this layer, in seconds.
+ /// </summary>
+ [FieldOrder(2)] public float LightOffDelay { get; set; }
+
+ /// <summary>
+ /// Gets the layer image offset to encoded layer data, and its length in bytes.
+ /// </summary>
+ [FieldOrder(3)] public uint DataAddress { get; set; }
+
+ /// <summary>
+ /// Gets the layer image length in bytes.
+ /// </summary>
+ [FieldOrder(4)] public uint DataSize { get; set; }
+ [FieldOrder(5)] public uint Unknown1 { get; set; }
+ [FieldOrder(6)] public uint Unknown2 { get; set; }
+ [FieldOrder(7)] public uint Unknown3 { get; set; }
+ [FieldOrder(8)] public uint Unknown4 { get; set; }
+
+ [Ignore] public byte[] EncodedRle { get; set; } = null!;
+
+ [Ignore] public PHZFile Parent { get; set; } = null!;
+
+ public LayerDef()
{
- /// <summary>
- /// Gets the build platform Z position for this layer, measured in millimeters.
- /// </summary>
- [FieldOrder(0)] public float PositionZ { get; set; }
-
- /// <summary>
- /// Gets the exposure time for this layer, in seconds.
- /// </summary>
- [FieldOrder(1)] public float ExposureTime { get; set; }
-
- /// <summary>
- /// Gets how long to keep the light off after exposing this layer, in seconds.
- /// </summary>
- [FieldOrder(2)] public float LightOffDelay { get; set; }
-
- /// <summary>
- /// Gets the layer image offset to encoded layer data, and its length in bytes.
- /// </summary>
- [FieldOrder(3)] public uint DataAddress { get; set; }
-
- /// <summary>
- /// Gets the layer image length in bytes.
- /// </summary>
- [FieldOrder(4)] public uint DataSize { get; set; }
- [FieldOrder(5)] public uint Unknown1 { get; set; }
- [FieldOrder(6)] public uint Unknown2 { get; set; }
- [FieldOrder(7)] public uint Unknown3 { get; set; }
- [FieldOrder(8)] public uint Unknown4 { get; set; }
-
- [Ignore] public byte[] EncodedRle { get; set; }
-
- [Ignore] public PHZFile Parent { get; set; }
-
- public LayerDef()
- {
- }
+ }
- public LayerDef(PHZFile parent, Layer layer)
- {
- Parent = parent;
- SetFrom(layer);
- }
+ public LayerDef(PHZFile parent, Layer layer)
+ {
+ Parent = parent;
+ SetFrom(layer);
+ }
- public void SetFrom(Layer layer)
- {
- PositionZ = layer.PositionZ;
- ExposureTime = layer.ExposureTime;
- LightOffDelay = layer.LightOffDelay;
- }
+ public void SetFrom(Layer layer)
+ {
+ PositionZ = layer.PositionZ;
+ ExposureTime = layer.ExposureTime;
+ LightOffDelay = layer.LightOffDelay;
+ }
+
+ public void CopyTo(Layer layer)
+ {
+ layer.PositionZ = PositionZ;
+ layer.ExposureTime = ExposureTime;
+ layer.LightOffDelay = LightOffDelay;
+ }
- public void CopyTo(Layer layer)
+ public unsafe Mat Decode(uint layerIndex, bool consumeData = true)
+ {
+ var image = EmguExtensions.InitMat(Parent.Resolution);
+ var span = image.GetBytePointer();
+
+ if (Parent.HeaderSettings.EncryptionKey > 0)
{
- layer.PositionZ = PositionZ;
- layer.ExposureTime = ExposureTime;
- layer.LightOffDelay = LightOffDelay;
+ LayerRleCryptBuffer(Parent.HeaderSettings.EncryptionKey, layerIndex, EncodedRle);
}
- public unsafe Mat Decode(uint layerIndex, bool consumeData = true)
- {
- var image = EmguExtensions.InitMat(Parent.Resolution);
- var span = image.GetBytePointer();
+ int limit = image.Width * image.Height;
+ int index = 0;
+ byte lastColor = 0;
- if (Parent.HeaderSettings.EncryptionKey > 0)
+ foreach (var code in EncodedRle)
+ {
+ if ((code & 0x80) == 0x80)
{
- LayerRleCryptBuffer(Parent.HeaderSettings.EncryptionKey, layerIndex, EncodedRle);
- }
+ //lastColor = (byte) (code << 1);
+ // // Convert from 7bpp to 8bpp (extending the last bit)
+ lastColor = (byte)(((code & 0x7f) << 1) | (code & 1));
+ if (lastColor >= 0xfc)
+ {
+ // Make 'white' actually white
+ lastColor = 0xff;
- int limit = image.Width * image.Height;
- int index = 0;
- byte lastColor = 0;
+ }
- foreach (var code in EncodedRle)
- {
- if ((code & 0x80) == 0x80)
+ if (index < limit)
{
- //lastColor = (byte) (code << 1);
- // // Convert from 7bpp to 8bpp (extending the last bit)
- lastColor = (byte)(((code & 0x7f) << 1) | (code & 1));
- if (lastColor >= 0xfc)
- {
- // Make 'white' actually white
- lastColor = 0xff;
-
- }
+ span[index] = lastColor;
+ }
+ else
+ {
+ image.Dispose();
+ throw new FileLoadException("Corrupted RLE data.");
+ }
+ index++;
+ }
+ else
+ {
+ for (uint i = 0; i < code; i++)
+ {
if (index < limit)
{
span[index] = lastColor;
@@ -519,695 +535,678 @@ namespace UVtools.Core.FileFormats
image.Dispose();
throw new FileLoadException("Corrupted RLE data.");
}
-
index++;
}
- else
- {
- for (uint i = 0; i < code; i++)
- {
- if (index < limit)
- {
- span[index] = lastColor;
- }
- else
- {
- image.Dispose();
- throw new FileLoadException("Corrupted RLE data.");
- }
- index++;
- }
- }
}
+ }
- if (consumeData)
- EncodedRle = null;
+ if (consumeData)
+ EncodedRle = null!;
- return image;
- }
+ return image;
+ }
- public void Encode(Mat image, uint layerIndex)
- {
- List<byte> rawData = new();
+ public void Encode(Mat image, uint layerIndex)
+ {
+ List<byte> rawData = new();
- //byte color = byte.MaxValue >> 1;
- byte color = byte.MaxValue;
- uint stride = 0;
+ //byte color = byte.MaxValue >> 1;
+ byte color = byte.MaxValue;
+ uint stride = 0;
- void AddRep()
+ void AddRep()
+ {
+ rawData.Add((byte)(color | 0x80));
+ stride--;
+ int done = 0;
+ while (done < stride)
{
- rawData.Add((byte)(color | 0x80));
- stride--;
- int done = 0;
- while (done < stride)
- {
- int todo = 0x7d;
+ int todo = 0x7d;
- if (stride - done < todo)
- {
- todo = (int)(stride - done);
- }
+ if (stride - done < todo)
+ {
+ todo = (int)(stride - done);
+ }
- rawData.Add((byte)(todo));
+ rawData.Add((byte)(todo));
- done += todo;
- }
+ done += todo;
}
+ }
- int halfWidth = image.Width / 2;
+ int halfWidth = image.Width / 2;
- //int pixel = 0;
- for (int y = 0; y < image.Height; y++)
+ //int pixel = 0;
+ for (int y = 0; y < image.Height; y++)
+ {
+ var span = image.GetRowSpan<byte>(y);
+ for (int x = 0; x < span.Length; x++)
{
- var span = image.GetRowSpan<byte>(y);
- for (int x = 0; x < span.Length; x++)
- {
- var grey7 = (byte)((span[x] >> 1) & 0x7f);
- if (grey7 > 0x7c)
- {
- grey7 = 0x7c;
- }
-
- if (color == byte.MaxValue)
- {
- color = grey7;
- stride = 1;
- }
- else if (grey7 != color || x == halfWidth)
- {
- AddRep();
- color = grey7;
- stride = 1;
- }
- else
- {
- stride++;
- }
+ var grey7 = (byte)((span[x] >> 1) & 0x7f);
+ if (grey7 > 0x7c)
+ {
+ grey7 = 0x7c;
}
- AddRep();
- color = byte.MaxValue;
+ if (color == byte.MaxValue)
+ {
+ color = grey7;
+ stride = 1;
+ }
+ else if (grey7 != color || x == halfWidth)
+ {
+ AddRep();
+ color = grey7;
+ stride = 1;
+ }
+ else
+ {
+ stride++;
+ }
}
+ AddRep();
+ color = byte.MaxValue;
+ }
- if (Parent.HeaderSettings.EncryptionKey > 0)
- {
- EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData);
- }
- else
- {
- EncodedRle = rawData.ToArray();
- }
- DataSize = (uint) EncodedRle.Length;
+ if (Parent.HeaderSettings.EncryptionKey > 0)
+ {
+ EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData);
}
-
- public override string ToString()
+ else
{
- return $"{nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
+ EncodedRle = rawData.ToArray();
}
+
+ DataSize = (uint) EncodedRle.Length;
}
- #endregion
- #endregion
+ public override string ToString()
+ {
+ return $"{nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}";
+ }
+ }
+ #endregion
- #region Properties
+ #endregion
- public Header HeaderSettings { get; protected internal set; } = new();
+ #region Properties
- public Preview[] Previews { get; protected internal set; }
+ public Header HeaderSettings { get; protected internal set; } = new();
- public LayerDef[] LayersDefinitions { get; private set; }
+ public Preview[] Previews { get; protected internal set; }
- public override FileFormatType FileType => FileFormatType.Binary;
+ public LayerDef[] LayersDefinitions { get; private set; } = null!;
- public override FileExtension[] FileExtensions { get; } = {
- new (typeof(PHZFile), "phz", "Chitubox PHZ"),
- };
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
+ public override FileExtension[] FileExtensions { get; } = {
+ new (typeof(PHZFile), "phz", "Chitubox PHZ"),
+ };
+
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- /*public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.ExposureSeconds,
- PrintParameterModifier.LightOffDelay,
- };*/
+ /*public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.ExposureSeconds,
+ PrintParameterModifier.LightOffDelay,
+ };*/
- public override Size[] ThumbnailsOriginalSize { get; } =
- {
- new(400, 300),
- new(200, 125)
- };
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(400, 300),
+ new(200, 125)
+ };
- public override uint[] AvailableVersions { get; } = { 2 };
+ public override uint[] AvailableVersions { get; } = { 2 };
- public override uint DefaultVersion => 2;
+ public override uint DefaultVersion => 2;
- public override uint Version
+ public override uint Version
+ {
+ get => HeaderSettings.Version;
+ set
{
- get => HeaderSettings.Version;
- set
- {
- base.Version = value;
- HeaderSettings.Version = base.Version;
- }
+ base.Version = value;
+ HeaderSettings.Version = base.Version;
}
+ }
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => HeaderSettings.BedSizeX;
+ set
{
- get => HeaderSettings.BedSizeX;
- set
- {
- HeaderSettings.BedSizeX = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ HeaderSettings.BedSizeX = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => HeaderSettings.BedSizeY;
+ set
{
- get => HeaderSettings.BedSizeY;
- set
- {
- HeaderSettings.BedSizeY = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ HeaderSettings.BedSizeY = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
+ public override float MachineZ
+ {
+ get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ;
+ set
{
- get => HeaderSettings.BedSizeZ > 0 ? HeaderSettings.BedSizeZ : base.MachineZ;
- set
- {
- HeaderSettings.BedSizeZ = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ HeaderSettings.BedSizeZ = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override Enumerations.FlipDirection DisplayMirror
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
+ set
{
- get => HeaderSettings.ProjectorType == 0 ? Enumerations.FlipDirection.None : Enumerations.FlipDirection.Horizontally;
- set
- {
- HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
- RaisePropertyChanged();
- }
+ HeaderSettings.ProjectorType = value == Enumerations.FlipDirection.None ? 0u : 1;
+ RaisePropertyChanged();
}
+ }
- public override byte AntiAliasing
- {
- get => (byte) HeaderSettings.AntiAliasLevelInfo;
- set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16));
- }
+ public override byte AntiAliasing
+ {
+ get => (byte) HeaderSettings.AntiAliasLevelInfo;
+ set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16));
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => HeaderSettings.LayerHeightMilimeter;
+ set
{
- get => HeaderSettings.LayerHeightMilimeter;
- set
- {
- HeaderSettings.LayerHeightMilimeter = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ HeaderSettings.LayerHeightMilimeter = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override float PrintHeight
- {
- get => base.PrintHeight;
- set => base.PrintHeight = HeaderSettings.OverallHeightMilimeter = base.PrintHeight;
- }
+ public override float PrintHeight
+ {
+ get => base.PrintHeight;
+ set => base.PrintHeight = HeaderSettings.OverallHeightMilimeter = base.PrintHeight;
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => HeaderSettings.LayerCount = base.LayerCount;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => HeaderSettings.LayerCount = base.LayerCount;
+ }
- public override ushort BottomLayerCount
- {
- get => (ushort) HeaderSettings.BottomLayersCount;
- set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount2 = HeaderSettings.BottomLayersCount = value);
- }
+ public override ushort BottomLayerCount
+ {
+ get => (ushort) HeaderSettings.BottomLayersCount;
+ set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount2 = HeaderSettings.BottomLayersCount = value);
+ }
- public override float BottomLightOffDelay
- {
- get => HeaderSettings.BottomLightOffDelay;
- set => base.BottomLightOffDelay = HeaderSettings.BottomLightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float BottomLightOffDelay
+ {
+ get => HeaderSettings.BottomLightOffDelay;
+ set => base.BottomLightOffDelay = HeaderSettings.BottomLightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float LightOffDelay
- {
- get => HeaderSettings.LightOffDelay;
- set => base.LightOffDelay = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float LightOffDelay
+ {
+ get => HeaderSettings.LightOffDelay;
+ set => base.LightOffDelay = HeaderSettings.LightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float BottomExposureTime
- {
- get => HeaderSettings.BottomExposureSeconds;
- set => base.BottomExposureTime = HeaderSettings.BottomExposureSeconds = (float)Math.Round(value, 2);
- }
+ public override float BottomExposureTime
+ {
+ get => HeaderSettings.BottomExposureSeconds;
+ set => base.BottomExposureTime = HeaderSettings.BottomExposureSeconds = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
{
- get => base.BottomWaitTimeBeforeCure;
- set
- {
- SetBottomLightOffDelay(value);
- base.BottomWaitTimeBeforeCure = value;
- }
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
{
- get => base.WaitTimeBeforeCure;
- set
- {
- SetNormalLightOffDelay(value);
- base.WaitTimeBeforeCure = value;
- }
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
}
+ }
- public override float ExposureTime
- {
- get => HeaderSettings.LayerExposureSeconds;
- set => base.ExposureTime = HeaderSettings.LayerExposureSeconds = (float)Math.Round(value, 2);
- }
+ public override float ExposureTime
+ {
+ get => HeaderSettings.LayerExposureSeconds;
+ set => base.ExposureTime = HeaderSettings.LayerExposureSeconds = (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 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 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 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 LiftSpeed
+ {
+ get => HeaderSettings.LiftSpeed;
+ set => base.LiftSpeed = HeaderSettings.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ public override float BottomRetractSpeed => RetractSpeed;
- public override float RetractSpeed
- {
- get => HeaderSettings.RetractSpeed;
- set => base.RetractSpeed = HeaderSettings.RetractSpeed = (float)Math.Round(value, 2);
- }
+ 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 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 byte LightPWM
+ {
+ get => (byte) HeaderSettings.LightPWM;
+ set => base.LightPWM = (byte) (HeaderSettings.LightPWM = value);
+ }
- public override float PrintTime
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- HeaderSettings.PrintTime = (uint)base.PrintTime;
- }
+ base.PrintTime = value;
+ HeaderSettings.PrintTime = (uint)base.PrintTime;
}
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- HeaderSettings.VolumeMl = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ HeaderSettings.VolumeMl = base.MaterialMilliliters;
}
+ }
- public override float MaterialGrams
- {
- get => (float) Math.Round(HeaderSettings.WeightG, 3);
- set => base.MaterialGrams = HeaderSettings.WeightG = (float) Math.Round(value, 3);
- }
+ public override float MaterialGrams
+ {
+ get => (float) Math.Round(HeaderSettings.WeightG, 3);
+ set => base.MaterialGrams = HeaderSettings.WeightG = (float) Math.Round(value, 3);
+ }
- public override float MaterialCost
- {
- get => (float) Math.Round(HeaderSettings.CostDollars, 3);
- set => base.MaterialCost = HeaderSettings.CostDollars = (float)Math.Round(value, 3);
- }
+ public override float MaterialCost
+ {
+ get => (float) Math.Round(HeaderSettings.CostDollars, 3);
+ set => base.MaterialCost = HeaderSettings.CostDollars = (float)Math.Round(value, 3);
+ }
- public override string MachineName
+ public override string MachineName
+ {
+ get => HeaderSettings.MachineName;
+ set
{
- get => HeaderSettings.MachineName;
- set
- {
- base.MachineName = HeaderSettings.MachineName = value;
- HeaderSettings.MachineNameSize = (uint)HeaderSettings.MachineName.Length;
- }
+ base.MachineName = HeaderSettings.MachineName = value;
+ HeaderSettings.MachineNameSize = (uint)HeaderSettings.MachineName.Length;
}
+ }
+
+ public override object[] Configs => new object[] { HeaderSettings };
- public override object[] Configs => new object[] { HeaderSettings };
+ #endregion
- #endregion
+ #region Constructors
+ public PHZFile()
+ {
+ Previews = new Preview[ThumbnailsCount];
+ }
+ #endregion
+
+ #region Methods
+ public override void Clear()
+ {
+ base.Clear();
- #region Constructors
- public PHZFile()
+ for (byte i = 0; i < ThumbnailsCount; i++)
{
- Previews = new Preview[ThumbnailsCount];
+ Previews[i] = new Preview();
}
- #endregion
- #region Methods
- public override void Clear()
- {
- base.Clear();
+ LayersDefinitions = null!;
+ }
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- Previews[i] = new Preview();
- }
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ /*if (HeaderSettings.EncryptionKey == 0)
+ {
+ Random rnd = new Random();
+ HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue);
+ }*/
- LayersDefinitions = null;
- }
+ LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
- protected override void EncodeInternally(OperationProgress progress)
+ for (byte i = 0; i < ThumbnailsCount; i++)
{
- /*if (HeaderSettings.EncryptionKey == 0)
- {
- Random rnd = new Random();
- HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue);
- }*/
+ var image = Thumbnails[i]!;
- LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
+ var bytes = Preview.Encode(image);
- for (byte i = 0; i < ThumbnailsCount; i++)
+ if (bytes.Length == 0) continue;
+
+ if (i == (byte) FileThumbnailSize.Small)
+ {
+ HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position;
+ }
+ else
{
- var image = Thumbnails[i];
+ HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position;
+ }
- var bytes = Preview.Encode(image);
- if (bytes.Length == 0) continue;
- if (i == (byte) FileThumbnailSize.Small)
- {
- HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position;
- }
- else
- {
- HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position;
- }
+ Preview preview = new()
+ {
+ ResolutionX = (uint) image.Width,
+ ResolutionY = (uint) image.Height,
+ ImageLength = (uint)bytes.Length,
+ };
+ preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
+ Helpers.SerializeWriteFileStream(outputFile, preview);
- Preview preview = new()
- {
- ResolutionX = (uint) image.Width,
- ResolutionY = (uint) image.Height,
- ImageLength = (uint)bytes.Length,
- };
+ outputFile.WriteBytes(bytes);
+ }
- preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview));
+ if (HeaderSettings.MachineNameSize > 0)
+ {
+ HeaderSettings.MachineNameAddress = (uint)outputFile.Position;
+ var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName);
+ outputFile.WriteBytes(machineBytes);
+ }
- Helpers.SerializeWriteFileStream(outputFile, preview);
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var layersHash = new Dictionary<string, LayerDef>();
+ LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
+ HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position;
+ uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + (uint)Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount;
- outputFile.WriteBytes(bytes);
- }
+ foreach (var batch in BatchLayersIndexes())
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = this[layerIndex].LayerMat)
+ {
+ LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]);
+ LayersDefinitions[layerIndex].Encode(mat, (uint)layerIndex);
+ }
+ progress.LockAndIncrement();
+ });
- if (HeaderSettings.MachineNameSize > 0)
+ foreach (var layerIndex in batch)
{
- HeaderSettings.MachineNameAddress = (uint)outputFile.Position;
- var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName);
- outputFile.WriteBytes(machineBytes);
- }
+ progress.Token.ThrowIfCancellationRequested();
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- var layersHash = new Dictionary<string, LayerDef>();
- LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
- HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position;
- uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + (uint)Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount;
+ var layerDef = LayersDefinitions[layerIndex];
+ LayerDef? layerDefHash = null;
- foreach (var batch in BatchLayersIndexes())
- {
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ if (HeaderSettings.EncryptionKey == 0)
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = this[layerIndex].LayerMat)
+ string hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle);
+ if (layersHash.TryGetValue(hash, out layerDefHash))
{
- LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]);
- LayersDefinitions[layerIndex].Encode(mat, (uint)layerIndex);
+ layerDef.DataAddress = layerDefHash.DataAddress;
+ layerDef.DataSize = layerDefHash.DataSize;
}
- progress.LockAndIncrement();
- });
-
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
-
- var layerDef = LayersDefinitions[layerIndex];
- LayerDef layerDefHash = null;
-
- if (HeaderSettings.EncryptionKey == 0)
+ else
{
- string hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle);
- if (layersHash.TryGetValue(hash, out layerDefHash))
- {
- layerDef.DataAddress = layerDefHash.DataAddress;
- layerDef.DataSize = layerDefHash.DataSize;
- }
- else
- {
- layersHash.Add(hash, layerDef);
- }
+ layersHash.Add(hash, layerDef);
}
+ }
- if (layerDefHash is null)
- {
- layerDef.DataAddress = layerDataCurrentOffset;
+ if (layerDefHash is null)
+ {
+ layerDef.DataAddress = layerDataCurrentOffset;
- outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
- layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle);
- }
+ outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
+ layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle);
+ }
- outputFile.Seek(layerDefCurrentOffset, SeekOrigin.Begin);
- layerDefCurrentOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef);
+ outputFile.Seek(layerDefCurrentOffset, SeekOrigin.Begin);
+ layerDefCurrentOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef);
- layerDef.EncodedRle = null; // Free
- }
+ layerDef.EncodedRle = null!; // Free
}
+ }
- HeaderSettings.ModifiedTimestampMinutes = (uint) DateTimeExtensions.TimestampMinutes;
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ HeaderSettings.ModifiedTimestampMinutes = (uint) DateTimeExtensions.TimestampMinutes;
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine(Previews[0]);
- Debug.WriteLine(Previews[1]);
- Debug.WriteLine("-End-");
- }
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(Previews[0]);
+ Debug.WriteLine(Previews[1]);
+ Debug.WriteLine("-End-");
+ }
- protected override void DecodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
+ //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.Magic != MAGIC_PHZ)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
- //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (HeaderSettings.Magic != MAGIC_PHZ)
- {
- throw new FileLoadException("Not a valid PHZ file!", FileFullPath);
- }
+ throw new FileLoadException("Not a valid PHZ file!", FileFullPath);
+ }
- HeaderSettings.AntiAliasLevel = 1;
+ HeaderSettings.AntiAliasLevel = 1;
- progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
- Debug.Write("Header -> ");
- Debug.WriteLine(HeaderSettings);
+ progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount);
+ Debug.Write("Header -> ");
+ Debug.WriteLine(HeaderSettings);
- for (byte i = 0; i < ThumbnailsCount; i++)
- {
- uint offsetAddress = i == 0
- ? HeaderSettings.PreviewSmallOffsetAddress
- : HeaderSettings.PreviewLargeOffsetAddress;
- if (offsetAddress == 0) continue;
+ for (byte i = 0; i < ThumbnailsCount; i++)
+ {
+ uint offsetAddress = i == 0
+ ? HeaderSettings.PreviewSmallOffsetAddress
+ : HeaderSettings.PreviewLargeOffsetAddress;
+ if (offsetAddress == 0) continue;
- inputFile.Seek(offsetAddress, SeekOrigin.Begin);
- Previews[i] = Helpers.Deserialize<Preview>(inputFile);
+ inputFile.Seek(offsetAddress, SeekOrigin.Begin);
+ Previews[i] = Helpers.Deserialize<Preview>(inputFile);
- Debug.Write($"Preview {i} -> ");
- Debug.WriteLine(Previews[i]);
+ Debug.Write($"Preview {i} -> ");
+ Debug.WriteLine(Previews[i]);
- inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
- byte[] rawImageData = new byte[Previews[i].ImageLength];
- inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength);
+ inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin);
+ byte[] rawImageData = new byte[Previews[i].ImageLength];
+ inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength);
- Thumbnails[i] = Previews[i].Decode(rawImageData);
- progress++;
- }
+ Thumbnails[i] = Previews[i].Decode(rawImageData);
+ progress++;
+ }
- if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
- {
- inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
- byte[] buffer = new byte[HeaderSettings.MachineNameSize];
- inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize);
- HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer);
- }
+ if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
+ {
+ inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
+ byte[] buffer = new byte[HeaderSettings.MachineNameSize];
+ inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize);
+ HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer);
+ }
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ LayersDefinitions = new LayerDef[HeaderSettings.LayerCount];
- progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount);
- foreach (var batch in BatchLayersIndexes())
+ progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount);
+ foreach (var batch in BatchLayersIndexes())
+ {
+ foreach (var layerIndex in batch)
{
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
- layerDef.Parent = this;
- LayersDefinitions[layerIndex] = layerDef;
+ var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
+ layerDef.Parent = this;
+ LayersDefinitions[layerIndex] = layerDef;
- Debug.Write($"LAYER {layerIndex} -> ");
- Debug.WriteLine(layerDef);
-
- if (DecodeType == FileDecodeType.Full)
- {
- inputFile.SeekDoWorkAndRewind(layerDef.DataAddress,
- () => { layerDef.EncodedRle = inputFile.ReadBytes(layerDef.DataSize); });
- }
- }
+ Debug.Write($"LAYER {layerIndex} -> ");
+ Debug.WriteLine(layerDef);
if (DecodeType == FileDecodeType.Full)
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex);
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- progress.LockAndIncrement();
- });
+ inputFile.SeekDoWorkAndRewind(layerDef.DataAddress,
+ () => { layerDef.EncodedRle = inputFile.ReadBytes(layerDef.DataSize); });
}
}
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ if (DecodeType == FileDecodeType.Full)
{
- LayersDefinitions[layerIndex].CopyTo(this[layerIndex]);
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+
+ using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex);
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ progress.LockAndIncrement();
+ });
}
}
- protected override void PartialSaveInternally(OperationProgress progress)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- HeaderSettings.ModifiedTimestampMinutes = (uint) DateTimeExtensions.TimestampMinutes;
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ LayersDefinitions[layerIndex].CopyTo(this[layerIndex]);
+ }
+ }
- /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
- {
- outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
- byte[] buffer = new byte[HeaderSettings.MachineNameSize];
- outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize);
- }*/
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ HeaderSettings.ModifiedTimestampMinutes = (uint) DateTimeExtensions.TimestampMinutes;
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0)
{
- LayersDefinitions[layerIndex].SetFrom(this[layerIndex]);
- outputFile.Seek(layerOffset, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]);
- layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]);
- }
- }
-
- #endregion
+ outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin);
+ byte[] buffer = new byte[HeaderSettings.MachineNameSize];
+ outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize);
+ }*/
- #region Static Methods
- public static byte[] LayerRleCrypt(uint seed, uint layerIndex, IEnumerable<byte> input)
+ uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
+ for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
{
- var result = input.ToArray();
- LayerRleCryptBuffer(seed, layerIndex, result);
- return result;
+ LayersDefinitions[layerIndex].SetFrom(this[layerIndex]);
+ outputFile.Seek(layerOffset, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]);
+ layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]);
}
+ }
- public static void LayerRleCryptBuffer(uint seed, uint layerIndex, byte[] input)
- {
- if (seed == 0) return;
- seed %= 0x4324;
- var init = seed * 0x34a32231;
- var key = (layerIndex ^ 0x3fad2212) * seed * 0x4910913d;
+ #endregion
- int index = 0;
- for (int i = 0; i < input.Length; i++)
- {
- var k = (byte)(key >> 8 * index);
+ #region Static Methods
+ public static byte[] LayerRleCrypt(uint seed, uint layerIndex, IEnumerable<byte> input)
+ {
+ var result = input.ToArray();
+ LayerRleCryptBuffer(seed, layerIndex, result);
+ return result;
+ }
- index++;
+ public static void LayerRleCryptBuffer(uint seed, uint layerIndex, byte[] input)
+ {
+ if (seed == 0) return;
+ seed %= 0x4324;
+ var init = seed * 0x34a32231;
+ var key = (layerIndex ^ 0x3fad2212) * seed * 0x4910913d;
- if ((index & 3) == 0)
- {
- key += init;
- index = 0;
- }
+ int index = 0;
+ for (int i = 0; i < input.Length; i++)
+ {
+ var k = (byte)(key >> 8 * index);
+
+ index++;
- input[i] = (byte)(input[i] ^ k);
+ if ((index & 3) == 0)
+ {
+ key += init;
+ index = 0;
}
+
+ input[i] = (byte)(input[i] ^ k);
}
- #endregion
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs
index d054234..b382c94 100644
--- a/UVtools.Core/FileFormats/PhotonSFile.cs
+++ b/UVtools.Core/FileFormats/PhotonSFile.cs
@@ -6,544 +6,543 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class PhotonSFile : FileFormat
{
- public class PhotonSFile : FileFormat
- {
- #region Constants
- public const byte RLEEncodingLimit = 128;
+ #region Constants
+ public const byte RLEEncodingLimit = 128;
- public const ushort RESOLUTION_X = 1440;
- public const ushort RESOLUTION_Y = 2560;
+ public const ushort RESOLUTION_X = 1440;
+ public const ushort RESOLUTION_Y = 2560;
- public const float DISPLAY_WIDTH = 68.04f;
- public const float DISPLAY_HEIGHT = 120.96f;
- public const float MACHINE_Z = 165f;
+ public const float DISPLAY_WIDTH = 68.04f;
+ public const float DISPLAY_HEIGHT = 120.96f;
+ public const float MACHINE_Z = 165f;
- #endregion
+ #endregion
- #region Members
+ #region Members
- private uint _resolutionX = RESOLUTION_X;
- private uint _resolutionY = RESOLUTION_Y;
+ private uint _resolutionX = RESOLUTION_X;
+ private uint _resolutionY = RESOLUTION_Y;
- #endregion
+ #endregion
- #region Sub Classes
+ #region Sub Classes
- #region Header
+ #region Header
- public class Header
+ public class Header
+ {
+ public const uint TAG1 = 2;
+ public const ushort TAG2 = 49;
+
+
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Tag1 { get; set; } = TAG1; // 2
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort Tag2 { get; set; } = TAG2; // 49
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public double XYPixelSize { get; set; } = 0.04725; // 0.04725
+ [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public double LayerHeight { get; set; }
+ [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public double ExposureSeconds { get; set; }
+ [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public double LightOffDelay { get; set; }
+ [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public double BottomExposureSeconds { get; set; }
+ [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint BottomLayerCount { get; set; }
+ [FieldOrder(8)] [FieldEndianness(Endianness.Big)] public double LiftHeight { get; set; } // mm
+ [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public double LiftSpeed { get; set; } // mm/s
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public double RetractSpeed { get; set; } // mm/s
+ [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public double VolumeMl { get; set; } // ml
+ [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionX { get; set; } = 224;
+ [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 42;
+ [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionY { get; set; } = 168;
+ [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public uint Unknown4 { get; set; } = 10;
+
+ public override string ToString()
{
- public const uint TAG1 = 2;
- public const ushort TAG2 = 49;
-
-
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Tag1 { get; set; } = TAG1; // 2
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort Tag2 { get; set; } = TAG2; // 49
- [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public double XYPixelSize { get; set; } = 0.04725; // 0.04725
- [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public double LayerHeight { get; set; }
- [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public double ExposureSeconds { get; set; }
- [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public double LightOffDelay { get; set; }
- [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public double BottomExposureSeconds { get; set; }
- [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint BottomLayerCount { get; set; }
- [FieldOrder(8)] [FieldEndianness(Endianness.Big)] public double LiftHeight { get; set; } // mm
- [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public double LiftSpeed { get; set; } // mm/s
- [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public double RetractSpeed { get; set; } // mm/s
- [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public double VolumeMl { get; set; } // ml
- [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionX { get; set; } = 224;
- [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 42;
- [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionY { get; set; } = 168;
- [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public uint Unknown4 { get; set; } = 10;
-
- public override string ToString()
- {
- return $"{nameof(Tag1)}: {Tag1}, {nameof(Tag2)}: {Tag2}, {nameof(XYPixelSize)}: {XYPixelSize}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureSeconds)}: {ExposureSeconds}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(PreviewResolutionX)}: {PreviewResolutionX}, {nameof(Unknown2)}: {Unknown2}, {nameof(PreviewResolutionY)}: {PreviewResolutionY}, {nameof(Unknown4)}: {Unknown4}";
- }
+ return $"{nameof(Tag1)}: {Tag1}, {nameof(Tag2)}: {Tag2}, {nameof(XYPixelSize)}: {XYPixelSize}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureSeconds)}: {ExposureSeconds}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(PreviewResolutionX)}: {PreviewResolutionX}, {nameof(Unknown2)}: {Unknown2}, {nameof(PreviewResolutionY)}: {PreviewResolutionY}, {nameof(Unknown4)}: {Unknown4}";
}
+ }
+
+ public class LayerHeader
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LayerCount { get; set; }
- public class LayerHeader
+ public override string ToString()
{
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LayerCount { get; set; }
+ return $"{nameof(LayerCount)}: {LayerCount}";
+ }
+ }
- public override string ToString()
- {
- return $"{nameof(LayerCount)}: {LayerCount}";
- }
+ #endregion
+
+ #region LayerDef
+
+ public class LayerDef
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Unknown1 { get; set; } = 44944;
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 0;
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint Unknown3 { get; set; } = 0;
+ [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint ResolutionX { get; set; } = RESOLUTION_X;
+ [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public uint ResolutionY { get; set; } = RESOLUTION_Y;
+ [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DataSize { get; set; }
+ [Ignore] public uint RleDataSize
+ {
+ get => (DataSize >> 3) - 4;
+ set => DataSize = (value + 4) << 3;
+ //get => DataSize / 8 - 4;
+ //set => DataSize = (value + 4) * 8;
}
- #endregion
+ [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public uint Unknown5 { get; set; } = 2684702720;
- #region LayerDef
+ [Ignore] public byte[] EncodedRle { get; set; } = null!;
- public class LayerDef
+ public LayerDef()
{
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Unknown1 { get; set; } = 44944;
- [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 0;
- [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint Unknown3 { get; set; } = 0;
- [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint ResolutionX { get; set; } = RESOLUTION_X;
- [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public uint ResolutionY { get; set; } = RESOLUTION_Y;
- [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DataSize { get; set; }
- [Ignore] public uint RleDataSize
- {
- get => (DataSize >> 3) - 4;
- set => DataSize = (value + 4) << 3;
- //get => DataSize / 8 - 4;
- //set => DataSize = (value + 4) * 8;
- }
+ }
- [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public uint Unknown5 { get; set; } = 2684702720;
+ public LayerDef(Mat mat)
+ {
+ ResolutionX = (uint)mat.Width;
+ ResolutionY = (uint)mat.Height;
+ }
- [Ignore] public byte[] EncodedRle { get; set; }
+ public override string ToString()
+ {
+ return $"{nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(DataSize)}: {DataSize}, {nameof(RleDataSize)}: {RleDataSize}, {nameof(Unknown5)}: {Unknown5}, {nameof(EncodedRle)}: {EncodedRle?.Length}";
+ }
- public LayerDef()
- {
- }
+ public unsafe byte[] Encode(Mat mat)
+ {
+ List<byte> rawData = new();
+ var spanMat = mat.GetBytePointer();
+ var imageLength = mat.GetLength();
- public LayerDef(Mat mat)
- {
- ResolutionX = (uint)mat.Width;
- ResolutionY = (uint)mat.Height;
- }
+ int rep = 0;
+ byte color = 0;
+ int totalPixels = 0;
- public override string ToString()
+ void AddRep()
{
- return $"{nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(DataSize)}: {DataSize}, {nameof(RleDataSize)}: {RleDataSize}, {nameof(Unknown5)}: {Unknown5}, {nameof(EncodedRle)}: {EncodedRle?.Length}";
+ if (rep <= 0) return;
+
+ totalPixels += rep;
+ rep--;
+ byte rle = (byte) (((rep & 1) > 0 ? 128 : 0) |
+ ((rep & 2) > 0 ? 64 : 0) |
+ ((rep & 4) > 0 ? 32 : 0) |
+ ((rep & 8) > 0 ? 16 : 0) |
+ ((rep & 16) > 0 ? 8 : 0) |
+ ((rep & 32) > 0 ? 4 : 0) |
+ ((rep & 64) > 0 ? 2 : 0) | color);
+
+ rawData.Add(rle);
}
- public unsafe byte[] Encode(Mat mat)
+ for (int i = 0; i < imageLength; i++)
{
- List<byte> rawData = new();
- var spanMat = mat.GetBytePointer();
- var imageLength = mat.GetLength();
-
- int rep = 0;
- byte color = 0;
- int totalPixels = 0;
-
- void AddRep()
+ byte thisColor = spanMat[i] <= 127 ? (byte)0 : (byte)1; // Sanitize no AA
+ if (thisColor != color)
{
- if (rep <= 0) return;
-
- totalPixels += rep;
- rep--;
- byte rle = (byte) (((rep & 1) > 0 ? 128 : 0) |
- ((rep & 2) > 0 ? 64 : 0) |
- ((rep & 4) > 0 ? 32 : 0) |
- ((rep & 8) > 0 ? 16 : 0) |
- ((rep & 16) > 0 ? 8 : 0) |
- ((rep & 32) > 0 ? 4 : 0) |
- ((rep & 64) > 0 ? 2 : 0) | color);
-
- rawData.Add(rle);
+ AddRep();
+ color = thisColor; // Sanitize no AA
+ rep = 1;
}
-
- for (int i = 0; i < imageLength; i++)
+ else
{
- byte thisColor = spanMat[i] <= 127 ? (byte)0 : (byte)1; // Sanitize no AA
- if (thisColor != color)
+ rep++;
+ if (rep == RLEEncodingLimit)
{
AddRep();
- color = thisColor; // Sanitize no AA
- rep = 1;
- }
- else
- {
- rep++;
- if (rep == RLEEncodingLimit)
- {
- AddRep();
- rep = 0;
- }
+ rep = 0;
}
}
+ }
- AddRep();
-
- if (totalPixels != imageLength)
- {
- throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {totalPixels} pixels.");
- }
+ AddRep();
- EncodedRle = rawData.ToArray();
- RleDataSize = (uint) EncodedRle.Length;
- return EncodedRle;
+ if (totalPixels != imageLength)
+ {
+ throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {totalPixels} pixels.");
}
- public Mat Decode(bool consumeRle = true)
- {
- var mat = EmguExtensions.InitMat(new Size((int) ResolutionX, (int) ResolutionY));
- //var matSpan = mat.GetBytePointer();
- var imageLength = mat.GetLength();
+ EncodedRle = rawData.ToArray();
+ RleDataSize = (uint) EncodedRle.Length;
+ return EncodedRle;
+ }
- int pixelPos = 0;
- foreach (var run in EncodedRle)
- {
- if (pixelPos > imageLength)
- {
- mat.Dispose();
- throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels.");
- }
+ public Mat Decode(bool consumeRle = true)
+ {
+ var mat = EmguExtensions.InitMat(new Size((int) ResolutionX, (int) ResolutionY));
+ //var matSpan = mat.GetBytePointer();
+ var imageLength = mat.GetLength();
- byte brightness = (byte) ((run & 0x01) * 255);
+ int pixelPos = 0;
+ foreach (var run in EncodedRle)
+ {
+ if (pixelPos > imageLength)
+ {
+ mat.Dispose();
+ throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels.");
+ }
- int numPixelsInRun =
- (((run & 128) > 0 ? 1 : 0) |
- ((run & 64) > 0 ? 2 : 0) |
- ((run & 32) > 0 ? 4 : 0) |
- ((run & 16) > 0 ? 8 : 0) |
- ((run & 8) > 0 ? 16 : 0) |
- ((run & 4) > 0 ? 32 : 0) |
- ((run & 2) > 0 ? 64 : 0)) + 1;
+ byte brightness = (byte) ((run & 0x01) * 255);
- mat.FillSpan(ref pixelPos, numPixelsInRun, brightness);
+ int numPixelsInRun =
+ (((run & 128) > 0 ? 1 : 0) |
+ ((run & 64) > 0 ? 2 : 0) |
+ ((run & 32) > 0 ? 4 : 0) |
+ ((run & 16) > 0 ? 8 : 0) |
+ ((run & 8) > 0 ? 16 : 0) |
+ ((run & 4) > 0 ? 32 : 0) |
+ ((run & 2) > 0 ? 64 : 0)) + 1;
- /*if (brightness == 0) // Don't fill black pixels
- {
- pixelPos += numPixelsInRun;
- continue;
- }
+ mat.FillSpan(ref pixelPos, numPixelsInRun, brightness);
- for (; numPixelsInRun > 0; numPixelsInRun--)
- {
- if (pixelPos > imageLength)
- {
- mat.Dispose();
- throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels.");
- }
- matSpan[pixelPos++] = brightness;
- }*/
+ /*if (brightness == 0) // Don't fill black pixels
+ {
+ pixelPos += numPixelsInRun;
+ continue;
}
- // Some slicers will not fill the pixel RLE to the end when the remaining pixels are trailing black,
- // this was triggering error on read because data checksum was incomplete, ignoring checksum now (#344)
- /*if (pixelPos != imageLength && pixelPos-1 != imageLength)
+ for (; numPixelsInRun > 0; numPixelsInRun--)
{
- mat.Dispose();
- throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {pixelPos} pixels.");
+ if (pixelPos > imageLength)
+ {
+ mat.Dispose();
+ throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels.");
+ }
+ matSpan[pixelPos++] = brightness;
}*/
+ }
- // Not required as mat is all black by default
- //for (;pixel < imageLength; pixel++) matSpan[pixel] = 0;
+ // Some slicers will not fill the pixel RLE to the end when the remaining pixels are trailing black,
+ // this was triggering error on read because data checksum was incomplete, ignoring checksum now (#344)
+ /*if (pixelPos != imageLength && pixelPos-1 != imageLength)
+ {
+ mat.Dispose();
+ throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {pixelPos} pixels.");
+ }*/
- if (consumeRle)
- EncodedRle = null;
+ // Not required as mat is all black by default
+ //for (;pixel < imageLength; pixel++) matSpan[pixel] = 0;
- return mat;
- }
+ if (consumeRle)
+ EncodedRle = null!;
+
+ return mat;
}
- #endregion
+ }
+ #endregion
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public Header HeaderSettings { get; protected internal set; } = new();
- public LayerHeader LayerSettings { get; protected internal set; } = new();
- public override FileFormatType FileType => FileFormatType.Binary;
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public LayerHeader LayerSettings { get; protected internal set; } = new();
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(PhotonSFile), "photons", "Chitubox PhotonS"),
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(PhotonSFile), "photons", "Chitubox PhotonS"),
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } =
- {
- PrintParameterModifier.BottomLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- //PrintParameterModifier.BottomLightOffDelay,
+ //PrintParameterModifier.BottomLightOffDelay,
- //PrintParameterModifier.BottomLiftHeight,
- //PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
- };
+ //PrintParameterModifier.BottomLiftHeight,
+ //PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+ };
- public override Size[] ThumbnailsOriginalSize { get; } = {new(224, 168) };
+ public override Size[]? ThumbnailsOriginalSize { get; } = {new(224, 168) };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => _resolutionX;
+ set
{
- get => _resolutionX;
- set
- {
- if(!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
- HeaderSettings.XYPixelSize = PixelSizeMax;
- }
+ if(!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
+ HeaderSettings.XYPixelSize = PixelSizeMax;
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => _resolutionY;
+ set
{
- get => _resolutionY;
- set
- {
- if (!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
- HeaderSettings.XYPixelSize = PixelSizeMax;
- }
+ if (!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
+ HeaderSettings.XYPixelSize = PixelSizeMax;
}
+ }
- public override float DisplayWidth
- {
- get => DISPLAY_WIDTH;
- set { }
- }
+ public override float DisplayWidth
+ {
+ get => DISPLAY_WIDTH;
+ set { }
+ }
- public override float DisplayHeight
- {
- get => DISPLAY_HEIGHT;
- set { }
- }
+ public override float DisplayHeight
+ {
+ get => DISPLAY_HEIGHT;
+ set { }
+ }
- public override Enumerations.FlipDirection DisplayMirror
- {
- get => Enumerations.FlipDirection.Horizontally;
- set { }
- }
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => Enumerations.FlipDirection.Horizontally;
+ set { }
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => (float) Layer.RoundHeight(HeaderSettings.LayerHeight);
+ set
{
- get => (float) Layer.RoundHeight(HeaderSettings.LayerHeight);
- set
- {
- HeaderSettings.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ HeaderSettings.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
- {
- get => MACHINE_Z;
- set { }
- }
+ public override float MachineZ
+ {
+ get => MACHINE_Z;
+ set { }
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = LayerSettings.LayerCount = LayerCount;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = LayerSettings.LayerCount = LayerCount;
+ }
- public override ushort BottomLayerCount
- {
- get => (ushort) HeaderSettings.BottomLayerCount;
- set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayerCount = value);
- }
+ public override ushort BottomLayerCount
+ {
+ get => (ushort) HeaderSettings.BottomLayerCount;
+ set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayerCount = value);
+ }
- public override float BottomLightOffDelay => LightOffDelay;
+ public override float BottomLightOffDelay => LightOffDelay;
- public override float LightOffDelay
- {
- get => (float)HeaderSettings.LightOffDelay;
- set => base.LightOffDelay = (float)(HeaderSettings.LightOffDelay = Math.Round(value, 2));
- }
+ public override float LightOffDelay
+ {
+ get => (float)HeaderSettings.LightOffDelay;
+ set => base.LightOffDelay = (float)(HeaderSettings.LightOffDelay = Math.Round(value, 2));
+ }
- public override float BottomWaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
{
- get => base.BottomWaitTimeBeforeCure;
- set
- {
- SetBottomLightOffDelay(value);
- base.BottomWaitTimeBeforeCure = value;
- }
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
{
- get => base.WaitTimeBeforeCure;
- set
- {
- SetNormalLightOffDelay(value);
- base.WaitTimeBeforeCure = value;
- }
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
}
+ }
- public override float BottomExposureTime
- {
- get => (float) HeaderSettings.BottomExposureSeconds;
- set => base.BottomExposureTime = (float) (HeaderSettings.BottomExposureSeconds = Math.Round(value, 2));
- }
+ public override float BottomExposureTime
+ {
+ get => (float) HeaderSettings.BottomExposureSeconds;
+ set => base.BottomExposureTime = (float) (HeaderSettings.BottomExposureSeconds = Math.Round(value, 2));
+ }
- public override float ExposureTime
- {
- get => (float) HeaderSettings.ExposureSeconds;
- set => base.ExposureTime = (float) (HeaderSettings.ExposureSeconds = Math.Round(value, 2));
- }
+ public override float ExposureTime
+ {
+ get => (float) HeaderSettings.ExposureSeconds;
+ set => base.ExposureTime = (float) (HeaderSettings.ExposureSeconds = Math.Round(value, 2));
+ }
- public override float BottomLiftHeight => LiftHeight;
+ public override float BottomLiftHeight => LiftHeight;
- public override float LiftHeight
- {
- get => (float) HeaderSettings.LiftHeight;
- set => base.LiftHeight = (float) (HeaderSettings.LiftHeight = Math.Round(value, 2));
- }
+ public override float LiftHeight
+ {
+ get => (float) HeaderSettings.LiftHeight;
+ set => base.LiftHeight = (float) (HeaderSettings.LiftHeight = Math.Round(value, 2));
+ }
- public override float BottomLiftSpeed => LiftSpeed;
+ public override float BottomLiftSpeed => LiftSpeed;
- public override float LiftSpeed
- {
- get => (float) Math.Round(HeaderSettings.LiftSpeed * 60.0, 2);
- set => base.LiftSpeed = (float) (HeaderSettings.LiftSpeed = Math.Round(value / 60.0, 2));
- }
+ public override float LiftSpeed
+ {
+ get => (float) Math.Round(HeaderSettings.LiftSpeed * 60.0, 2);
+ set => base.LiftSpeed = (float) (HeaderSettings.LiftSpeed = Math.Round(value / 60.0, 2));
+ }
- public override float BottomRetractSpeed => RetractSpeed;
+ public override float BottomRetractSpeed => RetractSpeed;
- public override float RetractSpeed
- {
- get => (float)Math.Round(HeaderSettings.RetractSpeed * 60.0, 2);
- set => base.RetractSpeed = (float) (HeaderSettings.RetractSpeed = (float) Math.Round(value / 60.0, 2));
- }
+ public override float RetractSpeed
+ {
+ get => (float)Math.Round(HeaderSettings.RetractSpeed * 60.0, 2);
+ set => base.RetractSpeed = (float) (HeaderSettings.RetractSpeed = (float) Math.Round(value / 60.0, 2));
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- HeaderSettings.VolumeMl = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ HeaderSettings.VolumeMl = base.MaterialMilliliters;
}
+ }
- public override string MachineName => "Anycubic Photon S";
+ public override string MachineName => "Anycubic Photon S";
- public override object[] Configs => new object[] { HeaderSettings };
+ public override object[] Configs => new object[] { HeaderSettings };
- #endregion
+ #endregion
- #region Constructors
- public PhotonSFile()
- {
- }
- #endregion
+ #region Constructors
+ public PhotonSFile()
+ {
+ }
+ #endregion
- #region Methods
+ #region Methods
- protected override void EncodeInternally(OperationProgress progress)
- {
- //throw new NotSupportedException("PhotonS is read-only format, please use pws instead!");
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- outputFile.WriteSerialize(HeaderSettings);
- outputFile.WriteBytes(EncodeImage(DATATYPE_BGR565, Thumbnails[0]));
- outputFile.WriteSerialize(LayerSettings);
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ //throw new NotSupportedException("PhotonS is read-only format, please use pws instead!");
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ outputFile.WriteSerialize(HeaderSettings);
+ outputFile.WriteBytes(EncodeImage(DATATYPE_BGR565, Thumbnails[0]!));
+ outputFile.WriteSerialize(LayerSettings);
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- var layerData = new LayerDef[LayerCount];
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var layerData = new LayerDef[LayerCount];
- foreach (var batch in BatchLayersIndexes())
+ foreach (var batch in BatchLayersIndexes())
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = this[layerIndex].LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = this[layerIndex].LayerMat)
- {
- layerData[layerIndex] = new LayerDef(mat);
- layerData[layerIndex].Encode(mat);
- }
- progress.LockAndIncrement();
- });
+ layerData[layerIndex] = new LayerDef(mat);
+ layerData[layerIndex].Encode(mat);
+ }
+ progress.LockAndIncrement();
+ });
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
+ foreach (var layerIndex in batch)
+ {
+ progress.Token.ThrowIfCancellationRequested();
- outputFile.WriteSerialize(layerData[layerIndex]);
- outputFile.WriteBytes(layerData[layerIndex].EncodedRle);
+ outputFile.WriteSerialize(layerData[layerIndex]);
+ outputFile.WriteBytes(layerData[layerIndex].EncodedRle);
- layerData[layerIndex].EncodedRle = null; // Free
- }
+ layerData[layerIndex].EncodedRle = null!; // Free
}
-
- Debug.WriteLine("Encode Results:");
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine("-End-");
}
- protected override void DecodeInternally(OperationProgress progress)
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
+
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2)
{
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2)
- {
- throw new FileLoadException("Not a valid PHOTONS file! TAGs doesn't match", FileFullPath);
- }
+ throw new FileLoadException("Not a valid PHOTONS file! TAGs doesn't match", FileFullPath);
+ }
- int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2);
- byte[] previewData = new byte[previewSize];
+ int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2);
+ byte[] previewData = new byte[previewSize];
- inputFile.ReadBytes(previewData);
- Thumbnails[0] = DecodeImage(DATATYPE_BGR565, previewData, HeaderSettings.PreviewResolutionX, HeaderSettings.PreviewResolutionY);
+ inputFile.ReadBytes(previewData);
+ Thumbnails[0] = DecodeImage(DATATYPE_BGR565, previewData, HeaderSettings.PreviewResolutionX, HeaderSettings.PreviewResolutionY);
- LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile);
+ LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile);
- Debug.WriteLine(HeaderSettings);
- Debug.WriteLine(LayerSettings);
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine(LayerSettings);
- LayerManager.Init(LayerSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- var layersDefinitions = new LayerDef[LayerSettings.LayerCount];
+ Init(LayerSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ var layersDefinitions = new LayerDef[LayerSettings.LayerCount];
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- foreach (var batch in BatchLayersIndexes())
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ foreach (var batch in BatchLayersIndexes())
+ {
+ foreach (var layerIndex in batch)
{
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
+ progress.Token.ThrowIfCancellationRequested();
- var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
- layersDefinitions[layerIndex] = layerDef;
+ var layerDef = Helpers.Deserialize<LayerDef>(inputFile);
+ layersDefinitions[layerIndex] = layerDef;
- if (DecodeType == FileDecodeType.Full) layerDef.EncodedRle = inputFile.ReadBytes(layerDef.RleDataSize);
- else inputFile.Seek(layerDef.RleDataSize, SeekOrigin.Current);
+ if (DecodeType == FileDecodeType.Full) layerDef.EncodedRle = inputFile.ReadBytes(layerDef.RleDataSize);
+ else inputFile.Seek(layerDef.RleDataSize, SeekOrigin.Current);
- Debug.Write($"LAYER {layerIndex} -> ");
- Debug.WriteLine(layerDef);
+ Debug.Write($"LAYER {layerIndex} -> ");
+ Debug.WriteLine(layerDef);
- if (layerIndex == 1)
- {
- // Auto fix resolution if needed
- ResolutionX = layersDefinitions[layerIndex].ResolutionX;
- ResolutionY = layersDefinitions[layerIndex].ResolutionY;
- }
- }
-
- if (DecodeType == FileDecodeType.Full)
+ if (layerIndex == 1)
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = layersDefinitions[layerIndex].Decode();
- this[layerIndex] = new Layer((uint)layerIndex, mat, this);
- progress.LockAndIncrement();
- });
+ // Auto fix resolution if needed
+ ResolutionX = layersDefinitions[layerIndex].ResolutionX;
+ ResolutionY = layersDefinitions[layerIndex].ResolutionY;
}
}
- LayerManager.RebuildLayersProperties();
+ if (DecodeType == FileDecodeType.Full)
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = layersDefinitions[layerIndex].Decode();
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ progress.LockAndIncrement();
+ });
+ }
}
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- }
+ RebuildLayersProperties();
+ }
- #endregion
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
index d66690e..3d081a2 100644
--- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
+++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
@@ -6,6 +6,8 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
+using Emgu.CV;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -13,1077 +15,1048 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using BinarySerialization;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class PhotonWorkshopFile : FileFormat
{
- public class PhotonWorkshopFile : FileFormat
+ #region Constants
+ public const byte VERSION_1 = 1;
+ public const ushort VERSION_515 = 515;
+ public const ushort VERSION_516 = 516;
+
+ public const byte MarkSize = 12;
+ public const byte RLE1EncodingLimit = 0x7d; // 125;
+ public const ushort RLE4EncodingLimit = 0xfff; // 4095;
+
+ public static readonly uint[] ReservedUintsAfterPreview =
{
- #region Constants
- public const byte VERSION_1 = 1;
- public const ushort VERSION_515 = 515;
- public const ushort VERSION_516 = 516;
-
- public const byte MarkSize = 12;
- public const byte RLE1EncodingLimit = 0x7d; // 125;
- public const ushort RLE4EncodingLimit = 0xfff; // 4095;
-
- public static readonly uint[] ReservedUintsAfterPreview =
- {
- 0,
- 16,
- uint.MaxValue,
- uint.MaxValue,
- uint.MaxValue,
- uint.MaxValue,
- 0
- };
+ 0,
+ 16,
+ uint.MaxValue,
+ uint.MaxValue,
+ uint.MaxValue,
+ uint.MaxValue,
+ 0
+ };
+
+ // CRC-16-ANSI (aka CRC-16-IBM) Polynomial: x^16 + x^15 + x^2 + 1
+ public static readonly int[] CRC16Table = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
+ };
+
+ #endregion
+
+ #region Enums
+ public enum LayerRleFormat
+ {
+ PWS,
+ PW0
+ }
+
+ public enum AnyCubicMachine : byte
+ {
+ AnyCubicPhotonS,
+ AnyCubicPhotonZero,
+ AnyCubicPhotonX,
+ AnyCubicPhotonUltra,
+ AnyCubicPhotonMono,
+ AnyCubicPhotonMonoSE,
+ AnyCubicPhotonMono4K,
+ AnyCubicPhotonMonoX,
+ AnyCubicPhotonMonoX6K,
+ AnyCubicPhotonMonoSQ,
+ }
+ #endregion
- // CRC-16-ANSI (aka CRC-16-IBM) Polynomial: x^16 + x^15 + x^2 + 1
- public static readonly int[] CRC16Table = {
- 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
- 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
- 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
- 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
- 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
- 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
- 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
- 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
- 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
- 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
- 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
- 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
- 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
- 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
- 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
- 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
- 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
- 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
- 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
- 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
- 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
- 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
- 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
- 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
- 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
- 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
- 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
- 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
- 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
- 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
- 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
- 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
- };
+ #region Sub Classes
+
+ #region FileMark
+ public class FileMark
+ {
+ public const string SectionMarkFile = "ANYCUBIC";
+
+ /// <summary>
+ /// Gets the file mark placeholder
+ /// Fixed to "ANYCUBIC"
+ /// 00
+ /// </summary>
+ [FieldOrder(0)]
+ [FieldLength(MarkSize)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string Mark { get; set; } = SectionMarkFile;
+
+ /// <summary>
+ /// Gets the file format version
+ /// 0C
+ /// </summary>
+ [FieldOrder(1)] public uint Version { get; set; } = VERSION_1;
+
+ /// <summary>
+ /// Gets the area num
+ /// 10, 4 for v1, 5 for v515, 8 for v516?
+ /// </summary>
+ [FieldOrder(2)] public uint AreaNum { get; set; }
+
+ /// <summary>
+ /// Gets the header start address
+ /// 14
+ /// </summary>
+ [FieldOrder(3)] public uint HeaderAddress { get; set; }
+
+ /// <summary>
+ /// 18
+ /// </summary>
+ [FieldOrder(4)] public uint Padding1 { get; set; }
+
+ /// <summary>
+ /// Gets the preview start offset
+ /// 1C
+ /// </summary>
+ [FieldOrder(5)] public uint PreviewAddress { get; set; }
+
+ /// <summary>
+ /// 20, Spotted on version 515 only
+ /// </summary>
+ [FieldOrder(6)] public uint PreviewEndAddress { get; set; }
+
+ /// <summary>
+ /// Gets the layer definition start address
+ /// 24
+ /// </summary>
+ [FieldOrder(7)] public uint LayerDefinitionAddress { get; set; }
+
+ /// <summary>
+ /// 28, Spotted on version 516 only
+ /// </summary>
+ [FieldOrder(8)] public uint ExtraAddress { get; set; }
+
+ /// <summary>
+ /// 2C, Spotted on version 516 only
+ /// </summary>
+ [SerializeWhen(nameof(Version), VERSION_516, ComparisonOperator.GreaterThanOrEqual)]
+ [FieldOrder(9)] public uint MachineAddress { get; set; }
- #endregion
+ /// <summary>
+ /// Gets layer image start address
+ /// </summary>
+ [FieldOrder(10)] public uint LayerImageAddress { get; set; }
- #region Enums
- public enum LayerRleFormat
+ public override string ToString()
{
- PWS,
- PW0
+ 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(ExtraAddress)}: {ExtraAddress}, {nameof(MachineAddress)}: {MachineAddress}, {nameof(LayerImageAddress)}: {LayerImageAddress}";
}
-
- public enum AnyCubicMachine : byte
- {
- AnyCubicPhotonS,
- AnyCubicPhotonZero,
- AnyCubicPhotonX,
- AnyCubicPhotonUltra,
- AnyCubicPhotonMono,
- AnyCubicPhotonMonoSE,
- AnyCubicPhotonMono4K,
- AnyCubicPhotonMonoX,
- AnyCubicPhotonMonoX6K,
- AnyCubicPhotonMonoSQ,
- }
- #endregion
-
- #region Sub Classes
-
- #region FileMark
- public class FileMark
- {
- public const string SectionMarkFile = "ANYCUBIC";
-
- /// <summary>
- /// Gets the file mark placeholder
- /// Fixed to "ANYCUBIC"
- /// 00
- /// </summary>
- [FieldOrder(0)]
- [FieldLength(MarkSize)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string Mark { get; set; } = SectionMarkFile;
-
- /// <summary>
- /// Gets the file format version
- /// 0C
- /// </summary>
- [FieldOrder(1)] public uint Version { get; set; } = VERSION_1;
-
- /// <summary>
- /// Gets the area num
- /// 10, 4 for v1, 5 for v515, 8 for v516?
- /// </summary>
- [FieldOrder(2)] public uint AreaNum { get; set; }
-
- /// <summary>
- /// Gets the header start address
- /// 14
- /// </summary>
- [FieldOrder(3)] public uint HeaderAddress { get; set; }
-
- /// <summary>
- /// 18
- /// </summary>
- [FieldOrder(4)] public uint Padding1 { get; set; }
-
- /// <summary>
- /// Gets the preview start offset
- /// 1C
- /// </summary>
- [FieldOrder(5)] public uint PreviewAddress { get; set; }
-
- /// <summary>
- /// 20, Spotted on version 515 only
- /// </summary>
- [FieldOrder(6)] public uint PreviewEndAddress { get; set; }
-
- /// <summary>
- /// Gets the layer definition start address
- /// 24
- /// </summary>
- [FieldOrder(7)] public uint LayerDefinitionAddress { get; set; }
-
- /// <summary>
- /// 28, Spotted on version 516 only
- /// </summary>
- [FieldOrder(8)] public uint ExtraAddress { get; set; }
-
- /// <summary>
- /// 2C, Spotted on version 516 only
- /// </summary>
- [SerializeWhen(nameof(Version), VERSION_516, ComparisonOperator.GreaterThanOrEqual)]
- [FieldOrder(9)] public uint MachineAddress { get; set; }
-
- /// <summary>
- /// Gets layer image start address
- /// </summary>
- [FieldOrder(10)] public uint LayerImageAddress { get; set; }
-
- public override string ToString()
- {
- 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(ExtraAddress)}: {ExtraAddress}, {nameof(MachineAddress)}: {MachineAddress}, {nameof(LayerImageAddress)}: {LayerImageAddress}";
- }
- }
- #endregion
+ }
+ #endregion
+
+ #region Section
+
+ public class SectionHeader
+ {
+ /// <summary>
+ /// Gets the section mark placeholder
+ /// </summary>
+ [FieldOrder(0)]
+ [FieldLength(MarkSize)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string Mark { get; set; } = null!;
+
+ /// <summary>
+ /// Gets the length of this section
+ /// </summary>
+ [FieldOrder(1)] public uint Length { get; set; }
+
+ public SectionHeader() { }
- #region Section
+ public SectionHeader(string mark, object obj, int extraLength = 0) : this(mark)
+ {
+ //Debug.WriteLine(Helpers.Serializer.SizeOf(obj));
+ Length = (uint)(Helpers.Serializer.SizeOf(obj) + extraLength);
+ }
- public class SectionHeader
+ public SectionHeader(string mark, uint length = 0)
{
- /// <summary>
- /// Gets the section mark placeholder
- /// </summary>
- [FieldOrder(0)]
- [FieldLength(MarkSize)]
- [SerializeAs(SerializedType.TerminatedString)]
- public string Mark { get; set; }
+ Mark = mark;
+ Length = length;
+ }
- /// <summary>
- /// Gets the length of this section
- /// </summary>
- [FieldOrder(1)] public uint Length { get; set; }
- public SectionHeader() { }
+ public void Validate(string mark, object? obj = null)
+ {
+ Validate(mark, 0, obj);
+ }
- public SectionHeader(string mark, object obj, int extraLength = 0) : this(mark)
+ public void Validate(string mark, int length, object? obj = null)
+ {
+ if (!Mark.Equals(mark))
{
- //Debug.WriteLine(Helpers.Serializer.SizeOf(obj));
- Length = (uint)(Helpers.Serializer.SizeOf(obj) + extraLength);
+ throw new FileLoadException($"'{Mark}' section expected, but got '{mark}'");
}
- public SectionHeader(string mark, uint length = 0)
+ if (obj is not null)
{
- Mark = mark;
- Length = length;
+ length += (int)Helpers.Serializer.SizeOf(obj);
}
-
- public void Validate(string mark, object obj = null)
+ if (length > 0 && Length != length)
{
- Validate(mark, 0, obj);
+ throw new FileLoadException($"{Mark} section bytes: expected {Length}, got {length}, difference: {(int)Length - length}");
}
+ }
- public void Validate(string mark, int length, object obj = null)
- {
- if (!Mark.Equals(mark))
- {
- throw new FileLoadException($"'{Mark}' section expected, but got '{mark}'");
- }
+ public override string ToString() => $"[{nameof(Mark)}: {Mark}, {nameof(Length)}: {Length}]";
+ }
- if (obj is not null)
- {
- length += (int)Helpers.Serializer.SizeOf(obj);
- }
+ #endregion
- if (length > 0 && Length != length)
- {
- throw new FileLoadException($"{Mark} section bytes: expected {Length}, got {length}, difference: {(int)Length - length}");
- }
- }
+ #region Header
+ public class Header
+ {
+ public const string SectionMark = "HEADER";
- public override string ToString() => $"[{nameof(Mark)}: {Mark}, {nameof(Length)}: {Length}]";
- }
-
- #endregion
-
- #region Header
- public class Header
- {
- public const string SectionMark = "HEADER";
-
- /// <summary>
- /// 30
- /// </summary>
- [FieldOrder(0)] public SectionHeader Section { get; set; }
-
- /// <summary>
- /// 40
- /// </summary>
- [FieldOrder(1)] public float PixelSizeUm { get; set; } = 47.25f;
-
- /// <summary>
- /// Layer height in mm
- /// 44
- /// </summary>
- [FieldOrder(2)] public float LayerHeight { get; set; }
-
- /// <summary>
- /// 48
- /// </summary>
- [FieldOrder(3)] public float ExposureTime { get; set; }
-
- /// <summary>
- /// 4C
- /// </summary>
- [FieldOrder(4)] public float WaitTimeBeforeCure { get; set; } = 1;
-
- /// <summary>
- /// 50
- /// </summary>
- [FieldOrder(5)] public float BottomExposureTime { get; set; }
-
- /// <summary>
- /// 54
- /// </summary>
- [FieldOrder(6)] public float BottomLayersCount { get; set; }
-
- /// <summary>
- /// 58
- /// </summary>
- [FieldOrder(7)] public float LiftHeight { get; set; } = 6;
- /// <summary>
- /// Gets the lift speed in mm/s
- /// 5C
- /// </summary>
- [FieldOrder(8)] public float LiftSpeed { get; set; } = 3; // mm/s
-
- /// <summary>
- /// Gets the retract speed in mm/s
- /// 60
- /// </summary>
- [FieldOrder(9)] public float RetractSpeed { get; set; } = 3; // mm/s
-
- /// <summary>
- /// 64
- /// </summary>
- [FieldOrder(10)] public float VolumeMl { get; set; }
-
- /// <summary>
- /// 68
- /// </summary>
- [FieldOrder(11)] public uint AntiAliasing { get; set; } = 1;
-
- /// <summary>
- /// 6C
- /// </summary>
- [FieldOrder(12)] public uint ResolutionX { get; set; }
-
- /// <summary>
- /// 70
- /// </summary>
- [FieldOrder(13)] public uint ResolutionY { get; set; }
-
- /// <summary>
- /// 74
- /// </summary>
- [FieldOrder(14)] public float WeightG { get; set; }
-
- /// <summary>
- /// 78
- /// </summary>
- [FieldOrder(15)] public float Price { get; set; }
-
- /// <summary>
- /// 24 00 00 00 $ or ¥ C2 A5 00 or € = E2 82 AC 00
- /// 7C
- /// </summary>
- [FieldOrder(16)] public uint PriceCurrencyDec { get; set; } = 0x24;
- [Ignore] public char PriceCurrencySymbol
- {
- get
- {
- return PriceCurrencyDec switch
- {
- 0x24 => '$',
- 0xA5C2 => '¥',
- 0x000020AC => '€',
- _ => '$'
- };
- }
- }
+ /// <summary>
+ /// 30
+ /// </summary>
+ [FieldOrder(0)] public SectionHeader Section { get; set; }
+
+ /// <summary>
+ /// 40
+ /// </summary>
+ [FieldOrder(1)] public float PixelSizeUm { get; set; } = 47.25f;
+
+ /// <summary>
+ /// Layer height in mm
+ /// 44
+ /// </summary>
+ [FieldOrder(2)] public float LayerHeight { get; set; }
+
+ /// <summary>
+ /// 48
+ /// </summary>
+ [FieldOrder(3)] public float ExposureTime { get; set; }
+
+ /// <summary>
+ /// 4C
+ /// </summary>
+ [FieldOrder(4)] public float WaitTimeBeforeCure { get; set; } = 1;
+
+ /// <summary>
+ /// 50
+ /// </summary>
+ [FieldOrder(5)] public float BottomExposureTime { get; set; }
+
+ /// <summary>
+ /// 54
+ /// </summary>
+ [FieldOrder(6)] public float BottomLayersCount { get; set; }
- /// <summary>
- /// 80
- /// </summary>
- [FieldOrder(17)] public uint PerLayerOverride { get; set; } // bool
+ /// <summary>
+ /// 58
+ /// </summary>
+ [FieldOrder(7)] public float LiftHeight { get; set; } = 6;
+ /// <summary>
+ /// Gets the lift speed in mm/s
+ /// 5C
+ /// </summary>
+ [FieldOrder(8)] public float LiftSpeed { get; set; } = 3; // mm/s
- /// <summary>
- /// 84
- /// </summary>
- [FieldOrder(18)] public uint PrintTime { get; set; }
+ /// <summary>
+ /// Gets the retract speed in mm/s
+ /// 60
+ /// </summary>
+ [FieldOrder(9)] public float RetractSpeed { get; set; } = 3; // mm/s
- /// <summary>
- /// 88, spotted on 516
- /// </summary>
- [FieldOrder(19)] public uint TransitionLayerCount { get; set; }
+ /// <summary>
+ /// 64
+ /// </summary>
+ [FieldOrder(10)] public float VolumeMl { get; set; }
- /// <summary>
- /// 8C
- /// </summary>
- [FieldOrder(20)] public uint Padding2 { get; set; }
+ /// <summary>
+ /// 68
+ /// </summary>
+ [FieldOrder(11)] public uint AntiAliasing { get; set; } = 1;
- //[SerializeUntil((char)'P')] [FieldOrder(21)] public List<byte> Offset { get; set; } = new();
+ /// <summary>
+ /// 6C
+ /// </summary>
+ [FieldOrder(12)] public uint ResolutionX { get; set; }
- public Header()
- {
- Section = new SectionHeader(SectionMark, this);
- }
+ /// <summary>
+ /// 70
+ /// </summary>
+ [FieldOrder(13)] public uint ResolutionY { get; set; }
+ /// <summary>
+ /// 74
+ /// </summary>
+ [FieldOrder(14)] public float WeightG { get; set; }
- public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(PixelSizeUm)}: {PixelSizeUm}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(AntiAliasing)}: {AntiAliasing}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(WeightG)}: {WeightG}, {nameof(Price)}: {Price}, {nameof(PriceCurrencyDec)}: {PriceCurrencyDec}, {nameof(PerLayerOverride)}: {PerLayerOverride}, {nameof(PrintTime)}: {PrintTime}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(Padding2)}: {Padding2}";
+ /// <summary>
+ /// 78
+ /// </summary>
+ [FieldOrder(15)] public float Price { get; set; }
- public void Validate(int offset = 0)
+ /// <summary>
+ /// 24 00 00 00 $ or ¥ C2 A5 00 or € = E2 82 AC 00
+ /// 7C
+ /// </summary>
+ [FieldOrder(16)] public uint PriceCurrencyDec { get; set; } = 0x24;
+ [Ignore] public char PriceCurrencySymbol
+ {
+ get
{
- Section.Validate(SectionMark, (int)-Helpers.Serializer.SizeOf(Section)+offset, this);
+ return PriceCurrencyDec switch
+ {
+ 0x24 => '$',
+ 0xA5C2 => '¥',
+ 0x000020AC => '€',
+ _ => '$'
+ };
}
}
- #endregion
+ /// <summary>
+ /// 80
+ /// </summary>
+ [FieldOrder(17)] public uint PerLayerOverride { get; set; } // bool
- #region Preview
+ /// <summary>
+ /// 84
+ /// </summary>
+ [FieldOrder(18)] public uint PrintTime { get; set; }
/// <summary>
- /// 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.
+ /// 88, spotted on 516
/// </summary>
- public class Preview
- {
- public const string SectionMark = "PREVIEW";
+ [FieldOrder(19)] public uint TransitionLayerCount { get; set; }
+
+ /// <summary>
+ /// 8C
+ /// </summary>
+ [FieldOrder(20)] public uint Padding2 { get; set; }
- /// <summary>
- /// 90
- /// </summary>
- [FieldOrder(0)] public SectionHeader Section { get; set; }
+ //[SerializeUntil((char)'P')] [FieldOrder(21)] public List<byte> Offset { get; set; } = new();
- /// <summary>
- /// Gets the image width, in pixels.
- /// A0
- /// </summary>
- [FieldOrder(1)] public uint ResolutionX { get; set; } = 224;
+ public Header()
+ {
+ Section = new SectionHeader(SectionMark, this);
+ }
- /// <summary>
- /// Gets the resolution of the image, in dpi.
- /// A4
- /// </summary>
- [FieldOrder(2)] public uint DpiResolution { get; set; } = 42;
- /// <summary>
- /// Gets the image height, in pixels.
- /// A8
- /// </summary>
- [FieldOrder(3)] public uint ResolutionY { get; set; } = 168;
+ public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(PixelSizeUm)}: {PixelSizeUm}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}, {nameof(BottomExposureTime)}: {BottomExposureTime}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(AntiAliasing)}: {AntiAliasing}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(WeightG)}: {WeightG}, {nameof(Price)}: {Price}, {nameof(PriceCurrencyDec)}: {PriceCurrencyDec}, {nameof(PerLayerOverride)}: {PerLayerOverride}, {nameof(PrintTime)}: {PrintTime}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(Padding2)}: {Padding2}";
- /*[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; }*/
+ public void Validate(int offset = 0)
+ {
+ Section.Validate(SectionMark, (int)-Helpers.Serializer.SizeOf(Section)+offset, this);
+ }
+ }
- [Ignore] public uint DataSize => ResolutionX * ResolutionY * 2;
+ #endregion
- // little-endian 16bit colors, RGB 565 encoded.
- //[FieldOrder(4)] [FieldLength(nameof(Section)+"."+nameof(SectionHeader.Length))]
- [Ignore] public byte[] Data { get; set; }
+ #region Preview
- public Preview()
- {
- Section = new SectionHeader(SectionMark, this);
- }
+ /// <summary>
+ /// 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.
+ /// </summary>
+ public class Preview
+ {
+ public const string SectionMark = "PREVIEW";
- public Preview(uint resolutionX, uint resolutionY, uint dpiResolution = 42) : this()
- {
- ResolutionX = resolutionX;
- ResolutionY = resolutionY;
- DpiResolution = dpiResolution;
- Data = new byte[DataSize];
- Section.Length += (uint)Data.Length;
- }
+ /// <summary>
+ /// 90
+ /// </summary>
+ [FieldOrder(0)] public SectionHeader Section { get; set; }
- /*public unsafe Mat Decode(bool consumeData = true)
- {
- Mat image = new(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
- var span = image.GetBytePointer();
+ /// <summary>
+ /// Gets the image width, in pixels.
+ /// A0
+ /// </summary>
+ [FieldOrder(1)] public uint ResolutionX { get; set; } = 224;
- int pixel = 0;
- for (uint i = 0; i < Data.Length; i += 2)
- {
- ushort color16 = BitExtensions.ToUShortLittleEndian(Data[i], Data[i+1]);
- byte r = (byte)((color16 >> 11) & 0x1f);
- byte g = (byte)((color16 >> 5) & 0x3f);
- byte b = (byte)((color16 >> 0) & 0x1f);
-
- span[pixel++] = (byte) ((b << 3) | (b & 0x7));
- span[pixel++] = (byte) ((g << 2) | (g & 0x3));
- span[pixel++] = (byte) ((r << 3) | (r & 0x7));
- }
+ /// <summary>
+ /// Gets the resolution of the image, in dpi.
+ /// A4
+ /// </summary>
+ [FieldOrder(2)] public uint DpiResolution { get; set; } = 42;
- if (consumeData)
- Data = null;
+ /// <summary>
+ /// Gets the image height, in pixels.
+ /// A8
+ /// </summary>
+ [FieldOrder(3)] public uint ResolutionY { get; set; } = 168;
- return image;
- }
+ /*[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; }*/
- public static unsafe Preview Encode(Mat image)
- {
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ [Ignore] public uint DataSize => ResolutionX * ResolutionY * 2;
- Preview preview = new((uint) image.Width, (uint) image.Height);
+ // little-endian 16bit colors, RGB 565 encoded.
+ //[FieldOrder(4)] [FieldLength(nameof(Section)+"."+nameof(SectionHeader.Length))]
+ [Ignore] public byte[] Data { get; set; } = null!;
- int i = 0;
- for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels)
- {
- // BGR
- byte b = (byte)(span[pixel] >> 3);
- byte g = (byte)(span[pixel+1] >> 2);
- byte r = (byte)(span[pixel+2] >> 3);
-
+ public Preview()
+ {
+ Section = new SectionHeader(SectionMark, this);
+ }
- ushort color = (ushort) ((r << 11) | (g << 5) | (b << 0));
+ public Preview(uint resolutionX, uint resolutionY, uint dpiResolution = 42) : this()
+ {
+ ResolutionX = resolutionX;
+ ResolutionY = resolutionY;
+ DpiResolution = dpiResolution;
+ Data = new byte[DataSize];
+ Section.Length += (uint)Data.Length;
+ }
- preview.Data[i++] = (byte) color;
- preview.Data[i++] = (byte) (color >> 8);
- }
+ /*public unsafe Mat Decode(bool consumeData = true)
+ {
+ Mat image = new(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3);
+ var span = image.GetBytePointer();
- return preview;
- }*/
-
- public override string ToString()
+ int pixel = 0;
+ for (uint i = 0; i < Data.Length; i += 2)
{
- return $"{nameof(Section)}: {Section}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(DpiResolution)}: {DpiResolution}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Data)}: {Data?.Length ?? 0}";
- }
+ ushort color16 = BitExtensions.ToUShortLittleEndian(Data[i], Data[i+1]);
+ byte r = (byte)((color16 >> 11) & 0x1f);
+ byte g = (byte)((color16 >> 5) & 0x3f);
+ byte b = (byte)((color16 >> 0) & 0x1f);
- public void Validate(int size)
- {
- Section.Validate(SectionMark, (int) (size - Helpers.Serializer.SizeOf(Section)), this);
+ span[pixel++] = (byte) ((b << 3) | (b & 0x7));
+ span[pixel++] = (byte) ((g << 2) | (g & 0x3));
+ span[pixel++] = (byte) ((r << 3) | (r & 0x7));
}
+
+ if (consumeData)
+ Data = null;
+
+ return image;
}
- #endregion
+ public static unsafe Preview Encode(Mat image)
+ {
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
+
+ Preview preview = new((uint) image.Width, (uint) image.Height);
+
+ int i = 0;
+ for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels)
+ {
+ // BGR
+ byte b = (byte)(span[pixel] >> 3);
+ byte g = (byte)(span[pixel+1] >> 2);
+ byte r = (byte)(span[pixel+2] >> 3);
+
+
+ ushort color = (ushort) ((r << 11) | (g << 5) | (b << 0));
- #region Layer
+ preview.Data[i++] = (byte) color;
+ preview.Data[i++] = (byte) (color >> 8);
+ }
- public class LayerDef
+ return preview;
+ }*/
+
+ public override string ToString()
{
- public const byte ClassSize = 32;
- /// <summary>
- /// Gets the layer image offset to encoded layer data, and its length in bytes.
- /// </summary>
- [FieldOrder(0)] public uint DataAddress { get; set; }
+ return $"{nameof(Section)}: {Section}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(DpiResolution)}: {DpiResolution}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Data)}: {Data?.Length ?? 0}";
+ }
- /// <summary>
- /// Gets the layer image length in bytes.
- /// </summary>
- [FieldOrder(1)] public uint DataLength { get; set; }
+ public void Validate(int size)
+ {
+ Section.Validate(SectionMark, (int) (size - Helpers.Serializer.SizeOf(Section)), this);
+ }
+ }
- [FieldOrder(2)] public float LiftHeight { get; set; }
+ #endregion
- [FieldOrder(3)] public float LiftSpeed { get; set; }
+ #region Layer
- /// <summary>
- /// Gets the exposure time for this layer, in seconds.
- /// </summary>
- [FieldOrder(4)] public float ExposureTime { get; set; }
+ public class LayerDef
+ {
+ public const byte ClassSize = 32;
+ /// <summary>
+ /// Gets the layer image offset to encoded layer data, and its length in bytes.
+ /// </summary>
+ [FieldOrder(0)] public uint DataAddress { get; set; }
- /// <summary>
- /// Gets the layer height for this layer, measured in millimeters.
- /// </summary>
- [FieldOrder(5)] public float LayerHeight { get; set; }
+ /// <summary>
+ /// Gets the layer image length in bytes.
+ /// </summary>
+ [FieldOrder(1)] public uint DataLength { get; set; }
- [FieldOrder(6)] public uint NonZeroPixelCount { get; set; }
- [FieldOrder(7)] public uint Padding1 { get; set; }
+ [FieldOrder(2)] public float LiftHeight { get; set; }
- [Ignore] public byte[] EncodedRle { get; set; }
- [Ignore] public PhotonWorkshopFile Parent { get; set; }
+ [FieldOrder(3)] public float LiftSpeed { get; set; }
- public LayerDef()
- {
- }
+ /// <summary>
+ /// Gets the exposure time for this layer, in seconds.
+ /// </summary>
+ [FieldOrder(4)] public float ExposureTime { get; set; }
- public LayerDef(PhotonWorkshopFile parent, Layer layer)
- {
- Parent = parent;
- SetFrom(layer);
- }
+ /// <summary>
+ /// Gets the layer height for this layer, measured in millimeters.
+ /// </summary>
+ [FieldOrder(5)] public float LayerHeight { get; set; }
- public void SetFrom(Layer layer)
- {
- LayerHeight = layer.RelativePositionZ;
- ExposureTime = layer.ExposureTime;
- LiftHeight = layer.LiftHeight;
- LiftSpeed = (float) Math.Round(layer.LiftSpeed / 60, 2);
- NonZeroPixelCount = layer.NonZeroPixelCount;
- }
+ [FieldOrder(6)] public uint NonZeroPixelCount { get; set; }
+ [FieldOrder(7)] public uint Padding1 { get; set; }
- public void CopyTo(Layer layer)
- {
- // Don't forget to compute LayerHeight outside here
- layer.ExposureTime = ExposureTime;
- layer.LiftHeight = LiftHeight;
- layer.LiftSpeed = (float)Math.Round(LiftSpeed * 60, 2);
- }
+ [Ignore] public byte[] EncodedRle { get; set; } = null!;
+ [Ignore] public PhotonWorkshopFile Parent { get; set; } = null!;
- public Mat Decode(bool consumeData = true)
- {
- var result = Parent.LayerImageFormat == LayerRleFormat.PWS ? DecodePWS() : DecodePW0();
- if (consumeData)
- EncodedRle = null;
+ public LayerDef()
+ {
+ }
- return result;
- }
+ public LayerDef(PhotonWorkshopFile parent, Layer layer)
+ {
+ Parent = parent;
+ SetFrom(layer);
+ }
- public byte[] Encode(Mat image)
- {
- EncodedRle = Parent.LayerImageFormat == LayerRleFormat.PWS ? EncodePWS(image) : EncodePW0(image);
- return EncodedRle;
- }
+ public void SetFrom(Layer layer)
+ {
+ LayerHeight = layer.RelativePositionZ;
+ ExposureTime = layer.ExposureTime;
+ LiftHeight = layer.LiftHeight;
+ LiftSpeed = (float) Math.Round(layer.LiftSpeed / 60, 2);
+ NonZeroPixelCount = layer.NonZeroPixelCount;
+ }
- private unsafe Mat DecodePWS()
- {
- var image = EmguExtensions.InitMat(Parent.Resolution);
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ public void CopyTo(Layer layer)
+ {
+ // Don't forget to compute LayerHeight outside here
+ layer.ExposureTime = ExposureTime;
+ layer.LiftHeight = LiftHeight;
+ layer.LiftSpeed = (float)Math.Round(LiftSpeed * 60, 2);
+ }
- int index = 0;
- for (byte bit = 0; bit < Parent.AntiAliasing; bit++)
- {
- int pixel = 0;
- for (; index < EncodedRle.Length; index++)
- {
- // Lower 7 bits is the repeat count for the bit (0..127)
- int reps = EncodedRle[index] & 0x7f;
+ public Mat Decode(bool consumeData = true)
+ {
+ var result = Parent.LayerImageFormat == LayerRleFormat.PWS ? DecodePWS() : DecodePW0();
+ if (consumeData)
+ EncodedRle = null!;
- // We only need to set the non-zero pixels
- // High bit is on for white, off for black
- if ((EncodedRle[index] & 0x80) != 0)
- {
- for (int i = 0; i < reps; i++)
- {
- span[pixel + i]++;
- }
- }
+ return result;
+ }
- pixel += reps;
+ public byte[] Encode(Mat image)
+ {
+ EncodedRle = Parent.LayerImageFormat == LayerRleFormat.PWS ? EncodePWS(image) : EncodePW0(image);
+ return EncodedRle;
+ }
- if (pixel == imageLength)
- {
- index++;
- break;
- }
+ private unsafe Mat DecodePWS()
+ {
+ var image = EmguExtensions.InitMat(Parent.Resolution);
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
+
+ int index = 0;
+ for (byte bit = 0; bit < Parent.AntiAliasing; bit++)
+ {
+ int pixel = 0;
+ for (; index < EncodedRle.Length; index++)
+ {
+ // Lower 7 bits is the repeat count for the bit (0..127)
+ int reps = EncodedRle[index] & 0x7f;
- if (pixel > imageLength)
+ // We only need to set the non-zero pixels
+ // High bit is on for white, off for black
+ if ((EncodedRle[index] & 0x80) != 0)
+ {
+ for (int i = 0; i < reps; i++)
{
- image.Dispose();
- throw new FileLoadException("Error image ran off the end");
+ span[pixel + i]++;
}
}
- }
- for (int i = 0; i < imageLength; i++)
- {
- int newC = span[i] * (256 / Parent.AntiAliasing);
+ pixel += reps;
+
+ if (pixel == imageLength)
+ {
+ index++;
+ break;
+ }
- if (newC > 0)
+ if (pixel > imageLength)
{
- newC--;
+ image.Dispose();
+ throw new FileLoadException("Error image ran off the end");
}
+ }
+ }
- span[i] = (byte)newC;
+ for (int i = 0; i < imageLength; i++)
+ {
+ int newC = span[i] * (256 / Parent.AntiAliasing);
+
+ if (newC > 0)
+ {
+ newC--;
}
- return image;
+ span[i] = (byte)newC;
}
- public unsafe byte[] EncodePWS(Mat image)
- {
- List<byte> rawData = new();
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ return image;
+ }
- bool obit;
- int rep;
+ public unsafe byte[] EncodePWS(Mat image)
+ {
+ List<byte> rawData = new();
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
- void AddRep()
- {
- if (rep <= 0) return;
+ bool obit;
+ int rep;
- byte by = (byte)rep;
+ void AddRep()
+ {
+ if (rep <= 0) return;
- if (obit)
- {
- by |= 0x80;
- //bitsOn += uint(rep)
- }
+ byte by = (byte)rep;
- rawData.Add(by);
+ if (obit)
+ {
+ by |= 0x80;
+ //bitsOn += uint(rep)
}
- for (byte aalevel = 1; aalevel <= Parent.AntiAliasing; aalevel++)
- {
- obit = false;
- rep = 0;
+ rawData.Add(by);
+ }
- //ngrey:= uint16(r | g | b)
- // thresholds:
- // aa 1: 127
- // aa 2: 255 127
- // aa 4: 255 191 127 63
- // aa 8: 255 223 191 159 127 95 63 31
- //byte threshold = (byte)(256 / Parent.AntiAliasing * aalevel - 1);
- // threshold := byte(int(255 * (level + 1) / (levels + 1))) + 1
- byte threshold = (byte) (255 * aalevel / (Parent.AntiAliasing + 1) + 1);
+ for (byte aalevel = 1; aalevel <= Parent.AntiAliasing; aalevel++)
+ {
+ obit = false;
+ rep = 0;
+ //ngrey:= uint16(r | g | b)
+ // thresholds:
+ // aa 1: 127
+ // aa 2: 255 127
+ // aa 4: 255 191 127 63
+ // aa 8: 255 223 191 159 127 95 63 31
+ //byte threshold = (byte)(256 / Parent.AntiAliasing * aalevel - 1);
+ // threshold := byte(int(255 * (level + 1) / (levels + 1))) + 1
+ byte threshold = (byte) (255 * aalevel / (Parent.AntiAliasing + 1) + 1);
- for (int pixel = 0; pixel < imageLength; pixel++)
- {
- var nbit = span[pixel] >= threshold;
- if (nbit == obit)
- {
- rep++;
+ for (int pixel = 0; pixel < imageLength; pixel++)
+ {
+ var nbit = span[pixel] >= threshold;
- if (rep == RLE1EncodingLimit)
- {
- AddRep();
- rep = 0;
- }
- }
- else
+ if (nbit == obit)
+ {
+ rep++;
+
+ if (rep == RLE1EncodingLimit)
{
AddRep();
- obit = nbit;
- rep = 1;
+ rep = 0;
}
}
-
- // Collect stragglers
- AddRep();
+ else
+ {
+ AddRep();
+ obit = nbit;
+ rep = 1;
+ }
}
- DataLength = (uint) rawData.Count;
-
- return rawData.ToArray();
+ // Collect stragglers
+ AddRep();
}
- private Mat DecodePW0()
- {
- var mat = EmguExtensions.InitMat(Parent.Resolution);
- var imageLength = mat.GetLength();
+ DataLength = (uint) rawData.Count;
+
+ return rawData.ToArray();
+ }
+
+ private Mat DecodePW0()
+ {
+ var mat = EmguExtensions.InitMat(Parent.Resolution);
+ var imageLength = mat.GetLength();
- int pixelPos = 0;
- for (int i = 0; i < EncodedRle.Length; i++)
+ int pixelPos = 0;
+ for (int i = 0; i < EncodedRle.Length; i++)
+ {
+ byte b = EncodedRle[i];
+ int code = b >> 4;
+ int repeat = b & 0xf;
+ byte color;
+ switch (code)
{
- byte b = EncodedRle[i];
- int code = b >> 4;
- int repeat = b & 0xf;
- byte color;
- switch (code)
- {
- case 0x0:
- color = 0;
- i++;
- //reps = reps * 256 + EncodedRle[i];
- if (i >= EncodedRle.Length)
- {
- repeat = imageLength - pixelPos;
- break;
- }
-
- repeat = (repeat << 8) + EncodedRle[i];
- break;
- case 0xf:
- color = 255;
- i++;
- //reps = reps * 256 + EncodedRle[i];
- if (i >= EncodedRle.Length)
- {
- repeat = imageLength - pixelPos;
- break;
- }
-
- repeat = (repeat << 8) + EncodedRle[i];
+ case 0x0:
+ color = 0;
+ i++;
+ //reps = reps * 256 + EncodedRle[i];
+ if (i >= EncodedRle.Length)
+ {
+ repeat = imageLength - pixelPos;
break;
- default:
- color = (byte) ((code << 4) | code);
- if (i >= EncodedRle.Length)
- {
- repeat = imageLength - pixelPos;
- }
+ }
+
+ repeat = (repeat << 8) + EncodedRle[i];
+ break;
+ case 0xf:
+ color = 255;
+ i++;
+ //reps = reps * 256 + EncodedRle[i];
+ if (i >= EncodedRle.Length)
+ {
+ repeat = imageLength - pixelPos;
break;
- }
+ }
- //color &= 0xff;
+ repeat = (repeat << 8) + EncodedRle[i];
+ break;
+ default:
+ color = (byte) ((code << 4) | code);
+ if (i >= EncodedRle.Length)
+ {
+ repeat = imageLength - pixelPos;
+ }
+ break;
+ }
- // We only need to set the non-zero pixels
- mat.FillSpan(ref pixelPos, repeat, color);
+ //color &= 0xff;
+ // We only need to set the non-zero pixels
+ mat.FillSpan(ref pixelPos, repeat, color);
- if (pixelPos == imageLength)
- {
- //i++;
- break;
- }
- if (pixelPos > imageLength)
- {
- mat.Dispose();
- throw new FileLoadException($"Error image ran off the end: {pixelPos - repeat}({repeat}) of {imageLength}");
- }
+ if (pixelPos == imageLength)
+ {
+ //i++;
+ break;
}
- if (pixelPos > 0 && pixelPos != imageLength)
+ if (pixelPos > imageLength)
{
mat.Dispose();
- throw new FileLoadException($"Error image ended short: {pixelPos} of {imageLength}");
+ throw new FileLoadException($"Error image ran off the end: {pixelPos - repeat}({repeat}) of {imageLength}");
}
-
- return mat;
}
- public unsafe byte[] EncodePW0(Mat image)
+ if (pixelPos > 0 && pixelPos != imageLength)
{
- List<byte> rawData = new();
- var span = image.GetBytePointer();
- var imageLength = image.GetLength();
+ mat.Dispose();
+ throw new FileLoadException($"Error image ended short: {pixelPos} of {imageLength}");
+ }
+
+ return mat;
+ }
+
+ public unsafe byte[] EncodePW0(Mat image)
+ {
+ List<byte> rawData = new();
+ var span = image.GetBytePointer();
+ var imageLength = image.GetLength();
- int lastColor = -1;
- int reps = 0;
+ int lastColor = -1;
+ int reps = 0;
- void PutReps()
+ void PutReps()
+ {
+ while (reps > 0)
{
- while (reps > 0)
- {
- int done = reps;
+ int done = reps;
- if (lastColor is 0 or 0xf)
- {
- if (done > RLE4EncodingLimit)
- {
- done = RLE4EncodingLimit;
- }
- //more:= []byte{ 0, 0}
- //binary.BigEndian.PutUint16(more, uint16(done | (color << 12)))
-
- //rle = append(rle, more...)
-
- ushort more = (ushort)(done | (lastColor << 12));
- rawData.Add((byte)(more >> 8));
- rawData.Add((byte)more);
- }
- else
+ if (lastColor is 0 or 0xf)
+ {
+ if (done > RLE4EncodingLimit)
{
- if (done > 0xf)
- {
- done = 0xf;
- }
- rawData.Add((byte)(done | lastColor << 4));
+ done = RLE4EncodingLimit;
}
+ //more:= []byte{ 0, 0}
+ //binary.BigEndian.PutUint16(more, uint16(done | (color << 12)))
- reps -= done;
- }
- }
-
- for (int i = 0; i < imageLength; i++)
- {
- int color = span[i] >> 4;
+ //rle = append(rle, more...)
- if (color == lastColor)
- {
- reps++;
+ ushort more = (ushort)(done | (lastColor << 12));
+ rawData.Add((byte)(more >> 8));
+ rawData.Add((byte)more);
}
else
{
- PutReps();
- lastColor = color;
- reps = 1;
+ if (done > 0xf)
+ {
+ done = 0xf;
+ }
+ rawData.Add((byte)(done | lastColor << 4));
}
- }
-
- PutReps();
- EncodedRle = rawData.ToArray();
- DataLength = (uint)rawData.Count;
-
- ushort crc = CRCRle4(EncodedRle);
- rawData.Add((byte)(crc >> 8));
- rawData.Add((byte)crc);
-
- return EncodedRle;
+ reps -= done;
+ }
}
- public static ushort CRCRle4(byte[] data)
+ for (int i = 0; i < imageLength; i++)
{
- ushort crc16 = 0;
- for (int i = 0; i < data.Length; i++)
+ int color = span[i] >> 4;
+
+ if (color == lastColor)
+ {
+ reps++;
+ }
+ else
{
- crc16 = (ushort) ((crc16 << 8) ^ CRC16Table[((crc16 >> 8) ^ CRC16Table[data[i]]) & 0xff]);
+ PutReps();
+ lastColor = color;
+ reps = 1;
}
+ }
- crc16 = (ushort) ((CRC16Table[crc16 & 0xff] * 0x100) + CRC16Table[(crc16 >> 8) & 0xff]);
+ PutReps();
- return crc16;
- }
+ EncodedRle = rawData.ToArray();
+ DataLength = (uint)rawData.Count;
- public ushort CRCEncodedRle()
- {
- return CRCRle4(EncodedRle);
- }
+ ushort crc = CRCRle4(EncodedRle);
+ rawData.Add((byte)(crc >> 8));
+ rawData.Add((byte)crc);
+
+ return EncodedRle;
+ }
- public override string ToString()
+ public static ushort CRCRle4(byte[] data)
+ {
+ ushort crc16 = 0;
+ for (int i = 0; i < data.Length; i++)
{
- return $"{nameof(DataAddress)}: {DataAddress}, {nameof(DataLength)}: {DataLength}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, {nameof(Padding1)}: {Padding1}, {nameof(EncodedRle)}: {EncodedRle?.Length ?? 0}";
+ crc16 = (ushort) ((crc16 << 8) ^ CRC16Table[((crc16 >> 8) ^ CRC16Table[data[i]]) & 0xff]);
}
+
+ crc16 = (ushort) ((CRC16Table[crc16 & 0xff] * 0x100) + CRC16Table[(crc16 >> 8) & 0xff]);
+
+ return crc16;
}
- #endregion
+ public ushort CRCEncodedRle()
+ {
+ return CRCRle4(EncodedRle);
+ }
- #region LayerDefinition
- public class LayerDefinition
+ public override string ToString()
{
- public const string SectionMark = "LAYERDEF";
+ return $"{nameof(DataAddress)}: {DataAddress}, {nameof(DataLength)}: {DataLength}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, {nameof(Padding1)}: {Padding1}, {nameof(EncodedRle)}: {EncodedRle?.Length ?? 0}";
+ }
+ }
- /// <summary>
- /// 1269C
- /// </summary>
- [FieldOrder(0)] public SectionHeader Section { get; set; }
+ #endregion
- [FieldOrder(1)] public uint LayerCount { get; set; }
+ #region LayerDefinition
+ public class LayerDefinition
+ {
+ public const string SectionMark = "LAYERDEF";
- [Ignore] public LayerDef[] Layers { get; set; }
+ /// <summary>
+ /// 1269C
+ /// </summary>
+ [FieldOrder(0)] public SectionHeader Section { get; set; }
- public LayerDefinition()
- {
- Section = new SectionHeader(SectionMark, this);
- }
+ [FieldOrder(1)] public uint LayerCount { get; set; }
- public LayerDefinition(uint layerCount) : this()
- {
- LayerCount = layerCount;
- Layers = new LayerDef[layerCount];
- Section.Length += (uint) Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount;
- }
+ [Ignore] public LayerDef[] Layers { get; set; } = null!;
- [Ignore]
- public LayerDef this[uint index]
- {
- get => Layers[index];
- set => Layers[index] = value;
- }
+ public LayerDefinition()
+ {
+ Section = new SectionHeader(SectionMark, this);
+ }
- [Ignore]
- public LayerDef this[int index]
- {
- get => Layers[index];
- set => Layers[index] = value;
- }
+ public LayerDefinition(uint layerCount) : this()
+ {
+ LayerCount = layerCount;
+ Layers = new LayerDef[layerCount];
+ Section.Length += (uint) Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount;
+ }
- public void Validate()
- {
- Section.Validate(SectionMark, (int) (LayerCount * Helpers.Serializer.SizeOf(new LayerDef()) - Helpers.Serializer.SizeOf(Section)), this);
- }
+ [Ignore]
+ public LayerDef this[uint index]
+ {
+ get => Layers[index];
+ set => Layers[index] = value;
+ }
- public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(LayerCount)}: {LayerCount}";
- }
- #endregion
-
- #region Extra
- public class Extra
- {
- public const string SectionMark = "EXTRA";
-
- //[FieldOrder(0)] public SectionHeader Section { get; set; }
- [FieldOrder(0)] [FieldLength(MarkSize)] [SerializeAs(SerializedType.TerminatedString)] public string Marker { get; set; }
- [FieldOrder(1)] public uint Unknown0 { get; set; } = 24;
- [FieldOrder(2)] public uint Unknown1 { get; set; } = 2;
- [FieldOrder(3)] public float BottomLiftHeight1 { get; set; }
- [FieldOrder(4)] public float BottomLiftSpeed1 { get; set; }
- [FieldOrder(5)] public float BottomRetractSpeed1 { get; set; }
- [FieldOrder(6)] public float BottomLiftHeight2 { get; set; }
- [FieldOrder(7)] public float BottomLiftSpeed2 { get; set; }
- [FieldOrder(8)] public float BottomRetractSpeed2 { get; set; }
- [FieldOrder(9)] public uint Unknown2 { get; set; } = 2;
- [FieldOrder(10)] public float LiftHeight1 { get; set; }
- [FieldOrder(11)] public float LiftSpeed1 { get; set; }
- [FieldOrder(12)] public float RetractSpeed1 { get; set; }
- [FieldOrder(13)] public float LiftHeight2 { get; set; }
- [FieldOrder(14)] public float LiftSpeed2 { get; set; }
- [FieldOrder(15)] public float RetractSpeed2 { get; set; }
-
- public Extra()
- {
- //Section = new SectionHeader(SectionMark, this);
- }
+ [Ignore]
+ public LayerDef this[int index]
+ {
+ get => Layers[index];
+ set => Layers[index] = value;
+ }
- public override string ToString()
- {
- return $"{nameof(Marker)}: {Marker}, {nameof(Unknown0)}: {Unknown0}, {nameof(Unknown1)}: {Unknown1}, {nameof(BottomLiftHeight1)}: {BottomLiftHeight1}, {nameof(BottomLiftSpeed1)}: {BottomLiftSpeed1}, {nameof(BottomRetractSpeed1)}: {BottomRetractSpeed1}, {nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Unknown2)}: {Unknown2}, {nameof(LiftHeight1)}: {LiftHeight1}, {nameof(LiftSpeed1)}: {LiftSpeed1}, {nameof(RetractSpeed1)}: {RetractSpeed1}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractSpeed2)}: {RetractSpeed2}";
- }
+ public void Validate()
+ {
+ Section.Validate(SectionMark, (int) (LayerCount * Helpers.Serializer.SizeOf(new LayerDef()) - Helpers.Serializer.SizeOf(Section)), this);
+ }
- public void Validate()
- {
- //Section.Validate(SectionMark, (int)-Helpers.Serializer.SizeOf(Section), this);
- }
+ public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(LayerCount)}: {LayerCount}";
+ }
+ #endregion
+
+ #region Extra
+ public class Extra
+ {
+ public const string SectionMark = "EXTRA";
+
+ //[FieldOrder(0)] public SectionHeader Section { get; set; }
+ [FieldOrder(0)] [FieldLength(MarkSize)] [SerializeAs(SerializedType.TerminatedString)] public string Marker { get; set; } = null!;
+ [FieldOrder(1)] public uint Unknown0 { get; set; } = 24;
+ [FieldOrder(2)] public uint Unknown1 { get; set; } = 2;
+ [FieldOrder(3)] public float BottomLiftHeight1 { get; set; }
+ [FieldOrder(4)] public float BottomLiftSpeed1 { get; set; }
+ [FieldOrder(5)] public float BottomRetractSpeed1 { get; set; }
+ [FieldOrder(6)] public float BottomLiftHeight2 { get; set; }
+ [FieldOrder(7)] public float BottomLiftSpeed2 { get; set; }
+ [FieldOrder(8)] public float BottomRetractSpeed2 { get; set; }
+ [FieldOrder(9)] public uint Unknown2 { get; set; } = 2;
+ [FieldOrder(10)] public float LiftHeight1 { get; set; }
+ [FieldOrder(11)] public float LiftSpeed1 { get; set; }
+ [FieldOrder(12)] public float RetractSpeed1 { get; set; }
+ [FieldOrder(13)] public float LiftHeight2 { get; set; }
+ [FieldOrder(14)] public float LiftSpeed2 { get; set; }
+ [FieldOrder(15)] public float RetractSpeed2 { get; set; }
+
+ public Extra()
+ {
+ //Section = new SectionHeader(SectionMark, this);
}
- #endregion
- #region Machine
- public class Machine
+ public override string ToString()
{
- public const string SectionMark = "MACHINE";
+ return $"{nameof(Marker)}: {Marker}, {nameof(Unknown0)}: {Unknown0}, {nameof(Unknown1)}: {Unknown1}, {nameof(BottomLiftHeight1)}: {BottomLiftHeight1}, {nameof(BottomLiftSpeed1)}: {BottomLiftSpeed1}, {nameof(BottomRetractSpeed1)}: {BottomRetractSpeed1}, {nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Unknown2)}: {Unknown2}, {nameof(LiftHeight1)}: {LiftHeight1}, {nameof(LiftSpeed1)}: {LiftSpeed1}, {nameof(RetractSpeed1)}: {RetractSpeed1}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractSpeed2)}: {RetractSpeed2}";
+ }
- [FieldOrder(0)] public SectionHeader Section { get; set; }
- [FieldOrder(1)] [FieldLength(96)] [SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; }
- [FieldOrder(2)] [FieldLength(24)] [SerializeAs(SerializedType.TerminatedString)] public string LayerImageFormat { get; set; } = "pw0img";
- [FieldOrder(3)] public float DisplayWidth { get; set; }
- [FieldOrder(4)] public float DisplayHeight { get; set; }
- [FieldOrder(5)] public float MachineZ { get; set; }
- [FieldOrder(6)] public uint Version { get; set; } = VERSION_516;
- [FieldOrder(7)] public uint Unknown { get; set; } = 6506241;
+ public void Validate()
+ {
+ //Section.Validate(SectionMark, (int)-Helpers.Serializer.SizeOf(Section), this);
+ }
+ }
+ #endregion
- public override string ToString()
- {
- return $"{nameof(Section)}: {Section}, {nameof(MachineName)}: {MachineName}, {nameof(LayerImageFormat)}: {LayerImageFormat}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(MachineZ)}: {MachineZ}, {nameof(Version)}: {Version}, {nameof(Unknown)}: {Unknown}";
- }
+ #region Machine
+ public class Machine
+ {
+ public const string SectionMark = "MACHINE";
+
+ [FieldOrder(0)] public SectionHeader Section { get; set; }
+ [FieldOrder(1)] [FieldLength(96)] [SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; } = null!;
+ [FieldOrder(2)] [FieldLength(24)] [SerializeAs(SerializedType.TerminatedString)] public string LayerImageFormat { get; set; } = "pw0img";
+ [FieldOrder(3)] public float DisplayWidth { get; set; }
+ [FieldOrder(4)] public float DisplayHeight { get; set; }
+ [FieldOrder(5)] public float MachineZ { get; set; }
+ [FieldOrder(6)] public uint Version { get; set; } = VERSION_516;
+ [FieldOrder(7)] public uint Unknown { get; set; } = 6506241;
+
+ public override string ToString()
+ {
+ return $"{nameof(Section)}: {Section}, {nameof(MachineName)}: {MachineName}, {nameof(LayerImageFormat)}: {LayerImageFormat}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(MachineZ)}: {MachineZ}, {nameof(Version)}: {Version}, {nameof(Unknown)}: {Unknown}";
+ }
- public Machine()
- {
- Section = new SectionHeader(SectionMark, this);
- }
+ public Machine()
+ {
+ Section = new SectionHeader(SectionMark, this);
+ }
- public Machine(int extraLength)
- {
- Section = new SectionHeader(SectionMark, this, extraLength);
- }
+ public Machine(int extraLength)
+ {
+ Section = new SectionHeader(SectionMark, this, extraLength);
+ }
- public Machine(bool includeSectionOnLength)
- {
- Section = new SectionHeader(SectionMark, this, includeSectionOnLength ? 16 : 0);
- }
+ public Machine(bool includeSectionOnLength)
+ {
+ Section = new SectionHeader(SectionMark, this, includeSectionOnLength ? 16 : 0);
+ }
- public void Validate()
- {
- Section.Validate(SectionMark, 0, this);
- }
+ public void Validate()
+ {
+ Section.Validate(SectionMark, 0, this);
}
- #endregion
+ }
+ #endregion
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public FileMark FileMarkSettings { get; protected internal set; } = new();
+ public FileMark FileMarkSettings { get; protected internal set; } = new();
- public Header HeaderSettings { get; protected internal set; } = new();
+ public Header HeaderSettings { get; protected internal set; } = new();
- public Preview PreviewSettings { get; protected internal set; } = new();
+ public Preview PreviewSettings { get; protected internal set; } = new();
- public LayerDefinition LayersDefinition { get; protected internal set; } = new();
- public Extra ExtraSettings { get; protected internal set; } = new();
- public Machine MachineSettings { get; protected internal set; } = new(true);
+ public LayerDefinition LayersDefinition { get; protected internal set; } = new();
+ public Extra ExtraSettings { get; protected internal set; } = new();
+ public Machine MachineSettings { get; protected internal set; } = new(true);
- public override FileFormatType FileType => FileFormatType.Binary;
+ public override FileFormatType FileType => FileFormatType.Binary;
- public override FileExtension[] FileExtensions { get; } = {
+ 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)"),
- new(typeof(PhotonWorkshopFile), "pwmx", "Photon Mono X (PWMX)"),
- new(typeof(PhotonWorkshopFile), "pwmb", "Photon Mono X 6K (PWMB)"),
- new(typeof(PhotonWorkshopFile), "pwmo", "Photon Mono (PWMO)"),
- new(typeof(PhotonWorkshopFile), "pwms", "Photon Mono SE (PWMS)"),
- new(typeof(PhotonWorkshopFile), "pwma", "Photon Mono 4K (PWMA)"),
- new(typeof(PhotonWorkshopFile), "pmsq", "Photon Mono SQ (PMSQ)"),
+ 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)"),
+ new(typeof(PhotonWorkshopFile), "pwmx", "Photon Mono X (PWMX)"),
+ new(typeof(PhotonWorkshopFile), "pwmb", "Photon Mono X 6K (PWMB)"),
+ new(typeof(PhotonWorkshopFile), "pwmo", "Photon Mono (PWMO)"),
+ new(typeof(PhotonWorkshopFile), "pwms", "Photon Mono SE (PWMS)"),
+ new(typeof(PhotonWorkshopFile), "pwma", "Photon Mono 4K (PWMA)"),
+ new(typeof(PhotonWorkshopFile), "pmsq", "Photon Mono SQ (PMSQ)"),
- };
+ };
- public override PrintParameterModifier[] PrintParameterModifiers
+ public override PrintParameterModifier[]? PrintParameterModifiers
+ {
+ get
{
- get
+ if (FileMarkSettings.Version >= VERSION_516)
{
- if (FileMarkSettings.Version >= VERSION_516)
- {
- return new[]
- {
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.TransitionLayerCount,
-
- PrintParameterModifier.WaitTimeBeforeCure,
-
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
-
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
-
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
- //PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- //PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- };
- }
-
return new[]
{
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.WaitTimeBeforeCure,
@@ -1094,813 +1067,839 @@ namespace UVtools.Core.FileFormats
PrintParameterModifier.BottomLiftSpeed,
PrintParameterModifier.LiftHeight,
PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.BottomRetractSpeed,
PrintParameterModifier.RetractSpeed,
+ //PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ //PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
};
}
+
+ return new[]
+ {
+ PrintParameterModifier.BottomLayerCount,
+
+ PrintParameterModifier.WaitTimeBeforeCure,
+
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+
+ PrintParameterModifier.RetractSpeed,
+ };
}
+ }
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- };
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ };
- public override Size[] ThumbnailsOriginalSize { get; } = {new(224, 168)};
+ public override Size[]? ThumbnailsOriginalSize { get; } = {new(224, 168)};
- public override uint[] AvailableVersions { get; } = { VERSION_1, VERSION_515, VERSION_516 };
+ public override uint[] AvailableVersions { get; } = { VERSION_1, VERSION_515, VERSION_516 };
- public override uint DefaultVersion => VERSION_1;
+ public override uint DefaultVersion => VERSION_1;
- public override uint Version
+ public override uint Version
+ {
+ get => FileMarkSettings.Version;
+ set
{
- get => FileMarkSettings.Version;
- set
- {
- base.Version = value;
- FileMarkSettings.Version = base.Version;
- }
+ base.Version = value;
+ FileMarkSettings.Version = base.Version;
}
+ }
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
{
- get => HeaderSettings.ResolutionX;
- set
- {
- HeaderSettings.ResolutionX = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => HeaderSettings.ResolutionY;
+ set
{
- get => HeaderSettings.ResolutionY;
- set
- {
- HeaderSettings.ResolutionY = value;
- RaisePropertyChanged();
- }
+ HeaderSettings.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get
{
- get
- {
- if (MachineSettings.DisplayWidth > 0) return MachineSettings.DisplayWidth;
- return PrinterModel switch
- {
- AnyCubicMachine.AnyCubicPhotonS => 68.04f,
- AnyCubicMachine.AnyCubicPhotonZero => 55.44f,
- AnyCubicMachine.AnyCubicPhotonX => 192,
- AnyCubicMachine.AnyCubicPhotonUltra => 102.40f,
- AnyCubicMachine.AnyCubicPhotonMono => 82.62f,
- AnyCubicMachine.AnyCubicPhotonMonoSE => 82.62f,
- AnyCubicMachine.AnyCubicPhotonMono4K => 132.90f,
- AnyCubicMachine.AnyCubicPhotonMonoX => 192,
- AnyCubicMachine.AnyCubicPhotonMonoX6K => 197,
- AnyCubicMachine.AnyCubicPhotonMonoSQ => 120,
- _ => 0
- };
- }
- set
- {
- MachineSettings.DisplayWidth = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ if (MachineSettings.DisplayWidth > 0) return MachineSettings.DisplayWidth;
+ return PrinterModel switch
+ {
+ AnyCubicMachine.AnyCubicPhotonS => 68.04f,
+ AnyCubicMachine.AnyCubicPhotonZero => 55.44f,
+ AnyCubicMachine.AnyCubicPhotonX => 192,
+ AnyCubicMachine.AnyCubicPhotonUltra => 102.40f,
+ AnyCubicMachine.AnyCubicPhotonMono => 82.62f,
+ AnyCubicMachine.AnyCubicPhotonMonoSE => 82.62f,
+ AnyCubicMachine.AnyCubicPhotonMono4K => 132.90f,
+ AnyCubicMachine.AnyCubicPhotonMonoX => 192,
+ AnyCubicMachine.AnyCubicPhotonMonoX6K => 197,
+ AnyCubicMachine.AnyCubicPhotonMonoSQ => 120,
+ _ => 0
+ };
}
- public override float DisplayHeight
+ set
{
- get
- {
- if (MachineSettings.DisplayHeight > 0) return MachineSettings.DisplayHeight;
- return PrinterModel switch
- {
- AnyCubicMachine.AnyCubicPhotonS => 120.96f,
- AnyCubicMachine.AnyCubicPhotonZero => 98.637f,
- AnyCubicMachine.AnyCubicPhotonX => 120,
- AnyCubicMachine.AnyCubicPhotonUltra => 57.60f,
- AnyCubicMachine.AnyCubicPhotonMono => 130.56f,
- AnyCubicMachine.AnyCubicPhotonMonoSE => 130.56f,
- AnyCubicMachine.AnyCubicPhotonMono4K => 80,
- AnyCubicMachine.AnyCubicPhotonMonoX => 120,
- AnyCubicMachine.AnyCubicPhotonMonoX6K => 122.80f,
- AnyCubicMachine.AnyCubicPhotonMonoSQ => 128,
- _ => 0
- };
- }
- set
- {
- MachineSettings.DisplayHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ MachineSettings.DisplayWidth = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
-
- public override float MachineZ
+ }
+ public override float DisplayHeight
+ {
+ get
{
- get
- {
- if (MachineSettings.MachineZ > 0) return MachineSettings.MachineZ;
- return PrinterModel switch
- {
- AnyCubicMachine.AnyCubicPhotonS => 165,
- AnyCubicMachine.AnyCubicPhotonZero => 150,
- AnyCubicMachine.AnyCubicPhotonX => 245,
- AnyCubicMachine.AnyCubicPhotonUltra => 165,
- AnyCubicMachine.AnyCubicPhotonMono => 165,
- AnyCubicMachine.AnyCubicPhotonMonoSE => 160,
- AnyCubicMachine.AnyCubicPhotonMono4K => 165,
- AnyCubicMachine.AnyCubicPhotonMonoX => 245,
- AnyCubicMachine.AnyCubicPhotonMonoX6K => 245,
- AnyCubicMachine.AnyCubicPhotonMonoSQ => 200,
- _ => 0
- };
- }
- set
- {
- MachineSettings.MachineZ = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ if (MachineSettings.DisplayHeight > 0) return MachineSettings.DisplayHeight;
+ return PrinterModel switch
+ {
+ AnyCubicMachine.AnyCubicPhotonS => 120.96f,
+ AnyCubicMachine.AnyCubicPhotonZero => 98.637f,
+ AnyCubicMachine.AnyCubicPhotonX => 120,
+ AnyCubicMachine.AnyCubicPhotonUltra => 57.60f,
+ AnyCubicMachine.AnyCubicPhotonMono => 130.56f,
+ AnyCubicMachine.AnyCubicPhotonMonoSE => 130.56f,
+ AnyCubicMachine.AnyCubicPhotonMono4K => 80,
+ AnyCubicMachine.AnyCubicPhotonMonoX => 120,
+ AnyCubicMachine.AnyCubicPhotonMonoX6K => 122.80f,
+ AnyCubicMachine.AnyCubicPhotonMonoSQ => 128,
+ _ => 0
+ };
}
-
- public override Enumerations.FlipDirection DisplayMirror
+ set
{
- get => Enumerations.FlipDirection.Horizontally;
- set {}
+ MachineSettings.DisplayHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override bool IsAntiAliasingEmulated => true;
-
- public override byte AntiAliasing
+ public override float MachineZ
+ {
+ get
{
- get => (byte) HeaderSettings.AntiAliasing;
- set
- {
- base.AntiAliasing = (byte)(HeaderSettings.AntiAliasing = value.Clamp(1, 16));
- ValidateAntiAliasingLevel();
- }
+ if (MachineSettings.MachineZ > 0) return MachineSettings.MachineZ;
+ return PrinterModel switch
+ {
+ AnyCubicMachine.AnyCubicPhotonS => 165,
+ AnyCubicMachine.AnyCubicPhotonZero => 150,
+ AnyCubicMachine.AnyCubicPhotonX => 245,
+ AnyCubicMachine.AnyCubicPhotonUltra => 165,
+ AnyCubicMachine.AnyCubicPhotonMono => 165,
+ AnyCubicMachine.AnyCubicPhotonMonoSE => 160,
+ AnyCubicMachine.AnyCubicPhotonMono4K => 165,
+ AnyCubicMachine.AnyCubicPhotonMonoX => 245,
+ AnyCubicMachine.AnyCubicPhotonMonoX6K => 245,
+ AnyCubicMachine.AnyCubicPhotonMonoSQ => 200,
+ _ => 0
+ };
}
-
- public override float LayerHeight
+ set
{
- get => HeaderSettings.LayerHeight;
- set
- {
- HeaderSettings.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ MachineSettings.MachineZ = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => Enumerations.FlipDirection.Horizontally;
+ set {}
+ }
+
+ public override bool IsAntiAliasingEmulated => true;
+
+ public override byte AntiAliasing
+ {
+ get => (byte) HeaderSettings.AntiAliasing;
+ set
{
- get => base.LayerCount;
- set => base.LayerCount = LayersDefinition.LayerCount = base.LayerCount;
+ base.AntiAliasing = (byte)(HeaderSettings.AntiAliasing = value.Clamp(1, 16));
+ ValidateAntiAliasingLevel();
}
+ }
- public override ushort BottomLayerCount
+ public override float LayerHeight
+ {
+ get => HeaderSettings.LayerHeight;
+ set
{
- get => (ushort) HeaderSettings.BottomLayersCount;
- set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount = value);
+ HeaderSettings.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Firmware;
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = LayersDefinition.LayerCount = base.LayerCount;
+ }
- public override ushort TransitionLayerCount
- {
- get => (ushort)(Version >= VERSION_516 ? HeaderSettings.TransitionLayerCount : 0);
- set => base.TransitionLayerCount = (ushort)(HeaderSettings.TransitionLayerCount = Math.Min(value, MaximumPossibleTransitionLayerCount));
- }
+ public override ushort BottomLayerCount
+ {
+ get => (ushort) HeaderSettings.BottomLayersCount;
+ set => base.BottomLayerCount = (ushort) (HeaderSettings.BottomLayersCount = value);
+ }
- public override float BottomLightOffDelay => BottomWaitTimeBeforeCure;
+ public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Firmware;
- public override float LightOffDelay => WaitTimeBeforeCure;
+ public override ushort TransitionLayerCount
+ {
+ get => (ushort)(Version >= VERSION_516 ? HeaderSettings.TransitionLayerCount : 0);
+ set => base.TransitionLayerCount = (ushort)(HeaderSettings.TransitionLayerCount = Math.Min(value, MaximumPossibleTransitionLayerCount));
+ }
- public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
+ public override float BottomLightOffDelay => BottomWaitTimeBeforeCure;
- public override float WaitTimeBeforeCure
- {
- get => HeaderSettings.WaitTimeBeforeCure;
- set => base.WaitTimeBeforeCure = HeaderSettings.WaitTimeBeforeCure = (float)Math.Round(value, 2);
- }
+ public override float LightOffDelay => WaitTimeBeforeCure;
- public override float BottomExposureTime
- {
- get => HeaderSettings.BottomExposureTime;
- set => base.BottomExposureTime = HeaderSettings.BottomExposureTime = (float) Math.Round(value, 2);
- }
+ public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
+
+ public override float WaitTimeBeforeCure
+ {
+ get => HeaderSettings.WaitTimeBeforeCure;
+ set => base.WaitTimeBeforeCure = HeaderSettings.WaitTimeBeforeCure = (float)Math.Round(value, 2);
+ }
+
+ public override float BottomExposureTime
+ {
+ get => HeaderSettings.BottomExposureTime;
+ set => base.BottomExposureTime = HeaderSettings.BottomExposureTime = (float) Math.Round(value, 2);
+ }
+
+ public override float ExposureTime
+ {
+ get => HeaderSettings.ExposureTime;
+ set => base.ExposureTime = HeaderSettings.ExposureTime = (float) Math.Round(value, 2);
+ }
- public override float ExposureTime
+ public override float BottomLiftHeight
+ {
+ get
{
- get => HeaderSettings.ExposureTime;
- set => base.ExposureTime = HeaderSettings.ExposureTime = (float) Math.Round(value, 2);
+ if (FileMarkSettings.Version >= VERSION_516) return ExtraSettings.BottomLiftHeight1;
+ return base.BottomLiftHeight;
}
-
- public override float BottomLiftHeight
+ set
{
- get
- {
- if (FileMarkSettings.Version >= VERSION_516) return ExtraSettings.BottomLiftHeight1;
- return base.BottomLiftHeight;
- }
- set
+ value = (float)Math.Round(value, 2);
+ ExtraSettings.BottomLiftHeight1 = value;
+ if (FileMarkSettings.Version >= VERSION_516)
{
- value = (float)Math.Round(value, 2);
- ExtraSettings.BottomLiftHeight1 = value;
- if (FileMarkSettings.Version >= VERSION_516)
+ base.BottomLiftHeight = (float)Math.Round(value + ExtraSettings.BottomLiftHeight2, 2);
+ foreach (var layer in this) // Fix layer value
{
- base.BottomLiftHeight = (float)Math.Round(value + ExtraSettings.BottomLiftHeight2, 2);
- foreach (var layer in this) // Fix layer value
- {
- if (!layer.IsBottomLayer) continue;
- layer.LiftHeight = base.BottomLiftHeight;
- }
+ if (!layer.IsBottomLayer) continue;
+ layer.LiftHeight = base.BottomLiftHeight;
}
- else base.BottomLiftHeight = value;
}
+ else base.BottomLiftHeight = value;
}
+ }
- public override float BottomLiftSpeed
+ public override float BottomLiftSpeed
+ {
+ get
{
- get
- {
- if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.BottomLiftSpeed1 * 60, 2);
- return base.BottomLiftSpeed;
- }
- set
- {
- value = (float)Math.Round(value, 2);
- ExtraSettings.BottomLiftSpeed1 = (float)Math.Round(value / 60, 2);
- base.BottomLiftSpeed = value;
- }
+ if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.BottomLiftSpeed1 * 60, 2);
+ return base.BottomLiftSpeed;
+ }
+ set
+ {
+ value = (float)Math.Round(value, 2);
+ ExtraSettings.BottomLiftSpeed1 = (float)Math.Round(value / 60, 2);
+ base.BottomLiftSpeed = value;
}
+ }
- public override float LiftHeight
+ public override float LiftHeight
+ {
+ get
{
- get
- {
- if (FileMarkSettings.Version >= VERSION_516) return ExtraSettings.LiftHeight1;
- return HeaderSettings.LiftHeight;
- }
- set
+ if (FileMarkSettings.Version >= VERSION_516) return ExtraSettings.LiftHeight1;
+ return HeaderSettings.LiftHeight;
+ }
+ set
+ {
+ value = (float)Math.Round(value, 2);
+ ExtraSettings.LiftHeight1 = value;
+ if (FileMarkSettings.Version >= VERSION_516)
{
- value = (float)Math.Round(value, 2);
- ExtraSettings.LiftHeight1 = value;
- if (FileMarkSettings.Version >= VERSION_516)
+ base.LiftHeight = HeaderSettings.LiftHeight = (float)Math.Round(value + ExtraSettings.LiftHeight2, 2);
+ foreach (var layer in this) // Fix layer value
{
- base.LiftHeight = HeaderSettings.LiftHeight = (float)Math.Round(value + ExtraSettings.LiftHeight2, 2);
- foreach (var layer in this) // Fix layer value
- {
- if(!layer.IsNormalLayer) continue;
- layer.LiftHeight = base.LiftHeight;
- }
+ if(!layer.IsNormalLayer) continue;
+ layer.LiftHeight = base.LiftHeight;
}
- else base.LiftHeight = value;
}
+ else base.LiftHeight = value;
}
+ }
- public override float LiftSpeed
+ public override float LiftSpeed
+ {
+ get
{
- get
- {
- if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.LiftSpeed1 * 60, 2);
- return (float)Math.Round(HeaderSettings.LiftSpeed * 60, 2);
- }
- set
- {
- value = (float)Math.Round(value, 2);
- HeaderSettings.LiftSpeed = ExtraSettings.LiftSpeed1 = (float)Math.Round(value / 60, 2);
- base.LiftSpeed = value;
- }
+ if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.LiftSpeed1 * 60, 2);
+ return (float)Math.Round(HeaderSettings.LiftSpeed * 60, 2);
}
+ set
+ {
+ value = (float)Math.Round(value, 2);
+ HeaderSettings.LiftSpeed = ExtraSettings.LiftSpeed1 = (float)Math.Round(value / 60, 2);
+ base.LiftSpeed = value;
+ }
+ }
- public override float BottomLiftHeight2
+ public override float BottomLiftHeight2
+ {
+ get => FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.BottomLiftHeight2 : 0;
+ set
{
- get => FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.BottomLiftHeight2 : 0;
- set
- {
- if (FileMarkSettings.Version < VERSION_516) return;
- var bottomLiftHeight = BottomLiftHeight;
- ExtraSettings.BottomLiftHeight2 = (float)Math.Round(value, 2);
- BottomLiftHeight = bottomLiftHeight;
- base.BottomLiftHeight2 = ExtraSettings.BottomLiftHeight2;
- }
+ if (FileMarkSettings.Version < VERSION_516) return;
+ var bottomLiftHeight = BottomLiftHeight;
+ ExtraSettings.BottomLiftHeight2 = (float)Math.Round(value, 2);
+ BottomLiftHeight = bottomLiftHeight;
+ base.BottomLiftHeight2 = ExtraSettings.BottomLiftHeight2;
}
+ }
- public override float BottomLiftSpeed2
+ public override float BottomLiftSpeed2
+ {
+ get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.BottomLiftSpeed2 * 60, 2) : 0;
+ set
{
- get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.BottomLiftSpeed2 * 60, 2) : 0;
- set
- {
- if (FileMarkSettings.Version < VERSION_516) return;
- value = (float)Math.Round(value, 2);
- ExtraSettings.BottomLiftSpeed2 = (float)Math.Round(value / 60, 2);
- base.BottomLiftSpeed2 = value;
- }
+ if (FileMarkSettings.Version < VERSION_516) return;
+ value = (float)Math.Round(value, 2);
+ ExtraSettings.BottomLiftSpeed2 = (float)Math.Round(value / 60, 2);
+ base.BottomLiftSpeed2 = value;
}
+ }
- public override float LiftHeight2
+ public override float LiftHeight2
+ {
+ get => FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.LiftHeight2 : 0;
+ set
{
- get => FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.LiftHeight2 : 0;
- set
- {
- if (FileMarkSettings.Version < VERSION_516) return;
- var liftHeight = LiftHeight;
- ExtraSettings.LiftHeight2 = (float)Math.Round(value, 2);
- LiftHeight = liftHeight;
- base.BottomLiftHeight2 = ExtraSettings.LiftHeight2;
- }
+ if (FileMarkSettings.Version < VERSION_516) return;
+ var liftHeight = LiftHeight;
+ ExtraSettings.LiftHeight2 = (float)Math.Round(value, 2);
+ LiftHeight = liftHeight;
+ base.BottomLiftHeight2 = ExtraSettings.LiftHeight2;
}
+ }
- public override float LiftSpeed2
+ public override float LiftSpeed2
+ {
+ get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.LiftSpeed2 * 60, 2) : 0;
+ set
{
- get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.LiftSpeed2 * 60, 2) : 0;
- set
- {
- if (FileMarkSettings.Version < VERSION_516) return;
- base.LiftSpeed2 = ExtraSettings.LiftSpeed2 = (float)Math.Round(value / 60, 2);
- }
+ if (FileMarkSettings.Version < VERSION_516) return;
+ base.LiftSpeed2 = ExtraSettings.LiftSpeed2 = (float)Math.Round(value / 60, 2);
}
+ }
- public override float BottomRetractSpeed
+ public override float BottomRetractSpeed
+ {
+ get
{
- get
+ if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.BottomRetractSpeed1 * 60, 2);
+ return RetractSpeed;
+ }
+ set
+ {
+ value = (float)Math.Round(value, 2);
+
+ if (FileMarkSettings.Version >= VERSION_516)
{
- if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.BottomRetractSpeed1 * 60, 2);
- return RetractSpeed;
+ ExtraSettings.BottomRetractSpeed1 = (float)Math.Round(value / 60, 2);
}
- set
+ else
{
- value = (float)Math.Round(value, 2);
-
- if (FileMarkSettings.Version >= VERSION_516)
- {
- ExtraSettings.BottomRetractSpeed1 = (float)Math.Round(value / 60, 2);
- }
- else
- {
- RetractSpeed = value;
- }
+ RetractSpeed = value;
}
}
+ }
- public override float RetractSpeed
+ public override float RetractSpeed
+ {
+ get => (float)Math.Round(HeaderSettings.RetractSpeed * 60, 2);
+ set
{
- get => (float)Math.Round(HeaderSettings.RetractSpeed * 60, 2);
- set
- {
- value = (float)Math.Round(value, 2);
- ExtraSettings.RetractSpeed1 = HeaderSettings.RetractSpeed = (float) Math.Round(value / 60, 2);
- base.RetractSpeed = value;
- }
+ value = (float)Math.Round(value, 2);
+ ExtraSettings.RetractSpeed1 = HeaderSettings.RetractSpeed = (float) Math.Round(value / 60, 2);
+ base.RetractSpeed = value;
}
+ }
- public override float BottomRetractSpeed2
+ public override float BottomRetractSpeed2
+ {
+ get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.BottomRetractSpeed2 * 60, 2) : 0;
+ set
{
- get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.BottomRetractSpeed2 * 60, 2) : 0;
- set
- {
- if (FileMarkSettings.Version < VERSION_516) return;
- value = (float)Math.Round(value, 2);
- ExtraSettings.BottomRetractSpeed2 = (float)Math.Round(value / 60, 2);
- base.BottomRetractSpeed2 = value;
- }
+ if (FileMarkSettings.Version < VERSION_516) return;
+ value = (float)Math.Round(value, 2);
+ ExtraSettings.BottomRetractSpeed2 = (float)Math.Round(value / 60, 2);
+ base.BottomRetractSpeed2 = value;
}
+ }
- public override float RetractSpeed2
+ public override float RetractSpeed2
+ {
+ get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.RetractSpeed2 * 60, 2) : 0;
+ set
{
- get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.RetractSpeed2 * 60, 2) : 0;
- set
- {
- if (FileMarkSettings.Version < VERSION_516) return;
- value = (float)Math.Round(value, 2);
- ExtraSettings.RetractSpeed2 = (float)Math.Round(value / 60, 2);
- base.RetractSpeed2 = value;
- }
+ if (FileMarkSettings.Version < VERSION_516) return;
+ value = (float)Math.Round(value, 2);
+ ExtraSettings.RetractSpeed2 = (float)Math.Round(value / 60, 2);
+ base.RetractSpeed2 = value;
}
+ }
- public override float PrintTime
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- HeaderSettings.PrintTime = (uint) base.PrintTime;
- }
+ base.PrintTime = value;
+ HeaderSettings.PrintTime = (uint) base.PrintTime;
}
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- HeaderSettings.VolumeMl = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ HeaderSettings.VolumeMl = base.MaterialMilliliters;
}
+ }
- public override float MaterialGrams
- {
- get => (float) Math.Round(HeaderSettings.WeightG, 3);
- set => base.MaterialGrams = HeaderSettings.WeightG = (float) Math.Round(value, 3);
- }
+ public override float MaterialGrams
+ {
+ get => (float) Math.Round(HeaderSettings.WeightG, 3);
+ set => base.MaterialGrams = HeaderSettings.WeightG = (float) Math.Round(value, 3);
+ }
- public override float MaterialCost
+ public override float MaterialCost
+ {
+ get => (float) Math.Round(HeaderSettings.Price, 3);
+ set => base.MaterialCost = HeaderSettings.Price = (float)Math.Round(value, 3);
+ }
+
+ public override string MachineName
+ {
+ get
{
- get => (float) Math.Round(HeaderSettings.Price, 3);
- set => base.MaterialCost = HeaderSettings.Price = (float)Math.Round(value, 3);
+ if (string.IsNullOrWhiteSpace(MachineSettings.MachineName)) return MachineSettings.MachineName;
+ return PrinterModel switch
+ {
+ AnyCubicMachine.AnyCubicPhotonS => "Photon S",
+ AnyCubicMachine.AnyCubicPhotonZero => "Photon Zero",
+ AnyCubicMachine.AnyCubicPhotonX => "Photon X",
+ AnyCubicMachine.AnyCubicPhotonUltra => "Photon Ultra",
+ AnyCubicMachine.AnyCubicPhotonMono => "Photon Mono",
+ AnyCubicMachine.AnyCubicPhotonMonoSE => "Photon Mono SE",
+ AnyCubicMachine.AnyCubicPhotonMono4K => "Photon Mono 4K",
+ AnyCubicMachine.AnyCubicPhotonMonoX => "Photon Mono X",
+ AnyCubicMachine.AnyCubicPhotonMonoX6K => "Photon Mono X 6K",
+ AnyCubicMachine.AnyCubicPhotonMonoSQ => "Photon Mono SQ",
+ _ => base.MachineName
+ };
}
+ set => base.MachineName = MachineSettings.MachineName = value;
+ }
+
+ public override object[] Configs => new object[] { FileMarkSettings, HeaderSettings, PreviewSettings, LayersDefinition, ExtraSettings, MachineSettings };
+
+ public LayerRleFormat LayerImageFormat =>
+ FileEndsWith(".pws")
+ ? LayerRleFormat.PWS
+ : LayerRleFormat.PW0;
- public override string MachineName
+ public AnyCubicMachine PrinterModel
+ {
+ get
{
- get
+ if (FileEndsWith(".pws"))
{
- if (string.IsNullOrWhiteSpace(MachineSettings.MachineName)) return MachineSettings.MachineName;
- return PrinterModel switch
- {
- AnyCubicMachine.AnyCubicPhotonS => "Photon S",
- AnyCubicMachine.AnyCubicPhotonZero => "Photon Zero",
- AnyCubicMachine.AnyCubicPhotonX => "Photon X",
- AnyCubicMachine.AnyCubicPhotonUltra => "Photon Ultra",
- AnyCubicMachine.AnyCubicPhotonMono => "Photon Mono",
- AnyCubicMachine.AnyCubicPhotonMonoSE => "Photon Mono SE",
- AnyCubicMachine.AnyCubicPhotonMono4K => "Photon Mono 4K",
- AnyCubicMachine.AnyCubicPhotonMonoX => "Photon Mono X",
- AnyCubicMachine.AnyCubicPhotonMonoX6K => "Photon Mono X 6K",
- AnyCubicMachine.AnyCubicPhotonMonoSQ => "Photon Mono SQ",
- _ => base.MachineName
- };
+ return AnyCubicMachine.AnyCubicPhotonS;
}
- set => base.MachineName = MachineSettings.MachineName = value;
- }
-
- public override object[] Configs => new object[] { FileMarkSettings, HeaderSettings, PreviewSettings, LayersDefinition, ExtraSettings, MachineSettings };
- public LayerRleFormat LayerImageFormat =>
- FileEndsWith(".pws")
- ? LayerRleFormat.PWS
- : LayerRleFormat.PW0;
+ if (FileEndsWith(".pw0"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonZero;
+ }
- public AnyCubicMachine PrinterModel
- {
- get
+ if (FileEndsWith(".pwx"))
{
- if (FileEndsWith(".pws"))
- {
- return AnyCubicMachine.AnyCubicPhotonS;
- }
+ return AnyCubicMachine.AnyCubicPhotonX;
+ }
- if (FileEndsWith(".pw0"))
- {
- return AnyCubicMachine.AnyCubicPhotonZero;
- }
+ if (FileEndsWith(".dlp"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonUltra;
+ }
- if (FileEndsWith(".pwx"))
- {
- return AnyCubicMachine.AnyCubicPhotonX;
- }
+ if (FileEndsWith(".pwmo"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonMono;
+ }
- if (FileEndsWith(".dlp"))
- {
- return AnyCubicMachine.AnyCubicPhotonUltra;
- }
+ if (FileEndsWith(".pwms"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonMonoSE;
+ }
- if (FileEndsWith(".pwmo"))
- {
- return AnyCubicMachine.AnyCubicPhotonMono;
- }
+ if (FileEndsWith(".pwma"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonMono4K;
+ }
- if (FileEndsWith(".pwms"))
- {
- return AnyCubicMachine.AnyCubicPhotonMonoSE;
- }
+ if (FileEndsWith(".pwmx"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonMonoX;
+ }
- if (FileEndsWith(".pwma"))
- {
- return AnyCubicMachine.AnyCubicPhotonMono4K;
- }
+ if (FileEndsWith(".pwmb"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonMonoX6K;
+ }
- if (FileEndsWith(".pwmx"))
- {
- return AnyCubicMachine.AnyCubicPhotonMonoX;
- }
+ if (FileEndsWith(".pmsq"))
+ {
+ return AnyCubicMachine.AnyCubicPhotonMonoSQ;
+ }
- if (FileEndsWith(".pwmb"))
- {
- return AnyCubicMachine.AnyCubicPhotonMonoX6K;
- }
+ return AnyCubicMachine.AnyCubicPhotonS;
+ }
+ }
+ #endregion
- if (FileEndsWith(".pmsq"))
- {
- return AnyCubicMachine.AnyCubicPhotonMonoSQ;
- }
+ #region Constructors
+ public PhotonWorkshopFile()
+ {
+ }
+ #endregion
- return AnyCubicMachine.AnyCubicPhotonS;
- }
- }
- #endregion
+ #region Methods
+ public override void Clear()
+ {
+ base.Clear();
- #region Constructors
- public PhotonWorkshopFile()
+ LayersDefinition = null!;
+ }
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ /*if (FileMarkSettings.AreaNum == 0)
+ {*/
+ FileMarkSettings.AreaNum = FileMarkSettings.Version switch
{
- }
- #endregion
+ VERSION_1 => 4,
+ VERSION_515 => 5,
+ VERSION_516 => 8,
+ _ => 4
+ };
+ //}
+ HeaderSettings.PixelSizeUm = PixelSizeMicronsMax;
+ MachineSettings.LayerImageFormat = $"{LayerImageFormat.ToString().ToLower()}Img";
+
+ HeaderSettings.PerLayerOverride = (byte)(AllLayersAreUsingGlobalParameters ? 0 : 1);
- #region Methods
- public override void Clear()
+
+ FileMarkSettings.HeaderAddress = (uint) Helpers.Serializer.SizeOf(FileMarkSettings);
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Create, FileAccess.Write);
+ if (FileMarkSettings.Version >= VERSION_516)
{
- base.Clear();
+ HeaderSettings.Section.Length = 84;
+ }
- LayersDefinition = null;
+ outputFile.Seek((int)FileMarkSettings.HeaderAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ if (FileMarkSettings.Version >= VERSION_516)
+ {
+ //outputFile.WriteUIntLittleEndian(0);
+ outputFile.Seek(4, SeekOrigin.Current); // Extra padding
}
- protected override void EncodeInternally(OperationProgress progress)
+ if (CreatedThumbnailsCount > 0)
{
- /*if (FileMarkSettings.AreaNum == 0)
- {*/
- FileMarkSettings.AreaNum = FileMarkSettings.Version switch
+ FileMarkSettings.PreviewAddress = (uint)outputFile.Position;
+ //Preview preview = Preview.Encode(Thumbnails[0]);
+ var preview = new Preview((uint)Thumbnails[0]!.Width, (uint)Thumbnails[0]!.Height)
{
- VERSION_1 => 4,
- VERSION_515 => 5,
- VERSION_516 => 8,
- _ => 4
+ Data = EncodeImage(DATATYPE_RGB565, Thumbnails[0]!)
};
- //}
- HeaderSettings.PixelSizeUm = PixelSizeMicronsMax;
- MachineSettings.LayerImageFormat = $"{LayerImageFormat.ToString().ToLower()}Img";
-
- HeaderSettings.PerLayerOverride = (byte)(LayerManager.AllLayersAreUsingGlobalParameters ? 0 : 1);
-
+ Helpers.SerializeWriteFileStream(outputFile, preview);
+ outputFile.WriteBytes(preview.Data);
+ FileMarkSettings.PreviewEndAddress = (uint)outputFile.Position;
+ }
- FileMarkSettings.HeaderAddress = (uint) Helpers.Serializer.SizeOf(FileMarkSettings);
- using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
- if (FileMarkSettings.Version >= VERSION_516)
+ if (FileMarkSettings.Version >= VERSION_515)
+ {
+ foreach (var uint32 in ReservedUintsAfterPreview)
{
- HeaderSettings.Section.Length = 84;
+ outputFile.WriteUIntLittleEndian(uint32);
}
+ }
- outputFile.Seek((int)FileMarkSettings.HeaderAddress, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
- if (FileMarkSettings.Version >= VERSION_516)
- {
- //outputFile.WriteUIntLittleEndian(0);
- outputFile.Seek(4, SeekOrigin.Current); // Extra padding
- }
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ FileMarkSettings.LayerDefinitionAddress = (uint)outputFile.Position;
+ LayersDefinition = new LayerDefinition(LayerCount);
+ Helpers.SerializeWriteFileStream(outputFile, LayersDefinition);
+ uint layerDefOffset = (uint)outputFile.Position;
+ uint layerRleOffset = (uint)(layerDefOffset + Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount);
- if (CreatedThumbnailsCount > 0)
- {
- FileMarkSettings.PreviewAddress = (uint)outputFile.Position;
- //Preview preview = Preview.Encode(Thumbnails[0]);
- var preview = new Preview((uint)Thumbnails[0].Width, (uint)Thumbnails[0].Height)
- {
- Data = EncodeImage(DATATYPE_RGB565, Thumbnails[0])
- };
- Helpers.SerializeWriteFileStream(outputFile, preview);
- outputFile.WriteBytes(preview.Data);
- FileMarkSettings.PreviewEndAddress = (uint)outputFile.Position;
- }
+ if (FileMarkSettings.Version >= VERSION_516)
+ {
+ outputFile.Seek(layerRleOffset, SeekOrigin.Begin);
+ FileMarkSettings.ExtraAddress = layerRleOffset;
+ Helpers.SerializeWriteFileStream(outputFile, ExtraSettings);
+ FileMarkSettings.MachineAddress = (uint)outputFile.Position;
+ Helpers.SerializeWriteFileStream(outputFile, MachineSettings);
+ layerRleOffset = (uint)outputFile.Position;
+ outputFile.Seek(layerDefOffset, SeekOrigin.Begin);
+ }
+ else if (FileMarkSettings.Version == VERSION_515)
+ {
+ FileMarkSettings.ExtraAddress = layerRleOffset;
+ }
+
+ FileMarkSettings.LayerImageAddress = layerRleOffset;
- if (FileMarkSettings.Version >= VERSION_515)
+ var layersHash = new Dictionary<string, LayerDef>();
+
+ foreach (var batch in BatchLayersIndexes())
+ {
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
{
- foreach (var uint32 in ReservedUintsAfterPreview)
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = this[layerIndex].LayerMat)
{
- outputFile.WriteUIntLittleEndian(uint32);
+ LayersDefinition.Layers[layerIndex] = new LayerDef(this, this[layerIndex]);
+ LayersDefinition.Layers[layerIndex].Encode(mat);
}
- }
+ progress.LockAndIncrement();
+ });
- progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
- FileMarkSettings.LayerDefinitionAddress = (uint)outputFile.Position;
- LayersDefinition = new LayerDefinition(LayerCount);
- Helpers.SerializeWriteFileStream(outputFile, LayersDefinition);
- uint layerDefOffset = (uint)outputFile.Position;
- uint layerRleOffset = (uint)(layerDefOffset + Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount);
-
- if (FileMarkSettings.Version >= VERSION_516)
- {
- outputFile.Seek(layerRleOffset, SeekOrigin.Begin);
- FileMarkSettings.ExtraAddress = layerRleOffset;
- Helpers.SerializeWriteFileStream(outputFile, ExtraSettings);
- FileMarkSettings.MachineAddress = (uint)outputFile.Position;
- Helpers.SerializeWriteFileStream(outputFile, MachineSettings);
- layerRleOffset = (uint)outputFile.Position;
- outputFile.Seek(layerDefOffset, SeekOrigin.Begin);
- }
- else if (FileMarkSettings.Version == VERSION_515)
+ foreach (var layerIndex in batch)
{
- FileMarkSettings.ExtraAddress = layerRleOffset;
- }
+ progress.Token.ThrowIfCancellationRequested();
- FileMarkSettings.LayerImageAddress = layerRleOffset;
+ var layerDef = LayersDefinition.Layers[layerIndex];
- var layersHash = new Dictionary<string, LayerDef>();
+ var hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle);
- foreach (var batch in BatchLayersIndexes())
- {
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
+ if (layersHash.TryGetValue(hash, out var layerDataHash))
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = this[layerIndex].LayerMat)
- {
- LayersDefinition.Layers[layerIndex] = new LayerDef(this, this[layerIndex]);
- LayersDefinition.Layers[layerIndex].Encode(mat);
- }
- progress.LockAndIncrement();
- });
-
- foreach (var layerIndex in batch)
+ layerDef.DataAddress = layerDataHash.DataAddress;
+ layerDef.DataLength = (uint)layerDataHash.EncodedRle.Length;
+ }
+ else
{
- progress.Token.ThrowIfCancellationRequested();
-
- var layerDef = LayersDefinition.Layers[layerIndex];
-
- var hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle);
-
- if (layersHash.TryGetValue(hash, out var layerDataHash))
- {
- layerDef.DataAddress = layerDataHash.DataAddress;
- layerDef.DataLength = (uint)layerDataHash.EncodedRle.Length;
- }
- else
- {
- layersHash.Add(hash, layerDef);
-
- layerDef.DataAddress = layerRleOffset;
+ layersHash.Add(hash, layerDef);
- outputFile.Seek(layerRleOffset, SeekOrigin.Begin);
- layerRleOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef.EncodedRle);
- }
+ layerDef.DataAddress = layerRleOffset;
- outputFile.Seek(layerDefOffset, SeekOrigin.Begin);
- layerDefOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef);
+ outputFile.Seek(layerRleOffset, SeekOrigin.Begin);
+ layerRleOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef.EncodedRle);
}
+
+ outputFile.Seek(layerDefOffset, SeekOrigin.Begin);
+ layerDefOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef);
}
-
- // Rewind
- outputFile.Seek(0, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, FileMarkSettings);
}
+
+ // Rewind
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, FileMarkSettings);
+ }
- protected override void DecodeInternally(OperationProgress progress)
- {
- using var inputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read);
- FileMarkSettings = Helpers.Deserialize<FileMark>(inputFile);
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ FileMarkSettings = Helpers.Deserialize<FileMark>(inputFile);
- Debug.Write("FileMark -> ");
- Debug.WriteLine(FileMarkSettings);
+ Debug.Write("FileMark -> ");
+ Debug.WriteLine(FileMarkSettings);
- if (!FileMarkSettings.Mark.Equals(FileMark.SectionMarkFile))
- {
- throw new FileLoadException(
- $"Invalid Filemark {FileMarkSettings.Mark}, expected {FileMark.SectionMarkFile}", FileFullPath);
- }
+ if (!FileMarkSettings.Mark.Equals(FileMark.SectionMarkFile))
+ {
+ throw new FileLoadException(
+ $"Invalid Filemark {FileMarkSettings.Mark}, expected {FileMark.SectionMarkFile}", FileFullPath);
+ }
- if (FileMarkSettings.Version is not VERSION_1 and not VERSION_515 and not VERSION_516)
- {
- throw new FileLoadException($"Invalid Version {FileMarkSettings.Version}, expected {VERSION_1} or {VERSION_515} or {VERSION_516}", FileFullPath);
- }
+ if (FileMarkSettings.Version is not VERSION_1 and not VERSION_515 and not VERSION_516)
+ {
+ throw new FileLoadException($"Invalid Version {FileMarkSettings.Version}, expected {VERSION_1} or {VERSION_515} or {VERSION_516}", FileFullPath);
+ }
- inputFile.Seek(FileMarkSettings.HeaderAddress, SeekOrigin.Begin);
- HeaderSettings = Helpers.Deserialize<Header>(inputFile);
- if (FileMarkSettings.Version >= VERSION_516)
- {
- inputFile.Seek(4, SeekOrigin.Current); // Extra padding
- }
+ inputFile.Seek(FileMarkSettings.HeaderAddress, SeekOrigin.Begin);
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ if (FileMarkSettings.Version >= VERSION_516)
+ {
+ inputFile.Seek(4, SeekOrigin.Current); // Extra padding
+ }
- Debug.Write("Header -> ");
- Debug.WriteLine(HeaderSettings);
+ Debug.Write("Header -> ");
+ Debug.WriteLine(HeaderSettings);
- HeaderSettings.Validate(FileMarkSettings.Version >= VERSION_516 ? 4 : 0);
+ HeaderSettings.Validate(FileMarkSettings.Version >= VERSION_516 ? 4 : 0);
- if (FileMarkSettings.PreviewAddress > 0)
- {
- inputFile.Seek(FileMarkSettings.PreviewAddress, SeekOrigin.Begin);
+ if (FileMarkSettings.PreviewAddress > 0)
+ {
+ inputFile.Seek(FileMarkSettings.PreviewAddress, SeekOrigin.Begin);
- PreviewSettings = Helpers.Deserialize<Preview>(inputFile);
- Debug.Write("Preview -> ");
- Debug.WriteLine(PreviewSettings);
+ PreviewSettings = Helpers.Deserialize<Preview>(inputFile);
+ Debug.Write("Preview -> ");
+ Debug.WriteLine(PreviewSettings);
- //PreviewSettings.Validate((int) PreviewSettings.DataSize);
+ //PreviewSettings.Validate((int) PreviewSettings.DataSize);
- PreviewSettings.Data = new byte[PreviewSettings.DataSize];
- inputFile.ReadBytes(PreviewSettings.Data);
+ PreviewSettings.Data = new byte[PreviewSettings.DataSize];
+ inputFile.ReadBytes(PreviewSettings.Data);
- Thumbnails[0] = DecodeImage(DATATYPE_RGB565, PreviewSettings.Data, PreviewSettings.ResolutionX, PreviewSettings.ResolutionY);
- //Thumbnails[0] = PreviewSettings.Decode();
- PreviewSettings.Data = null;
- }
+ Thumbnails[0] = DecodeImage(DATATYPE_RGB565, PreviewSettings.Data, PreviewSettings.ResolutionX, PreviewSettings.ResolutionY);
+ //Thumbnails[0] = PreviewSettings.Decode();
+ PreviewSettings.Data = null!;
+ }
- if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.ExtraAddress > 0)
- {
- inputFile.Seek(FileMarkSettings.ExtraAddress, SeekOrigin.Begin);
- ExtraSettings = Helpers.Deserialize<Extra>(inputFile);
- ExtraSettings.Validate();
- Debug.Write("Extra -> ");
- Debug.WriteLine(ExtraSettings);
- }
+ if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.ExtraAddress > 0)
+ {
+ inputFile.Seek(FileMarkSettings.ExtraAddress, SeekOrigin.Begin);
+ ExtraSettings = Helpers.Deserialize<Extra>(inputFile);
+ ExtraSettings.Validate();
+ Debug.Write("Extra -> ");
+ Debug.WriteLine(ExtraSettings);
+ }
- if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.MachineAddress > 0)
- {
- inputFile.Seek(FileMarkSettings.MachineAddress, SeekOrigin.Begin);
- MachineSettings = Helpers.Deserialize<Machine>(inputFile);
- MachineSettings.Validate();
- Debug.Write("Machine -> ");
- Debug.WriteLine(MachineSettings);
- }
+ if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.MachineAddress > 0)
+ {
+ inputFile.Seek(FileMarkSettings.MachineAddress, SeekOrigin.Begin);
+ MachineSettings = Helpers.Deserialize<Machine>(inputFile);
+ MachineSettings.Validate();
+ Debug.Write("Machine -> ");
+ Debug.WriteLine(MachineSettings);
+ }
- inputFile.Seek(FileMarkSettings.LayerDefinitionAddress, SeekOrigin.Begin);
+ inputFile.Seek(FileMarkSettings.LayerDefinitionAddress, SeekOrigin.Begin);
- LayersDefinition = Helpers.Deserialize<LayerDefinition>(inputFile);
- Debug.Write("LayersDefinition -> ");
- Debug.WriteLine(LayersDefinition);
+ LayersDefinition = Helpers.Deserialize<LayerDefinition>(inputFile);
+ Debug.Write("LayersDefinition -> ");
+ Debug.WriteLine(LayersDefinition);
- LayerManager.Init(LayersDefinition.LayerCount, DecodeType == FileDecodeType.Partial);
- LayersDefinition.Layers = new LayerDef[LayerCount];
+ Init(LayersDefinition.LayerCount, DecodeType == FileDecodeType.Partial);
+ LayersDefinition.Layers = new LayerDef[LayerCount];
- LayersDefinition.Validate();
+ LayersDefinition.Validate();
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- foreach (var batch in BatchLayersIndexes())
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ foreach (var batch in BatchLayersIndexes())
+ {
+ foreach (var layerIndex in batch)
{
- foreach (var layerIndex in batch)
- {
- progress.Token.ThrowIfCancellationRequested();
-
- LayersDefinition[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
- LayersDefinition[layerIndex].Parent = this;
- Debug.WriteLine($"Layer {layerIndex}: {LayersDefinition[layerIndex]}");
+ progress.Token.ThrowIfCancellationRequested();
- if (DecodeType == FileDecodeType.Full)
- {
- inputFile.SeekDoWorkAndRewind(LayersDefinition[layerIndex].DataAddress,
- () =>
- {
- LayersDefinition[layerIndex].EncodedRle = inputFile.ReadBytes(LayersDefinition[layerIndex].DataLength);
- });
- }
- }
+ LayersDefinition[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
+ LayersDefinition[layerIndex].Parent = this;
+ Debug.WriteLine($"Layer {layerIndex}: {LayersDefinition[layerIndex]}");
if (DecodeType == FileDecodeType.Full)
{
- Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- using var mat = LayersDefinition[layerIndex].Decode();
- this[layerIndex] = new Layer((uint)layerIndex, mat, this)
+ inputFile.SeekDoWorkAndRewind(LayersDefinition[layerIndex].DataAddress,
+ () =>
{
- PositionZ = LayersDefinition.Layers
- .Where((_, i) => i <= layerIndex)
- .Sum(def => def.LayerHeight),
- };
-
- progress.LockAndIncrement();
- });
+ LayersDefinition[layerIndex].EncodedRle = inputFile.ReadBytes(LayersDefinition[layerIndex].DataLength);
+ });
}
}
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ if (DecodeType == FileDecodeType.Full)
{
- LayersDefinition[layerIndex].CopyTo(this[layerIndex]);
- }
-
- if (FileMarkSettings.Version < VERSION_516)
- {
- // Fix the base.Bottoms
- SuppressRebuildPropertiesWork(() =>
+ Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex =>
{
- BottomLiftHeight = FirstLayer?.LiftHeight ?? LiftHeight;
- BottomLiftSpeed = FirstLayer?.LiftSpeed ?? LiftSpeed;
+ if (progress.Token.IsCancellationRequested) return;
+
+ using var mat = LayersDefinition[layerIndex].Decode();
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this)
+ {
+ PositionZ = LayersDefinition.Layers
+ .Where((_, i) => i <= layerIndex)
+ .Sum(def => def.LayerHeight),
+ };
+
+ progress.LockAndIncrement();
});
}
}
- protected override void PartialSaveInternally(OperationProgress progress)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- HeaderSettings.PerLayerOverride = LayerManager.AllLayersAreUsingGlobalParameters ? 0 : 1u;
-
- using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
- outputFile.Seek(FileMarkSettings.HeaderAddress, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ LayersDefinition[layerIndex].CopyTo(this[layerIndex]);
+ }
- if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.ExtraAddress > 0)
+ if (FileMarkSettings.Version < VERSION_516)
+ {
+ // Fix the base.Bottoms
+ SuppressRebuildPropertiesWork(() =>
{
- outputFile.Seek(FileMarkSettings.ExtraAddress, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, ExtraSettings);
- }
+ BottomLiftHeight = FirstLayer?.LiftHeight ?? LiftHeight;
+ BottomLiftSpeed = FirstLayer?.LiftSpeed ?? LiftSpeed;
+ });
+ }
+ }
- if (FileMarkSettings.MachineAddress > 0)
- {
- outputFile.Seek(FileMarkSettings.MachineAddress, SeekOrigin.Begin);
- Helpers.SerializeWriteFileStream(outputFile, MachineSettings);
- }
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ HeaderSettings.PerLayerOverride = AllLayersAreUsingGlobalParameters ? 0 : 1u;
- outputFile.Seek(FileMarkSettings.LayerDefinitionAddress + Helpers.Serializer.SizeOf(LayersDefinition), SeekOrigin.Begin);
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- LayersDefinition[layerIndex].SetFrom(this[layerIndex]);
- Helpers.SerializeWriteFileStream(outputFile, LayersDefinition[layerIndex]);
- }
+ using var outputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(FileMarkSettings.HeaderAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.ExtraAddress > 0)
+ {
+ outputFile.Seek(FileMarkSettings.ExtraAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, ExtraSettings);
}
- #endregion
+ if (FileMarkSettings.MachineAddress > 0)
+ {
+ outputFile.Seek(FileMarkSettings.MachineAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, MachineSettings);
+ }
+
+ outputFile.Seek(FileMarkSettings.LayerDefinitionAddress + Helpers.Serializer.SizeOf(LayersDefinition), SeekOrigin.Begin);
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ LayersDefinition[layerIndex].SetFrom(this[layerIndex]);
+ Helpers.SerializeWriteFileStream(outputFile, LayersDefinition[layerIndex]);
+ }
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs
index 115129e..be4bcd9 100644
--- a/UVtools.Core/FileFormats/SL1File.cs
+++ b/UVtools.Core/FileFormats/SL1File.cs
@@ -6,852 +6,852 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class SL1File : FileFormat
{
- public class SL1File : FileFormat
+ #region Constants
+
+ public const string IniConfig = "config.ini";
+ public const string IniPrusaslicer = "prusaslicer.ini";
+
+ public const string Keyword_FileFormat = "FILEFORMAT";
+ public const string Keyword_FileVersion = "FILEVERSION";
+
+ public const string Keyword_TransitionLayerCount = "TransitionLayerCount";
+ public const string Keyword_BottomLightOffDelay = "BottomLightOffDelay";
+ public const string Keyword_LightOffDelay = "LightOffDelay";
+ public const string Keyword_BottomWaitTimeBeforeCure = "BottomWaitTimeBeforeCure";
+ public const string Keyword_WaitTimeBeforeCure = "WaitTimeBeforeCure";
+ public const string Keyword_BottomWaitTimeAfterCure = "BottomWaitTimeAfterCure";
+ public const string Keyword_WaitTimeAfterCure = "WaitTimeAfterCure";
+ public const string Keyword_BottomLiftHeight = "BottomLiftHeight";
+ public const string Keyword_BottomLiftSpeed = "BottomLiftSpeed";
+ public const string Keyword_LiftHeight = "LiftHeight";
+ public const string Keyword_LiftSpeed = "LiftSpeed";
+ public const string Keyword_BottomLiftHeight2 = "BottomLiftHeight2";
+ public const string Keyword_BottomLiftSpeed2 = "BottomLiftSpeed2";
+ public const string Keyword_LiftHeight2 = "LiftHeight2";
+ public const string Keyword_LiftSpeed2 = "LiftSpeed2";
+ public const string Keyword_BottomWaitTimeAfterLift = "BottomWaitTimeAfterLift";
+ public const string Keyword_WaitTimeAfterLift = "WaitTimeAfterLift";
+ public const string Keyword_BottomRetractSpeed = "BottomRetractSpeed";
+ public const string Keyword_RetractSpeed = "RetractSpeed";
+ public const string Keyword_BottomRetractHeight2 = "BottomRetractHeight2";
+ public const string Keyword_BottomRetractSpeed2 = "BottomRetractSpeed2";
+ public const string Keyword_RetractHeight2 = "RetractHeight2";
+ public const string Keyword_RetractSpeed2 = "RetractSpeed2";
+ public const string Keyword_BottomLightPWM = "BottomLightPWM";
+ public const string Keyword_LightPWM = "LightPWM";
+ #endregion
+
+ #region Sub Classes
+
+ #region Printer
+ public class Printer
{
- #region Constants
-
- public const string IniConfig = "config.ini";
- public const string IniPrusaslicer = "prusaslicer.ini";
-
- public const string Keyword_FileFormat = "FILEFORMAT";
- public const string Keyword_FileVersion = "FILEVERSION";
-
- public const string Keyword_TransitionLayerCount = "TransitionLayerCount";
- public const string Keyword_BottomLightOffDelay = "BottomLightOffDelay";
- public const string Keyword_LightOffDelay = "LightOffDelay";
- public const string Keyword_BottomWaitTimeBeforeCure = "BottomWaitTimeBeforeCure";
- public const string Keyword_WaitTimeBeforeCure = "WaitTimeBeforeCure";
- public const string Keyword_BottomWaitTimeAfterCure = "BottomWaitTimeAfterCure";
- public const string Keyword_WaitTimeAfterCure = "WaitTimeAfterCure";
- public const string Keyword_BottomLiftHeight = "BottomLiftHeight";
- public const string Keyword_BottomLiftSpeed = "BottomLiftSpeed";
- public const string Keyword_LiftHeight = "LiftHeight";
- public const string Keyword_LiftSpeed = "LiftSpeed";
- public const string Keyword_BottomLiftHeight2 = "BottomLiftHeight2";
- public const string Keyword_BottomLiftSpeed2 = "BottomLiftSpeed2";
- public const string Keyword_LiftHeight2 = "LiftHeight2";
- public const string Keyword_LiftSpeed2 = "LiftSpeed2";
- public const string Keyword_BottomWaitTimeAfterLift = "BottomWaitTimeAfterLift";
- public const string Keyword_WaitTimeAfterLift = "WaitTimeAfterLift";
- public const string Keyword_BottomRetractSpeed = "BottomRetractSpeed";
- public const string Keyword_RetractSpeed = "RetractSpeed";
- public const string Keyword_BottomRetractHeight2 = "BottomRetractHeight2";
- public const string Keyword_BottomRetractSpeed2 = "BottomRetractSpeed2";
- public const string Keyword_RetractHeight2 = "RetractHeight2";
- public const string Keyword_RetractSpeed2 = "RetractSpeed2";
- public const string Keyword_BottomLightPWM = "BottomLightPWM";
- public const string Keyword_LightPWM = "LightPWM";
+ #region Printer
+ public string InheritsCummulative { get; set; } = string.Empty;
+ public string HostType { get; set; } = "octoprint";
+ public string PhysicalPrinterSettingsId { get; set; } = string.Empty;
+ public string PrinterSettingsId { get; set; } = string.Empty;
+ public string PrinterTechnology { get; set; } = "SLA";
+ public string PrinterModel { get; set; } = "SL1";
+ public string PrinterVariant { get; set; } = "default";
+ public string PrinterVendor { get; set; } = string.Empty;
+ public string DefaultSlaMaterialProfile { get; set; } = string.Empty;
+ public string DefaultSlaPrintProfile { get; set; } = "0.05 Normal";
+ public string PrinterNotes { get; set; } = string.Empty;
+ public string Thumbnails { get; set; } = "400x400,800x480";
#endregion
- #region Sub Classes
+ #region Size and Coordinates
+ public string BedCustomModel { get; set; } = string.Empty;
+ public string BedCustomTexture { get; set; } = string.Empty;
+ public string BedShape { get; set; } = "1.48x1.02,119.48x1.02,119.48x67.02,1.48x67.02";
+ public float MaxPrintHeight { get; set; } = 150;
+ #endregion
- #region Printer
- public class Printer
- {
- #region Printer
- public string InheritsCummulative { get; set; }
- public string HostType { get; set; } = "octoprint";
- public string PhysicalPrinterSettingsId { get; set; }
- public string PrinterSettingsId { get; set; }
- public string PrinterTechnology { get; set; } = "SLA";
- public string PrinterModel { get; set; } = "SL1";
- public string PrinterVariant { get; set; } = "default";
- public string PrinterVendor { get; set; }
- public string DefaultSlaMaterialProfile { get; set; }
- public string DefaultSlaPrintProfile { get; set; } = "0.05 Normal";
- public string PrinterNotes { get; set; }
- public string Thumbnails { get; set; } = "400x400,800x480";
- #endregion
-
- #region Size and Coordinates
- public string BedCustomModel { get; set; }
- public string BedCustomTexture { get; set; }
- public string BedShape { get; set; } = "1.48x1.02,119.48x1.02,119.48x67.02,1.48x67.02";
- public float MaxPrintHeight { get; set; } = 150;
- #endregion
-
- #region Display
- public float DisplayWidth { get; set; }
- public float DisplayHeight { get; set; }
- public uint DisplayPixelsX { get; set; }
- public uint DisplayPixelsY { get; set; }
- public string DisplayOrientation { get; set; } = "portrait";
- public byte DisplayMirrorX { get; set; } = 1;
- public byte DisplayMirrorY { get; set; }
- #endregion
-
- #region Tilt
-
- public float FastTiltTime { get; set; } = 5;
- public float SlowTiltTime { get; set; } = 8;
- public float AreaFill { get; set; } = 50;
- #endregion
-
- #region Corrections
-
- public string RelativeCorrection { get; set; } = "1,1";
- public float RelativeCorrectionX { get; set; } = 1;
- public float RelativeCorrectionY { get; set; } = 1;
- public float RelativeCorrectionZ { get; set; } = 1;
- public float AbsoluteCorrection { get; set; }
- public float ElefantFootCompensation { get; set; } = 0.2f;
- public float ElefantFootMinWidth { get; set; } = 0.2f;
- public float GammaCorrection { get; set; } = 1;
-
- #endregion
-
- #region Exposure
-
- public float MinExposureTime { get; set; } = 1;
- public float MaxExposureTime { get; set; } = 120;
- public float MinInitialExposureTime { get; set; } = 1;
- public float MaxInitialExposureTime { get; set; } = 300;
-
- #endregion
-
- #region Overrides
- public override string ToString()
- {
- return $"{nameof(PrinterSettingsId)}: {PrinterSettingsId}, {nameof(PrinterTechnology)}: {PrinterTechnology}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(PrinterVariant)}: {PrinterVariant}, {nameof(PrinterVendor)}: {PrinterVendor}, {nameof(DefaultSlaMaterialProfile)}: {DefaultSlaMaterialProfile}, {nameof(DefaultSlaPrintProfile)}: {DefaultSlaPrintProfile}, {nameof(PrinterNotes)}: {PrinterNotes}, {nameof(Thumbnails)}: {Thumbnails}, {nameof(BedCustomModel)}: {BedCustomModel}, {nameof(BedCustomTexture)}: {BedCustomTexture}, {nameof(BedShape)}: {BedShape}, {nameof(MaxPrintHeight)}: {MaxPrintHeight}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(DisplayPixelsX)}: {DisplayPixelsX}, {nameof(DisplayPixelsY)}: {DisplayPixelsY}, {nameof(DisplayOrientation)}: {DisplayOrientation}, {nameof(DisplayMirrorX)}: {DisplayMirrorX}, {nameof(DisplayMirrorY)}: {DisplayMirrorY}, {nameof(FastTiltTime)}: {FastTiltTime}, {nameof(SlowTiltTime)}: {SlowTiltTime}, {nameof(AreaFill)}: {AreaFill}, {nameof(RelativeCorrection)}: {RelativeCorrection}, {nameof(AbsoluteCorrection)}: {AbsoluteCorrection}, {nameof(ElefantFootCompensation)}: {ElefantFootCompensation}, {nameof(ElefantFootMinWidth)}: {ElefantFootMinWidth}, {nameof(GammaCorrection)}: {GammaCorrection}, {nameof(MinExposureTime)}: {MinExposureTime}, {nameof(MaxExposureTime)}: {MaxExposureTime}, {nameof(MinInitialExposureTime)}: {MinInitialExposureTime}, {nameof(MaxInitialExposureTime)}: {MaxInitialExposureTime}";
- }
- #endregion
- }
+ #region Display
+ public float DisplayWidth { get; set; }
+ public float DisplayHeight { get; set; }
+ public uint DisplayPixelsX { get; set; }
+ public uint DisplayPixelsY { get; set; }
+ public string DisplayOrientation { get; set; } = "portrait";
+ public byte DisplayMirrorX { get; set; } = 1;
+ public byte DisplayMirrorY { get; set; }
#endregion
- #region Material
- public class Material
- {
- #region Material
- public string MaterialVendor { get; set; }
- public string MaterialType { get; set; }
- public string SlaMaterialSettingsId { get; set; }
- public string MaterialColour { get; set; } = "#29B2B2";
- public float BottleCost { get; set; }
- public float BottleVolume { get; set; }
- public float BottleWeight { get; set; }
- public float MaterialDensity { get; set; }
- public string MaterialNotes { get; set; }
+ #region Tilt
- #endregion
+ public float FastTiltTime { get; set; } = 5;
+ public float SlowTiltTime { get; set; } = 8;
+ public float AreaFill { get; set; } = 50;
+ #endregion
- #region Layers
+ #region Corrections
- public float InitialLayerHeight { get; set; }
- #endregion
+ public string RelativeCorrection { get; set; } = "1,1";
+ public float RelativeCorrectionX { get; set; } = 1;
+ public float RelativeCorrectionY { get; set; } = 1;
+ public float RelativeCorrectionZ { get; set; } = 1;
+ public float AbsoluteCorrection { get; set; }
+ public float ElefantFootCompensation { get; set; } = 0.2f;
+ public float ElefantFootMinWidth { get; set; } = 0.2f;
+ public float GammaCorrection { get; set; } = 1;
- #region Exposure
+ #endregion
- public float ExposureTime { get; set; }
- public float InitialExposureTime { get; set; }
- #endregion
+ #region Exposure
- #region Corrections
- public string MaterialCorrection { get; set; } = "1,1,1";
- public float MaterialCorrectionX { get; set; } = 1;
- public float MaterialCorrectionY { get; set; } = 1;
- public float MaterialCorrectionZ { get; set; } = 1;
+ public float MinExposureTime { get; set; } = 1;
+ public float MaxExposureTime { get; set; } = 120;
+ public float MinInitialExposureTime { get; set; } = 1;
+ public float MaxInitialExposureTime { get; set; } = 300;
- #endregion
+ #endregion
- #region Material print profile
+ #region Overrides
+ public override string ToString()
+ {
+ return $"{nameof(PrinterSettingsId)}: {PrinterSettingsId}, {nameof(PrinterTechnology)}: {PrinterTechnology}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(PrinterVariant)}: {PrinterVariant}, {nameof(PrinterVendor)}: {PrinterVendor}, {nameof(DefaultSlaMaterialProfile)}: {DefaultSlaMaterialProfile}, {nameof(DefaultSlaPrintProfile)}: {DefaultSlaPrintProfile}, {nameof(PrinterNotes)}: {PrinterNotes}, {nameof(Thumbnails)}: {Thumbnails}, {nameof(BedCustomModel)}: {BedCustomModel}, {nameof(BedCustomTexture)}: {BedCustomTexture}, {nameof(BedShape)}: {BedShape}, {nameof(MaxPrintHeight)}: {MaxPrintHeight}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(DisplayPixelsX)}: {DisplayPixelsX}, {nameof(DisplayPixelsY)}: {DisplayPixelsY}, {nameof(DisplayOrientation)}: {DisplayOrientation}, {nameof(DisplayMirrorX)}: {DisplayMirrorX}, {nameof(DisplayMirrorY)}: {DisplayMirrorY}, {nameof(FastTiltTime)}: {FastTiltTime}, {nameof(SlowTiltTime)}: {SlowTiltTime}, {nameof(AreaFill)}: {AreaFill}, {nameof(RelativeCorrection)}: {RelativeCorrection}, {nameof(AbsoluteCorrection)}: {AbsoluteCorrection}, {nameof(ElefantFootCompensation)}: {ElefantFootCompensation}, {nameof(ElefantFootMinWidth)}: {ElefantFootMinWidth}, {nameof(GammaCorrection)}: {GammaCorrection}, {nameof(MinExposureTime)}: {MinExposureTime}, {nameof(MaxExposureTime)}: {MaxExposureTime}, {nameof(MinInitialExposureTime)}: {MinInitialExposureTime}, {nameof(MaxInitialExposureTime)}: {MaxInitialExposureTime}";
+ }
+ #endregion
+ }
+ #endregion
- public string MaterialPrintSpeed { get; set; } = "fast";
+ #region Material
+ public class Material
+ {
+ #region Material
+ public string MaterialVendor { get; set; } = string.Empty;
+ public string MaterialType { get; set; } = string.Empty;
+ public string SlaMaterialSettingsId { get; set; } = string.Empty;
+ public string MaterialColour { get; set; } = "#29B2B2";
+ public float BottleCost { get; set; }
+ public float BottleVolume { get; set; }
+ public float BottleWeight { get; set; }
+ public float MaterialDensity { get; set; }
+ public string MaterialNotes { get; set; } = string.Empty;
- #endregion
+ #endregion
- #region Dependencies
+ #region Layers
- public string CompatiblePrintersConditionCummulative { get; set; }
- public string CompatiblePrintsConditionCummulative { get; set; }
+ public float InitialLayerHeight { get; set; }
+ #endregion
- #endregion
+ #region Exposure
- #region Overrides
- public override string ToString()
- {
- return $"{nameof(MaterialVendor)}: {MaterialVendor}, {nameof(MaterialType)}: {MaterialType}, {nameof(SlaMaterialSettingsId)}: {SlaMaterialSettingsId}, {nameof(BottleCost)}: {BottleCost}, {nameof(BottleVolume)}: {BottleVolume}, {nameof(BottleWeight)}: {BottleWeight}, {nameof(MaterialDensity)}: {MaterialDensity}, {nameof(MaterialNotes)}: {MaterialNotes}, {nameof(InitialLayerHeight)}: {InitialLayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(InitialExposureTime)}: {InitialExposureTime}, {nameof(MaterialCorrection)}: {MaterialCorrection}, {nameof(CompatiblePrintersConditionCummulative)}: {CompatiblePrintersConditionCummulative}, {nameof(CompatiblePrintsConditionCummulative)}: {CompatiblePrintsConditionCummulative}";
- }
- #endregion
- }
+ public float ExposureTime { get; set; }
+ public float InitialExposureTime { get; set; }
#endregion
- #region Print
+ #region Corrections
+ public string MaterialCorrection { get; set; } = "1,1,1";
+ public float MaterialCorrectionX { get; set; } = 1;
+ public float MaterialCorrectionY { get; set; } = 1;
+ public float MaterialCorrectionZ { get; set; } = 1;
- public class Print
- {
- #region Print
- public string SlaPrintSettingsId { get; set; }
- #endregion
+ #endregion
- #region Layers
+ #region Material print profile
- public float LayerHeight { get; set; }
- public ushort FadedLayers { get; set; }
- #endregion
+ public string MaterialPrintSpeed { get; set; } = "fast";
- #region Supports
+ #endregion
- public byte SupportsEnable { get; set; }
-
- public float SupportHeadFrontDiameter { get; set; }
- public float SupportHeadPenetration { get; set; }
- public float SupportHeadWidth { get; set; }
-
- public ushort SupportPillarWideningFactor { set; get; }
- public float SupportPillarDiameter { get; set; }
- public string SupportSmallPillarDiameterPercent { get; set; }
- public float SupportMaxBridgesOnPillar { get; set; }
- public string SupportPillarConnectionMode { get; set; }
- public byte SupportBuildplateOnly { get; set; }
- public float SupportBaseDiameter { get; set; }
- public float SupportBaseHeight { get; set; }
- public float SupportBaseSafetyDistance { get; set; }
- public byte PadAroundObject { get; set; }
- public float SupportObjectElevation { get; set; }
-
-
- public ushort SupportCriticalAngle { get; set; }
- public float SupportMaxBridgeLength { get; set; }
- public float SupportMaxPillarLinkDistance { get; set; }
-
-
- public ushort SupportPointsDensityRelative { get; set; }
- public float SupportPointsMinimalDistance { get; set; }
-
- #endregion
-
- #region Pad
-
- public byte PadEnable { set; get; }
- public float PadWallThickness { set; get; }
- public float PadWallHeight { set; get; }
- public float PadBrimSize { set; get; }
- public float PadMaxMergeDistance { set; get; }
- public float PadWallSlope { set; get; }
- //public float PadAroundObject { set; get; }
- public byte PadAroundObjectEverywhere { set; get; }
- public float PadObjectGap { set; get; }
- public float PadObjectConnectorStride { set; get; }
- public float PadObjectConnectorWidth { set; get; }
- public float PadObjectConnectorPenetration { set; get; }
- #endregion
-
- #region Hollowing
- public byte HollowingEnable { set; get; }
- public float HollowingMinThickness { set; get; }
- public float HollowingQuality { set; get; }
- public float HollowingClosingDistance { set; get; }
- #endregion
-
- #region Advanced
- public float SliceClosingRadius { set; get; }
- public string SlicingMode { set; get; } = "regular";
- #endregion
-
- #region Output
- public string OutputFilenameFormat { set; get; }
- #endregion
-
- #region Dependencies
- public string CompatiblePrintsCondition { set; get; }
- #endregion
-
- #region Overrides
- public override string ToString()
- {
- return $"{nameof(SlaPrintSettingsId)}: {SlaPrintSettingsId}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(FadedLayers)}: {FadedLayers}, {nameof(SupportsEnable)}: {SupportsEnable}, {nameof(SupportHeadFrontDiameter)}: {SupportHeadFrontDiameter}, {nameof(SupportHeadPenetration)}: {SupportHeadPenetration}, {nameof(SupportHeadWidth)}: {SupportHeadWidth}, {nameof(SupportPillarWideningFactor)}: {SupportPillarWideningFactor}, {nameof(SupportPillarDiameter)}: {SupportPillarDiameter}, {nameof(SupportSmallPillarDiameterPercent)}: {SupportSmallPillarDiameterPercent}, {nameof(SupportMaxBridgesOnPillar)}: {SupportMaxBridgesOnPillar}, {nameof(SupportPillarConnectionMode)}: {SupportPillarConnectionMode}, {nameof(SupportBuildplateOnly)}: {SupportBuildplateOnly}, {nameof(SupportBaseDiameter)}: {SupportBaseDiameter}, {nameof(SupportBaseHeight)}: {SupportBaseHeight}, {nameof(SupportBaseSafetyDistance)}: {SupportBaseSafetyDistance}, {nameof(PadAroundObject)}: {PadAroundObject}, {nameof(SupportObjectElevation)}: {SupportObjectElevation}, {nameof(SupportCriticalAngle)}: {SupportCriticalAngle}, {nameof(SupportMaxBridgeLength)}: {SupportMaxBridgeLength}, {nameof(SupportMaxPillarLinkDistance)}: {SupportMaxPillarLinkDistance}, {nameof(SupportPointsDensityRelative)}: {SupportPointsDensityRelative}, {nameof(SupportPointsMinimalDistance)}: {SupportPointsMinimalDistance}, {nameof(PadEnable)}: {PadEnable}, {nameof(PadWallThickness)}: {PadWallThickness}, {nameof(PadWallHeight)}: {PadWallHeight}, {nameof(PadBrimSize)}: {PadBrimSize}, {nameof(PadMaxMergeDistance)}: {PadMaxMergeDistance}, {nameof(PadWallSlope)}: {PadWallSlope}, {nameof(PadAroundObjectEverywhere)}: {PadAroundObjectEverywhere}, {nameof(PadObjectGap)}: {PadObjectGap}, {nameof(PadObjectConnectorStride)}: {PadObjectConnectorStride}, {nameof(PadObjectConnectorWidth)}: {PadObjectConnectorWidth}, {nameof(PadObjectConnectorPenetration)}: {PadObjectConnectorPenetration}, {nameof(HollowingEnable)}: {HollowingEnable}, {nameof(HollowingMinThickness)}: {HollowingMinThickness}, {nameof(HollowingQuality)}: {HollowingQuality}, {nameof(HollowingClosingDistance)}: {HollowingClosingDistance}, {nameof(SliceClosingRadius)}: {SliceClosingRadius}, {nameof(OutputFilenameFormat)}: {OutputFilenameFormat}, {nameof(CompatiblePrintsCondition)}: {CompatiblePrintsCondition}";
- }
- #endregion
- }
+ #region Dependencies
- #endregion
+ public string CompatiblePrintersConditionCummulative { get; set; } = string.Empty;
+ public string CompatiblePrintsConditionCummulative { get; set; } = string.Empty;
- #region OutputConfig
+ #endregion
- public class OutputConfig
+ #region Overrides
+ public override string ToString()
{
- public string Action { get; set; } = "print";
- public string JobDir { get; set; }
- public float ExpTime { get; set; }
- public float ExpTimeFirst { get; set; }
- public float ExpUserProfile { get; set; }
- //public string FileCreationTimestamp { get; set; }
- public string FileCreationTimestamp {
- get
- {
- //2021-01-23 at 04:07:36 UTC
- var now = DateTime.UtcNow;
- return $"{now.Year}-{now.Month:D2}-{now.Day:D2} at {now.Hour:D2}:{now.Minute:D2}:{now.Second:D2} {now.Kind}";
- }
- set{}
- }
- public byte Hollow { get; set; }
- public float LayerHeight { get; set; }
- public string MaterialName { get; set; } = About.Software;
- public ushort NumFade { get; set; }
- public uint NumFast { get; set; }
- public ushort NumSlow { get; set; }
- public string PrintProfile { get; set; } = About.Software;
- public float PrintTime { get; set; }
- public string PrinterModel { get; set; } = "SL1";
- public string PrinterProfile { get; set; } = About.Software;
- public string PrinterVariant { get; set; } = "default";
- public string PrusaSlicerVersion { get; set; } = "PrusaSlicer-2.3.3+win64-202107161027";
- public float UsedMaterial { get; set; }
-
- public override string ToString()
- {
- return $"{nameof(Action)}: {Action}, {nameof(JobDir)}: {JobDir}, {nameof(ExpTime)}: {ExpTime}, {nameof(ExpTimeFirst)}: {ExpTimeFirst}, {nameof(FileCreationTimestamp)}: {FileCreationTimestamp}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(MaterialName)}: {MaterialName}, {nameof(NumFade)}: {NumFade}, {nameof(NumFast)}: {NumFast}, {nameof(NumSlow)}: {NumSlow}, {nameof(PrintProfile)}: {PrintProfile}, {nameof(PrintTime)}: {PrintTime}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(PrinterProfile)}: {PrinterProfile}, {nameof(PrinterVariant)}: {PrinterVariant}, {nameof(PrusaSlicerVersion)}: {PrusaSlicerVersion}, {nameof(UsedMaterial)}: {UsedMaterial}";
- }
+ return $"{nameof(MaterialVendor)}: {MaterialVendor}, {nameof(MaterialType)}: {MaterialType}, {nameof(SlaMaterialSettingsId)}: {SlaMaterialSettingsId}, {nameof(BottleCost)}: {BottleCost}, {nameof(BottleVolume)}: {BottleVolume}, {nameof(BottleWeight)}: {BottleWeight}, {nameof(MaterialDensity)}: {MaterialDensity}, {nameof(MaterialNotes)}: {MaterialNotes}, {nameof(InitialLayerHeight)}: {InitialLayerHeight}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(InitialExposureTime)}: {InitialExposureTime}, {nameof(MaterialCorrection)}: {MaterialCorrection}, {nameof(CompatiblePrintersConditionCummulative)}: {CompatiblePrintersConditionCummulative}, {nameof(CompatiblePrintsConditionCummulative)}: {CompatiblePrintsConditionCummulative}";
}
+ #endregion
+ }
+ #endregion
+ #region Print
+
+ public class Print
+ {
+ #region Print
+ public string SlaPrintSettingsId { get; set; } = string.Empty;
#endregion
+ #region Layers
+
+ public float LayerHeight { get; set; }
+ public ushort FadedLayers { get; set; }
#endregion
- #region Properties
- public Printer PrinterSettings { get; private set; } = new();
+ #region Supports
- public Material MaterialSettings { get; private set; } = new();
+ public byte SupportsEnable { get; set; }
+
+ public float SupportHeadFrontDiameter { get; set; }
+ public float SupportHeadPenetration { get; set; }
+ public float SupportHeadWidth { get; set; }
- public Print PrintSettings { get; private set; } = new();
+ public ushort SupportPillarWideningFactor { set; get; }
+ public float SupportPillarDiameter { get; set; }
+ public string SupportSmallPillarDiameterPercent { get; set; } = string.Empty;
+ public float SupportMaxBridgesOnPillar { get; set; }
+ public string SupportPillarConnectionMode { get; set; } = string.Empty;
+ public byte SupportBuildplateOnly { get; set; }
+ public float SupportBaseDiameter { get; set; }
+ public float SupportBaseHeight { get; set; }
+ public float SupportBaseSafetyDistance { get; set; }
+ public byte PadAroundObject { get; set; }
+ public float SupportObjectElevation { get; set; }
- public OutputConfig OutputConfigSettings { get; private set; } = new();
- public Statistics Statistics { get; } = new ();
+ public ushort SupportCriticalAngle { get; set; }
+ public float SupportMaxBridgeLength { get; set; }
+ public float SupportMaxPillarLinkDistance { get; set; }
- public override FileFormatType FileType => FileFormatType.Archive;
+ public ushort SupportPointsDensityRelative { get; set; }
+ public float SupportPointsMinimalDistance { get; set; }
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(SL1File), "sl1", "PrusaSlicer SL1"),
- new(typeof(SL1File), "sl1s", "PrusaSlicer SL1S Speed")
- };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
- };
+ #endregion
- public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } =
- {
- new(400, 400),
- new(800, 480)
- };
+ #region Pad
+
+ public byte PadEnable { set; get; }
+ public float PadWallThickness { set; get; }
+ public float PadWallHeight { set; get; }
+ public float PadBrimSize { set; get; }
+ public float PadMaxMergeDistance { set; get; }
+ public float PadWallSlope { set; get; }
+ //public float PadAroundObject { set; get; }
+ public byte PadAroundObjectEverywhere { set; get; }
+ public float PadObjectGap { set; get; }
+ public float PadObjectConnectorStride { set; get; }
+ public float PadObjectConnectorWidth { set; get; }
+ public float PadObjectConnectorPenetration { set; get; }
+ #endregion
- public override uint ResolutionX
- {
- get => PrinterSettings.DisplayPixelsX;
- set
- {
- PrinterSettings.DisplayPixelsX = value;
- RaisePropertyChanged();
- }
- }
+ #region Hollowing
+ public byte HollowingEnable { set; get; }
+ public float HollowingMinThickness { set; get; }
+ public float HollowingQuality { set; get; }
+ public float HollowingClosingDistance { set; get; }
+ #endregion
- public override uint ResolutionY
- {
- get => PrinterSettings.DisplayPixelsY;
- set
- {
- PrinterSettings.DisplayPixelsY = value;
- RaisePropertyChanged();
- }
- }
+ #region Advanced
+ public float SliceClosingRadius { set; get; }
+ public string SlicingMode { set; get; } = "regular";
+ #endregion
- public override float DisplayWidth
+ #region Output
+ public string OutputFilenameFormat { set; get; } = string.Empty;
+ #endregion
+
+ #region Dependencies
+ public string CompatiblePrintsCondition { set; get; } = string.Empty;
+ #endregion
+
+ #region Overrides
+ public override string ToString()
{
- get => PrinterSettings.DisplayWidth;
- set
- {
- PrinterSettings.DisplayWidth = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ return $"{nameof(SlaPrintSettingsId)}: {SlaPrintSettingsId}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(FadedLayers)}: {FadedLayers}, {nameof(SupportsEnable)}: {SupportsEnable}, {nameof(SupportHeadFrontDiameter)}: {SupportHeadFrontDiameter}, {nameof(SupportHeadPenetration)}: {SupportHeadPenetration}, {nameof(SupportHeadWidth)}: {SupportHeadWidth}, {nameof(SupportPillarWideningFactor)}: {SupportPillarWideningFactor}, {nameof(SupportPillarDiameter)}: {SupportPillarDiameter}, {nameof(SupportSmallPillarDiameterPercent)}: {SupportSmallPillarDiameterPercent}, {nameof(SupportMaxBridgesOnPillar)}: {SupportMaxBridgesOnPillar}, {nameof(SupportPillarConnectionMode)}: {SupportPillarConnectionMode}, {nameof(SupportBuildplateOnly)}: {SupportBuildplateOnly}, {nameof(SupportBaseDiameter)}: {SupportBaseDiameter}, {nameof(SupportBaseHeight)}: {SupportBaseHeight}, {nameof(SupportBaseSafetyDistance)}: {SupportBaseSafetyDistance}, {nameof(PadAroundObject)}: {PadAroundObject}, {nameof(SupportObjectElevation)}: {SupportObjectElevation}, {nameof(SupportCriticalAngle)}: {SupportCriticalAngle}, {nameof(SupportMaxBridgeLength)}: {SupportMaxBridgeLength}, {nameof(SupportMaxPillarLinkDistance)}: {SupportMaxPillarLinkDistance}, {nameof(SupportPointsDensityRelative)}: {SupportPointsDensityRelative}, {nameof(SupportPointsMinimalDistance)}: {SupportPointsMinimalDistance}, {nameof(PadEnable)}: {PadEnable}, {nameof(PadWallThickness)}: {PadWallThickness}, {nameof(PadWallHeight)}: {PadWallHeight}, {nameof(PadBrimSize)}: {PadBrimSize}, {nameof(PadMaxMergeDistance)}: {PadMaxMergeDistance}, {nameof(PadWallSlope)}: {PadWallSlope}, {nameof(PadAroundObjectEverywhere)}: {PadAroundObjectEverywhere}, {nameof(PadObjectGap)}: {PadObjectGap}, {nameof(PadObjectConnectorStride)}: {PadObjectConnectorStride}, {nameof(PadObjectConnectorWidth)}: {PadObjectConnectorWidth}, {nameof(PadObjectConnectorPenetration)}: {PadObjectConnectorPenetration}, {nameof(HollowingEnable)}: {HollowingEnable}, {nameof(HollowingMinThickness)}: {HollowingMinThickness}, {nameof(HollowingQuality)}: {HollowingQuality}, {nameof(HollowingClosingDistance)}: {HollowingClosingDistance}, {nameof(SliceClosingRadius)}: {SliceClosingRadius}, {nameof(OutputFilenameFormat)}: {OutputFilenameFormat}, {nameof(CompatiblePrintsCondition)}: {CompatiblePrintsCondition}";
}
+ #endregion
+ }
- public override float DisplayHeight
- {
- get => PrinterSettings.DisplayHeight;
- set
+ #endregion
+
+ #region OutputConfig
+
+ public class OutputConfig
+ {
+ public string Action { get; set; } = "print";
+ public string JobDir { get; set; } = string.Empty;
+ public float ExpTime { get; set; }
+ public float ExpTimeFirst { get; set; }
+ public float ExpUserProfile { get; set; }
+ //public string FileCreationTimestamp { get; set; }
+ public string FileCreationTimestamp {
+ get
{
- PrinterSettings.DisplayHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
+ //2021-01-23 at 04:07:36 UTC
+ var now = DateTime.UtcNow;
+ return $"{now.Year}-{now.Month:D2}-{now.Day:D2} at {now.Hour:D2}:{now.Minute:D2}:{now.Second:D2} {now.Kind}";
}
+ set{}
}
+ public byte Hollow { get; set; }
+ public float LayerHeight { get; set; }
+ public string? MaterialName { get; set; } = About.Software;
+ public ushort NumFade { get; set; }
+ public uint NumFast { get; set; }
+ public ushort NumSlow { get; set; }
+ public string PrintProfile { get; set; } = About.Software;
+ public float PrintTime { get; set; }
+ public string PrinterModel { get; set; } = "SL1";
+ public string PrinterProfile { get; set; } = About.Software;
+ public string PrinterVariant { get; set; } = "default";
+ public string PrusaSlicerVersion { get; set; } = "PrusaSlicer-2.3.3+win64-202107161027";
+ public float UsedMaterial { get; set; }
- public override float MachineZ
+ public override string ToString()
{
- get => PrinterSettings.MaxPrintHeight;
- set
- {
- PrinterSettings.MaxPrintHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ return $"{nameof(Action)}: {Action}, {nameof(JobDir)}: {JobDir}, {nameof(ExpTime)}: {ExpTime}, {nameof(ExpTimeFirst)}: {ExpTimeFirst}, {nameof(FileCreationTimestamp)}: {FileCreationTimestamp}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(MaterialName)}: {MaterialName}, {nameof(NumFade)}: {NumFade}, {nameof(NumFast)}: {NumFast}, {nameof(NumSlow)}: {NumSlow}, {nameof(PrintProfile)}: {PrintProfile}, {nameof(PrintTime)}: {PrintTime}, {nameof(PrinterModel)}: {PrinterModel}, {nameof(PrinterProfile)}: {PrinterProfile}, {nameof(PrinterVariant)}: {PrinterVariant}, {nameof(PrusaSlicerVersion)}: {PrusaSlicerVersion}, {nameof(UsedMaterial)}: {UsedMaterial}";
}
+ }
- public override Enumerations.FlipDirection DisplayMirror
- {
- get
- {
- if (PrinterSettings.DisplayMirrorX > 0 && PrinterSettings.DisplayMirrorY > 0) return Enumerations.FlipDirection.Both;
- if (PrinterSettings.DisplayMirrorX > 0) return Enumerations.FlipDirection.Horizontally;
- if (PrinterSettings.DisplayMirrorY > 0) return Enumerations.FlipDirection.Vertically;
- return Enumerations.FlipDirection.None;
- }
- set
- {
- PrinterSettings.DisplayMirrorX = (byte)(value is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both ? 1 : 0);
- PrinterSettings.DisplayMirrorY = (byte)(value is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both ? 1 : 0);
- RaisePropertyChanged();
- }
- }
+ #endregion
+
+ #endregion
+
+ #region Properties
+ public Printer PrinterSettings { get; private set; } = new();
+
+ public Material MaterialSettings { get; private set; } = new();
+
+ public Print PrintSettings { get; private set; } = new();
+
+ public OutputConfig OutputConfigSettings { get; private set; } = new();
+
+ public Statistics Statistics { get; } = new ();
+
+
+ public override FileFormatType FileType => FileFormatType.Archive;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(SL1File), "sl1", "PrusaSlicer SL1"),
+ new(typeof(SL1File), "sl1s", "PrusaSlicer SL1S Speed")
+ };
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+ };
- public override byte AntiAliasing
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(400, 400),
+ new(800, 480)
+ };
+
+ public override uint ResolutionX
+ {
+ get => PrinterSettings.DisplayPixelsX;
+ set
{
- get => (byte)(PrinterSettings.GammaCorrection > 0 ? 4 : 1);
- set => PrinterSettings.GammaCorrection = value > 0 ? 1 : 0;
+ PrinterSettings.DisplayPixelsX = value;
+ RaisePropertyChanged();
}
+ }
- public override float LayerHeight
+ public override uint ResolutionY
+ {
+ get => PrinterSettings.DisplayPixelsY;
+ set
{
- get => OutputConfigSettings.LayerHeight;
- set
- {
- OutputConfigSettings.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ PrinterSettings.DisplayPixelsY = value;
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
+ public override float DisplayWidth
+ {
+ get => PrinterSettings.DisplayWidth;
+ set
{
- get => base.LayerCount;
- set
- {
- OutputConfigSettings.NumSlow = 0;
- base.LayerCount = OutputConfigSettings.NumFast = base.LayerCount;
- }
+ PrinterSettings.DisplayWidth = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override ushort BottomLayerCount
+ public override float DisplayHeight
+ {
+ get => PrinterSettings.DisplayHeight;
+ set
{
- get => OutputConfigSettings.NumFade;
- set => base.BottomLayerCount = OutputConfigSettings.NumFade = value;
+ PrinterSettings.DisplayHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float BottomExposureTime
+ public override float MachineZ
+ {
+ get => PrinterSettings.MaxPrintHeight;
+ set
{
- get => OutputConfigSettings.ExpTimeFirst;
- set => base.BottomExposureTime = OutputConfigSettings.ExpTimeFirst = (float)Math.Round(value, 2);
+ PrinterSettings.MaxPrintHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float ExposureTime
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get
{
- get => OutputConfigSettings.ExpTime;
- set => base.ExposureTime = OutputConfigSettings.ExpTime = (float)Math.Round(value, 2);
+ if (PrinterSettings.DisplayMirrorX > 0 && PrinterSettings.DisplayMirrorY > 0) return Enumerations.FlipDirection.Both;
+ if (PrinterSettings.DisplayMirrorX > 0) return Enumerations.FlipDirection.Horizontally;
+ if (PrinterSettings.DisplayMirrorY > 0) return Enumerations.FlipDirection.Vertically;
+ return Enumerations.FlipDirection.None;
}
+ set
+ {
+ PrinterSettings.DisplayMirrorX = (byte)(value is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both ? 1 : 0);
+ PrinterSettings.DisplayMirrorY = (byte)(value is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both ? 1 : 0);
+ RaisePropertyChanged();
+ }
+ }
+
+ public override byte AntiAliasing
+ {
+ get => (byte)(PrinterSettings.GammaCorrection > 0 ? 4 : 1);
+ set => PrinterSettings.GammaCorrection = value > 0 ? 1 : 0;
+ }
- public override float PrintTime
+ public override float LayerHeight
+ {
+ get => OutputConfigSettings.LayerHeight;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- OutputConfigSettings.PrintTime = base.PrintTime;
- }
+ OutputConfigSettings.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override float MaterialMilliliters
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- OutputConfigSettings.UsedMaterial = base.MaterialMilliliters;
- }
+ OutputConfigSettings.NumSlow = 0;
+ base.LayerCount = OutputConfigSettings.NumFast = base.LayerCount;
}
+ }
- public override float MaterialGrams => (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 3);
+ public override ushort BottomLayerCount
+ {
+ get => OutputConfigSettings.NumFade;
+ set => base.BottomLayerCount = OutputConfigSettings.NumFade = value;
+ }
+
+ public override float BottomExposureTime
+ {
+ get => OutputConfigSettings.ExpTimeFirst;
+ set => base.BottomExposureTime = OutputConfigSettings.ExpTimeFirst = (float)Math.Round(value, 2);
+ }
- public override float MaterialCost => MaterialSettings.BottleVolume > 0 ? (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.BottleCost / MaterialSettings.BottleVolume, 3) : 0;
+ public override float ExposureTime
+ {
+ get => OutputConfigSettings.ExpTime;
+ set => base.ExposureTime = OutputConfigSettings.ExpTime = (float)Math.Round(value, 2);
+ }
- public override string MaterialName
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => OutputConfigSettings.MaterialName;
- set => base.MaterialName = OutputConfigSettings.MaterialName = value;
- }
+ base.PrintTime = value;
+ OutputConfigSettings.PrintTime = base.PrintTime;
+ }
+ }
- public override string MachineName
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => PrinterSettings.PrinterSettingsId;
- set => base.MachineName = PrinterSettings.PrinterSettingsId = value;
+ base.MaterialMilliliters = value;
+ OutputConfigSettings.UsedMaterial = base.MaterialMilliliters;
}
+ }
- public override object[] Configs => new object[] { PrinterSettings, MaterialSettings, PrintSettings, OutputConfigSettings };
- #endregion
+ public override float MaterialGrams => (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 3);
- #region Overrides
- public override string ToString()
- {
- return $"{nameof(FileFullPath)}: {FileFullPath}, {nameof(MaterialSettings)}: {MaterialSettings}, {nameof(PrintSettings)}: {PrintSettings}, {nameof(OutputConfigSettings)}: {OutputConfigSettings}, {nameof(Statistics)}: {Statistics}, {nameof(LayerCount)}: {LayerCount}, {nameof(PrintHeight)}: {PrintHeight}";
- }
+ public override float MaterialCost => MaterialSettings.BottleVolume > 0 ? (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.BottleCost / MaterialSettings.BottleVolume, 3) : 0;
- #endregion
+ public override string? MaterialName
+ {
+ get => OutputConfigSettings.MaterialName;
+ set => base.MaterialName = OutputConfigSettings.MaterialName = value;
+ }
- #region Contructors
- public SL1File() { }
- #endregion
+ public override string MachineName
+ {
+ get => PrinterSettings.PrinterSettingsId;
+ set => base.MachineName = PrinterSettings.PrinterSettingsId = value;
+ }
- #region Static Methods
- public static string IniKeyToMemberName(string keyName)
- {
- string memberName = string.Empty;
- string[] objs = keyName.Split('_');
- return objs.Aggregate(memberName, (current, obj) => current + obj.FirstCharToUpper());
- }
+ public override object[] Configs => new object[] { PrinterSettings, MaterialSettings, PrintSettings, OutputConfigSettings };
+ #endregion
- public static string MemberNameToIniKey(string memberName)
- {
- string iniKey = char.ToLowerInvariant(memberName[0]).ToString();
- for (var i = 1; i < memberName.Length; i++)
- {
- iniKey += char.IsUpper(memberName[i])
- ? $"_{char.ToLowerInvariant(memberName[i])}"
- : memberName[i].ToString();
- }
+ #region Overrides
+ public override string ToString()
+ {
+ return $"{nameof(FileFullPath)}: {FileFullPath}, {nameof(MaterialSettings)}: {MaterialSettings}, {nameof(PrintSettings)}: {PrintSettings}, {nameof(OutputConfigSettings)}: {OutputConfigSettings}, {nameof(Statistics)}: {Statistics}, {nameof(LayerCount)}: {LayerCount}, {nameof(PrintHeight)}: {PrintHeight}";
+ }
+ #endregion
- if (iniKey.EndsWith("_"))
- iniKey.Remove(iniKey.Length - 1);
+ #region Contructors
+ public SL1File() { }
+ #endregion
- return iniKey;
+ #region Static Methods
+ public static string IniKeyToMemberName(string keyName)
+ {
+ string memberName = string.Empty;
+ string[] objs = keyName.Split('_');
+ return objs.Aggregate(memberName, (current, obj) => current + obj.FirstCharToUpper());
+ }
+
+ public static string MemberNameToIniKey(string memberName)
+ {
+ string iniKey = char.ToLowerInvariant(memberName[0]).ToString();
+ for (var i = 1; i < memberName.Length; i++)
+ {
+ iniKey += char.IsUpper(memberName[i])
+ ? $"_{char.ToLowerInvariant(memberName[i])}"
+ : memberName[i].ToString();
}
+
+ if (iniKey.EndsWith("_"))
+ iniKey.Remove(iniKey.Length - 1);
+
+ return iniKey;
+ }
+
- #endregion
+ #endregion
+
+ #region Methods
+ public override void Clear()
+ {
+ base.Clear();
+ Statistics.Clear();
+ }
- #region Methods
- public override void Clear()
+ protected override bool OnBeforeConvertTo(FileFormat output)
+ {
+ int fileVersion = LookupCustomValue(Keyword_FileVersion, int.MinValue);
+ if (fileVersion > 0)
{
- base.Clear();
- Statistics.Clear();
+ output.Version = (uint)fileVersion;
}
- protected override bool OnBeforeConvertTo(FileFormat output)
+ return true;
+ }
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ var filename = FileFullPath;
+ if (filename!.EndsWith(TemporaryFileAppend)) filename = Path.GetFileNameWithoutExtension(filename); // tmp
+ filename = Path.GetFileNameWithoutExtension(filename); // sl1
+ OutputConfigSettings.JobDir = filename;
+ using ZipArchive outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ var entry = outputFile.CreateEntry("config.ini");
+ using (TextWriter tw = new StreamWriter(entry.Open()))
{
- int fileVersion = LookupCustomValue(Keyword_FileVersion, int.MinValue);
- if (fileVersion > 0)
+ var properties = OutputConfigSettings.GetType()
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance);
+
+ foreach (var property in properties)
{
- output.Version = (uint)fileVersion;
+ if (property.Name.Equals("Item")) continue;
+ var name = char.ToLowerInvariant(property.Name[0]) + property.Name[1..];
+ tw.WriteLine($"{name} = {property.GetValue(OutputConfigSettings)}");
}
- return true;
+ tw.Close();
}
- protected override void EncodeInternally(OperationProgress progress)
+ entry = outputFile.CreateEntry(IniPrusaslicer);
+ using (TextWriter tw = new StreamWriter(entry.Open()))
{
- var filename = FileFullPath;
- if (filename.EndsWith(TemporaryFileAppend)) filename = Path.GetFileNameWithoutExtension(filename); // tmp
- filename = Path.GetFileNameWithoutExtension(filename); // sl1
- OutputConfigSettings.JobDir = filename;
- using ZipArchive outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- var entry = outputFile.CreateEntry("config.ini");
- using (TextWriter tw = new StreamWriter(entry.Open()))
+ foreach (var config in Configs)
{
- var properties = OutputConfigSettings.GetType()
- .GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ if (ReferenceEquals(config, OutputConfigSettings))
+ continue;
+
+ var properties = config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
if (property.Name.Equals("Item")) continue;
- var name = char.ToLowerInvariant(property.Name[0]) + property.Name[1..];
- tw.WriteLine($"{name} = {property.GetValue(OutputConfigSettings)}");
+ tw.WriteLine($"{MemberNameToIniKey(property.Name)} = {property.GetValue(config)}");
}
-
- tw.Close();
}
- entry = outputFile.CreateEntry(IniPrusaslicer);
- using (TextWriter tw = new StreamWriter(entry.Open()))
- {
- foreach (var config in Configs)
- {
- if (ReferenceEquals(config, OutputConfigSettings))
- continue;
-
- var properties = config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
-
- foreach (var property in properties)
- {
- if (property.Name.Equals("Item")) continue;
- tw.WriteLine($"{MemberNameToIniKey(property.Name)} = {property.GetValue(config)}");
- }
- }
-
- tw.Close();
- }
+ tw.Close();
+ }
- foreach (var thumbnail in Thumbnails)
- {
- if (thumbnail is null) continue;
- using var stream = outputFile.CreateEntry($"thumbnail/thumbnail{thumbnail.Width}x{thumbnail.Height}.png").Open();
- stream.WriteBytes(thumbnail.GetPngByes());
- stream.Close();
- }
+ foreach (var thumbnail in Thumbnails)
+ {
+ if (thumbnail is null) continue;
+ using var stream = outputFile.CreateEntry($"thumbnail/thumbnail{thumbnail.Width}x{thumbnail.Height}.png").Open();
+ stream.WriteBytes(thumbnail.GetPngByes());
+ stream.Close();
+ }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- Layer layer = this[layerIndex];
- var layerImagePath = $"{filename}{layerIndex:D5}.png";
- //layer.Filename = layerImagePath;
- outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create);
- progress++;
- }
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ Layer layer = this[layerIndex];
+ var layerImagePath = $"{filename}{layerIndex:D5}.png";
+ //layer.Filename = layerImagePath;
+ outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
}
+ }
- protected override void DecodeInternally(OperationProgress progress)
- {
- PrinterSettings = new Printer();
- MaterialSettings = new Material();
- PrintSettings = new Print();
- OutputConfigSettings = new OutputConfig();
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ PrinterSettings = new Printer();
+ MaterialSettings = new Material();
+ PrintSettings = new Print();
+ OutputConfigSettings = new OutputConfig();
- Statistics.ExecutionTime.Restart();
+ Statistics.ExecutionTime.Restart();
- using (var inputFile = ZipFile.OpenRead(FileFullPath))
+ using (var inputFile = ZipFile.OpenRead(FileFullPath!))
+ {
+ List<string> iniFiles = new();
+ foreach (ZipArchiveEntry entity in inputFile.Entries)
{
- List<string> iniFiles = new();
- foreach (ZipArchiveEntry entity in inputFile.Entries)
+ if (!entity.Name.EndsWith(".ini")) continue;
+ iniFiles.Add(entity.Name);
+ using StreamReader streamReader = new(entity.Open());
+ string? line;
+ while ((line = streamReader.ReadLine()) != null)
{
- if (!entity.Name.EndsWith(".ini")) continue;
- iniFiles.Add(entity.Name);
- using StreamReader streamReader = new(entity.Open());
- string line;
- while ((line = streamReader.ReadLine()) != null)
- {
- string[] keyValue = line.Split(new[] {'='}, 2);
- if (keyValue.Length < 2) continue;
- keyValue[0] = keyValue[0].Trim();
- keyValue[1] = keyValue[1].Trim();
+ string[] keyValue = line.Split(new[] {'='}, 2);
+ if (keyValue.Length < 2) continue;
+ keyValue[0] = keyValue[0].Trim();
+ keyValue[1] = keyValue[1].Trim();
- var fieldName = IniKeyToMemberName(keyValue[0]);
- bool foundMember = false;
+ var fieldName = IniKeyToMemberName(keyValue[0]);
+ bool foundMember = false;
- foreach (var obj in Configs)
- {
- var attribute = obj.GetType().GetProperty(fieldName);
- if (attribute is null || !attribute.CanWrite) continue;
- //Debug.WriteLine(attribute.Name);
- Helpers.SetPropertyValue(attribute, obj, keyValue[1]);
+ foreach (var obj in Configs)
+ {
+ var attribute = obj.GetType().GetProperty(fieldName);
+ if (attribute is null || !attribute.CanWrite) continue;
+ //Debug.WriteLine(attribute.Name);
+ Helpers.SetPropertyValue(attribute, obj, keyValue[1]);
- Statistics.ImplementedKeys.Add(keyValue[0]);
- foundMember = true;
- }
+ Statistics.ImplementedKeys.Add(keyValue[0]);
+ foundMember = true;
+ }
- if (!foundMember)
- {
- Statistics.MissingKeys.Add(keyValue[0]);
- }
+ if (!foundMember)
+ {
+ Statistics.MissingKeys.Add(keyValue[0]);
}
}
+ }
- if (!iniFiles.Contains(IniConfig))
- {
- throw new FileLoadException($"Malformed file: {IniConfig} is missing.");
- }
- if (!iniFiles.Contains(IniPrusaslicer))
- {
- throw new FileLoadException($"Malformed file: {IniPrusaslicer} is missing.");
- }
+ if (!iniFiles.Contains(IniConfig))
+ {
+ throw new FileLoadException($"Malformed file: {IniConfig} is missing.");
+ }
+ if (!iniFiles.Contains(IniPrusaslicer))
+ {
+ throw new FileLoadException($"Malformed file: {IniPrusaslicer} is missing.");
+ }
- SuppressRebuildPropertiesWork(() =>
- {
- TransitionLayerCount = LookupCustomValue<ushort>(Keyword_TransitionLayerCount, 0);
- BottomLightOffDelay = LookupCustomValue(Keyword_BottomLightOffDelay, 0f);
- LightOffDelay = LookupCustomValue(Keyword_LightOffDelay, 0f);
+ SuppressRebuildPropertiesWork(() =>
+ {
+ TransitionLayerCount = LookupCustomValue<ushort>(Keyword_TransitionLayerCount, 0);
+ BottomLightOffDelay = LookupCustomValue(Keyword_BottomLightOffDelay, 0f);
+ LightOffDelay = LookupCustomValue(Keyword_LightOffDelay, 0f);
- BottomWaitTimeBeforeCure = LookupCustomValue(Keyword_BottomWaitTimeBeforeCure, 0f);
- WaitTimeBeforeCure = LookupCustomValue(Keyword_WaitTimeBeforeCure, 0f);
+ BottomWaitTimeBeforeCure = LookupCustomValue(Keyword_BottomWaitTimeBeforeCure, 0f);
+ WaitTimeBeforeCure = LookupCustomValue(Keyword_WaitTimeBeforeCure, 0f);
- BottomWaitTimeAfterCure = LookupCustomValue(Keyword_BottomWaitTimeAfterCure, 0f);
- WaitTimeAfterCure = LookupCustomValue(Keyword_WaitTimeAfterCure, 0f);
+ BottomWaitTimeAfterCure = LookupCustomValue(Keyword_BottomWaitTimeAfterCure, 0f);
+ WaitTimeAfterCure = LookupCustomValue(Keyword_WaitTimeAfterCure, 0f);
- BottomLiftHeight = LookupCustomValue(Keyword_BottomLiftHeight, DefaultBottomLiftHeight);
- BottomLiftSpeed = LookupCustomValue(Keyword_BottomLiftSpeed, DefaultBottomLiftSpeed);
+ BottomLiftHeight = LookupCustomValue(Keyword_BottomLiftHeight, DefaultBottomLiftHeight);
+ BottomLiftSpeed = LookupCustomValue(Keyword_BottomLiftSpeed, DefaultBottomLiftSpeed);
- LiftHeight = LookupCustomValue(Keyword_LiftHeight, DefaultLiftHeight);
- LiftSpeed = LookupCustomValue(Keyword_LiftSpeed, DefaultLiftSpeed);
+ LiftHeight = LookupCustomValue(Keyword_LiftHeight, DefaultLiftHeight);
+ LiftSpeed = LookupCustomValue(Keyword_LiftSpeed, DefaultLiftSpeed);
- BottomLiftHeight2 = LookupCustomValue(Keyword_BottomLiftHeight2, DefaultBottomLiftHeight2);
- BottomLiftSpeed2 = LookupCustomValue(Keyword_BottomLiftSpeed2, DefaultBottomLiftSpeed2);
+ BottomLiftHeight2 = LookupCustomValue(Keyword_BottomLiftHeight2, DefaultBottomLiftHeight2);
+ BottomLiftSpeed2 = LookupCustomValue(Keyword_BottomLiftSpeed2, DefaultBottomLiftSpeed2);
- LiftHeight2 = LookupCustomValue(Keyword_LiftHeight2, DefaultLiftHeight2);
- LiftSpeed2 = LookupCustomValue(Keyword_LiftSpeed2, DefaultLiftSpeed2);
+ LiftHeight2 = LookupCustomValue(Keyword_LiftHeight2, DefaultLiftHeight2);
+ LiftSpeed2 = LookupCustomValue(Keyword_LiftSpeed2, DefaultLiftSpeed2);
- BottomWaitTimeAfterLift = LookupCustomValue(Keyword_BottomWaitTimeAfterLift, 0f);
- WaitTimeAfterLift = LookupCustomValue(Keyword_WaitTimeAfterLift, 0f);
+ BottomWaitTimeAfterLift = LookupCustomValue(Keyword_BottomWaitTimeAfterLift, 0f);
+ WaitTimeAfterLift = LookupCustomValue(Keyword_WaitTimeAfterLift, 0f);
- BottomRetractSpeed = LookupCustomValue(Keyword_BottomRetractSpeed, DefaultBottomRetractSpeed);
- RetractSpeed = LookupCustomValue(Keyword_RetractSpeed, DefaultRetractSpeed);
+ BottomRetractSpeed = LookupCustomValue(Keyword_BottomRetractSpeed, DefaultBottomRetractSpeed);
+ RetractSpeed = LookupCustomValue(Keyword_RetractSpeed, DefaultRetractSpeed);
- BottomRetractHeight2 = LookupCustomValue(Keyword_BottomRetractHeight2, DefaultBottomRetractHeight2);
- RetractHeight2 = LookupCustomValue(Keyword_RetractHeight2, DefaultRetractHeight2);
- BottomRetractSpeed2 = LookupCustomValue(Keyword_BottomRetractSpeed2, DefaultBottomRetractSpeed2);
- RetractSpeed2 = LookupCustomValue(Keyword_RetractSpeed2, DefaultRetractSpeed2);
- BottomLightPWM = LookupCustomValue(Keyword_BottomLightPWM, DefaultLightPWM);
- LightPWM = LookupCustomValue(Keyword_LightPWM, DefaultBottomLightPWM);
- });
+ BottomRetractHeight2 = LookupCustomValue(Keyword_BottomRetractHeight2, DefaultBottomRetractHeight2);
+ RetractHeight2 = LookupCustomValue(Keyword_RetractHeight2, DefaultRetractHeight2);
+ BottomRetractSpeed2 = LookupCustomValue(Keyword_BottomRetractSpeed2, DefaultBottomRetractSpeed2);
+ RetractSpeed2 = LookupCustomValue(Keyword_RetractSpeed2, DefaultRetractSpeed2);
+ BottomLightPWM = LookupCustomValue(Keyword_BottomLightPWM, DefaultLightPWM);
+ LightPWM = LookupCustomValue(Keyword_LightPWM, DefaultBottomLightPWM);
+ });
- LayerManager.Init(OutputConfigSettings.NumSlow + OutputConfigSettings.NumFast, DecodeType == FileDecodeType.Partial);
+ Init(OutputConfigSettings.NumSlow + OutputConfigSettings.NumFast, DecodeType == FileDecodeType.Partial);
- progress.ItemCount = LayerCount;
+ progress.ItemCount = LayerCount;
- foreach (var entity in inputFile.Entries)
+ foreach (var entity in inputFile.Entries)
+ {
+ if (!entity.Name.EndsWith(".png")) continue;
+ if (entity.Name.StartsWith("thumbnail"))
{
- if (!entity.Name.EndsWith(".png")) continue;
- if (entity.Name.StartsWith("thumbnail"))
- {
- using var stream = entity.Open();
- Mat image = new();
- CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
- byte thumbnailIndex =
- (byte) (image.Width == ThumbnailsOriginalSize[(int) FileThumbnailSize.Small].Width &&
- image.Height == ThumbnailsOriginalSize[(int) FileThumbnailSize.Small].Height
- ? FileThumbnailSize.Small
- : FileThumbnailSize.Large);
- Thumbnails[thumbnailIndex] = image;
-
- //thumbnailIndex++;
-
- continue;
- }
-
- if (DecodeType == FileDecodeType.Full)
- {
- // - .png - 5 numbers
- string layerStr = entity.Name.Substring(entity.Name.Length - 4 - 5, 5);
- uint iLayer = uint.Parse(layerStr);
- using var stream = entity.Open();
- this[iLayer] = new Layer(iLayer, stream, LayerManager);
- }
+ using var stream = entity.Open();
+ Mat image = new();
+ CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
+ byte thumbnailIndex =
+ (byte) (image.Width == ThumbnailsOriginalSize![(int) FileThumbnailSize.Small].Width &&
+ image.Height == ThumbnailsOriginalSize[(int) FileThumbnailSize.Small].Height
+ ? FileThumbnailSize.Small
+ : FileThumbnailSize.Large);
+ Thumbnails[thumbnailIndex] = image;
+
+ //thumbnailIndex++;
+
+ continue;
+ }
- progress.ProcessedItems++;
+ if (DecodeType == FileDecodeType.Full)
+ {
+ // - .png - 5 numbers
+ string layerStr = entity.Name.Substring(entity.Name.Length - 4 - 5, 5);
+ uint iLayer = uint.Parse(layerStr);
+ using var stream = entity.Open();
+ this[iLayer] = new Layer(iLayer, stream, this);
}
+
+ progress.ProcessedItems++;
}
+ }
- if (TransitionLayerCount > 0)
- {
- SetTransitionLayers(TransitionLayerCount, false);
- }
+ if (TransitionLayerCount > 0)
+ {
+ SetTransitionLayers(TransitionLayerCount, false);
+ }
- LayerManager.GetBoundingRectangle(progress);
+ GetBoundingRectangle(progress);
- Statistics.ExecutionTime.Stop();
+ Statistics.ExecutionTime.Stop();
- Debug.WriteLine(Statistics);
+ Debug.WriteLine(Statistics);
+ }
+
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ //InputFile.CreateEntry("Modified");
+ using (TextWriter tw = new StreamWriter(outputFile.PutFileContent("config.ini", string.Empty, ZipArchiveMode.Update).Open()))
+ {
+ var properties = OutputConfigSettings.GetType()
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance);
+
+ foreach (var property in properties)
+ {
+ if (property.Name.Equals("Item")) continue;
+ var name = char.ToLowerInvariant(property.Name[0]) + property.Name[1..];
+ tw.WriteLine($"{name} = {property.GetValue(OutputConfigSettings)}");
+ }
+
+ tw.Close();
}
- protected override void PartialSaveInternally(OperationProgress progress)
+ using (TextWriter tw = new StreamWriter(outputFile.PutFileContent("prusaslicer.ini", string.Empty, ZipArchiveMode.Update).Open()))
{
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- //InputFile.CreateEntry("Modified");
- using (TextWriter tw = new StreamWriter(outputFile.PutFileContent("config.ini", string.Empty, ZipArchiveMode.Update).Open()))
+ foreach (var config in Configs)
{
- var properties = OutputConfigSettings.GetType()
- .GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ if (ReferenceEquals(config, OutputConfigSettings))
+ continue;
+
+ var properties = config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
if (property.Name.Equals("Item")) continue;
- var name = char.ToLowerInvariant(property.Name[0]) + property.Name[1..];
- tw.WriteLine($"{name} = {property.GetValue(OutputConfigSettings)}");
+ tw.WriteLine($"{MemberNameToIniKey(property.Name)} = {property.GetValue(config)}");
}
-
- tw.Close();
}
- using (TextWriter tw = new StreamWriter(outputFile.PutFileContent("prusaslicer.ini", string.Empty, ZipArchiveMode.Update).Open()))
- {
- foreach (var config in Configs)
- {
- if (ReferenceEquals(config, OutputConfigSettings))
- continue;
-
- var properties = config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ tw.Close();
+ }
- foreach (var property in properties)
- {
- if (property.Name.Equals("Item")) continue;
- tw.WriteLine($"{MemberNameToIniKey(property.Name)} = {property.GetValue(config)}");
- }
- }
+ //Decode(FileFullPath, progress);
- tw.Close();
- }
-
- //Decode(FileFullPath, progress);
+ }
- }
+ public T? LookupCustomValue<T>(string name, T? defaultValue, bool existsOnly = false)
+ {
+ //if (string.IsNullOrEmpty(PrinterSettings.PrinterNotes)) return defaultValue;
+ var result = string.Empty;
+ if(!existsOnly) name += '_';
- public T LookupCustomValue<T>(string name, T defaultValue, bool existsOnly = false)
+ foreach (var notes in new [] {MaterialSettings.MaterialNotes, PrinterSettings.PrinterNotes})
{
- //if (string.IsNullOrEmpty(PrinterSettings.PrinterNotes)) return defaultValue;
- var result = string.Empty;
- if(!existsOnly) name += '_';
+ if(string.IsNullOrWhiteSpace(notes)) continue;
- foreach (var notes in new [] {MaterialSettings.MaterialNotes, PrinterSettings.PrinterNotes})
- {
- if(string.IsNullOrWhiteSpace(notes)) continue;
-
- var lines = notes.Split(new[] { "\\r\\n", "\\r", "\\n" }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+ var lines = notes.Split(new[] { "\\r\\n", "\\r", "\\n" }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- foreach (var line in lines)
+ foreach (var line in lines)
+ {
+ if (!line.StartsWith(name)) continue;
+ if (existsOnly || line == name) return "true".Convert<T>();
+ var value = line.Remove(0, name.Length);
+ foreach (var c in value)
{
- if (!line.StartsWith(name)) continue;
- if (existsOnly || line == name) return "true".Convert<T>();
- var value = line.Remove(0, name.Length);
- foreach (var c in value)
+ if (typeof(T) == typeof(string))
{
- if (typeof(T) == typeof(string))
- {
- if (char.IsWhiteSpace(c)) break;
- }
- else
+ if (char.IsWhiteSpace(c)) break;
+ }
+ else
+ {
+ if (!char.IsLetterOrDigit(c) && c != '.')
{
- if (!char.IsLetterOrDigit(c) && c != '.')
- {
- break;
- }
+ break;
}
+ }
- result += c;
- }
+ result += c;
}
-
- if (!string.IsNullOrEmpty(result)) break; // Found a candidate
}
- return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>();
+ if (!string.IsNullOrEmpty(result)) break; // Found a candidate
}
- #endregion
+ return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>();
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs
index 954816a..d596d60 100644
--- a/UVtools.Core/FileFormats/UVJFile.cs
+++ b/UVtools.Core/FileFormats/UVJFile.cs
@@ -6,594 +6,593 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Newtonsoft.Json;
+using System.Text.Json;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+public class UVJFile : FileFormat
{
- public class UVJFile : FileFormat
+ #region Constants
+
+ private const string FileConfigName = "config.json";
+ private const string FolderImageName = "slice";
+ private const string FolderPreviewName = "preview";
+ private const string FilePreviewHugeName = "preview/huge.png";
+ private const string FilePreviewTinyName = "preview/tiny.png";
+ #endregion
+
+ #region Sub Classes
+
+ public class Millimeter
{
- #region Constants
+ public float X { get; set; }
+ public float Y { get; set; }
+ }
+
+ public class Size
+ {
+ public ushort X { get; set; }
+ public ushort Y { get; set; }
- private const string FileConfigName = "config.json";
- private const string FolderImageName = "slice";
- private const string FolderPreviewName = "preview";
- private const string FilePreviewHugeName = "preview/huge.png";
- private const string FilePreviewTinyName = "preview/tiny.png";
- #endregion
+ public Millimeter Millimeter { get; set; } = new();
- #region Sub Classes
+ public uint Layers { get; set; }
+ public float LayerHeight { get; set; }
- public class Millimeter
+ public override string ToString()
{
- public float X { get; set; }
- public float Y { get; set; }
+ return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Millimeter)}: {Millimeter}, {nameof(Layers)}: {Layers}, {nameof(LayerHeight)}: {LayerHeight}";
}
+ }
- public class Size
- {
- public ushort X { get; set; }
- public ushort Y { get; set; }
+ public class Exposure
+ {
+ public float LightOnTime { get; set; }
+ public float LightOffTime { get; set; }
+ public byte LightPWM { get; set; } = DefaultLightPWM;
+ public float LiftHeight { get; set; } = DefaultLiftHeight;
+ public float LiftSpeed { get; set; } = DefaultLiftSpeed;
+ public float LiftHeight2 { get; set; } = DefaultLiftHeight2;
+ public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2;
+ public float RetractHeight { get; set; }
+ public float RetractSpeed { get; set; } = DefaultRetractSpeed;
+ public float RetractHeight2 { get; set; }
+ public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2;
+
+ public override string ToString()
+ {
+ return $"{nameof(LightOnTime)}: {LightOnTime}, {nameof(LightOffTime)}: {LightOffTime}, {nameof(LightPWM)}: {LightPWM}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight)}: {RetractHeight}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}";
+ }
+ }
- public Millimeter Millimeter { get; set; } = new();
+ public class Bottom
+ {
+ public float LightOffTime { get; set; }
+ public float LightOnTime { get; set; }
+ public byte LightPWM { get; set; } = DefaultBottomLightPWM;
+ public float LiftHeight { get; set; } = DefaultBottomLiftHeight;
+ public float LiftSpeed { get; set; } = DefaultBottomLiftSpeed;
+ public float LiftHeight2 { get; set; } = DefaultBottomLiftHeight2;
+ public float LiftSpeed2 { get; set; } = DefaultBottomLiftSpeed2;
+ public float RetractHeight { get; set; }
+ public float RetractSpeed { get; set; } = DefaultBottomRetractSpeed;
+ public float RetractHeight2 { get; set; }
+ public float RetractSpeed2 { get; set; } = DefaultBottomRetractSpeed2;
+ public ushort Count { get; set; }
+
+ public override string ToString()
+ {
+ return $"{nameof(LightOffTime)}: {LightOffTime}, {nameof(LightOnTime)}: {LightOnTime}, {nameof(LightPWM)}: {LightPWM}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight)}: {RetractHeight}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(Count)}: {Count}";
+ }
+ }
- public uint Layers { get; set; }
- public float LayerHeight { get; set; }
+ public class LayerDef
+ {
+ public float Z { get; set; }
+ public Exposure Exposure { get; set; } = new();
- public override string ToString()
- {
- return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Millimeter)}: {Millimeter}, {nameof(Layers)}: {Layers}, {nameof(LayerHeight)}: {LayerHeight}";
- }
- }
+ public LayerDef() { }
- public class Exposure
+ public LayerDef(Layer layer)
{
- public float LightOnTime { get; set; }
- public float LightOffTime { get; set; }
- public byte LightPWM { get; set; } = DefaultLightPWM;
- public float LiftHeight { get; set; } = DefaultLiftHeight;
- public float LiftSpeed { get; set; } = DefaultLiftSpeed;
- public float LiftHeight2 { get; set; } = DefaultLiftHeight2;
- public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2;
- public float RetractHeight { get; set; }
- public float RetractSpeed { get; set; } = DefaultRetractSpeed;
- public float RetractHeight2 { get; set; }
- public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2;
-
- public override string ToString()
- {
- return $"{nameof(LightOnTime)}: {LightOnTime}, {nameof(LightOffTime)}: {LightOffTime}, {nameof(LightPWM)}: {LightPWM}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight)}: {RetractHeight}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}";
- }
+ SetFrom(layer);
}
- public class Bottom
+ public override string ToString()
{
- public float LightOffTime { get; set; }
- public float LightOnTime { get; set; }
- public byte LightPWM { get; set; } = DefaultBottomLightPWM;
- public float LiftHeight { get; set; } = DefaultBottomLiftHeight;
- public float LiftSpeed { get; set; } = DefaultBottomLiftSpeed;
- public float LiftHeight2 { get; set; } = DefaultBottomLiftHeight2;
- public float LiftSpeed2 { get; set; } = DefaultBottomLiftSpeed2;
- public float RetractHeight { get; set; }
- public float RetractSpeed { get; set; } = DefaultBottomRetractSpeed;
- public float RetractHeight2 { get; set; }
- public float RetractSpeed2 { get; set; } = DefaultBottomRetractSpeed2;
- public ushort Count { get; set; }
-
- public override string ToString()
- {
- return $"{nameof(LightOffTime)}: {LightOffTime}, {nameof(LightOnTime)}: {LightOnTime}, {nameof(LightPWM)}: {LightPWM}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight)}: {RetractHeight}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(Count)}: {Count}";
- }
+ return $"{nameof(Z)}: {Z}, {nameof(Exposure)}: {Exposure}";
}
- public class LayerDef
+ public void SetFrom(Layer layer)
{
- public float Z { get; set; }
- public Exposure Exposure { get; set; } = new();
-
- public LayerDef() { }
-
- public LayerDef(Layer layer)
- {
- SetFrom(layer);
- }
-
- public override string ToString()
- {
- return $"{nameof(Z)}: {Z}, {nameof(Exposure)}: {Exposure}";
- }
-
- public void SetFrom(Layer layer)
- {
- Z = layer.PositionZ;
- Exposure.LightOffTime = layer.LightOffDelay;
- Exposure.LightOnTime = layer.ExposureTime;
- Exposure.LiftHeight = layer.LiftHeight;
- Exposure.LiftSpeed = layer.LiftSpeed;
- Exposure.LiftHeight2 = layer.LiftHeight2;
- Exposure.LiftSpeed2 = layer.LiftSpeed2;
- Exposure.RetractHeight = layer.RetractHeight;
- Exposure.RetractSpeed = layer.RetractSpeed;
- Exposure.RetractHeight2 = layer.RetractHeight2;
- Exposure.RetractSpeed2 = layer.RetractSpeed2;
- Exposure.LightPWM = layer.LightPWM;
- }
-
- public void CopyTo(Layer layer)
- {
- layer.PositionZ = Z;
- layer.LightOffDelay = Exposure.LightOffTime;
- layer.ExposureTime = Exposure.LightOnTime;
- layer.LiftHeight = Exposure.LiftHeight;
- layer.LiftSpeed = Exposure.LiftSpeed;
- layer.LiftHeight2 = Exposure.LiftHeight2;
- layer.LiftSpeed2 = Exposure.LiftSpeed2;
- layer.RetractSpeed = Exposure.RetractSpeed;
- layer.RetractHeight2 = Exposure.RetractHeight2;
- layer.RetractSpeed2 = Exposure.RetractSpeed2;
- layer.LightPWM = Exposure.LightPWM;
- }
+ Z = layer.PositionZ;
+ Exposure.LightOffTime = layer.LightOffDelay;
+ Exposure.LightOnTime = layer.ExposureTime;
+ Exposure.LiftHeight = layer.LiftHeight;
+ Exposure.LiftSpeed = layer.LiftSpeed;
+ Exposure.LiftHeight2 = layer.LiftHeight2;
+ Exposure.LiftSpeed2 = layer.LiftSpeed2;
+ Exposure.RetractHeight = layer.RetractHeight;
+ Exposure.RetractSpeed = layer.RetractSpeed;
+ Exposure.RetractHeight2 = layer.RetractHeight2;
+ Exposure.RetractSpeed2 = layer.RetractSpeed2;
+ Exposure.LightPWM = layer.LightPWM;
}
- public class Properties
+ public void CopyTo(Layer layer)
{
- public Size Size { get; set; } = new ();
- public Exposure Exposure { get; set; } = new ();
- public Bottom Bottom { get; set; } = new ();
- public byte AntiAliasLevel { get; set; } = 1;
-
- public override string ToString()
- {
- return $"{nameof(Size)}: {Size}, {nameof(Exposure)}: {Exposure}, {nameof(Bottom)}: {Bottom}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}";
- }
+ layer.PositionZ = Z;
+ layer.LightOffDelay = Exposure.LightOffTime;
+ layer.ExposureTime = Exposure.LightOnTime;
+ layer.LiftHeight = Exposure.LiftHeight;
+ layer.LiftSpeed = Exposure.LiftSpeed;
+ layer.LiftHeight2 = Exposure.LiftHeight2;
+ layer.LiftSpeed2 = Exposure.LiftSpeed2;
+ layer.RetractSpeed = Exposure.RetractSpeed;
+ layer.RetractHeight2 = Exposure.RetractHeight2;
+ layer.RetractSpeed2 = Exposure.RetractSpeed2;
+ layer.LightPWM = Exposure.LightPWM;
}
+ }
+
+ public class Properties
+ {
+ public Size Size { get; set; } = new ();
+ public Exposure Exposure { get; set; } = new ();
+ public Bottom Bottom { get; set; } = new ();
+ public byte AntiAliasLevel { get; set; } = 1;
- public class Settings
+ public override string ToString()
{
- public Properties Properties { get; set; } = new();
- public List<LayerDef> Layers { get; set; } = new();
+ return $"{nameof(Size)}: {Size}, {nameof(Exposure)}: {Exposure}, {nameof(Bottom)}: {Bottom}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}";
+ }
+ }
- public override string ToString()
- {
- return $"{nameof(Properties)}: {Properties}, {nameof(Layers)}: {Layers.Count}";
- }
+ public class Settings
+ {
+ public Properties Properties { get; set; } = new();
+ public List<LayerDef>? Layers { get; set; } = new();
+
+ public override string ToString()
+ {
+ return $"{nameof(Properties)}: {Properties}, {nameof(Layers)}: {Layers?.Count}";
}
+ }
- #endregion
+ #endregion
- #region Properties
- public Settings JsonSettings { get; set; } = new();
+ #region Properties
+ public Settings JsonSettings { get; set; } = new();
- public override FileFormatType FileType => FileFormatType.Archive;
+ public override FileFormatType FileType => FileFormatType.Archive;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(UVJFile), "uvj", "UVJ")
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(UVJFile), "uvj", "UVJ")
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.ExposureTime,
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } =
- {
- new(400, 400),
- new(800, 480)
- };
+ public override System.Drawing.Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(400, 400),
+ new(800, 480)
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => JsonSettings.Properties.Size.X;
+ set
{
- get => JsonSettings.Properties.Size.X;
- set
- {
- JsonSettings.Properties.Size.X = (ushort) value;
- RaisePropertyChanged();
- }
+ JsonSettings.Properties.Size.X = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => JsonSettings.Properties.Size.Y;
+ set
{
- get => JsonSettings.Properties.Size.Y;
- set
- {
- JsonSettings.Properties.Size.Y = (ushort) value;
- RaisePropertyChanged();
- }
+ JsonSettings.Properties.Size.Y = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => JsonSettings.Properties.Size.Millimeter.X;
+ set
{
- get => JsonSettings.Properties.Size.Millimeter.X;
- set
- {
- JsonSettings.Properties.Size.Millimeter.X = (float) Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ JsonSettings.Properties.Size.Millimeter.X = (float) Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => JsonSettings.Properties.Size.Millimeter.Y;
+ set
{
- get => JsonSettings.Properties.Size.Millimeter.Y;
- set
- {
- JsonSettings.Properties.Size.Millimeter.Y = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ JsonSettings.Properties.Size.Millimeter.Y = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override Enumerations.FlipDirection DisplayMirror { get; set; }
+ public override Enumerations.FlipDirection DisplayMirror { get; set; }
- public override byte AntiAliasing
- {
- get => JsonSettings.Properties.AntiAliasLevel;
- set => base.AntiAliasing = JsonSettings.Properties.AntiAliasLevel = value.Clamp(1, 16);
- }
+ public override byte AntiAliasing
+ {
+ get => JsonSettings.Properties.AntiAliasLevel;
+ set => base.AntiAliasing = JsonSettings.Properties.AntiAliasLevel = value.Clamp(1, 16);
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => JsonSettings.Properties.Size.LayerHeight;
+ set
{
- get => JsonSettings.Properties.Size.LayerHeight;
- set
- {
- JsonSettings.Properties.Size.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ JsonSettings.Properties.Size.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = JsonSettings.Properties.Size.Layers = base.LayerCount;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = JsonSettings.Properties.Size.Layers = base.LayerCount;
+ }
- public override ushort BottomLayerCount
- {
- get => JsonSettings.Properties.Bottom.Count;
- set => base.BottomLayerCount = JsonSettings.Properties.Bottom.Count = value;
- }
+ public override ushort BottomLayerCount
+ {
+ get => JsonSettings.Properties.Bottom.Count;
+ set => base.BottomLayerCount = JsonSettings.Properties.Bottom.Count = value;
+ }
- public override float BottomLightOffDelay
- {
- get => JsonSettings.Properties.Bottom.LightOffTime;
- set => base.BottomLightOffDelay = JsonSettings.Properties.Bottom.LightOffTime = (float)Math.Round(value, 2);
- }
+ public override float BottomLightOffDelay
+ {
+ get => JsonSettings.Properties.Bottom.LightOffTime;
+ set => base.BottomLightOffDelay = JsonSettings.Properties.Bottom.LightOffTime = (float)Math.Round(value, 2);
+ }
- public override float LightOffDelay
- {
- get => JsonSettings.Properties.Exposure.LightOffTime;
- set => base.LightOffDelay = JsonSettings.Properties.Exposure.LightOffTime = (float)Math.Round(value, 2);
- }
+ public override float LightOffDelay
+ {
+ get => JsonSettings.Properties.Exposure.LightOffTime;
+ set => base.LightOffDelay = JsonSettings.Properties.Exposure.LightOffTime = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
{
- get => base.BottomWaitTimeBeforeCure;
- set
- {
- SetBottomLightOffDelay(value);
- base.BottomWaitTimeBeforeCure = value;
- }
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
}
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
{
- get => base.WaitTimeBeforeCure;
- set
- {
- SetNormalLightOffDelay(value);
- base.WaitTimeBeforeCure = value;
- }
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
}
+ }
- public override float BottomExposureTime
- {
- get => JsonSettings.Properties.Bottom.LightOnTime;
- set => base.BottomExposureTime = JsonSettings.Properties.Bottom.LightOnTime = (float)Math.Round(value, 2);
- }
+ public override float BottomExposureTime
+ {
+ get => JsonSettings.Properties.Bottom.LightOnTime;
+ set => base.BottomExposureTime = JsonSettings.Properties.Bottom.LightOnTime = (float)Math.Round(value, 2);
+ }
- public override float ExposureTime
- {
- get => JsonSettings.Properties.Exposure.LightOnTime;
- set => base.ExposureTime = JsonSettings.Properties.Exposure.LightOnTime = (float)Math.Round(value, 2);
- }
+ public override float ExposureTime
+ {
+ get => JsonSettings.Properties.Exposure.LightOnTime;
+ set => base.ExposureTime = JsonSettings.Properties.Exposure.LightOnTime = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftHeight
- {
- get => JsonSettings.Properties.Bottom.LiftHeight;
- set => base.BottomLiftHeight = JsonSettings.Properties.Bottom.LiftHeight = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftHeight
+ {
+ get => JsonSettings.Properties.Bottom.LiftHeight;
+ set => base.BottomLiftHeight = JsonSettings.Properties.Bottom.LiftHeight = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftSpeed
- {
- get => JsonSettings.Properties.Bottom.LiftSpeed;
- set => base.BottomLiftSpeed = JsonSettings.Properties.Bottom.LiftSpeed = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftSpeed
+ {
+ get => JsonSettings.Properties.Bottom.LiftSpeed;
+ set => base.BottomLiftSpeed = JsonSettings.Properties.Bottom.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float LiftHeight
- {
- get => JsonSettings.Properties.Exposure.LiftHeight;
- set => base.LiftHeight = JsonSettings.Properties.Exposure.LiftHeight = (float)Math.Round(value, 2);
- }
+ public override float LiftHeight
+ {
+ get => JsonSettings.Properties.Exposure.LiftHeight;
+ set => base.LiftHeight = JsonSettings.Properties.Exposure.LiftHeight = (float)Math.Round(value, 2);
+ }
- public override float LiftSpeed
- {
- get => JsonSettings.Properties.Exposure.LiftSpeed;
- set => base.LiftSpeed = JsonSettings.Properties.Exposure.LiftSpeed = (float)Math.Round(value, 2);
- }
+ public override float LiftSpeed
+ {
+ get => JsonSettings.Properties.Exposure.LiftSpeed;
+ set => base.LiftSpeed = JsonSettings.Properties.Exposure.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftHeight2
- {
- get => JsonSettings.Properties.Bottom.LiftHeight2;
- set => base.BottomLiftHeight2 = JsonSettings.Properties.Bottom.LiftHeight2 = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftHeight2
+ {
+ get => JsonSettings.Properties.Bottom.LiftHeight2;
+ set => base.BottomLiftHeight2 = JsonSettings.Properties.Bottom.LiftHeight2 = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftSpeed2
- {
- get => JsonSettings.Properties.Bottom.LiftSpeed2;
- set => base.BottomLiftSpeed2 = JsonSettings.Properties.Bottom.LiftSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftSpeed2
+ {
+ get => JsonSettings.Properties.Bottom.LiftSpeed2;
+ set => base.BottomLiftSpeed2 = JsonSettings.Properties.Bottom.LiftSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float LiftHeight2
- {
- get => JsonSettings.Properties.Exposure.LiftHeight2;
- set => base.LiftHeight2 = JsonSettings.Properties.Exposure.LiftHeight2 = (float)Math.Round(value, 2);
- }
+ public override float LiftHeight2
+ {
+ get => JsonSettings.Properties.Exposure.LiftHeight2;
+ set => base.LiftHeight2 = JsonSettings.Properties.Exposure.LiftHeight2 = (float)Math.Round(value, 2);
+ }
- public override float LiftSpeed2
- {
- get => JsonSettings.Properties.Exposure.LiftSpeed2;
- set => base.LiftSpeed2 = JsonSettings.Properties.Exposure.LiftSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float LiftSpeed2
+ {
+ get => JsonSettings.Properties.Exposure.LiftSpeed2;
+ set => base.LiftSpeed2 = JsonSettings.Properties.Exposure.LiftSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractSpeed
- {
- get => JsonSettings.Properties.Bottom.RetractSpeed;
- set => base.BottomRetractSpeed = JsonSettings.Properties.Bottom.RetractSpeed = (float)Math.Round(value, 2);
- }
+ public override float BottomRetractSpeed
+ {
+ get => JsonSettings.Properties.Bottom.RetractSpeed;
+ set => base.BottomRetractSpeed = JsonSettings.Properties.Bottom.RetractSpeed = (float)Math.Round(value, 2);
+ }
- public override float RetractSpeed
- {
- get => JsonSettings.Properties.Exposure.RetractSpeed;
- set => base.RetractSpeed = JsonSettings.Properties.Exposure.RetractSpeed = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed
+ {
+ get => JsonSettings.Properties.Exposure.RetractSpeed;
+ set => base.RetractSpeed = JsonSettings.Properties.Exposure.RetractSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractHeight2
- {
- get => JsonSettings.Properties.Bottom.RetractHeight2;
- set => base.BottomRetractHeight2 = JsonSettings.Properties.Bottom.RetractHeight2 = (float)Math.Round(value, 2);
- }
+ public override float BottomRetractHeight2
+ {
+ get => JsonSettings.Properties.Bottom.RetractHeight2;
+ set => base.BottomRetractHeight2 = JsonSettings.Properties.Bottom.RetractHeight2 = (float)Math.Round(value, 2);
+ }
+
+ public override float BottomRetractSpeed2
+ {
+ get => JsonSettings.Properties.Bottom.RetractSpeed2;
+ set => base.BottomRetractSpeed2 = JsonSettings.Properties.Bottom.RetractSpeed2 = (float)Math.Round(value, 2);
+ }
+
+ public override float RetractHeight2
+ {
+ get => JsonSettings.Properties.Exposure.RetractHeight2;
+ set => base.RetractHeight2 = JsonSettings.Properties.Exposure.RetractHeight2 = (float)Math.Round(value, 2);
+ }
+
+ public override float RetractSpeed2
+ {
+ get => JsonSettings.Properties.Exposure.RetractSpeed2;
+ set => base.RetractSpeed2 = JsonSettings.Properties.Exposure.RetractSpeed2 = (float)Math.Round(value, 2);
+ }
+
+
+ public override byte BottomLightPWM
+ {
+ get => JsonSettings.Properties.Bottom.LightPWM;
+ set => base.BottomLightPWM = JsonSettings.Properties.Bottom.LightPWM = value;
+ }
+
+ public override byte LightPWM
+ {
+ get => JsonSettings.Properties.Exposure.LightPWM;
+ set => base.LightPWM = JsonSettings.Properties.Exposure.LightPWM = value;
+ }
+
+ public override object[] Configs => new[] {(object) JsonSettings.Properties.Size, JsonSettings.Properties.Size.Millimeter, JsonSettings.Properties.Bottom, JsonSettings.Properties.Exposure};
+ #endregion
+
+ #region Methods
+
+ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(BottomRetractHeight)) JsonSettings.Properties.Bottom.RetractHeight = BottomRetractHeight;
+ else if (e.PropertyName == nameof(RetractHeight)) JsonSettings.Properties.Exposure.RetractHeight = RetractHeight;
+
+ base.OnPropertyChanged(e);
+ }
- public override float BottomRetractSpeed2
+ public override void Clear()
+ {
+ base.Clear();
+ JsonSettings.Layers = new List<LayerDef>();
+ }
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ // Redo layer data
+ if (JsonSettings.Layers is null)
{
- get => JsonSettings.Properties.Bottom.RetractSpeed2;
- set => base.BottomRetractSpeed2 = JsonSettings.Properties.Bottom.RetractSpeed2 = (float)Math.Round(value, 2);
+ JsonSettings.Layers = new List<LayerDef>();
}
-
- public override float RetractHeight2
+ else
{
- get => JsonSettings.Properties.Exposure.RetractHeight2;
- set => base.RetractHeight2 = JsonSettings.Properties.Exposure.RetractHeight2 = (float)Math.Round(value, 2);
+ JsonSettings.Layers.Clear();
}
-
- public override float RetractSpeed2
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- get => JsonSettings.Properties.Exposure.RetractSpeed2;
- set => base.RetractSpeed2 = JsonSettings.Properties.Exposure.RetractSpeed2 = (float)Math.Round(value, 2);
+ JsonSettings.Layers.Add(new LayerDef(this[layerIndex]));
}
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ outputFile.PutFileContent(FileConfigName, JsonSerializer.SerializeToUtf8Bytes(JsonSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Create);
- public override byte BottomLightPWM
+ if (CreatedThumbnailsCount > 0)
{
- get => JsonSettings.Properties.Bottom.LightPWM;
- set => base.BottomLightPWM = JsonSettings.Properties.Bottom.LightPWM = value;
+ using var stream = outputFile.CreateEntry(FilePreviewTinyName).Open();
+ stream.WriteBytes(Thumbnails[0]!.GetPngByes());
+ stream.Close();
}
- public override byte LightPWM
+ if (CreatedThumbnailsCount > 1)
{
- get => JsonSettings.Properties.Exposure.LightPWM;
- set => base.LightPWM = JsonSettings.Properties.Exposure.LightPWM = value;
+ using var stream = outputFile.CreateEntry(FilePreviewHugeName).Open();
+ stream.WriteBytes(Thumbnails[1]!.GetPngByes());
+ stream.Close();
}
- public override object[] Configs => new[] {(object) JsonSettings.Properties.Size, JsonSettings.Properties.Size.Millimeter, JsonSettings.Properties.Bottom, JsonSettings.Properties.Exposure};
- #endregion
-
- #region Methods
-
- protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- if (e.PropertyName == nameof(BottomRetractHeight)) JsonSettings.Properties.Bottom.RetractHeight = BottomRetractHeight;
- else if (e.PropertyName == nameof(RetractHeight)) JsonSettings.Properties.Exposure.RetractHeight = RetractHeight;
+ progress.Token.ThrowIfCancellationRequested();
- base.OnPropertyChanged(e);
- }
+ Layer layer = this[layerIndex];
- public override void Clear()
- {
- base.Clear();
- JsonSettings.Layers = new List<LayerDef>();
+ var layerimagePath = $"{FolderImageName}/{layerIndex:D8}.png";
+ outputFile.PutFileContent(layerimagePath, layer.CompressedBytes, ZipArchiveMode.Create);
+
+ progress++;
}
+ }
- protected override void EncodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using (var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read))
{
- // Redo layer data
- if (JsonSettings.Layers is null)
- {
- JsonSettings.Layers = new List<LayerDef>();
- }
- else
- {
- JsonSettings.Layers.Clear();
- }
-
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ var entry = inputFile.GetEntry(FileConfigName);
+ if (entry is null)
{
- JsonSettings.Layers.Add(new LayerDef(this[layerIndex]));
+ Clear();
+ throw new FileLoadException($"{FileConfigName} not found", FileFullPath);
}
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- outputFile.PutFileContent(FileConfigName, JsonConvert.SerializeObject(JsonSettings, Formatting.Indented), ZipArchiveMode.Create);
+ JsonSettings = JsonSerializer.Deserialize<Settings>(entry.Open())!;
+
+ Init(JsonSettings.Properties.Size.Layers, DecodeType == FileDecodeType.Partial);
- if (CreatedThumbnailsCount > 0)
+ entry = inputFile.GetEntry(FilePreviewTinyName);
+ if (entry is not null)
{
- using var stream = outputFile.CreateEntry(FilePreviewTinyName).Open();
- stream.WriteBytes(Thumbnails[0].GetPngByes());
+ using var stream = entry.Open();
+ Mat image = new();
+ CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
+ Thumbnails[0] = image;
stream.Close();
}
- if (CreatedThumbnailsCount > 1)
+ entry = inputFile.GetEntry(FilePreviewHugeName);
+ if (entry is not null)
{
- using var stream = outputFile.CreateEntry(FilePreviewHugeName).Open();
- stream.WriteBytes(Thumbnails[1].GetPngByes());
+ using var stream = entry.Open();
+ Mat image = new();
+ CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
+ Thumbnails[1] = image;
stream.Close();
}
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- progress.Token.ThrowIfCancellationRequested();
+ entry = inputFile.GetEntry($"{FolderImageName}/{layerIndex:D8}.png");
+ if (entry is null) continue;
- Layer layer = this[layerIndex];
-
- var layerimagePath = $"{FolderImageName}/{layerIndex:D8}.png";
- outputFile.PutFileContent(layerimagePath, layer.CompressedBytes, ZipArchiveMode.Create);
-
- progress++;
- }
- }
-
- protected override void DecodeInternally(OperationProgress progress)
- {
- using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
- {
- var entry = inputFile.GetEntry(FileConfigName);
- if (entry is null)
- {
- Clear();
- throw new FileLoadException($"{FileConfigName} not found", FileFullPath);
- }
-
- JsonSettings = Helpers.JsonDeserializeObject<Settings>(entry.Open());
-
- LayerManager.Init(JsonSettings.Properties.Size.Layers, DecodeType == FileDecodeType.Partial);
-
- entry = inputFile.GetEntry(FilePreviewTinyName);
- if (entry is not null)
+ if (DecodeType == FileDecodeType.Full)
{
using var stream = entry.Open();
- Mat image = new();
- CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
- Thumbnails[0] = image;
- stream.Close();
+ this[layerIndex] = new Layer(layerIndex, stream, this);
}
- entry = inputFile.GetEntry(FilePreviewHugeName);
- if (entry is not null)
+ if (JsonSettings.Layers?.Count > layerIndex)
{
- using var stream = entry.Open();
- Mat image = new();
- CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
- Thumbnails[1] = image;
- stream.Close();
+ JsonSettings.Layers[(int)layerIndex].CopyTo(this[layerIndex]);
}
-
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- entry = inputFile.GetEntry($"{FolderImageName}/{layerIndex:D8}.png");
- if (entry is null) continue;
-
- if (DecodeType == FileDecodeType.Full)
- {
- using var stream = entry.Open();
- this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
- }
-
- if (JsonSettings.Layers?.Count > layerIndex)
- {
- JsonSettings.Layers[(int)layerIndex].CopyTo(this[layerIndex]);
- }
- }
-
- progress.ProcessedItems++;
}
-
- LayerManager.GetBoundingRectangle(progress);
+
+ progress.ProcessedItems++;
}
- protected override void PartialSaveInternally(OperationProgress progress)
+ GetBoundingRectangle(progress);
+ }
+
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ if (JsonSettings.Layers is null)
{
- if (JsonSettings.Layers is null)
- {
- JsonSettings.Layers = new List<LayerDef>();
- }
- else
- {
- JsonSettings.Layers.Clear();
- }
+ JsonSettings.Layers = new List<LayerDef>();
+ }
+ else
+ {
+ JsonSettings.Layers.Clear();
+ }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- JsonSettings.Layers.Add(new LayerDef(this[layerIndex]));
- }
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ JsonSettings.Layers.Add(new LayerDef(this[layerIndex]));
+ }
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- outputFile.PutFileContent(FileConfigName, JsonConvert.SerializeObject(JsonSettings, Formatting.Indented), ZipArchiveMode.Update);
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ outputFile.PutFileContent(FileConfigName, JsonSerializer.SerializeToUtf8Bytes(JsonSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Update);
- //Decode(FileFullPath, progress);
- }
- #endregion
+ //Decode(FileFullPath, progress);
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/VDAFile.cs b/UVtools.Core/FileFormats/VDAFile.cs
index 8f83d41..d3183e4 100644
--- a/UVtools.Core/FileFormats/VDAFile.cs
+++ b/UVtools.Core/FileFormats/VDAFile.cs
@@ -17,412 +17,404 @@ using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+#region Sub Classes
+[Serializable]
+[XmlRoot(ElementName = "root")]
+public class VDARoot
{
- #region Sub Classes
[Serializable]
- [XmlRoot(ElementName = "root")]
- public class VDARoot
+ public class VDAFileInfo
{
[Serializable]
- public class VDAFileInfo
+ public class VDAVersion
{
- [Serializable]
- public class VDAVersion
- {
- public ushort Major { get; set; } = 1;
- public ushort Minor { get; set; } = 2;
+ public ushort Major { get; set; } = 1;
+ public ushort Minor { get; set; } = 2;
- }
+ }
+ [Serializable]
+ public class VDAWritten
+ {
[Serializable]
- public class VDAWritten
+ [XmlRoot(ElementName = "By")]
+ public class VDABy
{
- [Serializable]
- [XmlRoot(ElementName = "By")]
- public class VDABy
- {
- [XmlAttribute]
- public string ApplicationName { get; set; } = About.Software;
-
- [XmlAttribute]
- public string ApplicationVersion { get; set; } = About.VersionStr;
-
- public override string ToString()
- {
- return $"{ApplicationName} v{ApplicationVersion}";
- }
-
- public void Reset()
- {
- ApplicationName = About.Software;
- ApplicationVersion = About.VersionStr;
- }
- }
+ [XmlAttribute]
+ public string ApplicationName { get; set; } = About.Software;
- public VDABy By { get; set; } = new();
+ [XmlAttribute]
+ public string ApplicationVersion { get; set; } = About.VersionStr;
- public string When { get; set; } = DateTime.UtcNow.ToString("u");
+ public override string ToString()
+ {
+ return $"{ApplicationName} v{ApplicationVersion}";
+ }
public void Reset()
{
- When = DateTime.UtcNow.ToString("u");
- By.Reset();
+ ApplicationName = About.Software;
+ ApplicationVersion = About.VersionStr;
}
}
+ public VDABy By { get; set; } = new();
- public VDAVersion Version { get; set; } = new();
+ public string When { get; set; } = DateTime.UtcNow.ToString("u");
- public VDAWritten Written { get; set; } = new();
+ public void Reset()
+ {
+ When = DateTime.UtcNow.ToString("u");
+ By.Reset();
+ }
}
- [Serializable]
- public class VDASlices
- {
- public ushort Count { get; set; } = 1;
- [XmlElement("thickness")]
- public float LayerHeight { get; set; }
+ public VDAVersion Version { get; set; } = new();
- [XmlElement("startHeight")]
- public float StartHeight { get; set; }
+ public VDAWritten Written { get; set; } = new();
+ }
- [XmlElement("endHeight")]
- public float EndHeight { get; set; }
+ [Serializable]
+ public class VDASlices
+ {
+ public ushort Count { get; set; } = 1;
- [XmlElement("layersCount")]
- public uint LayerCount { get; set; }
- }
+ [XmlElement("thickness")]
+ public float LayerHeight { get; set; }
- [Serializable]
- public class VDAMachines
- {
- public string FileType { get; set; } = "ZIP File";
- public string Resolution { get; set; } = "1920*1080P";
- public string PixelXSize { get; set; } = "50um";
- public string PixelYSize { get; set; } = "50um";
+ [XmlElement("startHeight")]
+ public float StartHeight { get; set; }
- [XmlElement("Anti-Aliasing")]
- public byte AntiAliasing { get; set; } = 1;
+ [XmlElement("endHeight")]
+ public float EndHeight { get; set; }
- public float XLength { get; set; }
- public float YWidth { get; set; }
- public float ZHeight { get; set; }
- }
+ [XmlElement("layersCount")]
+ public uint LayerCount { get; set; }
+ }
- public class VDALayer
- {
- [XmlElement("Index")]
- public uint Index { get; set; }
+ [Serializable]
+ public class VDAMachines
+ {
+ public string FileType { get; set; } = "ZIP File";
+ public string Resolution { get; set; } = "1920*1080P";
+ public string PixelXSize { get; set; } = "50um";
+ public string PixelYSize { get; set; } = "50um";
- [XmlElement("zvalue")]
- public float ZPosition { get; set; }
+ [XmlElement("Anti-Aliasing")]
+ public byte AntiAliasing { get; set; } = 1;
- [XmlElement("filename")]
- public string Filename { get; set; }
+ public float XLength { get; set; }
+ public float YWidth { get; set; }
+ public float ZHeight { get; set; }
+ }
- public VDALayer()
- {
- }
+ public class VDALayer
+ {
+ [XmlElement("Index")]
+ public uint Index { get; set; }
- public VDALayer(uint index, float zPosition, string filename)
- {
- Index = index;
- ZPosition = zPosition;
- Filename = filename;
- }
- }
+ [XmlElement("zvalue")]
+ public float ZPosition { get; set; }
+ [XmlElement("filename")]
+ public string? Filename { get; set; }
- public VDAFileInfo FileInfo { get; set; } = new();
- public VDASlices Slices { get; set; } = new();
- public VDAMachines Machines { get; set; } = new();
- public List<VDALayer> Layers { get; set; } = new();
+ public VDALayer()
+ {
+ }
+
+ public VDALayer(uint index, float zPosition, string? filename)
+ {
+ Index = index;
+ ZPosition = zPosition;
+ Filename = filename;
+ }
}
- #endregion
- public class VDAFile : FileFormat
- {
- #region Constants
- #endregion
+ public VDAFileInfo FileInfo { get; set; } = new();
+ public VDASlices Slices { get; set; } = new();
+ public VDAMachines Machines { get; set; } = new();
+ public List<VDALayer> Layers { get; set; } = new();
+}
+#endregion
+
+public class VDAFile : FileFormat
+{
+ #region Constants
+
+ #endregion
- #region Properties
- public VDARoot ManifestFile { get; set; } = new ();
+ #region Properties
+ public VDARoot ManifestFile { get; set; } = new ();
- public override FileFormatType FileType => FileFormatType.Archive;
+ public override FileFormatType FileType => FileFormatType.Archive;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(VDAFile), "zip", "Voxeldance Additive Zip")
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(VDAFile), "zip", "Voxeldance Additive Zip")
+ };
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get
{
- get
- {
- var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries);
- if (resolution.Length < 2) return 0;
- uint.TryParse(resolution[0], out var xRes);
- return xRes;
- }
- set
- {
- ManifestFile.Machines.Resolution = $"{value}*{ResolutionY}P";
- RaisePropertyChanged();
- }
+ var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries);
+ if (resolution.Length < 2) return 0;
+ uint.TryParse(resolution[0], out var xRes);
+ return xRes;
}
-
- public override uint ResolutionY
+ set
{
- get
- {
- var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries);
- if (resolution.Length < 2) return 0;
- resolution[1] = resolution[1].TrimEnd('P');
- uint.TryParse(resolution[1], out var yRes);
- return yRes;
- }
- set
- {
- ManifestFile.Machines.Resolution = $"{ResolutionX}*{value}P";
- RaisePropertyChanged();
- }
+ ManifestFile.Machines.Resolution = $"{value}*{ResolutionY}P";
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override uint ResolutionY
+ {
+ get
{
- get
- {
- if (ManifestFile.Machines.XLength > 0) return ManifestFile.Machines.XLength;
+ var resolution = ManifestFile.Machines.Resolution.Split('*', StringSplitOptions.TrimEntries);
+ if (resolution.Length < 2) return 0;
+ resolution[1] = resolution[1].TrimEnd('P');
+ uint.TryParse(resolution[1], out var yRes);
+ return yRes;
+ }
+ set
+ {
+ ManifestFile.Machines.Resolution = $"{ResolutionX}*{value}P";
+ RaisePropertyChanged();
+ }
+ }
- var umStr= ManifestFile.Machines.PixelXSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase);
+ public override float DisplayWidth
+ {
+ get
+ {
+ if (ManifestFile.Machines.XLength > 0) return ManifestFile.Machines.XLength;
- if (ushort.TryParse(umStr, out var um) && um > 0)
- {
- return (float) Math.Round(ResolutionX * um / 1000f, 2);
- }
+ var umStr= ManifestFile.Machines.PixelXSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase);
- return ManifestFile.Machines.XLength;
- }
- set
+ if (ushort.TryParse(umStr, out var um) && um > 0)
{
- ManifestFile.Machines.XLength = (float) Math.Round(value, 2);
- ManifestFile.Machines.PixelXSize = $"{Math.Round(value / ResolutionX * 1000, 2)}um";
- RaisePropertyChanged();
+ return (float) Math.Round(ResolutionX * um / 1000f, 2);
}
- }
- public override float DisplayHeight
+ return ManifestFile.Machines.XLength;
+ }
+ set
{
- get
- {
- if (ManifestFile.Machines.YWidth > 0) return ManifestFile.Machines.YWidth;
+ ManifestFile.Machines.XLength = (float) Math.Round(value, 2);
+ ManifestFile.Machines.PixelXSize = $"{Math.Round(value / ResolutionX * 1000, 2)}um";
+ RaisePropertyChanged();
+ }
+ }
- var umStr = ManifestFile.Machines.PixelYSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase);
+ public override float DisplayHeight
+ {
+ get
+ {
+ if (ManifestFile.Machines.YWidth > 0) return ManifestFile.Machines.YWidth;
- if (ushort.TryParse(umStr, out var um) && um > 0)
- {
- return (float)Math.Round(ResolutionY * um / 1000f, 2);
- }
+ var umStr = ManifestFile.Machines.PixelYSize.Replace("um", string.Empty, StringComparison.OrdinalIgnoreCase);
- return ManifestFile.Machines.YWidth;
- }
- set
+ if (ushort.TryParse(umStr, out var um) && um > 0)
{
- ManifestFile.Machines.YWidth = (float)Math.Round(value, 2);
- ManifestFile.Machines.PixelYSize = $"{Math.Round(value / ResolutionY * 1000, 2)}um";
- RaisePropertyChanged();
+ return (float)Math.Round(ResolutionY * um / 1000f, 2);
}
- }
- public override float MachineZ
+ return ManifestFile.Machines.YWidth;
+ }
+ set
{
- get => ManifestFile.Machines.ZHeight > 0 ? ManifestFile.Machines.ZHeight : base.MachineZ;
- set
- {
- ManifestFile.Machines.ZHeight = value;
- RaisePropertyChanged();
- }
+ ManifestFile.Machines.YWidth = (float)Math.Round(value, 2);
+ ManifestFile.Machines.PixelYSize = $"{Math.Round(value / ResolutionY * 1000, 2)}um";
+ RaisePropertyChanged();
}
+ }
- public override byte AntiAliasing
+ public override float MachineZ
+ {
+ get => ManifestFile.Machines.ZHeight > 0 ? ManifestFile.Machines.ZHeight : base.MachineZ;
+ set
{
- get => ManifestFile.Machines.AntiAliasing;
- set => base.AntiAliasing = ManifestFile.Machines.AntiAliasing = value.Clamp(1, 16);
+ ManifestFile.Machines.ZHeight = value;
+ RaisePropertyChanged();
}
+ }
+
+ public override byte AntiAliasing
+ {
+ get => ManifestFile.Machines.AntiAliasing;
+ set => base.AntiAliasing = ManifestFile.Machines.AntiAliasing = value.Clamp(1, 16);
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => ManifestFile.Slices.LayerHeight;
+ set
{
- get => ManifestFile.Slices.LayerHeight;
- set
- {
- ManifestFile.Slices.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ ManifestFile.Slices.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
+
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = ManifestFile.Slices.LayerCount = base.LayerCount;
+ }
- public override uint LayerCount
+
+ public override object[] Configs => new object[] {
+ ManifestFile.FileInfo.Version,
+ ManifestFile.FileInfo.Written,
+ ManifestFile.Machines,
+ ManifestFile.Slices };
+
+ #endregion
+
+ #region Constructor
+ public VDAFile()
+ { }
+ #endregion
+
+ #region Methods
+
+ public override bool CanProcess(string fileFullPath)
+ {
+ if(!base.CanProcess(fileFullPath)) return false;
+
+ try
+ {
+ using var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read);
+ if (zip.Entries.Any(entry => entry.Name.EndsWith(".xml"))) return true;
+ }
+ catch (Exception e)
{
- get => base.LayerCount;
- set => base.LayerCount = ManifestFile.Slices.LayerCount = base.LayerCount;
+ Debug.WriteLine(e);
}
+
-
- public override object[] Configs => new object[] {
- ManifestFile.FileInfo.Version,
- ManifestFile.FileInfo.Written,
- ManifestFile.Machines,
- ManifestFile.Slices };
+ return false;
+ }
- #endregion
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ var manifestFilename = Filename!.
+ Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml").
+ Replace($".{FileExtensions[0].Extension}", ".xml");
- #region Constructor
- public VDAFile()
- { }
- #endregion
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ var layer = this[layerIndex];
+ var filename = $"{layerIndex + 1}".PadLeft(4, '0') + ".png";
+ outputFile.PutFileContent(filename, layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
+ }
- #region Methods
+ UpdateManifest();
- public override bool CanProcess(string fileFullPath)
+ var entry = outputFile.CreateEntry(manifestFilename);
+ using var stream = entry.Open();
+ XmlExtensions.Serialize(ManifestFile, stream, XmlExtensions.SettingsIndent, true);
+ }
+
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using (var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read))
{
- if(!base.CanProcess(fileFullPath)) return false;
+ var entry = inputFile.Entries.FirstOrDefault(zipEntry => zipEntry.Name.EndsWith(".xml"));
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException($".xml manifest not found", FileFullPath);
+ }
try
{
- using var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read);
- if (zip.Entries.Any(entry => entry.Name.EndsWith(".xml"))) return true;
+ using var stream = entry.Open();
+ ManifestFile = XmlExtensions.DeserializeFromStream<VDARoot>(stream);
}
catch (Exception e)
{
- Debug.WriteLine(e);
+ Clear();
+ throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
}
-
-
- return false;
- }
-
- protected override void EncodeInternally(OperationProgress progress)
- {
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- var manifestFilename = Filename.
- Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml").
- Replace($".{FileExtensions[0].Extension}", ".xml");
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- var layer = this[layerIndex];
- var filename = $"{layerIndex + 1}".PadLeft(4, '0') + ".png";
- outputFile.PutFileContent(filename, layer.CompressedBytes, ZipArchiveMode.Create);
- progress++;
- }
- UpdateManifest();
+ Init(ManifestFile.Slices.LayerCount, DecodeType == FileDecodeType.Partial);
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
- XmlSerializer serializer = new(ManifestFile.GetType());
- XmlSerializerNamespaces ns = new();
- ns.Add("", "");
- var entry = outputFile.CreateEntry(manifestFilename);
- using var stream = entry.Open();
- serializer.Serialize(stream, ManifestFile, ns);
- }
- protected override void DecodeInternally(OperationProgress progress)
- {
- using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- var entry = inputFile.Entries.FirstOrDefault(zipEntry => zipEntry.Name.EndsWith(".xml"));
+ if (progress.Token.IsCancellationRequested) break;
+ var filename = $"{layerIndex + 1}".PadLeft(4, '0')+".png";
+ entry = inputFile.GetEntry(filename);
if (entry is null)
{
Clear();
- throw new FileLoadException($".xml manifest not found", FileFullPath);
+ throw new FileLoadException($"Layer {filename} not found", FileFullPath);
}
- try
+ if (DecodeType == FileDecodeType.Full)
{
- var serializer = new XmlSerializer(ManifestFile.GetType());
using var stream = entry.Open();
- ManifestFile = (VDARoot)serializer.Deserialize(stream);
- }
- catch (Exception e)
- {
- Clear();
- throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
+ this[layerIndex] = new Layer(layerIndex, stream, this);
}
-
- LayerManager.Init(ManifestFile.Slices.LayerCount, DecodeType == FileDecodeType.Partial);
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
-
-
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- if (progress.Token.IsCancellationRequested) break;
- var filename = $"{layerIndex + 1}".PadLeft(4, '0')+".png";
- entry = inputFile.GetEntry(filename);
- if (entry is null)
- {
- Clear();
- throw new FileLoadException($"Layer {filename} not found", FileFullPath);
- }
-
- if (DecodeType == FileDecodeType.Full)
- {
- using var stream = entry.Open();
- this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
- }
-
- progress++;
- }
+ progress++;
}
-
- LayerManager.GetBoundingRectangle(progress);
}
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- bool deleted;
+ GetBoundingRectangle(progress);
+ }
- do
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ bool deleted;
+
+ do
+ {
+ deleted = false;
+ foreach (var zipEntry in outputFile.Entries)
{
- deleted = false;
- foreach (var zipEntry in outputFile.Entries)
- {
- if (!zipEntry.Name.EndsWith(".xml")) continue;
- zipEntry.Delete();
- deleted = true;
- break;
- }
- } while (deleted);
+ if (!zipEntry.Name.EndsWith(".xml")) continue;
+ zipEntry.Delete();
+ deleted = true;
+ break;
+ }
+ } while (deleted);
- var manifestFilename = Path.GetFileName(FileFullPath).
- Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml").
- Replace($".{FileExtensions[0].Extension}", ".xml");
+ var manifestFilename = Path.GetFileName(FileFullPath)!.
+ Replace($".{FileExtensions[0].Extension}{TemporaryFileAppend}", ".xml").
+ Replace($".{FileExtensions[0].Extension}", ".xml");
- UpdateManifest();
+ UpdateManifest();
- XmlSerializer serializer = new(ManifestFile.GetType());
- XmlSerializerNamespaces ns = new();
- ns.Add("", "");
- var entry = outputFile.CreateEntry(manifestFilename);
- using var stream = entry.Open();
- serializer.Serialize(stream, ManifestFile, ns);
- }
+ var entry = outputFile.CreateEntry(manifestFilename);
+ using var stream = entry.Open();
+ XmlExtensions.Serialize(ManifestFile, stream, XmlExtensions.SettingsIndent, true);
+ }
- public void UpdateManifest()
+ public void UpdateManifest()
+ {
+ ManifestFile.FileInfo.Written.Reset();
+ ManifestFile.Slices.StartHeight = FirstLayer?.PositionZ ?? 0;
+ ManifestFile.Slices.EndHeight = LastLayer?.PositionZ ?? 0;
+ ManifestFile.Layers.Clear();
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- ManifestFile.FileInfo.Written.Reset();
- ManifestFile.Slices.StartHeight = FirstLayer.PositionZ;
- ManifestFile.Slices.EndHeight = LastLayer.PositionZ;
- ManifestFile.Layers.Clear();
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- var layer = this[layerIndex];
- ManifestFile.Layers.Add(new VDARoot.VDALayer(layerIndex, layer.PositionZ, layer.FormatFileName(4, false)));
- }
+ var layer = this[layerIndex];
+ ManifestFile.Layers.Add(new VDARoot.VDALayer(layerIndex, layer.PositionZ, layer.FormatFileName(4, false)));
}
- #endregion
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs
index 4e0186b..ae37105 100644
--- a/UVtools.Core/FileFormats/VDTFile.cs
+++ b/UVtools.Core/FileFormats/VDTFile.cs
@@ -6,758 +6,760 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
+using System.Drawing;
using System.IO;
using System.IO.Compression;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Newtonsoft.Json;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Xml;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
-{
- public class VDTFile : FileFormat
- {
- #region Constants
-
- private const string FileManifestName = "manifest.json";
- private static string[] FilePreviewNames = {
- "Preview_Top.png",
- "Preview_Top_48.png",
- "Preview_Right.png",
- "Preview_Right_48.png",
- "Preview_Left.png",
- "Preview_Left_48.png",
- "Preview_Front.png",
- "Preview_Front_48.png",
- "Preview_FLT.png",
- "Preview_FLT_48.png",
- };
- #endregion
-
- #region Sub Classes
-
- public sealed class VDTManifest
- {
- [JsonProperty("application_name")] public string ApplicationName { get; set; } = About.Software;
+namespace UVtools.Core.FileFormats;
- [JsonProperty("application_version")] public string ApplicationVersion { get; set; } = About.VersionStr;
- //2021-04-09 17:48:46
- [JsonProperty("create_datetime")] public string CreateDateTime { get; set; } = DateTime.UtcNow.ToString("u");
+public class VDTFile : FileFormat
+{
+ #region Constants
+
+ private const string FileManifestName = "manifest.json";
+ private static readonly string[] FilePreviewNames = {
+ "Preview_Top.png",
+ "Preview_Top_48.png",
+ "Preview_Right.png",
+ "Preview_Right_48.png",
+ "Preview_Left.png",
+ "Preview_Left_48.png",
+ "Preview_Front.png",
+ "Preview_Front_48.png",
+ "Preview_FLT.png",
+ "Preview_FLT_48.png",
+ };
+ #endregion
+
+ #region Sub Classes
+
+ public sealed class VDTManifest
+ {
+ [JsonPropertyName("application_name")] public string ApplicationName { get; set; } = About.Software;
- [JsonProperty("file_version")] public byte FileVersion { get; set; } = 1;
+ [JsonPropertyName("application_version")] public string ApplicationVersion { get; set; } = About.VersionStr;
+ //2021-04-09 17:48:46
+ [JsonPropertyName("create_datetime")] public string CreateDateTime { get; set; } = DateTime.UtcNow.ToString("u");
- [JsonProperty("project_name")] public string ProjectName { get; set; } = "UVtools";
+ [JsonPropertyName("file_version")] public byte FileVersion { get; set; } = 1;
- [JsonProperty("machine")] public VDTMachine Machine { get; set; } = new();
- [JsonProperty("advanced_parameters")] public VDTAdvancedParameters AdvancedParameters { get; set; } = new();
- [JsonProperty("resin")] public VDTResin Resin { get; set; } = new();
- [JsonProperty("print")] public VDTPrint Print { get; set; } = new();
- [JsonProperty("print_statistics")] public VDTPrintStatistics PrintStatistics { get; set; } = new();
- [JsonProperty("layers")] public VDTLayer[] Layers { get; set; }
- }
+ [JsonPropertyName("project_name")] public string ProjectName { get; set; } = "UVtools";
- public sealed class VDTAdvancedParameters
- {
- [JsonProperty("antialasing_level")] public byte AntialasingLevel { get; set; } = 1;
- [JsonProperty("grey_level")] public byte GreyLevel { get; set; }
- [JsonProperty("image_blur_level")] public byte ImageBlurLevel { get; set; }
+ [JsonPropertyName("machine")] public VDTMachine Machine { get; set; } = new();
+ [JsonPropertyName("advanced_parameters")] public VDTAdvancedParameters AdvancedParameters { get; set; } = new();
+ [JsonPropertyName("resin")] public VDTResin Resin { get; set; } = new();
+ [JsonPropertyName("print")] public VDTPrint Print { get; set; } = new();
+ [JsonPropertyName("print_statistics")] public VDTPrintStatistics PrintStatistics { get; set; } = new();
+ [JsonPropertyName("layers")] public VDTLayer[]? Layers { get; set; }
+ }
- [JsonProperty("bottom_light_pwm")] public byte BottomLightPWM { get; set; } = DefaultBottomLightPWM;
- [JsonProperty("light_pwm")] public byte LightPWM { get; set; } = DefaultLightPWM;
+ public sealed class VDTAdvancedParameters
+ {
+ [JsonPropertyName("antialasing_level")] public byte AntialasingLevel { get; set; } = 1;
+ [JsonPropertyName("grey_level")] public byte GreyLevel { get; set; }
+ [JsonPropertyName("image_blur_level")] public byte ImageBlurLevel { get; set; }
- [JsonProperty("beam_compensation")] public float BeamCompensation { get; set; }
- }
+ [JsonPropertyName("bottom_light_pwm")] public byte BottomLightPWM { get; set; } = DefaultBottomLightPWM;
+ [JsonPropertyName("light_pwm")] public byte LightPWM { get; set; } = DefaultLightPWM;
- public sealed class VDTMachine
- {
- [JsonProperty("name")] public string Name { get; set; } = "Unknown";
- [JsonProperty("type")] public string Type { get; set; } = "Default";
- [JsonProperty("lcd_width")] public float DisplayWidth { get; set; }
- [JsonProperty("lcd_height")] public float DisplayHeight { get; set; }
- [JsonProperty("z_height")] public float ZHeight { get; set; }
- [JsonProperty("resolution_x")] public ushort ResolutionX { get; set; }
- [JsonProperty("resolution_y")] public ushort ResolutionY { get; set; }
- [JsonProperty("x_mirror")] public bool XMirror { get; set; }
- [JsonProperty("y_mirror")] public bool YMirror { get; set; }
- [JsonProperty("notes")] public string Notes { get; set; }
- [JsonProperty("uvtools_convert_to")] public string UVToolsConvertTo { get; set; }
- }
+ [JsonPropertyName("beam_compensation")] public float BeamCompensation { get; set; }
+ }
- public sealed class VDTResin
- {
- [JsonProperty("name")] public string Name { get; set; }
- [JsonProperty("cost")] public float Cost { get; set; }
- [JsonProperty("density")] public float Density { get; set; } = 1;
- [JsonProperty("notes")] public string Notes { get; set; }
- }
+ public sealed class VDTMachine
+ {
+ [JsonPropertyName("name")] public string Name { get; set; } = "Unknown";
+ [JsonPropertyName("type")] public string Type { get; set; } = "Default";
+ [JsonPropertyName("lcd_width")] public float DisplayWidth { get; set; }
+ [JsonPropertyName("lcd_height")] public float DisplayHeight { get; set; }
+ [JsonPropertyName("z_height")] public float ZHeight { get; set; }
+ [JsonPropertyName("resolution_x")] public ushort ResolutionX { get; set; }
+ [JsonPropertyName("resolution_y")] public ushort ResolutionY { get; set; }
+ [JsonPropertyName("x_mirror")] public bool XMirror { get; set; }
+ [JsonPropertyName("y_mirror")] public bool YMirror { get; set; }
+ [JsonPropertyName("notes")] public string Notes { get; set; } = string.Empty;
+ [JsonPropertyName("uvtools_convert_to")] public string UVToolsConvertTo { get; set; } = string.Empty;
+ }
- public sealed class VDTPrint
- {
- [JsonProperty("layer_thickness")] public float LayerHeight { get; set; }
- [JsonProperty("bottom_layers")] public ushort BottomLayers { get; set; } = DefaultBottomLayerCount;
- [JsonProperty("bottom_light_off_delay")] public float BottomLightOffDelay { get; set; }
- [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; }
- [JsonProperty("bottom_wait_time_before_cure")] public float BottomWaitTimeBeforeCure { get; set; }
- [JsonProperty("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; }
- [JsonProperty("bottom_exposure_time")] public float BottomExposureTime { get; set; } = DefaultBottomExposureTime;
- [JsonProperty("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime;
- [JsonProperty("bottom_wait_time_after_cure")] public float BottomWaitTimeAfterCure { get; set; }
- [JsonProperty("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; }
- [JsonProperty("bottom_lift_distance")] public float BottomLiftHeight { get; set; } = DefaultBottomLiftHeight;
- [JsonProperty("bottom_lift_speed")] public float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed;
- [JsonProperty("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight;
- [JsonProperty("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed;
- [JsonProperty("bottom_lift_distance2")] public float BottomLiftHeight2 { get; set; } = DefaultBottomLiftHeight2;
- [JsonProperty("bottom_lift_speed2")] public float BottomLiftSpeed2 { get; set; } = DefaultBottomLiftSpeed2;
- [JsonProperty("lift_distance2")] public float LiftHeight2 { get; set; } = DefaultLiftHeight2;
- [JsonProperty("lift_speed2")] public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2;
- [JsonProperty("bottom_wait_time_after_lift")] public float BottomWaitTimeAfterLift { get; set; }
- [JsonProperty("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; }
- [JsonProperty("bottom_retract_speed")] public float BottomRetractSpeed { get; set; } = DefaultBottomRetractSpeed;
- [JsonProperty("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed;
- [JsonProperty("bottom_retract_distance2")] public float BottomRetractHeight2 { get; set; } = DefaultBottomRetractHeight2;
- [JsonProperty("bottom_retract_speed2")] public float BottomRetractSpeed2 { get; set; } = DefaultBottomRetractSpeed2;
- [JsonProperty("retract_distance2")] public float RetractHeight2 { get; set; } = DefaultRetractHeight2;
- [JsonProperty("retract_speed2")] public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2;
- }
+ public sealed class VDTResin
+ {
+ [JsonPropertyName("name")] public string? Name { get; set; } = string.Empty;
+ [JsonPropertyName("cost")] public float Cost { get; set; }
+ [JsonPropertyName("density")] public float Density { get; set; } = 1;
+ [JsonPropertyName("notes")] public string Notes { get; set; } = string.Empty;
+ }
- public sealed class VDTPrintStatistics
- {
- [JsonProperty("price")] public float Price { get; set; }
- [JsonProperty("price_currency")] public string PriceCurrency { get; set; } = "€";
- [JsonProperty("estimated_time")] public uint EstimatedTime { get; set; }
- [JsonProperty("volume")] public float Volume { get; set; }
- [JsonProperty("weight")] public float Weight { get; set; }
- }
+ public sealed class VDTPrint
+ {
+ [JsonPropertyName("layer_thickness")] public float LayerHeight { get; set; }
+ [JsonPropertyName("bottom_layers")] public ushort BottomLayers { get; set; } = DefaultBottomLayerCount;
+ [JsonPropertyName("bottom_light_off_delay")] public float BottomLightOffDelay { get; set; }
+ [JsonPropertyName("light_off_delay")] public float LightOffDelay { get; set; }
+ [JsonPropertyName("bottom_wait_time_before_cure")] public float BottomWaitTimeBeforeCure { get; set; }
+ [JsonPropertyName("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; }
+ [JsonPropertyName("bottom_exposure_time")] public float BottomExposureTime { get; set; } = DefaultBottomExposureTime;
+ [JsonPropertyName("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime;
+ [JsonPropertyName("bottom_wait_time_after_cure")] public float BottomWaitTimeAfterCure { get; set; }
+ [JsonPropertyName("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; }
+ [JsonPropertyName("bottom_lift_distance")] public float BottomLiftHeight { get; set; } = DefaultBottomLiftHeight;
+ [JsonPropertyName("bottom_lift_speed")] public float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed;
+ [JsonPropertyName("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight;
+ [JsonPropertyName("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed;
+ [JsonPropertyName("bottom_lift_distance2")] public float BottomLiftHeight2 { get; set; } = DefaultBottomLiftHeight2;
+ [JsonPropertyName("bottom_lift_speed2")] public float BottomLiftSpeed2 { get; set; } = DefaultBottomLiftSpeed2;
+ [JsonPropertyName("lift_distance2")] public float LiftHeight2 { get; set; } = DefaultLiftHeight2;
+ [JsonPropertyName("lift_speed2")] public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2;
+ [JsonPropertyName("bottom_wait_time_after_lift")] public float BottomWaitTimeAfterLift { get; set; }
+ [JsonPropertyName("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; }
+ [JsonPropertyName("bottom_retract_speed")] public float BottomRetractSpeed { get; set; } = DefaultBottomRetractSpeed;
+ [JsonPropertyName("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed;
+ [JsonPropertyName("bottom_retract_distance2")] public float BottomRetractHeight2 { get; set; } = DefaultBottomRetractHeight2;
+ [JsonPropertyName("bottom_retract_speed2")] public float BottomRetractSpeed2 { get; set; } = DefaultBottomRetractSpeed2;
+ [JsonPropertyName("retract_distance2")] public float RetractHeight2 { get; set; } = DefaultRetractHeight2;
+ [JsonPropertyName("retract_speed2")] public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2;
+ }
- public sealed class VDTLayer
- {
- [JsonProperty("height")] public float PositionZ { get; set; }
- [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; }
- [JsonProperty("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; }
- [JsonProperty("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime;
- [JsonProperty("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; }
- [JsonProperty("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight;
- [JsonProperty("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed;
- [JsonProperty("lift_distance2")] public float LiftHeight2 { get; set; } = DefaultLiftHeight2;
- [JsonProperty("lift_speed2")] public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2;
- [JsonProperty("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; }
- [JsonProperty("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed;
- [JsonProperty("retract_distance2")] public float RetractHeight2 { get; set; } = DefaultRetractHeight2;
- [JsonProperty("retract_speed2")] public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2;
- [JsonProperty("light_pwm")] public byte LightPWM { get; set; } = DefaultLightPWM;
-
- public void SetFrom(Layer layer)
- {
- PositionZ = layer.PositionZ;
- LightOffDelay = layer.LightOffDelay;
- WaitTimeBeforeCure = layer.WaitTimeBeforeCure;
- ExposureTime = layer.ExposureTime;
- WaitTimeAfterCure = layer.WaitTimeAfterCure;
- LiftHeight = layer.LiftHeight;
- LiftSpeed = layer.LiftSpeed;
- LiftHeight2 = layer.LiftHeight2;
- LiftSpeed2 = layer.LiftSpeed2;
- WaitTimeAfterLift = layer.WaitTimeAfterLift;
- RetractSpeed = layer.RetractSpeed;
- RetractHeight2 = layer.RetractHeight2;
- RetractSpeed2 = layer.RetractSpeed2;
- LightPWM = layer.LightPWM;
- }
+ public sealed class VDTPrintStatistics
+ {
+ [JsonPropertyName("price")] public float Price { get; set; }
+ [JsonPropertyName("price_currency")] public string PriceCurrency { get; set; } = "€";
+ [JsonPropertyName("estimated_time")] public uint EstimatedTime { get; set; }
+ [JsonPropertyName("volume")] public float Volume { get; set; }
+ [JsonPropertyName("weight")] public float Weight { get; set; }
+ }
- public void CopyTo(Layer layer)
- {
- layer.PositionZ = PositionZ;
- layer.LightOffDelay = LightOffDelay;
- layer.WaitTimeBeforeCure = WaitTimeBeforeCure;
- layer.ExposureTime = ExposureTime;
- layer.WaitTimeAfterCure = WaitTimeAfterCure;
- layer.LiftHeight = LiftHeight;
- layer.LiftSpeed = LiftSpeed;
- layer.LiftHeight2 = LiftHeight2;
- layer.LiftSpeed2 = LiftSpeed2;
- layer.WaitTimeAfterLift = WaitTimeAfterLift;
- layer.RetractSpeed = RetractSpeed;
- layer.RetractHeight2 = RetractHeight2;
- layer.RetractSpeed2 = RetractSpeed2;
- layer.LightPWM = LightPWM;
- }
+ public sealed class VDTLayer
+ {
+ [JsonPropertyName("height")] public float PositionZ { get; set; }
+ [JsonPropertyName("light_off_delay")] public float LightOffDelay { get; set; }
+ [JsonPropertyName("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; }
+ [JsonPropertyName("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime;
+ [JsonPropertyName("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; }
+ [JsonPropertyName("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight;
+ [JsonPropertyName("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed;
+ [JsonPropertyName("lift_distance2")] public float LiftHeight2 { get; set; } = DefaultLiftHeight2;
+ [JsonPropertyName("lift_speed2")] public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2;
+ [JsonPropertyName("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; }
+ [JsonPropertyName("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed;
+ [JsonPropertyName("retract_distance2")] public float RetractHeight2 { get; set; } = DefaultRetractHeight2;
+ [JsonPropertyName("retract_speed2")] public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2;
+ [JsonPropertyName("light_pwm")] public byte LightPWM { get; set; } = DefaultLightPWM;
+
+ public void SetFrom(Layer layer)
+ {
+ PositionZ = layer.PositionZ;
+ LightOffDelay = layer.LightOffDelay;
+ WaitTimeBeforeCure = layer.WaitTimeBeforeCure;
+ ExposureTime = layer.ExposureTime;
+ WaitTimeAfterCure = layer.WaitTimeAfterCure;
+ LiftHeight = layer.LiftHeight;
+ LiftSpeed = layer.LiftSpeed;
+ LiftHeight2 = layer.LiftHeight2;
+ LiftSpeed2 = layer.LiftSpeed2;
+ WaitTimeAfterLift = layer.WaitTimeAfterLift;
+ RetractSpeed = layer.RetractSpeed;
+ RetractHeight2 = layer.RetractHeight2;
+ RetractSpeed2 = layer.RetractSpeed2;
+ LightPWM = layer.LightPWM;
+ }
+
+ public void CopyTo(Layer layer)
+ {
+ layer.PositionZ = PositionZ;
+ layer.LightOffDelay = LightOffDelay;
+ layer.WaitTimeBeforeCure = WaitTimeBeforeCure;
+ layer.ExposureTime = ExposureTime;
+ layer.WaitTimeAfterCure = WaitTimeAfterCure;
+ layer.LiftHeight = LiftHeight;
+ layer.LiftSpeed = LiftSpeed;
+ layer.LiftHeight2 = LiftHeight2;
+ layer.LiftSpeed2 = LiftSpeed2;
+ layer.WaitTimeAfterLift = WaitTimeAfterLift;
+ layer.RetractSpeed = RetractSpeed;
+ layer.RetractHeight2 = RetractHeight2;
+ layer.RetractSpeed2 = RetractSpeed2;
+ layer.LightPWM = LightPWM;
}
+ }
- #endregion
-
- #region Properties
- public VDTManifest ManifestFile { get; set; } = new();
-
- public override FileFormatType FileType => FileFormatType.Archive;
-
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(VDTFile), "vdt", "Voxeldance Tango VDT")
- };
-
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
-
- PrintParameterModifier.BottomLightOffDelay,
- PrintParameterModifier.LightOffDelay,
-
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
-
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
-
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
-
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
-
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
-
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
-
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
-
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
-
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.LightOffDelay,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
+ #endregion
+
+ #region Properties
+ public VDTManifest ManifestFile { get; set; } = new();
+
+ public override FileFormatType FileType => FileFormatType.Archive;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(VDTFile), "vdt", "Voxeldance Tango VDT")
+ };
+
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
+
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
+
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
+
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
+
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LightOffDelay,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } =
- {
- new(200, 200),
- new(48, 48),
- new(200, 200),
- new(48, 48),
- new(200, 200),
- new(48, 48),
- new(200, 200),
- new(48, 48),
- new(200, 200),
- new(48, 48),
- };
-
- public override uint[] AvailableVersions { get; } = { 1 };
-
- public override uint DefaultVersion => 1;
-
- public override uint Version
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(200, 200),
+ new(48, 48),
+ new(200, 200),
+ new(48, 48),
+ new(200, 200),
+ new(48, 48),
+ new(200, 200),
+ new(48, 48),
+ new(200, 200),
+ new(48, 48),
+ };
+
+ public override uint[] AvailableVersions { get; } = { 1 };
+
+ public override uint DefaultVersion => 1;
+
+ public override uint Version
+ {
+ get => ManifestFile.FileVersion;
+ set
{
- get => ManifestFile.FileVersion;
- set
- {
- base.Version = value;
- ManifestFile.FileVersion = (byte)base.Version;
- }
+ base.Version = value;
+ ManifestFile.FileVersion = (byte)base.Version;
}
+ }
- public override uint ResolutionX
+ public override uint ResolutionX
+ {
+ get => ManifestFile.Machine.ResolutionX;
+ set
{
- get => ManifestFile.Machine.ResolutionX;
- set
- {
- ManifestFile.Machine.ResolutionX = (ushort) value;
- RaisePropertyChanged();
- }
+ ManifestFile.Machine.ResolutionX = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override uint ResolutionY
+ public override uint ResolutionY
+ {
+ get => ManifestFile.Machine.ResolutionY;
+ set
{
- get => ManifestFile.Machine.ResolutionY;
- set
- {
- ManifestFile.Machine.ResolutionY = (ushort) value;
- RaisePropertyChanged();
- }
+ ManifestFile.Machine.ResolutionY = (ushort) value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override float DisplayWidth
+ {
+ get => ManifestFile.Machine.DisplayWidth;
+ set
{
- get => ManifestFile.Machine.DisplayWidth;
- set
- {
- ManifestFile.Machine.DisplayWidth = (float) Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ ManifestFile.Machine.DisplayWidth = (float) Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
+ public override float DisplayHeight
+ {
+ get => ManifestFile.Machine.DisplayHeight;
+ set
{
- get => ManifestFile.Machine.DisplayHeight;
- set
- {
- ManifestFile.Machine.DisplayHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ ManifestFile.Machine.DisplayHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override float MachineZ
+ public override float MachineZ
+ {
+ get => ManifestFile.Machine.ZHeight > 0 ? ManifestFile.Machine.ZHeight : base.MachineZ;
+ set
{
- get => ManifestFile.Machine.ZHeight > 0 ? ManifestFile.Machine.ZHeight : base.MachineZ;
- set
- {
- ManifestFile.Machine.ZHeight = (float)Math.Round(value, 2);
- RaisePropertyChanged();
- }
+ ManifestFile.Machine.ZHeight = (float)Math.Round(value, 2);
+ RaisePropertyChanged();
}
+ }
- public override Enumerations.FlipDirection DisplayMirror
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get
{
- 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();
- }
+ 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;
}
-
- public override byte AntiAliasing
+ set
{
- get => ManifestFile.AdvancedParameters.AntialasingLevel;
- set => base.AntiAliasing = ManifestFile.AdvancedParameters.AntialasingLevel = value.Clamp(1, 16);
+ 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 float LayerHeight
+ public override byte AntiAliasing
+ {
+ get => ManifestFile.AdvancedParameters.AntialasingLevel;
+ set => base.AntiAliasing = ManifestFile.AdvancedParameters.AntialasingLevel = value.Clamp(1, 16);
+ }
+
+ public override float LayerHeight
+ {
+ get => ManifestFile.Print.LayerHeight;
+ set
{
- get => ManifestFile.Print.LayerHeight;
- set
- {
- ManifestFile.Print.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ ManifestFile.Print.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- /* public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = base.LayerCount;
- }*/
+ /* public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = base.LayerCount;
+ }*/
- public override ushort BottomLayerCount
- {
- get => ManifestFile.Print.BottomLayers;
- set => base.BottomLayerCount = ManifestFile.Print.BottomLayers = value;
- }
+ public override ushort BottomLayerCount
+ {
+ get => ManifestFile.Print.BottomLayers;
+ set => base.BottomLayerCount = ManifestFile.Print.BottomLayers = value;
+ }
- public override float BottomLightOffDelay
- {
- get => ManifestFile.Print.BottomLightOffDelay;
- set => base.BottomLightOffDelay = ManifestFile.Print.BottomLightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float BottomLightOffDelay
+ {
+ get => ManifestFile.Print.BottomLightOffDelay;
+ set => base.BottomLightOffDelay = ManifestFile.Print.BottomLightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float LightOffDelay
- {
- get => ManifestFile.Print.LightOffDelay;
- set => base.LightOffDelay = ManifestFile.Print.LightOffDelay = (float)Math.Round(value, 2);
- }
+ public override float LightOffDelay
+ {
+ get => ManifestFile.Print.LightOffDelay;
+ set => base.LightOffDelay = ManifestFile.Print.LightOffDelay = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeBeforeCure
- {
- get => ManifestFile.Print.BottomWaitTimeBeforeCure;
- set => base.BottomWaitTimeBeforeCure = ManifestFile.Print.BottomWaitTimeBeforeCure = (float)Math.Round(value, 2);
- }
- public override float WaitTimeBeforeCure
- {
- get => ManifestFile.Print.WaitTimeBeforeCure;
- set => base.WaitTimeBeforeCure = ManifestFile.Print.WaitTimeBeforeCure = (float)Math.Round(value, 2);
- }
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => ManifestFile.Print.BottomWaitTimeBeforeCure;
+ set => base.BottomWaitTimeBeforeCure = ManifestFile.Print.BottomWaitTimeBeforeCure = (float)Math.Round(value, 2);
+ }
+ public override float WaitTimeBeforeCure
+ {
+ get => ManifestFile.Print.WaitTimeBeforeCure;
+ set => base.WaitTimeBeforeCure = ManifestFile.Print.WaitTimeBeforeCure = (float)Math.Round(value, 2);
+ }
- public override float BottomExposureTime
- {
- get => ManifestFile.Print.BottomExposureTime;
- set => base.BottomExposureTime = ManifestFile.Print.BottomExposureTime = (float)Math.Round(value, 2);
- }
+ public override float BottomExposureTime
+ {
+ get => ManifestFile.Print.BottomExposureTime;
+ set => base.BottomExposureTime = ManifestFile.Print.BottomExposureTime = (float)Math.Round(value, 2);
+ }
- public override float ExposureTime
- {
- get => ManifestFile.Print.ExposureTime;
- set => base.ExposureTime = ManifestFile.Print.ExposureTime = (float)Math.Round(value, 2);
- }
+ public override float ExposureTime
+ {
+ get => ManifestFile.Print.ExposureTime;
+ set => base.ExposureTime = ManifestFile.Print.ExposureTime = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeAfterCure
- {
- get => ManifestFile.Print.BottomWaitTimeAfterCure;
- set => base.BottomWaitTimeAfterCure = ManifestFile.Print.BottomWaitTimeAfterCure = (float)Math.Round(value, 2);
- }
- public override float WaitTimeAfterCure
- {
- get => ManifestFile.Print.WaitTimeAfterCure;
- set => base.WaitTimeAfterCure = ManifestFile.Print.WaitTimeAfterCure = (float)Math.Round(value, 2);
- }
+ public override float BottomWaitTimeAfterCure
+ {
+ get => ManifestFile.Print.BottomWaitTimeAfterCure;
+ set => base.BottomWaitTimeAfterCure = ManifestFile.Print.BottomWaitTimeAfterCure = (float)Math.Round(value, 2);
+ }
+ public override float WaitTimeAfterCure
+ {
+ get => ManifestFile.Print.WaitTimeAfterCure;
+ set => base.WaitTimeAfterCure = ManifestFile.Print.WaitTimeAfterCure = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftHeight
- {
- get => ManifestFile.Print.BottomLiftHeight;
- set => base.BottomLiftHeight = ManifestFile.Print.BottomLiftHeight = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftHeight
+ {
+ get => ManifestFile.Print.BottomLiftHeight;
+ set => base.BottomLiftHeight = ManifestFile.Print.BottomLiftHeight = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftSpeed
- {
- get => ManifestFile.Print.BottomLiftSpeed;
- set => base.BottomLiftSpeed = ManifestFile.Print.BottomLiftSpeed = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftSpeed
+ {
+ get => ManifestFile.Print.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = ManifestFile.Print.BottomLiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float LiftHeight
- {
- get => ManifestFile.Print.LiftHeight;
- set => base.LiftHeight = ManifestFile.Print.LiftHeight = (float)Math.Round(value, 2);
- }
+ public override float LiftHeight
+ {
+ get => ManifestFile.Print.LiftHeight;
+ set => base.LiftHeight = ManifestFile.Print.LiftHeight = (float)Math.Round(value, 2);
+ }
- public override float LiftSpeed
- {
- get => ManifestFile.Print.LiftSpeed;
- set => base.LiftSpeed = ManifestFile.Print.LiftSpeed = (float)Math.Round(value, 2);
- }
+ public override float LiftSpeed
+ {
+ get => ManifestFile.Print.LiftSpeed;
+ set => base.LiftSpeed = ManifestFile.Print.LiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftHeight2
- {
- get => ManifestFile.Print.BottomLiftHeight2;
- set => base.BottomLiftHeight2 = ManifestFile.Print.BottomLiftHeight2 = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftHeight2
+ {
+ get => ManifestFile.Print.BottomLiftHeight2;
+ set => base.BottomLiftHeight2 = ManifestFile.Print.BottomLiftHeight2 = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftSpeed2
- {
- get => ManifestFile.Print.BottomLiftSpeed2;
- set => base.BottomLiftSpeed2 = ManifestFile.Print.BottomLiftSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftSpeed2
+ {
+ get => ManifestFile.Print.BottomLiftSpeed2;
+ set => base.BottomLiftSpeed2 = ManifestFile.Print.BottomLiftSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float LiftHeight2
- {
- get => ManifestFile.Print.LiftHeight2;
- set => base.LiftHeight2 = ManifestFile.Print.LiftHeight2 = (float)Math.Round(value, 2);
- }
+ public override float LiftHeight2
+ {
+ get => ManifestFile.Print.LiftHeight2;
+ set => base.LiftHeight2 = ManifestFile.Print.LiftHeight2 = (float)Math.Round(value, 2);
+ }
- public override float LiftSpeed2
- {
- get => ManifestFile.Print.LiftSpeed2;
- set => base.LiftSpeed2 = ManifestFile.Print.LiftSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float LiftSpeed2
+ {
+ get => ManifestFile.Print.LiftSpeed2;
+ set => base.LiftSpeed2 = ManifestFile.Print.LiftSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float BottomWaitTimeAfterLift
- {
- get => ManifestFile.Print.BottomWaitTimeAfterLift;
- set => base.BottomWaitTimeAfterLift = ManifestFile.Print.BottomWaitTimeAfterLift = (float)Math.Round(value, 2);
- }
- public override float WaitTimeAfterLift
- {
- get => ManifestFile.Print.WaitTimeAfterLift;
- set => base.WaitTimeAfterLift = ManifestFile.Print.WaitTimeAfterLift = (float)Math.Round(value, 2);
- }
+ public override float BottomWaitTimeAfterLift
+ {
+ get => ManifestFile.Print.BottomWaitTimeAfterLift;
+ set => base.BottomWaitTimeAfterLift = ManifestFile.Print.BottomWaitTimeAfterLift = (float)Math.Round(value, 2);
+ }
+ public override float WaitTimeAfterLift
+ {
+ get => ManifestFile.Print.WaitTimeAfterLift;
+ set => base.WaitTimeAfterLift = ManifestFile.Print.WaitTimeAfterLift = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractSpeed
- {
- get => ManifestFile.Print.BottomRetractSpeed;
- set => base.BottomRetractSpeed = ManifestFile.Print.BottomRetractSpeed = (float)Math.Round(value, 2);
- }
+ public override float BottomRetractSpeed
+ {
+ get => ManifestFile.Print.BottomRetractSpeed;
+ set => base.BottomRetractSpeed = ManifestFile.Print.BottomRetractSpeed = (float)Math.Round(value, 2);
+ }
- public override float RetractSpeed
- {
- get => ManifestFile.Print.RetractSpeed;
- set => base.RetractSpeed = ManifestFile.Print.RetractSpeed = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed
+ {
+ get => ManifestFile.Print.RetractSpeed;
+ set => base.RetractSpeed = ManifestFile.Print.RetractSpeed = (float)Math.Round(value, 2);
+ }
- public override float BottomRetractHeight2
+ public override float BottomRetractHeight2
+ {
+ get => ManifestFile.Print.BottomRetractHeight2;
+ set
{
- get => ManifestFile.Print.BottomRetractHeight2;
- set
- {
- value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal);
- base.BottomRetractHeight2 = ManifestFile.Print.BottomRetractHeight2 = value;
- }
+ value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal);
+ base.BottomRetractHeight2 = ManifestFile.Print.BottomRetractHeight2 = value;
}
+ }
- public override float BottomRetractSpeed2
- {
- get => ManifestFile.Print.BottomRetractSpeed2;
- set => base.BottomRetractSpeed2 = ManifestFile.Print.BottomRetractSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float BottomRetractSpeed2
+ {
+ get => ManifestFile.Print.BottomRetractSpeed2;
+ set => base.BottomRetractSpeed2 = ManifestFile.Print.BottomRetractSpeed2 = (float)Math.Round(value, 2);
+ }
- public override float RetractHeight2
+ public override float RetractHeight2
+ {
+ get => ManifestFile.Print.RetractHeight2;
+ set
{
- get => ManifestFile.Print.RetractHeight2;
- set
- {
- value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
- base.RetractHeight2 = ManifestFile.Print.RetractHeight2 = value;
- }
+ value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
+ base.RetractHeight2 = ManifestFile.Print.RetractHeight2 = value;
}
+ }
- public override float RetractSpeed2
- {
- get => ManifestFile.Print.RetractSpeed2;
- set => base.RetractSpeed2 = ManifestFile.Print.RetractSpeed2 = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed2
+ {
+ get => ManifestFile.Print.RetractSpeed2;
+ set => base.RetractSpeed2 = ManifestFile.Print.RetractSpeed2 = (float)Math.Round(value, 2);
+ }
- public override byte BottomLightPWM
- {
- get => ManifestFile.AdvancedParameters.BottomLightPWM;
- set => base.BottomLightPWM = ManifestFile.AdvancedParameters.BottomLightPWM = value;
- }
+ public override byte BottomLightPWM
+ {
+ get => ManifestFile.AdvancedParameters.BottomLightPWM;
+ set => base.BottomLightPWM = ManifestFile.AdvancedParameters.BottomLightPWM = value;
+ }
- public override byte LightPWM
- {
- get => ManifestFile.AdvancedParameters.LightPWM;
- set => base.LightPWM = ManifestFile.AdvancedParameters.LightPWM = value;
- }
+ public override byte LightPWM
+ {
+ get => ManifestFile.AdvancedParameters.LightPWM;
+ set => base.LightPWM = ManifestFile.AdvancedParameters.LightPWM = value;
+ }
- public override float PrintTime
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- ManifestFile.PrintStatistics.EstimatedTime = (uint)base.PrintTime;
- }
+ base.PrintTime = value;
+ ManifestFile.PrintStatistics.EstimatedTime = (uint)base.PrintTime;
}
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- ManifestFile.PrintStatistics.Volume = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ ManifestFile.PrintStatistics.Volume = base.MaterialMilliliters;
}
+ }
- public override float MaterialGrams
- {
- get => (float)Math.Round(ManifestFile.PrintStatistics.Weight, 3);
- set => base.MaterialGrams = ManifestFile.PrintStatistics.Weight = (float)Math.Round(value, 3);
- }
+ public override float MaterialGrams
+ {
+ get => (float)Math.Round(ManifestFile.PrintStatistics.Weight, 3);
+ set => base.MaterialGrams = ManifestFile.PrintStatistics.Weight = (float)Math.Round(value, 3);
+ }
+
+ public override string? MaterialName
+ {
+ get => ManifestFile.Resin.Name;
+ set => base.MaterialName = ManifestFile.Resin.Name = value;
+ }
+
+ public override float MaterialCost
+ {
+ get => (float)Math.Round(ManifestFile.Resin.Cost, 3);
+ set => base.MaterialCost = ManifestFile.Resin.Cost = (float)Math.Round(value, 3);
+ }
+
+ public override string MachineName
+ {
+ get => ManifestFile.Machine.Name;
+ set => base.MachineName = ManifestFile.Machine.Name = value;
+ }
+
+ public override object[] Configs => new[] {(object)ManifestFile, ManifestFile.Machine, ManifestFile.AdvancedParameters, ManifestFile.Resin, ManifestFile.Print, ManifestFile.PrintStatistics};
+ #endregion
- public override string MaterialName
- {
- get => ManifestFile.Resin.Name;
- set => base.MaterialName = ManifestFile.Resin.Name = value;
- }
+ #region Methods
- public override float MaterialCost
+ public void RebuildVDTLayers()
+ {
+ ManifestFile.CreateDateTime = DateTime.UtcNow.ToString("u");
+ ManifestFile.Layers = new VDTLayer[LayerCount];
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- get => (float)Math.Round(ManifestFile.Resin.Cost, 3);
- set => base.MaterialCost = ManifestFile.Resin.Cost = (float)Math.Round(value, 3);
+ var layer = this[layerIndex];
+ ManifestFile.Layers[layerIndex] = new VDTLayer
+ {
+ PositionZ = layer.PositionZ,
+ LightOffDelay = layer.LightOffDelay,
+ WaitTimeBeforeCure = layer.WaitTimeBeforeCure,
+ ExposureTime = layer.ExposureTime,
+ WaitTimeAfterCure = layer.WaitTimeAfterCure,
+ LiftHeight = layer.LiftHeight,
+ LiftSpeed = layer.LiftSpeed,
+ LiftHeight2 = layer.LiftHeight2,
+ LiftSpeed2 = layer.LiftSpeed2,
+ WaitTimeAfterLift = layer.WaitTimeAfterLift,
+ RetractSpeed = layer.RetractSpeed,
+ RetractHeight2 = layer.RetractHeight2,
+ RetractSpeed2 = layer.RetractSpeed2,
+ LightPWM = layer.LightPWM
+ };
}
+ }
- public override string MachineName
+ protected override bool OnBeforeConvertTo(FileFormat output)
+ {
+ int fileVersion = LookupCustomValue(SL1File.Keyword_FileVersion, int.MinValue);
+ if (fileVersion > 0)
{
- get => ManifestFile.Machine.Name;
- set => base.MachineName = ManifestFile.Machine.Name = value;
+ output.Version = (uint)fileVersion;
}
- public override object[] Configs => new[] {(object)ManifestFile, ManifestFile.Machine, ManifestFile.AdvancedParameters, ManifestFile.Resin, ManifestFile.Print, ManifestFile.PrintStatistics};
- #endregion
+ return true;
+ }
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ // Redo layer data
+ RebuildVDTLayers();
- #region Methods
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ outputFile.PutFileContent(FileManifestName, JsonSerializer.SerializeToUtf8Bytes(ManifestFile, JsonExtensions.SettingsIndent), ZipArchiveMode.Create);
- public void RebuildVDTLayers()
+ if (CreatedThumbnailsCount > 0)
{
- ManifestFile.CreateDateTime = DateTime.UtcNow.ToString("u");
- ManifestFile.Layers = new VDTLayer[LayerCount];
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ for (int i = 0; i < FilePreviewNames.Length; i++)
{
- var layer = this[layerIndex];
- ManifestFile.Layers[layerIndex] = new VDTLayer
- {
- PositionZ = layer.PositionZ,
- LightOffDelay = layer.LightOffDelay,
- WaitTimeBeforeCure = layer.WaitTimeBeforeCure,
- ExposureTime = layer.ExposureTime,
- WaitTimeAfterCure = layer.WaitTimeAfterCure,
- LiftHeight = layer.LiftHeight,
- LiftSpeed = layer.LiftSpeed,
- LiftHeight2 = layer.LiftHeight2,
- LiftSpeed2 = layer.LiftSpeed2,
- WaitTimeAfterLift = layer.WaitTimeAfterLift,
- RetractSpeed = layer.RetractSpeed,
- RetractHeight2 = layer.RetractHeight2,
- RetractSpeed2 = layer.RetractSpeed2,
- LightPWM = layer.LightPWM
- };
+ if(Thumbnails.Length <= i) break;
+ if(Thumbnails[i] is null) continue;
+
+ using var stream = outputFile.CreateEntry(FilePreviewNames[i]).Open();
+ stream.WriteBytes(Thumbnails[i]!.GetPngByes());
+ stream.Close();
}
}
- protected override bool OnBeforeConvertTo(FileFormat output)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- int fileVersion = LookupCustomValue(SL1File.Keyword_FileVersion, int.MinValue);
- if (fileVersion > 0)
- {
- output.Version = (uint)fileVersion;
- }
+ progress.Token.ThrowIfCancellationRequested();
- return true;
+ var layer = this[layerIndex];
+ var layerImagePath = $"{layerIndex}.png";
+ outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
}
+ }
- protected override void EncodeInternally(OperationProgress progress)
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using (var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read))
{
- // Redo layer data
- RebuildVDTLayers();
+ var entry = inputFile.GetEntry(FileManifestName);
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException($"{FileManifestName} not found", FileFullPath);
+ }
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- outputFile.PutFileContent(FileManifestName, JsonConvert.SerializeObject(ManifestFile, Formatting.Indented), ZipArchiveMode.Create);
+ ManifestFile = JsonSerializer.Deserialize<VDTManifest>(entry.Open())!;
+
+ Init((uint) ManifestFile.Layers!.Length, DecodeType == FileDecodeType.Partial);
- if (CreatedThumbnailsCount > 0)
+ for (int i = 0; i < FilePreviewNames.Length; i++)
{
- for (int i = 0; i < FilePreviewNames.Length; i++)
- {
- if(Thumbnails.Length <= i) break;
- if(Thumbnails[i] is null) continue;
-
- using var stream = outputFile.CreateEntry(FilePreviewNames[i]).Open();
- stream.WriteBytes(Thumbnails[i].GetPngByes());
- stream.Close();
- }
+ if (Thumbnails.Length <= i) break;
+
+ entry = inputFile.GetEntry(FilePreviewNames[i]);
+ if (entry is null) continue;
+ using var stream = entry.Open();
+ Mat image = new();
+ CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
+ Thumbnails[i] = image;
+ stream.Close();
}
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- progress.Token.ThrowIfCancellationRequested();
+ var manifestLayer = ManifestFile.Layers[layerIndex];
+ entry = inputFile.GetEntry($"{layerIndex}.png");
+ if (entry is null) continue;
- var layer = this[layerIndex];
- var layerImagePath = $"{layerIndex}.png";
- outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create);
- progress++;
- }
- }
-
- protected override void DecodeInternally(OperationProgress progress)
- {
- using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
- {
- var entry = inputFile.GetEntry(FileManifestName);
- if (entry is null)
+ if (DecodeType == FileDecodeType.Full)
{
- Clear();
- throw new FileLoadException($"{FileManifestName} not found", FileFullPath);
+ using var stream = entry.Open();
+ this[layerIndex] = new Layer(layerIndex, stream, this);
}
- ManifestFile = Helpers.JsonDeserializeObject<VDTManifest>(entry.Open());
+ manifestLayer.CopyTo(this[layerIndex]);
+ }
- LayerManager.Init((uint) ManifestFile.Layers.Length, DecodeType == FileDecodeType.Partial);
-
- for (int i = 0; i < FilePreviewNames.Length; i++)
- {
- if (Thumbnails.Length <= i) break;
-
- entry = inputFile.GetEntry(FilePreviewNames[i]);
- if (entry is null) continue;
- using var stream = entry.Open();
- Mat image = new();
- CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image);
- Thumbnails[i] = image;
- stream.Close();
- }
+ progress.ProcessedItems++;
+ }
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- var manifestLayer = ManifestFile.Layers[layerIndex];
- entry = inputFile.GetEntry($"{layerIndex}.png");
- if (entry is null) continue;
+ GetBoundingRectangle(progress);
+ }
- if (DecodeType == FileDecodeType.Full)
- {
- using var stream = entry.Open();
- this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
- }
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ RebuildVDTLayers();
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ outputFile.PutFileContent(FileManifestName, JsonSerializer.SerializeToUtf8Bytes(ManifestFile, JsonExtensions.SettingsIndent), ZipArchiveMode.Update);
- manifestLayer.CopyTo(this[layerIndex]);
- }
-
- progress.ProcessedItems++;
- }
+ //Decode(FileFullPath, progress);
+ }
- LayerManager.GetBoundingRectangle(progress);
- }
+ public T? LookupCustomValue<T>(string name, T? defaultValue, bool existsOnly = false)
+ {
+ //if (string.IsNullOrEmpty(PrinterSettings.PrinterNotes)) return defaultValue;
+ var result = string.Empty;
+ if (!existsOnly) name += '_';
- protected override void PartialSaveInternally(OperationProgress progress)
+ foreach (var notes in new[] { ManifestFile.Machine.Notes })
{
- RebuildVDTLayers();
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- outputFile.PutFileContent(FileManifestName, JsonConvert.SerializeObject(ManifestFile, Formatting.Indented), ZipArchiveMode.Update);
-
- //Decode(FileFullPath, progress);
- }
+ if (string.IsNullOrWhiteSpace(notes)) continue;
- public T LookupCustomValue<T>(string name, T defaultValue, bool existsOnly = false)
- {
- //if (string.IsNullOrEmpty(PrinterSettings.PrinterNotes)) return defaultValue;
- var result = string.Empty;
- if (!existsOnly) name += '_';
+ var lines = notes.Split(new[] { "\\r\\n", "\\r", "\\n" }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- foreach (var notes in new[] { ManifestFile.Machine.Notes })
+ foreach (var line in lines)
{
- if (string.IsNullOrWhiteSpace(notes)) continue;
-
- var lines = notes.Split(new[] { "\\r\\n", "\\r", "\\n" }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
-
- foreach (var line in lines)
+ if (!line.StartsWith(name)) continue;
+ if (existsOnly || line == name) return "true".Convert<T>();
+ var value = line.Remove(0, name.Length);
+ foreach (var c in value)
{
- if (!line.StartsWith(name)) continue;
- if (existsOnly || line == name) return "true".Convert<T>();
- var value = line.Remove(0, name.Length);
- foreach (var c in value)
+ if (typeof(T) == typeof(string))
{
- if (typeof(T) == typeof(string))
- {
- if (char.IsWhiteSpace(c)) break;
- }
- else
+ if (char.IsWhiteSpace(c)) break;
+ }
+ else
+ {
+ if (!char.IsLetterOrDigit(c) && c != '.')
{
- if (!char.IsLetterOrDigit(c) && c != '.')
- {
- break;
- }
+ break;
}
+ }
- result += c;
- }
+ result += c;
}
-
- if (!string.IsNullOrEmpty(result)) break; // Found a candidate
}
- return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>();
+ if (!string.IsNullOrEmpty(result)) break; // Found a candidate
}
- #endregion
+
+ return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>();
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs
index e1d5ee7..e0984fe 100644
--- a/UVtools.Core/FileFormats/ZCodeFile.cs
+++ b/UVtools.Core/FileFormats/ZCodeFile.cs
@@ -6,6 +6,11 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.OpenSsl;
using System;
using System.Collections.Generic;
using System.Drawing;
@@ -14,644 +19,632 @@ using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Engines;
-using Org.BouncyCastle.OpenSsl;
using UVtools.Core.Extensions;
using UVtools.Core.GCode;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
+namespace UVtools.Core.FileFormats;
+
+[Serializable]
+[XmlRoot(ElementName = "Print")]
+public class ZCodePrint
{
[Serializable]
- [XmlRoot(ElementName = "Print")]
- public class ZCodePrint
+ [XmlRoot(ElementName = "Device")]
+ public class ZcodePrintDevice
{
- [Serializable]
- [XmlRoot(ElementName = "Device")]
- public class ZcodePrintDevice
- {
- [XmlAttribute("z")]
- public float MachineZ { get; set; } = 220;
+ [XmlAttribute("z")]
+ public float MachineZ { get; set; } = 220;
- [XmlAttribute("height")]
- public uint ResolutionY { get; set; } = 2400;
+ [XmlAttribute("height")]
+ public uint ResolutionY { get; set; } = 2400;
- [XmlAttribute("width")]
- public uint ResolutionX { get; set; } = 3840;
+ [XmlAttribute("width")]
+ public uint ResolutionX { get; set; } = 3840;
- [XmlAttribute("type")]
- public string MachineModel { get; set; } = "IBEE";
+ [XmlAttribute("type")]
+ public string MachineModel { get; set; } = "IBEE";
- [XmlAttribute("pixel_size")]
- public float PixelSize { get; set; } = 50;
+ [XmlAttribute("pixel_size")]
+ public float PixelSize { get; set; } = 50;
- }
+ }
+
+ [Serializable]
+ [XmlRoot(ElementName = "Profile")]
+ public class ZcodePrintProfile
+ {
+ [XmlAttribute("name")]
+ public string Name { get; set; } = "UVtools";
[Serializable]
- [XmlRoot(ElementName = "Profile")]
- public class ZcodePrintProfile
+ [XmlRoot(ElementName = "Slice")]
+ public class ZcodePrintProfileSlice
{
- [XmlAttribute("name")]
- public string Name { get; set; } = "UVtools";
+ [XmlAttribute("bottom_layers")]
+ public ushort BottomLayerCount { get; set; } = FileFormat.DefaultBottomLayerCount;
- [Serializable]
- [XmlRoot(ElementName = "Slice")]
- public class ZcodePrintProfileSlice
- {
- [XmlAttribute("bottom_layers")]
- public ushort BottomLayerCount { get; set; } = FileFormat.DefaultBottomLayerCount;
+ [XmlAttribute("exposure_bottom")]
+ public uint BottomExposureTime { get; set; } = (uint) (FileFormat.DefaultBottomExposureTime * 1000);
- [XmlAttribute("exposure_bottom")]
- public uint BottomExposureTime { get; set; } = (uint) (FileFormat.DefaultBottomExposureTime * 1000);
+ [XmlAttribute("exposure")]
+ public uint ExposureTime { get; set; } = (uint) (FileFormat.DefaultExposureTime * 1000);
- [XmlAttribute("exposure")]
- public uint ExposureTime { get; set; } = (uint) (FileFormat.DefaultExposureTime * 1000);
+ [XmlAttribute("height_bottom")]
+ public float BottomLiftHeight { get; set; } = FileFormat.DefaultBottomLiftHeight;
- [XmlAttribute("height_bottom")]
- public float BottomLiftHeight { get; set; } = FileFormat.DefaultBottomLiftHeight;
+ [XmlAttribute("speed_bottom")]
+ public float BottomLiftSpeed { get; set; } = FileFormat.DefaultBottomLiftSpeed;
- [XmlAttribute("speed_bottom")]
- public float BottomLiftSpeed { get; set; } = FileFormat.DefaultBottomLiftSpeed;
-
- [XmlAttribute("height")]
- public float LiftHeight { get; set; } = FileFormat.DefaultLiftHeight;
-
- [XmlAttribute("speed")]
- public float LiftSpeed { get; set; } = FileFormat.DefaultLiftSpeed;
+ [XmlAttribute("height")]
+ public float LiftHeight { get; set; } = FileFormat.DefaultLiftHeight;
- [XmlAttribute("cooldown_bottom")]
- public uint BottomLightOffDelay { get; set; }
+ [XmlAttribute("speed")]
+ public float LiftSpeed { get; set; } = FileFormat.DefaultLiftSpeed;
- [XmlAttribute("cooldown")]
- public uint LightOffDelay { get; set; }
+ [XmlAttribute("cooldown_bottom")]
+ public uint BottomLightOffDelay { get; set; }
- [XmlAttribute("thickness")]
- public float LayerHeight { get; set; } = FileFormat.DefaultLayerHeight;
+ [XmlAttribute("cooldown")]
+ public uint LightOffDelay { get; set; }
- [XmlAttribute("anti_aliasing_level")]
- public byte AntiAliasing { get; set; }
+ [XmlAttribute("thickness")]
+ public float LayerHeight { get; set; } = FileFormat.DefaultLayerHeight;
- [XmlAttribute("anti_aliasing_grey_level")]
- public byte AntiAliasingGrey { get; set; }
+ [XmlAttribute("anti_aliasing_level")]
+ public byte AntiAliasing { get; set; }
- [XmlAttribute("led_power")]
- public ushort LedPower { get; set; } = ZCodeFile.MaxLEDPower;
- }
+ [XmlAttribute("anti_aliasing_grey_level")]
+ public byte AntiAliasingGrey { get; set; }
- public ZcodePrintProfileSlice Slice { get; set; } = new();
+ [XmlAttribute("led_power")]
+ public ushort LedPower { get; set; } = ZCodeFile.MaxLEDPower;
}
- [Serializable]
- [XmlRoot(ElementName = "Job")]
- public class ZcodePrintJob
- {
- [XmlElement("name")]
- public string StlName { get; set; }
+ public ZcodePrintProfileSlice Slice { get; set; } = new();
+ }
- [XmlElement("previewImage")]
- public string PreviewImage { get; set; } = ZCodeFile.PreviewFilename;
+ [Serializable]
+ [XmlRoot(ElementName = "Job")]
+ public class ZcodePrintJob
+ {
+ [XmlElement("name")]
+ public string? StlName { get; set; }
- [XmlElement("layers")]
- public uint LayerCount { get; set; }
+ [XmlElement("previewImage")]
+ public string PreviewImage { get; set; } = ZCodeFile.PreviewFilename;
- [XmlElement("time")]
- public uint PrintTime { get; set; }
+ [XmlElement("layers")]
+ public uint LayerCount { get; set; }
- [XmlElement("volumn")]
- public float VolumeMl { get; set; }
+ [XmlElement("time")]
+ public uint PrintTime { get; set; }
- [XmlElement("thickness")]
- public float LayerHeight { get; set; } = FileFormat.DefaultLayerHeight;
+ [XmlElement("volumn")]
+ public float VolumeMl { get; set; }
- [XmlElement("price")]
- public float Price { get; set; }
+ [XmlElement("thickness")]
+ public float LayerHeight { get; set; } = FileFormat.DefaultLayerHeight;
- [XmlElement("weight")]
- public float WeightG { get; set; }
- }
+ [XmlElement("price")]
+ public float Price { get; set; }
- public ZcodePrintDevice Device = new();
+ [XmlElement("weight")]
+ public float WeightG { get; set; }
+ }
- public ZcodePrintProfile Profile = new();
+ public ZcodePrintDevice Device = new();
- public ZcodePrintJob Job = new();
+ public ZcodePrintProfile Profile = new();
- }
- public class ZCodeFile : FileFormat
- {
- #region Constants
+ public ZcodePrintJob Job = new();
- public const string GCodeFilename = "lcd.gcode";
- public const string ManifestFilename = "task.xml";
- public const string PreviewFilename = "preview.png";
- public const ushort MaxLEDPower = 300;
+}
+public class ZCodeFile : FileFormat
+{
+ #region Constants
- public const string GCodeStart = "G21;{0}" + // Set units to be mm
- "G90;{0}" + // Absolute Positioning
- "M106 S0;{0}" + // Light Off
- "G28 Z0;{0}"; // Home
+ public const string GCodeFilename = "lcd.gcode";
+ public const string ManifestFilename = "task.xml";
+ public const string PreviewFilename = "preview.png";
+ public const ushort MaxLEDPower = 300;
- public const string GCodeEnd = "M106 S0{0}" + // Light Off
- "G1 Z{1} F10;{0}" + // Raize Z
- "M18;{0}"; // Disable Motors
+ public const string GCodeStart = "G21;{0}" + // Set units to be mm
+ "G90;{0}" + // Absolute Positioning
+ "M106 S0;{0}" + // Light Off
+ "G28 Z0;{0}"; // Home
+ public const string GCodeEnd = "M106 S0{0}" + // Light Off
+ "G1 Z{1} F10;{0}" + // Raize Z
+ "M18;{0}"; // Disable Motors
- public const string Secret1 = "eHtZQkIuNhIfOk8/PjoDFyAqTyc2DHtZQkJBeRgfPS05PToXFzAuIS4UPiccBAYrSiJmNi4+KTUUFycsLjhLIjETKlgtFBAXNQQqLUUXODJcADhKFCZfIBY2NSYmRF01PgcBJhYiOi1KYwYEDBs4KzAgRQw/LxA5GgEAGDQkGzdHCV0mMSUGFj8dFj4iGhUCXCYgBBhHJwUaKjMFIhdYCUUiHzAuPi0xFD02Bg0vPS8HWz8sCjIBPgwXLSA9MA45Ah8OPSYUMCtXHhAPHxAOHg0WIhBfOgU5HyQfQwomXColZCJdKhZBbRAeJCwpGhhlQCsXOUoFDCAVNj9AJxUnBy4FNhRvBR9WRyEiDwIMGT0mDTogXQ0dOBw5RxwXGUQZBzIiWzo6MTIlXjwhJT0lNyY+KARhP0NbIx01Z25BJhVbRCMhRSI3KUNjGjwkJC4xFzdHLgYeQgQYNgcBDyIcMS0JIFgnGT1MAwkDFiI5AgQAOgovBTZEKUM2AhgeGCxVOBghOTctOgoSBQcsJj03PCUMGgsjBwN9DVwsXz8THhkjXAc6YzoMFx0fCwEcLCIoATIjMD42CB06BB8cLiQuBy8PETUtWEUQAhsrPBwWDDoGIj4FBwYBAgUIIjQ4IV9XDi5cHQ8xJh1mXnh7WUIqIjd1BiYmOS0nEHY/KjZBXnh7WQ==";
- public const string Secret2 = "eHtZQkIuNhIfOk8/OTEZHzdPJCkqeHtZQkJmPhMhAys+NTkeOS4mBxoQGxclKi0uIhQSJxguGyAUHDYuIAspLTJCKkA9ODM8BwI9DjgxGBk6DTlFAiwyLj8JGWMOODpeXwFsDjAYASYgYic5KV4GJCFlTQY+DSdnLEJXFSEwZyYAFjoHNzEuQFhdJEM5NRFcGh8wFCExLi49TmhcWUJCQV4QGDBPPzkxGR83TyQpKnh7WUJC";
- public static readonly string GCodeRSAPrivateKey = CryptExtensions.XORCipherString(CryptExtensions.Base64DecodeString(Secret1), About.Software);
- public static readonly string GCodeRSAPublicKey = CryptExtensions.XORCipherString(CryptExtensions.Base64DecodeString(Secret2), About.Software);
- #endregion
+ public const string Secret1 = "eHtZQkIuNhIfOk8/PjoDFyAqTyc2DHtZQkJBeRgfPS05PToXFzAuIS4UPiccBAYrSiJmNi4+KTUUFycsLjhLIjETKlgtFBAXNQQqLUUXODJcADhKFCZfIBY2NSYmRF01PgcBJhYiOi1KYwYEDBs4KzAgRQw/LxA5GgEAGDQkGzdHCV0mMSUGFj8dFj4iGhUCXCYgBBhHJwUaKjMFIhdYCUUiHzAuPi0xFD02Bg0vPS8HWz8sCjIBPgwXLSA9MA45Ah8OPSYUMCtXHhAPHxAOHg0WIhBfOgU5HyQfQwomXColZCJdKhZBbRAeJCwpGhhlQCsXOUoFDCAVNj9AJxUnBy4FNhRvBR9WRyEiDwIMGT0mDTogXQ0dOBw5RxwXGUQZBzIiWzo6MTIlXjwhJT0lNyY+KARhP0NbIx01Z25BJhVbRCMhRSI3KUNjGjwkJC4xFzdHLgYeQgQYNgcBDyIcMS0JIFgnGT1MAwkDFiI5AgQAOgovBTZEKUM2AhgeGCxVOBghOTctOgoSBQcsJj03PCUMGgsjBwN9DVwsXz8THhkjXAc6YzoMFx0fCwEcLCIoATIjMD42CB06BB8cLiQuBy8PETUtWEUQAhsrPBwWDDoGIj4FBwYBAgUIIjQ4IV9XDi5cHQ8xJh1mXnh7WUIqIjd1BiYmOS0nEHY/KjZBXnh7WQ==";
+ public const string Secret2 = "eHtZQkIuNhIfOk8/OTEZHzdPJCkqeHtZQkJmPhMhAys+NTkeOS4mBxoQGxclKi0uIhQSJxguGyAUHDYuIAspLTJCKkA9ODM8BwI9DjgxGBk6DTlFAiwyLj8JGWMOODpeXwFsDjAYASYgYic5KV4GJCFlTQY+DSdnLEJXFSEwZyYAFjoHNzEuQFhdJEM5NRFcGh8wFCExLi49TmhcWUJCQV4QGDBPPzkxGR83TyQpKnh7WUJC";
+ public static readonly string GCodeRSAPrivateKey = CryptExtensions.XORCipherString(CryptExtensions.Base64DecodeString(Secret1), About.Software);
+ public static readonly string GCodeRSAPublicKey = CryptExtensions.XORCipherString(CryptExtensions.Base64DecodeString(Secret2), About.Software);
- #region Sub Classes
+ #endregion
- #endregion
+ #region Sub Classes
- #region Properties
- public ZCodePrint ManifestFile { get; set; } = new ();
+ #endregion
- public override FileFormatType FileType => FileFormatType.Archive;
+ #region Properties
+ public ZCodePrint ManifestFile { get; set; } = new ();
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(ZCodeFile), "zcode", "UnizMaker IBEE (ZCode)")
- };
+ public override FileFormatType FileType => FileFormatType.Archive;
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(ZCodeFile), "zcode", "UnizMaker IBEE (ZCode)")
+ };
- PrintParameterModifier.BottomWaitTimeBeforeCure,
- PrintParameterModifier.WaitTimeBeforeCure,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomWaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.BottomWaitTimeAfterCure,
- PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.BottomLiftHeight,
- PrintParameterModifier.BottomLiftSpeed,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.BottomWaitTimeAfterCure,
+ PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.BottomLiftHeight2,
- PrintParameterModifier.BottomLiftSpeed2,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.BottomWaitTimeAfterLift,
- PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.BottomLiftHeight2,
+ PrintParameterModifier.BottomLiftSpeed2,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.BottomWaitTimeAfterLift,
+ PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.BottomRetractHeight2,
- PrintParameterModifier.BottomRetractSpeed2,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.BottomLightPWM,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomRetractHeight2,
+ PrintParameterModifier.BottomRetractSpeed2,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.ExposureTime,
- PrintParameterModifier.WaitTimeAfterCure,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.LiftHeight2,
- PrintParameterModifier.LiftSpeed2,
- PrintParameterModifier.WaitTimeAfterLift,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.RetractHeight2,
- PrintParameterModifier.RetractSpeed2,
- PrintParameterModifier.LightPWM,
- };
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,
+ };
- public override Size[] ThumbnailsOriginalSize { get; } = {new(640, 480)};
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.WaitTimeAfterCure,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.LiftHeight2,
+ PrintParameterModifier.LiftSpeed2,
+ PrintParameterModifier.WaitTimeAfterLift,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.RetractHeight2,
+ PrintParameterModifier.RetractSpeed2,
+ PrintParameterModifier.LightPWM,
+ };
- public override uint ResolutionX
- {
- get => ManifestFile.Device.ResolutionX;
- set
- {
- ManifestFile.Device.ResolutionX = value;
- RaisePropertyChanged();
- }
- }
+ public override Size[]? ThumbnailsOriginalSize { get; } = {new(640, 480)};
- public override uint ResolutionY
+ public override uint ResolutionX
+ {
+ get => ManifestFile.Device.ResolutionX;
+ set
{
- get => ManifestFile.Device.ResolutionY;
- set
- {
- ManifestFile.Device.ResolutionY = value;
- RaisePropertyChanged();
- }
+ ManifestFile.Device.ResolutionX = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayWidth
+ public override uint ResolutionY
+ {
+ get => ManifestFile.Device.ResolutionY;
+ set
{
- get => (float) Math.Round(ManifestFile.Device.ResolutionX * ManifestFile.Device.PixelSize / 1000, 2);
- set => RaisePropertyChanged();
+ ManifestFile.Device.ResolutionY = value;
+ RaisePropertyChanged();
}
+ }
- public override float DisplayHeight
- {
- get => (float)Math.Round(ManifestFile.Device.ResolutionY * ManifestFile.Device.PixelSize / 1000, 2);
- set => RaisePropertyChanged();
- }
+ public override float DisplayWidth
+ {
+ get => (float) Math.Round(ManifestFile.Device.ResolutionX * ManifestFile.Device.PixelSize / 1000, 2);
+ set => RaisePropertyChanged();
+ }
- public override float MachineZ
+ public override float DisplayHeight
+ {
+ get => (float)Math.Round(ManifestFile.Device.ResolutionY * ManifestFile.Device.PixelSize / 1000, 2);
+ set => RaisePropertyChanged();
+ }
+
+ public override float MachineZ
+ {
+ get => ManifestFile.Device.MachineZ > 0 ? ManifestFile.Device.MachineZ : base.MachineZ;
+ set
{
- get => ManifestFile.Device.MachineZ > 0 ? ManifestFile.Device.MachineZ : base.MachineZ;
- set
- {
- ManifestFile.Device.MachineZ = value;
- RaisePropertyChanged();
- }
+ ManifestFile.Device.MachineZ = value;
+ RaisePropertyChanged();
}
+ }
- public override Enumerations.FlipDirection DisplayMirror => Enumerations.FlipDirection.Vertically;
+ public override Enumerations.FlipDirection DisplayMirror => Enumerations.FlipDirection.Vertically;
- public override byte AntiAliasing
- {
- get => ManifestFile.Profile.Slice.AntiAliasing;
- set => base.AntiAliasing = ManifestFile.Profile.Slice.AntiAliasing = value.Clamp(1, 16);
- }
+ public override byte AntiAliasing
+ {
+ get => ManifestFile.Profile.Slice.AntiAliasing;
+ set => base.AntiAliasing = ManifestFile.Profile.Slice.AntiAliasing = value.Clamp(1, 16);
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => ManifestFile.Job.LayerHeight > 0 ? ManifestFile.Job.LayerHeight : ManifestFile.Profile.Slice.LayerHeight;
+ set
{
- get => ManifestFile.Job.LayerHeight > 0 ? ManifestFile.Job.LayerHeight : ManifestFile.Profile.Slice.LayerHeight;
- set
- {
- ManifestFile.Job.LayerHeight = ManifestFile.Profile.Slice.LayerHeight = Layer.RoundHeight(value);
- RaisePropertyChanged();
- }
+ ManifestFile.Job.LayerHeight = ManifestFile.Profile.Slice.LayerHeight = Layer.RoundHeight(value);
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
- {
- get => base.LayerCount;
- set => base.LayerCount = ManifestFile.Job.LayerCount = base.LayerCount;
- }
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = ManifestFile.Job.LayerCount = base.LayerCount;
+ }
- public override ushort BottomLayerCount
- {
- get => ManifestFile.Profile.Slice.BottomLayerCount;
- set => base.BottomLayerCount = ManifestFile.Profile.Slice.BottomLayerCount = value;
- }
+ public override ushort BottomLayerCount
+ {
+ get => ManifestFile.Profile.Slice.BottomLayerCount;
+ set => base.BottomLayerCount = ManifestFile.Profile.Slice.BottomLayerCount = value;
+ }
- public override float BottomLightOffDelay
- {
- get => BottomWaitTimeBeforeCure;
- set => BottomWaitTimeBeforeCure = value;
- }
+ public override float BottomLightOffDelay
+ {
+ get => BottomWaitTimeBeforeCure;
+ set => BottomWaitTimeBeforeCure = value;
+ }
- public override float LightOffDelay
- {
- get => WaitTimeBeforeCure;
- set => WaitTimeBeforeCure = value;
- }
+ public override float LightOffDelay
+ {
+ get => WaitTimeBeforeCure;
+ set => WaitTimeBeforeCure = value;
+ }
- public override float BottomWaitTimeBeforeCure
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.BottomLightOffDelay);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.BottomLightOffDelay);
- set
- {
- ManifestFile.Profile.Slice.BottomLightOffDelay = TimeExtensions.SecondsToMillisecondsUint(value);
- base.BottomWaitTimeBeforeCure = base.BottomLightOffDelay = value;
- }
+ ManifestFile.Profile.Slice.BottomLightOffDelay = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.BottomWaitTimeBeforeCure = base.BottomLightOffDelay = value;
}
+ }
- public override float WaitTimeBeforeCure
+ public override float WaitTimeBeforeCure
+ {
+ get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.LightOffDelay);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.LightOffDelay);
- set
- {
- ManifestFile.Profile.Slice.LightOffDelay = TimeExtensions.SecondsToMillisecondsUint(value);
- base.WaitTimeBeforeCure = base.LightOffDelay = value;
- }
+ ManifestFile.Profile.Slice.LightOffDelay = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.WaitTimeBeforeCure = base.LightOffDelay = value;
}
+ }
- public override float BottomExposureTime
+ public override float BottomExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.BottomExposureTime);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.BottomExposureTime);
- set
- {
- ManifestFile.Profile.Slice.BottomExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
- base.BottomExposureTime = value;
- }
+ ManifestFile.Profile.Slice.BottomExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.BottomExposureTime = value;
}
+ }
- public override float ExposureTime
+ public override float ExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.ExposureTime);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(ManifestFile.Profile.Slice.ExposureTime);
- set
- {
- ManifestFile.Profile.Slice.ExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
- base.ExposureTime = value;
- }
+ ManifestFile.Profile.Slice.ExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.ExposureTime = value;
}
+ }
- public override float BottomLiftHeight
- {
- get => ManifestFile.Profile.Slice.BottomLiftHeight;
- set => base.BottomLiftHeight = ManifestFile.Profile.Slice.BottomLiftHeight = (float)Math.Round(value, 2);
- }
+ public override float BottomLiftHeight
+ {
+ get => ManifestFile.Profile.Slice.BottomLiftHeight;
+ set => base.BottomLiftHeight = ManifestFile.Profile.Slice.BottomLiftHeight = (float)Math.Round(value, 2);
+ }
+
+ public override float LiftHeight
+ {
+ get => ManifestFile.Profile.Slice.LiftHeight;
+ set => base.LiftHeight = ManifestFile.Profile.Slice.LiftHeight = (float)Math.Round(value, 2);
+ }
+
+ public override float BottomLiftSpeed
+ {
+ get => ManifestFile.Profile.Slice.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = ManifestFile.Profile.Slice.BottomLiftSpeed = (float)Math.Round(value, 2);
+ }
- public override float LiftHeight
+ public override float LiftSpeed
+ {
+ get => ManifestFile.Profile.Slice.LiftSpeed;
+ set => base.LiftSpeed = ManifestFile.Profile.Slice.LiftSpeed = (float)Math.Round(value, 2);
+ }
+
+ public override byte LightPWM
+ {
+ get => (byte)(byte.MaxValue * ManifestFile.Profile.Slice.LedPower / MaxLEDPower);
+ set
{
- get => ManifestFile.Profile.Slice.LiftHeight;
- set => base.LiftHeight = ManifestFile.Profile.Slice.LiftHeight = (float)Math.Round(value, 2);
+ ManifestFile.Profile.Slice.LedPower = (ushort)(MaxLEDPower * value / byte.MaxValue);
+ base.LightPWM = value;
+ RaisePropertyChanged(nameof(BottomLightPWM));
}
+ }
- public override float BottomLiftSpeed
+ public override float PrintTime
+ {
+ get => base.PrintTime;
+ set
{
- get => ManifestFile.Profile.Slice.BottomLiftSpeed;
- set => base.BottomLiftSpeed = ManifestFile.Profile.Slice.BottomLiftSpeed = (float)Math.Round(value, 2);
+ base.PrintTime = value;
+ ManifestFile.Job.PrintTime = (uint) base.PrintTime;
}
+ }
- public override float LiftSpeed
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => ManifestFile.Profile.Slice.LiftSpeed;
- set => base.LiftSpeed = ManifestFile.Profile.Slice.LiftSpeed = (float)Math.Round(value, 2);
+ base.MaterialMilliliters = value;
+ ManifestFile.Job.VolumeMl = base.MaterialMilliliters;
}
+ }
- public override byte LightPWM
+ public override float MaterialGrams
+ {
+ get => ManifestFile.Job.WeightG;
+ set => base.MaterialGrams = ManifestFile.Job.WeightG = (float)Math.Round(value, 3);
+ }
+
+ public override float MaterialCost
+ {
+ get => ManifestFile.Job.Price;
+ set => base.MaterialCost = ManifestFile.Job.Price = (float)Math.Round(value, 3);
+ }
+
+ /*public override string MaterialName
+ {
+ get => HeaderSettings.Resin;
+ set
{
- get => (byte)(byte.MaxValue * ManifestFile.Profile.Slice.LedPower / MaxLEDPower);
- set
- {
- ManifestFile.Profile.Slice.LedPower = (ushort)(MaxLEDPower * value / byte.MaxValue);
- base.LightPWM = value;
- RaisePropertyChanged(nameof(BottomLightPWM));
- }
+ HeaderSettings.Resin = value;
+ RaisePropertyChanged();
}
+ }*/
+
+ public override string MachineName
+ {
+ get => ManifestFile.Device.MachineModel;
+ set => base.MachineName = ManifestFile.Device.MachineModel = value;
+ }
+
+ public override object[] Configs => new object[] { ManifestFile.Device, ManifestFile.Job, ManifestFile.Profile.Slice };
+
+ #endregion
- public override float PrintTime
+ #region Constructor
+ public ZCodeFile()
+ {
+ GCode = new GCodeBuilder
+ {
+ UseTailComma = true,
+ UseComments = false,
+ GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute,
+ GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.CentimetersPerMinute,
+ GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
+ GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenamePng1Started,
+ LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
+ EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1,
+ MaxLEDPower = MaxLEDPower,
+ CommandClearImage = {Enabled = false},
+ };
+ }
+ #endregion
+
+ #region Methods
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ if (Thumbnails.Length > 0 && Thumbnails[0] is not null)
{
- get => base.PrintTime;
- set
- {
- base.PrintTime = value;
- ManifestFile.Job.PrintTime = (uint) base.PrintTime;
- }
+ using var thumbnailsStream = outputFile.CreateEntry(PreviewFilename).Open();
+ thumbnailsStream.WriteBytes(Thumbnails[0]!.GetPngByes());
+ thumbnailsStream.Close();
}
- public override float MaterialMilliliters
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- ManifestFile.Job.VolumeMl = base.MaterialMilliliters;
- }
+ progress.Token.ThrowIfCancellationRequested();
+ var layer = this[layerIndex];
+ outputFile.PutFileContent($"{layerIndex + 1}.png", layer.CompressedBytes, ZipArchiveMode.Create);
+ progress++;
}
- public override float MaterialGrams
+ var entry = outputFile.CreateEntry(ManifestFilename);
+ using (var stream = entry.Open())
{
- get => ManifestFile.Job.WeightG;
- set => base.MaterialGrams = ManifestFile.Job.WeightG = (float)Math.Round(value, 3);
+ XmlExtensions.Serialize(ManifestFile, stream, XmlExtensions.SettingsIndent, true);
}
- public override float MaterialCost
+ outputFile.PutFileContent(GCodeFilename, EncryptGCode(progress), ZipArchiveMode.Create);
+ }
+
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read);
+ var entry = inputFile.GetEntry(ManifestFilename);
+ if (entry is null)
{
- get => ManifestFile.Job.Price;
- set => base.MaterialCost = ManifestFile.Job.Price = (float)Math.Round(value, 3);
+ Clear();
+ throw new FileLoadException($"{ManifestFilename} not found", FileFullPath);
}
- /*public override string MaterialName
+ try
{
- get => HeaderSettings.Resin;
- set
- {
- HeaderSettings.Resin = value;
- RaisePropertyChanged();
- }
- }*/
+ using var stream = entry.Open();
+ ManifestFile = XmlExtensions.DeserializeFromStream<ZCodePrint>(stream);
- public override string MachineName
+ }
+ catch (Exception e)
{
- get => ManifestFile.Device.MachineModel;
- set => base.MachineName = ManifestFile.Device.MachineModel = value;
+ Clear();
+ throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
}
- public override object[] Configs => new object[] { ManifestFile.Device, ManifestFile.Job, ManifestFile.Profile.Slice };
-
- #endregion
-
- #region Constructor
- public ZCodeFile()
+ entry = inputFile.GetEntry(GCodeFilename);
+ if (entry is null)
{
- GCode = new GCodeBuilder
- {
- UseTailComma = true,
- UseComments = false,
- GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Absolute,
- GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.CentimetersPerMinute,
- GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
- GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.FilenamePng1Started,
- LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G0,
- EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1,
- MaxLEDPower = MaxLEDPower,
- CommandClearImage = {Enabled = false},
- };
+ Clear();
+ throw new FileLoadException($"{GCodeFilename} not found", FileFullPath);
}
- #endregion
- #region Methods
+ var encryptEngine = new RsaEngine();
+ using var txtreader = new StringReader(GCodeRSAPublicKey);
+ var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
+ encryptEngine.Init(true, keyParameter);
- protected override void EncodeInternally(OperationProgress progress)
+ using (TextReader tr = new StreamReader(entry.Open()))
{
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- if (Thumbnails.Length > 0 && Thumbnails[0] is not null)
+ string? line;
+ progress.Reset("Decrypting GCode", (uint) (entry.Length / 88));
+ while ((line = tr.ReadLine()) != null)
{
- using var thumbnailsStream = outputFile.CreateEntry(PreviewFilename).Open();
- thumbnailsStream.WriteBytes(Thumbnails[0].GetPngByes());
- thumbnailsStream.Close();
- }
-
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- var layer = this[layerIndex];
- outputFile.PutFileContent($"{layerIndex + 1}.png", layer.CompressedBytes, ZipArchiveMode.Create);
+ if (string.IsNullOrEmpty(line)) continue;
+ if (!line.EndsWith("==")) continue;
+
+ byte[] data = System.Convert.FromBase64String(line);
+ var decodedBytes = encryptEngine.ProcessBlock(data, 0, data.Length);
+ decodedBytes = decodedBytes.Skip(2).SkipWhile(b => b is 255 or 0).ToArray();
+ GCode!.AppendLine(Encoding.UTF8.GetString(decodedBytes));
+
progress++;
}
- XmlSerializer serializer = new(ManifestFile.GetType());
- XmlSerializerNamespaces ns = new();
- ns.Add("", "");
- var entry = outputFile.CreateEntry(ManifestFilename);
- using (var stream = entry.Open())
- {
- serializer.Serialize(stream, ManifestFile, ns);
- }
-
- outputFile.PutFileContent(GCodeFilename, EncryptGCode(progress), ZipArchiveMode.Create);
+ tr.Close();
}
- protected override void DecodeInternally(OperationProgress progress)
+ Init(ManifestFile.Job.LayerCount, DecodeType == FileDecodeType.Partial);
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+
+ //var gcode = GCode.ToString();
+ //float lastPostZ = LayerHeight;
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- using var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read);
- var entry = inputFile.GetEntry(ManifestFilename);
+ if (progress.Token.IsCancellationRequested) break;
+ entry = inputFile.GetEntry($"{layerIndex+1}.png");
if (entry is null)
{
Clear();
- throw new FileLoadException($"{ManifestFilename} not found", FileFullPath);
+ throw new FileLoadException($"Layer {layerIndex+1} not found", FileFullPath);
}
- try
+ if (DecodeType == FileDecodeType.Full)
{
- var serializer = new XmlSerializer(ManifestFile.GetType());
using var stream = entry.Open();
- ManifestFile = (ZCodePrint)serializer.Deserialize(stream);
- }
- catch (Exception e)
- {
- Clear();
- throw new FileLoadException($"Unable to deserialize '{entry.Name}'\n{e}", FileFullPath);
+ this[layerIndex] = new Layer(layerIndex, stream, this);
}
+
+ progress++;
+ }
- entry = inputFile.GetEntry(GCodeFilename);
- if (entry is null)
- {
- Clear();
- throw new FileLoadException($"{GCodeFilename} not found", FileFullPath);
- }
+ GCode!.ParseLayersFromGCode(this);
- var encryptEngine = new RsaEngine();
- using var txtreader = new StringReader(GCodeRSAPublicKey);
- var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
- encryptEngine.Init(true, keyParameter);
+ entry = inputFile.GetEntry(PreviewFilename);
+ if (entry is not null)
+ {
+ Thumbnails[0] = new Mat();
+ CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
+ }
- using (TextReader tr = new StreamReader(entry.Open()))
- {
- string line;
- progress.Reset("Decrypting GCode", (uint) (entry.Length / 88));
- while ((line = tr.ReadLine()) != null)
- {
- if (string.IsNullOrEmpty(line)) continue;
- if (!line.EndsWith("==")) continue;
-
- byte[] data = System.Convert.FromBase64String(line);
- var decodedBytes = encryptEngine.ProcessBlock(data, 0, data.Length);
- decodedBytes = decodedBytes.Skip(2).SkipWhile(b => b is 255 or 0).ToArray();
- GCode.AppendLine(Encoding.UTF8.GetString(decodedBytes));
-
- progress++;
- }
+ GetBoundingRectangle(progress);
+ }
- tr.Close();
- }
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
- LayerManager.Init(ManifestFile.Job.LayerCount, DecodeType == FileDecodeType.Partial);
- progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ var entriesToRemove = outputFile.Entries.Where(zipEntry => zipEntry.Name.EndsWith(".gcode") || zipEntry.Name.EndsWith(".xml")).ToArray();
+ foreach (var zipEntry in entriesToRemove)
+ {
+ zipEntry.Delete();
+ }
- //var gcode = GCode.ToString();
- //float lastPostZ = LayerHeight;
+ var entry = outputFile.CreateEntry(ManifestFilename);
+ using var stream = entry.Open();
+ XmlExtensions.Serialize(ManifestFile, stream, XmlExtensions.SettingsIndent, true);
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- if (progress.Token.IsCancellationRequested) break;
- entry = inputFile.GetEntry($"{layerIndex+1}.png");
- if (entry is null)
- {
- Clear();
- throw new FileLoadException($"Layer {layerIndex+1} not found", FileFullPath);
- }
-
- if (DecodeType == FileDecodeType.Full)
- {
- using var stream = entry.Open();
- this[layerIndex] = new Layer(layerIndex, stream, LayerManager);
- }
-
- progress++;
- }
+ outputFile.PutFileContent(GCodeFilename, EncryptGCode(progress), ZipArchiveMode.Update);
- GCode.ParseLayersFromGCode(this);
+ //Decode(FileFullPath, progress);
+ }
- entry = inputFile.GetEntry(PreviewFilename);
- if (entry is not null)
+ private string EncryptGCode(OperationProgress progress)
+ {
+ RebuildGCode();
+ progress.Reset("Encrypting GCode", (uint) GCode!.Length);
+ StringBuilder sb = new();
+
+ var encryptEngine = new RsaEngine();
+ using var txtreader = new StringReader(GCodeRSAPrivateKey);
+ var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
+ encryptEngine.Init(true, keyParameter);
+
+ using StringReader sr = new(GCodeStr!);
+ string? line;
+ while ((line = sr.ReadLine()) != null)
+ {
+ line = line.Trim();
+ if (line == string.Empty || line[0] == ';') continue; // No empty lines nor comment start lines
+ progress += (uint)line.Length;
+
+ var data = Encoding.UTF8.GetBytes(line);
+ List<byte> padData = new(64) {0, 1, 0};
+ padData.AddRange(data);
+
+ if (padData.Count > 64)
{
- Thumbnails[0] = new Mat();
- CvInvoke.Imdecode(entry.Open().ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
+ throw new ArgumentOutOfRangeException($"Too long gcode line to encrypt, got: {padData.Count} bytes while expecting less than 64 bytes");
}
- LayerManager.GetBoundingRectangle(progress);
- }
-
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
-
- var entriesToRemove = outputFile.Entries.Where(zipEntry => zipEntry.Name.EndsWith(".gcode") || zipEntry.Name.EndsWith(".xml")).ToArray();
- foreach (var zipEntry in entriesToRemove)
+ while (padData.Count < 64)
{
- zipEntry.Delete();
+ padData.Insert(2, 255);
}
- XmlSerializer serializer = new(ManifestFile.GetType());
- XmlSerializerNamespaces ns = new();
- ns.Add("", "");
- var entry = outputFile.CreateEntry(ManifestFilename);
- using var stream = entry.Open();
- serializer.Serialize(stream, ManifestFile, ns);
+ var padDataArray = padData.ToArray();
+ //Debug.WriteLine(string.Join(", ", padDataArray));
- outputFile.PutFileContent(GCodeFilename, EncryptGCode(progress), ZipArchiveMode.Update);
-
- //Decode(FileFullPath, progress);
+ var encodedBytes = encryptEngine.ProcessBlock(padDataArray, 0, padDataArray.Length);
+ sb.AppendLine(System.Convert.ToBase64String(encodedBytes));
}
- private string EncryptGCode(OperationProgress progress)
- {
- RebuildGCode();
- progress.Reset("Encrypting GCode", (uint) GCode.Length);
- StringBuilder sb = new();
-
- var encryptEngine = new RsaEngine();
- using var txtreader = new StringReader(GCodeRSAPrivateKey);
- var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
- encryptEngine.Init(true, keyParameter);
-
- using StringReader sr = new(GCodeStr);
- string line;
- while ((line = sr.ReadLine()) != null)
- {
- line = line.Trim();
- if (line == string.Empty || line[0] == ';') continue; // No empty lines nor comment start lines
- progress += (uint)line.Length;
-
- var data = Encoding.UTF8.GetBytes(line);
- List<byte> padData = new(64) {0, 1, 0};
- padData.AddRange(data);
-
- if (padData.Count > 64)
- {
- throw new ArgumentOutOfRangeException($"Too long gcode line to encrypt, got: {padData.Count} bytes while expecting less than 64 bytes");
- }
-
- while (padData.Count < 64)
- {
- padData.Insert(2, 255);
- }
-
- var padDataArray = padData.ToArray();
- //Debug.WriteLine(string.Join(", ", padDataArray));
-
- var encodedBytes = encryptEngine.ProcessBlock(padDataArray, 0, padDataArray.Length);
- sb.AppendLine(System.Convert.ToBase64String(encodedBytes));
- }
-
- return sb.ToString();
- }
- #endregion
+ return sb.ToString();
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs
index 2774dfb..3bd0502 100644
--- a/UVtools.Core/FileFormats/ZCodexFile.cs
+++ b/UVtools.Core/FileFormats/ZCodexFile.cs
@@ -6,523 +6,524 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
+using System.Drawing;
using System.Globalization;
using System.IO;
using System.IO.Compression;
+using System.Text.Json;
using System.Text.RegularExpressions;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Newtonsoft.Json;
using UVtools.Core.Extensions;
using UVtools.Core.GCode;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.FileFormats
-{
- public class ZCodexFile : FileFormat
- {
- #region Constants
+namespace UVtools.Core.FileFormats;
- private const string GCodeStart = "G28\nG21\nG91\nM17\n";
- private const string GCodeKeywordSlice = "<Slice>";
- private const string GCodeKeywordDelayBlank = "<Delay_blank>";
- private const string GCodeKeywordDelayModel = "<Delay_model>";
- private const string GCodeKeywordDelaySupportPart = "<Delay_support_part>";
- private const string GCodeKeywordDelaySupportFull = "<Delay_support_full>";
- private const string FolderImages = "ResinSlicesData";
- private const string FolderImageName = "Slice";
- #endregion
+public class ZCodexFile : FileFormat
+{
+ #region Constants
+
+ private const string GCodeStart = "G28\nG21\nG91\nM17\n";
+ private const string GCodeKeywordSlice = "<Slice>";
+ private const string GCodeKeywordDelayBlank = "<Delay_blank>";
+ private const string GCodeKeywordDelayModel = "<Delay_model>";
+ private const string GCodeKeywordDelaySupportPart = "<Delay_support_part>";
+ private const string GCodeKeywordDelaySupportFull = "<Delay_support_full>";
+ private const string FolderImages = "ResinSlicesData";
+ private const string FolderImageName = "Slice";
+ #endregion
- #region Sub Classes
+ #region Sub Classes
- public class ResinMetadata
+ public class ResinMetadata
+ {
+ public class LayerData
{
- public class LayerData
- {
- public uint Layer { get; set; }
- public float UsedMaterialVolume { get; set; }
+ public uint Layer { get; set; }
+ public float UsedMaterialVolume { get; set; }
- }
-
- public string Guid { get; set; } = "07452AC2-7494-4576-BA60-BFEA8815F917";
- public string Material { get; set; }
- public string MaterialId { get; set; }
- public float LayerThickness { get; set; }
- public uint PrintTime { get; set; }
- public uint LayerTime { get; set; }
- public uint BottomLayersTime { get; set; }
- public uint AdditionalSupportLayerTime { get; set; }
- public ushort BottomLayersNumber { get; set; }
- public uint BlankingLayerTime { get; set; }
- public float TotalMaterialVolumeUsed { get; set; }
- public float TotalMaterialWeightUsed { get; set; }
- public uint TotalLayersCount { get; set; }
- public bool DisableSettingsChanges { get; set; }
-
- public List<LayerData> Layers { get; set; } = new();
}
- public class UserSettingsdata
+ public string Guid { get; set; } = "07452AC2-7494-4576-BA60-BFEA8815F917";
+ public string? Material { get; set; }
+ public string? MaterialId { get; set; }
+ public float LayerThickness { get; set; }
+ public uint PrintTime { get; set; }
+ public uint LayerTime { get; set; }
+ public uint BottomLayersTime { get; set; }
+ public uint AdditionalSupportLayerTime { get; set; }
+ public ushort BottomLayersNumber { get; set; }
+ public uint BlankingLayerTime { get; set; }
+ public float TotalMaterialVolumeUsed { get; set; }
+ public float TotalMaterialWeightUsed { get; set; }
+ public uint TotalLayersCount { get; set; }
+ public bool DisableSettingsChanges { get; set; }
+
+ public List<LayerData> Layers { get; set; } = new();
+ }
+
+ public class UserSettingsdata
+ {
+ public uint MaxLayer { get; set; }
+ public string? PrintTime { get; set; }
+ public float MaterialVolume { get; set; }
+ public byte IsAdvanced { get; set; }
+ public string Printer { get; set; } = "Zortrax Inkspire";
+ public string? MaterialType { get; set; }
+ public uint MaterialId { get; set; }
+ public string? LayerThickness { get; set; }
+ public byte RaftEnabled { get; set; }
+ public float RaftHeight { get; set; }
+ public float RaftOffset { get; set; }
+ public byte ModelLiftEnabled { get; set; }
+ public float ModelLiftHeight { get; set; }
+ public byte CrossSupportEnabled { get; set; }
+ public uint LayerExposureTime { get; set; }
+ //public uint LayerThicknessesDisplayTime { get; set; } arr
+ public uint ExposureOffTime { get; set; } = 5;
+ public uint BottomLayerExposureTime { get; set; }
+ public ushort BottomLayersCount { get; set; }
+ public byte SupportAdditionalExposureEnabled { get; set; }
+ public uint SupportAdditionalExposureTime { get; set; }
+ public float ZLiftDistance { get; set; } = 5;
+ public float ZLiftRetractRate { get; set; } = 100;
+ public float ZLiftFeedRate { get; set; } = 100;
+ public byte AntiAliasing { get; set; } = 0;
+ public float XCorrection { get; set; }
+ public float YCorrection { get; set; }
+ public byte HollowEnabled { get; set; }
+ public float HollowThickness { get; set; }
+ public byte InfillDensity { get; set; }
+ }
+
+ public class ZCodeMetadata
+ {
+ public class MaterialsData
{
- public uint MaxLayer { get; set; }
- public string PrintTime { get; set; }
- public float MaterialVolume { get; set; }
- public byte IsAdvanced { get; set; }
- public string Printer { get; set; } = "Zortrax Inkspire";
- public string MaterialType { get; set; }
- public uint MaterialId { get; set; }
- public string LayerThickness { get; set; }
- public byte RaftEnabled { get; set; }
- public float RaftHeight { get; set; }
- public float RaftOffset { get; set; }
- public byte ModelLiftEnabled { get; set; }
- public float ModelLiftHeight { get; set; }
- public byte CrossSupportEnabled { get; set; }
- public uint LayerExposureTime { get; set; }
- //public uint LayerThicknessesDisplayTime { get; set; } arr
- public uint ExposureOffTime { get; set; } = 5;
- public uint BottomLayerExposureTime { get; set; }
- public ushort BottomLayersCount { get; set; }
- public byte SupportAdditionalExposureEnabled { get; set; }
- public uint SupportAdditionalExposureTime { get; set; }
- public float ZLiftDistance { get; set; } = 5;
- public float ZLiftRetractRate { get; set; } = 100;
- public float ZLiftFeedRate { get; set; } = 100;
- public byte AntiAliasing { get; set; } = 0;
- public float XCorrection { get; set; }
- public float YCorrection { get; set; }
- public byte HollowEnabled { get; set; }
- public float HollowThickness { get; set; }
- public byte InfillDensity { get; set; }
+ public string? ExtruderType { get; set; }
+ public uint Id { get; set; }
+ public string? Name { get; set; }
+ public uint Usage { get; set; }
+ public uint Temperature { get; set; }
}
- public class ZCodeMetadata
+ public string ZCodexVersion { get; set; } = "2.0.0.0";
+ public string SoftwareVersion { get; set; } = "2.12.2.0";
+ public string MinFirmwareVersion { get; set; } = "20013";
+ public uint PrinterModelEnumId { get; set; } = 40;
+ public string PrinterName { get; set; } = "Inkspire";
+
+ public List<MaterialsData> Materials { get; set; } = new()
{
- public class MaterialsData
+ new MaterialsData
{
- public string ExtruderType { get; set; }
- public uint Id { get; set; }
- public string Name { get; set; }
- public uint Usage { get; set; }
- public uint Temperature { get; set; }
+ Name = "",
+ ExtruderType = "MAIN",
+ Id = 0,
+ Usage = 0,
+ Temperature = 0
}
+ };
+ public byte HeatbedTemperature { get; set; }
+ public byte ChamberTemperature { get; set; }
+ public uint CommandCount { get; set; }
+ public uint PrintTime { get; set; }
+ public float NozzleDiameter { get; set; }
+ public string? PrintBoundingBox { get; set; }
+ public string? Pauses { get; set; }
+ public string? MaterialUsages { get; set; }
+ }
- public string ZCodexVersion { get; set; } = "2.0.0.0";
- public string SoftwareVersion { get; set; } = "2.12.2.0";
- public string MinFirmwareVersion { get; set; } = "20013";
- public uint PrinterModelEnumId { get; set; } = 40;
- public string PrinterName { get; set; } = "Inkspire";
-
- public List<MaterialsData> Materials { get; set; } = new()
- {
- new MaterialsData
- {
- Name = "",
- ExtruderType = "MAIN",
- Id = 0,
- Usage = 0,
- Temperature = 0
- }
- };
- public byte HeatbedTemperature { get; set; }
- public byte ChamberTemperature { get; set; }
- public uint CommandCount { get; set; }
- public uint PrintTime { get; set; }
- public float NozzleDiameter { get; set; }
- public string PrintBoundingBox { get; set; }
- public string Pauses { get; set; }
- public string MaterialUsages { get; set; }
- }
-
- public class LayerData
- {
- public int SupportLayerFileIndex { get; set; } = -1;
- public int LayerFileIndex { get; set; } = -1;
- public ZipArchiveEntry SupportLayerEntry { get; set; }
- public ZipArchiveEntry LayerEntry { get; set; }
+ public class LayerData
+ {
+ public int SupportLayerFileIndex { get; set; } = -1;
+ public int LayerFileIndex { get; set; } = -1;
+ public ZipArchiveEntry? SupportLayerEntry { get; set; }
+ public ZipArchiveEntry? LayerEntry { get; set; }
- public bool HaveSupportLayer => !ReferenceEquals(SupportLayerEntry, null);
- }
+ public bool HaveSupportLayer => SupportLayerEntry is not null;
+ }
- #endregion
+ #endregion
- #region Properties
- public ResinMetadata ResinMetadataSettings { get; set; } = new();
- public UserSettingsdata UserSettings { get; set; } = new();
- public ZCodeMetadata ZCodeMetadataSettings { get; set; } = new();
+ #region Properties
+ public ResinMetadata ResinMetadataSettings { get; set; } = new();
+ public UserSettingsdata UserSettings { get; set; } = new();
+ public ZCodeMetadata ZCodeMetadataSettings { get; set; } = new();
- public List<LayerData> LayersSettings { get; } = new();
+ public List<LayerData> LayersSettings { get; } = new();
- public override FileFormatType FileType => FileFormatType.Archive;
+ public override FileFormatType FileType => FileFormatType.Archive;
- public override FileExtension[] FileExtensions { get; } = {
- new(typeof(ZCodexFile), "zcodex", "Z-Suite ZCodex")
- };
+ public override FileExtension[] FileExtensions { get; } = {
+ new(typeof(ZCodexFile), "zcodex", "Z-Suite ZCodex")
+ };
- public override PrintParameterModifier[] PrintParameterModifiers { get; } = {
- PrintParameterModifier.BottomLayerCount,
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
+ PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.WaitTimeBeforeCure,
+ PrintParameterModifier.WaitTimeBeforeCure,
- PrintParameterModifier.BottomExposureTime,
- PrintParameterModifier.ExposureTime,
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.BottomRetractSpeed,
- PrintParameterModifier.RetractSpeed,
- };
-
- public override PrintParameterModifier[] PrintParameterPerLayerModifiers { get; } = {
- PrintParameterModifier.PositionZ,
- PrintParameterModifier.LiftHeight,
- PrintParameterModifier.LiftSpeed,
- PrintParameterModifier.RetractSpeed,
- PrintParameterModifier.LightPWM,
- };
-
- public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = {new(320, 180)};
-
- public override uint ResolutionX
- {
- get => 1440;
- set { }
- }
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.BottomRetractSpeed,
+ PrintParameterModifier.RetractSpeed,
+ };
+
+ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { get; } = {
+ PrintParameterModifier.PositionZ,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+ PrintParameterModifier.LightPWM,
+ };
+
+ public override Size[]? ThumbnailsOriginalSize { get; } = {new(320, 180)};
+
+ public override uint ResolutionX
+ {
+ get => 1440;
+ set { }
+ }
- public override uint ResolutionY
- {
- get => 2560;
- set { }
- }
+ public override uint ResolutionY
+ {
+ get => 2560;
+ set { }
+ }
- public override float DisplayWidth
- {
- get => 74.67f;
- set {}
- }
- public override float DisplayHeight
- {
- get => 132.88f;
- set { }
- }
+ public override float DisplayWidth
+ {
+ get => 74.67f;
+ set {}
+ }
+ public override float DisplayHeight
+ {
+ get => 132.88f;
+ set { }
+ }
- public override Enumerations.FlipDirection DisplayMirror
- {
- get => Enumerations.FlipDirection.Horizontally;
- set { }
- }
+ public override Enumerations.FlipDirection DisplayMirror
+ {
+ get => Enumerations.FlipDirection.Horizontally;
+ set { }
+ }
- public override byte AntiAliasing
- {
- get => UserSettings.AntiAliasing;
- set => base.AntiAliasing = UserSettings.AntiAliasing = value.Clamp(1, 16);
- }
+ public override byte AntiAliasing
+ {
+ get => UserSettings.AntiAliasing;
+ set => base.AntiAliasing = UserSettings.AntiAliasing = value.Clamp(1, 16);
+ }
- public override float LayerHeight
+ public override float LayerHeight
+ {
+ get => ResinMetadataSettings.LayerThickness;
+ set
{
- get => ResinMetadataSettings.LayerThickness;
- set
- {
- ResinMetadataSettings.LayerThickness = Layer.RoundHeight(value);
- UserSettings.LayerThickness = $"{ResinMetadataSettings.LayerThickness} mm";
- RaisePropertyChanged();
- }
+ ResinMetadataSettings.LayerThickness = Layer.RoundHeight(value);
+ UserSettings.LayerThickness = $"{ResinMetadataSettings.LayerThickness} mm";
+ RaisePropertyChanged();
}
+ }
- public override uint LayerCount
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set
{
- get => base.LayerCount;
- set
- {
- base.LayerCount = ResinMetadataSettings.TotalLayersCount = base.LayerCount;
- UserSettings.MaxLayer = LastLayerIndex;
+ base.LayerCount = ResinMetadataSettings.TotalLayersCount = base.LayerCount;
+ UserSettings.MaxLayer = LastLayerIndex;
- }
}
+ }
- public override ushort BottomLayerCount
- {
- get => ResinMetadataSettings.BottomLayersNumber;
- set => base.BottomLayerCount = ResinMetadataSettings.BottomLayersNumber = UserSettings.BottomLayersCount = value;
- }
+ public override ushort BottomLayerCount
+ {
+ get => ResinMetadataSettings.BottomLayersNumber;
+ set => base.BottomLayerCount = ResinMetadataSettings.BottomLayersNumber = UserSettings.BottomLayersCount = value;
+ }
- public override float BottomLightOffDelay => BottomWaitTimeBeforeCure;
+ public override float BottomLightOffDelay => BottomWaitTimeBeforeCure;
- public override float LightOffDelay => WaitTimeBeforeCure;
- public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
- public override float WaitTimeBeforeCure
+ public override float LightOffDelay => WaitTimeBeforeCure;
+ public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure;
+ public override float WaitTimeBeforeCure
+ {
+ get => TimeExtensions.MillisecondsToSeconds(ResinMetadataSettings.BlankingLayerTime);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(ResinMetadataSettings.BlankingLayerTime);
- set
- {
- UserSettings.ExposureOffTime = ResinMetadataSettings.BlankingLayerTime = TimeExtensions.SecondsToMillisecondsUint(value);
- base.WaitTimeBeforeCure = base.LightOffDelay = value;
- }
+ UserSettings.ExposureOffTime = ResinMetadataSettings.BlankingLayerTime = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.WaitTimeBeforeCure = base.LightOffDelay = value;
}
+ }
- public override float BottomExposureTime
+ public override float BottomExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(UserSettings.BottomLayerExposureTime);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(UserSettings.BottomLayerExposureTime);
- set
- {
- ResinMetadataSettings.BottomLayersTime = UserSettings.BottomLayerExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
- base.BottomExposureTime = value;
- }
+ ResinMetadataSettings.BottomLayersTime = UserSettings.BottomLayerExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.BottomExposureTime = value;
}
+ }
- public override float ExposureTime
+ public override float ExposureTime
+ {
+ get => TimeExtensions.MillisecondsToSeconds(UserSettings.LayerExposureTime);
+ set
{
- get => TimeExtensions.MillisecondsToSeconds(UserSettings.LayerExposureTime);
- set
- {
- ResinMetadataSettings.LayerTime = UserSettings.LayerExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
- base.ExposureTime = value;
- }
+ ResinMetadataSettings.LayerTime = UserSettings.LayerExposureTime = TimeExtensions.SecondsToMillisecondsUint(value);
+ base.ExposureTime = value;
}
+ }
- public override float BottomLiftHeight => LiftHeight;
+ public override float BottomLiftHeight => LiftHeight;
- public override float LiftHeight
- {
- get => UserSettings.ZLiftDistance;
- set => base.LiftHeight = UserSettings.ZLiftDistance = (float)Math.Round(value, 2);
- }
+ public override float LiftHeight
+ {
+ get => UserSettings.ZLiftDistance;
+ set => base.LiftHeight = UserSettings.ZLiftDistance = (float)Math.Round(value, 2);
+ }
- public override float BottomLiftSpeed => LiftSpeed;
+ public override float BottomLiftSpeed => LiftSpeed;
- public override float LiftSpeed
- {
- get => UserSettings.ZLiftFeedRate;
- set => base.LiftSpeed = UserSettings.ZLiftFeedRate = (float)Math.Round(value, 2);
- }
+ public override float LiftSpeed
+ {
+ get => UserSettings.ZLiftFeedRate;
+ set => base.LiftSpeed = UserSettings.ZLiftFeedRate = (float)Math.Round(value, 2);
+ }
- public override float RetractSpeed
- {
- get => UserSettings.ZLiftRetractRate;
- set => base.RetractSpeed = UserSettings.ZLiftRetractRate = (float)Math.Round(value, 2);
- }
+ public override float RetractSpeed
+ {
+ get => UserSettings.ZLiftRetractRate;
+ set => base.RetractSpeed = UserSettings.ZLiftRetractRate = (float)Math.Round(value, 2);
+ }
- public override float PrintTime
+ public override float PrintTime
+ {
+ get => ResinMetadataSettings.PrintTime;
+ set
{
- get => ResinMetadataSettings.PrintTime;
- set
- {
- base.PrintTime = value;
- ResinMetadataSettings.PrintTime = (uint)base.PrintTime;
- TimeSpan ts = new(0, 0, (int)base.PrintTime);
- UserSettings.PrintTime = $"{ts.Hours}h {ts.Minutes}m";
- }
+ base.PrintTime = value;
+ ResinMetadataSettings.PrintTime = (uint)base.PrintTime;
+ TimeSpan ts = new(0, 0, (int)base.PrintTime);
+ UserSettings.PrintTime = $"{ts.Hours}h {ts.Minutes}m";
}
+ }
- public override float MaterialMilliliters
+ public override float MaterialMilliliters
+ {
+ get => base.MaterialMilliliters;
+ set
{
- get => base.MaterialMilliliters;
- set
- {
- base.MaterialMilliliters = value;
- ResinMetadataSettings.TotalMaterialVolumeUsed = base.MaterialMilliliters;
- }
+ base.MaterialMilliliters = value;
+ ResinMetadataSettings.TotalMaterialVolumeUsed = base.MaterialMilliliters;
}
+ }
- public override float MaterialGrams
- {
- get => (float) Math.Round(ResinMetadataSettings.TotalMaterialWeightUsed, 3);
- set => base.MaterialGrams = ResinMetadataSettings.TotalMaterialWeightUsed = (float) Math.Round(value, 3);
- }
+ public override float MaterialGrams
+ {
+ get => (float) Math.Round(ResinMetadataSettings.TotalMaterialWeightUsed, 3);
+ set => base.MaterialGrams = ResinMetadataSettings.TotalMaterialWeightUsed = (float) Math.Round(value, 3);
+ }
- public override string MaterialName
- {
- get => ResinMetadataSettings.Material;
- set => base.MaterialName = ResinMetadataSettings.Material = value;
- }
+ public override string? MaterialName
+ {
+ get => ResinMetadataSettings.Material;
+ set => base.MaterialName = ResinMetadataSettings.Material = value;
+ }
- public override string MachineName
+ public override string MachineName
+ {
+ get => ZCodeMetadataSettings.PrinterName;
+ set => base.MachineName = ZCodeMetadataSettings.PrinterName = value;
+ }
+
+ public override object[] Configs => new[] {(object) ResinMetadataSettings, UserSettings, ZCodeMetadataSettings};
+ #endregion
+
+ #region Constructor
+
+ public ZCodexFile()
+ {
+ GCode = new()
{
- get => ZCodeMetadataSettings.PrinterName;
- set => base.MachineName = ZCodeMetadataSettings.PrinterName = value;
- }
+ UseComments = true,
+ GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Partial,
+ GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
+ GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
+ GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started,
+ LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G1,
+ EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1
+ };
+ }
- public override object[] Configs => new[] {(object) ResinMetadataSettings, UserSettings, ZCodeMetadataSettings};
- #endregion
+ #endregion
- #region Constructor
+ #region Methods
- public ZCodexFile()
+ public override void Clear()
+ {
+ base.Clear();
+ LayersSettings.Clear();
+ }
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ float usedMaterial = MaterialMilliliters / LayerCount;
+ ResinMetadataSettings.Layers.Clear();
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
- GCode = new()
+ ResinMetadataSettings.Layers.Add(new ResinMetadata.LayerData
{
- UseComments = true,
- GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Partial,
- GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute,
- GCodeTimeUnit = GCodeBuilder.GCodeTimeUnits.Milliseconds,
- GCodeShowImageType = GCodeBuilder.GCodeShowImageTypes.LayerIndex0Started,
- LayerMoveCommand = GCodeBuilder.GCodeMoveCommands.G1,
- EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1
- };
+ Layer = layerIndex,
+ UsedMaterialVolume = usedMaterial
+ });
}
- #endregion
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Create);
+ outputFile.PutFileContent("ResinMetadata", JsonSerializer.SerializeToUtf8Bytes(ResinMetadataSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Create);
+ outputFile.PutFileContent("UserSettingsData", JsonSerializer.SerializeToUtf8Bytes(UserSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Create);
+ outputFile.PutFileContent("ZCodeMetadata", JsonSerializer.SerializeToUtf8Bytes(ZCodeMetadataSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Create);
- #region Methods
-
- public override void Clear()
+ if (CreatedThumbnailsCount > 0)
{
- base.Clear();
- LayersSettings.Clear();
+ using var stream = outputFile.CreateEntry("Preview.png").Open();
+ stream.WriteBytes(Thumbnails[0]!.GetPngByes());
+ stream.Close();
}
- protected override void EncodeInternally(OperationProgress progress)
- {
- float usedMaterial = MaterialMilliliters / LayerCount;
- ResinMetadataSettings.Layers.Clear();
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- ResinMetadataSettings.Layers.Add(new ResinMetadata.LayerData
- {
- Layer = layerIndex,
- UsedMaterialVolume = usedMaterial
- });
- }
-
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Create);
- outputFile.PutFileContent("ResinMetadata", JsonConvert.SerializeObject(ResinMetadataSettings, Formatting.Indented), ZipArchiveMode.Create);
- outputFile.PutFileContent("UserSettingsData", JsonConvert.SerializeObject(UserSettings, Formatting.Indented), ZipArchiveMode.Create);
- outputFile.PutFileContent("ZCodeMetadata", JsonConvert.SerializeObject(ZCodeMetadataSettings, Formatting.Indented), ZipArchiveMode.Create);
+ GCode!.Clear();
- if (CreatedThumbnailsCount > 0)
- {
- using var stream = outputFile.CreateEntry("Preview.png").Open();
- stream.WriteBytes(Thumbnails[0].GetPngByes());
- stream.Close();
- }
+ float lastZPosition = 0;
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
- GCode.Clear();
+ var layer = this[layerIndex];
+ GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}");
- float lastZPosition = 0;
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ if (lastZPosition != layer.PositionZ)
{
- progress.Token.ThrowIfCancellationRequested();
-
- var layer = this[layerIndex];
- GCode.AppendLine($"{GCodeKeywordSlice} {layerIndex}");
-
- if (lastZPosition != layer.PositionZ)
+ if (layer.LiftHeight > 0)
{
- if (layer.LiftHeight > 0)
- {
- GCode.AppendLine($"G1 Z{layer.LiftHeight} F{layer.LiftSpeed}");
- GCode.AppendLine($"G1 Z-{Layer.RoundHeight(layer.LiftHeight - layer.PositionZ + lastZPosition)} F{layer.RetractSpeed}");
- }
- else
- {
- GCode.AppendLine($"G1 Z{Layer.RoundHeight(layer.PositionZ- lastZPosition)} F{layer.LiftSpeed}");
- }
+ GCode.AppendLine($"G1 Z{layer.LiftHeight} F{layer.LiftSpeed}");
+ GCode.AppendLine($"G1 Z-{Layer.RoundHeight(layer.LiftHeight - layer.PositionZ + lastZPosition)} F{layer.RetractSpeed}");
}
- /*else
- {
- //GCode.AppendLine($";G1 Z{LiftHeight} F{LiftSpeed}; Already here");
- //GCode.AppendLine($";G1 Z-{LiftHeight - layer.PositionZ + lastZPosition} F{RetractSpeed}; Already here");
- }*/
-
- //GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}");
- //GCode.AppendLine($"G1 Z-{LiftHeight - LayerHeight} F{RetractSpeed}");
- GCode.AppendLine(GCodeKeywordDelayBlank);
- GCode.AppendLine("M106 S255");
- GCode.AppendLine(GCodeKeywordDelayModel);
- GCode.AppendLine("M106 S0");
-
-
- var layerimagePath = $"{FolderImages}/{FolderImageName}{layerIndex:D5}.png";
- using (var stream = outputFile.CreateEntry(layerimagePath).Open())
+ else
{
- //image.Save(stream, Helpers.PngEncoder);
- var byteArr = this[layerIndex].CompressedBytes;
- stream.Write(byteArr, 0, byteArr.Length);
- stream.Close();
+ GCode.AppendLine($"G1 Z{Layer.RoundHeight(layer.PositionZ- lastZPosition)} F{layer.LiftSpeed}");
}
+ }
+ /*else
+ {
+ //GCode.AppendLine($";G1 Z{LiftHeight} F{LiftSpeed}; Already here");
+ //GCode.AppendLine($";G1 Z-{LiftHeight - layer.PositionZ + lastZPosition} F{RetractSpeed}; Already here");
+ }*/
+
+ //GCode.AppendLine($"G1 Z{LiftHeight} F{LiftSpeed}");
+ //GCode.AppendLine($"G1 Z-{LiftHeight - LayerHeight} F{RetractSpeed}");
+ GCode.AppendLine(GCodeKeywordDelayBlank);
+ GCode.AppendLine("M106 S255");
+ GCode.AppendLine(GCodeKeywordDelayModel);
+ GCode.AppendLine("M106 S0");
- lastZPosition = layer.PositionZ;
- progress++;
+ var layerimagePath = $"{FolderImages}/{FolderImageName}{layerIndex:D5}.png";
+ using (var stream = outputFile.CreateEntry(layerimagePath).Open())
+ {
+ //image.Save(stream, Helpers.PngEncoder);
+ var byteArr = this[layerIndex].CompressedBytes;
+ stream.Write(byteArr!, 0, byteArr!.Length);
+ stream.Close();
}
- GCode.AppendLine($"G1 Z40.0 F{UserSettings.ZLiftFeedRate}");
- GCode.AppendLine("M18");
+ lastZPosition = layer.PositionZ;
- outputFile.PutFileContent("ResinGCodeData", GCode.ToString(), ZipArchiveMode.Create);
+ progress++;
}
- protected override void DecodeInternally(OperationProgress progress)
+ GCode.AppendLine($"G1 Z40.0 F{UserSettings.ZLiftFeedRate}");
+ GCode.AppendLine("M18");
+
+ outputFile.PutFileContent("ResinGCodeData", GCode.ToString(), ZipArchiveMode.Create);
+ }
+
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using (var inputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Read))
{
- using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
+ var entry = inputFile.GetEntry("ResinMetadata");
+ if (entry is null)
{
- var entry = inputFile.GetEntry("ResinMetadata");
- if (entry is null)
- {
- Clear();
- throw new FileLoadException("ResinMetadata not found", FileFullPath);
- }
+ Clear();
+ throw new FileLoadException("ResinMetadata not found", FileFullPath);
+ }
- ResinMetadataSettings = Helpers.JsonDeserializeObject<ResinMetadata>(entry.Open());
+ ResinMetadataSettings = JsonSerializer.Deserialize<ResinMetadata>(entry.Open())!;
- entry = inputFile.GetEntry("UserSettingsData");
- if (entry is null)
- {
- Clear();
- throw new FileLoadException("UserSettingsData not found", FileFullPath);
- }
+ entry = inputFile.GetEntry("UserSettingsData");
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException("UserSettingsData not found", FileFullPath);
+ }
- UserSettings = Helpers.JsonDeserializeObject<UserSettingsdata>(entry.Open());
+ UserSettings = JsonSerializer.Deserialize<UserSettingsdata>(entry.Open())!;
- entry = inputFile.GetEntry("ZCodeMetadata");
- if (entry is null)
- {
- Clear();
- throw new FileLoadException("ZCodeMetadata not found", FileFullPath);
- }
+ entry = inputFile.GetEntry("ZCodeMetadata");
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException("ZCodeMetadata not found", FileFullPath);
+ }
- ZCodeMetadataSettings = Helpers.JsonDeserializeObject<ZCodeMetadata>(entry.Open());
+ ZCodeMetadataSettings = JsonSerializer.Deserialize<ZCodeMetadata>(entry.Open())!;
- entry = inputFile.GetEntry("ResinGCodeData");
- if (entry is null)
- {
- Clear();
- throw new FileLoadException("ResinGCodeData not found", FileFullPath);
- }
+ entry = inputFile.GetEntry("ResinGCodeData");
+ if (entry is null)
+ {
+ Clear();
+ throw new FileLoadException("ResinGCodeData not found", FileFullPath);
+ }
- LayerManager.Init(ResinMetadataSettings.TotalLayersCount, DecodeType == FileDecodeType.Partial);
- GCode.Clear();
- using (TextReader tr = new StreamReader(entry.Open()))
+ Init(ResinMetadataSettings.TotalLayersCount, DecodeType == FileDecodeType.Partial);
+ GCode!.Clear();
+ using (TextReader tr = new StreamReader(entry.Open()))
+ {
+ string? line;
+ int layerIndex = 0;
+ int layerFileIndex = 0;
+ string layerimagePath = null!;
+ float currentHeight = 0;
+ while ((line = tr.ReadLine()) is not null)
{
- string line;
- int layerIndex = 0;
- int layerFileIndex = 0;
- string layerimagePath = null;
- float currentHeight = 0;
- while ((line = tr.ReadLine()) is not null)
+ GCode.AppendLine(line);
+ if (line.StartsWith(GCodeKeywordSlice))
{
- GCode.AppendLine(line);
- if (line.StartsWith(GCodeKeywordSlice))
- {
- layerFileIndex = int.Parse(line.Substring(GCodeKeywordSlice.Length));
- layerimagePath = $"{FolderImages}/{FolderImageName}{layerFileIndex:D5}.png";
- if (LayersSettings.Count - 1 < layerIndex) LayersSettings.Add(new LayerData());
- continue;
- }
+ layerFileIndex = int.Parse(line[GCodeKeywordSlice.Length..]);
+ layerimagePath = $"{FolderImages}/{FolderImageName}{layerFileIndex:D5}.png";
+ if (LayersSettings.Count - 1 < layerIndex) LayersSettings.Add(new LayerData());
+ continue;
+ }
- if (line.StartsWith(GCodeKeywordDelaySupportPart))
- {
- LayersSettings[layerIndex].SupportLayerFileIndex = layerFileIndex;
- LayersSettings[layerIndex].SupportLayerEntry = inputFile.GetEntry(layerimagePath);
- continue;
- }
+ if (line.StartsWith(GCodeKeywordDelaySupportPart))
+ {
+ LayersSettings[layerIndex].SupportLayerFileIndex = layerFileIndex;
+ LayersSettings[layerIndex].SupportLayerEntry = inputFile.GetEntry(layerimagePath);
+ continue;
+ }
- /*
- *
+ /*
+ *
<Slice> 0
G1 Z5.0 F100.0
G1 Z-4.9 F100.0
@@ -530,108 +531,107 @@ G1 Z-4.9 F100.0
M106 S255
<Delay_support_full>
M106 S0
- */
+ */
- var gcode = GCodeStr;
+ var gcode = GCodeStr!;
- if (line.StartsWith(GCodeKeywordDelaySupportFull) || line.StartsWith(GCodeKeywordDelayModel))
- {
- var startStr = $"{GCodeKeywordSlice} {layerIndex}";
- var stripGcode = gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length).Trim(' ', '\n', '\r', '\t');
+ if (line.StartsWith(GCodeKeywordDelaySupportFull) || line.StartsWith(GCodeKeywordDelayModel))
+ {
+ var startStr = $"{GCodeKeywordSlice} {layerIndex}";
+ var stripGcode = gcode[(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length)..].Trim(' ', '\n', '\r', '\t');
- float liftHeight = 0;
- float liftSpeed = GetBottomOrNormalValue((uint)layerIndex, BottomLiftSpeed, LiftSpeed);
- float retractSpeed = RetractSpeed;
- byte pwm = GetBottomOrNormalValue((uint)layerIndex, BottomLightPWM, LightPWM); ;
+ float liftHeight = 0;
+ float liftSpeed = GetBottomOrNormalValue((uint)layerIndex, BottomLiftSpeed, LiftSpeed);
+ float retractSpeed = RetractSpeed;
+ byte pwm = GetBottomOrNormalValue((uint)layerIndex, BottomLightPWM, LightPWM); ;
- //var currPos = Regex.Match(stripGcode, "G1 Z([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase);
- var moveG1Regex = Regex.Match(stripGcode, @"G1 Z([+-]?([0-9]*[.])?[0-9]+) F(\d+)", RegexOptions.IgnoreCase);
- var pwmM106Regex = Regex.Match(stripGcode, @"M106 S(\d+)", RegexOptions.IgnoreCase);
+ //var currPos = Regex.Match(stripGcode, "G1 Z([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase);
+ var moveG1Regex = Regex.Match(stripGcode, @"G1 Z([+-]?([0-9]*[.])?[0-9]+) F(\d+)", RegexOptions.IgnoreCase);
+ var pwmM106Regex = Regex.Match(stripGcode, @"M106 S(\d+)", RegexOptions.IgnoreCase);
+ if (moveG1Regex.Success)
+ {
+ var liftHeightTemp = float.Parse(moveG1Regex.Groups[1].Value, CultureInfo.InvariantCulture);
+ var liftSpeedTemp = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture);
+ moveG1Regex = moveG1Regex.NextMatch();
if (moveG1Regex.Success)
{
- var liftHeightTemp = float.Parse(moveG1Regex.Groups[1].Value, CultureInfo.InvariantCulture);
- var liftSpeedTemp = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture);
- moveG1Regex = moveG1Regex.NextMatch();
- if (moveG1Regex.Success)
- {
- liftHeight = liftHeightTemp;
- liftSpeed = liftSpeedTemp;
- var retractHeight = float.Parse(moveG1Regex.Groups[1].Value, CultureInfo.InvariantCulture);
- retractSpeed = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture);
- currentHeight = Layer.RoundHeight(currentHeight + liftHeightTemp + retractHeight);
- }
- else
- {
- currentHeight = Layer.RoundHeight(currentHeight + liftHeightTemp);
- }
+ liftHeight = liftHeightTemp;
+ liftSpeed = liftSpeedTemp;
+ var retractHeight = float.Parse(moveG1Regex.Groups[1].Value, CultureInfo.InvariantCulture);
+ retractSpeed = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture);
+ currentHeight = Layer.RoundHeight(currentHeight + liftHeightTemp + retractHeight);
}
-
- if (pwmM106Regex.Success)
+ else
{
- pwm = byte.Parse(pwmM106Regex.Groups[1].Value);
+ currentHeight = Layer.RoundHeight(currentHeight + liftHeightTemp);
}
+ }
+
+ if (pwmM106Regex.Success)
+ {
+ pwm = byte.Parse(pwmM106Regex.Groups[1].Value);
+ }
- LayersSettings[layerIndex].LayerFileIndex = layerFileIndex;
- LayersSettings[layerIndex].LayerEntry = inputFile.GetEntry(layerimagePath);
+ LayersSettings[layerIndex].LayerFileIndex = layerFileIndex;
+ LayersSettings[layerIndex].LayerEntry = inputFile.GetEntry(layerimagePath);
- if (DecodeType == FileDecodeType.Full)
- {
- using var stream = LayersSettings[layerIndex].LayerEntry.Open();
- this[layerIndex] = new Layer((uint)layerIndex, stream, LayerManager);
- }
+ if (DecodeType == FileDecodeType.Full)
+ {
+ using var stream = LayersSettings[layerIndex].LayerEntry!.Open();
+ this[layerIndex] = new Layer((uint)layerIndex, stream, this);
+ }
- this[layerIndex].PositionZ = currentHeight;
- this[layerIndex].LiftHeight = liftHeight;
- this[layerIndex].LiftSpeed = liftSpeed;
- this[layerIndex].RetractSpeed = retractSpeed;
- this[layerIndex].LightPWM = pwm;
- layerIndex++;
+ this[layerIndex].PositionZ = currentHeight;
+ this[layerIndex].LiftHeight = liftHeight;
+ this[layerIndex].LiftSpeed = liftSpeed;
+ this[layerIndex].RetractSpeed = retractSpeed;
+ this[layerIndex].LightPWM = pwm;
+ layerIndex++;
- progress++;
- }
+ progress++;
}
-
- tr.Close();
}
- entry = inputFile.GetEntry("Preview.png");
- if (entry is not null)
- {
- using var stream = entry.Open();
- CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
- stream.Close();
- }
+ tr.Close();
}
- BottomRetractSpeed = RetractSpeed; // Compability
- LayerManager.GetBoundingRectangle(progress);
+ entry = inputFile.GetEntry("Preview.png");
+ if (entry is not null)
+ {
+ using var stream = entry.Open();
+ CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, Thumbnails[0]);
+ stream.Close();
+ }
}
- public override void RebuildGCode()
- {
- var gcode = GCodeStr;
- gcode = Regex.Replace(gcode, @"Z[+]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)",
- $"Z{UserSettings.ZLiftDistance} F{UserSettings.ZLiftFeedRate}");
+ BottomRetractSpeed = RetractSpeed; // Compability
+ GetBoundingRectangle(progress);
+ }
- gcode = Regex.Replace(gcode, @"Z-[-]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)",
- $"Z-{UserSettings.ZLiftDistance - LayerHeight} F{UserSettings.ZLiftRetractRate}");
+ public override void RebuildGCode()
+ {
+ var gcode = GCodeStr!;
+ gcode = Regex.Replace(gcode, @"Z[+]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)",
+ $"Z{UserSettings.ZLiftDistance} F{UserSettings.ZLiftFeedRate}");
- GCode.Clear();
- GCode.Append(gcode);
+ gcode = Regex.Replace(gcode, @"Z-[-]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)",
+ $"Z-{UserSettings.ZLiftDistance - LayerHeight} F{UserSettings.ZLiftRetractRate}");
- RaisePropertyChanged(nameof(GCodeStr));
- }
+ GCode!.Clear();
+ GCode.Append(gcode);
- protected override void PartialSaveInternally(OperationProgress progress)
- {
- using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update);
- outputFile.PutFileContent("ResinMetadata", JsonConvert.SerializeObject(ResinMetadataSettings, Formatting.Indented), ZipArchiveMode.Update);
- outputFile.PutFileContent("UserSettingsData", JsonConvert.SerializeObject(UserSettings, Formatting.Indented), ZipArchiveMode.Update);
- outputFile.PutFileContent("ZCodeMetadata", JsonConvert.SerializeObject(ZCodeMetadataSettings, Formatting.Indented), ZipArchiveMode.Update);
- outputFile.PutFileContent("ResinGCodeData", GCodeStr, ZipArchiveMode.Update);
- }
+ RaisePropertyChanged(nameof(GCodeStr));
+ }
- #endregion
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = ZipFile.Open(FileFullPath!, ZipArchiveMode.Update);
+ outputFile.PutFileContent("ResinMetadata", JsonSerializer.SerializeToUtf8Bytes(ResinMetadataSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Update);
+ outputFile.PutFileContent("UserSettingsData", JsonSerializer.SerializeToUtf8Bytes(UserSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Update);
+ outputFile.PutFileContent("ZCodeMetadata", JsonSerializer.SerializeToUtf8Bytes(ZCodeMetadataSettings, JsonExtensions.SettingsIndent), ZipArchiveMode.Update);
+ outputFile.PutFileContent("ResinGCodeData", GCodeStr, ZipArchiveMode.Update);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs
index b1e9cd3..d56d9cd 100644
--- a/UVtools.Core/GCode/GCodeBuilder.cs
+++ b/UVtools.Core/GCode/GCodeBuilder.cs
@@ -22,1133 +22,1132 @@ using UVtools.Core.Layers;
using UVtools.Core.Objects;
using UVtools.Core.Operations;
-namespace UVtools.Core.GCode
+namespace UVtools.Core.GCode;
+
+public class GCodeBuilder : BindableBase
{
- public class GCodeBuilder : BindableBase
- {
- #region Commands
+ #region Commands
- public GCodeCommand CommandUnitsMillimetersG21 { get; } = new("G21", null, "Set units to be mm");
- public GCodeCommand CommandPositioningAbsoluteG90 { get; } = new("G90", null, "Absolute positioning");
- public GCodeCommand CommandPositioningPartialG91 { get; } = new("G91", null, "Partial positioning");
+ public GCodeCommand CommandUnitsMillimetersG21 { get; } = new("G21", null, "Set units to be mm");
+ public GCodeCommand CommandPositioningAbsoluteG90 { get; } = new("G90", null, "Absolute positioning");
+ public GCodeCommand CommandPositioningPartialG91 { get; } = new("G91", null, "Partial positioning");
- public GCodeCommand CommandMotorsOnM17 { get; } = new("M17", null, "Enable motors");
- public GCodeCommand CommandMotorsOffM18 { get; } = new("M18", null, "Disable motors");
+ public GCodeCommand CommandMotorsOnM17 { get; } = new("M17", null, "Enable motors");
+ public GCodeCommand CommandMotorsOffM18 { get; } = new("M18", null, "Disable motors");
- public GCodeCommand CommandHomeG28 { get; } = new("G28", "Z0", "Home Z");
+ public GCodeCommand CommandHomeG28 { get; } = new("G28", "Z0", "Home Z");
- public GCodeCommand CommandMoveG0 { get; } = new("G0", "Z{0} F{1}", "Move Z");
- public GCodeCommand CommandMoveG1 { get; } = new("G1", "Z{0} F{1}", "Move Z");
+ public GCodeCommand CommandMoveG0 { get; } = new("G0", "Z{0} F{1}", "Move Z");
+ public GCodeCommand CommandMoveG1 { get; } = new("G1", "Z{0} F{1}", "Move Z");
- public GCodeCommand CommandWaitG4 { get; } = new("G4", "P{0}", "Delay");
- public GCodeCommand CommandShowImageM6054 = new("M6054", "\"{0}\"", "Show image");
- public GCodeCommand CommandClearImage = new(";<Slice> Blank"); // Clear image
- public GCodeCommand CommandTurnLEDM106 { get; } = new("M106", "S{0}", "Turn LED");
- #endregion
+ public GCodeCommand CommandWaitG4 { get; } = new("G4", "P{0}", "Delay");
+ public GCodeCommand CommandShowImageM6054 = new("M6054", "\"{0}\"", "Show image");
+ public GCodeCommand CommandClearImage = new(";<Slice> Blank"); // Clear image
+ public GCodeCommand CommandTurnLEDM106 { get; } = new("M106", "S{0}", "Turn LED");
+ #endregion
- #region Enums
+ #region Enums
- public enum GCodePositioningTypes : byte
- {
- Absolute,
- Partial
- }
+ public enum GCodePositioningTypes : byte
+ {
+ Absolute,
+ Partial
+ }
- public enum GCodeTimeUnits : byte
- {
- /// <summary>
- /// ms
- /// </summary>
- Milliseconds,
- /// <summary>
- /// s
- /// </summary>
- Seconds
- }
+ public enum GCodeTimeUnits : byte
+ {
+ /// <summary>
+ /// ms
+ /// </summary>
+ Milliseconds,
+ /// <summary>
+ /// s
+ /// </summary>
+ Seconds
+ }
- public enum GCodeSpeedUnits : byte
- {
- /// <summary>
- /// mm/s
- /// </summary>
- MillimetersPerSecond,
- /// <summary>
- /// mm/m
- /// </summary>
- MillimetersPerMinute,
- /// <summary>
- /// cm/m
- /// </summary>
- CentimetersPerMinute,
- }
+ public enum GCodeSpeedUnits : byte
+ {
+ /// <summary>
+ /// mm/s
+ /// </summary>
+ MillimetersPerSecond,
+ /// <summary>
+ /// mm/m
+ /// </summary>
+ MillimetersPerMinute,
+ /// <summary>
+ /// cm/m
+ /// </summary>
+ CentimetersPerMinute,
+ }
- public enum GCodeMoveCommands : byte
- {
- G0, // Fast
- G1 // Interpolated
- }
+ public enum GCodeMoveCommands : byte
+ {
+ G0, // Fast
+ G1 // Interpolated
+ }
- public enum GCodeShowImageTypes : byte
- {
- FilenamePng0Started,
- FilenamePng1Started,
- LayerIndex0Started,
- LayerIndex1Started,
- }
- #endregion
+ public enum GCodeShowImageTypes : byte
+ {
+ FilenamePng0Started,
+ FilenamePng1Started,
+ LayerIndex0Started,
+ LayerIndex1Started,
+ }
+ #endregion
- #region Members
- private readonly StringBuilder _gcode = new();
+ #region Members
+ private readonly StringBuilder _gcode = new();
- private GCodePositioningTypes _gCodePositioningType = GCodePositioningTypes.Absolute;
- private GCodeTimeUnits _gCodeTimeUnit = GCodeTimeUnits.Milliseconds;
- private GCodeSpeedUnits _gCodeSpeedUnit = GCodeSpeedUnits.MillimetersPerMinute;
- private GCodeShowImageTypes _gCodeShowImageType = GCodeShowImageTypes.FilenamePng1Started;
- private bool _syncMovementsWithDelay;
- private bool _useTailComma = true;
- private bool _useComments = true;
- private ushort _maxLedPower = byte.MaxValue;
- private uint _lineCount;
- private GCodeMoveCommands _layerMoveCommand;
- private GCodeMoveCommands _endGCodeMoveCommand;
-
- #endregion
-
- #region Properties
-
- public GCodePositioningTypes GCodePositioningType
- {
- get => _gCodePositioningType;
- set => RaiseAndSetIfChanged(ref _gCodePositioningType, value);
- }
+ private GCodePositioningTypes _gCodePositioningType = GCodePositioningTypes.Absolute;
+ private GCodeTimeUnits _gCodeTimeUnit = GCodeTimeUnits.Milliseconds;
+ private GCodeSpeedUnits _gCodeSpeedUnit = GCodeSpeedUnits.MillimetersPerMinute;
+ private GCodeShowImageTypes _gCodeShowImageType = GCodeShowImageTypes.FilenamePng1Started;
+ private bool _syncMovementsWithDelay;
+ private bool _useTailComma = true;
+ private bool _useComments = true;
+ private ushort _maxLedPower = byte.MaxValue;
+ private uint _lineCount;
+ private GCodeMoveCommands _layerMoveCommand;
+ private GCodeMoveCommands _endGCodeMoveCommand;
- public GCodeTimeUnits GCodeTimeUnit
- {
- get => _gCodeTimeUnit;
- set => RaiseAndSetIfChanged(ref _gCodeTimeUnit, value);
- }
+ #endregion
- public GCodeSpeedUnits GCodeSpeedUnit
- {
- get => _gCodeSpeedUnit;
- set => RaiseAndSetIfChanged(ref _gCodeSpeedUnit, value);
- }
+ #region Properties
- public GCodeShowImageTypes GCodeShowImageType
- {
- get => _gCodeShowImageType;
- set => RaiseAndSetIfChanged(ref _gCodeShowImageType, value);
- }
+ public GCodePositioningTypes GCodePositioningType
+ {
+ get => _gCodePositioningType;
+ set => RaiseAndSetIfChanged(ref _gCodePositioningType, value);
+ }
- public GCodeMoveCommands LayerMoveCommand
- {
- get => _layerMoveCommand;
- set => RaiseAndSetIfChanged(ref _layerMoveCommand, value);
- }
+ public GCodeTimeUnits GCodeTimeUnit
+ {
+ get => _gCodeTimeUnit;
+ set => RaiseAndSetIfChanged(ref _gCodeTimeUnit, value);
+ }
- public GCodeMoveCommands EndGCodeMoveCommand
- {
- get => _endGCodeMoveCommand;
- set => RaiseAndSetIfChanged(ref _endGCodeMoveCommand, value);
- }
+ public GCodeSpeedUnits GCodeSpeedUnit
+ {
+ get => _gCodeSpeedUnit;
+ set => RaiseAndSetIfChanged(ref _gCodeSpeedUnit, value);
+ }
- public bool SyncMovementsWithDelay
- {
- get => _syncMovementsWithDelay;
- set => RaiseAndSetIfChanged(ref _syncMovementsWithDelay, value);
- }
+ public GCodeShowImageTypes GCodeShowImageType
+ {
+ get => _gCodeShowImageType;
+ set => RaiseAndSetIfChanged(ref _gCodeShowImageType, value);
+ }
- public bool UseTailComma
- {
- get => _useTailComma;
- set => RaiseAndSetIfChanged(ref _useTailComma, value);
- }
+ public GCodeMoveCommands LayerMoveCommand
+ {
+ get => _layerMoveCommand;
+ set => RaiseAndSetIfChanged(ref _layerMoveCommand, value);
+ }
- public bool UseComments
- {
- get => _useComments;
- set => RaiseAndSetIfChanged(ref _useComments, value);
- }
+ public GCodeMoveCommands EndGCodeMoveCommand
+ {
+ get => _endGCodeMoveCommand;
+ set => RaiseAndSetIfChanged(ref _endGCodeMoveCommand, value);
+ }
- public ushort MaxLEDPower
- {
- get => _maxLedPower;
- set => RaiseAndSetIfChanged(ref _maxLedPower, value);
- }
+ public bool SyncMovementsWithDelay
+ {
+ get => _syncMovementsWithDelay;
+ set => RaiseAndSetIfChanged(ref _syncMovementsWithDelay, value);
+ }
- public string BeginStartGCodeComments { get; set; } = ";START_GCODE_BEGIN";
- public string EndStartGCodeComments { get; set; } = ";END_GCODE_BEGIN";
+ public bool UseTailComma
+ {
+ get => _useTailComma;
+ set => RaiseAndSetIfChanged(ref _useTailComma, value);
+ }
- public string BeginLayerComments { get; set; } = ";LAYER_START:{0}" + Environment.NewLine +
- ";PositionZ:{1}mm";
+ public bool UseComments
+ {
+ get => _useComments;
+ set => RaiseAndSetIfChanged(ref _useComments, value);
+ }
- public string EndLayerComments { get; set; } = ";LAYER_END";
+ public ushort MaxLEDPower
+ {
+ get => _maxLedPower;
+ set => RaiseAndSetIfChanged(ref _maxLedPower, value);
+ }
+
+ public string BeginStartGCodeComments { get; set; } = ";START_GCODE_BEGIN";
+ public string EndStartGCodeComments { get; set; } = ";END_GCODE_BEGIN";
+
+ public string BeginLayerComments { get; set; } = ";LAYER_START:{0}" + Environment.NewLine +
+ ";PositionZ:{1}mm";
+
+ public string EndLayerComments { get; set; } = ";LAYER_END";
- public string BeginEndGCodeComments { get; set; } = ";START_GCODE_END";
- public string EndEndGCodeComments { get; set; } = ";END_GCODE_END" + Environment.NewLine +
- ";<Completed>";
+ public string BeginEndGCodeComments { get; set; } = ";START_GCODE_END";
+ public string EndEndGCodeComments { get; set; } = ";END_GCODE_END" + Environment.NewLine +
+ ";<Completed>";
- public uint LineCount
+ public uint LineCount
+ {
+ get => _lineCount;
+ set
{
- get => _lineCount;
- set
- {
- if(!RaiseAndSetIfChanged(ref _lineCount, value)) return;
- //RaisePropertyChanged(nameof(IsEmpty));
- }
+ if(!RaiseAndSetIfChanged(ref _lineCount, value)) return;
+ //RaisePropertyChanged(nameof(IsEmpty));
}
+ }
- public bool IsEmpty => _lineCount <= 0 || _gcode.Length <= 0;
- public int Length => _gcode.Length;
+ public bool IsEmpty => _lineCount <= 0 || _gcode.Length <= 0;
+ public int Length => _gcode.Length;
- #endregion
+ #endregion
- #region StringBuilder
+ #region StringBuilder
- public void Append(string text)
- {
- _gcode.Append(text);
- }
+ public void Append(string text)
+ {
+ _gcode.Append(text);
+ }
- public void Append(StringBuilder sb)
- {
- _gcode.Append(sb);
- }
+ public void Append(StringBuilder sb)
+ {
+ _gcode.Append(sb);
+ }
- public void AppendLine()
- {
- _gcode.AppendLine();
- //LineCount++;
- }
+ public void AppendLine()
+ {
+ _gcode.AppendLine();
+ //LineCount++;
+ }
- public void AppendLine(string line)
- {
- if (line is null) return;
- _gcode.AppendLine(line);
- LineCount++;
- }
+ public void AppendLine(string line)
+ {
+ if (line is null) return;
+ _gcode.AppendLine(line);
+ LineCount++;
+ }
- public void AppendLine(string line, params object[] args)
- {
- if (line is null) return;
- _gcode.AppendLine(string.Format(line, args));
- LineCount++;
- }
+ public void AppendLine(string line, params object[] args)
+ {
+ if (line is null) return;
+ _gcode.AppendLine(string.Format(line, args));
+ LineCount++;
+ }
- public void AppendLine(GCodeCommand command)
- {
- if (!command.Enabled) return;
- AppendLine(command.ToString(_useComments, _useTailComma));
- LineCount++;
- }
+ public void AppendLine(GCodeCommand command)
+ {
+ if (!command.Enabled) return;
+ AppendLine(command.ToString(_useComments, _useTailComma));
+ LineCount++;
+ }
- public void AppendLineOverrideComment(GCodeCommand command, string comment, params object[] args)
- {
- if (!command.Enabled) return;
- AppendLine(command.ToStringOverrideComment(_useComments, _useTailComma, comment, args));
- LineCount++;
- }
+ public void AppendLineOverrideComment(GCodeCommand command, string? comment, params object[] args)
+ {
+ if (!command.Enabled) return;
+ AppendLine(command.ToStringOverrideComment(_useComments, _useTailComma, comment, args));
+ LineCount++;
+ }
- public void AppendLine(GCodeCommand command, params object[] args)
- {
- if (!command.Enabled) return;
- AppendLine(command.ToString(_useComments, _useTailComma, args));
- LineCount++;
- }
+ public void AppendLine(GCodeCommand command, params object[] args)
+ {
+ if (!command.Enabled) return;
+ AppendLine(command.ToString(_useComments, _useTailComma, args));
+ LineCount++;
+ }
- public void AppendFormat(string format, params object[] args)
- {
- _gcode.AppendFormat(format, args);
- LineCount += (uint)format.Count(c => c == '\n');
- }
+ public void AppendFormat(string format, params object[] args)
+ {
+ _gcode.AppendFormat(format, args);
+ LineCount += (uint)format.Count(c => c == '\n');
+ }
- public void AppendLineIfCanComment(string line)
- {
- if (string.IsNullOrWhiteSpace(line) || !_useComments) return;
- AppendLine(line);
- }
+ public void AppendLineIfCanComment(string line)
+ {
+ if (string.IsNullOrWhiteSpace(line) || !_useComments) return;
+ AppendLine(line);
+ }
- public void AppendLineIfCanComment(string line, params object[] args)
- {
- if (string.IsNullOrWhiteSpace(line) || !_useComments) return;
- AppendLine(line, args);
- }
+ public void AppendLineIfCanComment(string line, params object[] args)
+ {
+ if (string.IsNullOrWhiteSpace(line) || !_useComments) return;
+ AppendLine(line, args);
+ }
+
+ public void AppendComment(string comment)
+ {
+ if (string.IsNullOrWhiteSpace(comment) || !_useComments) return;
+ AppendLine($";{comment}");
+ }
- public void AppendComment(string comment)
+ public void Clear()
+ {
+ _gcode.Clear();
+ LineCount = 0;
+ }
+
+ public override string ToString() => _gcode.ToString();
+ #endregion
+
+ #region Methods
+
+ public string FormatGCodeLine(string line, string? comment = null)
+ {
+ if (line[0] == ';') return line;
+ if (_useComments && !string.IsNullOrWhiteSpace(comment))
{
- if (string.IsNullOrWhiteSpace(comment) || !_useComments) return;
- AppendLine($";{comment}");
+ line += $";{comment}";
}
-
- public void Clear()
+ else if (_useTailComma)
{
- _gcode.Clear();
- LineCount = 0;
+ line += ';';
}
- public override string ToString() => _gcode.ToString();
- #endregion
-
- #region Methods
+ return line;
+ }
- public string FormatGCodeLine(string line, string comment = null)
- {
- if (line[0] == ';') return line;
- if (_useComments && !string.IsNullOrWhiteSpace(comment))
- {
- line += $";{comment}";
- }
- else if (_useTailComma)
- {
- line += ';';
- }
+ public void AppendUVtools()
+ {
+ AppendComment($"Generated by {About.Software} v{About.VersionStr} {About.Arch} @ {DateTime.UtcNow}");
+ }
- return line;
- }
+ public void AppendStartGCode()
+ {
+ AppendLineIfCanComment(BeginStartGCodeComments);
+ AppendUnitsMmG21();
+ AppendPositioningType();
+ AppendLightOffM106();
+ AppendMotorsOn();
+ AppendClearImage();
+ AppendHomeZG28();
+ AppendLineIfCanComment(EndStartGCodeComments);
+ AppendLine();
+ }
- public void AppendUVtools()
+ public void AppendEndGCode(float raiseZ = 0, float feedRate = 0)
+ {
+ AppendLineIfCanComment(BeginEndGCodeComments);
+ AppendLightOffM106();
+ if (raiseZ > 0)
{
- AppendComment($"Generated by {About.Software} v{About.VersionStr} {About.Arch} @ {DateTime.UtcNow}");
+ if (_endGCodeMoveCommand == GCodeMoveCommands.G0)
+ AppendMoveG0(raiseZ, feedRate);
+ else
+ AppendMoveG1(raiseZ, feedRate);
}
- public void AppendStartGCode()
+ AppendMotorsOff();
+ AppendLineIfCanComment(EndEndGCodeComments);
+ }
+
+ public void AppendUnitsMmG21()
+ {
+ AppendLine(CommandUnitsMillimetersG21);
+ }
+
+ public void AppendPositioningType()
+ {
+ switch (GCodePositioningType)
{
- AppendLineIfCanComment(BeginStartGCodeComments);
- AppendUnitsMmG21();
- AppendPositioningType();
- AppendLightOffM106();
- AppendMotorsOn();
- AppendClearImage();
- AppendHomeZG28();
- AppendLineIfCanComment(EndStartGCodeComments);
- AppendLine();
+ case GCodePositioningTypes.Absolute:
+ AppendLine(CommandPositioningAbsoluteG90);
+ break;
+ case GCodePositioningTypes.Partial:
+ AppendLine(CommandPositioningPartialG91);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
}
+ }
- public void AppendEndGCode(float raiseZ = 0, float feedRate = 0)
- {
- AppendLineIfCanComment(BeginEndGCodeComments);
- AppendLightOffM106();
- if (raiseZ > 0)
- {
- if (_endGCodeMoveCommand == GCodeMoveCommands.G0)
- AppendMoveG0(raiseZ, feedRate);
- else
- AppendMoveG1(raiseZ, feedRate);
- }
+ public void AppendMotorsOn()
+ {
+ AppendLine(CommandMotorsOnM17);
+ }
+ public void AppendMotorsOff()
+ {
+ AppendLine(CommandMotorsOffM18);
+ }
+
+ public void AppendTurnMotors(bool enable)
+ {
+ if (enable)
+ AppendMotorsOn();
+ else
AppendMotorsOff();
- AppendLineIfCanComment(EndEndGCodeComments);
- }
+ }
- public void AppendUnitsMmG21()
- {
- AppendLine(CommandUnitsMillimetersG21);
- }
+ public void AppendHomeZG28()
+ {
+ AppendLine(CommandHomeG28);
+ }
- public void AppendPositioningType()
- {
- switch (GCodePositioningType)
- {
- case GCodePositioningTypes.Absolute:
- AppendLine(CommandPositioningAbsoluteG90);
- break;
- case GCodePositioningTypes.Partial:
- AppendLine(CommandPositioningPartialG91);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
+ public void AppendMoveGx(float z, float feedRate)
+ {
+ if(_layerMoveCommand == GCodeMoveCommands.G0)
+ AppendMoveG0(z, feedRate);
+ else
+ AppendMoveG1(z, feedRate);
+ }
- public void AppendMotorsOn()
- {
- AppendLine(CommandMotorsOnM17);
- }
+ public void AppendLiftMoveGx(List<(float z, float feedrate)> lifts, List<(float z, float feedrate)> retracts, float waitAfterLift = 0, float waitAfterRetract = 0, Layer? layer = null)
+ {
+ if (_layerMoveCommand == GCodeMoveCommands.G0)
+ AppendLiftMoveG0(lifts, retracts, waitAfterLift, waitAfterRetract, layer);
+ else
+ AppendLiftMoveG1(lifts, retracts, waitAfterLift, waitAfterRetract, layer);
+ }
- public void AppendMotorsOff()
- {
- AppendLine(CommandMotorsOffM18);
- }
- public void AppendTurnMotors(bool enable)
- {
- if (enable)
- AppendMotorsOn();
- else
- AppendMotorsOff();
- }
+ public void AppendMoveG0(float z, float feedRate)
+ {
+ if(z == 0 || feedRate <= 0) return;
+ AppendLine(CommandMoveG0, z, feedRate);
+ }
- public void AppendHomeZG28()
- {
- AppendLine(CommandHomeG28);
- }
+ public void AppendLiftMoveG0(List<(float z, float feedrate)> lifts, List<(float z, float feedrate)> retracts, float waitAfterLift = 0, float waitAfterRetract = 0, Layer? layer = null)
+ {
+ if (lifts.Count == 0 || lifts.All(tuple => tuple.z <= 0)) return;
- public void AppendMoveGx(float z, float feedRate)
+ for (var i = 0; i < lifts.Count; i++)
{
- if(_layerMoveCommand == GCodeMoveCommands.G0)
- AppendMoveG0(z, feedRate);
- else
- AppendMoveG1(z, feedRate);
+ if (lifts[i].z <= 0) continue;
+ AppendLineOverrideComment(CommandMoveG0, $"Z Lift ({i+1})", lifts[i].z, lifts[i].feedrate); // Z Lift
}
- public void AppendLiftMoveGx(List<(float z, float feedrate)> lifts, List<(float z, float feedrate)> retracts, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null)
+ if (_syncMovementsWithDelay && layer is not null)
{
- if (_layerMoveCommand == GCodeMoveCommands.G0)
- AppendLiftMoveG0(lifts, retracts, waitAfterLift, waitAfterRetract, layer);
- else
- AppendLiftMoveG1(lifts, retracts, waitAfterLift, waitAfterRetract, layer);
+ var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f);
+ var time = ConvertFromSeconds(seconds);
+ AppendWaitG4($"0{time}", "Sync movement");
}
-
- public void AppendMoveG0(float z, float feedRate)
+ if (waitAfterLift > 0)
{
- if(z == 0 || feedRate <= 0) return;
- AppendLine(CommandMoveG0, z, feedRate);
+ AppendWaitG4(waitAfterLift, "Wait after lift");
}
- public void AppendLiftMoveG0(List<(float z, float feedrate)> lifts, List<(float z, float feedrate)> retracts, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null)
+ if (retracts.Count > 0 || retracts.All(tuple => tuple.z != 0))
{
- if (lifts.Count == 0 || lifts.All(tuple => tuple.z <= 0)) return;
-
- for (var i = 0; i < lifts.Count; i++)
+ for (var i = 0; i < retracts.Count; i++)
{
- if (lifts[i].z <= 0) continue;
- AppendLineOverrideComment(CommandMoveG0, $"Z Lift ({i+1})", lifts[i].z, lifts[i].feedrate); // Z Lift
+ if (retracts[i].z == 0) continue;
+ AppendLineOverrideComment(CommandMoveG0, $"Retract ({i+1})", retracts[i].z, retracts[i].feedrate);
}
if (_syncMovementsWithDelay && layer is not null)
{
- var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f);
+ var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f);
var time = ConvertFromSeconds(seconds);
AppendWaitG4($"0{time}", "Sync movement");
}
+ }
- if (waitAfterLift > 0)
- {
- AppendWaitG4(waitAfterLift, "Wait after lift");
- }
+ if (waitAfterRetract > 0)
+ {
+ AppendWaitG4(waitAfterRetract, "Wait after retract");
+ }
+ }
- if (retracts.Count > 0 || retracts.All(tuple => tuple.z != 0))
- {
- for (var i = 0; i < retracts.Count; i++)
- {
- if (retracts[i].z == 0) continue;
- AppendLineOverrideComment(CommandMoveG0, $"Retract ({i+1})", retracts[i].z, retracts[i].feedrate);
- }
+ public void AppendLiftMoveG0(float upZ, float upFeedrate, float downZ, float downFeedrate,
+ float waitAfterLift = 0, float waitAfterRetract = 0, Layer? layer = null)
+ => AppendLiftMoveG0(
+ new List<(float, float)> { new(upZ, upFeedrate) },
+ new List<(float, float)> { new(downZ, downFeedrate) },
+ waitAfterLift, waitAfterRetract, layer);
- if (_syncMovementsWithDelay && layer is not null)
- {
- var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f);
- var time = ConvertFromSeconds(seconds);
- AppendWaitG4($"0{time}", "Sync movement");
- }
- }
+ public void AppendMoveG1(float z, float feedRate)
+ {
+ if (z == 0 || feedRate <= 0) return;
+ AppendLine(CommandMoveG1, z, feedRate);
+ }
- if (waitAfterRetract > 0)
- {
- AppendWaitG4(waitAfterRetract, "Wait after retract");
- }
- }
+ public void AppendLiftMoveG1(List<(float z, float feedrate)> lifts, List<(float z, float feedrate)> retracts, float waitAfterLift = 0, float waitAfterRetract = 0, Layer? layer = null)
+ {
+ if (lifts.Count == 0 || lifts.All(tuple => tuple.z <= 0)) return;
- public void AppendLiftMoveG0(float upZ, float upFeedrate, float downZ, float downFeedrate,
- float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null)
- => AppendLiftMoveG0(
- new List<(float, float)> { new(upZ, upFeedrate) },
- new List<(float, float)> { new(downZ, downFeedrate) },
- waitAfterLift, waitAfterRetract, layer);
+ for (var i = 0; i < lifts.Count; i++)
+ {
+ if (lifts[i].z <= 0) continue;
+ AppendLineOverrideComment(CommandMoveG1, $"Z Lift ({i+1})", lifts[i].z, lifts[i].feedrate); // Z Lift
+ }
- public void AppendMoveG1(float z, float feedRate)
+ if (_syncMovementsWithDelay && layer is not null)
{
- if (z == 0 || feedRate <= 0) return;
- AppendLine(CommandMoveG1, z, feedRate);
+ var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f);
+ var time = ConvertFromSeconds(seconds);
+ AppendWaitG4($"0{time}", "Sync movement");
}
- public void AppendLiftMoveG1(List<(float z, float feedrate)> lifts, List<(float z, float feedrate)> retracts, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null)
+ if (waitAfterLift > 0)
{
- if (lifts.Count == 0 || lifts.All(tuple => tuple.z <= 0)) return;
+ AppendWaitG4(waitAfterLift, "Wait after lift");
+ }
- for (var i = 0; i < lifts.Count; i++)
+ if (retracts.Count > 0 || retracts.All(tuple => tuple.z != 0))
+ {
+ for (var i = 0; i < retracts.Count; i++)
{
- if (lifts[i].z <= 0) continue;
- AppendLineOverrideComment(CommandMoveG1, $"Z Lift ({i+1})", lifts[i].z, lifts[i].feedrate); // Z Lift
+ if (retracts[i].z == 0) continue;
+ AppendLineOverrideComment(CommandMoveG1, $"Retract ({i+1})", retracts[i].z, retracts[i].feedrate);
}
if (_syncMovementsWithDelay && layer is not null)
{
- var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f);
+ var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f);
var time = ConvertFromSeconds(seconds);
AppendWaitG4($"0{time}", "Sync movement");
}
+ }
- if (waitAfterLift > 0)
- {
- AppendWaitG4(waitAfterLift, "Wait after lift");
- }
+ if (waitAfterRetract > 0)
+ {
+ AppendWaitG4(waitAfterRetract, "Wait after retract");
+ }
+ }
- if (retracts.Count > 0 || retracts.All(tuple => tuple.z != 0))
- {
- for (var i = 0; i < retracts.Count; i++)
- {
- if (retracts[i].z == 0) continue;
- AppendLineOverrideComment(CommandMoveG1, $"Retract ({i+1})", retracts[i].z, retracts[i].feedrate);
- }
+ public void AppendLiftMoveG1(float upZ, float upFeedrate, float downZ, float downFeedrate,
+ float waitAfterLift = 0, float waitAfterRetract = 0, Layer? layer = null)
+ => AppendLiftMoveG0(
+ new List<(float, float)> { new(upZ, upFeedrate) },
+ new List<(float, float)> { new(downZ, downFeedrate) },
+ waitAfterLift, waitAfterRetract, layer);
- if (_syncMovementsWithDelay && layer is not null)
- {
- var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f);
- var time = ConvertFromSeconds(seconds);
- AppendWaitG4($"0{time}", "Sync movement");
- }
- }
+ public void AppendWaitG4(float time, string? comment = null)
+ {
+ if (time < 0) return;
+ AppendLineOverrideComment(CommandWaitG4, comment, time);
+ }
- if (waitAfterRetract > 0)
- {
- AppendWaitG4(waitAfterRetract, "Wait after retract");
- }
- }
+ public void AppendWaitG4(string timeStr, string? comment = null)
+ {
+ if (!float.TryParse(timeStr, out var time)) return;
+ if (time < 0) return;
+ AppendLineOverrideComment(CommandWaitG4, comment, timeStr);
+ }
- public void AppendLiftMoveG1(float upZ, float upFeedrate, float downZ, float downFeedrate,
- float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null)
- => AppendLiftMoveG0(
- new List<(float, float)> { new(upZ, upFeedrate) },
- new List<(float, float)> { new(downZ, downFeedrate) },
- waitAfterLift, waitAfterRetract, layer);
+ public void AppendTurnLightM106(ushort value)
+ {
+ AppendLineOverrideComment(CommandTurnLEDM106, "Turn LED " + (value == 0 ? "OFF" : "ON"), value);
+ }
- public void AppendWaitG4(float time, string comment = null)
- {
- if (time < 0) return;
- AppendLineOverrideComment(CommandWaitG4, comment, time);
- }
+ public void AppendLightOffM106() => AppendTurnLightM106(0);
+ public void AppendLightFullM106() => AppendTurnLightM106(_maxLedPower);
- public void AppendWaitG4(string timeStr, string comment = null)
- {
- if (!float.TryParse(timeStr, out var time)) return;
- if (time < 0) return;
- AppendLineOverrideComment(CommandWaitG4, comment, timeStr);
- }
+ public void AppendClearImage()
+ {
+ AppendLine(CommandClearImage);
+ }
- public void AppendTurnLightM106(ushort value)
- {
- AppendLineOverrideComment(CommandTurnLEDM106, "Turn LED " + (value == 0 ? "OFF" : "ON"), value);
- }
+ public void AppendExposure(float time, ushort pwmValue = 255)
+ {
+ if (pwmValue <= 0 || time <= 0) return;
- public void AppendLightOffM106() => AppendTurnLightM106(0);
- public void AppendLightFullM106() => AppendTurnLightM106(_maxLedPower);
+ AppendTurnLightM106(pwmValue);
+ AppendWaitG4(time, "Cure time/delay");
+ AppendLightOffM106();
+ AppendClearImage();
+ }
- public void AppendClearImage()
- {
- AppendLine(CommandClearImage);
- }
+ public void AppendShowImageM6054(string filename)
+ {
+ AppendLine(CommandShowImageM6054, filename);
+ }
- public void AppendExposure(float time, ushort pwmValue = 255)
- {
- if (pwmValue <= 0 || time <= 0) return;
+ public void AppendShowImageM6054(uint layerIndex)
+ {
+ AppendLine(CommandShowImageM6054, layerIndex);
+ }
- AppendTurnLightM106(pwmValue);
- AppendWaitG4(time, "Cure time/delay");
- AppendLightOffM106();
- AppendClearImage();
- }
+ public string GetShowImageString(uint layerIndex) => _gCodeShowImageType switch
+ {
+ GCodeShowImageTypes.FilenamePng0Started => $"{layerIndex}.png",
+ GCodeShowImageTypes.FilenamePng1Started => $"{layerIndex + 1}.png",
+ GCodeShowImageTypes.LayerIndex0Started => $"{layerIndex}",
+ GCodeShowImageTypes.LayerIndex1Started => $"{layerIndex + 1}",
+ _ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}")
+ };
+
+ public string GetShowImageString(string value) => _gCodeShowImageType switch
+ {
+ GCodeShowImageTypes.FilenamePng0Started => $"{value}.png",
+ GCodeShowImageTypes.FilenamePng1Started => $"{value}.png",
+ GCodeShowImageTypes.LayerIndex0Started => $"{value}",
+ GCodeShowImageTypes.LayerIndex1Started => $"{value}",
+ _ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}")
+ };
+
+ public void RebuildGCode(FileFormat slicerFile, StringBuilder? header) => RebuildGCode(slicerFile, header?.ToString());
+ public void RebuildGCode(FileFormat slicerFile, string? header = null)
+ {
+ Clear();
+ AppendUVtools();
- public void AppendShowImageM6054(string filename)
- {
- AppendLine(CommandShowImageM6054, filename);
- }
+ if (slicerFile.LayerCount == 0) return;
- public void AppendShowImageM6054(uint layerIndex)
+ if (!string.IsNullOrWhiteSpace(header))
{
- AppendLine(CommandShowImageM6054, layerIndex);
+ Append(header);
+ AppendLine();
}
- public string GetShowImageString(uint layerIndex) => _gCodeShowImageType switch
- {
- GCodeShowImageTypes.FilenamePng0Started => $"{layerIndex}.png",
- GCodeShowImageTypes.FilenamePng1Started => $"{layerIndex + 1}.png",
- GCodeShowImageTypes.LayerIndex0Started => $"{layerIndex}",
- GCodeShowImageTypes.LayerIndex1Started => $"{layerIndex + 1}",
- _ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}")
- };
+ AppendStartGCode();
- public string GetShowImageString(string value) => _gCodeShowImageType switch
- {
- GCodeShowImageTypes.FilenamePng0Started => $"{value}.png",
- GCodeShowImageTypes.FilenamePng1Started => $"{value}.png",
- GCodeShowImageTypes.LayerIndex0Started => $"{value}",
- GCodeShowImageTypes.LayerIndex1Started => $"{value}",
- _ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}")
- };
+ float lastZPosition = 0;
- public void RebuildGCode(FileFormat slicerFile, StringBuilder header) => RebuildGCode(slicerFile, header?.ToString());
- public void RebuildGCode(FileFormat slicerFile, string header = null)
+ // Defaults for: Absolute, mm/min and s
+ for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
{
- Clear();
- AppendUVtools();
+ var layer = slicerFile[layerIndex];
+
+ float waitBeforeCure = ConvertFromSeconds(layer.WaitTimeBeforeCure);
+ float exposureTime = ConvertFromSeconds(layer.ExposureTime);
+ float waitAfterCure = ConvertFromSeconds(layer.WaitTimeAfterCure);
+ float liftHeight = layer.LiftHeight;
+ //float liftZPos = Layer.RoundHeight(liftHeight + layer.PositionZ);
+ //float liftZPosAbs = liftZPos;
+ float liftSpeed = ConvertFromMillimetersPerMinute(layer.LiftSpeed);
+ float liftHeight2 = layer.LiftHeight2;
+ float liftSpeed2 = ConvertFromMillimetersPerMinute(layer.LiftSpeed2);
+ float waitAfterLift = ConvertFromSeconds(layer.WaitTimeAfterLift);
+ float retractHeight = layer.RetractHeight;
+ float retractSpeed = ConvertFromMillimetersPerMinute(layer.RetractSpeed);
+ float retractHeight2 = layer.RetractHeight2;
+ float retractSpeed2 = ConvertFromMillimetersPerMinute(layer.RetractSpeed2);
+ float liftHeightTotal = layer.LiftHeightTotal;
+ ushort pwmValue = layer.LightPWM;
+ if (_maxLedPower != byte.MaxValue)
+ {
+ pwmValue = (ushort)(_maxLedPower * pwmValue / byte.MaxValue);
+ }
- if (slicerFile.LayerCount == 0) return;
+ var lifts = new List<(float z, float feedrate)>();
+ var retracts = new List<(float z, float feedrate)>();
- if (!string.IsNullOrWhiteSpace(header))
+ switch (GCodePositioningType)
{
- Append(header);
- AppendLine();
- }
+ case GCodePositioningTypes.Absolute:
+ var absLiftPos = Layer.RoundHeight(liftHeight + layer.PositionZ);
+ var absLiftPos2 = Layer.RoundHeight(absLiftPos + liftHeight2);
+ var absRetractPos = Layer.RoundHeight(absLiftPos2 - retractHeight);
+ if (liftHeight > 0) lifts.Add((absLiftPos, liftSpeed));
+ if (liftHeight2 > 0) lifts.Add((absLiftPos2, liftSpeed2));
+ if (retractHeight > 0 && absRetractPos >= layer.PositionZ) retracts.Add((absRetractPos, retractSpeed));
+ if (retractHeight2 > 0 && absRetractPos > layer.PositionZ) retracts.Add((layer.PositionZ, retractSpeed2));
+ break;
+ case GCodePositioningTypes.Partial:
+ var partialLiftPos = Layer.RoundHeight(layer.PositionZ - lastZPosition + liftHeight);
+ if (liftHeight > 0)
+ {
+ lifts.Add((partialLiftPos, liftSpeed));
+ if (liftHeight2 > 0) lifts.Add((liftHeight2, liftSpeed2));
+ }
+ else
+ {
+ if (liftHeight2 > 0) lifts.Add((Layer.RoundHeight(partialLiftPos + liftHeight2), liftSpeed2));
+ }
- AppendStartGCode();
+ if (Layer.RoundHeight(retractHeight + retractHeight2) != liftHeightTotal) // Fail-safe
+ {
+ retracts.Add((-liftHeightTotal, retractSpeed));
+ }
+ else
+ {
+ if (retractHeight > 0) retracts.Add((-retractHeight, retractSpeed));
+ if (retractHeight2 > 0) retracts.Add((-retractHeight2, retractSpeed2));
+ }
- float lastZPosition = 0;
+ break;
+ }
- // Defaults for: Absolute, mm/min and s
- for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
- {
- var layer = slicerFile[layerIndex];
-
- float waitBeforeCure = ConvertFromSeconds(layer.WaitTimeBeforeCure);
- float exposureTime = ConvertFromSeconds(layer.ExposureTime);
- float waitAfterCure = ConvertFromSeconds(layer.WaitTimeAfterCure);
- float liftHeight = layer.LiftHeight;
- //float liftZPos = Layer.RoundHeight(liftHeight + layer.PositionZ);
- //float liftZPosAbs = liftZPos;
- float liftSpeed = ConvertFromMillimetersPerMinute(layer.LiftSpeed);
- float liftHeight2 = layer.LiftHeight2;
- float liftSpeed2 = ConvertFromMillimetersPerMinute(layer.LiftSpeed2);
- float waitAfterLift = ConvertFromSeconds(layer.WaitTimeAfterLift);
- float retractHeight = layer.RetractHeight;
- float retractSpeed = ConvertFromMillimetersPerMinute(layer.RetractSpeed);
- float retractHeight2 = layer.RetractHeight2;
- float retractSpeed2 = ConvertFromMillimetersPerMinute(layer.RetractSpeed2);
- float liftHeightTotal = layer.LiftHeightTotal;
- ushort pwmValue = layer.LightPWM;
- if (_maxLedPower != byte.MaxValue)
- {
- pwmValue = (ushort)(_maxLedPower * pwmValue / byte.MaxValue);
- }
+ AppendLineIfCanComment(BeginLayerComments, layerIndex, layer.PositionZ);
- var lifts = new List<(float z, float feedrate)>();
- var retracts = new List<(float z, float feedrate)>();
+ //if (layer.CanExpose)
+ //{ Dont check this for compability
+ AppendShowImageM6054(GetShowImageString(layerIndex));
+ //}
+ if (liftHeightTotal > 0 && Layer.RoundHeight(liftHeightTotal + layer.PositionZ) > layer.PositionZ)
+ {
+ AppendLiftMoveGx(lifts, retracts, waitAfterLift, 0, layer);
+ }
+ else if (lastZPosition != layer.PositionZ) // Ensure Z is on correct position
+ {
switch (GCodePositioningType)
{
case GCodePositioningTypes.Absolute:
- var absLiftPos = Layer.RoundHeight(liftHeight + layer.PositionZ);
- var absLiftPos2 = Layer.RoundHeight(absLiftPos + liftHeight2);
- var absRetractPos = Layer.RoundHeight(absLiftPos2 - retractHeight);
- if (liftHeight > 0) lifts.Add((absLiftPos, liftSpeed));
- if (liftHeight2 > 0) lifts.Add((absLiftPos2, liftSpeed2));
- if (retractHeight > 0 && absRetractPos >= layer.PositionZ) retracts.Add((absRetractPos, retractSpeed));
- if (retractHeight2 > 0 && absRetractPos > layer.PositionZ) retracts.Add((layer.PositionZ, retractSpeed2));
+ AppendMoveGx(layer.PositionZ, lastZPosition < layer.PositionZ ? liftSpeed : retractSpeed);
break;
case GCodePositioningTypes.Partial:
- var partialLiftPos = Layer.RoundHeight(layer.PositionZ - lastZPosition + liftHeight);
- if (liftHeight > 0)
- {
- lifts.Add((partialLiftPos, liftSpeed));
- if (liftHeight2 > 0) lifts.Add((liftHeight2, liftSpeed2));
- }
- else
- {
- if (liftHeight2 > 0) lifts.Add((Layer.RoundHeight(partialLiftPos + liftHeight2), liftSpeed2));
- }
-
- if (Layer.RoundHeight(retractHeight + retractHeight2) != liftHeightTotal) // Fail-safe
- {
- retracts.Add((-liftHeightTotal, retractSpeed));
- }
- else
- {
- if (retractHeight > 0) retracts.Add((-retractHeight, retractSpeed));
- if (retractHeight2 > 0) retracts.Add((-retractHeight2, retractSpeed2));
- }
-
+ AppendMoveGx(Layer.RoundHeight(layer.PositionZ - lastZPosition), lastZPosition < layer.PositionZ ? liftSpeed : retractSpeed);
break;
}
- AppendLineIfCanComment(BeginLayerComments, layerIndex, layer.PositionZ);
-
- //if (layer.CanExpose)
- //{ Dont check this for compability
- AppendShowImageM6054(GetShowImageString(layerIndex));
- //}
-
- if (liftHeightTotal > 0 && Layer.RoundHeight(liftHeightTotal + layer.PositionZ) > layer.PositionZ)
- {
- AppendLiftMoveGx(lifts, retracts, waitAfterLift, 0, layer);
- }
- else if (lastZPosition != layer.PositionZ) // Ensure Z is on correct position
- {
- switch (GCodePositioningType)
- {
- case GCodePositioningTypes.Absolute:
- AppendMoveGx(layer.PositionZ, lastZPosition < layer.PositionZ ? liftSpeed : retractSpeed);
- break;
- case GCodePositioningTypes.Partial:
- AppendMoveGx(Layer.RoundHeight(layer.PositionZ - lastZPosition), lastZPosition < layer.PositionZ ? liftSpeed : retractSpeed);
- break;
- }
-
- }
+ }
- AppendWaitG4(waitBeforeCure, "Wait before cure"); // Safer to parse if present
- AppendExposure(exposureTime, pwmValue);
- if(waitAfterCure > 0) AppendWaitG4(waitAfterCure, "Wait after cure");
+ AppendWaitG4(waitBeforeCure, "Wait before cure"); // Safer to parse if present
+ AppendExposure(exposureTime, pwmValue);
+ if(waitAfterCure > 0) AppendWaitG4(waitAfterCure, "Wait after cure");
- AppendLineIfCanComment(EndLayerComments, layerIndex, layer.PositionZ);
- AppendLine();
+ AppendLineIfCanComment(EndLayerComments, layerIndex, layer.PositionZ);
+ AppendLine();
- lastZPosition = layer.PositionZ;
- }
+ lastZPosition = layer.PositionZ;
+ }
- float finalRaiseZPosition = Math.Max(lastZPosition, slicerFile.MachineZ);
- switch (GCodePositioningType)
- {
+ float finalRaiseZPosition = Math.Max(lastZPosition, slicerFile.MachineZ);
+ switch (GCodePositioningType)
+ {
- case GCodePositioningTypes.Partial:
- finalRaiseZPosition = Layer.RoundHeight(finalRaiseZPosition - lastZPosition);
- break;
- }
+ case GCodePositioningTypes.Partial:
+ finalRaiseZPosition = Layer.RoundHeight(finalRaiseZPosition - lastZPosition);
+ break;
+ }
- AppendEndGCode(finalRaiseZPosition, ConvertFromMillimetersPerMinute(slicerFile.RetractSpeed));
- }
+ AppendEndGCode(finalRaiseZPosition, ConvertFromMillimetersPerMinute(slicerFile.RetractSpeed));
+ }
- public void RebuildGCode(FileFormat slicerFile, object[] configs, string separator = ":")
+ public void RebuildGCode(FileFormat slicerFile, object[]? configs, string separator = ":")
+ {
+ StringBuilder? sb = null;
+ if (configs is not null)
{
- StringBuilder sb = null;
- if (configs is not null)
+ sb = new StringBuilder();
+ foreach (var config in configs)
{
- sb = new StringBuilder();
- foreach (var config in configs)
+ foreach (var propertyInfo in config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
- foreach (var propertyInfo in config.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
+ string name;
+ if (displayNameAttribute is null)
{
- var displayNameAttribute = propertyInfo.GetCustomAttributes(false).OfType<DisplayNameAttribute>().FirstOrDefault();
- string name;
- if (displayNameAttribute is null)
- {
- name = propertyInfo.Name;
- if(name == "Item") continue;
- }
- else
- {
- name = displayNameAttribute.DisplayName;
- }
- sb.AppendLine($";{name}{separator}{propertyInfo.GetValue(config)}");
+ name = propertyInfo.Name;
+ if(name == "Item") continue;
}
+ else
+ {
+ name = displayNameAttribute.DisplayName;
+ }
+ sb.AppendLine($";{name}{separator}{propertyInfo.GetValue(config)}");
}
}
-
- RebuildGCode(slicerFile, sb);
}
- public GCodePositioningTypes ParsePositioningTypeFromGCode(string gcode)
- {
- using var reader = new StringReader(gcode);
- string line;
- while ((line = reader.ReadLine()) != null)
- {
- if (line.StartsWith(CommandPositioningAbsoluteG90.Command)) return GCodePositioningTypes.Absolute;
- if (line.StartsWith(CommandPositioningPartialG91.Command)) return GCodePositioningTypes.Partial;
- }
+ RebuildGCode(slicerFile, sb);
+ }
- return _gCodePositioningType;
+ public GCodePositioningTypes ParsePositioningTypeFromGCode(string gcode)
+ {
+ using var reader = new StringReader(gcode);
+ string? line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ if (line.StartsWith(CommandPositioningAbsoluteG90.Command)) return GCodePositioningTypes.Absolute;
+ if (line.StartsWith(CommandPositioningPartialG91.Command)) return GCodePositioningTypes.Partial;
}
- public void ParseLayersFromGCode(FileFormat slicerFile, bool rebuildGlobalTable = true) =>
- ParseLayersFromGCode(slicerFile, null, rebuildGlobalTable);
+ return _gCodePositioningType;
+ }
- public void ParseLayersFromGCode(FileFormat slicerFile, string gcode, bool rebuildGlobalTable = true)
- {
- if (slicerFile.LayerCount == 0) return;
+ public void ParseLayersFromGCode(FileFormat slicerFile, bool rebuildGlobalTable = true) =>
+ ParseLayersFromGCode(slicerFile, null, rebuildGlobalTable);
+
+ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebuildGlobalTable = true)
+ {
+ if (slicerFile.LayerCount == 0) return;
- if (string.IsNullOrWhiteSpace(gcode))
+ if (string.IsNullOrWhiteSpace(gcode))
+ {
+ gcode = _gcode.ToString();
+ if (string.IsNullOrWhiteSpace(gcode)) return;
+ }
+
+ var positionType = GCodePositioningTypes.Absolute;
+
+ // test
+ /*gcode =
+ "G90\r\n" +
+ "M6054 \"1.png\";\r\n" +
+ "G4 P0;\r\n" +
+ "M106 S300;\r\n" +
+ "G4 P36000;\r\n" +
+ "M106 S0;\r\n" +
+ "G4 P5000;\r\n" +
+ "\r\n"
+ ;*/
+
+ var layerBlock = new GCodeLayer(slicerFile);
+
+ using var reader = new StringReader(gcode);
+ string? line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ line = line.Trim();
+ if (string.IsNullOrWhiteSpace(line)) continue;
+
+ // Search for and switch position type when needed
+ if (line.StartsWith(CommandPositioningAbsoluteG90.Command))
{
- gcode = _gcode.ToString();
- if (string.IsNullOrWhiteSpace(gcode)) return;
+ positionType = GCodePositioningTypes.Absolute;
+ continue;
}
- var positionType = GCodePositioningTypes.Absolute;
-
- // test
- /*gcode =
- "G90\r\n" +
- "M6054 \"1.png\";\r\n" +
- "G4 P0;\r\n" +
- "M106 S300;\r\n" +
- "G4 P36000;\r\n" +
- "M106 S0;\r\n" +
- "G4 P5000;\r\n" +
- "\r\n"
- ;*/
-
- var layerBlock = new GCodeLayer(slicerFile);
-
- using var reader = new StringReader(gcode);
- string line;
- while ((line = reader.ReadLine()) != null)
+ if (line.StartsWith(CommandPositioningPartialG91.Command))
{
- line = line.Trim();
- if (string.IsNullOrWhiteSpace(line)) continue;
-
- // Search for and switch position type when needed
- if (line.StartsWith(CommandPositioningAbsoluteG90.Command))
- {
- positionType = GCodePositioningTypes.Absolute;
- continue;
- }
-
- if (line.StartsWith(CommandPositioningPartialG91.Command))
- {
- positionType = GCodePositioningTypes.Partial;
- continue;
- }
+ positionType = GCodePositioningTypes.Partial;
+ continue;
+ }
- Match match = null;
+ Match match;
- // Display image
- if (line.StartsWith(CommandShowImageM6054.Command))
+ // Display image
+ if (line.StartsWith(CommandShowImageM6054.Command))
+ {
+ match = Regex.Match(line,
+ CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")),
+ RegexOptions.IgnoreCase);
+ if (match.Success && match.Groups.Count >= 2) // Begin new layer
{
- match = Regex.Match(line,
- CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")),
- RegexOptions.IgnoreCase);
- if (match.Success && match.Groups.Count >= 2) // Begin new layer
- {
- var layerIndex = uint.Parse(match.Groups[1].Value);
- if (_gCodeShowImageType is GCodeShowImageTypes.FilenamePng1Started or GCodeShowImageTypes
+ var layerIndex = uint.Parse(match.Groups[1].Value);
+ if (_gCodeShowImageType is GCodeShowImageTypes.FilenamePng1Started or GCodeShowImageTypes
.LayerIndex1Started) layerIndex--;
- if (layerIndex > slicerFile.LayerCount)
- {
- throw new FileLoadException(
- $"GCode parser detected the layer {layerIndex}, but the file was sliced to {slicerFile.LayerCount} layers.",
- slicerFile.FileFullPath);
- }
+ if (layerIndex > slicerFile.LayerCount)
+ {
+ throw new FileLoadException(
+ $"GCode parser detected the layer {layerIndex}, but the file was sliced to {slicerFile.LayerCount} layers.",
+ slicerFile.FileFullPath);
+ }
- // Propagate values before switch to the new layer
- layerBlock.SetLayer(true);
- layerBlock.LayerIndex = layerIndex;
+ // Propagate values before switch to the new layer
+ layerBlock.SetLayer(true);
+ layerBlock.LayerIndex = layerIndex;
- continue;
- }
+ continue;
}
+ }
- if (!layerBlock.IsValid) continue; // No layer yet found to work on, skip
+ if (!layerBlock.IsValid) continue; // No layer yet found to work on, skip
- // Check moves
- if (line.StartsWith(CommandMoveG0.Command) || line.StartsWith(CommandMoveG1.Command))
+ // Check moves
+ if (line.StartsWith(CommandMoveG0.Command) || line.StartsWith(CommandMoveG1.Command))
+ {
+ var moveG0Regex = Regex.Match(line,
+ CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"),
+ RegexOptions.IgnoreCase);
+ var moveG1Regex = Regex.Match(line,
+ CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"),
+ RegexOptions.IgnoreCase);
+ match = moveG0Regex.Success && moveG0Regex.Groups.Count >= 2 ? moveG0Regex : moveG1Regex;
+
+ if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed2.HasValue && layerBlock.LightOffCount < 2)
{
- var moveG0Regex = Regex.Match(line,
- CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"),
- RegexOptions.IgnoreCase);
- var moveG1Regex = Regex.Match(line,
- CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"),
- RegexOptions.IgnoreCase);
- match = moveG0Regex.Success && moveG0Regex.Groups.Count >= 2 ? moveG0Regex : moveG1Regex;
-
- if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed2.HasValue && layerBlock.LightOffCount < 2)
- {
- float pos = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
- float speed = ConvertToMillimetersPerMinute(float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture));
+ float pos = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
+ float speed = ConvertToMillimetersPerMinute(float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture));
- layerBlock.Movements.Add(new(pos, speed));
- layerBlock.AssignMovements(positionType);
-
- /*if (!layerBlock.PositionZ.HasValue) // Lift or pos, set here for now
- {
- switch (positionType)
- {
- case GCodePositioningTypes.Absolute:
- layerBlock.PositionZ = pos;
- break;
- case GCodePositioningTypes.Partial:
- layerBlock.PositionZ = Layer.RoundHeight(positionZ + pos);
- break;
- }
-
- layerBlock.LiftSpeed = speed;
- continue;
- }
-
-
- // ~90% sure its retract here, still is possible to bug this with 2 lifts
- layerBlock.RetractSpeed = speed;
+ layerBlock.Movements.Add(new(pos, speed));
+ layerBlock.AssignMovements(positionType);
+ /*if (!layerBlock.PositionZ.HasValue) // Lift or pos, set here for now
+ {
switch (positionType)
{
case GCodePositioningTypes.Absolute:
- layerBlock.LiftHeight = Layer.RoundHeight(layerBlock.PositionZ.Value - pos);
- layerBlock.PositionZ = positionZ = pos;
+ layerBlock.PositionZ = pos;
break;
case GCodePositioningTypes.Partial:
- layerBlock.LiftHeight = layerBlock.PositionZ - positionZ;
- layerBlock.PositionZ = positionZ = Layer.RoundHeight(layerBlock.PositionZ.Value + pos);
+ layerBlock.PositionZ = Layer.RoundHeight(positionZ + pos);
break;
- }*/
+ }
+ layerBlock.LiftSpeed = speed;
continue;
}
+
+
+ // ~90% sure its retract here, still is possible to bug this with 2 lifts
+ layerBlock.RetractSpeed = speed;
+
+ switch (positionType)
+ {
+ case GCodePositioningTypes.Absolute:
+ layerBlock.LiftHeight = Layer.RoundHeight(layerBlock.PositionZ.Value - pos);
+ layerBlock.PositionZ = positionZ = pos;
+ break;
+ case GCodePositioningTypes.Partial:
+ layerBlock.LiftHeight = layerBlock.PositionZ - positionZ;
+ layerBlock.PositionZ = positionZ = Layer.RoundHeight(layerBlock.PositionZ.Value + pos);
+ break;
+ }*/
+
+ continue;
}
+ }
- // Check for waits
- if (line.StartsWith(CommandWaitG4.Command))
+ // Check for waits
+ if (line.StartsWith(CommandWaitG4.Command))
+ {
+ match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"),
+ RegexOptions.IgnoreCase);
+ if (match.Success && match.Groups.Count >= 2)
{
- match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"),
- RegexOptions.IgnoreCase);
- if (match.Success && match.Groups.Count >= 2)
+ if (_syncMovementsWithDelay && match.Groups[1].Value.StartsWith('0')) continue; // Sync movement delay, skip
+
+ var waitTime = float.Parse(match.Groups[1].Value);
+
+ if (layerBlock.PositionZ.HasValue &&
+ layerBlock.LiftHeight.HasValue &&
+ !layerBlock.RetractSpeed.HasValue) // Must be wait time after lift, if not, don't blame me!
{
- if (_syncMovementsWithDelay && match.Groups[1].Value.StartsWith('0')) continue; // Sync movement delay, skip
+ layerBlock.WaitTimeAfterLift ??= 0;
+ layerBlock.WaitTimeAfterLift += ConvertToSeconds(waitTime);
+ continue;
+ }
- var waitTime = float.Parse(match.Groups[1].Value);
+ if (!layerBlock.LightPWM.HasValue) // Before cure
+ {
+ layerBlock.WaitTimeBeforeCure ??= 0;
+ layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime);
+ continue;
+ }
- if (layerBlock.PositionZ.HasValue &&
- layerBlock.LiftHeight.HasValue &&
- !layerBlock.RetractSpeed.HasValue) // Must be wait time after lift, if not, don't blame me!
- {
- layerBlock.WaitTimeAfterLift ??= 0;
- layerBlock.WaitTimeAfterLift += ConvertToSeconds(waitTime);
- continue;
- }
+ if (layerBlock.IsExposing) // Must be exposure time, if not, don't blame me!
+ {
+ layerBlock.ExposureTime ??= 0;
+ layerBlock.ExposureTime += ConvertToSeconds(waitTime);
+ continue;
+ }
- if (!layerBlock.LightPWM.HasValue) // Before cure
+ if (layerBlock.IsAfterLightOff)
+ {
+ if (!layerBlock.WaitTimeBeforeCure.HasValue) // Novamaker fix, delay on last line, broke logic but safer
{
layerBlock.WaitTimeBeforeCure ??= 0;
layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime);
- continue;
- }
-
- if (layerBlock.IsExposing) // Must be exposure time, if not, don't blame me!
- {
- layerBlock.ExposureTime ??= 0;
- layerBlock.ExposureTime += ConvertToSeconds(waitTime);
- continue;
}
-
- if (layerBlock.IsAfterLightOff)
+ else
{
- if (!layerBlock.WaitTimeBeforeCure.HasValue) // Novamaker fix, delay on last line, broke logic but safer
- {
- layerBlock.WaitTimeBeforeCure ??= 0;
- layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime);
- }
- else
- {
- layerBlock.WaitTimeAfterCure ??= 0;
- layerBlock.WaitTimeAfterCure += ConvertToSeconds(waitTime);
- }
-
- continue;
+ layerBlock.WaitTimeAfterCure ??= 0;
+ layerBlock.WaitTimeAfterCure += ConvertToSeconds(waitTime);
}
continue;
}
+
+ continue;
}
+ }
- // Check LightPWM
- if (line.StartsWith(CommandTurnLEDM106.Command))
+ // Check LightPWM
+ if (line.StartsWith(CommandTurnLEDM106.Command))
+ {
+ match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"),
+ RegexOptions.IgnoreCase);
+ if (match.Success && match.Groups.Count >= 2)
{
- match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"),
- RegexOptions.IgnoreCase);
- if (match.Success && match.Groups.Count >= 2)
+ byte pwm;
+ if (_maxLedPower == byte.MaxValue)
{
- byte pwm;
- if (_maxLedPower == byte.MaxValue)
- {
- pwm = byte.Parse(match.Groups[1].Value);
- }
- else
- {
- ushort pwmValue = ushort.Parse(match.Groups[1].Value);
- pwm = (byte) (pwmValue * byte.MaxValue / _maxLedPower);
- }
-
- if (pwm == 0 && layerBlock.LightPWM.HasValue)
- {
- layerBlock.LightOffCount++;
- }
- else if (!layerBlock.IsAfterLightOff)
- {
- layerBlock.LightPWM = pwm;
- }
+ pwm = byte.Parse(match.Groups[1].Value);
+ }
+ else
+ {
+ ushort pwmValue = ushort.Parse(match.Groups[1].Value);
+ pwm = (byte) (pwmValue * byte.MaxValue / _maxLedPower);
+ }
- continue;
+ if (pwm == 0 && layerBlock.LightPWM.HasValue)
+ {
+ layerBlock.LightOffCount++;
+ }
+ else if (!layerBlock.IsAfterLightOff)
+ {
+ layerBlock.LightPWM = pwm;
}
+
+ continue;
}
}
+ }
- // Propagate values of left over layer
- layerBlock.SetLayer();
+ // Propagate values of left over layer
+ layerBlock.SetLayer();
- /*for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
+ /*for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
+ {
+ var layer = slicerFile[layerIndex];
+ if(layer is null) continue;
+ var startStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex));
+ var endStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex+1));
+ gcode = gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length + 1);
+ var endStrIndex = gcode.IndexOf(endStr, StringComparison.Ordinal);
+ var stripGcode = endStrIndex > 0 ? gcode[..endStrIndex] : gcode;//.Trim(' ', '\n', '\r', '\t');
+
+ float liftHeight = 0;// this allow read back no lifts slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLiftHeight, slicerFile.LiftHeight);
+ float liftSpeed = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLiftSpeed, slicerFile.LiftSpeed);
+ float retractSpeed = slicerFile.RetractSpeed;
+ float lightOffDelay = 0;
+ byte pwm = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLightPWM, slicerFile.LightPWM);
+ float exposureTime = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomExposureTime, slicerFile.ExposureTime);
+ var moveG0Regex = Regex.Matches(stripGcode, CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(\d+)"), RegexOptions.IgnoreCase);
+ var moveG1Regex = Regex.Matches(stripGcode, CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(\d+)"), RegexOptions.IgnoreCase);
+ var waitG4Regex = Regex.Matches(stripGcode, CommandWaitG4.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase);
+ var pwmM106Regex = Regex.Match(stripGcode, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase);
+ var moveRegex = moveG0Regex.Count > 0 ? moveG0Regex : moveG1Regex;
+
+ if (moveRegex.Count >= 1 && moveRegex[0].Success)
{
- var layer = slicerFile[layerIndex];
- if(layer is null) continue;
- var startStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex));
- var endStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex+1));
- gcode = gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length + 1);
- var endStrIndex = gcode.IndexOf(endStr, StringComparison.Ordinal);
- var stripGcode = endStrIndex > 0 ? gcode[..endStrIndex] : gcode;//.Trim(' ', '\n', '\r', '\t');
-
- float liftHeight = 0;// this allow read back no lifts slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLiftHeight, slicerFile.LiftHeight);
- float liftSpeed = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLiftSpeed, slicerFile.LiftSpeed);
- float retractSpeed = slicerFile.RetractSpeed;
- float lightOffDelay = 0;
- byte pwm = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLightPWM, slicerFile.LightPWM);
- float exposureTime = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomExposureTime, slicerFile.ExposureTime);
- var moveG0Regex = Regex.Matches(stripGcode, CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(\d+)"), RegexOptions.IgnoreCase);
- var moveG1Regex = Regex.Matches(stripGcode, CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(\d+)"), RegexOptions.IgnoreCase);
- var waitG4Regex = Regex.Matches(stripGcode, CommandWaitG4.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase);
- var pwmM106Regex = Regex.Match(stripGcode, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase);
- var moveRegex = moveG0Regex.Count > 0 ? moveG0Regex : moveG1Regex;
-
- if (moveRegex.Count >= 1 && moveRegex[0].Success)
+ float liftPosTemp = float.Parse(moveRegex[0].Groups[1].Value, CultureInfo.InvariantCulture);
+ float liftSpeedTemp = ConvertToMillimetersPerMinute(float.Parse(moveRegex[0].Groups[3].Value, CultureInfo.InvariantCulture));
+
+ if (moveRegex.Count >= 2 && moveRegex[1].Success)
{
- float liftPosTemp = float.Parse(moveRegex[0].Groups[1].Value, CultureInfo.InvariantCulture);
- float liftSpeedTemp = ConvertToMillimetersPerMinute(float.Parse(moveRegex[0].Groups[3].Value, CultureInfo.InvariantCulture));
+ float retractPos = float.Parse(moveRegex[1].Groups[1].Value, CultureInfo.InvariantCulture);
+ retractSpeed = ConvertToMillimetersPerMinute(float.Parse(moveRegex[1].Groups[3].Value, CultureInfo.InvariantCulture));
+ liftSpeed = liftSpeedTemp;
- if (moveRegex.Count >= 2 && moveRegex[1].Success)
+ switch (positionType)
+ {
+ case GCodePositioningTypes.Absolute:
+ liftHeight = Layer.RoundHeight(liftPosTemp - retractPos);
+ positionZ = retractPos;
+ break;
+ case GCodePositioningTypes.Partial:
+ liftHeight = liftPosTemp;
+ positionZ = Layer.RoundHeight(positionZ + liftPosTemp + retractPos);
+ break;
+ }
+ }
+ else
+ {
+ if (liftPosTemp - positionZ <= FileFormat.MaximumLayerHeight)
{
- float retractPos = float.Parse(moveRegex[1].Groups[1].Value, CultureInfo.InvariantCulture);
- retractSpeed = ConvertToMillimetersPerMinute(float.Parse(moveRegex[1].Groups[3].Value, CultureInfo.InvariantCulture));
- liftSpeed = liftSpeedTemp;
-
switch (positionType)
{
case GCodePositioningTypes.Absolute:
- liftHeight = Layer.RoundHeight(liftPosTemp - retractPos);
- positionZ = retractPos;
+ positionZ = liftPosTemp;
break;
case GCodePositioningTypes.Partial:
- liftHeight = liftPosTemp;
- positionZ = Layer.RoundHeight(positionZ + liftPosTemp + retractPos);
+ positionZ = Layer.RoundHeight(positionZ + liftPosTemp);
break;
}
}
- else
- {
- if (liftPosTemp - positionZ <= FileFormat.MaximumLayerHeight)
- {
- switch (positionType)
- {
- case GCodePositioningTypes.Absolute:
- positionZ = liftPosTemp;
- break;
- case GCodePositioningTypes.Partial:
- positionZ = Layer.RoundHeight(positionZ + liftPosTemp);
- break;
- }
- }
- }
}
+ }
- if (pwmM106Regex.Success)
+ if (pwmM106Regex.Success)
+ {
+ if (_maxLedPower == byte.MaxValue)
{
- if (_maxLedPower == byte.MaxValue)
- {
- pwm = byte.Parse(pwmM106Regex.Groups[1].Value);
- }
- else
- {
- ushort pwmValue = ushort.Parse(pwmM106Regex.Groups[1].Value);
- pwm = (byte)(pwmValue * byte.MaxValue / _maxLedPower);
- }
+ pwm = byte.Parse(pwmM106Regex.Groups[1].Value);
}
-
- if (waitG4Regex.Count >= 1 && waitG4Regex[0].Success)
+ else
{
- lightOffDelay = ConvertToSeconds(float.Parse(waitG4Regex[0].Groups[1].Value, CultureInfo.InvariantCulture));
-
- if (waitG4Regex.Count >= 2 && waitG4Regex[1].Success)
- {
- exposureTime = ConvertToSeconds(float.Parse(waitG4Regex[1].Groups[1].Value, CultureInfo.InvariantCulture));
- }
- else // Only one match, meaning light off delay is not present and the only time is the cure time
- {
- exposureTime = lightOffDelay;
- lightOffDelay = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLightOffDelay, slicerFile.LightOffDelay);
- }
+ ushort pwmValue = ushort.Parse(pwmM106Regex.Groups[1].Value);
+ pwm = (byte)(pwmValue * byte.MaxValue / _maxLedPower);
}
-
- layer.PositionZ = positionZ;
- layer.ExposureTime = exposureTime;
- layer.LiftHeight = liftHeight;
- layer.LiftSpeed = liftSpeed;
- layer.RetractSpeed = retractSpeed;
- layer.LightOffDelay = lightOffDelay;
- layer.LightPWM = pwm;
}
- */
- if (rebuildGlobalTable)
+ if (waitG4Regex.Count >= 1 && waitG4Regex[0].Success)
{
- slicerFile.UpdateGlobalPropertiesFromLayers();
+ lightOffDelay = ConvertToSeconds(float.Parse(waitG4Regex[0].Groups[1].Value, CultureInfo.InvariantCulture));
+
+ if (waitG4Regex.Count >= 2 && waitG4Regex[1].Success)
+ {
+ exposureTime = ConvertToSeconds(float.Parse(waitG4Regex[1].Groups[1].Value, CultureInfo.InvariantCulture));
+ }
+ else // Only one match, meaning light off delay is not present and the only time is the cure time
+ {
+ exposureTime = lightOffDelay;
+ lightOffDelay = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLightOffDelay, slicerFile.LightOffDelay);
+ }
}
+
+ layer.PositionZ = positionZ;
+ layer.ExposureTime = exposureTime;
+ layer.LiftHeight = liftHeight;
+ layer.LiftSpeed = liftSpeed;
+ layer.RetractSpeed = retractSpeed;
+ layer.LightOffDelay = lightOffDelay;
+ layer.LightPWM = pwm;
}
+ */
- public StringReader GetStringReader()
+ if (rebuildGlobalTable)
{
- return new(_gcode.ToString());
+ slicerFile.UpdateGlobalPropertiesFromLayers();
}
+ }
- /// <summary>
- /// Converts seconds to current gcode norm
- /// </summary>
- /// <param name="seconds"></param>
- /// <returns></returns>
- public float ConvertFromSeconds(float seconds)
+ public StringReader GetStringReader()
+ {
+ return new(_gcode.ToString());
+ }
+
+ /// <summary>
+ /// Converts seconds to current gcode norm
+ /// </summary>
+ /// <param name="seconds"></param>
+ /// <returns></returns>
+ public float ConvertFromSeconds(float seconds)
+ {
+ return _gCodeTimeUnit switch
{
- return _gCodeTimeUnit switch
- {
- GCodeTimeUnits.Seconds => seconds,
- GCodeTimeUnits.Milliseconds => TimeExtensions.SecondsToMilliseconds(seconds),
- _ => throw new InvalidExpressionException($"Unhandled time unit for {_gCodeTimeUnit}")
- };
- }
+ GCodeTimeUnits.Seconds => seconds,
+ GCodeTimeUnits.Milliseconds => TimeExtensions.SecondsToMilliseconds(seconds),
+ _ => throw new InvalidExpressionException($"Unhandled time unit for {_gCodeTimeUnit}")
+ };
+ }
- /// <summary>
- /// Converts speed in mm/min to current gcode norm
- /// </summary>
- /// <param name="mmMin">Millimeters per minute</param>
- /// <returns></returns>
- public float ConvertFromMillimetersPerMinute(float mmMin)
+ /// <summary>
+ /// Converts speed in mm/min to current gcode norm
+ /// </summary>
+ /// <param name="mmMin">Millimeters per minute</param>
+ /// <returns></returns>
+ public float ConvertFromMillimetersPerMinute(float mmMin)
+ {
+ return _gCodeSpeedUnit switch
{
- return _gCodeSpeedUnit switch
- {
- GCodeSpeedUnits.MillimetersPerMinute => mmMin,
- GCodeSpeedUnits.MillimetersPerSecond => (float) Math.Round(mmMin / 60, 2),
- GCodeSpeedUnits.CentimetersPerMinute => (float) Math.Round(mmMin / 10, 2),
- _ => throw new InvalidExpressionException($"Unhandled speed unit for {_gCodeSpeedUnit}")
- };
- }
+ GCodeSpeedUnits.MillimetersPerMinute => mmMin,
+ GCodeSpeedUnits.MillimetersPerSecond => (float) Math.Round(mmMin / 60, 2),
+ GCodeSpeedUnits.CentimetersPerMinute => (float) Math.Round(mmMin / 10, 2),
+ _ => throw new InvalidExpressionException($"Unhandled speed unit for {_gCodeSpeedUnit}")
+ };
+ }
- /// <summary>
- /// Converts time from current gcode norm in <see cref="GCodeTimeUnit"/> to s
- /// </summary>
- /// <param name="time">Time in <see cref="GCodeTimeUnit"/></param>
- /// <returns></returns>
- public float ConvertToSeconds(float time)
+ /// <summary>
+ /// Converts time from current gcode norm in <see cref="GCodeTimeUnit"/> to s
+ /// </summary>
+ /// <param name="time">Time in <see cref="GCodeTimeUnit"/></param>
+ /// <returns></returns>
+ public float ConvertToSeconds(float time)
+ {
+ return _gCodeTimeUnit switch
{
- return _gCodeTimeUnit switch
- {
- GCodeTimeUnits.Seconds => time,
- GCodeTimeUnits.Milliseconds => TimeExtensions.MillisecondsToSeconds(time),
- _ => throw new InvalidExpressionException($"Unhandled time unit for {_gCodeTimeUnit}")
- };
- }
+ GCodeTimeUnits.Seconds => time,
+ GCodeTimeUnits.Milliseconds => TimeExtensions.MillisecondsToSeconds(time),
+ _ => throw new InvalidExpressionException($"Unhandled time unit for {_gCodeTimeUnit}")
+ };
+ }
- /// <summary>
- /// Converts speed from current gcode norm in <see cref="GCodeSpeedUnit"/> to mm/min
- /// </summary>
- /// <param name="speed">Speed in <see cref="GCodeSpeedUnit"/></param>
- /// <returns></returns>
- public float ConvertToMillimetersPerMinute(float speed)
+ /// <summary>
+ /// Converts speed from current gcode norm in <see cref="GCodeSpeedUnit"/> to mm/min
+ /// </summary>
+ /// <param name="speed">Speed in <see cref="GCodeSpeedUnit"/></param>
+ /// <returns></returns>
+ public float ConvertToMillimetersPerMinute(float speed)
+ {
+ return _gCodeSpeedUnit switch
{
- return _gCodeSpeedUnit switch
- {
- GCodeSpeedUnits.MillimetersPerMinute => speed,
- GCodeSpeedUnits.MillimetersPerSecond => (float)Math.Round(speed * 60, 2),
- GCodeSpeedUnits.CentimetersPerMinute => (float)Math.Round(speed * 10, 2),
- _ => throw new InvalidExpressionException($"Unhandled speed unit for {_gCodeSpeedUnit}")
- };
- }
+ GCodeSpeedUnits.MillimetersPerMinute => speed,
+ GCodeSpeedUnits.MillimetersPerSecond => (float)Math.Round(speed * 60, 2),
+ GCodeSpeedUnits.CentimetersPerMinute => (float)Math.Round(speed * 10, 2),
+ _ => throw new InvalidExpressionException($"Unhandled speed unit for {_gCodeSpeedUnit}")
+ };
}
- #endregion
}
+#endregion \ No newline at end of file
diff --git a/UVtools.Core/GCode/GCodeCommand.cs b/UVtools.Core/GCode/GCodeCommand.cs
index e74fb4d..4073cc6 100644
--- a/UVtools.Core/GCode/GCodeCommand.cs
+++ b/UVtools.Core/GCode/GCodeCommand.cs
@@ -6,115 +6,114 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.GCode
+namespace UVtools.Core.GCode;
+
+public class GCodeCommand
{
- public class GCodeCommand
- {
- /// <summary>
- /// Gets or sets if this command is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
+ /// <summary>
+ /// Gets or sets if this command is enabled
+ /// </summary>
+ public bool Enabled { get; set; } = true;
- /// <summary>
- /// Gets or sets the command name
- /// </summary>
- public string Command { get; set; }
+ /// <summary>
+ /// Gets or sets the command name
+ /// </summary>
+ public string Command { get; set; } = null!;
- /// <summary>
- /// Gets or sets the arguments for this command
- /// </summary>
- public string Arguments { get; set; }
+ /// <summary>
+ /// Gets or sets the arguments for this command
+ /// </summary>
+ public string? Arguments { get; set; }
- /// <summary>
- /// Gets or sets the comment
- /// </summary>
- public string Comment { get; set; }
+ /// <summary>
+ /// Gets or sets the comment
+ /// </summary>
+ public string? Comment { get; set; }
- public GCodeCommand() { }
+ public GCodeCommand() { }
- public GCodeCommand(string command, string arguments = null, string comment = null, bool enabled = true)
- {
- Enabled = enabled;
- Command = command;
- Arguments = arguments;
- Comment = comment;
- }
+ public GCodeCommand(string command, string? arguments = null, string? comment = null, bool enabled = true)
+ {
+ Enabled = enabled;
+ Command = command;
+ Arguments = arguments;
+ Comment = comment;
+ }
- public void Set(string command, string arguments, string comment, bool enabled = true)
- {
- Enabled = enabled;
- Command = command;
- Arguments = arguments;
- Comment = comment;
- }
+ public void Set(string command, string? arguments, string? comment, bool enabled = true)
+ {
+ Enabled = enabled;
+ Command = command;
+ Arguments = arguments;
+ Comment = comment;
+ }
- public void Set(string command, string arguments)
- {
- Command = command;
- Arguments = arguments;
- }
+ public void Set(string command, string arguments)
+ {
+ Command = command;
+ Arguments = arguments;
+ }
- public void Set(string command)
- {
- Command = command;
- }
+ public void Set(string command)
+ {
+ Command = command;
+ }
- public string ToString(bool showComment, bool showTailComma = true, string overrideComment = null)
+ public string ToString(bool showComment, bool showTailComma = true, string? overrideComment = null)
+ {
+ var result = Command;
+ if (!string.IsNullOrWhiteSpace(Arguments))
+ result += $" {Arguments}";
+
+ if (result[0] == ';') return result;
+
+ var comment = string.IsNullOrWhiteSpace(overrideComment) ? Comment : overrideComment;
+ if (showComment && !string.IsNullOrWhiteSpace(comment))
{
- var result = Command;
- if (!string.IsNullOrWhiteSpace(Arguments))
- result += $" {Arguments}";
-
- if (result[0] == ';') return result;
-
- var comment = string.IsNullOrWhiteSpace(overrideComment) ? Comment : overrideComment;
- if (showComment && !string.IsNullOrWhiteSpace(comment))
- {
- result += $";{comment}";
- }
- else if (showTailComma)
- {
- result += ';';
- }
-
- return result;
+ result += $";{comment}";
}
- public string ToString(bool showComment, bool showTailComma = true, params object[] args) =>
- string.Format(ToString(showComment, showTailComma), args);
- public string ToStringOverrideComment(string comment, params object[] args) => ToStringOverrideComment(true, true, comment, args);
- public string ToStringOverrideComment(bool showComment, bool showTailComma, string comment, params object[] args) =>
- string.Format(ToString(showComment, showTailComma, comment), args);
- public string ToString(params object[] args) => ToString(true, true, args);
- public string ToStringWithoutComments() => ToString(false, false);
- public string ToStringWithoutComments(params object[] args) => ToString(false, false, args);
-
- public override string ToString()
+ else if (showTailComma)
{
- var result = Command;
- if (!string.IsNullOrWhiteSpace(Arguments))
- result += $" {Arguments}";
- if (!string.IsNullOrWhiteSpace(Comment))
- result += $";{Comment}";
- return result;
+ result += ';';
}
+
+ return result;
+ }
+ public string ToString(bool showComment, bool showTailComma = true, params object[] args) =>
+ string.Format(ToString(showComment, showTailComma), args);
+ public string ToStringOverrideComment(string comment, params object[] args) => ToStringOverrideComment(true, true, comment, args);
+ public string ToStringOverrideComment(bool showComment, bool showTailComma, string? comment, params object[] args) =>
+ string.Format(ToString(showComment, showTailComma, comment), args);
+ public string ToString(params object[] args) => ToString(true, true, args);
+ public string ToStringWithoutComments() => ToString(false, false);
+ public string ToStringWithoutComments(params object[] args) => ToString(false, false, args);
+
+ public override string ToString()
+ {
+ var result = Command;
+ if (!string.IsNullOrWhiteSpace(Arguments))
+ result += $" {Arguments}";
+ if (!string.IsNullOrWhiteSpace(Comment))
+ result += $";{Comment}";
+ return result;
+ }
- protected bool Equals(GCodeCommand other)
- {
- return Command == other.Command;
- }
+ protected bool Equals(GCodeCommand other)
+ {
+ return Command == other.Command;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((GCodeCommand) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((GCodeCommand) obj);
+ }
- public override int GetHashCode()
- {
- return (Command != null ? Command.GetHashCode() : 0);
- }
+ public override int GetHashCode()
+ {
+ return (Command != null ? Command.GetHashCode() : 0);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/GCode/GCodeLayer.cs b/UVtools.Core/GCode/GCodeLayer.cs
index 659a0df..baf2c27 100644
--- a/UVtools.Core/GCode/GCodeLayer.cs
+++ b/UVtools.Core/GCode/GCodeLayer.cs
@@ -12,308 +12,307 @@ using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
-namespace UVtools.Core.GCode
+namespace UVtools.Core.GCode;
+
+public class GCodeLayer
{
- public class GCodeLayer
+ private float? _positionZ;
+ private float? _waitTimeBeforeCure;
+ private float? _exposureTime;
+ private float? _waitTimeAfterCure;
+ private float? _liftHeight;
+ private float? _liftSpeed;
+ private float? _liftHeight2;
+ private float? _liftSpeed2;
+ private float? _waitTimeAfterLift;
+ private float? _retractSpeed;
+ private float? _retractHeight2;
+ private float? _retractSpeed2;
+
+ public enum GCodeLastParsedLine : byte
{
- private float? _positionZ;
- private float? _waitTimeBeforeCure;
- private float? _exposureTime;
- private float? _waitTimeAfterCure;
- private float? _liftHeight;
- private float? _liftSpeed;
- private float? _liftHeight2;
- private float? _liftSpeed2;
- private float? _waitTimeAfterLift;
- private float? _retractSpeed;
- private float? _retractHeight2;
- private float? _retractSpeed2;
-
- public enum GCodeLastParsedLine : byte
- {
- LayerIndex,
- }
+ LayerIndex,
+ }
- public bool IsValid => LayerIndex.HasValue;
+ public bool IsValid => LayerIndex.HasValue;
- public FileFormat SlicerFile { get; }
- public List<(float Pos, float Speed)> Movements = new();
- public uint? LayerIndex { get; set; }
+ public FileFormat SlicerFile { get; }
+ public List<(float Pos, float Speed)> Movements = new();
+ public uint? LayerIndex { get; set; }
- public float? PositionZ
- {
- get => _positionZ;
- set => _positionZ = value;
- }
+ public float? PositionZ
+ {
+ get => _positionZ;
+ set => _positionZ = value;
+ }
- public float PreviousPositionZ { get; set; }
+ public float PreviousPositionZ { get; set; }
- public float? WaitTimeBeforeCure
- {
- get => _waitTimeBeforeCure;
- set => _waitTimeBeforeCure = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? WaitTimeBeforeCure
+ {
+ get => _waitTimeBeforeCure;
+ set => _waitTimeBeforeCure = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public float? ExposureTime
- {
- get => _exposureTime;
- set => _exposureTime = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? ExposureTime
+ {
+ get => _exposureTime;
+ set => _exposureTime = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public float? WaitTimeAfterCure
- {
- get => _waitTimeAfterCure;
- set => _waitTimeAfterCure = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? WaitTimeAfterCure
+ {
+ get => _waitTimeAfterCure;
+ set => _waitTimeAfterCure = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public float? LiftHeight
- {
- get => _liftHeight;
- set => _liftHeight = value is null ? null : Layer.RoundHeight(value.Value);
- }
+ public float? LiftHeight
+ {
+ get => _liftHeight;
+ set => _liftHeight = value is null ? null : Layer.RoundHeight(value.Value);
+ }
- public float? LiftSpeed
- {
- get => _liftSpeed;
- set => _liftSpeed = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? LiftSpeed
+ {
+ get => _liftSpeed;
+ set => _liftSpeed = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public float LiftHeightTotal => Layer.RoundHeight((LiftHeight ?? 0) + (LiftHeight2 ?? 0));
+ public float LiftHeightTotal => Layer.RoundHeight((LiftHeight ?? 0) + (LiftHeight2 ?? 0));
- public float? LiftHeight2
- {
- get => _liftHeight2;
- set => _liftHeight2 = value is null ? null : Layer.RoundHeight(value.Value);
- }
+ public float? LiftHeight2
+ {
+ get => _liftHeight2;
+ set => _liftHeight2 = value is null ? null : Layer.RoundHeight(value.Value);
+ }
- public float? LiftSpeed2
- {
- get => _liftSpeed2;
- set => _liftSpeed2 = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? LiftSpeed2
+ {
+ get => _liftSpeed2;
+ set => _liftSpeed2 = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public float? WaitTimeAfterLift
- {
- get => _waitTimeAfterLift;
- set => _waitTimeAfterLift = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? WaitTimeAfterLift
+ {
+ get => _waitTimeAfterLift;
+ set => _waitTimeAfterLift = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public float? RetractSpeed
- {
- get => _retractSpeed;
- set => _retractSpeed = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? RetractSpeed
+ {
+ get => _retractSpeed;
+ set => _retractSpeed = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public float? RetractHeight2
- {
- get => _retractHeight2;
- set => _retractHeight2 = value is null ? null : Layer.RoundHeight(value.Value);
- }
+ public float? RetractHeight2
+ {
+ get => _retractHeight2;
+ set => _retractHeight2 = value is null ? null : Layer.RoundHeight(value.Value);
+ }
- public float? RetractSpeed2
- {
- get => _retractSpeed2;
- set => _retractSpeed2 = value is null ? null : (float)Math.Round(value.Value, 2);
- }
+ public float? RetractSpeed2
+ {
+ get => _retractSpeed2;
+ set => _retractSpeed2 = value is null ? null : (float)Math.Round(value.Value, 2);
+ }
- public byte? LightPWM { get; set; }
+ public byte? LightPWM { get; set; }
- public bool IsExposing => LightPWM.HasValue && !IsAfterLightOff;
- public bool IsExposed => LightPWM.HasValue && IsAfterLightOff;
+ public bool IsExposing => LightPWM.HasValue && !IsAfterLightOff;
+ public bool IsExposed => LightPWM.HasValue && IsAfterLightOff;
- public byte LightOffCount { get; set; }
- public bool IsAfterLightOff => LightOffCount > 0;
+ public byte LightOffCount { get; set; }
+ public bool IsAfterLightOff => LightOffCount > 0;
- public GCodeLayer(FileFormat slicerFile)
- {
- SlicerFile = slicerFile;
- }
+ public GCodeLayer(FileFormat slicerFile)
+ {
+ SlicerFile = slicerFile;
+ }
- public void Init()
- {
- PreviousPositionZ = PositionZ ?? 0;
-
- Movements.Clear();
- LayerIndex = null;
- PositionZ = null;
- WaitTimeBeforeCure = null;
- ExposureTime = null;
- WaitTimeAfterCure = null;
- LiftHeight = null;
- LiftSpeed = null;
- LiftHeight2 = null;
- LiftSpeed2 = null;
- WaitTimeAfterLift = null;
- RetractSpeed = null;
- RetractHeight2 = null;
- RetractSpeed2 = null;
- LightPWM = null;
- LightOffCount = 0;
- }
+ public void Init()
+ {
+ PreviousPositionZ = PositionZ ?? 0;
+
+ Movements.Clear();
+ LayerIndex = null;
+ PositionZ = null;
+ WaitTimeBeforeCure = null;
+ ExposureTime = null;
+ WaitTimeAfterCure = null;
+ LiftHeight = null;
+ LiftSpeed = null;
+ LiftHeight2 = null;
+ LiftSpeed2 = null;
+ WaitTimeAfterLift = null;
+ RetractSpeed = null;
+ RetractHeight2 = null;
+ RetractSpeed2 = null;
+ LightPWM = null;
+ LightOffCount = 0;
+ }
- public void AssignMovements(GCodeBuilder.GCodePositioningTypes positionType)
- {
- if (Movements.Count == 0) return;
- float currentZ = PreviousPositionZ;
+ public void AssignMovements(GCodeBuilder.GCodePositioningTypes positionType)
+ {
+ if (Movements.Count == 0) return;
+ float currentZ = PreviousPositionZ;
- PositionZ = null;
- LiftHeight = null;
- LiftSpeed = null;
- LiftHeight2 = null;
- LiftSpeed2 = null;
- RetractSpeed = null;
- RetractHeight2 = null;
- RetractSpeed2 = null;
+ PositionZ = null;
+ LiftHeight = null;
+ LiftSpeed = null;
+ LiftHeight2 = null;
+ LiftSpeed2 = null;
+ RetractSpeed = null;
+ RetractHeight2 = null;
+ RetractSpeed2 = null;
- var previousLayerEmpty = LayerIndex > 0 && SlicerFile[LayerIndex.Value - 1].NonZeroPixelCount <= 1;
+ var previousLayerEmpty = LayerIndex > 0 && SlicerFile[LayerIndex.Value - 1].NonZeroPixelCount <= 1;
- for (int i = 0; i < Movements.Count; i++)
+ for (int i = 0; i < Movements.Count; i++)
+ {
+ var (pos, speed) = Movements[i];
+ float partialPositionZ;
+ switch (positionType)
{
- var (pos, speed) = Movements[i];
- float partialPositionZ;
- switch (positionType)
- {
- case GCodeBuilder.GCodePositioningTypes.Absolute:
- partialPositionZ = Layer.RoundHeight(pos - currentZ);
- currentZ = pos;
- break;
- case GCodeBuilder.GCodePositioningTypes.Partial:
- partialPositionZ = pos;
- currentZ = Layer.RoundHeight(currentZ + pos);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(positionType));
- }
+ case GCodeBuilder.GCodePositioningTypes.Absolute:
+ partialPositionZ = Layer.RoundHeight(pos - currentZ);
+ currentZ = pos;
+ break;
+ case GCodeBuilder.GCodePositioningTypes.Partial:
+ partialPositionZ = pos;
+ currentZ = Layer.RoundHeight(currentZ + pos);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(positionType));
+ }
- // Fail-safe check
+ // Fail-safe check
- if (currentZ < PreviousPositionZ && !previousLayerEmpty)
- {
- throw new NotSupportedException(
+ if (currentZ < PreviousPositionZ && !previousLayerEmpty)
+ {
+ throw new NotSupportedException(
$"GCode parsing error: Attempting to crash the print on the LCD due a lower position ({currentZ}mm) than the previous layer ({PreviousPositionZ}mm).\n" +
"Do not attempt to print this file!");
- }
+ }
- // Last movement on the list, must be position Z
- if (i == Movements.Count - 1)
+ // Last movement on the list, must be position Z
+ if (i == Movements.Count - 1)
+ {
+ PositionZ = currentZ;
+ if (LiftHeight.HasValue && partialPositionZ < 0)
{
- PositionZ = currentZ;
- if (LiftHeight.HasValue && partialPositionZ < 0)
- {
- RetractSpeed = speed; // A lift exists, and its descending, set to retract speed of this move
- }
- break;
+ RetractSpeed = speed; // A lift exists, and its descending, set to retract speed of this move
}
+ break;
+ }
- if (partialPositionZ == 0) continue;
- var height = Math.Abs(partialPositionZ);
+ if (partialPositionZ == 0) continue;
+ var height = Math.Abs(partialPositionZ);
- if (currentZ < PreviousPositionZ) // Check for inverse lifts
- {
- LiftHeight ??= 0;
- LiftHeight = Math.Min(LiftHeight.Value, -currentZ);
- LiftSpeed = speed;
- continue;
- }
+ if (currentZ < PreviousPositionZ) // Check for inverse lifts
+ {
+ LiftHeight ??= 0;
+ LiftHeight = Math.Min(LiftHeight.Value, -currentZ);
+ LiftSpeed = speed;
+ continue;
+ }
- if (partialPositionZ > 0) // Is a lift
+ if (partialPositionZ > 0) // Is a lift
+ {
+ // Lift 1
+ if (!LiftHeight.HasValue)
{
- // Lift 1
- if (!LiftHeight.HasValue)
- {
- LiftHeight = height;
- LiftSpeed = speed;
- continue;
- }
-
- // Lift 2
- LiftHeight2 ??= 0;
- LiftHeight2 += height;
- LiftSpeed2 = speed;
-
+ LiftHeight = height;
+ LiftSpeed = speed;
continue;
}
- if(!LiftHeight.HasValue) continue; // Fail-safe: Retract without a lift? Skip
+ // Lift 2
+ LiftHeight2 ??= 0;
+ LiftHeight2 += height;
+ LiftSpeed2 = speed;
- // Is a extra retract (2)
- RetractHeight2 ??= 0;
- RetractHeight2 += height;
- RetractSpeed2 = speed;
+ continue;
}
- if (Movements.Count == 1) // Only 1 move, this is the PositionZ only
- {
- LiftSpeed = Movements[0].Speed;
- return;
- }
+ if(!LiftHeight.HasValue) continue; // Fail-safe: Retract without a lift? Skip
- // Sanitize
- if (PositionZ.HasValue && LiftHeight.HasValue)
- {
- if (LiftHeight < 0) // Inverse lift
- {
- LiftHeight = Layer.RoundHeight(Math.Abs(PositionZ.Value + LiftHeight.Value));
- }
- else if (!IsExposed) // Lift before exposure order, need to remove layer height as offset
- {
- var liftHeight = Layer.RoundHeight(LiftHeight.Value - (PositionZ.Value - PreviousPositionZ));
- if (liftHeight <= 0) return; // Something not right or not the correct moment, skip
- LiftHeight = liftHeight;
- }
- }
+ // Is a extra retract (2)
+ RetractHeight2 ??= 0;
+ RetractHeight2 += height;
+ RetractSpeed2 = speed;
+ }
- if (PositionZ.HasValue && LiftHeight.HasValue && !IsExposed)
- {
-
- }
+ if (Movements.Count == 1) // Only 1 move, this is the PositionZ only
+ {
+ LiftSpeed = Movements[0].Speed;
+ return;
+ }
- if (RetractHeight2.HasValue) // Need to fix the purpose of this value
+ // Sanitize
+ if (PositionZ.HasValue && LiftHeight.HasValue)
+ {
+ if (LiftHeight < 0) // Inverse lift
{
- RetractHeight2 = Layer.RoundHeight(LiftHeightTotal - RetractHeight2.Value);
- (RetractSpeed, RetractSpeed2) = (RetractSpeed2, RetractSpeed);
+ LiftHeight = Layer.RoundHeight(Math.Abs(PositionZ.Value + LiftHeight.Value));
}
-
- if (LiftHeight.HasValue && RetractHeight2.HasValue) // Sanitize RetractHeight2 value
+ else if (!IsExposed) // Lift before exposure order, need to remove layer height as offset
{
- RetractHeight2 = Math.Clamp(RetractHeight2.Value, 0, LiftHeightTotal);
+ var liftHeight = Layer.RoundHeight(LiftHeight.Value - (PositionZ.Value - PreviousPositionZ));
+ if (liftHeight <= 0) return; // Something not right or not the correct moment, skip
+ LiftHeight = liftHeight;
}
}
- /// <summary>
- /// Set gathered data to the layer
- /// </summary>
- public void SetLayer(bool reinit = false)
+ if (PositionZ.HasValue && LiftHeight.HasValue && !IsExposed)
{
- if (!IsValid) return;
- uint layerIndex = LayerIndex.Value;
- var layer = SlicerFile[layerIndex];
+
+ }
+
+ if (RetractHeight2.HasValue) // Need to fix the purpose of this value
+ {
+ RetractHeight2 = Layer.RoundHeight(LiftHeightTotal - RetractHeight2.Value);
+ (RetractSpeed, RetractSpeed2) = (RetractSpeed2, RetractSpeed);
+ }
+
+ if (LiftHeight.HasValue && RetractHeight2.HasValue) // Sanitize RetractHeight2 value
+ {
+ RetractHeight2 = Math.Clamp(RetractHeight2.Value, 0, LiftHeightTotal);
+ }
+ }
+
+ /// <summary>
+ /// Set gathered data to the layer
+ /// </summary>
+ public void SetLayer(bool reinit = false)
+ {
+ if (!IsValid) return;
+ uint layerIndex = LayerIndex!.Value;
+ var layer = SlicerFile[layerIndex];
- PositionZ ??= PreviousPositionZ;
- layer.PositionZ = PositionZ.Value;
- layer.WaitTimeBeforeCure = WaitTimeBeforeCure ?? 0;
- layer.ExposureTime = ExposureTime ?? 0;
- layer.WaitTimeAfterCure = WaitTimeAfterCure ?? 0;
- layer.LiftHeight = LiftHeight ?? 0;
- layer.LiftSpeed = LiftSpeed ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
- layer.LiftHeight2 = LiftHeight2 ?? 0;
- layer.LiftSpeed2 = LiftSpeed2 ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2);
- layer.WaitTimeAfterLift = WaitTimeAfterLift ?? 0;
- layer.RetractSpeed = RetractSpeed ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed);
- layer.RetractHeight2 = RetractHeight2 ?? 0;
- layer.RetractSpeed2 = RetractSpeed2 ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
- layer.LightPWM = LightPWM ?? 0;//SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
-
- if (SlicerFile.GCode.SyncMovementsWithDelay) // Dirty fix of the value
+ PositionZ ??= PreviousPositionZ;
+ layer.PositionZ = PositionZ.Value;
+ layer.WaitTimeBeforeCure = WaitTimeBeforeCure ?? 0;
+ layer.ExposureTime = ExposureTime ?? 0;
+ layer.WaitTimeAfterCure = WaitTimeAfterCure ?? 0;
+ layer.LiftHeight = LiftHeight ?? 0;
+ layer.LiftSpeed = LiftSpeed ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
+ layer.LiftHeight2 = LiftHeight2 ?? 0;
+ layer.LiftSpeed2 = LiftSpeed2 ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2);
+ layer.WaitTimeAfterLift = WaitTimeAfterLift ?? 0;
+ layer.RetractSpeed = RetractSpeed ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed);
+ layer.RetractHeight2 = RetractHeight2 ?? 0;
+ layer.RetractSpeed2 = RetractSpeed2 ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
+ layer.LightPWM = LightPWM ?? 0;//SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
+
+ if (SlicerFile.GCode!.SyncMovementsWithDelay) // Dirty fix of the value
+ {
+ var syncTime = OperationCalculator.LightOffDelayC.CalculateSeconds(layer, 1.5f);
+ if (syncTime < layer.WaitTimeBeforeCure)
{
- var syncTime = OperationCalculator.LightOffDelayC.CalculateSeconds(layer, 1.5f);
- if (syncTime < layer.WaitTimeBeforeCure)
- {
- layer.WaitTimeBeforeCure = (float) Math.Round(layer.WaitTimeBeforeCure - syncTime, 2);
- }
+ layer.WaitTimeBeforeCure = (float) Math.Round(layer.WaitTimeBeforeCure - syncTime, 2);
}
-
- if(reinit) Init();
}
+
+ if(reinit) Init();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Helpers.cs b/UVtools.Core/Helpers.cs
index 5443edf..7807a12 100644
--- a/UVtools.Core/Helpers.cs
+++ b/UVtools.Core/Helpers.cs
@@ -6,112 +6,105 @@
* of this license document, but changing it is not allowed.
*/
+using BinarySerialization;
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
-using BinarySerialization;
-using Newtonsoft.Json;
+using System.Text.Json;
using UVtools.Core.Extensions;
-namespace UVtools.Core
+namespace UVtools.Core;
+
+/// <summary>
+/// A helper class with utilities
+/// </summary>
+public static class Helpers
{
/// <summary>
- /// A helper class with utilities
+ /// Gets the <see cref="BinarySerializer"/> instance
/// </summary>
- public static class Helpers
- {
- /// <summary>
- /// Gets the <see cref="BinarySerializer"/> instance
- /// </summary>
- public static BinarySerializer Serializer { get; } = new() {Endianness = Endianness.Little };
+ public static BinarySerializer Serializer { get; } = new() {Endianness = Endianness.Little };
- public static MemoryStream Serialize(object value)
- {
- MemoryStream stream = new();
- Serializer.Serialize(stream, value);
- return stream;
- }
-
- public static T Deserialize<T>(Stream stream)
- {
- return Serializer.Deserialize<T>(stream);
- }
-
- public static uint SerializeWriteFileStream(FileStream fs, object value, int offset = 0)
- {
- using var stream = Serialize(value);
- return fs.WriteStream(stream, offset);
- }
+ public static MemoryStream Serialize(object value)
+ {
+ MemoryStream stream = new();
+ Serializer.Serialize(stream, value);
+ return stream;
+ }
- public static T JsonDeserializeObject<T>(Stream stream)
- {
- using TextReader tr = new StreamReader(stream);
- return JsonConvert.DeserializeObject<T>(tr.ReadToEnd());
- }
+ public static T Deserialize<T>(Stream stream)
+ {
+ return Serializer.Deserialize<T>(stream);
+ }
- public static bool SetPropertyValue(PropertyInfo attribute, object obj, string value)
- {
- var name = attribute.PropertyType.Name.ToLower();
- switch (name)
- {
- case "string":
- attribute.SetValue(obj, value.Convert<string>());
- return true;
- case "boolean":
- if(char.IsDigit(value[0])) attribute.SetValue(obj, !value.Equals("0"));
- else attribute.SetValue(obj, value.Equals("True", StringComparison.InvariantCultureIgnoreCase));
- return true;
- case "byte":
- if (value.Equals("true", StringComparison.InvariantCultureIgnoreCase)) attribute.SetValue(obj, (byte)1);
- else if (value.Equals("false", StringComparison.InvariantCultureIgnoreCase)) attribute.SetValue(obj, byte.MinValue);
- else attribute.SetValue(obj, value.Convert<byte>());
- return true;
- case "sbyte":
- attribute.SetValue(obj, value.Convert<sbyte>());
- return true;
- case "uint16":
- attribute.SetValue(obj, value.Convert<ushort>());
- return true;
- case "int16":
- attribute.SetValue(obj, value.Convert<short>());
- return true;
- case "uint32":
- attribute.SetValue(obj, value.Convert<uint>());
- return true;
- case "int32":
- attribute.SetValue(obj, value.Convert<int>());
- return true;
- case "uint64":
- attribute.SetValue(obj, value.Convert<ulong>());
- return true;
- case "int64":
- attribute.SetValue(obj, value.Convert<long>());
- return true;
- case "single":
- attribute.SetValue(obj, (float)Math.Round(float.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
- return true;
- case "double":
- attribute.SetValue(obj, Math.Round(double.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
- return true;
- case "decimal":
- attribute.SetValue(obj, Math.Round(decimal.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
- return true;
- default:
- throw new Exception($"Data type '{name}' not recognized, contact developer.");
- }
- }
+ public static uint SerializeWriteFileStream(FileStream fs, object value, int offset = 0)
+ {
+ using var stream = Serialize(value);
+ return fs.WriteStream(stream, offset);
+ }
- public static int GetPixelPosition(int width, int x, int y)
+ public static bool SetPropertyValue(PropertyInfo attribute, object obj, string value)
+ {
+ var name = attribute.PropertyType.Name.ToLower();
+ switch (name)
{
- return width * y + x;
+ case "string":
+ attribute.SetValue(obj, value.Convert<string>());
+ return true;
+ case "boolean":
+ if(char.IsDigit(value[0])) attribute.SetValue(obj, !value.Equals("0"));
+ else attribute.SetValue(obj, value.Equals("True", StringComparison.InvariantCultureIgnoreCase));
+ return true;
+ case "byte":
+ if (value.Equals("true", StringComparison.InvariantCultureIgnoreCase)) attribute.SetValue(obj, (byte)1);
+ else if (value.Equals("false", StringComparison.InvariantCultureIgnoreCase)) attribute.SetValue(obj, byte.MinValue);
+ else attribute.SetValue(obj, value.Convert<byte>());
+ return true;
+ case "sbyte":
+ attribute.SetValue(obj, value.Convert<sbyte>());
+ return true;
+ case "uint16":
+ attribute.SetValue(obj, value.Convert<ushort>());
+ return true;
+ case "int16":
+ attribute.SetValue(obj, value.Convert<short>());
+ return true;
+ case "uint32":
+ attribute.SetValue(obj, value.Convert<uint>());
+ return true;
+ case "int32":
+ attribute.SetValue(obj, value.Convert<int>());
+ return true;
+ case "uint64":
+ attribute.SetValue(obj, value.Convert<ulong>());
+ return true;
+ case "int64":
+ attribute.SetValue(obj, value.Convert<long>());
+ return true;
+ case "single":
+ attribute.SetValue(obj, (float)Math.Round(float.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
+ return true;
+ case "double":
+ attribute.SetValue(obj, Math.Round(double.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
+ return true;
+ case "decimal":
+ attribute.SetValue(obj, Math.Round(decimal.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
+ return true;
+ default:
+ throw new Exception($"Data type '{name}' not recognized, contact developer.");
}
+ }
- public static void SwapVariables<T>(ref T var1, ref T var2)
- {
- (var1, var2) = (var2, var1);
- }
+ public static int GetPixelPosition(int width, int x, int y)
+ {
+ return width * y + x;
+ }
- public static float BrightnessToPercent(byte brightness, byte roundPlates = 2) => (float)Math.Round(brightness * 100 / 255f, roundPlates);
+ public static void SwapVariables<T>(ref T var1, ref T var2)
+ {
+ (var1, var2) = (var2, var1);
}
-}
+
+ public static float BrightnessToPercent(byte brightness, byte roundPlates = 2) => (float)Math.Round(brightness * 100 / 255f, roundPlates);
+} \ No newline at end of file
diff --git a/UVtools.Core/Layers/Issue.cs b/UVtools.Core/Layers/Issue.cs
index cb9c980..217d4ce 100644
--- a/UVtools.Core/Layers/Issue.cs
+++ b/UVtools.Core/Layers/Issue.cs
@@ -10,72 +10,71 @@ using System;
using System.Drawing;
using UVtools.Core.Extensions;
-namespace UVtools.Core.Layers
+namespace UVtools.Core.Layers;
+
+public class Issue
{
- public class Issue
+ /// <summary>
+ /// Gets the issue type associated
+ /// </summary>
+ public MainIssue? Parent { get; internal set; }
+
+ public MainIssue.IssueType? Type => Parent?.Type;
+
+ /// <summary>
+ /// Gets the layer where this issue is present
+ /// </summary>
+ public Layer Layer { get; init; }
+
+ /// <summary>
+ /// Gets the layer index
+ /// </summary>
+ public uint LayerIndex => Layer.Index;
+
+ /// <summary>
+ /// Gets the bounding rectangle of the area
+ /// </summary>
+ public Rectangle BoundingRectangle { get; init; }
+
+ /// <summary>
+ /// Gets the number of pixels
+ /// </summary>
+ public uint PixelsCount { get; init; }
+
+ /// <summary>
+ /// Gets the area of the issue
+ /// </summary>
+ public double Area { get; init; }
+
+ public Point FirstPoint { get; init; } = new(-1,-1);
+
+ public Issue(Layer layer, Rectangle boundingRectangle, double area)
+ {
+ Layer = layer;
+ BoundingRectangle = boundingRectangle;
+ Area = area;
+ }
+
+ public Issue(Layer layer, Rectangle boundingRectangle = default) : this(layer, boundingRectangle, boundingRectangle.Area())
+ { }
+
+ public virtual void Sort(){ }
+
+ protected bool Equals(Issue other)
+ {
+ return Type == other.Type && LayerIndex == other.LayerIndex && BoundingRectangle.Equals(other.BoundingRectangle) && PixelsCount == other.PixelsCount && Area.Equals(other.Area);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((Issue)obj);
+ }
+
+ public override int GetHashCode()
{
- /// <summary>
- /// Gets the issue type associated
- /// </summary>
- public MainIssue Parent { get; internal set; }
-
- public MainIssue.IssueType? Type => Parent?.Type;
-
- /// <summary>
- /// Gets the layer where this issue is present
- /// </summary>
- public Layer Layer { get; init; }
-
- /// <summary>
- /// Gets the layer index
- /// </summary>
- public uint LayerIndex => Layer.Index;
-
- /// <summary>
- /// Gets the bounding rectangle of the area
- /// </summary>
- public Rectangle BoundingRectangle { get; init; }
-
- /// <summary>
- /// Gets the number of pixels
- /// </summary>
- public uint PixelsCount { get; init; }
-
- /// <summary>
- /// Gets the area of the issue
- /// </summary>
- public double Area { get; init; }
-
- public Point FirstPoint { get; init; } = new(-1,-1);
-
- public Issue(Layer layer, Rectangle boundingRectangle, double area)
- {
- Layer = layer;
- BoundingRectangle = boundingRectangle;
- Area = area;
- }
-
- public Issue(Layer layer, Rectangle boundingRectangle = default) : this(layer, boundingRectangle, boundingRectangle.Area())
- { }
-
- public virtual void Sort(){ }
-
- protected bool Equals(Issue other)
- {
- return Type == other.Type && LayerIndex == other.LayerIndex && BoundingRectangle.Equals(other.BoundingRectangle) && PixelsCount == other.PixelsCount && Area.Equals(other.Area);
- }
-
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((Issue)obj);
- }
-
- public override int GetHashCode()
- {
- return HashCode.Combine(Layer, BoundingRectangle, PixelsCount, Area);
- }
+ return HashCode.Combine(Layer, BoundingRectangle, PixelsCount, Area);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Layers/IssueOfContours.cs b/UVtools.Core/Layers/IssueOfContours.cs
index 2c433b3..9b83464 100644
--- a/UVtools.Core/Layers/IssueOfContours.cs
+++ b/UVtools.Core/Layers/IssueOfContours.cs
@@ -11,45 +11,44 @@ using System.Drawing;
using System.Linq;
using UVtools.Core.Extensions;
-namespace UVtools.Core.Layers
+namespace UVtools.Core.Layers;
+
+public sealed class IssueOfContours : Issue
{
- public sealed class IssueOfContours : Issue
- {
- /// <summary>
- /// Gets the points contours of the issue
- /// </summary>
- public Point[][] Contours { get; init; }
+ /// <summary>
+ /// Gets the points contours of the issue
+ /// </summary>
+ public Point[][] Contours { get; init; }
- public IssueOfContours(Layer layer, IEnumerable<Point[]> contours, Rectangle boundingRectangle, double area) : base(layer, boundingRectangle, area)
- {
- Contours = contours.ToArray();
- PixelsCount = (uint)area;
- FirstPoint = Contours[0][0];
- }
+ public IssueOfContours(Layer layer, IEnumerable<Point[]> contours, Rectangle boundingRectangle, double area) : base(layer, boundingRectangle, area)
+ {
+ Contours = contours.ToArray();
+ PixelsCount = (uint)area;
+ FirstPoint = Contours[0][0];
+ }
- public IssueOfContours(Layer layer, IEnumerable<Point[]> contours, Rectangle boundingRectangle = default) : this(layer, contours, boundingRectangle, boundingRectangle.Area())
- { }
+ public IssueOfContours(Layer layer, IEnumerable<Point[]> contours, Rectangle boundingRectangle = default) : this(layer, contours, boundingRectangle, boundingRectangle.Area())
+ { }
- private bool Equals(IssueOfContours other)
+ private bool Equals(IssueOfContours other)
+ {
+ if (!base.Equals(other)) return false;
+ if (Contours.Length != other.Contours.Length) return false;
+ for (int i = 0; i < Contours.Length; i++)
{
- if (!base.Equals(other)) return false;
- if (Contours.Length != other.Contours.Length) return false;
- for (int i = 0; i < Contours.Length; i++)
- {
- if (!Contours[i].SequenceEqual(other.Contours[i])) return false;
- }
-
- return true;
+ if (!Contours[i].SequenceEqual(other.Contours[i])) return false;
}
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is IssueOfContours other && Equals(other);
- }
+ return true;
+ }
- public override int GetHashCode()
- {
- return (Contours != null ? Contours.GetHashCode() : 0);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is IssueOfContours other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return (Contours != null ? Contours.GetHashCode() : 0);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Layers/IssueOfPoints.cs b/UVtools.Core/Layers/IssueOfPoints.cs
index c3e5454..fd335bc 100644
--- a/UVtools.Core/Layers/IssueOfPoints.cs
+++ b/UVtools.Core/Layers/IssueOfPoints.cs
@@ -10,35 +10,34 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
-namespace UVtools.Core.Layers
+namespace UVtools.Core.Layers;
+
+public sealed class IssueOfPoints : Issue
{
- public sealed class IssueOfPoints : Issue
- {
- /// <summary>
- /// Gets the points containing the coordinates of the issue
- /// </summary>
- public Point[] Points { get; init; }
+ /// <summary>
+ /// Gets the points containing the coordinates of the issue
+ /// </summary>
+ public Point[] Points { get; init; }
- public IssueOfPoints(Layer layer, IEnumerable<Point> points, Rectangle boundingRectangle = default) : base(layer, boundingRectangle, points.Count())
- {
- Points = points.ToArray();
- PixelsCount = (uint)Points.Length;
- FirstPoint = Points[0];
- }
+ public IssueOfPoints(Layer layer, IEnumerable<Point> points, Rectangle boundingRectangle = default) : base(layer, boundingRectangle, points.Count())
+ {
+ Points = points.ToArray();
+ PixelsCount = (uint)Points.Length;
+ FirstPoint = Points[0];
+ }
- private bool Equals(IssueOfPoints other)
- {
- return Points.SequenceEqual(other.Points);
- }
+ private bool Equals(IssueOfPoints other)
+ {
+ return Points.SequenceEqual(other.Points);
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is IssueOfPoints other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is IssueOfPoints other && Equals(other);
+ }
- public override int GetHashCode()
- {
- return (Points != null ? Points.GetHashCode() : 0);
- }
+ public override int GetHashCode()
+ {
+ return (Points != null ? Points.GetHashCode() : 0);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs
index 408d5d6..a070d03 100644
--- a/UVtools.Core/Layers/Layer.cs
+++ b/UVtools.Core/Layers/Layer.cs
@@ -6,1298 +6,1361 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
using UVtools.Core.Operations;
-namespace UVtools.Core.Layers
+namespace UVtools.Core.Layers;
+
+/// <summary>
+/// Represent a Layer
+/// </summary>
+public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
{
+ #region Constants
+ public const byte HeightPrecision = 3;
+ public const decimal HeightPrecisionIncrement = 0.001M;
+ public const decimal MinimumHeight = 0.01M;
+ public const decimal MaximumHeight = 0.2M;
+ #endregion
+
+ #region Members
+
+ public object Mutex = new();
+ private byte[]? _compressedBytes;
+ private uint _nonZeroPixelCount;
+ private Rectangle _boundingRectangle = Rectangle.Empty;
+ private bool _isModified;
+ private uint _index;
+ private float _positionZ;
+ private float _lightOffDelay;
+ private float _waitTimeBeforeCure;
+ private float _exposureTime;
+ private float _waitTimeAfterCure;
+ private float _liftHeight = FileFormat.DefaultLiftHeight;
+ private float _liftSpeed = FileFormat.DefaultLiftSpeed;
+ private float _liftHeight2 = FileFormat.DefaultLiftHeight2;
+ private float _liftSpeed2 = FileFormat.DefaultLiftSpeed2;
+ private float _waitTimeAfterLift;
+ private float _retractSpeed = FileFormat.DefaultRetractSpeed;
+ private float _retractHeight2 = FileFormat.DefaultRetractHeight2;
+ private float _retractSpeed2 = FileFormat.DefaultRetractSpeed2;
+ private byte _lightPWM = FileFormat.DefaultLightPWM;
+ private float _materialMilliliters;
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets the parent SlicerFile
+ /// </summary>
+ public FileFormat SlicerFile { get; set; }
+
/// <summary>
- /// Represent a Layer
+ /// Gets the number of non zero pixels on this layer image
/// </summary>
- public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
- {
- #region Constants
- public const byte HeightPrecision = 3;
- public const decimal HeightPrecisionIncrement = 0.001M;
- public const decimal MinimumHeight = 0.01M;
- public const decimal MaximumHeight = 0.2M;
- #endregion
-
- #region Members
-
- public object Mutex = new();
- private byte[] _compressedBytes;
- private uint _nonZeroPixelCount;
- private Rectangle _boundingRectangle = Rectangle.Empty;
- private bool _isModified;
- private uint _index;
- private float _positionZ;
- private float _lightOffDelay;
- private float _waitTimeBeforeCure;
- private float _exposureTime;
- private float _waitTimeAfterCure;
- private float _liftHeight = FileFormat.DefaultLiftHeight;
- private float _liftSpeed = FileFormat.DefaultLiftSpeed;
- private float _liftHeight2 = FileFormat.DefaultLiftHeight2;
- private float _liftSpeed2 = FileFormat.DefaultLiftSpeed2;
- private float _waitTimeAfterLift;
- private float _retractSpeed = FileFormat.DefaultRetractSpeed;
- private float _retractHeight2 = FileFormat.DefaultRetractHeight2;
- private float _retractSpeed2 = FileFormat.DefaultRetractSpeed2;
- private byte _lightPWM = FileFormat.DefaultLightPWM;
- private float _materialMilliliters;
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Gets the parent layer manager
- /// </summary>
- public LayerManager ParentLayerManager { get; set; }
-
- public FileFormat SlicerFile => ParentLayerManager?.SlicerFile;
-
- /// <summary>
- /// Gets the number of non zero pixels on this layer image
- /// </summary>
- public uint NonZeroPixelCount
+ public uint NonZeroPixelCount
+ {
+ get => _nonZeroPixelCount;
+ internal set
{
- get => _nonZeroPixelCount;
- internal set
- {
- if (!RaiseAndSetIfChanged(ref _nonZeroPixelCount, value)) return;
- RaisePropertyChanged(nameof(Area));
- RaisePropertyChanged(nameof(Volume));
- MaterialMilliliters = -1; // Recalculate
- }
+ if (!RaiseAndSetIfChanged(ref _nonZeroPixelCount, value)) return;
+ RaisePropertyChanged(nameof(Area));
+ RaisePropertyChanged(nameof(Volume));
+ MaterialMilliliters = -1; // Recalculate
}
+ }
+
+ /// <summary>
+ /// Gets if this layer is empty/all black pixels
+ /// </summary>
+ public bool IsEmpty => _nonZeroPixelCount == 0;
- /// <summary>
- /// Gets if this layer is empty/all black pixels
- /// </summary>
- public bool IsEmpty => _nonZeroPixelCount == 0;
-
- /// <summary>
- /// Gets the layer area (XY)
- /// Pixel size * number of pixels
- /// </summary>
- public float Area => GetArea(3);
-
- /// <summary>
- /// Gets the layer volume (XYZ)
- /// Pixel size * number of pixels * layer height
- /// </summary>
- public float Volume => GetVolume(3);
-
- /// <summary>
- /// Gets the bounding rectangle for the image area
- /// </summary>
- public Rectangle BoundingRectangle
+ /// <summary>
+ /// Gets the layer area (XY) in mm^2
+ /// Pixel size * number of pixels
+ /// </summary>
+ public float Area => GetArea(3);
+
+ /// <summary>
+ /// Gets the layer volume (XYZ) in mm^3
+ /// Pixel size * number of pixels * layer height
+ /// </summary>
+ public float Volume => GetVolume(3);
+
+ /// <summary>
+ /// Gets the bounding rectangle for the image area
+ /// </summary>
+ public Rectangle BoundingRectangle
+ {
+ get => _boundingRectangle;
+ internal set
{
- get => _boundingRectangle;
- internal set
- {
- RaiseAndSetIfChanged(ref _boundingRectangle, value);
- RaisePropertyChanged(nameof(BoundingRectangleMillimeters));
- }
+ RaiseAndSetIfChanged(ref _boundingRectangle, value);
+ RaisePropertyChanged(nameof(BoundingRectangleMillimeters));
}
+ }
- /// <summary>
- /// Gets the bounding rectangle for the image area in millimeters
- /// </summary>
- public RectangleF BoundingRectangleMillimeters
+ /// <summary>
+ /// Gets the bounding rectangle for the image area in millimeters
+ /// </summary>
+ public RectangleF BoundingRectangleMillimeters
+ {
+ get
{
- get
- {
- if (SlicerFile is null) return RectangleF.Empty;
- var pixelSize = SlicerFile.PixelSize;
- return new RectangleF(
- (float) Math.Round(_boundingRectangle.X * pixelSize.Width, 2),
- (float)Math.Round(_boundingRectangle.Y * pixelSize.Height, 2),
- (float)Math.Round(_boundingRectangle.Width * pixelSize.Width, 2),
- (float)Math.Round(_boundingRectangle.Height * pixelSize.Height, 2));
- }
+ var pixelSize = SlicerFile.PixelSize;
+ return new RectangleF(
+ (float) Math.Round(_boundingRectangle.X * pixelSize.Width, 2),
+ (float)Math.Round(_boundingRectangle.Y * pixelSize.Height, 2),
+ (float)Math.Round(_boundingRectangle.Width * pixelSize.Width, 2),
+ (float)Math.Round(_boundingRectangle.Height * pixelSize.Height, 2));
}
+ }
+
+ /// <summary>
+ /// Gets if is the first layer
+ /// </summary>
+ public bool IsFirstLayer => _index == 0;
+
+ /// <summary>
+ /// Gets if layer is between first and last layer, aka, not first nor last layer
+ /// </summary>
+ public bool IsIntermediateLayer => !IsFirstLayer && !IsLastLayer;
- /// <summary>
- /// Gets if is the first layer
- /// </summary>
- public bool IsFirstLayer => _index == 0;
-
- /// <summary>
- /// Gets if layer is between first and last layer, aka, not first nor last layer
- /// </summary>
- public bool IsIntermediateLayer => !IsFirstLayer && !IsLastLayer;
-
- /// <summary>
- /// Gets if is the last layer
- /// </summary>
- public bool IsLastLayer => _index >= ParentLayerManager.LastLayerIndex;
-
- /// <summary>
- /// Gets if is in the bottom layer group
- /// </summary>
- public bool IsBottomLayer => (uint)(PositionZ / SlicerFile.LayerHeight) <= SlicerFile.BottomLayerCount;
-
- /// <summary>
- /// Gets if is in the normal layer group
- /// </summary>
- public bool IsNormalLayer => !IsBottomLayer;
-
- /// <summary>
- /// Gets if this layer is also an transition layer
- /// </summary>
- public bool IsTransitionLayer
+ /// <summary>
+ /// Gets if is the last layer
+ /// </summary>
+ public bool IsLastLayer => _index >= SlicerFile.LastLayerIndex;
+
+ /// <summary>
+ /// Gets if is in the bottom layer group
+ /// </summary>
+ public bool IsBottomLayer
+ {
+ get
{
- get
+ var bottomLayers = SlicerFile.BottomLayerCount;
+ if (_index < bottomLayers) return true;
+
+ // For same positioned layers
+ /*uint layerCount = 1;
+ bool nullFallback = false;
+ for (uint layerIndex = 1; layerIndex < _index && layerCount < bottomLayers; layerIndex++)
{
- if (SlicerFile is null) return false;
- return SlicerFile.TransitionLayerCount <= Number;
+ if (SlicerFile[layerIndex] is null)
+ {
+ nullFallback = true;
+ break;
+ }
+ if (SlicerFile[layerIndex].RelativePositionZ != 0) layerCount++;
}
+
+ if (nullFallback) return PositionZ / SlicerFile.LayerHeight <= bottomLayers;
+ return layerCount <= bottomLayers;*/
+ return PositionZ / SlicerFile.LayerHeight <= bottomLayers;
}
+ }
- /// <summary>
- /// Gets the previous layer, returns null if no previous layer
- /// </summary>
- public Layer PreviousLayer
- {
- get
- {
- if (ParentLayerManager is null || IsFirstLayer || _index > ParentLayerManager.Count) return null;
- return ParentLayerManager[_index - 1];
- }
+ /// <summary>
+ /// Gets if is in the normal layer group
+ /// </summary>
+ public bool IsNormalLayer => !IsBottomLayer;
+ /// <summary>
+ /// Gets if this layer is also an transition layer
+ /// </summary>
+ public bool IsTransitionLayer => SlicerFile.TransitionLayerCount <= Number;
+
+ /// <summary>
+ /// Gets the previous layer, returns null if no previous layer
+ /// </summary>
+ public Layer? PreviousLayer
+ {
+ get
+ {
+ if (IsFirstLayer || _index > SlicerFile.Count) return null;
+ return SlicerFile[_index - 1];
}
- /// <summary>
- /// Gets the next layer, returns null if no next layer
- /// </summary>
- public Layer NextLayer
+ }
+
+ /// <summary>
+ /// Gets the next layer, returns null if no next layer
+ /// </summary>
+ public Layer? NextLayer
+ {
+ get
{
- get
- {
- if (ParentLayerManager is null || _index >= ParentLayerManager.LastLayerIndex) return null;
- return ParentLayerManager[_index + 1];
- }
+ if (_index >= SlicerFile.LastLayerIndex) return null;
+ return SlicerFile[_index + 1];
}
+ }
- /// <summary>
- /// Gets the layer index
- /// </summary>
- public uint Index
+ /// <summary>
+ /// Gets the layer index
+ /// </summary>
+ public uint Index
+ {
+ get => _index;
+ set
{
- get => _index;
- set
- {
- if(!RaiseAndSetIfChanged(ref _index, value)) return;
- RaisePropertyChanged(nameof(Number));
- }
+ if(!RaiseAndSetIfChanged(ref _index, value)) return;
+ RaisePropertyChanged(nameof(Number));
}
+ }
- /// <summary>
- /// Gets the layer number, 1 started
- /// </summary>
- public uint Number => _index + 1;
+ /// <summary>
+ /// Gets the layer number, 1 started
+ /// </summary>
+ public uint Number => _index + 1;
- /// <summary>
- /// Gets or sets the absolute layer position on Z in mm
- /// </summary>
- public float PositionZ
+ /// <summary>
+ /// Gets or sets the absolute layer position on Z in mm
+ /// </summary>
+ public float PositionZ
+ {
+ get => _positionZ;
+ set
{
- get => _positionZ;
- set
- {
- if (!RaiseAndSetIfChanged(ref _positionZ, RoundHeight(value))) return;
- RaisePropertyChanged(nameof(RelativePositionZ));
- RaisePropertyChanged(nameof(LayerHeight));
- //MaterialMilliliters = -1; // Recalculate
- }
+ //if (value < 0) throw new ArgumentOutOfRangeException(nameof(PositionZ), "Value can't be negative");
+ if (!RaiseAndSetIfChanged(ref _positionZ, RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(RelativePositionZ));
+ RaisePropertyChanged(nameof(LayerHeight));
+ //MaterialMilliliters = -1; // Recalculate
}
+ }
- /// <summary>
- /// Gets the relative layer position on Z in mm (Relative to the previous layer)
- /// </summary>
- public float RelativePositionZ
+ /// <summary>
+ /// Gets the relative layer position on Z in mm (Relative to the previous layer)
+ /// </summary>
+ public float RelativePositionZ
+ {
+ get
{
- get
- {
- var previousLayer = PreviousLayer;
- return previousLayer is null ? LayerHeight : RoundHeight(_positionZ - previousLayer.PositionZ);
- }
+ var previousLayer = PreviousLayer;
+ return previousLayer is null ? _positionZ : RoundHeight(_positionZ - previousLayer.PositionZ);
}
+ set => PositionZ = _positionZ - RelativePositionZ + value;
+ }
- /// <summary>
- /// Gets or sets the wait time in seconds before cure the layer
- /// AKA: Light-off delay
- /// Chitubox: Rest time after retract
- /// Lychee: Wait before print
- /// </summary>
- public float WaitTimeBeforeCure
+ /// <summary>
+ /// Gets or sets the wait time in seconds before cure the layer
+ /// AKA: Light-off delay
+ /// Chitubox: Rest time after retract
+ /// Lychee: Wait before print
+ /// </summary>
+ public float WaitTimeBeforeCure
+ {
+ get => _waitTimeBeforeCure;
+ set
{
- get => _waitTimeBeforeCure;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeBeforeCure, SlicerFile.WaitTimeBeforeCure);
- if (!RaiseAndSetIfChanged(ref _waitTimeBeforeCure, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeBeforeCure, SlicerFile.WaitTimeBeforeCure);
+ if (!RaiseAndSetIfChanged(ref _waitTimeBeforeCure, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the exposure time in seconds
- /// </summary>
- public float ExposureTime
+ /// <summary>
+ /// Gets or sets the exposure time in seconds
+ /// </summary>
+ public float ExposureTime
+ {
+ get => _exposureTime;
+ set
{
- get => _exposureTime;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime);
- if(!RaiseAndSetIfChanged(ref _exposureTime, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime);
+ if(!RaiseAndSetIfChanged(ref _exposureTime, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the wait time in seconds after cure the layer
- /// Chitubox: Rest time before lift
- /// Lychee: Wait after print
- /// </summary>
- public float WaitTimeAfterCure
+ /// <summary>
+ /// Gets or sets the wait time in seconds after cure the layer
+ /// Chitubox: Rest time before lift
+ /// Lychee: Wait after print
+ /// </summary>
+ public float WaitTimeAfterCure
+ {
+ get => _waitTimeAfterCure;
+ set
{
- get => _waitTimeAfterCure;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterCure, SlicerFile.WaitTimeAfterCure);
- if (!RaiseAndSetIfChanged(ref _waitTimeAfterCure, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterCure, SlicerFile.WaitTimeAfterCure);
+ if (!RaiseAndSetIfChanged(ref _waitTimeAfterCure, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the layer off time in seconds
- /// </summary>
- public float LightOffDelay
+ /// <summary>
+ /// Gets or sets the layer off time in seconds
+ /// </summary>
+ public float LightOffDelay
+ {
+ get => _lightOffDelay;
+ set
{
- get => _lightOffDelay;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLightOffDelay, SlicerFile.LightOffDelay);
- if(!RaiseAndSetIfChanged(ref _lightOffDelay, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLightOffDelay, SlicerFile.LightOffDelay);
+ if(!RaiseAndSetIfChanged(ref _lightOffDelay, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets: Total lift height (lift1 + lift2)
- /// Sets: Lift1 with value and lift2 with 0
- /// </summary>
- public float LiftHeightTotal
+ /// <summary>
+ /// Gets: Total lift height (lift1 + lift2)
+ /// Sets: Lift1 with value and lift2 with 0
+ /// </summary>
+ public float LiftHeightTotal
+ {
+ get => (float)Math.Round(_liftHeight + _liftHeight2, 2);
+ set
{
- get => (float)Math.Round(_liftHeight + _liftHeight2, 2);
- set
- {
- LiftHeight = (float)Math.Round(value, 2);
- LiftHeight2 = 0;
- }
+ LiftHeight = (float)Math.Round(value, 2);
+ LiftHeight2 = 0;
}
+ }
- /// <summary>
- /// Gets or sets the lift height in mm
- /// </summary>
- public float LiftHeight
+ /// <summary>
+ /// Gets or sets the lift height in mm
+ /// </summary>
+ public float LiftHeight
+ {
+ get => _liftHeight;
+ set
{
- get => _liftHeight;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight);
- if(!RaiseAndSetIfChanged(ref _liftHeight, value)) return;
- RaisePropertyChanged(nameof(LiftHeightTotal));
- RetractHeight2 = _retractHeight2; // Sanitize
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight);
+ if(!RaiseAndSetIfChanged(ref _liftHeight, value)) return;
+ RaisePropertyChanged(nameof(LiftHeightTotal));
+ RetractHeight2 = _retractHeight2; // Sanitize
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the speed in mm/min
- /// </summary>
- public float LiftSpeed
+ /// <summary>
+ /// Gets or sets the speed in mm/min
+ /// </summary>
+ public float LiftSpeed
+ {
+ get => _liftSpeed;
+ set
{
- get => _liftSpeed;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
- if(!RaiseAndSetIfChanged(ref _liftSpeed, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
+ if(!RaiseAndSetIfChanged(ref _liftSpeed, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the lift height in mm
- /// </summary>
- public float LiftHeight2
+ /// <summary>
+ /// Gets or sets the lift height in mm
+ /// </summary>
+ public float LiftHeight2
+ {
+ get => _liftHeight2;
+ set
{
- get => _liftHeight2;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2);
- if (!RaiseAndSetIfChanged(ref _liftHeight2, value)) return;
- RaisePropertyChanged(nameof(LiftHeightTotal));
- RetractHeight2 = _retractHeight2; // Sanitize
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2);
+ if (!RaiseAndSetIfChanged(ref _liftHeight2, value)) return;
+ RaisePropertyChanged(nameof(LiftHeightTotal));
+ RetractHeight2 = _retractHeight2; // Sanitize
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the speed in mm/min
- /// </summary>
- public float LiftSpeed2
+ /// <summary>
+ /// Gets or sets the speed in mm/min
+ /// </summary>
+ public float LiftSpeed2
+ {
+ get => _liftSpeed2;
+ set
{
- get => _liftSpeed2;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2);
- if (!RaiseAndSetIfChanged(ref _liftSpeed2, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2);
+ if (!RaiseAndSetIfChanged(ref _liftSpeed2, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- public float WaitTimeAfterLift
+ public float WaitTimeAfterLift
+ {
+ get => _waitTimeAfterLift;
+ set
{
- get => _waitTimeAfterLift;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterLift, SlicerFile.WaitTimeAfterLift);
- if (!RaiseAndSetIfChanged(ref _waitTimeAfterLift, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value < 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterLift, SlicerFile.WaitTimeAfterLift);
+ if (!RaiseAndSetIfChanged(ref _waitTimeAfterLift, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets: Total retract height (retract1 + retract2) alias of <see cref="LiftHeightTotal"/>
- /// </summary>
- public float RetractHeightTotal => LiftHeightTotal;
+ /// <summary>
+ /// Gets: Total retract height (retract1 + retract2) alias of <see cref="LiftHeightTotal"/>
+ /// </summary>
+ public float RetractHeightTotal => LiftHeightTotal;
- /// <summary>
- /// Gets the retract height in mm
- /// </summary>
- public float RetractHeight => (float)Math.Round(LiftHeightTotal - _retractHeight2, 2);
+ /// <summary>
+ /// Gets the retract height in mm
+ /// </summary>
+ public float RetractHeight => (float)Math.Round(LiftHeightTotal - _retractHeight2, 2);
- /// <summary>
- /// Gets the speed in mm/min for the retracts
- /// </summary>
- public float RetractSpeed
+ /// <summary>
+ /// Gets the speed in mm/min for the retracts
+ /// </summary>
+ public float RetractSpeed
+ {
+ get => _retractSpeed;
+ set
{
- get => _retractSpeed;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed);
- if (!RaiseAndSetIfChanged(ref _retractSpeed, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed);
+ if (!RaiseAndSetIfChanged(ref _retractSpeed, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the second retract height in mm
- /// </summary>
- public virtual float RetractHeight2
+ /// <summary>
+ /// Gets or sets the second retract height in mm
+ /// </summary>
+ public virtual float RetractHeight2
+ {
+ get => _retractHeight2;
+ set
{
- get => _retractHeight2;
- set
- {
- value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
- RaiseAndSetIfChanged(ref _retractHeight2, value);
- RaisePropertyChanged(nameof(RetractHeight));
- RaisePropertyChanged(nameof(RetractHeightTotal));
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal);
+ RaiseAndSetIfChanged(ref _retractHeight2, value);
+ RaisePropertyChanged(nameof(RetractHeight));
+ RaisePropertyChanged(nameof(RetractHeightTotal));
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets the speed in mm/min for the retracts
- /// </summary>
- public virtual float RetractSpeed2
+ /// <summary>
+ /// Gets the speed in mm/min for the retracts
+ /// </summary>
+ public virtual float RetractSpeed2
+ {
+ get => _retractSpeed2;
+ set
{
- get => _retractSpeed2;
- set
- {
- value = (float)Math.Round(value, 2);
- if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
- if (!RaiseAndSetIfChanged(ref _retractSpeed2, value)) return;
- SlicerFile?.UpdatePrintTimeQueued();
- }
+ value = (float)Math.Round(value, 2);
+ if (value <= 0) value = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
+ if (!RaiseAndSetIfChanged(ref _retractSpeed2, value)) return;
+ SlicerFile.UpdatePrintTimeQueued();
}
+ }
- /// <summary>
- /// Gets or sets the pwm value from 0 to 255
- /// </summary>
- public byte LightPWM
+ /// <summary>
+ /// Gets or sets the pwm value from 0 to 255
+ /// </summary>
+ public byte LightPWM
+ {
+ get => _lightPWM;
+ set
{
- get => _lightPWM;
- set
- {
- //if (value == 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
- //if (value == 0) value = FileFormat.DefaultLightPWM;
- RaiseAndSetIfChanged(ref _lightPWM, value);
- }
+ //if (value == 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
+ //if (value == 0) value = FileFormat.DefaultLightPWM;
+ RaiseAndSetIfChanged(ref _lightPWM, value);
}
+ }
- /// <summary>
- /// Gets the minimum used speed in mm/min
- /// </summary>
- public float MinimumSpeed
+ /// <summary>
+ /// Gets the minimum used speed in mm/min
+ /// </summary>
+ public float MinimumSpeed
+ {
+ get
{
- get
- {
- float speed = float.MaxValue;
- if (LiftSpeed > 0) speed = Math.Min(speed, LiftSpeed);
- if (LiftSpeed2 > 0) speed = Math.Min(speed, LiftSpeed2);
- if (RetractSpeed > 0) speed = Math.Min(speed, RetractSpeed);
- if (RetractSpeed2 > 0) speed = Math.Min(speed, RetractSpeed2);
- if (Math.Abs(speed - float.MaxValue) < 0.01) return 0;
-
- return speed;
- }
+ float speed = float.MaxValue;
+ if (LiftSpeed > 0) speed = Math.Min(speed, LiftSpeed);
+ if (LiftSpeed2 > 0) speed = Math.Min(speed, LiftSpeed2);
+ if (RetractSpeed > 0) speed = Math.Min(speed, RetractSpeed);
+ if (RetractSpeed2 > 0) speed = Math.Min(speed, RetractSpeed2);
+ if (Math.Abs(speed - float.MaxValue) < 0.01) return 0;
+
+ return speed;
}
+ }
- /// <summary>
- /// Gets the maximum used speed in mm/min
- /// </summary>
- public float MaximumSpeed
+ /// <summary>
+ /// Gets the maximum used speed in mm/min
+ /// </summary>
+ public float MaximumSpeed
+ {
+ get
{
- get
- {
- float speed = LiftSpeed;
- speed = Math.Max(speed, LiftSpeed2);
- speed = Math.Max(speed, RetractSpeed);
- speed = Math.Max(speed, RetractSpeed2);
+ float speed = LiftSpeed;
+ speed = Math.Max(speed, LiftSpeed2);
+ speed = Math.Max(speed, RetractSpeed);
+ speed = Math.Max(speed, RetractSpeed2);
- return speed;
- }
+ return speed;
}
+ }
- /// <summary>
- /// Gets if this layer can be exposed to UV light
- /// </summary>
- public bool CanExpose => _exposureTime > 0 && _lightPWM > 0;
+ /// <summary>
+ /// Gets if this layer can be exposed to UV light
+ /// </summary>
+ public bool CanExpose => _exposureTime > 0 && _lightPWM > 0;
- /// <summary>
- /// Gets the layer height in millimeters of this layer
- /// </summary>
- public float LayerHeight
+ /// <summary>
+ /// Gets the layer height in millimeters of this layer
+ /// </summary>
+ public float LayerHeight
+ {
+ get
{
- get
- {
- if (IsFirstLayer) return _positionZ;
- var previousLayer = this;
-
- while ((previousLayer = previousLayer.PreviousLayer) is not null) // This cycle returns the correct layer height if two or more layers have the same position z
- {
- var layerHeight = RoundHeight(_positionZ - previousLayer.PositionZ);
- //Debug.WriteLine($"Layer {_index}-{previousLayer.Index}: {_positionZ} - {previousLayer.PositionZ}: {layerHeight}");
- if (layerHeight == 0f) continue;
- if (layerHeight < 0f) break;
- return layerHeight;
- }
+ if (IsFirstLayer) return _positionZ;
+ var previousLayer = this;
- return SlicerFile?.LayerHeight ?? 0;
+ while ((previousLayer = previousLayer!.PreviousLayer) is not null) // This cycle returns the correct layer height if two or more layers have the same position z
+ {
+ var layerHeight = RoundHeight(_positionZ - previousLayer.PositionZ);
+ //Debug.WriteLine($"Layer {_index}-{previousLayer.Index}: {_positionZ} - {previousLayer.PositionZ}: {layerHeight}");
+ if (layerHeight == 0f) continue;
+ if (layerHeight < 0f) break;
+ return layerHeight;
}
+
+ return SlicerFile.LayerHeight;
}
+ }
- /// <summary>
- /// Gets the computed material milliliters spent on this layer
- /// </summary>
- public float MaterialMilliliters
+ /// <summary>
+ /// Gets the computed material milliliters spent on this layer
+ /// </summary>
+ public float MaterialMilliliters
+ {
+ get => _materialMilliliters;
+ set
{
- get => _materialMilliliters;
- set
+ if (SlicerFile is null) return;
+ //var globalMilliliters = SlicerFile.MaterialMilliliters - _materialMilliliters;
+ if (value < 0)
{
- if (SlicerFile is null) return;
- //var globalMilliliters = SlicerFile.MaterialMilliliters - _materialMilliliters;
- if (value < 0)
- {
- value = (float) Math.Round(GetVolume() / 1000f, 4);
- }
-
- if(!RaiseAndSetIfChanged(ref _materialMilliliters, value)) return;
- RaisePropertyChanged(nameof(MaterialMillilitersPercent));
- SlicerFile.MaterialMilliliters = -1; // Recalculate global
- //ParentLayerManager.MaterialMillilitersTimer.Stop();
- //if(!ParentLayerManager.MaterialMillilitersTimer.Enabled)
- // ParentLayerManager.MaterialMillilitersTimer.Start();
+ value = (float) Math.Round(GetVolume() / 1000f, 4);
}
+
+ if(!RaiseAndSetIfChanged(ref _materialMilliliters, value)) return;
+ RaisePropertyChanged(nameof(MaterialMillilitersPercent));
+ SlicerFile.MaterialMilliliters = -1; // Recalculate global
+ //ParentLayerManager.MaterialMillilitersTimer.Stop();
+ //if(!ParentLayerManager.MaterialMillilitersTimer.Enabled)
+ // ParentLayerManager.MaterialMillilitersTimer.Start();
}
+ }
- /// <summary>
- /// Gets the computed material milliliters percentage compared to the rest of the model
- /// </summary>
- public float MaterialMillilitersPercent => _materialMilliliters * 100 / SlicerFile.MaterialMilliliters;
+ /// <summary>
+ /// Gets the computed material milliliters percentage compared to the rest of the model
+ /// </summary>
+ public float MaterialMillilitersPercent => _materialMilliliters * 100 / SlicerFile.MaterialMilliliters;
- /// <summary>
- /// Gets or sets layer image compressed data
- /// </summary>
- public byte[] CompressedBytes
+ /// <summary>
+ /// Gets or sets layer image compressed data
+ /// </summary>
+ public byte[]? CompressedBytes
+ {
+ get => _compressedBytes;
+ set
{
- get => _compressedBytes;
- set
- {
- _compressedBytes = value;
- IsModified = true;
- if (ParentLayerManager is not null)
- ParentLayerManager.BoundingRectangle = Rectangle.Empty;
- RaisePropertyChanged();
- RaisePropertyChanged(nameof(HaveImage));
- }
+ _compressedBytes = value;
+ IsModified = true;
+ SlicerFile.BoundingRectangle = Rectangle.Empty;
+ RaisePropertyChanged();
+ RaisePropertyChanged(nameof(HaveImage));
}
+ }
- /// <summary>
- /// True if this layer have an valid initialized image, otherwise false
- /// </summary>
- public bool HaveImage => _compressedBytes is not null && _compressedBytes.Length > 0;
+ /// <summary>
+ /// True if this layer have an valid initialized image, otherwise false
+ /// </summary>
+ public bool HaveImage => _compressedBytes is not null && _compressedBytes.Length > 0;
- /// <summary>
- /// Gets or sets a new image instance
- /// </summary>
- public Mat LayerMat
+ /// <summary>
+ /// Gets or sets a new image instance
+ /// </summary>
+ public Mat LayerMat
+ {
+ get
{
- get
- {
- if (!HaveImage) return null;
- Mat mat = new();
- CvInvoke.Imdecode(_compressedBytes, ImreadModes.Grayscale, mat);
- return mat;
- }
- set
- {
- CompressedBytes = value.GetPngByes();
- GetBoundingRectangle(value, true);
- RaisePropertyChanged();
- }
+ if (!HaveImage) return null!;
+ Mat mat = new();
+ CvInvoke.Imdecode(_compressedBytes, ImreadModes.Grayscale, mat);
+ return mat;
}
-
- //public Mat LayerMatBoundingRectangle => new(LayerMat, BoundingRectangle);
-
- /// <summary>
- /// Gets a new Brg image instance
- /// </summary>
- public Mat BrgMat
+ set
{
- get
- {
- var mat = LayerMat;
- CvInvoke.CvtColor(mat, mat, ColorConversion.Gray2Bgr);
- return mat;
- }
+ CompressedBytes = value?.GetPngByes();
+ GetBoundingRectangle(value, true);
+ RaisePropertyChanged();
}
+ }
- /// <summary>
- /// Gets a computed layer filename, padding zeros are equal to layer count digits
- /// </summary>
- public string Filename => FormatFileName("layer");
+ //public Mat LayerMatBoundingRectangle => new(LayerMat, BoundingRectangle);
- /// <summary>
- /// Gets if layer image has been modified
- /// </summary>
- public bool IsModified
+ /// <summary>
+ /// Gets a new Brg image instance
+ /// </summary>
+ public Mat BrgMat
+ {
+ get
{
- get => _isModified;
- set => RaiseAndSetIfChanged(ref _isModified, value);
+ var mat = LayerMat;
+ if (mat is null) return null!;
+ CvInvoke.CvtColor(mat, mat, ColorConversion.Gray2Bgr);
+ return mat;
}
+ }
+
+ /// <summary>
+ /// Gets a computed layer filename, padding zeros are equal to layer count digits
+ /// </summary>
+ public string Filename => FormatFileName("layer");
+
+ /// <summary>
+ /// Gets if layer image has been modified
+ /// </summary>
+ public bool IsModified
+ {
+ get => _isModified;
+ set => RaiseAndSetIfChanged(ref _isModified, value);
+ }
- /// <summary>
- /// Gets if this layer have same value parameters as global settings
- /// </summary>
- public bool IsUsingGlobalParameters
+ /// <summary>
+ /// Gets if this layer have same value parameters as global settings
+ /// </summary>
+ public bool IsUsingGlobalParameters
+ {
+ get
{
- get
+ if (SlicerFile is null) return false; // Cant verify
+ if (IsBottomLayer)
{
- if (SlicerFile is null) return false; // Cant verify
- if (IsBottomLayer)
- {
- if (
- _positionZ != RoundHeight(SlicerFile.LayerHeight * Number) ||
- _lightOffDelay != SlicerFile.BottomLightOffDelay ||
- _waitTimeBeforeCure != SlicerFile.BottomWaitTimeBeforeCure ||
- _exposureTime != SlicerFile.BottomExposureTime ||
- _waitTimeAfterCure != SlicerFile.BottomWaitTimeAfterCure ||
- _liftHeight != SlicerFile.BottomLiftHeight ||
- _liftSpeed != SlicerFile.BottomLiftSpeed ||
- _liftHeight2 != SlicerFile.BottomLiftHeight2 ||
- _liftSpeed2 != SlicerFile.BottomLiftSpeed2 ||
- _waitTimeAfterLift != SlicerFile.BottomWaitTimeAfterLift ||
- _retractSpeed != SlicerFile.BottomRetractSpeed ||
- _retractHeight2 != SlicerFile.BottomRetractHeight2 ||
- _retractSpeed2 != SlicerFile.BottomRetractSpeed2 ||
- _lightPWM != SlicerFile.BottomLightPWM
- )
- return false;
- }
- else
- {
- if (
- _positionZ != RoundHeight(SlicerFile.LayerHeight * Number) ||
- _lightOffDelay != SlicerFile.LightOffDelay ||
- _waitTimeBeforeCure != SlicerFile.WaitTimeBeforeCure ||
- _exposureTime != SlicerFile.ExposureTime ||
- _waitTimeAfterCure != SlicerFile.WaitTimeAfterCure ||
- _liftHeight != SlicerFile.LiftHeight ||
- _liftSpeed != SlicerFile.LiftSpeed ||
- _liftHeight2 != SlicerFile.LiftHeight2 ||
- _liftSpeed2 != SlicerFile.LiftSpeed2 ||
- _waitTimeAfterLift != SlicerFile.WaitTimeAfterLift ||
- _retractSpeed != SlicerFile.RetractSpeed ||
- _retractHeight2 != SlicerFile.RetractHeight2 ||
- _retractSpeed2 != SlicerFile.RetractSpeed2 ||
- _lightPWM != SlicerFile.LightPWM
- )
- return false;
- }
-
- return true;
+ if (
+ _positionZ != RoundHeight(SlicerFile.LayerHeight * Number) ||
+ _lightOffDelay != SlicerFile.BottomLightOffDelay ||
+ _waitTimeBeforeCure != SlicerFile.BottomWaitTimeBeforeCure ||
+ _exposureTime != SlicerFile.BottomExposureTime ||
+ _waitTimeAfterCure != SlicerFile.BottomWaitTimeAfterCure ||
+ _liftHeight != SlicerFile.BottomLiftHeight ||
+ _liftSpeed != SlicerFile.BottomLiftSpeed ||
+ _liftHeight2 != SlicerFile.BottomLiftHeight2 ||
+ _liftSpeed2 != SlicerFile.BottomLiftSpeed2 ||
+ _waitTimeAfterLift != SlicerFile.BottomWaitTimeAfterLift ||
+ _retractSpeed != SlicerFile.BottomRetractSpeed ||
+ _retractHeight2 != SlicerFile.BottomRetractHeight2 ||
+ _retractSpeed2 != SlicerFile.BottomRetractSpeed2 ||
+ _lightPWM != SlicerFile.BottomLightPWM
+ )
+ return false;
}
+ else
+ {
+ if (
+ _positionZ != RoundHeight(SlicerFile.LayerHeight * Number) ||
+ _lightOffDelay != SlicerFile.LightOffDelay ||
+ _waitTimeBeforeCure != SlicerFile.WaitTimeBeforeCure ||
+ _exposureTime != SlicerFile.ExposureTime ||
+ _waitTimeAfterCure != SlicerFile.WaitTimeAfterCure ||
+ _liftHeight != SlicerFile.LiftHeight ||
+ _liftSpeed != SlicerFile.LiftSpeed ||
+ _liftHeight2 != SlicerFile.LiftHeight2 ||
+ _liftSpeed2 != SlicerFile.LiftSpeed2 ||
+ _waitTimeAfterLift != SlicerFile.WaitTimeAfterLift ||
+ _retractSpeed != SlicerFile.RetractSpeed ||
+ _retractHeight2 != SlicerFile.RetractHeight2 ||
+ _retractSpeed2 != SlicerFile.RetractSpeed2 ||
+ _lightPWM != SlicerFile.LightPWM
+ )
+ return false;
+ }
+
+ return true;
}
+ }
- /// <summary>
- /// True if this layer is using TSMC values, otherwise false
- /// </summary>
- public bool IsUsingTSMC => LiftHeight2 > 0 || RetractHeight2 > 0;
+ /// <summary>
+ /// True if this layer is using TSMC values, otherwise false
+ /// </summary>
+ public bool IsUsingTSMC => LiftHeight2 > 0 || RetractHeight2 > 0;
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public Layer(uint index, LayerManager parentLayerManager)
- {
- ParentLayerManager = parentLayerManager;
- _index = index;
-
- if (parentLayerManager is null) return;
- _positionZ = SlicerFile.GetHeightFromLayer(index);
- _lightOffDelay = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLightOffDelay, SlicerFile.LightOffDelay);
- _waitTimeBeforeCure = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeBeforeCure, SlicerFile.WaitTimeBeforeCure);
- _exposureTime = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime);
- _waitTimeAfterCure = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterCure, SlicerFile.WaitTimeAfterCure);
- _liftHeight = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight);
- _liftSpeed = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
- _liftHeight2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2);
- _liftSpeed2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2);
- _waitTimeAfterLift = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterLift, SlicerFile.WaitTimeAfterLift);
- _retractSpeed = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed);
- _retractHeight2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractHeight2, SlicerFile.RetractHeight2);
- _retractSpeed2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
- _lightPWM = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
- }
+ public Layer(uint index, FileFormat slicerFile)
+ {
+ SlicerFile = slicerFile;
+ _index = index;
- public Layer(uint index, FileFormat slicerFile) : this(index, slicerFile.LayerManager) {}
+ //if (slicerFile is null) return;
+ _positionZ = SlicerFile.GetHeightFromLayer(index);
+ ResetParameters();
+ }
- public Layer(uint index, byte[] compressedBytes, LayerManager parentLayerManager) : this(index, parentLayerManager)
- {
- CompressedBytes = compressedBytes;
- _isModified = false;
- /*if (compressedBytes.Length > 0)
- {
- GetBoundingRectangle();
- }*/
- }
+ public Layer(uint index, byte[] compressedBytes, FileFormat slicerFile) : this(index, slicerFile)
+ {
+ CompressedBytes = compressedBytes;
+ _isModified = false;
+ }
- public Layer(uint index, byte[] compressedBytes, FileFormat slicerFile) : this(index, compressedBytes, slicerFile.LayerManager)
- {}
+ public Layer(uint index, Mat layerMat, FileFormat slicerFile) : this(index, slicerFile)
+ {
+ LayerMat = layerMat;
+ _isModified = false;
+ }
- public Layer(uint index, Mat layerMat, LayerManager parentLayerManager) : this(index, parentLayerManager)
- {
- LayerMat = layerMat;
- _isModified = false;
- }
+ public Layer(uint index, Stream stream, FileFormat slicerFile) : this(index, stream.ToArray(), slicerFile) { }
+ #endregion
- public Layer(uint index, Mat layerMat, FileFormat slicerFile) : this(index, layerMat, slicerFile.LayerManager) { }
+ #region Equatables
- public Layer(uint index, Stream stream, LayerManager parentLayerManager) : this(index, stream?.ToArray(), parentLayerManager) { }
- public Layer(uint index, Stream stream, FileFormat slicerFile) : this(index, stream?.ToArray(), slicerFile.LayerManager) { }
- #endregion
+ public static bool operator ==(Layer obj1, Layer obj2)
+ {
+ return obj1.Equals(obj2);
+ }
- #region Equatables
+ public static bool operator !=(Layer obj1, Layer obj2)
+ {
+ return !obj1.Equals(obj2);
+ }
- public static bool operator ==(Layer obj1, Layer obj2)
- {
- return obj1.Equals(obj2);
- }
+ public static bool operator >(Layer obj1, Layer obj2)
+ {
+ return obj1.Index > obj2.Index;
+ }
- public static bool operator !=(Layer obj1, Layer obj2)
- {
- return !obj1.Equals(obj2);
- }
+ public static bool operator <(Layer obj1, Layer obj2)
+ {
+ return obj1.Index < obj2.Index;
+ }
- public static bool operator >(Layer obj1, Layer obj2)
- {
- return obj1.Index > obj2.Index;
- }
+ public static bool operator >=(Layer obj1, Layer obj2)
+ {
+ return obj1.Index >= obj2.Index;
+ }
- public static bool operator <(Layer obj1, Layer obj2)
- {
- return obj1.Index < obj2.Index;
- }
+ public static bool operator <=(Layer obj1, Layer obj2)
+ {
+ return obj1.Index <= obj2.Index;
+ }
- public static bool operator >=(Layer obj1, Layer obj2)
- {
- return obj1.Index >= obj2.Index;
- }
+ public bool Equals(uint other)
+ {
+ return Index == other;
+ }
- public static bool operator <=(Layer obj1, Layer obj2)
- {
- return obj1.Index <= obj2.Index;
- }
+ public bool Equals(Layer? other)
+ {
+ if (other is null) return false;
+ if (ReferenceEquals(this, other)) return true;
+ if (_index != other._index) return false;
+ if (_compressedBytes?.Length != other._compressedBytes?.Length) return false;
+ return _compressedBytes.AsSpan().SequenceEqual(other._compressedBytes.AsSpan());
+ //return Equals(_compressedBytes, other._compressedBytes);
+ }
- public bool Equals(uint other)
- {
- return Index == other;
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((Layer)obj);
+ }
- public bool Equals(Layer other)
- {
- if (other is null) return false;
- if (ReferenceEquals(this, other)) return true;
- if (_index != other._index) return false;
- if (_compressedBytes?.Length != other._compressedBytes?.Length) return false;
- return _compressedBytes.AsSpan().SequenceEqual(other._compressedBytes.AsSpan());
- //return Equals(_compressedBytes, other._compressedBytes);
- }
+ public override int GetHashCode()
+ {
+ return (_compressedBytes != null ? _compressedBytes.GetHashCode() : 0);
+ }
- public override bool Equals(object obj)
+ private sealed class IndexRelationalComparer : IComparer<Layer>
+ {
+ public int Compare(Layer? x, Layer? y)
{
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((Layer)obj);
+ if (ReferenceEquals(x, y)) return 0;
+ if (ReferenceEquals(null, y)) return 1;
+ if (ReferenceEquals(null, x)) return -1;
+ return x.Index.CompareTo(y.Index);
}
+ }
- public override int GetHashCode()
- {
- return (_compressedBytes != null ? _compressedBytes.GetHashCode() : 0);
- }
+ public static IComparer<Layer> IndexComparer { get; } = new IndexRelationalComparer();
+ #endregion
- private sealed class IndexRelationalComparer : IComparer<Layer>
- {
- public int Compare(Layer x, Layer y)
- {
- if (ReferenceEquals(x, y)) return 0;
- if (ReferenceEquals(null, y)) return 1;
- if (ReferenceEquals(null, x)) return -1;
- return x.Index.CompareTo(y.Index);
- }
- }
+ #region Formaters
- public static IComparer<Layer> IndexComparer { get; } = new IndexRelationalComparer();
- #endregion
+ public override string ToString()
+ {
+ return $"{nameof(Index)}: {Index}, " +
+ $"{nameof(Filename)}: {Filename}, " +
+ $"{nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, " +
+ $"{nameof(BoundingRectangle)}: {BoundingRectangle}, " +
+ $"{nameof(IsBottomLayer)}: {IsBottomLayer}, " +
+ $"{nameof(IsNormalLayer)}: {IsNormalLayer}, " +
+ $"{nameof(LayerHeight)}: {LayerHeight}mm, " +
+ $"{nameof(PositionZ)}: {PositionZ}mm, " +
+ $"{nameof(LightOffDelay)}: {LightOffDelay}s, " +
+ $"{nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}s, " +
+ $"{nameof(ExposureTime)}: {ExposureTime}s, " +
+ $"{nameof(WaitTimeAfterCure)}: {WaitTimeAfterCure}s, " +
+ $"{nameof(LiftHeight)}: {LiftHeight}mm, " +
+ $"{nameof(LiftSpeed)}: {LiftSpeed}mm/mim, " +
+ $"{nameof(LiftHeight2)}: {LiftHeight2}mm, " +
+ $"{nameof(LiftSpeed2)}: {LiftSpeed2}mm/mim, " +
+ $"{nameof(WaitTimeAfterLift)}: {WaitTimeAfterLift}s, " +
+ $"{nameof(RetractHeight)}: {RetractHeight}mm, " +
+ $"{nameof(RetractSpeed)}: {RetractSpeed}mm/mim, " +
+ $"{nameof(RetractHeight2)}: {RetractHeight2}mm, " +
+ $"{nameof(RetractSpeed2)}: {RetractSpeed2}mm/mim, " +
+ $"{nameof(LightPWM)}: {LightPWM}, " +
+ $"{nameof(IsModified)}: {IsModified}, " +
+ $"{nameof(IsUsingGlobalParameters)}: {IsUsingGlobalParameters}";
+ }
+ #endregion
- #region Formaters
+ #region Methods
- public override string ToString()
- {
- return $"{nameof(Index)}: {Index}, " +
- $"{nameof(Filename)}: {Filename}, " +
- $"{nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, " +
- $"{nameof(BoundingRectangle)}: {BoundingRectangle}, " +
- $"{nameof(IsBottomLayer)}: {IsBottomLayer}, " +
- $"{nameof(IsNormalLayer)}: {IsNormalLayer}, " +
- $"{nameof(LayerHeight)}: {LayerHeight}mm, " +
- $"{nameof(PositionZ)}: {PositionZ}mm, " +
- $"{nameof(LightOffDelay)}: {LightOffDelay}s, " +
- $"{nameof(WaitTimeBeforeCure)}: {WaitTimeBeforeCure}s, " +
- $"{nameof(ExposureTime)}: {ExposureTime}s, " +
- $"{nameof(WaitTimeAfterCure)}: {WaitTimeAfterCure}s, " +
- $"{nameof(LiftHeight)}: {LiftHeight}mm, " +
- $"{nameof(LiftSpeed)}: {LiftSpeed}mm/mim, " +
- $"{nameof(LiftHeight2)}: {LiftHeight2}mm, " +
- $"{nameof(LiftSpeed2)}: {LiftSpeed2}mm/mim, " +
- $"{nameof(WaitTimeAfterLift)}: {WaitTimeAfterLift}s, " +
- $"{nameof(RetractHeight)}: {RetractHeight}mm, " +
- $"{nameof(RetractSpeed)}: {RetractSpeed}mm/mim, " +
- $"{nameof(RetractHeight2)}: {RetractHeight2}mm, " +
- $"{nameof(RetractSpeed2)}: {RetractSpeed2}mm/mim, " +
- $"{nameof(LightPWM)}: {LightPWM}, " +
- $"{nameof(IsModified)}: {IsModified}, " +
- $"{nameof(IsUsingGlobalParameters)}: {IsUsingGlobalParameters}";
- }
- #endregion
+ /// <summary>
+ /// Reset all parameters to the default values from the global parameters
+ /// </summary>
+ public void ResetParameters()
+ {
+ _lightOffDelay = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLightOffDelay, SlicerFile.LightOffDelay);
+ _waitTimeBeforeCure = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeBeforeCure, SlicerFile.WaitTimeBeforeCure);
+ _exposureTime = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime);
+ _waitTimeAfterCure = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterCure, SlicerFile.WaitTimeAfterCure);
+ _liftHeight = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight);
+ _liftSpeed = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
+ _liftHeight2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2);
+ _liftSpeed2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2);
+ _waitTimeAfterLift = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomWaitTimeAfterLift, SlicerFile.WaitTimeAfterLift);
+ _retractSpeed = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed);
+ _retractHeight2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractHeight2, SlicerFile.RetractHeight2);
+ _retractSpeed2 = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
+ _lightPWM = SlicerFile.GetBottomOrNormalValue(this, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
+ }
- #region Methods
+ /// <summary>
+ /// Gets the layer area (XY) in mm^2
+ /// Pixel size * number of pixels
+ /// </summary>
+ public float GetArea() => SlicerFile.PixelArea * _nonZeroPixelCount;
- /// <summary>
- /// Gets the layer area (XY)
- /// Pixel size * number of pixels
- /// </summary>
- public float GetArea() => (SlicerFile?.PixelArea ?? 0) * _nonZeroPixelCount;
+ /// <summary>
+ /// Gets the layer area (XY) in mm^2
+ /// Pixel size * number of pixels
+ /// </summary>
+ public float GetArea(byte roundToDigits) => (float)Math.Round(GetArea(), roundToDigits);
- /// <summary>
- /// Gets the layer area (XY)
- /// Pixel size * number of pixels
- /// </summary>
- public float GetArea(byte roundToDigits) => (float)Math.Round(GetArea(), roundToDigits);
+ /// <summary>
+ /// Gets the layer volume (XYZ) in mm^3
+ /// Pixel size * number of pixels * layer height
+ /// </summary>
+ public float GetVolume() => GetArea() * LayerHeight;
- /// <summary>
- /// Gets the layer volume (XYZ)
- /// Pixel size * number of pixels * layer height
- /// </summary>
- public float GetVolume() => GetArea() * LayerHeight;
+ /// <summary>
+ /// Gets the layer volume (XYZ) in mm^3
+ /// Pixel size * number of pixels * layer height
+ /// </summary>
+ public float GetVolume(byte roundToDigits) => (float)Math.Round(GetArea() * LayerHeight, roundToDigits);
- /// <summary>
- /// Gets the layer volume (XYZ)
- /// Pixel size * number of pixels * layer height
- /// </summary>
- public float GetVolume(byte roundToDigits) => (float)Math.Round(GetArea() * LayerHeight, roundToDigits);
+ public float CalculateMotorMovementTime(float extraTime = 0)
+ {
+ return OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime);
+ }
- public float CalculateMotorMovementTime(float extraTime = 0)
- {
- return OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime);
- }
+ public float CalculateLightOffDelay(float extraTime = 0)
+ {
+ if (SlicerFile is null) return OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime);
+ return SlicerFile.SupportsGCode ? extraTime : OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime);
+ }
- public float CalculateLightOffDelay(float extraTime = 0)
- {
- if (SlicerFile is null) return OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime);
- return SlicerFile.SupportsGCode ? extraTime : OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime);
- }
+ public void SetLightOffDelay(float extraTime = 0)
+ {
+ LightOffDelay = CalculateLightOffDelay(extraTime);
+ }
- public void SetLightOffDelay(float extraTime = 0)
+ /// <summary>
+ /// Gets the wait time before cure, if not available calculate it from light off delay
+ /// </summary>
+ /// <returns></returns>
+ public float GetBottomWaitTimeBeforeCure()
+ {
+ if (SlicerFile.CanUseLayerWaitTimeBeforeCure)
{
- LightOffDelay = CalculateLightOffDelay(extraTime);
+ return WaitTimeBeforeCure;
}
- /// <summary>
- /// Attempt to set wait time before cure if supported, otherwise fallback to light-off delay
- /// </summary>
- /// <param name="time">The time to set</param>
- /// <param name="zeroLightOffDelayCalculateBase">When true and time is zero, it will calculate light-off delay without extra time, otherwise false to set light-off delay to 0 when time is 0</param>
- public void SetWaitTimeBeforeCureOrLightOffDelay(float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ if (SlicerFile.CanUseLayerLightOffDelay)
{
- if (SlicerFile.CanUseLayerWaitTimeBeforeCure)
- {
- LightOffDelay = 0;
- WaitTimeBeforeCure = time;
- }
- else
- {
- if (time == 0 && !zeroLightOffDelayCalculateBase)
- {
- LightOffDelay = 0;
- return;
- }
- SetLightOffDelay(time);
- }
+ return (float)Math.Max(0, Math.Round(LightOffDelay - CalculateLightOffDelay(), 2));
}
- /// <summary>
- /// Zero all 'wait times / delays' for this layer
- /// </summary>
- public void SetNoDelays()
+ return 0;
+ }
+
+ /// <summary>
+ /// Attempt to set wait time before cure if supported, otherwise fallback to light-off delay
+ /// </summary>
+ /// <param name="time">The time to set</param>
+ /// <param name="zeroLightOffDelayCalculateBase">When true and time is zero, it will calculate light-off delay without extra time, otherwise false to set light-off delay to 0 when time is 0</param>
+ public void SetWaitTimeBeforeCureOrLightOffDelay(float time = 0, bool zeroLightOffDelayCalculateBase = false)
+ {
+ if (SlicerFile.CanUseLayerWaitTimeBeforeCure)
{
LightOffDelay = 0;
- WaitTimeBeforeCure = 0;
- WaitTimeAfterCure = 0;
- WaitTimeAfterLift = 0;
+ WaitTimeBeforeCure = time;
}
-
- public string FormatFileName(string prepend, byte padDigits, bool layerIndexZeroStarted = true, string appendExt = ".png")
+ else if(SlicerFile.CanUseLayerLightOffDelay)
{
- var index = Index;
- if (!layerIndexZeroStarted)
+ if (time == 0 && !zeroLightOffDelayCalculateBase)
{
- index++;
+ LightOffDelay = 0;
+ return;
}
- return $"{prepend}{index.ToString().PadLeft(padDigits, '0')}{appendExt}";
+ SetLightOffDelay(time);
}
+ }
- public string FormatFileName(string prepend = "", bool layerIndexZeroStarted = true, string appendExt = ".png")
- => FormatFileName(prepend, ParentLayerManager.LayerDigits, layerIndexZeroStarted, appendExt);
-
- public string FormatFileName(byte padDigits, bool layerIndexZeroStarted = true, string appendExt = ".png")
- => FormatFileName(string.Empty, padDigits, layerIndexZeroStarted, appendExt);
-
+ /// <summary>
+ /// Zero all 'wait times / delays' for this layer
+ /// </summary>
+ public void SetNoDelays()
+ {
+ LightOffDelay = 0;
+ WaitTimeBeforeCure = 0;
+ WaitTimeAfterCure = 0;
+ WaitTimeAfterLift = 0;
+ }
- public Rectangle GetBoundingRectangle(Mat mat = null, bool reCalculate = false)
+ public string FormatFileName(string prepend, byte padDigits, bool layerIndexZeroStarted = true, string appendExt = ".png")
+ {
+ var index = Index;
+ if (!layerIndexZeroStarted)
{
- if (_nonZeroPixelCount > 0 && !reCalculate)
- {
- return BoundingRectangle;
- }
- bool needDispose = false;
- if (mat is null)
- {
- if (_compressedBytes is null || _compressedBytes.Length == 0)
- {
- NonZeroPixelCount = 0;
- return Rectangle.Empty;
- }
- mat = LayerMat;
- needDispose = true;
- }
+ index++;
+ }
+ return $"{prepend}{index.ToString().PadLeft(padDigits, '0')}{appendExt}";
+ }
- NonZeroPixelCount = (uint)CvInvoke.CountNonZero(mat);
- BoundingRectangle = _nonZeroPixelCount > 0 ? CvInvoke.BoundingRectangle(mat) : Rectangle.Empty;
+ public string FormatFileName(string prepend = "", bool layerIndexZeroStarted = true, string appendExt = ".png")
+ => FormatFileName(prepend, SlicerFile.LayerDigits, layerIndexZeroStarted, appendExt);
+ public string FormatFileName(byte padDigits, bool layerIndexZeroStarted = true, string appendExt = ".png")
+ => FormatFileName(string.Empty, padDigits, layerIndexZeroStarted, appendExt);
- if (needDispose) mat.Dispose();
+ public Rectangle GetBoundingRectangle(Mat? mat = null, bool reCalculate = false)
+ {
+ if (_nonZeroPixelCount > 0 && !reCalculate)
+ {
return BoundingRectangle;
}
-
- public bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, decimal value)
+ bool needDispose = false;
+ if (mat is null)
{
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.PositionZ))
+ if (_compressedBytes is null || _compressedBytes.Length == 0)
{
- PositionZ = (float)value;
- return true;
+ NonZeroPixelCount = 0;
+ return Rectangle.Empty;
}
+ mat = LayerMat;
+ needDispose = true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LightOffDelay))
- {
- LightOffDelay = (float)value;
- return true;
- }
+ NonZeroPixelCount = (uint)CvInvoke.CountNonZero(mat);
+ BoundingRectangle = _nonZeroPixelCount > 0 ? CvInvoke.BoundingRectangle(mat) : Rectangle.Empty;
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.WaitTimeBeforeCure))
- {
- WaitTimeBeforeCure = (float)value;
- return true;
- }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.ExposureTime))
- {
- ExposureTime = (float)value;
- return true;
- }
+ if (needDispose) mat!.Dispose();
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.WaitTimeAfterCure))
- {
- WaitTimeAfterCure = (float)value;
- return true;
- }
+ return BoundingRectangle;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftHeight))
- {
- LiftHeight = (float)value;
- return true;
- }
+ public bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, decimal value)
+ {
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.PositionZ))
+ {
+ PositionZ = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftSpeed))
- {
- LiftSpeed = (float)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LightOffDelay))
+ {
+ LightOffDelay = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftHeight2))
- {
- LiftHeight2 = (float)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.WaitTimeBeforeCure))
+ {
+ WaitTimeBeforeCure = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftSpeed2))
- {
- LiftSpeed2 = (float)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.ExposureTime))
+ {
+ ExposureTime = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.WaitTimeAfterLift))
- {
- WaitTimeAfterLift = (float)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.WaitTimeAfterCure))
+ {
+ WaitTimeAfterCure = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractSpeed))
- {
- RetractSpeed = (float)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftHeight))
+ {
+ LiftHeight = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractHeight2))
- {
- RetractHeight2 = (float)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftSpeed))
+ {
+ LiftSpeed = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractSpeed2))
- {
- RetractSpeed2 = (float)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftHeight2))
+ {
+ LiftHeight2 = (float)value;
+ return true;
+ }
- if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LightPWM))
- {
- LightPWM = (byte)value;
- return true;
- }
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftSpeed2))
+ {
+ LiftSpeed2 = (float)value;
+ return true;
+ }
- return false;
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.WaitTimeAfterLift))
+ {
+ WaitTimeAfterLift = (float)value;
+ return true;
}
- public byte SetValuesFromPrintParametersModifiers(FileFormat.PrintParameterModifier[] modifiers)
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractSpeed))
{
- if (modifiers is null) return 0;
- byte changed = 0;
- foreach (var modifier in modifiers)
- {
- if (!modifier.HasChanged) continue;
- SetValueFromPrintParameterModifier(modifier, modifier.NewValue);
- changed++;
- }
+ RetractSpeed = (float)value;
+ return true;
+ }
+
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractHeight2))
+ {
+ RetractHeight2 = (float)value;
+ return true;
+ }
+
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractSpeed2))
+ {
+ RetractSpeed2 = (float)value;
+ return true;
+ }
- return changed;
+ if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LightPWM))
+ {
+ LightPWM = (byte)value;
+ return true;
}
- /*
- /// <summary>
- /// Gets all islands start pixel location for this layer
- /// https://www.geeksforgeeks.org/find-number-of-islands/
- /// </summary>
- /// <returns><see cref="List{T}"/> holding all islands coordinates</returns>
- public List<LayerIssue> GetIssues(uint requiredPixelsToSupportIsland = 5)
+ return false;
+ }
+
+ public byte SetValuesFromPrintParametersModifiers(FileFormat.PrintParameterModifier[]? modifiers)
+ {
+ if (modifiers is null) return 0;
+ byte changed = 0;
+ foreach (var modifier in modifiers)
{
- if (requiredPixelsToSupportIsland == 0)
- requiredPixelsToSupportIsland = 1;
+ if (!modifier.HasChanged) continue;
+ SetValueFromPrintParameterModifier(modifier, modifier.NewValue);
+ changed++;
+ }
+
+ return changed;
+ }
+
+ /*
+ /// <summary>
+ /// Gets all islands start pixel location for this layer
+ /// https://www.geeksforgeeks.org/find-number-of-islands/
+ /// </summary>
+ /// <returns><see cref="List{T}"/> holding all islands coordinates</returns>
+ public List<LayerIssue> GetIssues(uint requiredPixelsToSupportIsland = 5)
+ {
+ if (requiredPixelsToSupportIsland == 0)
+ requiredPixelsToSupportIsland = 1;
- // These arrays are used to
- // get row and column numbers
- // of 8 neighbors of a given cell
- List<LayerIssue> result = new();
- List<Point> pixels = new();
+ // These arrays are used to
+ // get row and column numbers
+ // of 8 neighbors of a given cell
+ List<LayerIssue> result = new();
+ List<Point> pixels = new();
- var mat = LayerMat;
- var bytes = mat.GetDataSpan<byte>();
+ var mat = LayerMat;
+ var bytes = mat.GetDataSpan<byte>();
- var previousLayerImage = PreviousLayer()?.LayerMat;
- var previousBytes = previousLayerImage?.GetBytes();
+ var previousLayerImage = PreviousLayer()?.LayerMat;
+ var previousBytes = previousLayerImage?.GetBytes();
- // Make a bool array to
- // mark visited cells.
- // Initially all cells
- // are unvisited
- bool[,] visited = new bool[mat.Width, mat.Height];
+ // Make a bool array to
+ // mark visited cells.
+ // Initially all cells
+ // are unvisited
+ bool[,] visited = new bool[mat.Width, mat.Height];
- // Initialize count as 0 and
- // traverse through the all
- // cells of given matrix
- //uint count = 0;
+ // Initialize count as 0 and
+ // traverse through the all
+ // cells of given matrix
+ //uint count = 0;
- // Island checker
- sbyte[] rowNbr = { -1, -1, -1, 0, 0, 1, 1, 1 };
- sbyte[] colNbr = { -1, 0, 1, -1, 1, -1, 0, 1 };
- const uint minPixel = 10;
- const uint minPixelForSupportIsland = 200;
- int pixelIndex;
- uint islandSupportingPixels;
- if (Index > 0)
+ // Island checker
+ sbyte[] rowNbr = { -1, -1, -1, 0, 0, 1, 1, 1 };
+ sbyte[] colNbr = { -1, 0, 1, -1, 1, -1, 0, 1 };
+ const uint minPixel = 10;
+ const uint minPixelForSupportIsland = 200;
+ int pixelIndex;
+ uint islandSupportingPixels;
+ if (Index > 0)
+ {
+ for (int y = 0; y < mat.Height; y++)
{
- for (int y = 0; y < mat.Height; y++)
+ for (int x = 0; x < mat.Width; x++)
{
- for (int x = 0; x < mat.Width; x++)
- {
- pixelIndex = y * mat.Width + x;
+ pixelIndex = y * mat.Width + x;
- if (bytes[pixelIndex] > minPixel && !visited[x, y])
- {
- // If a cell with value 1 is not
- // visited yet, then new island
- // found, Visit all cells in this
- // island and increment island count
- pixels.Clear();
- pixels.Add(new Point(x, y));
- islandSupportingPixels = previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0;
+ if (bytes[pixelIndex] > minPixel && !visited[x, y])
+ {
+ // If a cell with value 1 is not
+ // visited yet, then new island
+ // found, Visit all cells in this
+ // island and increment island count
+ pixels.Clear();
+ pixels.Add(new Point(x, y));
+ islandSupportingPixels = previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0;
- int minX = x;
- int maxX = x;
- int minY = y;
- int maxY = y;
+ int minX = x;
+ int maxX = x;
+ int minY = y;
+ int maxY = y;
- int x2;
- int y2;
+ int x2;
+ int y2;
- Queue<Point> queue = new();
- queue.Enqueue(new Point(x, y));
- // Mark this cell as visited
- visited[x, y] = true;
+ Queue<Point> queue = new();
+ queue.Enqueue(new Point(x, y));
+ // Mark this cell as visited
+ visited[x, y] = true;
- while (queue.Count > 0)
+ while (queue.Count > 0)
+ {
+ var point = queue.Dequeue();
+ y2 = point.Y;
+ x2 = point.X;
+ for (byte k = 0; k < 8; k++)
{
- var point = queue.Dequeue();
- y2 = point.Y;
- x2 = point.X;
- for (byte k = 0; k < 8; k++)
+ //if (isSafe(y2 + rowNbr[k], x2 + colNbr[k]))
+ var tempy2 = y2 + rowNbr[k];
+ var tempx2 = x2 + colNbr[k];
+ pixelIndex = tempy2 * mat.Width + tempx2;
+ if (tempy2 >= 0 &&
+ tempy2 < mat.Height &&
+ tempx2 >= 0 && tempx2 < mat.Width &&
+ bytes[pixelIndex] >= minPixel &&
+ !visited[tempx2, tempy2])
{
- //if (isSafe(y2 + rowNbr[k], x2 + colNbr[k]))
- var tempy2 = y2 + rowNbr[k];
- var tempx2 = x2 + colNbr[k];
- pixelIndex = tempy2 * mat.Width + tempx2;
- if (tempy2 >= 0 &&
- tempy2 < mat.Height &&
- tempx2 >= 0 && tempx2 < mat.Width &&
- bytes[pixelIndex] >= minPixel &&
- !visited[tempx2, tempy2])
- {
- visited[tempx2, tempy2] = true;
- point = new Point(tempx2, tempy2);
- pixels.Add(point);
- queue.Enqueue(point);
-
- minX = Math.Min(minX, tempx2);
- maxX = Math.Max(maxX, tempx2);
- minY = Math.Min(minY, tempy2);
- maxY = Math.Max(maxY, tempy2);
-
- islandSupportingPixels += previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0;
- }
+ visited[tempx2, tempy2] = true;
+ point = new Point(tempx2, tempy2);
+ pixels.Add(point);
+ queue.Enqueue(point);
+
+ minX = Math.Min(minX, tempx2);
+ maxX = Math.Max(maxX, tempx2);
+ minY = Math.Min(minY, tempy2);
+ maxY = Math.Max(maxY, tempy2);
+
+ islandSupportingPixels += previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0;
}
}
- //count++;
-
- if (islandSupportingPixels >= requiredPixelsToSupportIsland)
- continue; // Not a island, bounding is strong
- if (islandSupportingPixels > 0 && pixels.Count < requiredPixelsToSupportIsland &&
- islandSupportingPixels >= Math.Max(1, pixels.Count / 2)) continue; // Not a island
- result.Add(new LayerIssue(this, LayerIssue.IssueType.Island, pixels.ToArray(), new Rectangle(minX, minY, maxX - minX, maxY - minY)));
}
+ //count++;
+
+ if (islandSupportingPixels >= requiredPixelsToSupportIsland)
+ continue; // Not a island, bounding is strong
+ if (islandSupportingPixels > 0 && pixels.Count < requiredPixelsToSupportIsland &&
+ islandSupportingPixels >= Math.Max(1, pixels.Count / 2)) continue; // Not a island
+ result.Add(new LayerIssue(this, LayerIssue.IssueType.Island, pixels.ToArray(), new Rectangle(minX, minY, maxX - minX, maxY - minY)));
}
}
}
+ }
- pixels.Clear();
+ pixels.Clear();
- // TouchingBounds Checker
- for (int x = 0; x < mat.Width; x++) // Check Top and Bottom bounds
+ // TouchingBounds Checker
+ for (int x = 0; x < mat.Width; x++) // Check Top and Bottom bounds
+ {
+ if (bytes[x] >= 200) // Top
{
- if (bytes[x] >= 200) // Top
- {
- pixels.Add(new Point(x, 0));
- }
-
- if (bytes[mat.Width * mat.Height - mat.Width + x] >= 200) // Bottom
- {
- pixels.Add(new Point(x, mat.Height - 1));
- }
+ pixels.Add(new Point(x, 0));
}
- for (int y = 0; y < mat.Height; y++) // Check Left and Right bounds
+ if (bytes[mat.Width * mat.Height - mat.Width + x] >= 200) // Bottom
{
- if (bytes[y * mat.Width] >= 200) // Left
- {
- pixels.Add(new Point(0, y));
- }
-
- if (bytes[y * mat.Width + mat.Width - 1] >= 200) // Right
- {
- pixels.Add(new Point(mat.Width - 1, y));
- }
+ pixels.Add(new Point(x, mat.Height - 1));
}
+ }
- if (pixels.Count > 0)
+ for (int y = 0; y < mat.Height; y++) // Check Left and Right bounds
+ {
+ if (bytes[y * mat.Width] >= 200) // Left
{
- result.Add(new LayerIssue(this, LayerIssue.IssueType.TouchingBound, pixels.ToArray()));
+ pixels.Add(new Point(0, y));
}
- pixels.Clear();
-
- return result;
- }*/
-
- /// <summary>
- /// Copy all lift parameters from this layer to an target layer
- /// </summary>
- /// <param name="layer"></param>
- public void CopyLiftTo(Layer layer)
- {
- layer.LiftHeight = _liftHeight;
- layer.LiftHeight2 = _liftHeight2;
- layer.LiftSpeed = _liftSpeed;
- layer.LiftSpeed2 = _liftSpeed2;
- layer.RetractHeight2 = _retractHeight2;
- layer.RetractSpeed = _retractSpeed;
- layer.RetractSpeed2 = _retractSpeed2;
+ if (bytes[y * mat.Width + mat.Width - 1] >= 200) // Right
+ {
+ pixels.Add(new Point(mat.Width - 1, y));
+ }
}
- /// <summary>
- /// Copy the image and related parameters from this layer to an target layer
- /// </summary>
- /// <param name="layer"></param>
- public void CopyImageTo(Layer layer)
+ if (pixels.Count > 0)
{
- if (!HaveImage) return;
- layer.CompressedBytes = _compressedBytes.ToArray();
- layer.BoundingRectangle = _boundingRectangle;
- layer.NonZeroPixelCount = _nonZeroPixelCount;
+ result.Add(new LayerIssue(this, LayerIssue.IssueType.TouchingBound, pixels.ToArray()));
}
- public Layer Clone()
+ pixels.Clear();
+
+ return result;
+ }*/
+
+ /// <summary>
+ /// Copy all parameters from this layer to an target layer
+ /// </summary>
+ /// <param name="layer"></param>
+ public void CopyParametersTo(Layer layer)
+ {
+ CopyWaitTimesTo(layer);
+ CopyExposureTo(layer);
+ CopyLiftTo(layer);
+ }
+
+ /// <summary>
+ /// Copy all exposure parameters from this layer to an target layer
+ /// </summary>
+ /// <param name="layer"></param>
+ public void CopyExposureTo(Layer layer)
+ {
+ layer.ExposureTime = _exposureTime;
+ layer.LightPWM = _lightPWM;
+ }
+
+ /// <summary>
+ /// Copy all wait parameters from this layer to an target layer
+ /// </summary>
+ /// <param name="layer"></param>
+ public void CopyWaitTimesTo(Layer layer)
+ {
+ layer.LightOffDelay = _lightOffDelay;
+ layer.WaitTimeBeforeCure = _waitTimeBeforeCure;
+ layer.WaitTimeAfterCure = _waitTimeAfterCure;
+ layer.WaitTimeAfterLift = _waitTimeAfterLift;
+ }
+
+ /// <summary>
+ /// Copy all lift parameters from this layer to an target layer
+ /// </summary>
+ /// <param name="layer"></param>
+ public void CopyLiftTo(Layer layer)
+ {
+ layer.LiftHeight = _liftHeight;
+ layer.LiftHeight2 = _liftHeight2;
+ layer.LiftSpeed = _liftSpeed;
+ layer.LiftSpeed2 = _liftSpeed2;
+ layer.RetractHeight2 = _retractHeight2;
+ layer.RetractSpeed = _retractSpeed;
+ layer.RetractSpeed2 = _retractSpeed2;
+ }
+
+ /// <summary>
+ /// Copy the image and related parameters from this layer to an target layer
+ /// </summary>
+ /// <param name="layer"></param>
+ public void CopyImageTo(Layer layer)
+ {
+ if (!HaveImage) return;
+ layer.CompressedBytes = _compressedBytes?.ToArray();
+ layer.BoundingRectangle = _boundingRectangle;
+ layer.NonZeroPixelCount = _nonZeroPixelCount;
+ }
+
+ public Layer Clone()
+ {
+ //var layer = (Layer)MemberwiseClone();
+ //layer.CompressedBytes = _compressedBytes.ToArray();
+ //Debug.WriteLine(ReferenceEquals(_compressedBytes, layer.CompressedBytes));
+ //return layer;
+ return new (_index, CompressedBytes?.ToArray()!, SlicerFile)
{
- //var layer = (Layer)MemberwiseClone();
- //layer.CompressedBytes = _compressedBytes.ToArray();
- //Debug.WriteLine(ReferenceEquals(_compressedBytes, layer.CompressedBytes));
- //return layer;
- return new (_index, CompressedBytes?.ToArray(), ParentLayerManager)
- {
- _positionZ = _positionZ,
- _lightOffDelay = _lightOffDelay,
- _waitTimeBeforeCure = _waitTimeBeforeCure,
- _exposureTime = _exposureTime,
- _waitTimeAfterCure = _waitTimeAfterCure,
- _liftHeight = _liftHeight,
- _liftSpeed = _liftSpeed,
- _liftHeight2 = _liftHeight2,
- _liftSpeed2 = _liftSpeed2,
- _waitTimeAfterLift = _waitTimeAfterLift,
- _retractSpeed = _retractSpeed,
- _retractHeight2 = _retractHeight2,
- _retractSpeed2 = _retractSpeed2,
- _lightPWM = _lightPWM,
- _boundingRectangle = _boundingRectangle,
- _nonZeroPixelCount = _nonZeroPixelCount,
- _isModified = _isModified,
- _materialMilliliters = _materialMilliliters
- };
- }
- #endregion
+ _positionZ = _positionZ,
+ _lightOffDelay = _lightOffDelay,
+ _waitTimeBeforeCure = _waitTimeBeforeCure,
+ _exposureTime = _exposureTime,
+ _waitTimeAfterCure = _waitTimeAfterCure,
+ _liftHeight = _liftHeight,
+ _liftSpeed = _liftSpeed,
+ _liftHeight2 = _liftHeight2,
+ _liftSpeed2 = _liftSpeed2,
+ _waitTimeAfterLift = _waitTimeAfterLift,
+ _retractSpeed = _retractSpeed,
+ _retractHeight2 = _retractHeight2,
+ _retractSpeed2 = _retractSpeed2,
+ _lightPWM = _lightPWM,
+ _boundingRectangle = _boundingRectangle,
+ _nonZeroPixelCount = _nonZeroPixelCount,
+ _isModified = _isModified,
+ _materialMilliliters = _materialMilliliters
+ };
+ }
+ #endregion
- #region Static Methods
+ #region Static Methods
- public static float RoundHeight(float height) => (float) Math.Round(height, HeightPrecision);
- public static double RoundHeight(double height) => Math.Round(height, HeightPrecision);
- public static decimal RoundHeight(decimal height) => Math.Round(height, HeightPrecision);
+ public static float RoundHeight(float height) => (float) Math.Round(height, HeightPrecision);
+ public static double RoundHeight(double height) => Math.Round(height, HeightPrecision);
+ public static decimal RoundHeight(decimal height) => Math.Round(height, HeightPrecision);
- public static string ShowHeight(float height) => string.Format($"{{0:F{HeightPrecision}}}", height);
- public static string ShowHeight(double height) => string.Format($"{{0:F{HeightPrecision}}}", height);
- public static string ShowHeight(decimal height) => string.Format($"{{0:F{HeightPrecision}}}", height);
+ public static string ShowHeight(float height) => string.Format($"{{0:F{HeightPrecision}}}", height);
+ public static string ShowHeight(double height) => string.Format($"{{0:F{HeightPrecision}}}", height);
+ public static string ShowHeight(decimal height) => string.Format($"{{0:F{HeightPrecision}}}", height);
- public static Layer[] CloneLayers(Layer[] layers)
+ public static Layer[] CloneLayers(Layer[] layers)
+ {
+ var clonedLayers = new Layer[layers.Length];
+ for (uint layerIndex = 0; layerIndex < layers.Length; layerIndex++)
{
- var clonedLayers = new Layer[layers.Length];
- for (uint layerIndex = 0; layerIndex < layers.Length; layerIndex++)
- {
- clonedLayers[layerIndex] = layers[layerIndex]?.Clone();
- }
- return clonedLayers;
+ clonedLayers[layerIndex] = layers[layerIndex].Clone();
}
-
- #endregion
+ return clonedLayers;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Layers/LayerIssue.cs b/UVtools.Core/Layers/LayerIssue.cs
deleted file mode 100644
index 28fdd3c..0000000
--- a/UVtools.Core/Layers/LayerIssue.cs
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * GNU AFFERO GENERAL PUBLIC LICENSE
- * Version 3, 19 November 2007
- * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- * Everyone is permitted to copy and distribute verbatim copies
- * of this license document, but changing it is not allowed.
- */
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using UVtools.Core.Extensions;
-using UVtools.Core.Layers;
-using UVtools.Core.Objects;
-
-namespace UVtools.Core
-{
- #region LayerIssue Class
-
- public class LayerIssueOLD : IEquatable<LayerIssueOLD>, IEnumerable<Point>
- {
- public enum IssueType : byte
- {
- Island,
- Overhang,
- ResinTrap,
- SuctionCup,
- TouchingBound,
- PrintHeight,
- EmptyLayer,
- Debug
- //HoleSandwich,
- }
-
- /// <summary>
- /// Gets the parent layer
- /// </summary>
- public Layer Layer { get; init; }
-
- /// <summary>
- /// Gets the layer index
- /// </summary>
- public uint LayerIndex => Layer.Index;
-
- /// <summary>
- /// Gets the issue type associated
- /// </summary>
- public IssueType Type { get; init; }
-
- /// <summary>
- /// Gets the pixels points containing the issue
- /// </summary>
- public Point[] Pixels { get; init; }
-
- /// <summary>
- /// Gets the contours containing the issue
- /// </summary>
- public Point[][] Contours { get; init; }
-
- public int PixelsCount
- {
- get
- {
- if (Pixels is not null) return Pixels.Length;
- if (Contours is not null) return (int)Area;
- return 0;
- }
- }
-
- /// <summary>
- /// Gets the bounding rectangle of the pixel area
- /// </summary>
- public Rectangle BoundingRectangle { get; init; }
-
- /// <summary>
- /// Gets the area of the issue
- /// </summary>
- public double Area { get; init; }
-
- /// <summary>
- /// Gets the X coordinate for the first point, -1 if doesn't exists
- /// </summary>
- public int X => FirstPoint.X;
-
- /// <summary>
- /// Gets the Y coordinate for the first point, -1 if doesn't exists
- /// </summary>
- public int Y => FirstPoint.Y;
-
- /// <summary>
- /// Gets the XY point for first point
- /// </summary>
- public Point FirstPoint
- {
- get
- {
- if (Pixels?.Length > 0) return Pixels[0];
- if (Contours?.Length > 0) return Contours[0][0];
- return new Point(-1, -1);
- }
- }
-
- public string FirstPointStr => $"{FirstPoint.X}, {FirstPoint.Y}";
-
- /// <summary>
- /// Check if this issue have a valid start point to show
- /// </summary>
- public bool HaveValidPoint => Pixels?.Length > 0 || Contours?.Length > 0;
-
- public LayerIssueOLD(Layer layer, IssueType type, Point[] pixels = null, Point[][] contours = null, Rectangle boundingRectangle = default)
- {
- Layer = layer;
- Type = type;
- Contours = contours;
- Pixels = pixels;
- BoundingRectangle = boundingRectangle;
- Area = (uint)boundingRectangle.Area();
- }
-
- public LayerIssueOLD(Layer layer, IssueType type, Point[] pixels = null, Rectangle boundingRectangle = default) :
- this(layer, type, pixels, null, boundingRectangle) { }
-
-
- public LayerIssueOLD(Layer layer, IssueType type, Point[][] contours, Rectangle boundingRectangle = default) :
- this(layer, type, null, contours, boundingRectangle) { }
-
- public LayerIssueOLD(Layer layer, IssueType type) :
- this(layer, type, null, null, default)
- { }
-
- public Point this[uint index] => Pixels[index];
-
- public Point this[int index] => Pixels[index];
-
- public IEnumerator<Point> GetEnumerator()
- {
- return ((IEnumerable<Point>)Pixels).GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- public override string ToString()
- {
- return $"{nameof(Type)}: {Type}, Layer: {Layer.Index}, {nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(PixelsCount)}: {PixelsCount}";
- }
-
-
- public bool Equals(LayerIssueOLD other)
- {
- if (other is null) return false;
- if (ReferenceEquals(this, other)) return true;
- return Layer.Index == other.Layer.Index
- && Type == other.Type
- && PixelsCount == other.PixelsCount
- && Pixels is not null && other.Pixels is not null && Pixels.SequenceEqual(other.Pixels)
- && Contours is not null && other.Contours is not null && Contours.SequenceEqual(other.Contours)
- //&& BoundingRectangle.Equals(other.BoundingRectangle)
- ;
- }
-
- public override bool Equals(object obj)
- {
- if (obj is null) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((LayerIssueOLD) obj);
- }
-
- public override int GetHashCode()
- {
- unchecked
- {
- var hashCode = (Layer != null ? Layer.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (int) Type;
- hashCode = (hashCode * 397) ^ (Pixels != null ? Pixels.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (Contours != null ? Contours.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ BoundingRectangle.GetHashCode();
- return hashCode;
- }
- }
- }
- #endregion
-
- #region LayerHollowArea
-
- public class LayerHollowArea : IEnumerable<Point[]>
- {
- public enum AreaType : byte
- {
- Unknown = 0,
- Trap,
- Drain
- }
- /// <summary>
- /// Gets contours
- /// </summary>
- public Point[][] Contours { get; }
-
- public Rectangle BoundingRectangle { get; }
-
- public double Area { get; set; }
-
- public AreaType Type { get; set; } = AreaType.Unknown;
-
- public bool Processed { get; set; }
-
- #region Indexers
- public Point[] this[uint index]
- {
- get => index < Contours.Length ? Contours[index] : null;
- set => Contours[index] = value;
- }
-
- public Point[] this[int index]
- {
- get => index < Contours.Length ? Contours[index] : null;
- set => Contours[index] = value;
- }
-
- #endregion
-
- public IEnumerator<Point[]> GetEnumerator()
- {
- return ((IEnumerable<Point[]>)Contours).GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- public LayerHollowArea() { }
-
- public LayerHollowArea(Point[][] contours, Rectangle boundingRectangle, double area, AreaType type = AreaType.Unknown)
- {
- Contours = contours;
- BoundingRectangle = boundingRectangle;
- Area = area;
- Type = type;
- }
- }
- #endregion
-
- #region ResinTrapGround
-
- public sealed class ResinTrapGroup : BindableBase, IList<LayerHollowArea>
- {
- #region List Implementation
- private readonly List<LayerHollowArea> hollowAreaList = new();
- private LayerHollowArea.AreaType _currentAreaType = LayerHollowArea.AreaType.Trap;
-
- public IEnumerator<LayerHollowArea> GetEnumerator()
- {
- return hollowAreaList.GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return ((IEnumerable) hollowAreaList).GetEnumerator();
- }
-
- public void Add(LayerHollowArea item)
- {
- if (item.Type == LayerHollowArea.AreaType.Drain)
- {
- CurrentAreaType = LayerHollowArea.AreaType.Drain;
- }
- else if (_currentAreaType == LayerHollowArea.AreaType.Drain)
- {
- item.Type = LayerHollowArea.AreaType.Drain;
- }
- hollowAreaList.Add(item);
- }
-
- public void Add(ResinTrapGroup group)
- {
- foreach (var area in group)
- {
- Add(area);
- }
- }
-
- public void Clear()
- {
- CurrentAreaType = LayerHollowArea.AreaType.Trap;
- hollowAreaList.Clear();
- }
-
- public bool Contains(LayerHollowArea item)
- {
- return hollowAreaList.Contains(item);
- }
-
- public void CopyTo(LayerHollowArea[] array, int arrayIndex)
- {
- hollowAreaList.CopyTo(array, arrayIndex);
- }
-
- public bool Remove(LayerHollowArea item)
- {
- var result = hollowAreaList.Remove(item);
- if (Count == 0) CurrentAreaType = LayerHollowArea.AreaType.Trap;
- return result;
- }
-
- public int Count => hollowAreaList.Count;
-
- public bool IsReadOnly => false;
-
- public int IndexOf(LayerHollowArea item)
- {
- return hollowAreaList.IndexOf(item);
- }
-
- public void Insert(int index, LayerHollowArea item)
- {
- if (item.Type == LayerHollowArea.AreaType.Drain)
- {
- CurrentAreaType = LayerHollowArea.AreaType.Drain;
- }
- else if (_currentAreaType == LayerHollowArea.AreaType.Drain)
- {
- item.Type = LayerHollowArea.AreaType.Drain;
- }
- hollowAreaList.Insert(index, item);
- }
-
- public void RemoveAt(int index)
- {
- hollowAreaList.RemoveAt(index);
- if (Count == 0) CurrentAreaType = LayerHollowArea.AreaType.Trap;
- }
-
- public LayerHollowArea this[int index]
- {
- get => hollowAreaList[index];
- set => hollowAreaList[index] = value;
- }
- #endregion
-
- #region Properties
- public LayerHollowArea.AreaType CurrentAreaType
- {
- get => _currentAreaType;
- set
- {
- if(!RaiseAndSetIfChanged(ref _currentAreaType, value)) return;
- foreach (var area in this) // Update previous items
- {
- area.Type = _currentAreaType;
- }
- }
- }
- #endregion
- }
-
- public sealed class ResinTrapTree
- {
- public List<ResinTrapGroup> Groups { get; } = new();
-
- public ResinTrapGroup FindHollowGroup(LayerHollowArea hollowArea)
- {
- var i = FindHollowGroupIndex(hollowArea);
- return i >= 0 ? Groups[i] : null;
- }
-
- public int FindHollowGroupIndex(LayerHollowArea hollowArea)
- {
- for (var i = 0; i < Groups.Count; i++)
- {
- if (Groups[i].Any(area => ReferenceEquals(area, hollowArea)))
- {
- return i;
- }
- }
-
- return -1;
- }
-
- public ResinTrapGroup AddRoot(LayerHollowArea hollowArea) => AddRoot(hollowArea, out _);
- public ResinTrapGroup AddRoot(LayerHollowArea hollowArea, out int index)
- {
- index = FindHollowGroupIndex(hollowArea);
- if (index < 0) // Not found
- {
- index = Groups.Count;
- Groups.Add(new(){hollowArea});
- }
- else // Exists
- {
- Groups[index].Add(hollowArea);
- }
-
- return Groups[index];
- }
-
- public ResinTrapGroup AddChild(ResinTrapGroup group, LayerHollowArea hollowArea)
- {
- // This will find if the area exists in any other group,
- // If yes then the groups are merged, otherwise it will be added to the parent group
-
- var findGroup = FindHollowGroup(hollowArea);
- if (findGroup is not null && !ReferenceEquals(group, findGroup))
- {
- return MergeGroups(group, findGroup, true);
- }
- if(group.IndexOf(hollowArea) == -1) group.Add(hollowArea);
- return group;
- }
-
- /// <summary>
- /// Merges two groups
- /// </summary>
- /// <param name="group1"></param>
- /// <param name="group2"></param>
- /// <param name="manageGroups">True to remove old groups and add the new to group list</param>
- /// <returns>A new group instance holding group1 and group2 items</returns>
- public ResinTrapGroup MergeGroups(ResinTrapGroup group1, ResinTrapGroup group2, bool manageGroups = false)
- {
- ResinTrapGroup newGroup = new (){group1, group2};
- if (manageGroups)
- {
- Groups.Remove(group1);
- Groups.Remove(group2);
- Groups.Add(newGroup);
- }
- return newGroup;
- }
- }
- #endregion
-
-}
diff --git a/UVtools.Core/Layers/LayerIssueConfiguration.cs b/UVtools.Core/Layers/LayerIssueConfiguration.cs
index 947b10c..630b533 100644
--- a/UVtools.Core/Layers/LayerIssueConfiguration.cs
+++ b/UVtools.Core/Layers/LayerIssueConfiguration.cs
@@ -8,257 +8,255 @@
using System.Collections.Generic;
-namespace UVtools.Core.Layers
+namespace UVtools.Core.Layers;
+
+#region LayerIssue Class
+
+public sealed class IssuesDetectionConfiguration
+{
+ public IslandDetectionConfiguration IslandConfig { get; }
+ public OverhangDetectionConfiguration OverhangConfig { get; }
+ public ResinTrapDetectionConfiguration ResinTrapConfig { get; }
+ public TouchingBoundDetectionConfiguration TouchingBoundConfig { get; }
+ public PrintHeightDetectionConfiguration PrintHeightConfig { get; }
+ public bool EmptyLayerConfig { get; }
+
+ public IssuesDetectionConfiguration(IslandDetectionConfiguration islandConfig,
+ OverhangDetectionConfiguration overhangConfig,
+ ResinTrapDetectionConfiguration resinTrapConfig,
+ TouchingBoundDetectionConfiguration touchingBoundConfig,
+ PrintHeightDetectionConfiguration printHeightConfig,
+ bool emptyLayerConfig)
+ {
+ IslandConfig = islandConfig;
+ OverhangConfig = overhangConfig;
+ ResinTrapConfig = resinTrapConfig;
+ TouchingBoundConfig = touchingBoundConfig;
+ PrintHeightConfig = printHeightConfig;
+ EmptyLayerConfig = emptyLayerConfig;
+ }
+}
+
+public sealed class IslandDetectionConfiguration
{
- #region LayerIssue Class
+ /// <summary>
+ /// Gets or sets if the detection is enabled
+ /// </summary>
+ public bool Enabled { get; set; } = true;
+
+ /// <summary>
+ /// Gets or sets a list of layers to check for islands, absent layers will not be checked.
+ /// Set to null to check every layer
+ /// </summary>
+ public List<uint>? WhiteListLayers { get; set; } = null;
+
+ /// <summary>
+ /// Combines the island and overhang detections for a better more realistic detection and to discard false-positives. (Slower)
+ /// If enabled, and when a island is found, it will check for overhangs on that same island, if no overhang found then the island will be discarded and considered safe, otherwise it will flag as an island issue.
+ /// Note: Overhangs settings will be used to configure the detection.Enabling Overhangs is not required for this procedure to work.
+ /// </summary>
+ public bool EnhancedDetection { get; set; } = true;
- public sealed class IssuesDetectionConfiguration
+ /// <summary>
+ /// Gets the setting for whether or not diagonal bonds are considered when evaluation islands.
+ /// If true, all 8 neighbors of a pixel (including diagonals) will be considered when finding
+ /// individual components on the layer, if false only 4 neighbors (right, left, above, below)
+ /// will be considered..
+ /// </summary>
+ public bool AllowDiagonalBonds { get; set; } = false;
+
+ /// <summary>
+ /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white
+ /// Set to 0 to disable this operation
+ /// </summary>
+ public byte BinaryThreshold { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the required area size (x*y) to consider process a island (0-65535)
+ /// </summary>
+ public ushort RequiredAreaToProcessCheck { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the required brightness for check a pixel under a island (0-255)
+ /// </summary>
+ public byte RequiredPixelBrightnessToProcessCheck { get; set; } = 10;
+
+ /// <summary>
+ /// Gets the required number of pixels to support a island and discard it as a issue (0-255)
+ /// </summary>
+ public byte RequiredPixelsToSupport { get; set; } = 10;
+
+ /// <summary>
+ /// Gets the required multiplier from the island pixels to support same island and discard it as a issue
+ /// </summary>
+ public decimal RequiredPixelsToSupportMultiplier { get; set; } = 0.25m;
+
+ /// <summary>
+ /// Gets the required brightness of supporting pixels to count as a valid support (0-255)
+ /// </summary>
+ public byte RequiredPixelBrightnessToSupport { get; set; } = 150;
+
+ public IslandDetectionConfiguration(bool enabled = true)
{
- public IslandDetectionConfiguration IslandConfig { get; }
- public OverhangDetectionConfiguration OverhangConfig { get; }
- public ResinTrapDetectionConfiguration ResinTrapConfig { get; }
- public TouchingBoundDetectionConfiguration TouchingBoundConfig { get; }
- public PrintHeightDetectionConfiguration PrintHeightConfig { get; }
- public bool EmptyLayerConfig { get; }
-
- public IssuesDetectionConfiguration(IslandDetectionConfiguration islandConfig,
- OverhangDetectionConfiguration overhangConfig,
- ResinTrapDetectionConfiguration resinTrapConfig,
- TouchingBoundDetectionConfiguration touchingBoundConfig,
- PrintHeightDetectionConfiguration printHeightConfig,
- bool emptyLayerConfig)
- {
- IslandConfig = islandConfig;
- OverhangConfig = overhangConfig;
- ResinTrapConfig = resinTrapConfig;
- TouchingBoundConfig = touchingBoundConfig;
- PrintHeightConfig = printHeightConfig;
- EmptyLayerConfig = emptyLayerConfig;
- }
+ Enabled = enabled;
}
- public sealed class IslandDetectionConfiguration
+ public IslandDetectionConfiguration Clone()
{
- /// <summary>
- /// Gets or sets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
- /// Gets or sets a list of layers to check for islands, absent layers will not be checked.
- /// Set to null to check every layer
- /// </summary>
- public List<uint> WhiteListLayers { get; set; } = null;
-
- /// <summary>
- /// Combines the island and overhang detections for a better more realistic detection and to discard false-positives. (Slower)
- /// If enabled, and when a island is found, it will check for overhangs on that same island, if no overhang found then the island will be discarded and considered safe, otherwise it will flag as an island issue.
- /// Note: Overhangs settings will be used to configure the detection.Enabling Overhangs is not required for this procedure to work.
- /// </summary>
- public bool EnhancedDetection { get; set; } = true;
-
- /// <summary>
- /// Gets the setting for whether or not diagonal bonds are considered when evaluation islands.
- /// If true, all 8 neighbors of a pixel (including diagonals) will be considered when finding
- /// individual components on the layer, if false only 4 neighbors (right, left, above, below)
- /// will be considered..
- /// </summary>
- public bool AllowDiagonalBonds { get; set; } = false;
-
- /// <summary>
- /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white
- /// Set to 0 to disable this operation
- /// </summary>
- public byte BinaryThreshold { get; set; } = 1;
-
- /// <summary>
- /// Gets the required area size (x*y) to consider process a island (0-65535)
- /// </summary>
- public ushort RequiredAreaToProcessCheck { get; set; } = 1;
-
- /// <summary>
- /// Gets the required brightness for check a pixel under a island (0-255)
- /// </summary>
- public byte RequiredPixelBrightnessToProcessCheck { get; set; } = 10;
-
- /// <summary>
- /// Gets the required number of pixels to support a island and discard it as a issue (0-255)
- /// </summary>
- public byte RequiredPixelsToSupport { get; set; } = 10;
-
- /// <summary>
- /// Gets the required multiplier from the island pixels to support same island and discard it as a issue
- /// </summary>
- public decimal RequiredPixelsToSupportMultiplier { get; set; } = 0.25m;
-
- /// <summary>
- /// Gets the required brightness of supporting pixels to count as a valid support (0-255)
- /// </summary>
- public byte RequiredPixelBrightnessToSupport { get; set; } = 150;
-
- public IslandDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
-
- public IslandDetectionConfiguration Clone()
- {
- var clone = MemberwiseClone() as IslandDetectionConfiguration;
- return clone;
- }
+ return (MemberwiseClone() as IslandDetectionConfiguration)!;
}
+}
+/// <summary>
+/// Overhang configuration
+/// </summary>
+public sealed class OverhangDetectionConfiguration
+{
/// <summary>
- /// Overhang configuration
+ /// Gets or sets if the detection is enabled
/// </summary>
- public sealed class OverhangDetectionConfiguration
- {
- /// <summary>
- /// Gets or sets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
- /// Gets or sets a list of layers to check for overhangs, absent layers will not be checked.
- /// Set to null to check every layer
- /// </summary>
- public List<uint> WhiteListLayers { get; set; } = null;
-
- /// <summary>
- /// Gets or sets if should take in consideration the islands, if yes a island can't be a overhang at same time, otherwise islands and overhangs can be shared
- /// </summary>
- public bool IndependentFromIslands { get; set; } = true;
-
- /// <summary>
- /// After compute overhangs, masses with a number of pixels bellow this number will be discarded (Not a overhang)
- /// </summary>
- public byte RequiredPixelsToConsider { get; set; } = 1;
+ public bool Enabled { get; set; } = true;
+
+ /// <summary>
+ /// Gets or sets a list of layers to check for overhangs, absent layers will not be checked.
+ /// Set to null to check every layer
+ /// </summary>
+ public List<uint>? WhiteListLayers { get; set; } = null;
+
+ /// <summary>
+ /// Gets or sets if should take in consideration the islands, if yes a island can't be a overhang at same time, otherwise islands and overhangs can be shared
+ /// </summary>
+ public bool IndependentFromIslands { get; set; } = true;
+
+ /// <summary>
+ /// After compute overhangs, masses with a number of pixels bellow this number will be discarded (Not a overhang)
+ /// </summary>
+ public byte RequiredPixelsToConsider { get; set; } = 1;
- /// <summary>
- /// Previous layer will be subtracted from current layer, after will erode by this value.
- /// The survived pixels are potential overhangs.
- /// </summary>
- public byte ErodeIterations { get; set; } = 40;
-
- public OverhangDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
- }
+ /// <summary>
+ /// Previous layer will be subtracted from current layer, after will erode by this value.
+ /// The survived pixels are potential overhangs.
+ /// </summary>
+ public byte ErodeIterations { get; set; } = 40;
- public sealed class ResinTrapDetectionConfiguration
+ public OverhangDetectionConfiguration(bool enabled = true)
{
- /// <summary>
- /// Gets or sets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
- /// Gets or sets the starting layer index for the detection which will also be considered a drain layer.
- /// Use this setting to bypass complicated rafts by selected the model first real layer.
- /// </summary>
- public uint StartLayerIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white
- /// Set to 0 to disable this operation
- /// </summary>
- public byte BinaryThreshold { get; set; } = 127;
-
- /// <summary>
- /// Gets the required area size (x*y) to consider process a hollow area (0-255)
- /// </summary>
- public byte RequiredAreaToProcessCheck { get; set; } = 1;
-
- /// <summary>
- /// Gets the number of black pixels required to consider a drain
- /// </summary>
- public byte RequiredBlackPixelsToDrain { get; set; } = 10;
-
- /// <summary>
- /// Gets the maximum pixel brightness to be a drain pixel (0-150)
- /// </summary>
- public byte MaximumPixelBrightnessToDrain { get; set; } = 30;
-
- /// <summary>
- /// Gets if suction cups can also be detected together with resin traps
- /// </summary>
- public bool DetectSuctionCups { get; set; } = true;
-
- /// <summary>
- /// Required minimum area to be considered a suction cup
- /// </summary>
- public uint RequiredAreaToConsiderSuctionCup { get; set; } = 100;
-
- /// <summary>
- /// Required minimum height (in mm) to be considered a suction cup
- /// </summary>
- public decimal RequiredHeightToConsiderSuctionCup { get; set; } = 0.5m;
-
-
- public ResinTrapDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ Enabled = enabled;
}
+}
+public sealed class ResinTrapDetectionConfiguration
+{
+ /// <summary>
+ /// Gets or sets if the detection is enabled
+ /// </summary>
+ public bool Enabled { get; set; } = true;
- public sealed class TouchingBoundDetectionConfiguration
+ /// <summary>
+ /// Gets or sets the starting layer index for the detection which will also be considered a drain layer.
+ /// Use this setting to bypass complicated rafts by selected the model first real layer.
+ /// </summary>
+ public uint StartLayerIndex { get; set; }
+
+ /// <summary>
+ /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white
+ /// Set to 0 to disable this operation
+ /// </summary>
+ public byte BinaryThreshold { get; set; } = 127;
+
+ /// <summary>
+ /// Gets the required area size (x*y) to consider process a hollow area (0-255)
+ /// </summary>
+ public byte RequiredAreaToProcessCheck { get; set; } = 1;
+
+ /// <summary>
+ /// Gets the number of black pixels required to consider a drain
+ /// </summary>
+ public byte RequiredBlackPixelsToDrain { get; set; } = 10;
+
+ /// <summary>
+ /// Gets the maximum pixel brightness to be a drain pixel (0-150)
+ /// </summary>
+ public byte MaximumPixelBrightnessToDrain { get; set; } = 30;
+
+ /// <summary>
+ /// Gets if suction cups can also be detected together with resin traps
+ /// </summary>
+ public bool DetectSuctionCups { get; set; } = true;
+
+ /// <summary>
+ /// Required minimum area to be considered a suction cup
+ /// </summary>
+ public uint RequiredAreaToConsiderSuctionCup { get; set; } = 100;
+
+ /// <summary>
+ /// Required minimum height (in mm) to be considered a suction cup
+ /// </summary>
+ public decimal RequiredHeightToConsiderSuctionCup { get; set; } = 0.5m;
+
+
+ public ResinTrapDetectionConfiguration(bool enabled = true)
{
- /// <summary>
- /// Gets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
- /// Gets the minimum pixel brightness to be a touching bound
- /// </summary>
- public byte MinimumPixelBrightness { get; set; } = 127;
-
- /// <summary>
- /// Gets or sets the margin in pixels from left edge to check for touching white pixels
- /// </summary>
- public byte MarginLeft { get; set; } = 5;
-
- /// <summary>
- /// Gets or sets the margin in pixels from top to check for touching white pixels
- /// </summary>
- public byte MarginTop { get; set; } = 5;
-
- /// <summary>
- /// Gets or sets the margin in pixels from right edge to check for touching white pixels
- /// </summary>
- public byte MarginRight { get; set; } = 5;
-
- /// <summary>
- /// Gets or sets the margin in pixels from bottom edge to check for touching white pixels
- /// </summary>
- public byte MarginBottom { get; set; } = 5;
-
-
- public TouchingBoundDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ Enabled = enabled;
}
+}
+
+
+public sealed class TouchingBoundDetectionConfiguration
+{
+ /// <summary>
+ /// Gets if the detection is enabled
+ /// </summary>
+ public bool Enabled { get; set; } = true;
+
+ /// <summary>
+ /// Gets the minimum pixel brightness to be a touching bound
+ /// </summary>
+ public byte MinimumPixelBrightness { get; set; } = 127;
+
+ /// <summary>
+ /// Gets or sets the margin in pixels from left edge to check for touching white pixels
+ /// </summary>
+ public byte MarginLeft { get; set; } = 5;
+
+ /// <summary>
+ /// Gets or sets the margin in pixels from top to check for touching white pixels
+ /// </summary>
+ public byte MarginTop { get; set; } = 5;
+
+ /// <summary>
+ /// Gets or sets the margin in pixels from right edge to check for touching white pixels
+ /// </summary>
+ public byte MarginRight { get; set; } = 5;
+
+ /// <summary>
+ /// Gets or sets the margin in pixels from bottom edge to check for touching white pixels
+ /// </summary>
+ public byte MarginBottom { get; set; } = 5;
- public sealed class PrintHeightDetectionConfiguration
+
+ public TouchingBoundDetectionConfiguration(bool enabled = true)
{
- /// <summary>
- /// Gets if the detection is enabled
- /// </summary>
- public bool Enabled { get; set; } = true;
-
- /// <summary>
- /// Get the offset from top to sum to printer max Z height
- /// </summary>
- public float Offset { get; set; }
-
- public PrintHeightDetectionConfiguration(bool enabled = true)
- {
- Enabled = enabled;
- }
+ Enabled = enabled;
}
+}
+
+public sealed class PrintHeightDetectionConfiguration
+{
+ /// <summary>
+ /// Gets if the detection is enabled
+ /// </summary>
+ public bool Enabled { get; set; } = true;
+
+ /// <summary>
+ /// Get the offset from top to sum to printer max Z height
+ /// </summary>
+ public float Offset { get; set; }
- #endregion
+ public PrintHeightDetectionConfiguration(bool enabled = true)
+ {
+ Enabled = enabled;
+ }
}
+
+#endregion \ No newline at end of file
diff --git a/UVtools.Core/Layers/LayerManager.cs b/UVtools.Core/Layers/LayerManager.cs
deleted file mode 100644
index 1863120..0000000
--- a/UVtools.Core/Layers/LayerManager.cs
+++ /dev/null
@@ -1,1202 +0,0 @@
-/*
- * GNU AFFERO GENERAL PUBLIC LICENSE
- * Version 3, 19 November 2007
- * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- * Everyone is permitted to copy and distribute verbatim copies
- * of this license document, but changing it is not allowed.
- */
-
-using System;
-using System.Collections;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Drawing;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
-using MoreLinq.Extensions;
-using UVtools.Core.EmguCV;
-using UVtools.Core.Extensions;
-using UVtools.Core.FileFormats;
-using UVtools.Core.Layers;
-using UVtools.Core.Managers;
-using UVtools.Core.Objects;
-using UVtools.Core.Operations;
-using UVtools.Core.PixelEditor;
-
-namespace UVtools.Core
-{
- public class LayerManager : BindableBase, IList<Layer>, IDisposable
- {
- #region Properties
- public FileFormat SlicerFile { get; set; }
-
- private Layer[] _layers;
-
- /// <summary>
- /// Layers List
- /// </summary>
- public Layer[] Layers
- {
- get => _layers;
- set
- {
- //if (ReferenceEquals(_layers, value)) return;
-
- var rebuildProperties = false;
- var oldLayerCount = LayerCount;
- var oldLayers = _layers;
- _layers = value;
- BoundingRectangle = Rectangle.Empty;
-
- if (LayerCount != oldLayerCount)
- {
- SlicerFile.LayerCount = LayerCount;
- }
-
- SlicerFile.RequireFullEncode = true;
- SlicerFile.PrintHeight = SlicerFile.PrintHeight;
- SlicerFile.UpdatePrintTime();
-
- if (value is not null && LayerCount > 0)
- {
- //SetAllIsModified(true);
-
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) // Forced sanitize
- {
- if (_layers[layerIndex] is null) continue;
- _layers[layerIndex].Index = layerIndex;
- _layers[layerIndex].ParentLayerManager = this;
-
- if (layerIndex >= oldLayerCount || layerIndex < oldLayerCount && !_layers[layerIndex].Equals(oldLayers[layerIndex]))
- {
- // Marks as modified only if layer image changed on this index
- _layers[layerIndex].IsModified = true;
- }
- }
-
- if (LayerCount != oldLayerCount && !SlicerFile.SuppressRebuildProperties && LastLayer is not null)
- {
- RebuildLayersProperties();
- rebuildProperties = true;
- }
- }
-
- if (!rebuildProperties)
- {
- SlicerFile.MaterialMilliliters = -1;
- SlicerFile.RebuildGCode();
- }
-
- RaisePropertyChanged();
- }
- }
-
- /// <summary>
- /// Gets the last layer index
- /// </summary>
- public uint LastLayerIndex => LayerCount - 1;
-
- /// <summary>
- /// Gets the first layer
- /// </summary>
- public Layer FirstLayer => _layers?[0];
-
- /// <summary>
- /// Gets the last layer
- /// </summary>
- public Layer LastLayer => _layers?[^1];
-
- /// <summary>
- /// Gets the smallest bottom layer using the pixel count
- /// </summary>
- public Layer SmallestBottomLayer => _layers?.Where(layer => layer.IsBottomLayer && !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
-
- /// <summary>
- /// Gets the largest bottom layer using the pixel count
- /// </summary>
- public Layer LargestBottomLayer => _layers?.Where(layer => layer.IsBottomLayer && !layer.IsEmpty).MaxBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
-
- /// <summary>
- /// Gets the smallest normal layer using the pixel count
- /// </summary>
- public Layer SmallestNormalLayer => _layers?.Where(layer => layer.IsNormalLayer && !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
-
- /// <summary>
- /// Gets the largest layer using the pixel count
- /// </summary>
- public Layer LargestNormalLayer => _layers?.Where(layer => layer.IsNormalLayer && !layer.IsEmpty).MaxBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
-
- /// <summary>
- /// Gets the smallest normal layer using the pixel count
- /// </summary>
- public Layer SmallestLayer => _layers?.Where(layer => !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
-
- /// <summary>
- /// Gets the largest layer using the pixel count
- /// </summary>
- public Layer LargestLayer => _layers?.MaxBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
-
- /// <summary>
- /// Gets all bottom layers
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> BottomLayers => this.Where(layer => layer.IsBottomLayer);
-
- /// <summary>
- /// Gets all normal layers
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> NormalLayers => this.Where(layer => layer.IsNormalLayer);
-
- /// <summary>
- /// Gets all transition layers
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> TransitionLayers => this.Where(layer => layer.IsTransitionLayer);
-
- /// <summary>
- /// Gets all layers that use TSMC values
- /// </summary>
- /// <returns></returns>
- public IEnumerable<Layer> TsmcLayers => this.Where(layer => layer.IsUsingTSMC);
-
-
- /// <summary>
- /// Gets the bounding rectangle of the object
- /// </summary>
- private Rectangle _boundingRectangle = Rectangle.Empty;
-
- /// <summary>
- /// Gets the bounding rectangle of the object
- /// </summary>
- public Rectangle BoundingRectangle
- {
- get => GetBoundingRectangle();
- set
- {
- RaiseAndSetIfChanged(ref _boundingRectangle, value);
- RaisePropertyChanged(nameof(BoundingRectangleMillimeters));
- }
- }
-
- /// <summary>
- /// Gets the bounding rectangle of the object in millimeters
- /// </summary>
- public RectangleF BoundingRectangleMillimeters
- {
- get
- {
- if (SlicerFile is null) return RectangleF.Empty;
- var pixelSize = SlicerFile.PixelSize;
- return new RectangleF(
- (float)Math.Round(_boundingRectangle.X * pixelSize.Width, 2),
- (float)Math.Round(_boundingRectangle.Y * pixelSize.Height, 2),
- (float)Math.Round(_boundingRectangle.Width * pixelSize.Width, 2),
- (float)Math.Round(_boundingRectangle.Height * pixelSize.Height, 2));
- }
- }
-
- public void Init(Layer[] layers)
- {
- var oldLayerCount = LayerCount;
- _layers = layers;
- if (LayerCount != oldLayerCount)
- {
- SlicerFile.LayerCount = LayerCount;
- }
- }
-
- public void Init(uint layerCount, bool initializeLayers = false)
- {
- var layers = new Layer[layerCount];
- if (initializeLayers)
- {
- for (uint layerIndex = 0; layerIndex < layerCount; layerIndex++)
- {
- layers[layerIndex] = new Layer(layerIndex, this);
- }
- }
-
- Init(layers);
- }
-
- public void Add(Layer layer)
- {
- Layers = Enumerable.Append(_layers, layer).ToArray();
- }
-
- public void Add(IEnumerable<Layer> layers)
- {
- var list = _layers.ToList();
- list.AddRange(layers);
- Layers = list.ToArray();
- }
-
- public void Clear()
- {
- //Layers = Array.Empty<Layer>();
- _layers = null;
- }
-
- public bool Contains(Layer layer)
- {
- return _layers.Contains(layer);
- }
-
- public void CopyTo(Layer[] array, int arrayIndex)
- {
- _layers.CopyTo(array, arrayIndex);
- }
-
- public bool Remove(Layer layer)
- {
- var list = _layers.ToList();
- var result = list.Remove(layer);
- if (result)
- {
- Layers = list.ToArray();
- }
-
- return result;
- }
-
- public int IndexOf(Layer layer)
- {
- for (int layerIndex = 0; layerIndex < Count; layerIndex++)
- {
- if (_layers[layerIndex].Equals(layer)) return layerIndex;
- }
-
- return -1;
- }
-
- public void Prepend(Layer layer) => Insert(0, layer);
- public void Prepend(IEnumerable<Layer> layers) => InsertRange(0, layers);
- public void Append(Layer layer) => Add(layer);
- public void AppendRange(IEnumerable<Layer> layers) => Add(layers);
-
- public void Insert(int index, Layer layer)
- {
- if (index < 0) return;
- if (index > Count)
- {
- Add(layer); // Append
- return;
- }
-
- var list = _layers.ToList();
- list.Insert(index, layer);
- Layers = list.ToArray();
- }
-
- public void InsertRange(int index, IEnumerable<Layer> layers)
- {
- if (index < 0) return;
-
- if (index > Count)
- {
- Add(layers);
- return;
- }
-
- var list = _layers.ToList();
- list.InsertRange(index, layers);
- Layers = list.ToArray();
- }
-
- public void RemoveAt(int index)
- {
- if (index >= LastLayerIndex) return;
- var list = _layers.ToList();
- list.RemoveAt(index);
- Layers = list.ToArray();
- }
-
- public void RemoveRange(int index, int count)
- {
- if (count <= 0 || index >= LastLayerIndex) return;
- var list = _layers.ToList();
- list.RemoveRange(index, count);
- Layers = list.ToArray();
- }
-
- /// <summary>
- /// Removes all null layers in the collection
- /// </summary>
- public void RemoveNulls()
- {
- Layers = _layers.Where(layer => layer is not null).ToArray();
- }
-
- public int Count => _layers?.Length ?? 0;
-
- public bool IsReadOnly => false;
-
- /// <summary>
- /// Gets the layers count
- /// </summary>
- public uint LayerCount => (uint)(_layers?.Length ?? 0);
-
- public byte LayerDigits => (byte)LayerCount.ToString().Length;
-
- /// <summary>
- /// Gets if any layer got modified, otherwise false
- /// </summary>
- public bool IsModified
- {
- get
- {
- for (uint i = 0; i < LayerCount; i++)
- {
- if (_layers[i].IsModified) return true;
- }
- return false;
- }
- }
-
- /// <summary>
- /// True if all layers are using same value parameters as global settings, otherwise false
- /// </summary>
- public bool AllLayersAreUsingGlobalParameters => _layers.Where(layer => layer is not null).All(layer => layer.IsUsingGlobalParameters);
-
- /// <summary>
- /// True if any layer is using TSMC, otherwise false when none of layers is using TSMC
- /// </summary>
- public bool AnyLayerIsUsingTSMC => _layers.Where(layer => layer is not null).Any(layer => layer.IsUsingTSMC);
-
- //public float LayerHeight => Layers[0].PositionZ;
-
- #endregion
-
- #region Constructors
- public LayerManager(FileFormat slicerFile)
- {
- SlicerFile = slicerFile;
- }
-
- public LayerManager(uint layerCount, FileFormat slicerFile) : this(slicerFile)
- {
- SlicerFile = slicerFile;
- Init(layerCount);
- }
- #endregion
-
- #region Indexers
- public Layer this[uint index]
- {
- get => _layers[index];
- set => SetLayer(index, value);
- }
-
- public Layer this[int index]
- {
- get => _layers[index];
- set => SetLayer((uint) index, value);
- }
-
- public Layer this[long index]
- {
- get => _layers[index];
- set => SetLayer((uint) index, value);
- }
-
- public Layer[] this[System.Range range] => _layers[range];
-
- #endregion
-
- #region Numerators
- public IEnumerator<Layer> GetEnumerator()
- {
- return ((IEnumerable<Layer>)Layers).GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- #endregion
-
- #region Static Methods
- /// <summary>
- /// Compress a layer from a <see cref="Stream"/>
- /// </summary>
- /// <param name="input"><see cref="Stream"/> to compress</param>
- /// <returns>Compressed byte array</returns>
- public static byte[] CompressLayer(Stream input)
- {
- return CompressLayer(input.ToArray());
- }
-
- /// <summary>
- /// Compress a layer from a byte array
- /// </summary>
- /// <param name="input">byte array to compress</param>
- /// <returns>Compressed byte array</returns>
- public static byte[] CompressLayer(byte[] input)
- {
- return input;
- /*using (MemoryStream output = new MemoryStream())
- {
- using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal))
- {
- dstream.Write(input, 0, input.Length);
- }
- return output.ToArray();
- }*/
- }
-
- /// <summary>
- /// Decompress a layer from a byte array
- /// </summary>
- /// <param name="input">byte array to decompress</param>
- /// <returns>Decompressed byte array</returns>
- public static byte[] DecompressLayer(byte[] input)
- {
- return input;
- /*using (MemoryStream ms = new MemoryStream(input))
- {
- using (MemoryStream output = new MemoryStream())
- {
- using (DeflateStream dstream = new DeflateStream(ms, CompressionMode.Decompress))
- {
- dstream.CopyTo(output);
- }
- return output.ToArray();
- }
- }*/
- }
- #endregion
-
- #region Methods
-
- /// <summary>
- /// Sanitize layers and thrown exception if a severe problem is found
- /// </summary>
- /// <returns>True if one or more corrections has been applied, otherwise false</returns>
- public bool Sanitize()
- {
- bool appliedCorrections = false;
-
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- // Check for null layers
- if (this[layerIndex] is null) throw new InvalidDataException($"Layer {layerIndex} was defined but doesn't contain a valid image.");
- if (layerIndex <= 0) continue;
- // Check for bigger position z than it successor
- if (this[layerIndex - 1].PositionZ > this[layerIndex].PositionZ && this[layerIndex - 1].NonZeroPixelCount > 1)
- throw new InvalidDataException($"Layer {layerIndex - 1} ({this[layerIndex - 1].PositionZ}mm) have a higher Z position than the successor layer {layerIndex} ({this[layerIndex].PositionZ}mm).\n");
- }
-
- if (SlicerFile.ResolutionX == 0 || SlicerFile.ResolutionY == 0)
- {
- var layer = FirstLayer;
- if (layer is not null)
- {
- using var mat = layer.LayerMat;
-
- if (mat.Size.HaveZero())
- {
- throw new FileLoadException($"File resolution ({SlicerFile.Resolution}) is invalid and can't be auto fixed due invalid layers with same problem ({mat.Size}).", SlicerFile.FileFullPath);
- }
-
- SlicerFile.Resolution = mat.Size;
- appliedCorrections = true;
- }
- }
-
- // Fix 0mm positions at layer 0
- if (this[0].PositionZ == 0)
- {
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- this[layerIndex].PositionZ = Layer.RoundHeight(this[layerIndex].PositionZ + SlicerFile.LayerHeight);
- }
-
- appliedCorrections = true;
- }
-
- // Fix LightPWM of 0
- if (SlicerFile.LightPWM == 0)
- {
- SlicerFile.LightPWM = FileFormat.DefaultLightPWM;
- appliedCorrections = true;
- }
- if (SlicerFile.BottomLightPWM == 0)
- {
- SlicerFile.BottomLightPWM = FileFormat.DefaultBottomLightPWM;
- appliedCorrections = true;
- }
-
- return appliedCorrections;
- }
-
- /// <summary>
- /// Rebuild layer properties based on slice settings
- /// </summary>
- public void RebuildLayersProperties(bool recalculateZPos = true, string property = null)
- {
- //var layerHeight = SlicerFile.LayerHeight;
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- var layer = this[layerIndex];
- layer.Index = layerIndex;
- layer.ParentLayerManager = this;
-
- if (recalculateZPos)
- {
- layer.PositionZ = SlicerFile.GetHeightFromLayer(layerIndex);
- }
-
- if (property != string.Empty)
- {
- if (property is null or nameof(SlicerFile.BottomLayerCount))
- {
- layer.LightOffDelay = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLightOffDelay, SlicerFile.LightOffDelay);
- layer.WaitTimeBeforeCure = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomWaitTimeBeforeCure, SlicerFile.WaitTimeBeforeCure);
- layer.ExposureTime = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime);
- layer.WaitTimeAfterCure = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomWaitTimeAfterCure, SlicerFile.WaitTimeAfterCure);
- layer.LiftHeight = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight);
- layer.LiftSpeed = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed);
- layer.LiftHeight2 = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2);
- layer.LiftSpeed2 = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2);
- layer.WaitTimeAfterLift = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomWaitTimeAfterLift, SlicerFile.WaitTimeAfterLift);
- layer.RetractSpeed = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed);
- layer.RetractHeight2 = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractHeight2, SlicerFile.RetractHeight2);
- layer.RetractSpeed2 = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
- layer.LightPWM = SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
- }
- else
- {
- if (layer.IsBottomLayer)
- {
- if (property == nameof(SlicerFile.BottomLightOffDelay)) layer.LightOffDelay = SlicerFile.BottomLightOffDelay;
- else if (property == nameof(SlicerFile.BottomWaitTimeBeforeCure)) layer.WaitTimeBeforeCure = SlicerFile.BottomWaitTimeBeforeCure;
- else if (property == nameof(SlicerFile.BottomExposureTime)) layer.ExposureTime = SlicerFile.BottomExposureTime;
- else if (property == nameof(SlicerFile.BottomWaitTimeAfterCure)) layer.WaitTimeAfterCure = SlicerFile.BottomWaitTimeAfterCure;
- else if (property == nameof(SlicerFile.BottomLiftHeight)) layer.LiftHeight = SlicerFile.BottomLiftHeight;
- else if (property == nameof(SlicerFile.BottomLiftSpeed)) layer.LiftSpeed = SlicerFile.BottomLiftSpeed;
- else if (property == nameof(SlicerFile.BottomLiftHeight2)) layer.LiftHeight2 = SlicerFile.BottomLiftHeight2;
- else if (property == nameof(SlicerFile.BottomLiftSpeed2)) layer.LiftSpeed2 = SlicerFile.BottomLiftSpeed2;
- else if (property == nameof(SlicerFile.BottomWaitTimeAfterLift)) layer.WaitTimeAfterLift = SlicerFile.BottomWaitTimeAfterLift;
- else if (property == nameof(SlicerFile.BottomRetractSpeed)) layer.RetractSpeed = SlicerFile.BottomRetractSpeed;
- else if (property == nameof(SlicerFile.BottomRetractHeight2)) layer.RetractHeight2 = SlicerFile.BottomRetractHeight2;
- else if (property == nameof(SlicerFile.BottomRetractSpeed2)) layer.RetractSpeed2 = SlicerFile.BottomRetractSpeed2;
- else if (property == nameof(SlicerFile.BottomLightPWM)) layer.LightPWM = SlicerFile.BottomLightPWM;
-
- // Propagate value to layer when bottom property does not exists
- else if (property == nameof(SlicerFile.LightOffDelay) && !SlicerFile.CanUseBottomLightOffDelay) layer.LightOffDelay = SlicerFile.LightOffDelay;
- else if (property == nameof(SlicerFile.WaitTimeBeforeCure) && !SlicerFile.CanUseBottomWaitTimeBeforeCure) layer.WaitTimeBeforeCure = SlicerFile.WaitTimeBeforeCure;
- else if (property == nameof(SlicerFile.ExposureTime) && !SlicerFile.CanUseBottomExposureTime) layer.ExposureTime = SlicerFile.ExposureTime;
- else if (property == nameof(SlicerFile.WaitTimeAfterCure) && !SlicerFile.CanUseBottomWaitTimeAfterCure) layer.WaitTimeAfterCure = SlicerFile.WaitTimeAfterCure;
- else if (property == nameof(SlicerFile.LiftHeight) && !SlicerFile.CanUseBottomLiftHeight) layer.LiftHeight = SlicerFile.LiftHeight;
- else if (property == nameof(SlicerFile.LiftSpeed) && !SlicerFile.CanUseBottomLiftSpeed) layer.LiftSpeed = SlicerFile.LiftSpeed;
- else if (property == nameof(SlicerFile.LiftHeight2) && !SlicerFile.CanUseBottomLiftHeight2) layer.LiftHeight2 = SlicerFile.LiftHeight2;
- else if (property == nameof(SlicerFile.LiftSpeed2) && !SlicerFile.CanUseBottomLiftSpeed2) layer.LiftSpeed2 = SlicerFile.LiftSpeed2;
- else if (property == nameof(SlicerFile.WaitTimeAfterLift) && !SlicerFile.CanUseBottomWaitTimeAfterLift) layer.WaitTimeAfterLift = SlicerFile.WaitTimeAfterLift;
- else if (property == nameof(SlicerFile.RetractSpeed) && !SlicerFile.CanUseBottomRetractSpeed) layer.RetractSpeed = SlicerFile.RetractSpeed;
- else if (property == nameof(SlicerFile.RetractHeight2) && !SlicerFile.CanUseBottomRetractHeight2) layer.RetractHeight2 = SlicerFile.RetractHeight2;
- else if (property == nameof(SlicerFile.RetractSpeed2) && !SlicerFile.CanUseRetractSpeed2) layer.RetractSpeed2 = SlicerFile.RetractSpeed2;
- else if (property == nameof(SlicerFile.LightPWM) && !SlicerFile.CanUseBottomLightPWM) layer.LightPWM = SlicerFile.LightPWM;
- }
- else // Normal layers
- {
- if (property == nameof(SlicerFile.LightOffDelay)) layer.LightOffDelay = SlicerFile.LightOffDelay;
- else if (property == nameof(SlicerFile.WaitTimeBeforeCure)) layer.WaitTimeBeforeCure = SlicerFile.WaitTimeBeforeCure;
- else if (property == nameof(SlicerFile.ExposureTime)) layer.ExposureTime = SlicerFile.ExposureTime;
- else if (property == nameof(SlicerFile.WaitTimeAfterCure)) layer.WaitTimeAfterCure = SlicerFile.WaitTimeAfterCure;
- else if (property == nameof(SlicerFile.LiftHeight)) layer.LiftHeight = SlicerFile.LiftHeight;
- else if (property == nameof(SlicerFile.LiftSpeed)) layer.LiftSpeed = SlicerFile.LiftSpeed;
- else if (property == nameof(SlicerFile.LiftHeight2)) layer.LiftHeight2 = SlicerFile.LiftHeight2;
- else if (property == nameof(SlicerFile.LiftSpeed2)) layer.LiftSpeed2 = SlicerFile.LiftSpeed2;
- else if (property == nameof(SlicerFile.WaitTimeAfterLift)) layer.WaitTimeAfterLift = SlicerFile.WaitTimeAfterLift;
- else if (property == nameof(SlicerFile.RetractSpeed)) layer.RetractSpeed = SlicerFile.RetractSpeed;
- else if (property == nameof(SlicerFile.RetractHeight2)) layer.RetractHeight2 = SlicerFile.RetractHeight2;
- else if (property == nameof(SlicerFile.RetractSpeed2)) layer.RetractSpeed2 = SlicerFile.RetractSpeed2;
- else if (property == nameof(SlicerFile.LightPWM)) layer.LightPWM = SlicerFile.LightPWM;
- }
- }
- }
-
- layer.MaterialMilliliters = -1; // Recalculate this value to be sure
- }
-
- SlicerFile?.RebuildGCode();
- }
-
- /// <summary>
- /// Set LiftHeight to 0 if previous and current have same PositionZ
- /// <param name="zeroLightOffDelay">If true also set light off to 0, otherwise current value will be kept.</param>
- /// </summary>
- public void SetNoLiftForSamePositionedLayers(bool zeroLightOffDelay = false)
- => SetLiftForSamePositionedLayers(0, zeroLightOffDelay);
-
- public void SetLiftForSamePositionedLayers(float liftHeight = 0, bool zeroLightOffDelay = false)
- {
- for (int layerIndex = 1; layerIndex < LayerCount; layerIndex++)
- {
- var layer = this[layerIndex];
- if (this[layerIndex - 1].PositionZ != layer.PositionZ) continue;
- layer.LiftHeightTotal = liftHeight;
- layer.WaitTimeAfterLift = 0;
- if (zeroLightOffDelay)
- {
- layer.LightOffDelay = 0;
- layer.WaitTimeBeforeCure = 0;
- layer.WaitTimeAfterCure = 0;
- }
- }
- SlicerFile?.RebuildGCode();
- }
-
- public IEnumerable<Layer> GetSamePositionedLayers()
- {
- for (int layerIndex = 1; layerIndex < LayerCount; layerIndex++)
- {
- var layer = this[layerIndex];
- if (this[layerIndex - 1].PositionZ != layer.PositionZ) continue;
- yield return layer;
- }
- }
-
- public IEnumerable<Layer> GetDistinctLayersByPositionZ(uint layerIndexStart = 0) =>
- GetDistinctLayersByPositionZ(layerIndexStart, LastLayerIndex);
-
- public IEnumerable<Layer> GetDistinctLayersByPositionZ(uint layerIndexStart, uint layerIndexEnd)
- {
- return layerIndexEnd - layerIndexStart >= LastLayerIndex
- ? SlicerFile.DistinctBy(layer => layer.PositionZ)
- : SlicerFile.Where((_, layerIndex) => layerIndex >= layerIndexStart && layerIndex <= layerIndexEnd).DistinctBy(layer => layer.PositionZ);
- }
-
- public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex, MatCacheManager cacheManager, out uint lastLayerIndex)
- {
- var startLayerPositionZ = this[layerIndex].PositionZ;
- lastLayerIndex = layerIndex;
- var layerMat = cacheManager.Get1(layerIndex).Clone();
-
- for (var curIndex = layerIndex + 1; curIndex < LayerCount && this[curIndex].PositionZ == startLayerPositionZ; curIndex++)
- {
- CvInvoke.Max(layerMat, cacheManager.Get1(curIndex), layerMat);
- lastLayerIndex = curIndex;
- }
-
- return layerMat;
- }
-
- public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex, MatCacheManager cacheManager)
- => GetMergedMatForSequentialPositionedLayers(layerIndex, cacheManager, out _);
-
- public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex, out uint lastLayerIndex)
- {
- var startLayer = this[layerIndex];
- lastLayerIndex = layerIndex;
- var layerMat = startLayer.LayerMat;
-
- for (var curIndex = layerIndex + 1; curIndex < LayerCount && this[curIndex].PositionZ == startLayer.PositionZ; curIndex++)
- {
- using var nextLayer = this[curIndex].LayerMat;
- CvInvoke.Max(nextLayer, layerMat, layerMat);
- lastLayerIndex = curIndex;
- }
-
- return layerMat;
- }
-
- public Mat GetMergedMatForSequentialPositionedLayers(uint layerIndex)
- => GetMergedMatForSequentialPositionedLayers(layerIndex, out _);
-
- public Rectangle GetBoundingRectangle(OperationProgress progress = null)
- {
- var firstLayer = FirstLayer;
- if (!_boundingRectangle.IsEmpty || LayerCount == 0 || firstLayer is null || !firstLayer.HaveImage) return _boundingRectangle;
- progress ??= new OperationProgress(OperationProgress.StatusOptimizingBounds, LayerCount - 1);
- _boundingRectangle = Rectangle.Empty;
- uint firstValidLayerBounds = 0;
-
- void FindFirstBoundingRectangle()
- {
- for (uint layerIndex = 0; layerIndex < Count; layerIndex++)
- {
- firstValidLayerBounds = layerIndex;
- if (this[layerIndex] is null || this[layerIndex].BoundingRectangle == Rectangle.Empty) continue;
- _boundingRectangle = this[layerIndex].BoundingRectangle;
- break;
- }
- }
-
- FindFirstBoundingRectangle();
- //_boundingRectangle = firstLayer.BoundingRectangle;
-
- if (_boundingRectangle.IsEmpty) // Safe checking, all layers haven't a bounding rectangle
- {
- progress.Reset(OperationProgress.StatusOptimizingBounds, LayerCount-1);
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- this[layerIndex].GetBoundingRectangle();
-
- progress.LockAndIncrement();
- });
-
- if (progress.Token.IsCancellationRequested)
- {
- _boundingRectangle = Rectangle.Empty;
- progress.Token.ThrowIfCancellationRequested();
- }
-
- FindFirstBoundingRectangle();
- }
-
- if (firstValidLayerBounds+1 < LayerCount)
- {
- progress.Reset(OperationProgress.StatusCalculatingBounds, LayerCount - firstValidLayerBounds - 1);
- for (var i = firstValidLayerBounds+1; i < LayerCount; i++)
- {
- if (this[i] is null || this[i].BoundingRectangle.IsEmpty) continue;
- _boundingRectangle = Rectangle.Union(_boundingRectangle, this[i].BoundingRectangle);
- progress++;
- }
- }
-
- RaisePropertyChanged(nameof(BoundingRectangle));
- return _boundingRectangle;
- }
-
- /// <summary>
- /// Sets a layer
- /// </summary>
- /// <param name="index">Layer index</param>
- /// <param name="layer">Layer to add</param>
- /// <param name="makeClone">True to add a clone of the layer</param>
- public void SetLayer(uint index, Layer layer, bool makeClone = false)
- {
- if (_layers[index] is not null && layer is not null) layer.IsModified = true;
- _layers[index] = makeClone && layer is not null ? layer.Clone() : layer;
- if (layer is null) return;
- layer.Index = index;
- layer.ParentLayerManager = this;
- }
-
- /// <summary>
- /// Add a list of layers
- /// </summary>
- /// <param name="layers">Layers to add</param>
- /// <param name="makeClone">True to add a clone of layers</param>
- public void SetLayers(IEnumerable<Layer> layers, bool makeClone = false)
- {
- foreach (var layer in layers)
- {
- SetLayer(layer.Index, layer, makeClone);
- }
- }
-
- /// <summary>
- /// Get layer given index
- /// </summary>
- /// <param name="index">Layer index</param>
- /// <returns></returns>
- public Layer GetLayer(uint index)
- {
- return _layers[index];
- }
-
- public Layer GetSmallestLayerBetween(uint layerStartIndex, uint layerEndIndex)
- {
- return _layers?.Where((layer, index) => !layer.IsEmpty && index >= layerStartIndex && index <= layerEndIndex).MinBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
- }
-
- public Layer GetLargestLayerBetween(uint layerStartIndex, uint layerEndIndex)
- {
- return _layers?.Where((layer, index) => !layer.IsEmpty && index >= layerStartIndex && index <= layerEndIndex).MaxBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
- }
-
- public IEnumerable<Layer> GetLayersFromHeightRange(float startPositionZ, float endPositionZ)
- {
- return this.Where(layer => layer.PositionZ >= startPositionZ && layer.PositionZ <= endPositionZ);
- }
-
- public IEnumerable<Layer> GetLayersFromHeightRange(float endPositionZ)
- {
- return this.Where(layer => layer.PositionZ <= endPositionZ);
- }
-
- public static void MutateGetVarsIterationChamfer(uint startLayerIndex, uint endLayerIndex, int iterationsStart, int iterationsEnd, ref bool isFade, out float iterationSteps, out int maxIteration)
- {
- iterationSteps = 0;
- maxIteration = 0;
- isFade = isFade && startLayerIndex != endLayerIndex && iterationsStart != iterationsEnd;
- if (!isFade) return;
- iterationSteps = Math.Abs((iterationsStart - (float)iterationsEnd) / ((float)endLayerIndex - startLayerIndex));
- maxIteration = Math.Max(iterationsStart, iterationsEnd);
- }
-
- public static int MutateGetIterationVar(bool isFade, int iterationsStart, int iterationsEnd, float iterationSteps, int maxIteration, uint startLayerIndex, uint layerIndex)
- {
- if (!isFade) return iterationsStart;
- // calculate iterations based on range
- int iterations = (int)(iterationsStart < iterationsEnd
- ? iterationsStart + (layerIndex - startLayerIndex) * iterationSteps
- : iterationsStart - (layerIndex - startLayerIndex) * iterationSteps);
-
- // constrain
- return Math.Min(Math.Max(0, iterations), maxIteration);
- }
-
- public static int MutateGetIterationChamfer(uint layerIndex, uint startLayerIndex, uint endLayerIndex, int iterationsStart,
- int iterationsEnd, bool isFade)
- {
- MutateGetVarsIterationChamfer(startLayerIndex, endLayerIndex, iterationsStart, iterationsEnd, ref isFade,
- out float iterationSteps, out int maxIteration);
- return MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, layerIndex);
- }
-
- public void DrawModifications(IList<PixelOperation> drawings, OperationProgress progress = null)
- {
- progress ??= new OperationProgress();
- progress.Reset("Drawings", (uint) drawings.Count);
-
- ConcurrentDictionary<uint, Mat> modifiedLayers = new();
- for (var i = 0; i < drawings.Count; i++)
- {
- var operation = drawings[i];
-
- if (operation.OperationType == PixelOperation.PixelOperationType.Drawing)
- {
- var operationDrawing = (PixelDrawing) operation;
- var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat);
-
- if (operationDrawing.BrushSize == 1)
- {
- mat.SetByte(operation.Location.X, operation.Location.Y, operationDrawing.Brightness);
- continue;
- }
-
- mat.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location,
- new MCvScalar(operationDrawing.Brightness), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType);
- /*switch (operationDrawing.BrushShape)
- {
- case PixelDrawing.BrushShapeType.Square:
- CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
- break;
- case PixelDrawing.BrushShapeType.Circle:
- CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2,
- new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }*/
- }
- else if (operation.OperationType == PixelOperation.PixelOperationType.Text)
- {
- var operationText = (PixelText)operation;
- var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat);
-
- mat.PutTextRotated(operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(operationText.Brightness), operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.LineAlignment, operationText.Angle);
- }
- else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser)
- {
- var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat);
-
- using var layerContours = mat.FindContours(out var hierarchy, RetrType.Tree);
-
- if (mat.GetByte(operation.Location) >= 10)
- {
- using var vec = EmguContours.GetContoursInside(layerContours, hierarchy, operation.Location);
-
- if (vec.Size > 0)
- {
- CvInvoke.DrawContours(mat, vec, -1, new MCvScalar(operation.PixelBrightness), -1);
- }
- }
- }
- else if (operation.OperationType == PixelOperation.PixelOperationType.Supports)
- {
- var operationSupport = (PixelSupport)operation;
- int drawnLayers = 0;
- for (int operationLayer = (int)operation.LayerIndex-1; operationLayer >= 0; operationLayer--)
- {
- var mat = modifiedLayers.GetOrAdd((uint) operationLayer, u => this[operationLayer].LayerMat);
- int radius = (operationLayer > 10 ? Math.Min(operationSupport.TipDiameter + drawnLayers, operationSupport.PillarDiameter) : operationSupport.BaseDiameter) / 2;
- uint whitePixels;
-
- int yStart = Math.Max(0, operation.Location.Y - operationSupport.TipDiameter / 2);
- int xStart = Math.Max(0, operation.Location.X - operationSupport.TipDiameter / 2);
-
- using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter)))
- {
- using var matCircleMask = matCircleRoi.NewBlank();
- CvInvoke.Circle(matCircleMask, new Point(operationSupport.TipDiameter / 2, operationSupport.TipDiameter / 2),
- operationSupport.TipDiameter / 2, new MCvScalar(operation.PixelBrightness), -1);
- CvInvoke.BitwiseAnd(matCircleRoi, matCircleMask, matCircleMask);
- whitePixels = (uint) CvInvoke.CountNonZero(matCircleMask);
- }
-
- if (whitePixels >= Math.Pow(operationSupport.TipDiameter, 2) / 3)
- {
- //CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(255), -1);
- if (drawnLayers == 0) continue; // Supports nonexistent, keep digging
- break; // White area end supporting
- }
-
- CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(operation.PixelBrightness), -1, operationSupport.LineType);
- drawnLayers++;
- }
- }
- else if (operation.OperationType == PixelOperation.PixelOperationType.DrainHole)
- {
- uint drawnLayers = 0;
- var operationDrainHole = (PixelDrainHole)operation;
- for (int operationLayer = (int)operation.LayerIndex; operationLayer >= 0; operationLayer--)
- {
- var mat = modifiedLayers.GetOrAdd((uint)operationLayer, u => this[operationLayer].LayerMat);
- int radius = operationDrainHole.Diameter / 2;
- uint blackPixels;
-
- int yStart = Math.Max(0, operation.Location.Y - radius);
- int xStart = Math.Max(0, operation.Location.X - radius);
-
- using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter)))
- {
- using var matCircleRoiInv = new Mat();
- CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv);
- using var matCircleMask = matCircleRoi.NewBlank();
- CvInvoke.Circle(matCircleMask, new Point(radius, radius), radius, EmguExtensions.WhiteColor, -1);
- CvInvoke.BitwiseAnd(matCircleRoiInv, matCircleMask, matCircleMask);
- blackPixels = (uint) CvInvoke.CountNonZero(matCircleMask);
- }
-
- if (blackPixels >= Math.Pow(operationDrainHole.Diameter, 2) / 3) // Enough area to drain?
- {
- if (drawnLayers == 0) continue; // Drill not found a target yet, keep digging
- break; // Stop drill drain found!
- }
-
- CvInvoke.Circle(mat, operation.Location, radius, EmguExtensions.BlackColor, -1, operationDrainHole.LineType);
- drawnLayers++;
- }
- }
-
- progress++;
- }
-
- progress.Reset("Saving", (uint) modifiedLayers.Count);
- Parallel.ForEach(modifiedLayers, CoreSettings.ParallelOptions, modifiedLayer =>
- {
- this[modifiedLayer.Key].LayerMat = modifiedLayer.Value;
- modifiedLayer.Value.Dispose();
-
- progress.LockAndIncrement();
- });
-
- }
-
- /// <summary>
- /// Set the IsModified property for all layers
- /// </summary>
- public void SetAllIsModified(bool isModified)
- {
- for (uint i = 0; i < LayerCount; i++)
- {
- if(Layers[i] is null) continue;
- Layers[i].IsModified = isModified;
- }
- }
-
- /// <summary>
- /// Reallocate with new size
- /// </summary>
- /// <returns></returns>
- public Layer[] ReallocateNew(uint newLayerCount, bool makeClone = false)
- {
- var layers = new Layer[newLayerCount];
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- if (layerIndex >= newLayerCount) break;
- var layer = this[layerIndex];
- layers[layerIndex] = makeClone && layer is not null ? layer.Clone() : layer;
- }
-
- return layers;
- }
-
- /// <summary>
- /// Reallocate layer count with a new size
- /// </summary>
- /// <param name="newLayerCount">New layer count</param>
- /// <param name="initBlack"></param>
- public void Reallocate(uint newLayerCount, bool initBlack = false)
- {
- var oldLayerCount = LayerCount;
- int differenceLayerCount = (int)newLayerCount - Count;
- if (differenceLayerCount == 0) return;
- var newLayers = new Layer[newLayerCount];
-
- Array.Copy(_layers, 0, newLayers, 0, Math.Min(newLayerCount, newLayers.Length));
-
- if (differenceLayerCount > 0 && initBlack)
- {
- using var blackMat = EmguExtensions.InitMat(SlicerFile.Resolution);
- var pngBytes = blackMat.GetPngByes();
- for (var layerIndex = oldLayerCount; layerIndex < newLayerCount; layerIndex++)
- {
- newLayers[layerIndex] = new Layer(layerIndex, pngBytes.ToArray(), this);
- }
- }
-
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- Layers = newLayers;
- });
- }
-
- /// <summary>
- /// Reallocate at given index
- /// </summary>
- /// <returns></returns>
- public void ReallocateInsert(uint insertAtLayerIndex, uint layerCount, bool initBlack = false)
- {
- if (layerCount == 0) return;
- insertAtLayerIndex = Math.Min(insertAtLayerIndex, LayerCount);
- var newLayers = new Layer[LayerCount + layerCount];
-
- // Copy from start to insert index
- if(insertAtLayerIndex > 0)
- Array.Copy(_layers, 0, newLayers, 0, insertAtLayerIndex);
-
- // Rearrange from last insert to end
- if(insertAtLayerIndex < LayerCount)
- Array.Copy(
- _layers, insertAtLayerIndex,
- newLayers, insertAtLayerIndex + layerCount,
- LayerCount - insertAtLayerIndex);
- /*for (uint layerIndex = insertAtLayerIndex; layerIndex < _layers.Length; layerIndex++)
- {
- newLayers[layerCount + layerIndex] = _layers[layerIndex];
- newLayers[layerCount + layerIndex].Index = layerCount + layerIndex;
- }*/
-
- // Allocate new layers in between
- if (initBlack)
- {
- using var blackMat = EmguExtensions.InitMat(SlicerFile.Resolution);
- var pngBytes = blackMat.GetPngByes();
- for (var layerIndex = insertAtLayerIndex; layerIndex < insertAtLayerIndex + layerCount; layerIndex++)
- {
- newLayers[layerIndex] = new Layer(layerIndex, pngBytes.ToArray(), this);
- }
- }
-
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- Layers = newLayers;
- });
- }
-
- /// <summary>
- /// Reallocate at a kept range
- /// </summary>
- /// <param name="startLayerIndex"></param>
- /// <param name="endLayerIndex"></param>
- public void ReallocateKeepRange(uint startLayerIndex, uint endLayerIndex)
- {
- if ((int)(endLayerIndex - startLayerIndex) < 0) return;
- var newLayers = new Layer[1 + endLayerIndex - startLayerIndex];
-
- Array.Copy(_layers, startLayerIndex, newLayers, 0, newLayers.Length);
- /*uint currentLayerIndex = 0;
- for (uint layerIndex = startLayerIndex; layerIndex <= endLayerIndex; layerIndex++)
- {
- newLayers[currentLayerIndex++] = _layers[layerIndex];
- }*/
-
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- Layers = newLayers;
- });
- }
-
- /// <summary>
- /// Reallocate at start
- /// </summary>
- /// <returns></returns>
- public void ReallocateStart(uint layerCount, bool initBlack = false) => ReallocateInsert(0, layerCount, initBlack);
-
- /// <summary>
- /// Reallocate at end
- /// </summary>
- /// <returns></returns>
- public void ReallocateEnd(uint layerCount, bool initBlack = false) => ReallocateInsert(LayerCount, layerCount, initBlack);
-
- /// <summary>
- /// Allocate layers from a Mat array
- /// </summary>
- /// <param name="mats"></param>
- /// <returns>The new Layer array</returns>
- public Layer[] AllocateFromMat(Mat[] mats)
- {
- var layers = new Layer[mats.Length];
- Parallel.For(0, mats.Length, CoreSettings.ParallelOptions, i =>
- {
- layers[i] = new Layer((uint)i, mats[i], this);
- });
-
- return layers;
- }
-
- /// <summary>
- /// Allocate layers from a Mat array and set them to the current file
- /// </summary>
- /// <param name="mats"></param>
- /// /// <returns>The new Layer array</returns>
- public Layer[] AllocateAndSetFromMat(Mat[] mats)
- {
- var layers = AllocateFromMat(mats);
- Layers = layers;
- return layers;
- }
-
- /// <summary>
- /// Clone this object
- /// </summary>
- /// <returns></returns>
- public LayerManager Clone()
- {
- LayerManager layerManager = new(SlicerFile);
- layerManager.Init(CloneLayers());
- /*foreach (var layer in this)
- {
- layerManager[layer.Index] = layer.Clone();
- }*/
- layerManager.BoundingRectangle = BoundingRectangle;
-
- return layerManager;
- }
-
- /// <summary>
- /// Clone layers
- /// </summary>
- /// <returns></returns>
- public Layer[] CloneLayers()
- {
- return Layer.CloneLayers(_layers);
- }
-
- public void Dispose() { }
-
- #endregion
-
- #region Formater
-
- public override string ToString()
- {
- return $"{nameof(BoundingRectangle)}: {BoundingRectangle}, {nameof(LayerCount)}: {LayerCount}, {nameof(IsModified)}: {IsModified}";
- }
-
- #endregion
- }
-}
diff --git a/UVtools.Core/Layers/MainIssue.cs b/UVtools.Core/Layers/MainIssue.cs
index 14ca0dd..fbc9ec9 100644
--- a/UVtools.Core/Layers/MainIssue.cs
+++ b/UVtools.Core/Layers/MainIssue.cs
@@ -13,161 +13,160 @@ using System.Drawing;
using System.Linq;
using UVtools.Core.Extensions;
-namespace UVtools.Core.Layers
+namespace UVtools.Core.Layers;
+
+public class MainIssue : IReadOnlyList<Issue>
{
- public class MainIssue : IReadOnlyList<Issue>
+ public enum IssueType : byte
{
- public enum IssueType : byte
- {
- Island,
- Overhang,
- ResinTrap,
- SuctionCup,
- TouchingBound,
- PrintHeight,
- EmptyLayer,
- Debug
- //HoleSandwich,
- }
+ Island,
+ Overhang,
+ ResinTrap,
+ SuctionCup,
+ TouchingBound,
+ PrintHeight,
+ EmptyLayer,
+ Debug
+ //HoleSandwich,
+ }
- /// <summary>
- /// Gets the issue type associated
- /// </summary>
- public IssueType Type { get; init; }
-
- /// <summary>
- /// Gets the layer where issue is present and starts
- /// </summary>
- public Layer StartLayer => Childs[0].Layer;
-
- /// <summary>
- /// Gets the layer where issue ends
- /// </summary>
- public Layer EndLayer => Childs[^1].Layer;
-
- /// <summary>
- /// Gets the layer index
- /// </summary>
- public uint StartLayerIndex => StartLayer.Index;
-
- /// <summary>
- /// Gets the layer index
- /// </summary>
- public uint EndLayerIndex => EndLayer.Index;
-
- /// <summary>
- /// Gets the number of layers in this range
- /// </summary>
- public uint LayerRangeCount => 1 + EndLayerIndex - StartLayerIndex;
-
- public string LayerInfoStr => StartLayerIndex == EndLayerIndex
- ? $"{StartLayerIndex}"
- : $"{StartLayerIndex}-{EndLayerIndex} ({LayerRangeCount})";
-
- /// <summary>
- /// Gets the total height that represents this issue
- /// </summary>
- public float TotalHeight => Layer.RoundHeight(StartLayer.LayerHeight + EndLayer.PositionZ - StartLayer.PositionZ);
-
- /// <summary>
- /// Gets the bounding rectangle of the area
- /// </summary>
- public Rectangle BoundingRectangle { get; init; }
-
- /// <summary>
- /// Gets the area of the issue
- /// </summary>
- public uint PixelCount { get; init; }
-
- /// <summary>
- /// Gets the area of the issue
- /// </summary>
- public double Area { get; init; }
-
- /// <summary>
- /// Gets all issues inside this main issue
- /// </summary>
- public Issue[] Childs { get; init; }
-
- public MainIssue(IssueType type, Rectangle boundingRectangle = default)
- {
- Type = type;
- BoundingRectangle = boundingRectangle;
- Area = boundingRectangle.Area();
- }
+ /// <summary>
+ /// Gets the issue type associated
+ /// </summary>
+ public IssueType Type { get; init; }
+
+ /// <summary>
+ /// Gets the layer where issue is present and starts
+ /// </summary>
+ public Layer StartLayer => Childs[0].Layer;
+
+ /// <summary>
+ /// Gets the layer where issue ends
+ /// </summary>
+ public Layer EndLayer => Childs[^1].Layer;
+
+ /// <summary>
+ /// Gets the layer index
+ /// </summary>
+ public uint StartLayerIndex => StartLayer.Index;
+
+ /// <summary>
+ /// Gets the layer index
+ /// </summary>
+ public uint EndLayerIndex => EndLayer.Index;
+
+ /// <summary>
+ /// Gets the number of layers in this range
+ /// </summary>
+ public uint LayerRangeCount => 1 + EndLayerIndex - StartLayerIndex;
+
+ public string LayerInfoStr => StartLayerIndex == EndLayerIndex
+ ? $"{StartLayerIndex}"
+ : $"{StartLayerIndex}-{EndLayerIndex} ({LayerRangeCount})";
+
+ /// <summary>
+ /// Gets the total height that represents this issue
+ /// </summary>
+ public float TotalHeight => Layer.RoundHeight(StartLayer.LayerHeight + EndLayer.PositionZ - StartLayer.PositionZ);
+
+ /// <summary>
+ /// Gets the bounding rectangle of the area
+ /// </summary>
+ public Rectangle BoundingRectangle { get; init; }
+
+ /// <summary>
+ /// Gets the area of the issue
+ /// </summary>
+ public uint PixelCount { get; init; }
+
+ /// <summary>
+ /// Gets the area of the issue
+ /// </summary>
+ public double Area { get; init; }
+
+ /// <summary>
+ /// Gets all issues inside this main issue
+ /// </summary>
+ public Issue[] Childs { get; init; } = null!;
+
+ public MainIssue(IssueType type, Rectangle boundingRectangle = default)
+ {
+ Type = type;
+ BoundingRectangle = boundingRectangle;
+ Area = boundingRectangle.Area();
+ }
- public MainIssue(IssueType type, Issue issue) : this(type, issue.BoundingRectangle)
- {
- Childs = new[] { issue };
- issue.Parent = this;
- PixelCount = issue.PixelsCount;
- }
+ public MainIssue(IssueType type, Issue issue) : this(type, issue.BoundingRectangle)
+ {
+ Childs = new[] { issue };
+ issue.Parent = this;
+ PixelCount = issue.PixelsCount;
+ }
- public MainIssue(IssueType type, IEnumerable<Issue> issues) : this(type)
+ public MainIssue(IssueType type, IEnumerable<Issue> issues) : this(type)
+ {
+ var boundingRectangle = Rectangle.Empty;
+ double area = 0;
+ foreach (var issue in issues)
{
- var boundingRectangle = Rectangle.Empty;
- double area = 0;
- foreach (var issue in issues)
+ issue.Parent = this;
+ area += issue.Area / issue.Layer.LayerHeight;
+ PixelCount += issue.PixelsCount;
+ if (issue.BoundingRectangle.IsEmpty) continue;
+ if (boundingRectangle.IsEmpty)
{
- issue.Parent = this;
- area += issue.Area / issue.Layer.LayerHeight;
- PixelCount += issue.PixelsCount;
- if (issue.BoundingRectangle.IsEmpty) continue;
- if (boundingRectangle.IsEmpty)
- {
- boundingRectangle = issue.BoundingRectangle;
- continue;
- }
-
- boundingRectangle.Intersect(issue.BoundingRectangle);
+ boundingRectangle = issue.BoundingRectangle;
+ continue;
}
- BoundingRectangle = boundingRectangle;
- Area = area;
- Childs = issues.OrderBy(issue => issue.LayerIndex).ToArray();
- Sort();
+ boundingRectangle.Intersect(issue.BoundingRectangle);
}
- private void Sort()
- {
- Array.Sort(Childs, (issue, issue1) => issue.LayerIndex.CompareTo(issue1.LayerIndex));
- }
+ BoundingRectangle = boundingRectangle;
+ Area = area;
+ Childs = issues.OrderBy(issue => issue.LayerIndex).ToArray();
+ Sort();
+ }
+
+ private void Sort()
+ {
+ Array.Sort(Childs, (issue, issue1) => issue.LayerIndex.CompareTo(issue1.LayerIndex));
+ }
- public bool IsIssueInBetween(int layerIndex) => layerIndex >= StartLayerIndex && layerIndex <= EndLayerIndex;
- public bool IsIssueInBetween(uint layerIndex) => layerIndex >= StartLayerIndex && layerIndex <= EndLayerIndex;
- public bool IsIssueInBetween(Layer layer) => IsIssueInBetween(layer.Index);
+ public bool IsIssueInBetween(int layerIndex) => layerIndex >= StartLayerIndex && layerIndex <= EndLayerIndex;
+ public bool IsIssueInBetween(uint layerIndex) => layerIndex >= StartLayerIndex && layerIndex <= EndLayerIndex;
+ public bool IsIssueInBetween(Layer layer) => IsIssueInBetween(layer.Index);
- public IEnumerator<Issue> GetEnumerator()
- {
- return ((IEnumerable<Issue>)Childs).GetEnumerator();
- }
+ public IEnumerator<Issue> GetEnumerator()
+ {
+ return ((IEnumerable<Issue>)Childs).GetEnumerator();
+ }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return Childs.GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return Childs.GetEnumerator();
+ }
- public int Count => Childs.Length;
+ public int Count => Childs.Length;
- public Issue this[int index] => Childs[index];
+ public Issue this[int index] => Childs[index];
- protected bool Equals(MainIssue other)
- {
- return Type == other.Type && BoundingRectangle.Equals(other.BoundingRectangle) && PixelCount == other.PixelCount && Area.Equals(other.Area) && Childs.SequenceEqual(other.Childs);
- }
+ protected bool Equals(MainIssue other)
+ {
+ return Type == other.Type && BoundingRectangle.Equals(other.BoundingRectangle) && PixelCount == other.PixelCount && Area.Equals(other.Area) && Childs.SequenceEqual(other.Childs);
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((MainIssue)obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((MainIssue)obj);
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine((int)Type, BoundingRectangle, PixelCount, Area, Childs);
- }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine((int)Type, BoundingRectangle, PixelCount, Area, Childs);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Managers/ClipboardManager.cs b/UVtools.Core/Managers/ClipboardManager.cs
index 0ab372f..f2ad3f6 100644
--- a/UVtools.Core/Managers/ClipboardManager.cs
+++ b/UVtools.Core/Managers/ClipboardManager.cs
@@ -17,422 +17,421 @@ using UVtools.Core.Layers;
using UVtools.Core.Objects;
using UVtools.Core.Operations;
-namespace UVtools.Core.Managers
+namespace UVtools.Core.Managers;
+
+public sealed class ClipboardItem : List<Layer>
{
- public sealed class ClipboardItem : List<Layer>
- {
- private Operation _operation;
+ private Operation _operation = null!;
- #region Properties
+ #region Properties
- /// <summary>
- /// Gets the LayerCount for this clip
- /// </summary>
- public uint LayerCount { get; }
+ /// <summary>
+ /// Gets the LayerCount for this clip
+ /// </summary>
+ public uint LayerCount { get; }
- /// <summary>
- /// Gets the LayerHeight for this clip
- /// </summary>
- public float LayerHeight { get; }
+ /// <summary>
+ /// Gets the LayerHeight for this clip
+ /// </summary>
+ public float LayerHeight { get; }
- /// <summary>
- /// Gets the Resolution for this clip
- /// </summary>
- public Size Resolution { get; }
+ /// <summary>
+ /// Gets the Resolution for this clip
+ /// </summary>
+ public Size Resolution { get; }
- /// <summary>
- /// Gets the description of this operation
- /// </summary>
- public string Description { get; set; }
+ /// <summary>
+ /// Gets the description of this operation
+ /// </summary>
+ public string? Description { get; set; }
- public bool IsFullBackup { get; set; }
+ public bool IsFullBackup { get; set; }
- public Operation Operation
+ public Operation Operation
+ {
+ get => _operation;
+ set
{
- get => _operation;
- set
- {
- _operation = value;
- _operation.ImportedFrom = Operation.OperationImportFrom.Undo;
- }
+ _operation = value;
+ _operation.ImportedFrom = Operation.OperationImportFrom.Undo;
}
+ }
- #endregion
+ #endregion
- #region Constructor
- public ClipboardItem(FileFormat slicerFile, Operation operation, bool isFullBackup = false) : this(slicerFile, string.Empty, isFullBackup)
- {
- Operation = operation;
- string description = operation.ToString();
- if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}";
- Description = description;
- }
+ #region Constructor
+ public ClipboardItem(FileFormat slicerFile, Operation operation, bool isFullBackup = false) : this(slicerFile, string.Empty, isFullBackup)
+ {
+ Operation = operation;
+ string description = operation.ToString();
+ if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}";
+ Description = description;
+ }
- public ClipboardItem(FileFormat slicerFile, string description = null, bool isFullBackup = false)
- {
- LayerCount = slicerFile.LayerCount;
- LayerHeight = slicerFile.LayerHeight;
- Resolution = slicerFile.Resolution;
- Description = description;
- IsFullBackup = isFullBackup;
- }
- #endregion
-
- #region Methods
- public override string ToString()
- {
- return $"{(IsFullBackup ? "* " : "")}{Description} ({Count})";
- }
- #endregion
+ public ClipboardItem(FileFormat slicerFile, string? description = null, bool isFullBackup = false)
+ {
+ LayerCount = slicerFile.LayerCount;
+ LayerHeight = slicerFile.LayerHeight;
+ Resolution = slicerFile.Resolution;
+ Description = description;
+ IsFullBackup = isFullBackup;
}
+ #endregion
- public sealed class ClipboardManager : BindableBase, IList<ClipboardItem>
+ #region Methods
+ public override string ToString()
{
- #region Properties
+ return $"{(IsFullBackup ? "* " : "")}{Description} ({Count})";
+ }
+ #endregion
+}
- public RangeObservableCollection<ClipboardItem> Items { get; } = new();
+public sealed class ClipboardManager : BindableBase, IList<ClipboardItem>
+{
+ #region Properties
+
+ public RangeObservableCollection<ClipboardItem> Items { get; } = new();
- public FileFormat SlicerFile { get; set; }
+ public FileFormat SlicerFile { get; set; } = null!;
- private int _currentIndex = -1;
- private Layer[] _snapshotLayers;
- private bool _reallocatedLayerCount;
- private bool _suppressRestore;
+ private int _currentIndex = -1;
+ private Layer[]? _snapshotLayers;
+ private bool _reallocatedLayerCount;
+ private bool _suppressRestore;
- /// <summary>
- /// Gets the index of current item
- /// </summary>
- public int CurrentIndex {
- get => _currentIndex;
- set
+ /// <summary>
+ /// Gets the index of current item
+ /// </summary>
+ public int CurrentIndex {
+ get => _currentIndex;
+ set
+ {
+ if (_currentIndex == value) return;
+ if (value < -1) value = -1;
+ if (value >= Count) value = Count-1;
+ var oldIndex = _currentIndex;
+ _currentIndex = value;
+ //if (!RaiseAndSetIfChanged(ref _currentIndex, value)) return;
+
+ if (value >= 0 && !SuppressRestore)
{
- if (_currentIndex == value) return;
- if (value < -1) value = -1;
- if (value >= Count) value = Count-1;
- var oldIndex = _currentIndex;
- _currentIndex = value;
- //if (!RaiseAndSetIfChanged(ref _currentIndex, value)) return;
-
- if (value >= 0 && !SuppressRestore)
+ ReallocatedLayerCount = false;
+ int dir = oldIndex < _currentIndex ? 1 : -1;
+
+ for (int i = oldIndex + dir; i >= 0 && i < Count; i += dir)
{
- ReallocatedLayerCount = false;
- int dir = oldIndex < _currentIndex ? 1 : -1;
+ var clip = this[i];
- for (int i = oldIndex + dir; i >= 0 && i < Count; i += dir)
+ Layer[] layers;
+ if (clip.IsFullBackup)
{
- var clip = this[i];
+ if(!_reallocatedLayerCount && SlicerFile.LayerCount != clip.Count) ReallocatedLayerCount = true;
+ layers = clip.ToArray();
+ }
+ else
+ {
+ layers = SlicerFile.Layers.ToArray();
- Layer[] layers;
- if (clip.IsFullBackup)
- {
- if(!_reallocatedLayerCount && SlicerFile.LayerCount != clip.Count) ReallocatedLayerCount = true;
- layers = clip.ToArray();
- }
- else
+ if (SlicerFile.LayerCount != clip.LayerCount) // Need resize layer manager
{
- layers = SlicerFile.LayerManager.Layers.ToArray();
-
- if (SlicerFile.LayerCount != clip.LayerCount) // Need resize layer manager
- {
- //layers = SlicerFile.LayerManager.ReallocateNew(clip.LayerCount);
- layers = SlicerFile.LayerManager.ReallocateNew(clip.LayerCount);
- ReallocatedLayerCount = true;
- }
-
- foreach (var layer in clip)
- {
- layers[layer.Index] = layer;
- }
+ //layers = SlicerFile.LayerManager.ReallocateNew(clip.LayerCount);
+ layers = SlicerFile.ReallocateNew(clip.LayerCount);
+ ReallocatedLayerCount = true;
}
- if (SlicerFile.LayerHeight != clip.LayerHeight)
+ foreach (var layer in clip)
{
- SlicerFile.LayerHeight = clip.LayerHeight;
+ layers[layer.Index] = layer;
}
+ }
- if (SlicerFile.Resolution != clip.Resolution)
- {
- SlicerFile.Resolution = clip.Resolution;
- }
+ if (SlicerFile.LayerHeight != clip.LayerHeight)
+ {
+ SlicerFile.LayerHeight = clip.LayerHeight;
+ }
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerManager.Layers = Layer.CloneLayers(layers);
- });
-
- if (i == _currentIndex) break;
+ if (SlicerFile.Resolution != clip.Resolution)
+ {
+ SlicerFile.Resolution = clip.Resolution;
}
- }
- RaisePropertyChanged();
- RaisePropertyChanged(nameof(CurrentIndexCountStr));
- RaisePropertyChanged(nameof(CanUndo));
- RaisePropertyChanged(nameof(CanRedo));
- return;
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.Layers = Layer.CloneLayers(layers);
+ });
+
+ if (i == _currentIndex) break;
+ }
}
+
+ RaisePropertyChanged();
+ RaisePropertyChanged(nameof(CurrentIndexCountStr));
+ RaisePropertyChanged(nameof(CanUndo));
+ RaisePropertyChanged(nameof(CanRedo));
+ return;
}
+ }
- public string CurrentIndexCountStr => (CurrentIndex + 1).ToString().PadLeft(Count.ToString().Length, '0');
+ public string CurrentIndexCountStr => (CurrentIndex + 1).ToString().PadLeft(Count.ToString().Length, '0');
- public bool SuppressRestore
- {
- get => _suppressRestore;
- set => RaiseAndSetIfChanged(ref _suppressRestore, value);
- }
+ public bool SuppressRestore
+ {
+ get => _suppressRestore;
+ set => RaiseAndSetIfChanged(ref _suppressRestore, value);
+ }
- public bool ReallocatedLayerCount
- {
- get => _reallocatedLayerCount;
- set => RaiseAndSetIfChanged(ref _reallocatedLayerCount, value);
- }
+ public bool ReallocatedLayerCount
+ {
+ get => _reallocatedLayerCount;
+ set => RaiseAndSetIfChanged(ref _reallocatedLayerCount, value);
+ }
- public Layer[] SnapshotLayers
- {
- get => _snapshotLayers;
- private set => RaiseAndSetIfChanged(ref _snapshotLayers, value);
- }
+ public Layer[]? SnapshotLayers
+ {
+ get => _snapshotLayers;
+ private set => RaiseAndSetIfChanged(ref _snapshotLayers, value);
+ }
- public ClipboardItem CurrentClip => _currentIndex < 0 || _currentIndex >= Count ? null : this[_currentIndex];
+ public ClipboardItem? CurrentClip => _currentIndex < 0 || _currentIndex >= Count ? null : this[_currentIndex];
- public bool CanUndo => CurrentIndex < Count - 1;
- public bool CanRedo => CurrentIndex > 0;
+ public bool CanUndo => CurrentIndex < Count - 1;
+ public bool CanRedo => CurrentIndex > 0;
- #endregion
+ #endregion
- #region Singleton
- private static readonly Lazy<ClipboardManager> InstanceHolder =
- new(() => new ClipboardManager());
+ #region Singleton
+ private static readonly Lazy<ClipboardManager> InstanceHolder =
+ new(() => new ClipboardManager());
- public static ClipboardManager Instance => InstanceHolder.Value;
- #endregion
+ public static ClipboardManager Instance => InstanceHolder.Value;
+ #endregion
- #region Constructor
- private ClipboardManager() { }
- #endregion
+ #region Constructor
+ private ClipboardManager() { }
+ #endregion
- #region List Implementation
- public IEnumerator<ClipboardItem> GetEnumerator() => Items.GetEnumerator();
+ #region List Implementation
+ public IEnumerator<ClipboardItem> GetEnumerator() => Items.GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- public void Add(ClipboardItem item) => Items.Add(item);
+ public void Add(ClipboardItem item) => Items.Add(item);
- public void Clear()
- {
- Items.Clear();
- CurrentIndex = -1;
- }
+ public void Clear()
+ {
+ Items.Clear();
+ CurrentIndex = -1;
+ }
- public void Clear(bool keepOriginal)
+ public void Clear(bool keepOriginal)
+ {
+ if (!keepOriginal)
{
- if (!keepOriginal)
- {
- Clear();
- return;
- }
-
- if (Count == 0) return;
-
- Init(SlicerFile);
+ Clear();
+ return;
}
- public bool Contains(ClipboardItem item) => Items.Contains(item);
+ if (Count == 0) return;
- public void CopyTo(ClipboardItem[] array, int arrayIndex) => Items.CopyTo(array, arrayIndex);
+ Init(SlicerFile);
+ }
- public bool Remove(ClipboardItem item) => Items.Remove(item);
+ public bool Contains(ClipboardItem item) => Items.Contains(item);
- public int Count => Items.Count;
- public bool IsReadOnly => false;
- public int IndexOf(ClipboardItem item) => Items.IndexOf(item);
+ public void CopyTo(ClipboardItem[] array, int arrayIndex) => Items.CopyTo(array, arrayIndex);
- public void Insert(int index, ClipboardItem item) => Items.Insert(index, item);
+ public bool Remove(ClipboardItem item) => Items.Remove(item);
- public void RemoveAt(int index) => Items.RemoveAt(index);
+ public int Count => Items.Count;
+ public bool IsReadOnly => false;
+ public int IndexOf(ClipboardItem item) => Items.IndexOf(item);
- public ClipboardItem this[int index]
- {
- get => Items[index];
- set => Items[index] = value;
- }
- #endregion
+ public void Insert(int index, ClipboardItem item) => Items.Insert(index, item);
- #region Methods
+ public void RemoveAt(int index) => Items.RemoveAt(index);
- public void SuppressRestoreWork(Action action)
- {
- try
- {
- SuppressRestore = true;
- action.Invoke();
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- finally
- {
- SuppressRestore = false;
- }
- }
+ public ClipboardItem this[int index]
+ {
+ get => Items[index];
+ set => Items[index] = value;
+ }
+ #endregion
- /// <summary>
- /// Clears the manager
- /// </summary>
- public void Reset() => Init(null);
+ #region Methods
- /// <summary>
- /// Clears and init clipboard
- /// </summary>
- /// <param name="slicerFile"></param>
- public void Init(FileFormat slicerFile)
+ public void SuppressRestoreWork(Action action)
+ {
+ try
{
- Clear();
- SlicerFile = slicerFile;
- if (slicerFile is null || slicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return;
- SuppressRestoreWork(() =>
- {
- var clip = new ClipboardItem(SlicerFile, "Original layers", true);
- clip.AddRange(SlicerFile.LayerManager.CloneLayers());
- Add(clip);
- slicerFile.SuppressRebuildPropertiesWork(() => CurrentIndex = 0);
- });
+ SuppressRestore = true;
+ action.Invoke();
}
-
- /// <summary>
- /// Snapshot layers and prepare manager to collect modified layers with <see cref="Clip"/>
- /// </summary>
- public void Snapshot(Layer[] layers = null)
+ catch (Exception e)
{
- SnapshotLayers = layers ?? SlicerFile.LayerManager.CloneLayers();
+ Debug.WriteLine(e);
+ throw;
}
-
- public void RestoreSnapshot()
+ finally
{
- if (_snapshotLayers is null) return;
- SlicerFile.LayerManager.Layers = _snapshotLayers;
- SnapshotLayers = null;
+ SuppressRestore = false;
}
+ }
- /// <summary>
- /// Collect differences and create a clip
- /// </summary>
- public ClipboardItem Clip(string description = null, Layer[] layers = null, bool doFullBackup = false)
+ /// <summary>
+ /// Clears the manager
+ /// </summary>
+ public void Reset() => Init(null!);
+
+ /// <summary>
+ /// Clears and init clipboard
+ /// </summary>
+ /// <param name="slicerFile"></param>
+ public void Init(FileFormat slicerFile)
+ {
+ Clear();
+ SlicerFile = slicerFile;
+ if (slicerFile is null || slicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return;
+ SuppressRestoreWork(() =>
{
- ClipboardItem safeClip = null;
- if (!doFullBackup)
- {
- if (layers is not null) Snapshot(layers);
- if (_snapshotLayers is null) throw new InvalidOperationException("A snapshot is required before perform a clip");
+ var clip = new ClipboardItem(SlicerFile, "Original layers", true);
+ clip.AddRange(SlicerFile.CloneLayers());
+ Add(clip);
+ slicerFile.SuppressRebuildPropertiesWork(() => CurrentIndex = 0);
+ });
+ }
- if (_snapshotLayers.Length != SlicerFile.LayerCount)
- {
- doFullBackup = true; // Force full backup when layer count changes
- if (Count > 0 && !this[0].IsFullBackup)
- {
- safeClip = new ClipboardItem(SlicerFile, "Fail-safe full backup", true);
- safeClip.AddRange(_snapshotLayers);
- }
+ /// <summary>
+ /// Snapshot layers and prepare manager to collect modified layers with <see cref="Clip"/>
+ /// </summary>
+ public void Snapshot(Layer[]? layers = null)
+ {
+ SnapshotLayers = layers ?? SlicerFile.CloneLayers();
+ }
- //Insert(0, safeClip);
- }
- }
+ public void RestoreSnapshot()
+ {
+ if (_snapshotLayers is null) return;
+ SlicerFile.Layers = _snapshotLayers;
+ SnapshotLayers = null;
+ }
- var clip = new ClipboardItem(SlicerFile, description, doFullBackup);
+ /// <summary>
+ /// Collect differences and create a clip
+ /// </summary>
+ public ClipboardItem? Clip(string? description = null, Layer[]? layers = null, bool doFullBackup = false)
+ {
+ ClipboardItem? safeClip = null;
+ if (!doFullBackup)
+ {
+ if (layers is not null) Snapshot(layers);
+ if (_snapshotLayers is null) throw new InvalidOperationException("A snapshot is required before perform a clip");
- if (doFullBackup)
+ if (_snapshotLayers.Length != SlicerFile.LayerCount)
{
- clip.AddRange(SlicerFile.LayerManager.CloneLayers());
- }
- else
- {
- int layerIndex = 0;
- for (;
- layerIndex < SlicerFile.LayerCount
- && layerIndex < _snapshotLayers.Length;
- layerIndex++)
- {
- //if(SnapshotLayers.Count - 1 < layerIndex) break;
- if (_snapshotLayers[layerIndex].Equals(SlicerFile[layerIndex])) continue;
- clip.Add(SlicerFile[layerIndex].Clone());
- }
-
- // Collect leftovers from snapshot
- // This happens when current state has less layers then the snapshot/previous
- // So we need to preserve them
- for (; layerIndex < SlicerFile.LayerCount; layerIndex++)
+ doFullBackup = true; // Force full backup when layer count changes
+ if (Count > 0 && !this[0].IsFullBackup)
{
- clip.Add(SlicerFile[layerIndex].Clone());
+ safeClip = new ClipboardItem(SlicerFile, "Fail-safe full backup", true);
+ safeClip.AddRange(_snapshotLayers);
}
- if (clip.Count == SlicerFile.LayerCount)
- {
- clip.IsFullBackup = true;
- }
+ //Insert(0, safeClip);
}
+ }
- SnapshotLayers = null;
+ var clip = new ClipboardItem(SlicerFile, description, doFullBackup);
- if (clip.Count == 0)
+ if (doFullBackup)
+ {
+ clip.AddRange(SlicerFile.CloneLayers());
+ }
+ else
+ {
+ int layerIndex = 0;
+ for (;
+ layerIndex < SlicerFile.LayerCount
+ && layerIndex < _snapshotLayers!.Length;
+ layerIndex++)
{
- if(Count > 0 && this[0].LayerCount == clip.LayerCount)
- return null;
+ //if(SnapshotLayers.Count - 1 < layerIndex) break;
+ if (_snapshotLayers[layerIndex].Equals(SlicerFile[layerIndex])) continue;
+ clip.Add(SlicerFile[layerIndex].Clone());
}
- SuppressRestoreWork(() =>
+ // Collect leftovers from snapshot
+ // This happens when current state has less layers then the snapshot/previous
+ // So we need to preserve them
+ for (; layerIndex < SlicerFile.LayerCount; layerIndex++)
{
- var oldCurrentIndex = _currentIndex;
- CurrentIndex = -1;
-
- // Remove all redo's for integrity
- for (int i = oldCurrentIndex - 1; i >= 0; i--)
- {
- //if(this[i].IsFullBackup) continue; // Full backups can survive
- RemoveAt(i);
- }
-
- if (safeClip is not null)
- {
- Insert(0, safeClip);
- }
- Insert(0, clip);
+ clip.Add(SlicerFile[layerIndex].Clone());
+ }
- CurrentIndex = 0;
- });
-
- return clip;
+ if (clip.Count == SlicerFile.LayerCount)
+ {
+ clip.IsFullBackup = true;
+ }
}
- /// <summary>
- /// Collect differences and create a clip
- /// </summary>
- public ClipboardItem Clip(Operation operation, Layer[] layersSnapshot = null, bool doFullBackup = false)
- {
- string description = operation.ToString();
- if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}";
- var clip = Clip(description, layersSnapshot, doFullBackup);
- if (clip is null) return null;
- clip.Operation = operation;
- return clip;
- }
+ SnapshotLayers = null;
- public void Undo()
+ if (clip.Count == 0)
{
- if (!CanUndo) return;
- CurrentIndex++;
+ if(Count > 0 && this[0].LayerCount == clip.LayerCount)
+ return null;
}
- public void Redo()
+ SuppressRestoreWork(() =>
{
- if (!CanRedo) return;
- CurrentIndex--;
- }
+ var oldCurrentIndex = _currentIndex;
+ CurrentIndex = -1;
- public void SetToOldest() => CurrentIndex = Count-1;
- public void SetToNewest() => CurrentIndex = 0;
+ // Remove all redo's for integrity
+ for (int i = oldCurrentIndex - 1; i >= 0; i--)
+ {
+ //if(this[i].IsFullBackup) continue; // Full backups can survive
+ RemoveAt(i);
+ }
- #endregion
+ if (safeClip is not null)
+ {
+ Insert(0, safeClip);
+ }
+ Insert(0, clip);
+
+ CurrentIndex = 0;
+ });
+
+ return clip;
}
-}
+
+ /// <summary>
+ /// Collect differences and create a clip
+ /// </summary>
+ public ClipboardItem? Clip(Operation operation, Layer[]? layersSnapshot = null, bool doFullBackup = false)
+ {
+ string description = operation.ToString();
+ if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}";
+ var clip = Clip(description, layersSnapshot, doFullBackup);
+ if (clip is null) return null;
+ clip.Operation = operation;
+ return clip;
+ }
+
+ public void Undo()
+ {
+ if (!CanUndo) return;
+ CurrentIndex++;
+ }
+
+ public void Redo()
+ {
+ if (!CanRedo) return;
+ CurrentIndex--;
+ }
+
+ public void SetToOldest() => CurrentIndex = Count-1;
+ public void SetToNewest() => CurrentIndex = 0;
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs
index b3614a3..ff34f58 100644
--- a/UVtools.Core/Managers/IssueManager.cs
+++ b/UVtools.Core/Managers/IssueManager.cs
@@ -1,14 +1,13 @@
-using System;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Util;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
-using Emgu.CV.Util;
using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
@@ -16,907 +15,907 @@ using UVtools.Core.Layers;
using UVtools.Core.Operations;
using UVtools.Core.PixelEditor;
-namespace UVtools.Core.Managers
+namespace UVtools.Core.Managers;
+
+public sealed class IssueManager : RangeObservableCollection<MainIssue>
{
- public sealed class IssueManager : RangeObservableCollection<MainIssue>
+ public FileFormat SlicerFile { get; }
+
+ public List<MainIssue> IgnoredIssues { get; } = new();
+
+ public IssueManager(FileFormat slicerFile)
{
- public FileFormat SlicerFile { get; }
+ SlicerFile = slicerFile;
+ }
- public List<MainIssue> IgnoredIssues { get; } = new();
+ /// <summary>
+ /// Gets the visible <see cref="MainIssue"/> aka not ignored
+ /// </summary>
+ /// <returns></returns>
+ public MainIssue[] GetVisible()
+ {
+ return this.Where(mainIssue => !IgnoredIssues.Contains(mainIssue)).ToArray();
+ }
- public IssueManager(FileFormat slicerFile)
+ public static Issue[] GetIssues(IEnumerable<MainIssue> issues)
+ {
+ var result = new List<Issue>();
+ foreach (var mainIssue in issues)
{
- SlicerFile = slicerFile;
+ result.AddRange(mainIssue);
}
- /// <summary>
- /// Gets the visible <see cref="MainIssue"/> aka not ignored
- /// </summary>
- /// <returns></returns>
- public MainIssue[] GetVisible()
- {
- return this.Where(mainIssue => !IgnoredIssues.Contains(mainIssue)).ToArray();
- }
+ return result.ToArray();
+ }
- public static Issue[] GetIssues(IEnumerable<MainIssue> issues)
+ public static Issue[] GetIssuesBy(IEnumerable<MainIssue> issues, MainIssue.IssueType type, uint layerIndex)
+ {
+ var result = new List<Issue>();
+ foreach (var mainIssue in issues)
{
- var result = new List<Issue>();
- foreach (var mainIssue in issues)
+ if (mainIssue.Type != type) continue;
+ if (!mainIssue.IsIssueInBetween(layerIndex)) continue;
+ foreach (var issue in mainIssue)
{
- result.AddRange(mainIssue);
+ if (issue.LayerIndex != layerIndex) continue;
+ result.Add(issue);
}
-
- return result.ToArray();
}
- public static Issue[] GetIssuesBy(IEnumerable<MainIssue> issues, MainIssue.IssueType type, uint layerIndex)
- {
- var result = new List<Issue>();
- foreach (var mainIssue in issues)
- {
- if (mainIssue.Type != type) continue;
- if (!mainIssue.IsIssueInBetween(layerIndex)) continue;
- foreach (var issue in mainIssue)
- {
- if (issue.LayerIndex != layerIndex) continue;
- result.Add(issue);
- }
- }
+ return result.ToArray();
+ }
- return result.ToArray();
+ public static Issue[] GetIssuesBy(IEnumerable<MainIssue> issues, MainIssue.IssueType type)
+ {
+ var result = new List<Issue>();
+ foreach (var mainIssue in issues)
+ {
+ if (mainIssue.Type != type) continue;
+ result.AddRange(mainIssue);
}
- public static Issue[] GetIssuesBy(IEnumerable<MainIssue> issues, MainIssue.IssueType type)
+ return result.ToArray();
+ }
+
+
+ public static Issue[] GetIssuesBy(IEnumerable<MainIssue> issues, uint layerIndex)
+ {
+ var result = new List<Issue>();
+ foreach (var mainIssue in issues)
{
- var result = new List<Issue>();
- foreach (var mainIssue in issues)
+ if (!mainIssue.IsIssueInBetween(layerIndex)) continue;
+ foreach (var issue in mainIssue)
{
- if (mainIssue.Type != type) continue;
- result.AddRange(mainIssue);
+ if (issue.LayerIndex != layerIndex) continue;
+ result.Add(issue);
}
-
- return result.ToArray();
}
+ return result.ToArray();
+ }
- public static Issue[] GetIssuesBy(IEnumerable<MainIssue> issues, uint layerIndex)
- {
- var result = new List<Issue>();
- foreach (var mainIssue in issues)
- {
- if (!mainIssue.IsIssueInBetween(layerIndex)) continue;
- foreach (var issue in mainIssue)
- {
- if (issue.LayerIndex != layerIndex) continue;
- result.Add(issue);
- }
- }
+ public Issue[] GetIssues()
+ {
+ return GetIssues(this);
+ }
- return result.ToArray();
- }
+ public Issue[] GetIssuesBy(MainIssue.IssueType type)
+ {
+ return GetIssuesBy(this, type);
+ }
- public Issue[] GetIssues()
- {
- return GetIssues(this);
- }
+ public Issue[] GetIssuesBy(MainIssue.IssueType type, uint layerIndex)
+ {
+ return GetIssuesBy(this, type, layerIndex);
+ }
- public Issue[] GetIssuesBy(MainIssue.IssueType type)
+ public Issue[] GetIssuesBy(uint layerIndex)
+ {
+ return GetIssuesBy(this, layerIndex);
+ }
+
+ public List<MainIssue> DetectIssues(
+ IslandDetectionConfiguration? islandConfig = null,
+ OverhangDetectionConfiguration? overhangConfig = null,
+ ResinTrapDetectionConfiguration? resinTrapConfig = null,
+ TouchingBoundDetectionConfiguration? touchBoundConfig = null,
+ PrintHeightDetectionConfiguration? printHeightConfig = null,
+ bool emptyLayersConfig = true,
+ OperationProgress? progress = null)
+ {
+ if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return new List<MainIssue>();
+
+ islandConfig ??= new IslandDetectionConfiguration();
+ overhangConfig ??= new OverhangDetectionConfiguration();
+ resinTrapConfig ??= new ResinTrapDetectionConfiguration();
+ touchBoundConfig ??= new TouchingBoundDetectionConfiguration();
+ printHeightConfig ??= new PrintHeightDetectionConfiguration();
+ progress ??= new OperationProgress();
+
+ var result = new ConcurrentBag<MainIssue>();
+ //var layerHollowAreas = new ConcurrentDictionary<uint, List<LayerHollowArea>>();
+ var resinTraps = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
+ var suctionCups = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
+ var externalContours = new VectorOfVectorOfPoint[SlicerFile.LayerCount];
+ var hollows = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
+ var airContours = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
+ var resinTrapsContoursArea = new double[SlicerFile.LayerCount][];
+
+ bool IsIgnored(MainIssue issue) => IgnoredIssues.Count > 0 && IgnoredIssues.Contains(issue);
+
+ bool AddIssue(MainIssue issue)
{
- return GetIssuesBy(this, type);
+ if (IsIgnored(issue)) return false;
+ result.Add(issue);
+ return true;
}
- public Issue[] GetIssuesBy(MainIssue.IssueType type, uint layerIndex)
+ List<MainIssue> GetResult()
{
- return GetIssuesBy(this, type, layerIndex);
+ return result.OrderBy(mainIssue => mainIssue.Type).ThenBy(issue => issue.StartLayerIndex).ThenByDescending(issue => issue.Area).ToList();
}
- public Issue[] GetIssuesBy(uint layerIndex)
+ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorOfPoint? externals)
{
- return GetIssuesBy(this, layerIndex);
+ CvInvoke.BitwiseNot(input, output);
+ if (externals is null || externals.Size == 0) return;
+ CvInvoke.DrawContours(output, externals, -1, EmguExtensions.BlackColor, -1);
}
- public List<MainIssue> DetectIssues(
- IslandDetectionConfiguration islandConfig = null,
- OverhangDetectionConfiguration overhangConfig = null,
- ResinTrapDetectionConfiguration resinTrapConfig = null,
- TouchingBoundDetectionConfiguration touchBoundConfig = null,
- PrintHeightDetectionConfiguration printHeightConfig = null,
- bool emptyLayersConfig = true,
- OperationProgress progress = null)
+ if (printHeightConfig.Enabled && SlicerFile.MachineZ > 0)
{
- if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial) return null;
-
- islandConfig ??= new IslandDetectionConfiguration();
- overhangConfig ??= new OverhangDetectionConfiguration();
- resinTrapConfig ??= new ResinTrapDetectionConfiguration();
- touchBoundConfig ??= new TouchingBoundDetectionConfiguration();
- printHeightConfig ??= new PrintHeightDetectionConfiguration();
- progress ??= new OperationProgress();
-
- var result = new ConcurrentBag<MainIssue>();
- //var layerHollowAreas = new ConcurrentDictionary<uint, List<LayerHollowArea>>();
- var resinTraps = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
- var suctionCups = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
- var externalContours = new VectorOfVectorOfPoint[SlicerFile.LayerCount];
- var hollows = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
- var airContours = new List<VectorOfVectorOfPoint>[SlicerFile.LayerCount];
- var resinTrapsContoursArea = new double[SlicerFile.LayerCount][];
-
- bool IsIgnored(MainIssue issue) => IgnoredIssues.Count > 0 && IgnoredIssues.Contains(issue);
-
- bool AddIssue(MainIssue issue)
- {
- if (IsIgnored(issue)) return false;
- result.Add(issue);
- return true;
- }
-
- List<MainIssue> GetResult()
- {
- return result.OrderBy(mainIssue => mainIssue.Type).ThenBy(issue => issue.StartLayerIndex).ThenByDescending(issue => issue.Area).ToList();
- }
-
- void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorOfPoint externals)
+ float printHeightWithOffset = Layer.RoundHeight(SlicerFile.MachineZ + printHeightConfig.Offset);
+ if (SlicerFile.PrintHeight > printHeightWithOffset)
{
- CvInvoke.BitwiseNot(input, output);
- if (externals is null || externals.Size == 0) return;
- CvInvoke.DrawContours(output, externals, -1, EmguExtensions.BlackColor, -1);
+ var issues = (from layer in SlicerFile where layer.PositionZ > printHeightWithOffset select new Issue(layer)).ToList();
+ if(issues.Count > 0) AddIssue(new MainIssue(MainIssue.IssueType.PrintHeight, issues));
}
+ }
- if (printHeightConfig.Enabled && SlicerFile.MachineZ > 0)
+ if (emptyLayersConfig)
+ {
+ foreach (var layer in SlicerFile)
{
- float printHeightWithOffset = Layer.RoundHeight(SlicerFile.MachineZ + printHeightConfig.Offset);
- if (SlicerFile.PrintHeight > printHeightWithOffset)
+ if (layer.IsEmpty)
{
- var issues = (from layer in SlicerFile where layer.PositionZ > printHeightWithOffset select new Issue(layer)).ToList();
- if(issues.Count > 0) AddIssue(new MainIssue(MainIssue.IssueType.PrintHeight, issues));
+ AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer)));
}
}
+ }
- if (emptyLayersConfig)
- {
- foreach (var layer in SlicerFile)
- {
- if (layer.IsEmpty)
- {
- AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer)));
- }
- }
- }
+ if (islandConfig.Enabled || overhangConfig.Enabled || resinTrapConfig.Enabled || touchBoundConfig.Enabled)
+ {
+ progress.Reset(OperationProgress.StatusIslands, SlicerFile.LayerCount);
- if (islandConfig.Enabled || overhangConfig.Enabled || resinTrapConfig.Enabled || touchBoundConfig.Enabled)
+ // Detect contours
+ Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndexInt =>
{
- progress.Reset(OperationProgress.StatusIslands, SlicerFile.LayerCount);
+ if (progress.Token.IsCancellationRequested) return;
+ uint layerIndex = (uint)layerIndexInt;
+ var layer = SlicerFile[layerIndex];
- // Detect contours
- Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndexInt =>
+ if (layer.IsEmpty)
{
- if (progress.Token.IsCancellationRequested) return;
- uint layerIndex = (uint)layerIndexInt;
- var layer = SlicerFile[layerIndex];
+ progress.LockAndIncrement();
+ return;
+ }
- if (layer.IsEmpty)
- {
- progress.LockAndIncrement();
- return;
- }
+ // Spare a decoding cycle
+ if (!touchBoundConfig.Enabled &&
+ !resinTrapConfig.Enabled &&
+ (!overhangConfig.Enabled || overhangConfig.Enabled && (layerIndex == 0 || overhangConfig.WhiteListLayers is not null && !overhangConfig.WhiteListLayers.Contains(layerIndex))) &&
+ (!islandConfig.Enabled || islandConfig.Enabled && (layerIndex == 0 || islandConfig.WhiteListLayers is not null && !islandConfig.WhiteListLayers.Contains(layerIndex)))
+ )
+ {
+ progress.LockAndIncrement();
- // Spare a decoding cycle
- if (!touchBoundConfig.Enabled &&
- !resinTrapConfig.Enabled &&
- (!overhangConfig.Enabled || overhangConfig.Enabled && (layerIndex == 0 || overhangConfig.WhiteListLayers is not null && !overhangConfig.WhiteListLayers.Contains(layerIndex))) &&
- (!islandConfig.Enabled || islandConfig.Enabled && (layerIndex == 0 || islandConfig.WhiteListLayers is not null && !islandConfig.WhiteListLayers.Contains(layerIndex)))
- )
- {
- progress.LockAndIncrement();
+ return;
+ }
- return;
- }
+ using (var image = layer.LayerMat)
+ {
+ var step = image.GetRealStep();
+ var span = image.GetDataByteSpan();
- using (var image = layer.LayerMat)
+ if (touchBoundConfig.Enabled)
{
- var step = image.GetRealStep();
- var span = image.GetDataByteSpan();
-
- if (touchBoundConfig.Enabled)
+ // TouchingBounds Checker
+ List<Point> pixels = new();
+ bool touchTop = layer.BoundingRectangle.Top <= touchBoundConfig.MarginTop;
+ bool touchBottom = layer.BoundingRectangle.Bottom >= image.Height - touchBoundConfig.MarginBottom;
+ bool touchLeft = layer.BoundingRectangle.Left <= touchBoundConfig.MarginLeft;
+ bool touchRight = layer.BoundingRectangle.Right >=
+ image.Width - touchBoundConfig.MarginRight;
+
+ int minx = int.MaxValue;
+ int miny = int.MaxValue;
+ int maxx = 0;
+ int maxy = 0;
+
+ if (touchTop || touchBottom)
{
- // TouchingBounds Checker
- List<Point> pixels = new();
- bool touchTop = layer.BoundingRectangle.Top <= touchBoundConfig.MarginTop;
- bool touchBottom = layer.BoundingRectangle.Bottom >= image.Height - touchBoundConfig.MarginBottom;
- bool touchLeft = layer.BoundingRectangle.Left <= touchBoundConfig.MarginLeft;
- bool touchRight = layer.BoundingRectangle.Right >=
- image.Width - touchBoundConfig.MarginRight;
-
- int minx = int.MaxValue;
- int miny = int.MaxValue;
- int maxx = 0;
- int maxy = 0;
-
- if (touchTop || touchBottom)
+ for (int x = 0; x < image.Width; x++) // Check Top and Bottom bounds
{
- for (int x = 0; x < image.Width; x++) // Check Top and Bottom bounds
+ if (touchTop)
{
- if (touchTop)
+ for (int y = 0; y < touchBoundConfig.MarginTop; y++) // Top
{
- for (int y = 0; y < touchBoundConfig.MarginTop; y++) // Top
+ if (span[image.GetPixelPos(x, y)] >=
+ touchBoundConfig.MinimumPixelBrightness)
{
- if (span[image.GetPixelPos(x, y)] >=
- touchBoundConfig.MinimumPixelBrightness)
- {
- pixels.Add(new Point(x, y));
- minx = Math.Min(minx, x);
- miny = Math.Min(miny, y);
- maxx = Math.Max(maxx, x);
- maxy = Math.Max(maxy, y);
- }
+ pixels.Add(new Point(x, y));
+ minx = Math.Min(minx, x);
+ miny = Math.Min(miny, y);
+ maxx = Math.Max(maxx, x);
+ maxy = Math.Max(maxy, y);
}
}
+ }
- if (touchBottom)
+ if (touchBottom)
+ {
+ for (int y = image.Height - touchBoundConfig.MarginBottom;
+ y < image.Height;
+ y++) // Bottom
{
- for (int y = image.Height - touchBoundConfig.MarginBottom;
- y < image.Height;
- y++) // Bottom
+ if (span[image.GetPixelPos(x, y)] >=
+ touchBoundConfig.MinimumPixelBrightness)
{
- if (span[image.GetPixelPos(x, y)] >=
- touchBoundConfig.MinimumPixelBrightness)
- {
- pixels.Add(new Point(x, y));
- minx = Math.Min(minx, x);
- miny = Math.Min(miny, y);
- maxx = Math.Max(maxx, x);
- maxy = Math.Max(maxy, y);
- }
+ pixels.Add(new Point(x, y));
+ minx = Math.Min(minx, x);
+ miny = Math.Min(miny, y);
+ maxx = Math.Max(maxx, x);
+ maxy = Math.Max(maxy, y);
}
}
-
}
+
}
+ }
- if (touchLeft || touchRight)
+ if (touchLeft || touchRight)
+ {
+ for (int y = touchBoundConfig.MarginTop;
+ y < image.Height - touchBoundConfig.MarginBottom;
+ y++) // Check Left and Right bounds
{
- for (int y = touchBoundConfig.MarginTop;
- y < image.Height - touchBoundConfig.MarginBottom;
- y++) // Check Left and Right bounds
+ if (touchLeft)
{
- if (touchLeft)
+ for (int x = 0; x < touchBoundConfig.MarginLeft; x++) // Left
{
- for (int x = 0; x < touchBoundConfig.MarginLeft; x++) // Left
+ if (span[image.GetPixelPos(x, y)] >=
+ touchBoundConfig.MinimumPixelBrightness)
{
- if (span[image.GetPixelPos(x, y)] >=
- touchBoundConfig.MinimumPixelBrightness)
- {
- pixels.Add(new Point(x, y));
- minx = Math.Min(minx, x);
- miny = Math.Min(miny, y);
- maxx = Math.Max(maxx, x);
- maxy = Math.Max(maxy, y);
- }
+ pixels.Add(new Point(x, y));
+ minx = Math.Min(minx, x);
+ miny = Math.Min(miny, y);
+ maxx = Math.Max(maxx, x);
+ maxy = Math.Max(maxy, y);
}
}
+ }
- if (touchRight)
+ if (touchRight)
+ {
+ for (int x = image.Width - touchBoundConfig.MarginRight;
+ x < image.Width;
+ x++) // Right
{
- for (int x = image.Width - touchBoundConfig.MarginRight;
- x < image.Width;
- x++) // Right
+ if (span[image.GetPixelPos(x, y)] >=
+ touchBoundConfig.MinimumPixelBrightness)
{
- if (span[image.GetPixelPos(x, y)] >=
- touchBoundConfig.MinimumPixelBrightness)
- {
- pixels.Add(new Point(x, y));
- minx = Math.Min(minx, x);
- miny = Math.Min(miny, y);
- maxx = Math.Max(maxx, x);
- maxy = Math.Max(maxy, y);
- }
+ pixels.Add(new Point(x, y));
+ minx = Math.Min(minx, x);
+ miny = Math.Min(miny, y);
+ maxx = Math.Max(maxx, x);
+ maxy = Math.Max(maxy, y);
}
}
}
}
+ }
- if (pixels.Count > 0)
- {
- AddIssue(new MainIssue(MainIssue.IssueType.TouchingBound, new IssueOfPoints(layer, pixels,
- new Rectangle(minx, miny, maxx - minx + 1, maxy - miny + 1))));
- }
+ if (pixels.Count > 0)
+ {
+ AddIssue(new MainIssue(MainIssue.IssueType.TouchingBound, new IssueOfPoints(layer, pixels,
+ new Rectangle(minx, miny, maxx - minx + 1, maxy - miny + 1))));
}
+ }
+
+ if (layerIndex > 0) // No islands nor overhangs for layer 0
+ {
+ Mat? previousImage = null;
+ Span<byte> previousSpan = null;
- if (layerIndex > 0) // No islands nor overhangs for layer 0
+ if (islandConfig.Enabled)
{
- Mat previousImage = null;
- Span<byte> previousSpan = null;
+ bool canProcessCheck = true;
+ if (islandConfig.WhiteListLayers is not null) // Check white list
+ {
+ if (!islandConfig.WhiteListLayers.Contains(layerIndex))
+ {
+ canProcessCheck = false;
+ }
+ }
- if (islandConfig.Enabled)
+ if (canProcessCheck)
{
- bool canProcessCheck = true;
- if (islandConfig.WhiteListLayers is not null) // Check white list
+ bool needDispose = false;
+ Mat islandImage;
+ if (islandConfig.BinaryThreshold > 0)
{
- if (!islandConfig.WhiteListLayers.Contains(layerIndex))
- {
- canProcessCheck = false;
- }
+ needDispose = true;
+ islandImage = new();
+ CvInvoke.Threshold(image, islandImage, islandConfig.BinaryThreshold, byte.MaxValue,
+ ThresholdType.Binary);
+ }
+ else
+ {
+ islandImage = image;
+ }
+
+ using Mat labels = new();
+ using Mat stats = new();
+ using Mat centroids = new();
+ var numLabels = CvInvoke.ConnectedComponentsWithStats(islandImage, labels, stats,
+ centroids,
+ islandConfig.AllowDiagonalBonds
+ ? LineType.EightConnected
+ : LineType.FourConnected);
+
+ if (needDispose)
+ {
+ islandImage.Dispose();
}
- if (canProcessCheck)
+ // Get array that contains details of each connected component
+ var ccStats = stats.GetData();
+ //stats[i][0]: Left Edge of Connected Component
+ //stats[i][1]: Top Edge of Connected Component
+ //stats[i][2]: Width of Connected Component
+ //stats[i][3]: Height of Connected Component
+ //stats[i][4]: Total Area (in pixels) in Connected Component
+
+ var labelSpan = labels.GetDataSpan<int>();
+
+ for (int i = 1; i < numLabels; i++)
{
- bool needDispose = false;
- Mat islandImage;
- if (islandConfig.BinaryThreshold > 0)
- {
- needDispose = true;
- islandImage = new();
- CvInvoke.Threshold(image, islandImage, islandConfig.BinaryThreshold, byte.MaxValue,
- ThresholdType.Binary);
- }
- else
- {
- islandImage = image;
- }
+ Rectangle rect = new(
+ (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Left)!,
+ (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Top)!,
+ (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Width)!,
+ (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Height)!);
- using Mat labels = new();
- using Mat stats = new();
- using Mat centroids = new();
- var numLabels = CvInvoke.ConnectedComponentsWithStats(islandImage, labels, stats,
- centroids,
- islandConfig.AllowDiagonalBonds
- ? LineType.EightConnected
- : LineType.FourConnected);
+ if (rect.Area() < islandConfig.RequiredAreaToProcessCheck)
+ continue;
- if (needDispose)
+ if (previousImage is null)
{
- islandImage.Dispose();
+ previousImage = SlicerFile[layerIndex - 1].LayerMat;
+ previousSpan = previousImage.GetDataByteSpan();
}
- // Get array that contains details of each connected component
- var ccStats = stats.GetData();
- //stats[i][0]: Left Edge of Connected Component
- //stats[i][1]: Top Edge of Connected Component
- //stats[i][2]: Width of Connected Component
- //stats[i][3]: Height of Connected Component
- //stats[i][4]: Total Area (in pixels) in Connected Component
+ List<Point> points = new();
+ uint pixelsSupportingIsland = 0;
- var labelSpan = labels.GetDataSpan<int>();
-
- for (int i = 1; i < numLabels; i++)
+ for (int y = rect.Y; y < rect.Bottom; y++)
+ for (int x = rect.X; x < rect.Right; x++)
{
- Rectangle rect = new(
- (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Left),
- (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Top),
- (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Width),
- (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Height));
+ int pixel = step * y + x;
+ if (
+ labelSpan[pixel] !=
+ i || // Background pixel or a pixel from another component within the bounding rectangle
+ span[pixel] < islandConfig.RequiredPixelBrightnessToProcessCheck // Low brightness, ignore
+ ) continue;
- if (rect.Area() < islandConfig.RequiredAreaToProcessCheck)
- continue;
+ points.Add(new Point(x, y));
- if (previousImage is null)
+ if (previousSpan[pixel] >= islandConfig.RequiredPixelBrightnessToSupport)
{
- previousImage = SlicerFile[layerIndex - 1].LayerMat;
- previousSpan = previousImage.GetDataByteSpan();
+ pixelsSupportingIsland++;
}
+ }
- List<Point> points = new();
- uint pixelsSupportingIsland = 0;
+ if (points.Count == 0) continue; // Should never happen
- for (int y = rect.Y; y < rect.Bottom; y++)
- for (int x = rect.X; x < rect.Right; x++)
- {
- int pixel = step * y + x;
- if (
- labelSpan[pixel] !=
- i || // Background pixel or a pixel from another component within the bounding rectangle
- span[pixel] < islandConfig.RequiredPixelBrightnessToProcessCheck // Low brightness, ignore
- ) continue;
+ var requiredSupportingPixels = Math.Max(1, points.Count * islandConfig.RequiredPixelsToSupportMultiplier);
- points.Add(new Point(x, y));
+ /*if (pixelsSupportingIsland >= islandConfig.RequiredPixelsToSupport)
+ isIsland = false; // Not a island, bounding is strong, i think...
+ else if (pixelsSupportingIsland > 0 &&
+ points.Count < islandConfig.RequiredPixelsToSupport &&
+ pixelsSupportingIsland >= Math.Max(1, points.Count / 2))
+ isIsland = false; // Not a island, but maybe weak bounding...*/
- if (previousSpan[pixel] >= islandConfig.RequiredPixelBrightnessToSupport)
- {
- pixelsSupportingIsland++;
- }
- }
+ IssueOfPoints? island = null;
+ if (pixelsSupportingIsland < requiredSupportingPixels)
+ {
+ island = new IssueOfPoints(layer, points.ToArray(), rect);
+ }
- if (points.Count == 0) continue; // Should never happen
+ // Check for overhangs
+ if (overhangConfig.Enabled && !overhangConfig.IndependentFromIslands &&
+ island is null
+ || island is not null && islandConfig.EnhancedDetection &&
+ pixelsSupportingIsland >= 10
+ )
+ {
+ points.Clear();
+ using var imageRoi = image.Roi(rect);
+ using var previousImageRoi = previousImage.Roi(rect);
+ using var subtractedImage = new Mat();
+ var anchor = new Point(-1, -1);
+ CvInvoke.Subtract(imageRoi, previousImageRoi, subtractedImage);
+ CvInvoke.Threshold(subtractedImage, subtractedImage, 127, 255,
+ ThresholdType.Binary);
- var requiredSupportingPixels = Math.Max(1, points.Count * islandConfig.RequiredPixelsToSupportMultiplier);
+ CvInvoke.Erode(subtractedImage, subtractedImage, EmguExtensions.Kernel3x3Rectangle,
+ anchor, overhangConfig.ErodeIterations, BorderType.Default, default);
- /*if (pixelsSupportingIsland >= islandConfig.RequiredPixelsToSupport)
- isIsland = false; // Not a island, bounding is strong, i think...
- else if (pixelsSupportingIsland > 0 &&
- points.Count < islandConfig.RequiredPixelsToSupport &&
- pixelsSupportingIsland >= Math.Max(1, points.Count / 2))
- isIsland = false; // Not a island, but maybe weak bounding...*/
+ var subtractedSpan = subtractedImage.GetDataByteSpan();
+ var subtractedStep = subtractedImage.GetRealStep();
- IssueOfPoints island = null;
- if (pixelsSupportingIsland < requiredSupportingPixels)
+ for (int y = 0; y < subtractedImage.Height; y++)
+ for (int x = 0; x < subtractedStep; x++)
{
- island = new IssueOfPoints(layer, points.ToArray(), rect);
+ int labelX = rect.X + x;
+ int labelY = rect.Y + y;
+ int pixel = subtractedImage.GetPixelPos(x, y);
+ int pixelLabel = labelY * step + labelX;
+ if (labelSpan[pixelLabel] != i || subtractedSpan[pixel] == 0)
+ continue;
+
+ points.Add(new Point(labelX, labelY));
}
- // Check for overhangs
- if (overhangConfig.Enabled && !overhangConfig.IndependentFromIslands &&
- island is null
- || island is not null && islandConfig.EnhancedDetection &&
- pixelsSupportingIsland >= 10
- )
+ if (points.Count >= overhangConfig.RequiredPixelsToConsider
+ ) // Overhang
{
- points.Clear();
- using var imageRoi = image.Roi(rect);
- using var previousImageRoi = previousImage.Roi(rect);
- using var subtractedImage = new Mat();
- var anchor = new Point(-1, -1);
- CvInvoke.Subtract(imageRoi, previousImageRoi, subtractedImage);
- CvInvoke.Threshold(subtractedImage, subtractedImage, 127, 255,
- ThresholdType.Binary);
-
- CvInvoke.Erode(subtractedImage, subtractedImage, EmguExtensions.Kernel3x3Rectangle,
- anchor, overhangConfig.ErodeIterations, BorderType.Default, default);
-
- var subtractedSpan = subtractedImage.GetDataByteSpan();
- var subtractedStep = subtractedImage.GetRealStep();
-
- for (int y = 0; y < subtractedImage.Height; y++)
- for (int x = 0; x < subtractedStep; x++)
- {
- int labelX = rect.X + x;
- int labelY = rect.Y + y;
- int pixel = subtractedImage.GetPixelPos(x, y);
- int pixelLabel = labelY * step + labelX;
- if (labelSpan[pixelLabel] != i || subtractedSpan[pixel] == 0)
- continue;
-
- points.Add(new Point(labelX, labelY));
- }
-
- if (points.Count >= overhangConfig.RequiredPixelsToConsider
- ) // Overhang
- {
- AddIssue(new MainIssue(MainIssue.IssueType.Overhang, new IssueOfPoints(layer, points, rect)));
- }
- else if (islandConfig.EnhancedDetection) // No overhang
- {
- island = null;
- }
+ AddIssue(new MainIssue(MainIssue.IssueType.Overhang, new IssueOfPoints(layer, points, rect)));
+ }
+ else if (islandConfig.EnhancedDetection) // No overhang
+ {
+ island = null;
}
-
- if (island is not null)
- AddIssue(new MainIssue(MainIssue.IssueType.Island, island));
}
+
+ if (island is not null)
+ AddIssue(new MainIssue(MainIssue.IssueType.Island, island));
}
}
+ }
- // Overhangs
- if (!islandConfig.Enabled && overhangConfig.Enabled ||
- (islandConfig.Enabled && overhangConfig.Enabled &&
- overhangConfig.IndependentFromIslands))
+ // Overhangs
+ if (!islandConfig.Enabled && overhangConfig.Enabled ||
+ (islandConfig.Enabled && overhangConfig.Enabled &&
+ overhangConfig.IndependentFromIslands))
+ {
+ bool canProcessCheck = true;
+ if (overhangConfig.WhiteListLayers is not null) // Check white list
{
- bool canProcessCheck = true;
- if (overhangConfig.WhiteListLayers is not null) // Check white list
+ if (!overhangConfig.WhiteListLayers.Contains(layerIndex))
{
- if (!overhangConfig.WhiteListLayers.Contains(layerIndex))
- {
- canProcessCheck = false;
- }
+ canProcessCheck = false;
}
+ }
- if (canProcessCheck)
- {
- previousImage ??= SlicerFile[layerIndex - 1].LayerMat;
+ if (canProcessCheck)
+ {
+ previousImage ??= SlicerFile[layerIndex - 1].LayerMat;
- using var subtractedImage = new Mat();
- using var vecPoints = new VectorOfPoint();
- var anchor = new Point(-1, -1);
+ using var subtractedImage = new Mat();
+ using var vecPoints = new VectorOfPoint();
+ var anchor = new Point(-1, -1);
- CvInvoke.Subtract(image, previousImage, subtractedImage);
- CvInvoke.Threshold(subtractedImage, subtractedImage, 127, 255,
- ThresholdType.Binary);
+ CvInvoke.Subtract(image, previousImage, subtractedImage);
+ CvInvoke.Threshold(subtractedImage, subtractedImage, 127, 255,
+ ThresholdType.Binary);
- CvInvoke.Erode(subtractedImage, subtractedImage, EmguExtensions.Kernel3x3Rectangle,
- anchor, overhangConfig.ErodeIterations, BorderType.Default, default);
+ CvInvoke.Erode(subtractedImage, subtractedImage, EmguExtensions.Kernel3x3Rectangle,
+ anchor, overhangConfig.ErodeIterations, BorderType.Default, default);
- CvInvoke.FindNonZero(subtractedImage, vecPoints);
- if (vecPoints.Size >= overhangConfig.RequiredPixelsToConsider)
- {
- AddIssue(new MainIssue(MainIssue.IssueType.Overhang, new IssueOfPoints(layer, vecPoints.ToArray(), layer.BoundingRectangle)
- ));
- }
+ CvInvoke.FindNonZero(subtractedImage, vecPoints);
+ if (vecPoints.Size >= overhangConfig.RequiredPixelsToConsider)
+ {
+ AddIssue(new MainIssue(MainIssue.IssueType.Overhang, new IssueOfPoints(layer, vecPoints.ToArray(), layer.BoundingRectangle)
+ ));
}
}
-
- previousImage?.Dispose();
}
- if (resinTrapConfig.Enabled)
- {
- /* this used to calculate all contours for the layers, however new algorithm crops the layers to the overall bounding box
- * so the contours produced here are not translated properly. We will generate contours during the algorithm itself later */
+ previousImage?.Dispose();
+ }
- bool needDispose = false;
- Mat resinTrapImage;
- if (resinTrapConfig.BinaryThreshold > 0)
- {
- resinTrapImage = new Mat();
- CvInvoke.Threshold(image, resinTrapImage, resinTrapConfig.BinaryThreshold, byte.MaxValue, ThresholdType.Binary);
- }
- else
- {
- needDispose = true;
- resinTrapImage = image;
- }
- var contourLayer = resinTrapImage.Roi(SlicerFile.BoundingRectangle);
+ if (resinTrapConfig.Enabled)
+ {
+ /* this used to calculate all contours for the layers, however new algorithm crops the layers to the overall bounding box
+ * so the contours produced here are not translated properly. We will generate contours during the algorithm itself later */
- using var contours = contourLayer.FindContours(out var heirarchy, RetrType.Tree);
- externalContours[layerIndex] = EmguContours.GetExternalContours(contours, heirarchy);
- hollows[layerIndex] = EmguContours.GetNegativeContoursInGroups(contours, heirarchy);
- resinTrapsContoursArea[layerIndex] = EmguContours.GetContoursArea(hollows[layerIndex]);
+ bool needDispose = false;
+ Mat resinTrapImage;
+ if (resinTrapConfig.BinaryThreshold > 0)
+ {
+ resinTrapImage = new Mat();
+ CvInvoke.Threshold(image, resinTrapImage, resinTrapConfig.BinaryThreshold, byte.MaxValue, ThresholdType.Binary);
+ }
+ else
+ {
+ needDispose = true;
+ resinTrapImage = image;
+ }
+ var contourLayer = resinTrapImage.Roi(SlicerFile.BoundingRectangle);
- if (needDispose)
- {
- resinTrapImage.Dispose();
- }
+ using var contours = contourLayer.FindContours(out var heirarchy, RetrType.Tree);
+ externalContours[layerIndex] = EmguContours.GetExternalContours(contours, heirarchy);
+ hollows[layerIndex] = EmguContours.GetNegativeContoursInGroups(contours, heirarchy);
+ resinTrapsContoursArea[layerIndex] = EmguContours.GetContoursArea(hollows[layerIndex]);
- /*//
- //hierarchy[i][0]: the index of the next contour of the same level
- //hierarchy[i][1]: the index of the previous contour of the same level
- //hierarchy[i][2]: the index of the first child
- //hierarchy[i][3]: the index of the parent
- //
- var listHollowArea = new List<LayerHollowArea>();
- var hollowGroups = EmguContours.GetNegativeContoursInGroups(contours, hierarchy);
- var areas = EmguContours.GetContoursArea(hollowGroups);
-
- for (var i = 0; i < hollowGroups.Count; i++)
- {
- if (areas[i] < resinTrapConfig.RequiredAreaToProcessCheck) continue;
- var rect = CvInvoke.BoundingRectangle(hollowGroups[i][0]);
- listHollowArea.Add(new LayerHollowArea(hollowGroups[i].ToArrayOfArray(),
- rect,
- areas[i],
- layer.Index <= resinTrapConfig.StartLayerIndex ||
- layer.Index == LayerCount - 1 // First and Last layers, always drains
- ? LayerHollowArea.AreaType.Drain
- : LayerHollowArea.AreaType.Unknown));
- }
+ if (needDispose)
+ {
+ resinTrapImage.Dispose();
+ }
- if (listHollowArea.Count > 0) layerHollowAreas.TryAdd(layer.Index, listHollowArea);*/
+ /*//
+ //hierarchy[i][0]: the index of the next contour of the same level
+ //hierarchy[i][1]: the index of the previous contour of the same level
+ //hierarchy[i][2]: the index of the first child
+ //hierarchy[i][3]: the index of the parent
+ //
+ var listHollowArea = new List<LayerHollowArea>();
+ var hollowGroups = EmguContours.GetNegativeContoursInGroups(contours, hierarchy);
+ var areas = EmguContours.GetContoursArea(hollowGroups);
+
+ for (var i = 0; i < hollowGroups.Count; i++)
+ {
+ if (areas[i] < resinTrapConfig.RequiredAreaToProcessCheck) continue;
+ var rect = CvInvoke.BoundingRectangle(hollowGroups[i][0]);
+ listHollowArea.Add(new LayerHollowArea(hollowGroups[i].ToArrayOfArray(),
+ rect,
+ areas[i],
+ layer.Index <= resinTrapConfig.StartLayerIndex ||
+ layer.Index == LayerCount - 1 // First and Last layers, always drains
+ ? LayerHollowArea.AreaType.Drain
+ : LayerHollowArea.AreaType.Unknown));
}
+
+ if (listHollowArea.Count > 0) layerHollowAreas.TryAdd(layer.Index, listHollowArea);*/
}
+ }
- progress.LockAndIncrement();
- }); // Parallel end
- }
+ progress.LockAndIncrement();
+ }); // Parallel end
+ }
- if (resinTrapConfig.Enabled)
- {
- //progress.Reset("Detecting Air Boundaries (Resin traps)", LayerCount);
- //if (progress.Token.IsCancellationRequested) return result.OrderBy(issue => issue.Type).ThenBy(issue => issue.LayerIndex).ThenBy(issue => issue.Area).ToList();
- progress.Reset("Detection pass 1 of 2 (Resin traps)", SlicerFile.LayerCount, resinTrapConfig.StartLayerIndex);
+ if (resinTrapConfig.Enabled)
+ {
+ //progress.Reset("Detecting Air Boundaries (Resin traps)", LayerCount);
+ //if (progress.Token.IsCancellationRequested) return result.OrderBy(issue => issue.Type).ThenBy(issue => issue.LayerIndex).ThenBy(issue => issue.Area).ToList();
+ progress.Reset("Detection pass 1 of 2 (Resin traps)", SlicerFile.LayerCount, resinTrapConfig.StartLayerIndex);
- using var matCache = new MatCacheManager(SlicerFile, 0, 2)
+ using var matCache = new MatCacheManager(SlicerFile, 0, 2)
+ {
+ AfterCacheAction = mats =>
{
- AfterCacheAction = mats =>
+ mats[1] = mats[0].Roi(SlicerFile.BoundingRectangle);
+ if (resinTrapConfig.MaximumPixelBrightnessToDrain > 0)
{
- mats[1] = mats[0].Roi(SlicerFile.BoundingRectangle);
- if (resinTrapConfig.MaximumPixelBrightnessToDrain > 0)
- {
- CvInvoke.Threshold(mats[1], mats[1], resinTrapConfig.MaximumPixelBrightnessToDrain, byte.MaxValue, ThresholdType.Binary);
- }
+ CvInvoke.Threshold(mats[1], mats[1], resinTrapConfig.MaximumPixelBrightnessToDrain, byte.MaxValue, ThresholdType.Binary);
}
- };
+ }
+ };
- /* define all mats up front, reducing allocations */
- var currentAirMap = EmguExtensions.InitMat(SlicerFile.BoundingRectangle.Size);
- var layerAirMap = currentAirMap.NewBlank();
- /* the first pass does bottom to top, and tracks anything it thinks is a resin trap */
- for (var layerIndex = resinTrapConfig.StartLayerIndex; layerIndex < SlicerFile.LayerCount; layerIndex++)
- {
- if (progress.Token.IsCancellationRequested) return GetResult();
+ /* define all mats up front, reducing allocations */
+ var currentAirMap = EmguExtensions.InitMat(SlicerFile.BoundingRectangle.Size);
+ var layerAirMap = currentAirMap.NewBlank();
+ /* the first pass does bottom to top, and tracks anything it thinks is a resin trap */
+ for (var layerIndex = resinTrapConfig.StartLayerIndex; layerIndex < SlicerFile.LayerCount; layerIndex++)
+ {
+ if (progress.Token.IsCancellationRequested) return GetResult();
- var curLayer = matCache.Get(layerIndex, 1);
- //CacheLayers(layerIndex, true);
- //var curLayer = matTargetCache[layerIndex];
+ var curLayer = matCache.Get(layerIndex, 1);
+ //CacheLayers(layerIndex, true);
+ //var curLayer = matTargetCache[layerIndex];
- //curLayer.Save($"D:\\dump\\{layerIndex}_a.png");
+ //curLayer.Save($"D:\\dump\\{layerIndex}_a.png");
- /* find hollows of current layer */
- GenerateAirMap(curLayer, layerAirMap, externalContours[layerIndex]);
+ /* find hollows of current layer */
+ GenerateAirMap(curLayer, layerAirMap, externalContours[layerIndex]);
- //layerAirMap.Save($"D:\\dump\\{layerIndex}_b.png");
+ //layerAirMap.Save($"D:\\dump\\{layerIndex}_b.png");
- if (layerIndex == resinTrapConfig.StartLayerIndex)
- {
- currentAirMap = layerAirMap.Clone();
- }
+ if (layerIndex == resinTrapConfig.StartLayerIndex)
+ {
+ currentAirMap = layerAirMap.Clone();
+ }
- //currentAirMap.Save($"D:\\dump\\{layerIndex}_c.png");
+ //currentAirMap.Save($"D:\\dump\\{layerIndex}_c.png");
- /* remove solid areas of current layer from the air map */
- CvInvoke.Subtract(currentAirMap, curLayer, currentAirMap);
+ /* remove solid areas of current layer from the air map */
+ CvInvoke.Subtract(currentAirMap, curLayer, currentAirMap);
- //currentAirMap.Save($"D:\\dump\\{layerIndex}_d.png");
+ //currentAirMap.Save($"D:\\dump\\{layerIndex}_d.png");
- /* add in areas of air in current layer to air map */
- CvInvoke.BitwiseOr(layerAirMap, currentAirMap, currentAirMap);
+ /* add in areas of air in current layer to air map */
+ CvInvoke.BitwiseOr(layerAirMap, currentAirMap, currentAirMap);
- //currentAirMap.Save($"D:\\dump\\{layerIndex}_e.png");
+ //currentAirMap.Save($"D:\\dump\\{layerIndex}_e.png");
- if (hollows[layerIndex] is not null)
+ if (hollows[layerIndex] is not null)
+ {
+ resinTraps[layerIndex] = new();
+ airContours[layerIndex] = new();
+ Parallel.For(0, hollows[layerIndex].Count, CoreSettings.ParallelOptions, i =>
{
- resinTraps[layerIndex] = new();
- airContours[layerIndex] = new();
- Parallel.For(0, hollows[layerIndex].Count, CoreSettings.ParallelOptions, i =>
+ //for (var i = 0; i < hollows[layerIndex].Count; i++)
+ //{
+ if (progress.Token.IsCancellationRequested) return;
+ if (resinTrapsContoursArea[layerIndex][i] < resinTrapConfig.RequiredAreaToProcessCheck) return;
+
+ /* intersect current contour, with the current airmap. */
+ using var currentContour = curLayer.NewBlank();
+ using var airOverlap = curLayer.NewBlank();
+ CvInvoke.DrawContours(currentContour, hollows[layerIndex][i], -1, EmguExtensions.WhiteColor, -1);
+ CvInvoke.BitwiseAnd(currentAirMap, currentContour, airOverlap);
+ var overlapCount = CvInvoke.CountNonZero(airOverlap);
+
+ lock (SlicerFile[layerIndex].Mutex)
{
- //for (var i = 0; i < hollows[layerIndex].Count; i++)
- //{
- if (progress.Token.IsCancellationRequested) return;
- if (resinTrapsContoursArea[layerIndex][i] < resinTrapConfig.RequiredAreaToProcessCheck) return;
-
- /* intersect current contour, with the current airmap. */
- using var currentContour = curLayer.NewBlank();
- using var airOverlap = curLayer.NewBlank();
- CvInvoke.DrawContours(currentContour, hollows[layerIndex][i], -1, EmguExtensions.WhiteColor, -1);
- CvInvoke.BitwiseAnd(currentAirMap, currentContour, airOverlap);
- var overlapCount = CvInvoke.CountNonZero(airOverlap);
+ if (overlapCount == 0)
+ {
+ /* this countour does *not* overlap known air */
- lock (SlicerFile[layerIndex].Mutex)
+ /* add a resin trap (for now... will be revisited in part 2) */
+ resinTraps[layerIndex].Add(hollows[layerIndex][i]);
+ }
+ else
{
- if (overlapCount == 0)
+ if (overlapCount >= resinTrapConfig.RequiredBlackPixelsToDrain)
{
- /* this countour does *not* overlap known air */
+ /* this contour does overlap air, add it to the current air map and remember this contour was air-connected for 2nd pass */
+ airContours[layerIndex].Add(hollows[layerIndex][i]);
- /* add a resin trap (for now... will be revisited in part 2) */
- resinTraps[layerIndex].Add(hollows[layerIndex][i]);
+ CvInvoke.BitwiseOr(currentContour, currentAirMap, currentAirMap);
}
else
{
- if (overlapCount >= resinTrapConfig.RequiredBlackPixelsToDrain)
- {
- /* this contour does overlap air, add it to the current air map and remember this contour was air-connected for 2nd pass */
- airContours[layerIndex].Add(hollows[layerIndex][i]);
-
- CvInvoke.BitwiseOr(currentContour, currentAirMap, currentAirMap);
- }
- else
- {
- /* it overlapped ,but not by enough, treat as solid */
- CvInvoke.Subtract(currentAirMap, currentContour, currentAirMap);
- }
+ /* it overlapped ,but not by enough, treat as solid */
+ CvInvoke.Subtract(currentAirMap, currentContour, currentAirMap);
}
}
+ }
- });
+ });
- if (progress.Token.IsCancellationRequested)
- return GetResult();
- }
+ if (progress.Token.IsCancellationRequested)
+ return GetResult();
+ }
- //matCache[layerIndex].Dispose();
- //matCache[layerIndex] = null;
- //matTargetCache[layerIndex] = null;
- matCache.Consume(layerIndex);
+ //matCache[layerIndex].Dispose();
+ //matCache[layerIndex] = null;
+ //matTargetCache[layerIndex] = null;
+ matCache.Consume(layerIndex);
- progress++;
- }
+ progress++;
+ }
+ if (progress.Token.IsCancellationRequested) return GetResult();
+ progress.Reset("Detection pass 2 of 2 (Resin traps)", SlicerFile.LayerCount, resinTrapConfig.StartLayerIndex);
+ /* starting over again but this time from the top to the bottom */
+ if (currentAirMap is not null)
+ {
+ currentAirMap.Dispose();
+ currentAirMap = null;
+ }
+
+ var resinTrapGroups = new List<List<(VectorOfVectorOfPoint contour, uint layerIndex)>>();
+
+ matCache.Direction = false;
+ matCache.Clear();
+
+ for (int layerIndex = resinTraps.Length - 1; layerIndex >= resinTrapConfig.StartLayerIndex; layerIndex--)
+ {
if (progress.Token.IsCancellationRequested) return GetResult();
- progress.Reset("Detection pass 2 of 2 (Resin traps)", SlicerFile.LayerCount, resinTrapConfig.StartLayerIndex);
- /* starting over again but this time from the top to the bottom */
- if (currentAirMap is not null)
+
+ var curLayer = matCache.Get((uint)layerIndex, 1);
+
+ if (layerIndex == resinTraps.Length - 1)
{
- currentAirMap.Dispose();
- currentAirMap = null;
+ /* this is subtly different that for the first pass, we don't use GenerateAirMap for the initial airmap */
+ /* instead we use a bitwise not, this way anything that is open/hollow on the top layer is treated as air */
+ currentAirMap = curLayer.Clone();
+ CvInvoke.BitwiseNot(curLayer, currentAirMap);
}
- var resinTrapGroups = new List<List<(VectorOfVectorOfPoint contour, uint layerIndex)>>();
+ /* we still modify the airmap like normal, where we account for the air areas of the layer, and any contours that might overlap...*/
+ GenerateAirMap(curLayer, layerAirMap, externalContours[layerIndex]);
- matCache.Direction = false;
- matCache.Clear();
-
- for (int layerIndex = resinTraps.Length - 1; layerIndex >= resinTrapConfig.StartLayerIndex; layerIndex--)
+ /* Update air map with any hollows that were found to be air-connected during first pass */
+ if (airContours[layerIndex] is not null)
{
- if (progress.Token.IsCancellationRequested) return GetResult();
-
- var curLayer = matCache.Get((uint)layerIndex, 1);
+ Parallel.ForEach(airContours[layerIndex], CoreSettings.ParallelOptions, vec =>
+ CvInvoke.DrawContours(layerAirMap, vec, -1, EmguExtensions.WhiteColor, -1)
+ );
+ }
- if (layerIndex == resinTraps.Length - 1)
- {
- /* this is subtly different that for the first pass, we don't use GenerateAirMap for the initial airmap */
- /* instead we use a bitwise not, this way anything that is open/hollow on the top layer is treated as air */
- currentAirMap = curLayer.Clone();
- CvInvoke.BitwiseNot(curLayer, currentAirMap);
- }
+ /* remove solid areas of current layer from the air map */
+ CvInvoke.Subtract(currentAirMap, curLayer, currentAirMap);
- /* we still modify the airmap like normal, where we account for the air areas of the layer, and any contours that might overlap...*/
- GenerateAirMap(curLayer, layerAirMap, externalContours[layerIndex]);
+ /* add in areas of air in current layer to air map */
+ CvInvoke.BitwiseOr(layerAirMap, currentAirMap, currentAirMap);
- /* Update air map with any hollows that were found to be air-connected during first pass */
- if (airContours[layerIndex] is not null)
+ if (resinTraps[layerIndex] is not null)
+ {
+ suctionCups[layerIndex] = new();
+ /* here we don't worry about finding contours on the layer, the bottom to top pass did that already */
+ /* all we care about is contours the first pass thought were resin traps, since there was no access to air from the bottom */
+ Parallel.For(0, resinTraps[layerIndex].Count, CoreSettings.ParallelOptions, x =>
{
- Parallel.ForEach(airContours[layerIndex], CoreSettings.ParallelOptions, vec =>
- CvInvoke.DrawContours(layerAirMap, vec, -1, EmguExtensions.WhiteColor, -1)
- );
- }
+ if (progress.Token.IsCancellationRequested) return;
- /* remove solid areas of current layer from the air map */
- CvInvoke.Subtract(currentAirMap, curLayer, currentAirMap);
+ /* check if each contour overlaps known air */
+ using var currentContour = curLayer.NewBlank();
+ using var airOverlap = curLayer.NewBlank();
+ CvInvoke.DrawContours(currentContour, resinTraps[layerIndex][x], -1, EmguExtensions.WhiteColor, -1);
- /* add in areas of air in current layer to air map */
- CvInvoke.BitwiseOr(layerAirMap, currentAirMap, currentAirMap);
+ CvInvoke.BitwiseAnd(currentAirMap, currentContour, airOverlap);
+ var overlapCount = CvInvoke.CountNonZero(airOverlap);
- if (resinTraps[layerIndex] is not null)
- {
- suctionCups[layerIndex] = new();
- /* here we don't worry about finding contours on the layer, the bottom to top pass did that already */
- /* all we care about is contours the first pass thought were resin traps, since there was no access to air from the bottom */
- Parallel.For(0, resinTraps[layerIndex].Count, CoreSettings.ParallelOptions, x =>
+ //lock (SlicerFile[layerIndex].Mutex)
+ //{
+ if (overlapCount >= resinTrapConfig.RequiredBlackPixelsToDrain)
{
- if (progress.Token.IsCancellationRequested) return;
+ /* this contour does overlap air, add this it our air map */
+ CvInvoke.BitwiseOr(currentContour, currentAirMap, currentAirMap, currentContour);
+ /* Always add the removed contour to suctionTraps (even if we aren't reporting suction traps)
+ * This is because contours that are placed on here get removed from resin traps in the next stage
+ * if you don't put them here, they never get removed even if they should :) */
- /* check if each contour overlaps known air */
- using var currentContour = curLayer.NewBlank();
- using var airOverlap = curLayer.NewBlank();
- CvInvoke.DrawContours(currentContour, resinTraps[layerIndex][x], -1, EmguExtensions.WhiteColor, -1);
+ /* if we haven't defined a suctionTrap list for this layer, do so */
- CvInvoke.BitwiseAnd(currentAirMap, currentContour, airOverlap);
- var overlapCount = CvInvoke.CountNonZero(airOverlap);
-
- //lock (SlicerFile[layerIndex].Mutex)
- //{
- if (overlapCount >= resinTrapConfig.RequiredBlackPixelsToDrain)
+ lock (SlicerFile[layerIndex].Mutex)
{
- /* this contour does overlap air, add this it our air map */
- CvInvoke.BitwiseOr(currentContour, currentAirMap, currentAirMap, currentContour);
- /* Always add the removed contour to suctionTraps (even if we aren't reporting suction traps)
- * This is because contours that are placed on here get removed from resin traps in the next stage
- * if you don't put them here, they never get removed even if they should :) */
+ /* since we know it isn't a resin trap, it becomes a suction trap */
+ suctionCups[layerIndex].Add(resinTraps[layerIndex][x]);
- /* if we haven't defined a suctionTrap list for this layer, do so */
-
- lock (SlicerFile[layerIndex].Mutex)
+ for (var groupIndex = resinTrapGroups.Count - 1; groupIndex >= 0; groupIndex--)
{
- /* since we know it isn't a resin trap, it becomes a suction trap */
- suctionCups[layerIndex].Add(resinTraps[layerIndex][x]);
-
- for (var groupIndex = resinTrapGroups.Count - 1; groupIndex >= 0; groupIndex--)
+ var group = resinTrapGroups[groupIndex];
+ if (group[^1].layerIndex > layerIndex + 1)
{
- var group = resinTrapGroups[groupIndex];
- if (group[^1].layerIndex > layerIndex + 1)
- {
- // this group is disconnected from current layer by at least 1 layer, no need to process anything from here anymore
- //group.Clear();
- //resinTrapGroups.Remove(group);
- continue;
- }
+ // this group is disconnected from current layer by at least 1 layer, no need to process anything from here anymore
+ //group.Clear();
+ //resinTrapGroups.Remove(group);
+ continue;
+ }
- for (var contourIndex = group.Count - 1; contourIndex >= 0; contourIndex--)
- {
- if (group[contourIndex].layerIndex > layerIndex + 1) break;
- var testContour = group[contourIndex].contour;
+ for (var contourIndex = group.Count - 1; contourIndex >= 0; contourIndex--)
+ {
+ if (group[contourIndex].layerIndex > layerIndex + 1) break;
+ var testContour = group[contourIndex].contour;
- if (!EmguContours.ContoursIntersect(testContour, resinTraps[layerIndex][x])) continue;
- // if any contours in this group, that are on the previous layer, overlap the new suction area, they are all suction areas
+ if (!EmguContours.ContoursIntersect(testContour, resinTraps[layerIndex][x])) continue;
+ // if any contours in this group, that are on the previous layer, overlap the new suction area, they are all suction areas
- foreach (var item in group)
+ foreach (var item in group)
+ {
+ suctionCups[item.layerIndex].Add(item.contour);
+ if (item.layerIndex != layerIndex)
{
- suctionCups[item.layerIndex].Add(item.contour);
- if (item.layerIndex != layerIndex)
- {
- resinTraps[item.layerIndex].Remove(item.contour);
- }
+ resinTraps[item.layerIndex].Remove(item.contour);
}
-
- group.Clear();
- resinTrapGroups.Remove(group);
- break;
}
+
+ group.Clear();
+ resinTrapGroups.Remove(group);
+ break;
}
}
- /* to keep things tidy while we iterate resin traps, it will be left in the list for now, and removed later */
}
- else
- {
- /* doesn't overlap by enough, remove from air map */
- CvInvoke.Subtract(currentAirMap, currentContour, currentAirMap, currentContour);
+ /* to keep things tidy while we iterate resin traps, it will be left in the list for now, and removed later */
+ }
+ else
+ {
+ /* doesn't overlap by enough, remove from air map */
+ CvInvoke.Subtract(currentAirMap, currentContour, currentAirMap, currentContour);
- lock (SlicerFile[layerIndex].Mutex)
+ lock (SlicerFile[layerIndex].Mutex)
+ {
+ /* put it in a group of resin traps, used when a subsequent layer becomes a suction cup, it can convert any overlapping groups to suction cup */
+ /* select new LayerIssue(this[layerIndex], LayerIssue.IssueType.ResinTrap, area.Contour, area.BoundingRectangle)) */
+ var overlappingGroupIndexes = new List<int>();
+ for (var groupIndex = 0; groupIndex < resinTrapGroups.Count; groupIndex++)
{
- /* put it in a group of resin traps, used when a subsequent layer becomes a suction cup, it can convert any overlapping groups to suction cup */
- /* select new LayerIssue(this[layerIndex], LayerIssue.IssueType.ResinTrap, area.Contour, area.BoundingRectangle)) */
- var overlappingGroupIndexes = new List<int>();
- for (var groupIndex = 0; groupIndex < resinTrapGroups.Count; groupIndex++)
- {
- if (resinTrapGroups[groupIndex][^1].layerIndex != layerIndex && resinTrapGroups[groupIndex][^1].layerIndex != layerIndex + 1) continue;
+ if (resinTrapGroups[groupIndex][^1].layerIndex != layerIndex && resinTrapGroups[groupIndex][^1].layerIndex != layerIndex + 1) continue;
- if (EmguContours.ContoursIntersect(resinTrapGroups[groupIndex][^1].contour, resinTraps[layerIndex][x]))
- {
- overlappingGroupIndexes.Add(groupIndex);
- }
+ if (EmguContours.ContoursIntersect(resinTrapGroups[groupIndex][^1].contour, resinTraps[layerIndex][x]))
+ {
+ overlappingGroupIndexes.Add(groupIndex);
}
+ }
- if (overlappingGroupIndexes.Count == 0)
+ if (overlappingGroupIndexes.Count == 0)
+ {
+ // no overlaps, make a single issue
+ resinTrapGroups.Add(new List<(VectorOfVectorOfPoint contour, uint layerIndex)> { (resinTraps[layerIndex][x], (uint)layerIndex) });
+ }
+ else if (overlappingGroupIndexes.Count == 1)
+ {
+ resinTrapGroups[overlappingGroupIndexes[0]].Add((resinTraps[layerIndex][x], (uint)layerIndex));
+ }
+ else
+ {
+ var combinedGroup = new List<(VectorOfVectorOfPoint contour, uint layerIndex)>();
+ foreach (var index in overlappingGroupIndexes)
{
- // no overlaps, make a single issue
- resinTrapGroups.Add(new List<(VectorOfVectorOfPoint contour, uint layerIndex)> { (resinTraps[layerIndex][x], (uint)layerIndex) });
+ combinedGroup.AddRange(resinTrapGroups[index]);
}
- else if (overlappingGroupIndexes.Count == 1)
+
+ for (var index = overlappingGroupIndexes.Count - 1; index >= 0; index--)
{
- resinTrapGroups[overlappingGroupIndexes[0]].Add((resinTraps[layerIndex][x], (uint)layerIndex));
+ resinTrapGroups[overlappingGroupIndexes[index]].Clear();
+ resinTrapGroups.RemoveAt(overlappingGroupIndexes[index]);
}
- else
- {
- var combinedGroup = new List<(VectorOfVectorOfPoint contour, uint layerIndex)>();
- foreach (var index in overlappingGroupIndexes)
- {
- combinedGroup.AddRange(resinTrapGroups[index]);
- }
- for (var index = overlappingGroupIndexes.Count - 1; index >= 0; index--)
- {
- resinTrapGroups[overlappingGroupIndexes[index]].Clear();
- resinTrapGroups.RemoveAt(overlappingGroupIndexes[index]);
- }
-
- combinedGroup.Add((resinTraps[layerIndex][x], (uint)layerIndex));
- resinTrapGroups.Add(combinedGroup);
- }
+ combinedGroup.Add((resinTraps[layerIndex][x], (uint)layerIndex));
+ resinTrapGroups.Add(combinedGroup);
}
}
- //}
- });
+ }
+ //}
+ });
- /* anything that converted to a suction trap needs to removed from resinTraps. Loop backwards so indexes don't shift */
- if (suctionCups[layerIndex] is not null)
+ /* anything that converted to a suction trap needs to removed from resinTraps. Loop backwards so indexes don't shift */
+ if (suctionCups[layerIndex] is not null)
+ {
+ for (var i = suctionCups[layerIndex].Count - 1; i >= 0; i--)
{
- for (var i = suctionCups[layerIndex].Count - 1; i >= 0; i--)
- {
- resinTraps[layerIndex].Remove(suctionCups[layerIndex][i]);
- if (resinTraps[layerIndex].Count > 0) continue;
- resinTraps[layerIndex] = null;
- break;
- }
+ resinTraps[layerIndex].Remove(suctionCups[layerIndex][i]);
+ if (resinTraps[layerIndex].Count > 0) continue;
+ resinTraps[layerIndex] = null!;
+ break;
}
}
+ }
- matCache.Consume((uint)layerIndex);
+ matCache.Consume((uint)layerIndex);
- progress++;
- }
- if (progress.Token.IsCancellationRequested) return GetResult();
+ progress++;
+ }
+ if (progress.Token.IsCancellationRequested) return GetResult();
- /* translate all contour points by ROI x and y */
- var offsetBy = new Point(SlicerFile.BoundingRectangle.X, SlicerFile.BoundingRectangle.Y);
- foreach (var listOfLayers in new[] { resinTraps, suctionCups })
+ /* translate all contour points by ROI x and y */
+ var offsetBy = new Point(SlicerFile.BoundingRectangle.X, SlicerFile.BoundingRectangle.Y);
+ foreach (var listOfLayers in new[] { resinTraps, suctionCups })
+ {
+ Parallel.ForEach(listOfLayers.Where(list => list is not null), contoursGroups =>
{
- Parallel.ForEach(listOfLayers.Where(list => list is not null), contoursGroups =>
+ for (var groupIndex = 0; groupIndex < contoursGroups.Count; groupIndex++)
{
- for (var groupIndex = 0; groupIndex < contoursGroups.Count; groupIndex++)
- {
- var contours = contoursGroups[groupIndex];
+ var contours = contoursGroups[groupIndex];
- var arrayOfArrayOfPoints = contours.ToArrayOfArray();
+ var arrayOfArrayOfPoints = contours.ToArrayOfArray();
- foreach (var pointArray in arrayOfArrayOfPoints)
- for (var i = 0; i < pointArray.Length; i++)
- pointArray[i].Offset(offsetBy);
+ foreach (var pointArray in arrayOfArrayOfPoints)
+ for (var i = 0; i < pointArray.Length; i++)
+ pointArray[i].Offset(offsetBy);
- contoursGroups[groupIndex].Dispose();
- contoursGroups[groupIndex] = new VectorOfVectorOfPoint(arrayOfArrayOfPoints);
- }
+ contoursGroups[groupIndex].Dispose();
+ contoursGroups[groupIndex] = new VectorOfVectorOfPoint(arrayOfArrayOfPoints);
+ }
- //progress.LockAndIncrement();
- });
- }
+ //progress.LockAndIncrement();
+ });
+ }
- if (progress.Token.IsCancellationRequested) return GetResult();
+ if (progress.Token.IsCancellationRequested) return GetResult();
- if (resinTrapConfig.DetectSuctionCups)
- progress.Reset("Interpolating areas (Resin traps & suction cups)", (uint)(resinTraps.Count(list => list is not null) + suctionCups.Count(list => list is not null)));
- else
- progress.Reset("Interpolating areas (Resin traps)", (uint)(resinTraps.Count(list => list is not null)));
+ if (resinTrapConfig.DetectSuctionCups)
+ progress.Reset("Interpolating areas (Resin traps & suction cups)", (uint)(resinTraps.Count(list => list is not null) + suctionCups.Count(list => list is not null)));
+ else
+ progress.Reset("Interpolating areas (Resin traps)", (uint)(resinTraps.Count(list => list is not null)));
- Parallel.Invoke(() =>
+ Parallel.Invoke(() =>
{
var resinTrapGroups = new List<List<IssueOfContours>>();
@@ -1052,65 +1051,64 @@ namespace UVtools.Core.Managers
}
});
- // Dispose
- foreach (var listOfVectors in new[] { resinTraps, suctionCups, hollows, airContours })
+ // Dispose
+ foreach (var listOfVectors in new[] { resinTraps, suctionCups, hollows, airContours })
+ {
+ foreach (var vectorArray in listOfVectors)
{
- foreach (var vectorArray in listOfVectors)
+ if (vectorArray is null) continue;
+ foreach (var vector in vectorArray)
{
- if (vectorArray is null) continue;
- foreach (var vector in vectorArray)
- {
- vector?.Dispose();
- }
+ vector?.Dispose();
}
}
+ }
- foreach (var vector in externalContours)
- {
- vector?.Dispose();
- }
-
+ foreach (var vector in externalContours)
+ {
+ vector?.Dispose();
}
- return GetResult();
}
- public MainIssue[] DrillSuctionCupsForIssues(IEnumerable<MainIssue> issues, int ventHoleDiameter, OperationProgress progress)
+ return GetResult();
+ }
+
+ public MainIssue[] DrillSuctionCupsForIssues(IEnumerable<MainIssue> issues, int ventHoleDiameter, OperationProgress progress)
+ {
+ var drillOps = new List<PixelOperation>();
+ var drilledIssues = new List<MainIssue>();
+ //var suctionReliefSize = (ushort)Math.Max(SlicerFile.PpmmMax * 0.8, 17);
+ /* for each suction cup issue that is an initial layer */
+ foreach (var mainIssue in issues)
{
- Point GetDrillLocation(IssueOfContours issue, int diameter)
- {
- using var vecCentroid = new VectorOfPoint(issue.Contours[0]);
- var centroid = EmguContour.GetCentroid(vecCentroid);
- if (centroid.IsAnyNegative()) return centroid;
- using var circleCheck = EmguExtensions.InitMat(issue.BoundingRectangle.Size);
- using var contourMat = EmguExtensions.InitMat(issue.BoundingRectangle.Size);
-
- var inverseOffset = new Point(issue.BoundingRectangle.X * -1, issue.BoundingRectangle.Y * -1);
- using var vec = new VectorOfVectorOfPoint(issue.Contours);
- CvInvoke.DrawContours(contourMat, vec, -1, EmguExtensions.WhiteColor, -1, LineType.EightConnected, null, int.MaxValue, inverseOffset);
- CvInvoke.Circle(circleCheck, new(centroid.X + inverseOffset.X, centroid.Y + inverseOffset.Y), diameter, EmguExtensions.WhiteColor, -1);
- CvInvoke.BitwiseAnd(circleCheck, contourMat, circleCheck);
-
- return CvInvoke.CountNonZero(circleCheck) > 0
- ? centroid /* 5px centroid is inside layer! drill baby drill */
- : new Point(-1,-1); /* centroid is not inside the actual contour, no drill */
- }
+ var drillPoint = GetDrillLocation((IssueOfContours)mainIssue[0], ventHoleDiameter);
+ if (drillPoint.IsAnyNegative()) continue;
+ drillOps.Add(new PixelDrainHole(mainIssue.StartLayerIndex, drillPoint, (ushort)ventHoleDiameter));
+ drilledIssues.Add(mainIssue);
+ }
- var drillOps = new List<PixelOperation>();
- var drilledIssues = new List<MainIssue>();
- //var suctionReliefSize = (ushort)Math.Max(SlicerFile.PpmmMax * 0.8, 17);
- /* for each suction cup issue that is an initial layer */
- foreach (var mainIssue in issues)
- {
- var drillPoint = GetDrillLocation((IssueOfContours)mainIssue[0], ventHoleDiameter);
- if (drillPoint.IsAnyNegative()) continue;
- drillOps.Add(new PixelDrainHole(mainIssue.StartLayerIndex, drillPoint, (ushort)ventHoleDiameter));
- drilledIssues.Add(mainIssue);
- }
+ SlicerFile.DrawModifications(drillOps, progress);
- SlicerFile.LayerManager.DrawModifications(drillOps, progress);
+ return drilledIssues.ToArray();
+ }
- return drilledIssues.ToArray();
- }
+ public static Point GetDrillLocation(IssueOfContours issue, int diameter)
+ {
+ using var vecCentroid = new VectorOfPoint(issue.Contours[0]);
+ var centroid = EmguContour.GetCentroid(vecCentroid);
+ if (centroid.IsAnyNegative()) return centroid;
+ using var circleCheck = EmguExtensions.InitMat(issue.BoundingRectangle.Size);
+ using var contourMat = EmguExtensions.InitMat(issue.BoundingRectangle.Size);
+
+ var inverseOffset = new Point(issue.BoundingRectangle.X * -1, issue.BoundingRectangle.Y * -1);
+ using var vec = new VectorOfVectorOfPoint(issue.Contours);
+ CvInvoke.DrawContours(contourMat, vec, -1, EmguExtensions.WhiteColor, -1, LineType.EightConnected, null, int.MaxValue, inverseOffset);
+ CvInvoke.Circle(circleCheck, new(centroid.X + inverseOffset.X, centroid.Y + inverseOffset.Y), diameter, EmguExtensions.WhiteColor, -1);
+ CvInvoke.BitwiseAnd(circleCheck, contourMat, circleCheck);
+
+ return CvInvoke.CountNonZero(circleCheck) > 0
+ ? centroid /* 5px centroid is inside layer! drill baby drill */
+ : new Point(-1,-1); /* centroid is not inside the actual contour, no drill */
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Managers/KernelCacheManager.cs b/UVtools.Core/Managers/KernelCacheManager.cs
index aec7f88..909eafd 100644
--- a/UVtools.Core/Managers/KernelCacheManager.cs
+++ b/UVtools.Core/Managers/KernelCacheManager.cs
@@ -6,50 +6,49 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Collections.Concurrent;
-using Emgu.CV;
using UVtools.Core.Extensions;
-namespace UVtools.Core.Managers
+namespace UVtools.Core.Managers;
+
+public class KernelCacheManager : IDisposable
{
- public class KernelCacheManager : IDisposable
- {
- private readonly ConcurrentDictionary<int, Mat> _kernelCache = new();
+ private readonly ConcurrentDictionary<int, Mat> _kernelCache = new();
- public bool UseDynamicKernel { get; set; }
+ public bool UseDynamicKernel { get; set; }
- private readonly Mat _defaultKernel;
+ private readonly Mat _defaultKernel;
- public KernelCacheManager(bool useDynamicKernel = false, Mat defaultKernel = null)
- {
- UseDynamicKernel = useDynamicKernel;
- _defaultKernel = defaultKernel ?? EmguExtensions.Kernel3x3Rectangle;
- }
+ public KernelCacheManager(bool useDynamicKernel = false, Mat? defaultKernel = null)
+ {
+ UseDynamicKernel = useDynamicKernel;
+ _defaultKernel = defaultKernel ?? EmguExtensions.Kernel3x3Rectangle;
+ }
- public Mat Get(ref int iterations)
- {
- if (!UseDynamicKernel) return _defaultKernel;
- var mat = _kernelCache.GetOrAdd(iterations, i => EmguExtensions.GetDynamicKernel(ref i));
- iterations = 1;
- return mat;
- }
+ public Mat Get(ref int iterations)
+ {
+ if (!UseDynamicKernel) return _defaultKernel;
+ var mat = _kernelCache.GetOrAdd(iterations, i => EmguExtensions.GetDynamicKernel(ref i));
+ iterations = 1;
+ return mat;
+ }
- /// <summary>
- /// Clears and dispose all the cache
- /// </summary>
- public void Clear()
+ /// <summary>
+ /// Clears and dispose all the cache
+ /// </summary>
+ public void Clear()
+ {
+ foreach (var (_, mat) in _kernelCache)
{
- foreach (var (_, mat) in _kernelCache)
- {
- if(ReferenceEquals(mat, EmguExtensions.Kernel3x3Rectangle)) mat?.Dispose();
- }
+ if(ReferenceEquals(mat, EmguExtensions.Kernel3x3Rectangle)) mat?.Dispose();
}
+ }
- /// <inheritdoc />
- public void Dispose()
- {
- Clear();
- }
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Clear();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Managers/MatCacheManager.cs b/UVtools.Core/Managers/MatCacheManager.cs
index adabcba..82c19d2 100644
--- a/UVtools.Core/Managers/MatCacheManager.cs
+++ b/UVtools.Core/Managers/MatCacheManager.cs
@@ -6,311 +6,310 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
-namespace UVtools.Core.Managers
+namespace UVtools.Core.Managers;
+
+public class MatCacheManager : IDisposable
{
- public class MatCacheManager : IDisposable
+ /// <summary>
+ /// Gets the slicer file
+ /// </summary>
+ public FileFormat SlicerFile { get; }
+
+ /// <summary>
+ /// Gets or sets the cache count of items to keep in memory
+ /// </summary>
+ public ushort CacheCount { get; set; }
+
+ /// <summary>
+ /// Gets the number of cached elements per a cache entry
+ /// </summary>
+ public byte ElementsPerCache { get; }
+
+ /// <summary>
+ /// Gets the starting layer index range
+ /// </summary>
+ public uint LayerIndexStart { get; }
+
+ /// <summary>
+ /// Gets the ending layer index range
+ /// </summary>
+ public uint LayerIndexEnd { get; }
+
+ /// <summary>
+ /// Gets the size of this collection
+ /// </summary>
+ public uint CollectionSize => LayerIndexEnd - LayerIndexStart + 1;
+
+ /// <summary>
+ /// Gets the image rotation to cache
+ /// </summary>
+ public Enumerations.RotateDirection Rotate { get; init; } = Enumerations.RotateDirection.None;
+
+ /// <summary>
+ /// Gets the image flip to cache
+ /// </summary>
+ public Enumerations.FlipDirection Flip { get; init; } = Enumerations.FlipDirection.None;
+
+ /// <summary>
+ /// Gets if striping anti-aliasing is enabled, cache will be threshold'ed
+ /// </summary>
+ public bool StripAntiAliasing { get; init; }
+
+ /// <summary>
+ /// Gets or sets the cache direction, false to go backwards, true to go forward
+ /// </summary>
+ public bool Direction { get; set; } = true;
+
+ /// <summary>
+ /// If enabled it will not calculate the cache index given a layer index, set this to true to use your own indexing without pair them with layers
+ /// </summary>
+ public bool UseRawIndexInsteadOfLayerIndex { get; init; }
+
+ /// <summary>
+ /// Gets or sets the auto dispose mode, it will dispose all mat's below of above passed index
+ /// </summary>
+ public bool AutoDispose { get; set; } = false;
+
+ /// <summary>
+ /// Gets or sets the amount of last mats to keep cached when <see cref="AutoDispose"/> is enabled
+ /// </summary>
+ public ushort AutoDisposeKeepLast { get; set; } = 0;
+
+ /// <summary>
+ /// Gets the cache mat array
+ /// </summary>
+ private Mat[][] MatCache { get; }
+
+ /// <summary>
+ /// Gets or sets the action to trigger after cache the initial <see cref="Mat"/>
+ /// </summary>
+ public Action<Mat[]>? AfterCacheAction { get; set; }
+
+ public MatCacheManager(FileFormat slicerFile, ushort cacheCount = 0, byte elementsPerCache = 1) : this(slicerFile, 0, slicerFile.LastLayerIndex, cacheCount, elementsPerCache)
+ { }
+
+ public MatCacheManager(FileFormat slicerFile, uint collectionSize, ushort cacheCount = 0, byte elementsPerCache = 1) : this(slicerFile, 0, collectionSize-1, cacheCount, elementsPerCache)
+ { }
+
+ public MatCacheManager(Operation operation, ushort cacheCount = 0, byte elementsPerCache = 1) : this(operation.SlicerFile, operation.LayerIndexStart, operation.LayerIndexEnd, cacheCount, elementsPerCache)
+ {}
+
+ public MatCacheManager(FileFormat slicerFile, uint layerIndexStart, uint layerIndexEnd, ushort cacheCount = 0, byte elementsPerCache = 1)
{
- /// <summary>
- /// Gets the slicer file
- /// </summary>
- public FileFormat SlicerFile { get; }
-
- /// <summary>
- /// Gets or sets the cache count of items to keep in memory
- /// </summary>
- public ushort CacheCount { get; set; }
-
- /// <summary>
- /// Gets the number of cached elements per a cache entry
- /// </summary>
- public byte ElementsPerCache { get; }
-
- /// <summary>
- /// Gets the starting layer index range
- /// </summary>
- public uint LayerIndexStart { get; }
-
- /// <summary>
- /// Gets the ending layer index range
- /// </summary>
- public uint LayerIndexEnd { get; }
-
- /// <summary>
- /// Gets the size of this collection
- /// </summary>
- public uint CollectionSize => LayerIndexEnd - LayerIndexStart + 1;
-
- /// <summary>
- /// Gets the image rotation to cache
- /// </summary>
- public Enumerations.RotateDirection Rotate { get; init; } = Enumerations.RotateDirection.None;
-
- /// <summary>
- /// Gets the image flip to cache
- /// </summary>
- public Enumerations.FlipDirection Flip { get; init; } = Enumerations.FlipDirection.None;
-
- /// <summary>
- /// Gets if striping anti-aliasing is enabled, cache will be threshold'ed
- /// </summary>
- public bool StripAntiAliasing { get; init; }
-
- /// <summary>
- /// Gets or sets the cache direction, false to go backwards, true to go forward
- /// </summary>
- public bool Direction { get; set; } = true;
-
- /// <summary>
- /// If enabled it will not calculate the cache index given a layer index, set this to true to use your own indexing without pair them with layers
- /// </summary>
- public bool UseRawIndexInsteadOfLayerIndex { get; init; }
-
- /// <summary>
- /// Gets or sets the auto dispose mode, it will dispose all mat's below of above passed index
- /// </summary>
- public bool AutoDispose { get; set; } = false;
-
- /// <summary>
- /// Gets or sets the amount of last mats to keep cached when <see cref="AutoDispose"/> is enabled
- /// </summary>
- public ushort AutoDisposeKeepLast { get; set; } = 0;
-
- /// <summary>
- /// Gets the cache mat array
- /// </summary>
- private Mat[][] MatCache { get; }
-
- /// <summary>
- /// Gets or sets the action to trigger after cache the initial <see cref="Mat"/>
- /// </summary>
- public Action<Mat[]> AfterCacheAction { get; set; }
-
- public MatCacheManager(FileFormat slicerFile, ushort cacheCount = 0, byte elementsPerCache = 1) : this(slicerFile, 0, slicerFile.LastLayerIndex, cacheCount, elementsPerCache)
- { }
-
- public MatCacheManager(FileFormat slicerFile, uint collectionSize, ushort cacheCount = 0, byte elementsPerCache = 1) : this(slicerFile, 0, collectionSize-1, cacheCount, elementsPerCache)
- { }
-
- public MatCacheManager(Operation operation, ushort cacheCount = 0, byte elementsPerCache = 1) : this(operation.SlicerFile, operation.LayerIndexStart, operation.LayerIndexEnd, cacheCount, elementsPerCache)
- {}
-
- public MatCacheManager(FileFormat slicerFile, uint layerIndexStart, uint layerIndexEnd, ushort cacheCount = 0, byte elementsPerCache = 1)
- {
- if (cacheCount == 0) cacheCount = (ushort)(Environment.ProcessorCount * 5);
- if (layerIndexEnd == 0) layerIndexEnd = slicerFile.LayerCount;
- SlicerFile = slicerFile;
- LayerIndexStart = layerIndexStart;
- LayerIndexEnd = layerIndexEnd;
- CacheCount = cacheCount;
- ElementsPerCache = elementsPerCache;
- MatCache = new Mat[CollectionSize][];
-
- if (layerIndexStart == 0) UseRawIndexInsteadOfLayerIndex = true;
- }
+ if (cacheCount == 0) cacheCount = (ushort)(Environment.ProcessorCount * 5);
+ if (layerIndexEnd == 0) layerIndexEnd = slicerFile.LayerCount;
+ SlicerFile = slicerFile;
+ LayerIndexStart = layerIndexStart;
+ LayerIndexEnd = layerIndexEnd;
+ CacheCount = cacheCount;
+ ElementsPerCache = elementsPerCache;
+ MatCache = new Mat[CollectionSize][];
+
+ if (layerIndexStart == 0) UseRawIndexInsteadOfLayerIndex = true;
+ }
- /// <summary>
- /// Gets the cache index from a layer index, required when layer range is not 0 started
- /// </summary>
- /// <param name="layerIndex"></param>
- /// <returns></returns>
- private uint LayerIndexToCacheIndex(uint layerIndex)
- {
- return UseRawIndexInsteadOfLayerIndex ? layerIndex : layerIndex - LayerIndexStart;
- }
+ /// <summary>
+ /// Gets the cache index from a layer index, required when layer range is not 0 started
+ /// </summary>
+ /// <param name="layerIndex"></param>
+ /// <returns></returns>
+ private uint LayerIndexToCacheIndex(uint layerIndex)
+ {
+ return UseRawIndexInsteadOfLayerIndex ? layerIndex : layerIndex - LayerIndexStart;
+ }
- /// <summary>
- /// Gets the layer index from the cache index, required when layer range is not 0 started
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- private uint CacheIndexToLayerIndex(uint index)
- {
- return UseRawIndexInsteadOfLayerIndex ? index : index + LayerIndexStart;
- }
+ /// <summary>
+ /// Gets the layer index from the cache index, required when layer range is not 0 started
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ private uint CacheIndexToLayerIndex(uint index)
+ {
+ return UseRawIndexInsteadOfLayerIndex ? index : index + LayerIndexStart;
+ }
- /// <summary>
- /// Gets all cached mat's given an index
- /// </summary>
- /// <param name="layerIndex"></param>
- /// <returns></returns>
- public Mat[] Get(uint layerIndex)
+ /// <summary>
+ /// Gets all cached mat's given an index
+ /// </summary>
+ /// <param name="layerIndex"></param>
+ /// <returns></returns>
+ public Mat[] Get(uint layerIndex)
+ {
+ uint index = LayerIndexToCacheIndex(layerIndex);
+ if (MatCache[index] is null)
{
- uint index = LayerIndexToCacheIndex(layerIndex);
- if (MatCache[index] is null)
+ if (AutoDispose) // Dispose as go
{
- if (AutoDispose) // Dispose as go
- {
- ClearButKeep(layerIndex, AutoDisposeKeepLast);
- }
+ ClearButKeep(layerIndex, AutoDisposeKeepLast);
+ }
- int fromLayerIndex = (int)layerIndex;
- int toLayerIndex = (int)Math.Min(LayerIndexEnd+1, layerIndex + CacheCount);
+ int fromLayerIndex = (int)layerIndex;
+ int toLayerIndex = (int)Math.Min(LayerIndexEnd+1, layerIndex + CacheCount);
- if (!Direction)
+ if (!Direction)
+ {
+ toLayerIndex = fromLayerIndex + 1;
+ fromLayerIndex = Math.Max((int)LayerIndexStart, fromLayerIndex - CacheCount);
+ }
+
+ Parallel.For(fromLayerIndex, toLayerIndex, CoreSettings.ParallelOptions,
+ currentLayerIndex =>
{
- toLayerIndex = fromLayerIndex + 1;
- fromLayerIndex = Math.Max((int)LayerIndexStart, fromLayerIndex - CacheCount);
- }
+ var currentCacheIndex = LayerIndexToCacheIndex((uint)currentLayerIndex);
+ if (MatCache[currentCacheIndex] is not null) return; // Already cached
+ MatCache[currentCacheIndex] = new Mat[ElementsPerCache];
+ MatCache[currentCacheIndex][0] = SlicerFile[currentLayerIndex].LayerMat;
- Parallel.For(fromLayerIndex, toLayerIndex, CoreSettings.ParallelOptions,
- currentLayerIndex =>
+ if (Flip != Enumerations.FlipDirection.None)
{
- var currentCacheIndex = LayerIndexToCacheIndex((uint)currentLayerIndex);
- if (MatCache[currentCacheIndex] is not null) return; // Already cached
- MatCache[currentCacheIndex] = new Mat[ElementsPerCache];
- MatCache[currentCacheIndex][0] = SlicerFile[currentLayerIndex].LayerMat;
-
- if (Flip != Enumerations.FlipDirection.None)
- {
- CvInvoke.Flip(MatCache[currentCacheIndex][0], MatCache[currentCacheIndex][0], Enumerations.ToOpenCVFlipType(Flip));
- }
-
- if (Rotate != Enumerations.RotateDirection.None)
- {
- CvInvoke.Rotate(MatCache[currentCacheIndex][0], MatCache[currentCacheIndex][0], Enumerations.ToOpenCVRotateFlags(Rotate));
- }
-
- if (StripAntiAliasing)
- {
- CvInvoke.Threshold(MatCache[currentCacheIndex][0], MatCache[currentCacheIndex][0], 127, byte.MaxValue, ThresholdType.Binary);
- }
-
- AfterCacheAction?.Invoke(MatCache[currentCacheIndex]);
- });
- }
+ CvInvoke.Flip(MatCache[currentCacheIndex][0], MatCache[currentCacheIndex][0], Enumerations.ToOpenCVFlipType(Flip));
+ }
- return MatCache[index];
+ if (Rotate != Enumerations.RotateDirection.None)
+ {
+ CvInvoke.Rotate(MatCache[currentCacheIndex][0], MatCache[currentCacheIndex][0], Enumerations.ToOpenCVRotateFlags(Rotate));
+ }
+
+ if (StripAntiAliasing)
+ {
+ CvInvoke.Threshold(MatCache[currentCacheIndex][0], MatCache[currentCacheIndex][0], 127, byte.MaxValue, ThresholdType.Binary);
+ }
+
+ AfterCacheAction?.Invoke(MatCache[currentCacheIndex]);
+ });
}
- public Mat Get(uint layerIndex, byte elementIndex)
+ return MatCache[index];
+ }
+
+ public Mat Get(uint layerIndex, byte elementIndex)
+ {
+ return Get(layerIndex)[elementIndex];
+ }
+
+ public Mat Get1(uint layerIndex)
+ {
+ return Get(layerIndex)[0];
+ }
+
+ public (Mat mat1, Mat mat2) Get2(uint layerIndex)
+ {
+ var mats = Get(layerIndex);
+ return (mats[0], mats[1]);
+ }
+
+ public (Mat mat1, Mat mat2, Mat mat3) Get3(uint layerIndex)
+ {
+ var mats = Get(layerIndex);
+ return (mats[0], mats[1], mats[2]);
+ }
+
+
+ public void GetAndConsumeWork(uint layerIndex, Action<Mat[]> action)
+ {
+ try
{
- return Get(layerIndex)[elementIndex];
+ action.Invoke(Get(layerIndex));
}
-
- public Mat Get1(uint layerIndex)
+ catch (Exception e)
{
- return Get(layerIndex)[0];
+ Debug.WriteLine(e);
+ throw;
}
-
- public (Mat mat1, Mat mat2) Get2(uint layerIndex)
+ finally
{
- var mats = Get(layerIndex);
- return (mats[0], mats[1]);
+ Consume(layerIndex);
}
+ }
- public (Mat mat1, Mat mat2, Mat mat3) Get3(uint layerIndex)
+ public void GetAndConsumeWork(uint layerIndex, Action<Mat> action)
+ {
+ try
{
- var mats = Get(layerIndex);
- return (mats[0], mats[1], mats[2]);
+ action.Invoke(Get1(layerIndex));
}
-
-
- public void GetAndConsumeWork(uint layerIndex, Action<Mat[]> action)
+ catch (Exception e)
{
- try
- {
- action.Invoke(Get(layerIndex));
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- finally
- {
- Consume(layerIndex);
- }
+ Debug.WriteLine(e);
+ throw;
}
-
- public void GetAndConsumeWork(uint layerIndex, Action<Mat> action)
+ finally
{
- try
- {
- action.Invoke(Get1(layerIndex));
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- finally
- {
- Consume(layerIndex);
- }
+ Consume(layerIndex);
}
+ }
- public void GetAndConsumeWork(uint layerIndex, byte elementIndex, Action<Mat> action)
+ public void GetAndConsumeWork(uint layerIndex, byte elementIndex, Action<Mat> action)
+ {
+ try
{
- try
- {
- action.Invoke(Get(layerIndex)[elementIndex]);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- finally
- {
- Consume(layerIndex);
- }
+ action.Invoke(Get(layerIndex)[elementIndex]);
}
-
- /// <summary>
- /// Consume and dispose the cached Mats for given layer index
- /// </summary>
- /// <param name="layerIndex"></param>
- public void Consume(uint layerIndex)
+ catch (Exception e)
{
- var index = LayerIndexToCacheIndex(layerIndex);
- if(MatCache[index] is null) return;
- for (int i = 0; i < MatCache[index].Length; i++)
- {
- MatCache[index][i]?.Dispose();
- }
- MatCache[index] = null;
+ Debug.WriteLine(e);
+ throw;
+ }
+ finally
+ {
+ Consume(layerIndex);
}
+ }
- /// <summary>
- /// Clears and dispose the cache but keep the selected index and optionally the last n indexes
- /// </summary>
- /// <param name="layerIndex"></param>
- /// <param name="keepLast"></param>
- public void ClearButKeep(uint layerIndex, ushort keepLast = 0)
+ /// <summary>
+ /// Consume and dispose the cached Mats for given layer index
+ /// </summary>
+ /// <param name="layerIndex"></param>
+ public void Consume(uint layerIndex)
+ {
+ var index = LayerIndexToCacheIndex(layerIndex);
+ if(MatCache[index] is null) return;
+ for (int i = 0; i < MatCache[index].Length; i++)
{
- if (Direction)
- {
- for (int i = (int)layerIndex - 1 - keepLast; i >= LayerIndexStart; i--) Consume((uint)i);
- }
- else
- {
- for (int i = (int)layerIndex + 1 + keepLast; i <= LayerIndexEnd; i++) Consume((uint)i);
- }
+ MatCache[index][i]?.Dispose();
}
+ MatCache[index] = null!;
+ }
- /// <summary>
- /// Clears and dispose all the cache
- /// </summary>
- public void Clear()
+ /// <summary>
+ /// Clears and dispose the cache but keep the selected index and optionally the last n indexes
+ /// </summary>
+ /// <param name="layerIndex"></param>
+ /// <param name="keepLast"></param>
+ public void ClearButKeep(uint layerIndex, ushort keepLast = 0)
+ {
+ if (Direction)
{
- for (uint i = 0; i < MatCache.Length; i++)
- {
- Consume(CacheIndexToLayerIndex(i));
- }
+ for (int i = (int)layerIndex - 1 - keepLast; i >= LayerIndexStart; i--) Consume((uint)i);
+ }
+ else
+ {
+ for (int i = (int)layerIndex + 1 + keepLast; i <= LayerIndexEnd; i++) Consume((uint)i);
}
+ }
- /// <inheritdoc />
- public void Dispose()
+ /// <summary>
+ /// Clears and dispose all the cache
+ /// </summary>
+ public void Clear()
+ {
+ for (uint i = 0; i < MatCache.Length; i++)
{
- Clear();
+ Consume(CacheIndexToLayerIndex(i));
}
}
-}
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Clear();
+ }
+} \ No newline at end of file
diff --git a/UVtools.Core/Managers/MaterialManager.cs b/UVtools.Core/Managers/MaterialManager.cs
index 397a633..db34814 100644
--- a/UVtools.Core/Managers/MaterialManager.cs
+++ b/UVtools.Core/Managers/MaterialManager.cs
@@ -12,270 +12,265 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Xml.Serialization;
+using UVtools.Core.Extensions;
using UVtools.Core.Objects;
-namespace UVtools.Core.Managers
+namespace UVtools.Core.Managers;
+
+public class MaterialManager : BindableBase, IList<Material>
{
- public class MaterialManager : BindableBase, IList<Material>
- {
- #region Settings
+ #region Settings
- public static string FilePath;
- #endregion
+ public static string FilePath = Path.Combine(CoreSettings.DefaultSettingsFolderAndEnsureCreation, "materials.xml");
+ #endregion
- #region Singleton
+ #region Singleton
- private static Lazy<MaterialManager> _instanceHolder =
- new(() => new MaterialManager());
+ private static Lazy<MaterialManager> _instanceHolder =
+ new(() => new MaterialManager());
- /// <summary>
- /// Instance of <see cref="UserSettings"/> (singleton)
- /// </summary>
- public static MaterialManager Instance => _instanceHolder.Value;
+ /// <summary>
+ /// Instance of <see cref="UserSettings"/> (singleton)
+ /// </summary>
+ public static MaterialManager Instance => _instanceHolder.Value;
- //public static List<Operation> Operations => _instance.Operations;
- #endregion
+ //public static List<Operation> Operations => _instance.Operations;
+ #endregion
- #region Members
+ #region Members
- private RangeObservableCollection<Material> _materials = new();
+ private RangeObservableCollection<Material> _materials = new();
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public RangeObservableCollection<Material> Materials
- {
- get => _materials;
- set => RaiseAndSetIfChanged(ref _materials, value);
- }
+ public RangeObservableCollection<Material> Materials
+ {
+ get => _materials;
+ set => RaiseAndSetIfChanged(ref _materials, value);
+ }
- /// <summary>
- /// Gets the total number of bottles in stock
- /// </summary>
- public int BottlesInStock => this.Sum(material => material.BottlesInStock);
-
- /// <summary>
- /// Gets the total number of bottles ever owned
- /// </summary>
- public int OwnedBottles => this.Sum(material => material.OwnedBottles);
-
- /// <summary>
- /// Gets the total of consumed volume in milliliters
- /// </summary>
- public decimal ConsumedVolume => this.Sum(material => material.ConsumedVolume);
-
- /// <summary>
- /// Gets the total of consumed volume in liters
- /// </summary>
- public decimal ConsumedVolumeLiters => this.Sum(material => material.ConsumedVolumeLiters);
-
- /// <summary>
- /// Gets the total volume in stock in milliliters
- /// </summary>
- public decimal VolumeInStock => this.Sum(material => material.VolumeInStock);
-
- /// <summary>
- /// Gets the total volume in stock in liters
- /// </summary>
- public decimal VolumeInStockLiters => VolumeInStock / 1000;
-
- /// <summary>
- /// Gets the total costs
- /// </summary>
- public decimal TotalCost => this.Sum(material => material.TotalCost);
-
- /// <summary>
- /// Gets the total print time in hours
- /// </summary>
- public double PrintTime => this.Sum(material => material.PrintTime);
-
- /// <summary>
- /// Gets the total print time
- /// </summary>
- public TimeSpan PrintTimeSpan => TimeSpan.FromHours(PrintTime);
-
- #endregion
-
- #region Constructor
- private MaterialManager()
- {
- }
- #endregion
+ /// <summary>
+ /// Gets the total number of bottles in stock
+ /// </summary>
+ public int BottlesInStock => this.Sum(material => material.BottlesInStock);
+
+ /// <summary>
+ /// Gets the total number of bottles ever owned
+ /// </summary>
+ public int OwnedBottles => this.Sum(material => material.OwnedBottles);
+
+ /// <summary>
+ /// Gets the total of consumed volume in milliliters
+ /// </summary>
+ public decimal ConsumedVolume => this.Sum(material => material.ConsumedVolume);
+
+ /// <summary>
+ /// Gets the total of consumed volume in liters
+ /// </summary>
+ public decimal ConsumedVolumeLiters => this.Sum(material => material.ConsumedVolumeLiters);
+
+ /// <summary>
+ /// Gets the total volume in stock in milliliters
+ /// </summary>
+ public decimal VolumeInStock => this.Sum(material => material.VolumeInStock);
+
+ /// <summary>
+ /// Gets the total volume in stock in liters
+ /// </summary>
+ public decimal VolumeInStockLiters => VolumeInStock / 1000;
+
+ /// <summary>
+ /// Gets the total costs
+ /// </summary>
+ public decimal TotalCost => this.Sum(material => material.TotalCost);
+
+ /// <summary>
+ /// Gets the total print time in hours
+ /// </summary>
+ public double PrintTime => this.Sum(material => material.PrintTime);
+
+ /// <summary>
+ /// Gets the total print time
+ /// </summary>
+ public TimeSpan PrintTimeSpan => TimeSpan.FromHours(PrintTime);
+
+ #endregion
+
+ #region Constructor
+ private MaterialManager()
+ {
+ }
+ #endregion
- #region Methods
- public Material this[int index]
- {
- get => _materials[index];
- set => _materials[index] = value;
- }
+ #region Methods
+ public Material this[int index]
+ {
+ get => _materials[index];
+ set => _materials[index] = value;
+ }
+
+ public Material this[uint index]
+ {
+ get => _materials[(int) index];
+ set => _materials[(int) index] = value;
+ }
- public Material this[uint index]
+ public Material? this[Material material]
+ {
+ get
{
- get => _materials[(int) index];
- set => _materials[(int) index] = value;
+ var index = IndexOf(material);
+ return index < 0 ? this[index] : null;
}
-
- public Material this[Material material]
+ set
{
- get
- {
- var index = IndexOf(material);
- return index < 0 ? this[index] : null;
- }
- set
- {
- var index = IndexOf(material);
- if(index >= 0) this[index] = value;
- }
+ var index = IndexOf(material);
+ if(index >= 0) this[index] = value!;
}
+ }
- public IEnumerator<Material> GetEnumerator() => _materials.GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ public IEnumerator<Material> GetEnumerator() => _materials.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- public void Add(Material item)
- {
- _materials.Add(item);
- RaisePropertiesChanged();
- }
+ public void Add(Material item)
+ {
+ _materials.Add(item);
+ RaisePropertiesChanged();
+ }
- public void Add(Material item, bool save)
- {
- Add(item);
- if (save) Save();
- }
+ public void Add(Material item, bool save)
+ {
+ Add(item);
+ if (save) Save();
+ }
- public void Clear()
- {
- _materials.Clear();
- RaisePropertiesChanged();
- }
+ public void Clear()
+ {
+ _materials.Clear();
+ RaisePropertiesChanged();
+ }
- public void Clear(bool save)
- {
- Clear();
- if(save) Save();
- }
+ public void Clear(bool save)
+ {
+ Clear();
+ if(save) Save();
+ }
- public bool Contains(Material item) => _materials.Contains(item);
+ public bool Contains(Material item) => _materials.Contains(item);
+
+ public void CopyTo(Material[] array, int arrayIndex)
+ {
+ _materials.CopyTo(array, arrayIndex);
+ RaisePropertiesChanged();
+ }
- public void CopyTo(Material[] array, int arrayIndex)
+ public bool Remove(Material item)
+ {
+ if (_materials.Remove(item))
{
- _materials.CopyTo(array, arrayIndex);
RaisePropertiesChanged();
+ return true;
}
- public bool Remove(Material item)
- {
- if (_materials.Remove(item))
- {
- RaisePropertiesChanged();
- return true;
- }
+ return false;
+ }
- return false;
- }
+ public void Remove(Material item, bool save)
+ {
+ Remove(item);
+ if (save) Save();
+ }
- public void Remove(Material item, bool save)
- {
- Remove(item);
- if (save) Save();
- }
+ public void RemoveRange(IEnumerable<Material> collection)
+ {
+ _materials.RemoveRange(collection);
+ }
- public void RemoveRange(IEnumerable<Material> collection)
- {
- _materials.RemoveRange(collection);
- }
+ public int Count => _materials.Count;
+ public bool IsReadOnly => false;
+ public int IndexOf(Material item) => _materials.IndexOf(item);
- public int Count => _materials.Count;
- public bool IsReadOnly => false;
- public int IndexOf(Material item) => _materials.IndexOf(item);
+ public void Insert(int index, Material item)
+ {
+ _materials.Insert(index, item);
+ RaisePropertiesChanged();
+ }
- public void Insert(int index, Material item)
- {
- _materials.Insert(index, item);
- RaisePropertiesChanged();
- }
+ public void Insert(int index, Material item, bool save)
+ {
+ Insert(index, item);
+ if (save) Save();
+ }
- public void Insert(int index, Material item, bool save)
- {
- Insert(index, item);
- if (save) Save();
- }
+ public void RemoveAt(int index)
+ {
+ _materials.RemoveAt(index);
+ RaisePropertiesChanged();
+ }
- public void RemoveAt(int index)
- {
- _materials.RemoveAt(index);
- RaisePropertiesChanged();
- }
+ public void RemoveAt(int index, bool save)
+ {
+ RemoveAt(index);
+ if (save) Save();
+ }
+
+ public void RaisePropertiesChanged()
+ {
+ RaisePropertyChanged(nameof(BottlesInStock));
+ RaisePropertyChanged(nameof(OwnedBottles));
+ RaisePropertyChanged(nameof(ConsumedVolume));
+ RaisePropertyChanged(nameof(ConsumedVolumeLiters));
+ RaisePropertyChanged(nameof(VolumeInStock));
+ RaisePropertyChanged(nameof(VolumeInStockLiters));
+ RaisePropertyChanged(nameof(TotalCost));
+ RaisePropertyChanged(nameof(PrintTime));
+ RaisePropertyChanged(nameof(PrintTimeSpan));
+ RaisePropertyChanged(nameof(Count));
+ }
- public void RemoveAt(int index, bool save)
+ /// <summary>
+ /// Load settings from file
+ /// </summary>
+ public static void Load()
+ {
+ if (string.IsNullOrWhiteSpace(FilePath) || !File.Exists(FilePath))
{
- RemoveAt(index);
- if (save) Save();
+ return;
}
- public void RaisePropertiesChanged()
+ try
{
- RaisePropertyChanged(nameof(BottlesInStock));
- RaisePropertyChanged(nameof(OwnedBottles));
- RaisePropertyChanged(nameof(ConsumedVolume));
- RaisePropertyChanged(nameof(ConsumedVolumeLiters));
- RaisePropertyChanged(nameof(VolumeInStock));
- RaisePropertyChanged(nameof(VolumeInStockLiters));
- RaisePropertyChanged(nameof(TotalCost));
- RaisePropertyChanged(nameof(PrintTime));
- RaisePropertyChanged(nameof(PrintTimeSpan));
- RaisePropertyChanged(nameof(Count));
+ var instance = XmlExtensions.DeserializeFromFile<MaterialManager>(FilePath);
+ _instanceHolder = new Lazy<MaterialManager>(() => instance);
}
-
- /// <summary>
- /// Load settings from file
- /// </summary>
- public static void Load()
+ catch (Exception e)
{
- if (string.IsNullOrWhiteSpace(FilePath) || !File.Exists(FilePath))
- {
- return;
- }
-
- var serializer = new XmlSerializer(Instance.GetType());
- try
- {
- using var myXmlReader = new StreamReader(FilePath);
- var instance = (MaterialManager)serializer.Deserialize(myXmlReader);
- _instanceHolder = new Lazy<MaterialManager>(() => instance);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.ToString());
- }
+ Debug.WriteLine(e.ToString());
}
+ }
- /// <summary>
- /// Save settings to file
- /// </summary>
- public static void Save()
+ /// <summary>
+ /// Save settings to file
+ /// </summary>
+ public static void Save()
+ {
+ try
{
- var serializer = new XmlSerializer(Instance.GetType());
- try
- {
- using var myXmlWriter = new StreamWriter(FilePath);
- serializer.Serialize(myXmlWriter, Instance);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.ToString());
- }
+ XmlExtensions.SerializeToFile(Instance, FilePath, XmlExtensions.SettingsIndent);
}
-
- public void SortByName()
+ catch (Exception e)
{
- _materials.Sort((material, material1) => string.Compare(material.Name, material1.Name, StringComparison.Ordinal));
+ Debug.WriteLine(e.ToString());
}
+ }
- #endregion
+ public void SortByName()
+ {
+ _materials.Sort((material, material1) => string.Compare(material.Name, material1.Name, StringComparison.Ordinal));
}
+
+ #endregion
} \ No newline at end of file
diff --git a/UVtools.Core/Managers/OperationSessionManager.cs b/UVtools.Core/Managers/OperationSessionManager.cs
index 02b18bc..787b261 100644
--- a/UVtools.Core/Managers/OperationSessionManager.cs
+++ b/UVtools.Core/Managers/OperationSessionManager.cs
@@ -11,121 +11,120 @@ using System.Collections.Generic;
using System.Linq;
using UVtools.Core.Operations;
-namespace UVtools.Core.Managers
+namespace UVtools.Core.Managers;
+
+public class OperationSessionManager : IList<Operation>
{
- public class OperationSessionManager : IList<Operation>
- {
- #region Settings
+ #region Settings
- //public static string FilePath;
- #endregion
+ //public static string FilePath;
+ #endregion
- #region Singleton
+ #region Singleton
- private static Lazy<OperationSessionManager> _instanceHolder =
- new(() => new OperationSessionManager());
+ private static readonly Lazy<OperationSessionManager> _instanceHolder =
+ new(() => new OperationSessionManager());
- public static OperationSessionManager Instance => _instanceHolder.Value;
+ public static OperationSessionManager Instance => _instanceHolder.Value;
- #endregion
+ #endregion
- #region Members
+ #region Members
- private readonly List<Operation> _operations = new();
+ private readonly List<Operation> _operations = new();
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- #endregion
-
- #region Constructor
- private OperationSessionManager()
- {
- }
- #endregion
-
- #region Methods
-
- public Operation Find(Type type)
- {
- return this.FirstOrDefault(operation => operation.GetType() == type);
- }
-
- public Operation Find(Operation fromOperation) => Find(fromOperation.GetType());
-
- #endregion
-
- #region List Implementation
- public IEnumerator<Operation> GetEnumerator()
- {
- return _operations.GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return ((IEnumerable) _operations).GetEnumerator();
- }
-
- public void Add(Operation item)
- {
- if (item is null) return;
- _operations.RemoveAll(operation => operation.GetType() == item.GetType());
- var operation = item.Clone();
- operation.ClearROIandMasks();
- operation.ImportedFrom = Operation.OperationImportFrom.Session;
- _operations.Add(operation);
- }
-
- public void Clear()
- {
- _operations.Clear();
- }
-
- public bool Contains(Operation item)
- {
- return _operations.Contains(item);
- }
-
- public void CopyTo(Operation[] array, int arrayIndex)
- {
- _operations.CopyTo(array, arrayIndex);
- }
-
- public bool Remove(Operation item)
- {
- return _operations.Remove(item);
- }
-
- public int Count => _operations.Count;
-
- public bool IsReadOnly => ((ICollection<Operation>) _operations).IsReadOnly;
-
- public int IndexOf(Operation item)
- {
- return _operations.IndexOf(item);
- }
-
- public void Insert(int index, Operation item)
- {
- if (item is null) return;
- _operations.RemoveAll(operation => operation.GetType() == item.GetType());
- var operation = item.Clone();
- operation.ImportedFrom = Operation.OperationImportFrom.Session;
- _operations.Insert(index, operation);
- }
-
- public void RemoveAt(int index)
- {
- _operations.RemoveAt(index);
- }
-
- public Operation this[int index]
- {
- get => _operations[index];
- set => _operations[index] = value;
- }
- #endregion
+ #endregion
+
+ #region Constructor
+ private OperationSessionManager()
+ {
+ }
+ #endregion
+
+ #region Methods
+
+ public Operation? Find(Type type)
+ {
+ return this.FirstOrDefault(operation => operation.GetType() == type);
+ }
+
+ public Operation? Find(Operation fromOperation) => Find(fromOperation.GetType());
+
+ #endregion
+
+ #region List Implementation
+ public IEnumerator<Operation> GetEnumerator()
+ {
+ return _operations.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable) _operations).GetEnumerator();
+ }
+
+ public void Add(Operation item)
+ {
+ if (item is null) return;
+ _operations.RemoveAll(operation => operation.GetType() == item.GetType());
+ var operation = item.Clone();
+ operation.ClearROIandMasks();
+ operation.ImportedFrom = Operation.OperationImportFrom.Session;
+ _operations.Add(operation);
+ }
+
+ public void Clear()
+ {
+ _operations.Clear();
+ }
+
+ public bool Contains(Operation item)
+ {
+ return _operations.Contains(item);
+ }
+
+ public void CopyTo(Operation[] array, int arrayIndex)
+ {
+ _operations.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(Operation item)
+ {
+ return _operations.Remove(item);
+ }
+
+ public int Count => _operations.Count;
+
+ public bool IsReadOnly => ((ICollection<Operation>) _operations).IsReadOnly;
+
+ public int IndexOf(Operation item)
+ {
+ return _operations.IndexOf(item);
+ }
+
+ public void Insert(int index, Operation item)
+ {
+ if (item is null) return;
+ _operations.RemoveAll(operation => operation.GetType() == item.GetType());
+ var operation = item.Clone();
+ operation.ImportedFrom = Operation.OperationImportFrom.Session;
+ _operations.Insert(index, operation);
+ }
+
+ public void RemoveAt(int index)
+ {
+ _operations.RemoveAt(index);
+ }
+
+ public Operation this[int index]
+ {
+ get => _operations[index];
+ set => _operations[index] = value;
}
+ #endregion
} \ No newline at end of file
diff --git a/UVtools.Core/Managers/SuggestionManager.cs b/UVtools.Core/Managers/SuggestionManager.cs
new file mode 100644
index 0000000..af7c7c7
--- /dev/null
+++ b/UVtools.Core/Managers/SuggestionManager.cs
@@ -0,0 +1,168 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Xml.Serialization;
+using UVtools.Core;
+using UVtools.Core.Extensions;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Structures;
+
+[Serializable]
+public class SuggestionManager
+{
+ #region Properties
+ /// <summary>
+ /// Default filepath for store
+ /// </summary>
+ public static string FilePath => Path.Combine(CoreSettings.DefaultSettingsFolderAndEnsureCreation, "suggestions.xml");
+
+ public SuggestionBottomLayerCount BottomLayerCount { get; set; } = new();
+ public SuggestionWaitTimeBeforeCure WaitTimeBeforeCure { get; set; } = new();
+ public SuggestionWaitTimeAfterCure WaitTimeAfterCure { get; set; } = new();
+ public SuggestionLayerHeight LayerHeight { get; set; } = new();
+
+ /// <summary>
+ /// Gets all suggestions
+ /// </summary>
+ [XmlIgnore]
+ public Suggestion[] Suggestions
+ {
+ get
+ {
+ return new Suggestion[]
+ {
+ BottomLayerCount,
+ WaitTimeBeforeCure,
+ WaitTimeAfterCure,
+ LayerHeight
+ };
+ }
+ set
+ {
+ foreach (var suggestion in value)
+ {
+ Set(suggestion);
+ }
+
+ Save();
+ }
+ }
+
+ public void Set(Suggestion suggestion)
+ {
+ switch (suggestion)
+ {
+ case SuggestionBottomLayerCount bottomLayerCount:
+ BottomLayerCount = bottomLayerCount;
+ break;
+ case SuggestionWaitTimeBeforeCure waitTimeBeforeCure:
+ WaitTimeBeforeCure = waitTimeBeforeCure;
+ break;
+ case SuggestionWaitTimeAfterCure waitTimeAfterCure:
+ WaitTimeAfterCure = waitTimeAfterCure;
+ break;
+ case SuggestionLayerHeight layerHeight:
+ LayerHeight = layerHeight;
+ break;
+ default: throw new ArgumentOutOfRangeException(nameof(suggestion));
+ }
+ }
+
+ public Suggestion? Get(Type type)
+ {
+ return Suggestions.FirstOrDefault(suggestion => suggestion.GetType() == type);
+ }
+
+ #endregion
+
+ #region Singleton
+
+ private static Lazy<SuggestionManager> _instanceHolder =
+ new(() => new SuggestionManager());
+
+ /// <summary>
+ /// Instance (singleton)
+ /// </summary>
+ public static SuggestionManager Instance => _instanceHolder.Value;
+
+ //public static List<Operation> Operations => _instance.Operations;
+ #endregion
+
+ #region Constructor
+
+ private SuggestionManager()
+ { }
+
+ #endregion
+
+ #region Static Methods
+
+ public static void SetSuggestion(Suggestion suggestion, bool save = false)
+ {
+ Instance.Set(suggestion);
+ if(save) Save();
+ }
+
+ public static Suggestion? GetSuggestion(Type type)
+ {
+ return Instance.Get(type);
+ }
+
+ /// <summary>
+ /// Reset to defaults
+ /// </summary>
+ /// <param name="save">True to save settings on file, otherwise false</param>
+ public static void Reset(bool save = true)
+ {
+ _instanceHolder = new Lazy<SuggestionManager>(() => new SuggestionManager());
+ if (save) Save();
+ }
+
+
+ /// <summary>
+ /// Load settings from file
+ /// </summary>
+ public static void Load()
+ {
+ if (!File.Exists(FilePath))
+ {
+ return;
+ }
+
+ try
+ {
+ var instance = XmlExtensions.DeserializeFromFile<SuggestionManager>(FilePath);
+ _instanceHolder = new Lazy<SuggestionManager>(() => instance);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ }
+ }
+
+ /// <summary>
+ /// Save settings to file
+ /// </summary>
+ public static void Save()
+ {
+ try
+ {
+ XmlExtensions.SerializeToFile(Instance, FilePath, XmlExtensions.SettingsIndent);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ }
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/AMFMeshFile.cs b/UVtools.Core/MeshFormats/AMFMeshFile.cs
index ab715d0..b4e43f4 100644
--- a/UVtools.Core/MeshFormats/AMFMeshFile.cs
+++ b/UVtools.Core/MeshFormats/AMFMeshFile.cs
@@ -13,127 +13,126 @@ using System.Numerics;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public class AMFMeshFile : MeshFile
{
- public class AMFMeshFile : MeshFile
- {
- #region Members
- private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
- private FileStream _triangleStream;
- #endregion
+ #region Members
+ private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
+ private FileStream _triangleStream = null!;
+ #endregion
- #region Properties
- public static FileExtension FileExtension => new(typeof(AMFMeshFile), "amf", "Additive Manufacturing Format");
- #endregion
+ #region Properties
+ public static FileExtension FileExtension => new(typeof(AMFMeshFile), "amf", "Additive Manufacturing Format");
+ #endregion
- #region Constructor
- public AMFMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
+ #region Constructor
+ public AMFMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat? slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
- #endregion
+ #endregion
- #region Methods
- public override void BeginWrite()
- {
- /* Create a stream to store the triangles (faces) as they come through */
- _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
+ #region Methods
+ public override void BeginWrite()
+ {
+ /* Create a stream to store the triangles (faces) as they come through */
+ _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
- MeshStream.WriteLineLF("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
- MeshStream.WriteLineLF("<amf unit=\"millimeter\" version=\"1.1\">");
- MeshStream.WriteLineLF($"\t<metadata type=\"name\">{FilenameWithoutExtension}</metadata>");
- MeshStream.WriteLineLF($"\t<metadata type=\"author\">{HeaderComment}</metadata>");
- MeshStream.WriteLineLF("\t<object id=\"0\">");
- MeshStream.WriteLineLF("\t\t<mesh>");
- MeshStream.WriteLineLF("\t\t\t<vertices>");
+ MeshStream.WriteLineLF("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ MeshStream.WriteLineLF("<amf unit=\"millimeter\" version=\"1.1\">");
+ MeshStream.WriteLineLF($"\t<metadata type=\"name\">{FilenameWithoutExtension}</metadata>");
+ MeshStream.WriteLineLF($"\t<metadata type=\"author\">{HeaderComment}</metadata>");
+ MeshStream.WriteLineLF("\t<object id=\"0\">");
+ MeshStream.WriteLineLF("\t\t<mesh>");
+ MeshStream.WriteLineLF("\t\t\t<vertices>");
+ }
+
+ public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ {
+ if (!_vertexCache.ContainsKey(p1))
+ {
+ MeshStream.WriteLineLF($"\t\t\t\t<vertex>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t<coordinates>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<x>{p1.X:F6}</x>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<y>{p1.Y:F6}</y>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<z>{p1.Z:F6}</z>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t</coordinates>");
+ MeshStream.WriteLineLF($"\t\t\t\t</vertex>");
+ _vertexCache.Add(p1, VertexCount);
+ VertexCount++;
}
- public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ if (!_vertexCache.ContainsKey(p2))
{
- if (!_vertexCache.ContainsKey(p1))
- {
- MeshStream.WriteLineLF($"\t\t\t\t<vertex>");
- MeshStream.WriteLineLF($"\t\t\t\t\t<coordinates>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<x>{p1.X:F6}</x>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<y>{p1.Y:F6}</y>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<z>{p1.Z:F6}</z>");
- MeshStream.WriteLineLF($"\t\t\t\t\t</coordinates>");
- MeshStream.WriteLineLF($"\t\t\t\t</vertex>");
- _vertexCache.Add(p1, VertexCount);
- VertexCount++;
- }
-
- if (!_vertexCache.ContainsKey(p2))
- {
- MeshStream.WriteLineLF($"\t\t\t\t<vertex>");
- MeshStream.WriteLineLF($"\t\t\t\t\t<coordinates>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<x>{p2.X:F6}</x>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<y>{p2.Y:F6}</y>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<z>{p2.Z:F6}</z>");
- MeshStream.WriteLineLF($"\t\t\t\t\t</coordinates>");
- MeshStream.WriteLineLF($"\t\t\t\t</vertex>");
- _vertexCache.Add(p2, VertexCount);
- VertexCount++;
- }
-
- if (!_vertexCache.ContainsKey(p3))
- {
- MeshStream.WriteLineLF($"\t\t\t\t<vertex>");
- MeshStream.WriteLineLF($"\t\t\t\t\t<coordinates>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<x>{p3.X:F6}</x>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<y>{p3.Y:F6}</y>");
- MeshStream.WriteLineLF($"\t\t\t\t\t\t<z>{p3.Z:F6}</z>");
- MeshStream.WriteLineLF($"\t\t\t\t\t</coordinates>");
- MeshStream.WriteLineLF($"\t\t\t\t</vertex>");
- _vertexCache.Add(p3, VertexCount);
- VertexCount++;
- }
-
- _triangleStream.WriteLineLF($"\t\t\t\t<triangle>");
- _triangleStream.WriteLineLF($"\t\t\t\t\t<v1>{_vertexCache[p1]}</v1>");
- _triangleStream.WriteLineLF($"\t\t\t\t\t<v2>{_vertexCache[p2]}</v2>");
- _triangleStream.WriteLineLF($"\t\t\t\t\t<v3>{_vertexCache[p3]}</v3>");
- _triangleStream.WriteLineLF($"\t\t\t\t</triangle>");
-
- TriangleCount++;
-
- /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
- * an allocation of a bigger buffer and copy of the kvp's */
- if (_vertexCache.Count >= VertexCacheSize - 10)
- {
- _vertexCache.Clear();
- }
+ MeshStream.WriteLineLF($"\t\t\t\t<vertex>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t<coordinates>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<x>{p2.X:F6}</x>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<y>{p2.Y:F6}</y>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<z>{p2.Z:F6}</z>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t</coordinates>");
+ MeshStream.WriteLineLF($"\t\t\t\t</vertex>");
+ _vertexCache.Add(p2, VertexCount);
+ VertexCount++;
}
- public override void EndWrite()
+ if (!_vertexCache.ContainsKey(p3))
{
- _vertexCache.Clear();
+ MeshStream.WriteLineLF($"\t\t\t\t<vertex>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t<coordinates>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<x>{p3.X:F6}</x>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<y>{p3.Y:F6}</y>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t\t<z>{p3.Z:F6}</z>");
+ MeshStream.WriteLineLF($"\t\t\t\t\t</coordinates>");
+ MeshStream.WriteLineLF($"\t\t\t\t</vertex>");
+ _vertexCache.Add(p3, VertexCount);
+ VertexCount++;
+ }
+
+ _triangleStream.WriteLineLF($"\t\t\t\t<triangle>");
+ _triangleStream.WriteLineLF($"\t\t\t\t\t<v1>{_vertexCache[p1]}</v1>");
+ _triangleStream.WriteLineLF($"\t\t\t\t\t<v2>{_vertexCache[p2]}</v2>");
+ _triangleStream.WriteLineLF($"\t\t\t\t\t<v3>{_vertexCache[p3]}</v3>");
+ _triangleStream.WriteLineLF($"\t\t\t\t</triangle>");
- MeshStream.WriteLineLF("\t\t\t</vertices>");
- MeshStream.WriteLineLF("\t\t\t<volume>");
- MeshStream.WriteLineLF("\t\t\t\t<metadata type=\"name\">Model</metadata>");
-
- _triangleStream.Seek(0, SeekOrigin.Begin);
- _triangleStream.CopyTo(MeshStream);
- _triangleStream.Dispose();
-
- MeshStream.WriteLineLF("\t\t\t</volume>");
- MeshStream.WriteLineLF("\t\t</mesh>");
- MeshStream.WriteLineLF("\t</object>");
- MeshStream.WriteLineLF("</amf>");
-
- var tmpFile = PathExtensions.GetTempFilePathWithExtension("tmp", $"{About.Software}_");
- if (File.Exists(tmpFile)) File.Delete(tmpFile);
- using (var zip = ZipFile.Open(tmpFile, ZipArchiveMode.Create))
- {
- MeshStream.Seek(0, SeekOrigin.Begin);
- zip.PutFileContent(Filename, MeshStream, ZipArchiveMode.Create);
- }
- MeshStream.Dispose();
+ TriangleCount++;
- File.Move(tmpFile, FilePath, true);
+ /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
+ * an allocation of a bigger buffer and copy of the kvp's */
+ if (_vertexCache.Count >= VertexCacheSize - 10)
+ {
+ _vertexCache.Clear();
}
+ }
+
+ public override void EndWrite()
+ {
+ _vertexCache.Clear();
+
+ MeshStream.WriteLineLF("\t\t\t</vertices>");
+ MeshStream.WriteLineLF("\t\t\t<volume>");
+ MeshStream.WriteLineLF("\t\t\t\t<metadata type=\"name\">Model</metadata>");
- #endregion
+ _triangleStream.Seek(0, SeekOrigin.Begin);
+ _triangleStream.CopyTo(MeshStream);
+ _triangleStream.Dispose();
+
+ MeshStream.WriteLineLF("\t\t\t</volume>");
+ MeshStream.WriteLineLF("\t\t</mesh>");
+ MeshStream.WriteLineLF("\t</object>");
+ MeshStream.WriteLineLF("</amf>");
+
+ var tmpFile = PathExtensions.GetTempFilePathWithExtension("tmp", $"{About.Software}_");
+ if (File.Exists(tmpFile)) File.Delete(tmpFile);
+ using (var zip = ZipFile.Open(tmpFile, ZipArchiveMode.Create))
+ {
+ MeshStream.Seek(0, SeekOrigin.Begin);
+ zip.PutFileContent(Filename, MeshStream, ZipArchiveMode.Create);
+ }
+ MeshStream.Dispose();
+
+ File.Move(tmpFile, FilePath, true);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/Consortium3MFMeshFile.cs b/UVtools.Core/MeshFormats/Consortium3MFMeshFile.cs
index 5e681ef..01a1601 100644
--- a/UVtools.Core/MeshFormats/Consortium3MFMeshFile.cs
+++ b/UVtools.Core/MeshFormats/Consortium3MFMeshFile.cs
@@ -13,132 +13,131 @@ using System.Numerics;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public class Consortium3MFMeshFile : MeshFile
{
- public class Consortium3MFMeshFile : MeshFile
- {
- #region Members
- private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
- private FileStream _triangleStream;
- #endregion
+ #region Members
+ private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
+ private FileStream _triangleStream = null!;
+ #endregion
- #region Properties
- public static FileExtension FileExtension => new(typeof(Consortium3MFMeshFile), "3mf", "3D Manufacturing Format");
- #endregion
+ #region Properties
+ public static FileExtension FileExtension => new(typeof(Consortium3MFMeshFile), "3mf", "3D Manufacturing Format");
+ #endregion
- #region Constructor
- public Consortium3MFMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
+ #region Constructor
+ public Consortium3MFMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat? slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
- #endregion
+ #endregion
- #region Methods
- public override void BeginWrite()
- {
- /* Create a stream to store the triangles (faces) as they come through */
- _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
+ #region Methods
+ public override void BeginWrite()
+ {
+ /* Create a stream to store the triangles (faces) as they come through */
+ _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
- MeshStream.WriteLineLF("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
- MeshStream.WriteLineLF("<model unit=\"millimeter\" xml:lang=\"en-US\" xmlns:m=\"http://schemas.microsoft.com/3dmanufacturing/material/2015/02\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">");
- MeshStream.WriteLineLF("\t<metadata name=\"Copyright\">");
- MeshStream.WriteLineLF($"\t\t{HeaderComment}");
- MeshStream.WriteLineLF("\t</metadata>");
- MeshStream.WriteLineLF("\t<resources>");
- MeshStream.WriteLineLF("\t\t<object id=\"1\" type=\"model\">");
- MeshStream.WriteLineLF("\t\t\t<mesh>");
- MeshStream.WriteLineLF("\t\t\t\t<vertices>");
- }
+ MeshStream.WriteLineLF("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ MeshStream.WriteLineLF("<model unit=\"millimeter\" xml:lang=\"en-US\" xmlns:m=\"http://schemas.microsoft.com/3dmanufacturing/material/2015/02\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">");
+ MeshStream.WriteLineLF("\t<metadata name=\"Copyright\">");
+ MeshStream.WriteLineLF($"\t\t{HeaderComment}");
+ MeshStream.WriteLineLF("\t</metadata>");
+ MeshStream.WriteLineLF("\t<resources>");
+ MeshStream.WriteLineLF("\t\t<object id=\"1\" type=\"model\">");
+ MeshStream.WriteLineLF("\t\t\t<mesh>");
+ MeshStream.WriteLineLF("\t\t\t\t<vertices>");
+ }
- public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ {
+ if (!_vertexCache.ContainsKey(p1))
{
- if (!_vertexCache.ContainsKey(p1))
- {
- MeshStream.WriteLineLF($"\t\t\t\t\t<vertex x=\"{p1.X:F6}\" y=\"{p1.Y:F6}\" z=\"{p1.Z:F6}\" />");
- _vertexCache.Add(p1, VertexCount);
- VertexCount++;
- }
+ MeshStream.WriteLineLF($"\t\t\t\t\t<vertex x=\"{p1.X:F6}\" y=\"{p1.Y:F6}\" z=\"{p1.Z:F6}\" />");
+ _vertexCache.Add(p1, VertexCount);
+ VertexCount++;
+ }
- if (!_vertexCache.ContainsKey(p2))
- {
- MeshStream.WriteLineLF($"\t\t\t\t\t<vertex x=\"{p2.X:F6}\" y=\"{p2.Y:F6}\" z=\"{p2.Z:F6}\" />");
- _vertexCache.Add(p2, VertexCount);
- VertexCount++;
- }
+ if (!_vertexCache.ContainsKey(p2))
+ {
+ MeshStream.WriteLineLF($"\t\t\t\t\t<vertex x=\"{p2.X:F6}\" y=\"{p2.Y:F6}\" z=\"{p2.Z:F6}\" />");
+ _vertexCache.Add(p2, VertexCount);
+ VertexCount++;
+ }
- if (!_vertexCache.ContainsKey(p3))
- {
- MeshStream.WriteLineLF($"\t\t\t\t\t<vertex x=\"{p3.X:F6}\" y=\"{p3.Y:F6}\" z=\"{p3.Z:F6}\" />");
- _vertexCache.Add(p3, VertexCount);
- VertexCount++;
- }
+ if (!_vertexCache.ContainsKey(p3))
+ {
+ MeshStream.WriteLineLF($"\t\t\t\t\t<vertex x=\"{p3.X:F6}\" y=\"{p3.Y:F6}\" z=\"{p3.Z:F6}\" />");
+ _vertexCache.Add(p3, VertexCount);
+ VertexCount++;
+ }
- _triangleStream.WriteLineLF($"\t\t\t\t\t<triangle v1=\"{_vertexCache[p1]}\" v2=\"{_vertexCache[p2]}\" v3=\"{_vertexCache[p3]}\" />");
+ _triangleStream.WriteLineLF($"\t\t\t\t\t<triangle v1=\"{_vertexCache[p1]}\" v2=\"{_vertexCache[p2]}\" v3=\"{_vertexCache[p3]}\" />");
- TriangleCount++;
+ TriangleCount++;
- /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
- * an allocation of a bigger buffer and copy of the kvp's */
- if (_vertexCache.Count >= VertexCacheSize - 10)
- {
- _vertexCache.Clear();
- }
- }
-
- public override void EndWrite()
+ /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
+ * an allocation of a bigger buffer and copy of the kvp's */
+ if (_vertexCache.Count >= VertexCacheSize - 10)
{
_vertexCache.Clear();
+ }
+ }
- MeshStream.WriteLineLF("\t\t\t\t</vertices>");
- MeshStream.WriteLineLF("\t\t\t\t<triangles>");
-
- _triangleStream.Seek(0, SeekOrigin.Begin);
- _triangleStream.CopyTo(MeshStream);
- _triangleStream.Dispose();
-
- MeshStream.WriteLineLF("\t\t\t\t</triangles>");
- MeshStream.WriteLineLF("\t\t\t</mesh>");
- MeshStream.WriteLineLF("\t\t</object>");
- MeshStream.WriteLineLF("\t</resources>");
- MeshStream.WriteLineLF("\t<build>");
- MeshStream.WriteLineLF("\t\t<item objectid=\"1\" />");
- MeshStream.WriteLineLF("\t</build>");
- MeshStream.WriteLineLF("</model>");
-
- var tmpFile = PathExtensions.GetTempFilePathWithExtension("tmp", $"{About.Software}_");
- if (File.Exists(tmpFile)) File.Delete(tmpFile);
- bool haveThumbnail = SlicerFile is not null && SlicerFile.CreatedThumbnailsCount > 0;
- using (var zip = ZipFile.Open(tmpFile, ZipArchiveMode.Create))
- {
- zip.PutFileContent("[Content_Types].xml", "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
- "\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">" +
- //"\n\t<Default Extension=\"jpeg\" ContentType=\"image/jpeg\" />" +
- //"\n\t<Default Extension=\"jpg\" ContentType=\"image/jpeg\" />" +
- "\n\t<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" />" +
- "\n\t<Default Extension=\"model\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\" />" +
- "\n\t<Default Extension=\"png\" ContentType=\"image/png\" />" +
- //"\n\t<Default Extension=\"texture\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodeltexture\" />" +
- "\n</Types>\n", ZipArchiveMode.Create);
- zip.PutFileContent("_rels/.rels", "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
- "\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" +
- "\n\t<Relationship Target=\"/3D/3dmodel.model\" Id=\"rel0\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\" />" +
- (haveThumbnail ? "\n\t<Relationship Target=\"/Metadata/thumbnail.png\" Id=\"rel1\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\" />" : string.Empty) +
- "\n</Relationships>\r\n", ZipArchiveMode.Create);
+ public override void EndWrite()
+ {
+ _vertexCache.Clear();
+
+ MeshStream.WriteLineLF("\t\t\t\t</vertices>");
+ MeshStream.WriteLineLF("\t\t\t\t<triangles>");
+
+ _triangleStream.Seek(0, SeekOrigin.Begin);
+ _triangleStream.CopyTo(MeshStream);
+ _triangleStream.Dispose();
+
+ MeshStream.WriteLineLF("\t\t\t\t</triangles>");
+ MeshStream.WriteLineLF("\t\t\t</mesh>");
+ MeshStream.WriteLineLF("\t\t</object>");
+ MeshStream.WriteLineLF("\t</resources>");
+ MeshStream.WriteLineLF("\t<build>");
+ MeshStream.WriteLineLF("\t\t<item objectid=\"1\" />");
+ MeshStream.WriteLineLF("\t</build>");
+ MeshStream.WriteLineLF("</model>");
+
+ var tmpFile = PathExtensions.GetTempFilePathWithExtension("tmp", $"{About.Software}_");
+ if (File.Exists(tmpFile)) File.Delete(tmpFile);
+ bool haveThumbnail = SlicerFile is not null && SlicerFile.CreatedThumbnailsCount > 0;
+ using (var zip = ZipFile.Open(tmpFile, ZipArchiveMode.Create))
+ {
+ zip.PutFileContent("[Content_Types].xml", "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+ "\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">" +
+ //"\n\t<Default Extension=\"jpeg\" ContentType=\"image/jpeg\" />" +
+ //"\n\t<Default Extension=\"jpg\" ContentType=\"image/jpeg\" />" +
+ "\n\t<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" />" +
+ "\n\t<Default Extension=\"model\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\" />" +
+ "\n\t<Default Extension=\"png\" ContentType=\"image/png\" />" +
+ //"\n\t<Default Extension=\"texture\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodeltexture\" />" +
+ "\n</Types>\n", ZipArchiveMode.Create);
+ zip.PutFileContent("_rels/.rels", "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
+ "\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">" +
+ "\n\t<Relationship Target=\"/3D/3dmodel.model\" Id=\"rel0\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\" />" +
+ (haveThumbnail ? "\n\t<Relationship Target=\"/Metadata/thumbnail.png\" Id=\"rel1\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\" />" : string.Empty) +
+ "\n</Relationships>\r\n", ZipArchiveMode.Create);
- MeshStream.Seek(0, SeekOrigin.Begin);
- zip.PutFileContent("3D/3dmodel.model", MeshStream, ZipArchiveMode.Create);
+ MeshStream.Seek(0, SeekOrigin.Begin);
+ zip.PutFileContent("3D/3dmodel.model", MeshStream, ZipArchiveMode.Create);
- if (haveThumbnail)
- {
- var mat = SlicerFile.GetThumbnail(true);
- zip.PutFileContent("Metadata/thumbnail.png", mat.GetPngByes(), ZipArchiveMode.Create);
- }
+ if (haveThumbnail)
+ {
+ var mat = SlicerFile!.GetThumbnail(true);
+ zip.PutFileContent("Metadata/thumbnail.png", mat!.GetPngByes(), ZipArchiveMode.Create);
}
- MeshStream.Dispose();
-
- File.Move(tmpFile, FilePath, true);
}
-
- #endregion
+ MeshStream.Dispose();
+
+ File.Move(tmpFile, FilePath, true);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/MeshFile.cs b/UVtools.Core/MeshFormats/MeshFile.cs
index 595a0c5..0b02f5c 100644
--- a/UVtools.Core/MeshFormats/MeshFile.cs
+++ b/UVtools.Core/MeshFormats/MeshFile.cs
@@ -14,130 +14,129 @@ using System.Numerics;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public abstract class MeshFile : IDisposable
{
- public abstract class MeshFile : IDisposable
+ #region Constants
+ public const int VertexCacheSize = 84000; // About 1MB
+ #endregion
+
+ #region Static
+
+ public static readonly FileExtension[] AvailableMeshFiles =
{
- #region Constants
- public const int VertexCacheSize = 84000; // About 1MB
- #endregion
-
- #region Static
-
- public static readonly FileExtension[] AvailableMeshFiles =
- {
- STLMeshFile.FileExtension,
- Consortium3MFMeshFile.FileExtension,
- AMFMeshFile.FileExtension,
- WRLMeshFile.FileExtension,
- OBJMeshFile.FileExtension,
- PLYMeshFile.FileExtension,
- OFFMeshFile.FileExtension,
- };
-
- public static string HeaderComment => $"Exported from {About.SoftwareWithVersion} @ {DateTime.UtcNow:u}";
-
- public static FileExtension FindFileExtension(string filePath)
- {
- var ext = Path.GetExtension(filePath);
- return AvailableMeshFiles.FirstOrDefault(fileExtension => $".{fileExtension.Extension}" == ext);
- }
-
- public static MeshFile CreateInstance(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.BINARY, FileFormat slicerFile = null)
- {
- var fileExtension = FindFileExtension(filePath);
- return fileExtension?.FileFormatType.CreateInstance<MeshFile>(filePath, fileMode, fileFormat, slicerFile);
- }
-
- #endregion
-
- #region Enums
-
- public enum MeshFileFormat : byte
- {
- [Description("Binary")]
- BINARY,
- [Description("ASCII")]
- ASCII
- }
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Gets the file format for this mesh
- /// </summary>
- public MeshFileFormat FileFormat { get; } = MeshFileFormat.BINARY;
-
- /// <summary>
- /// Gets the <see cref="FileFormat"/> from model export
- /// </summary>
- public FileFormat SlicerFile { get; }
-
- /// <summary>
- /// Gets the file path of the stream
- /// </summary>
- public string FilePath { get; }
-
- /// <summary>
- /// Gets the file name with extension from <see cref="FilePath"/>
- /// </summary>
- public string Filename => Path.GetFileName(FilePath);
- public string FilenameWithoutExtension => Path.GetFileNameWithoutExtension(FilePath);
-
- /// <summary>
- /// Gets the current file stream
- /// </summary>
- public FileStream MeshStream { get; }
-
- /// <summary>
- /// Gets the number of vertexes, this is often triangle count * 3
- /// </summary>
- public uint VertexCount { get; protected set; }
-
- /// <summary>
- /// Gets the number of triangles
- /// </summary>
- public uint TriangleCount { get; protected set; }
- #endregion
+ STLMeshFile.FileExtension,
+ Consortium3MFMeshFile.FileExtension,
+ AMFMeshFile.FileExtension,
+ WRLMeshFile.FileExtension,
+ OBJMeshFile.FileExtension,
+ PLYMeshFile.FileExtension,
+ OFFMeshFile.FileExtension,
+ };
+
+ public static string HeaderComment => $"Exported from {About.SoftwareWithVersion} @ {DateTime.UtcNow:u}";
+
+ public static FileExtension? FindFileExtension(string filePath)
+ {
+ var ext = Path.GetExtension(filePath);
+ return AvailableMeshFiles.FirstOrDefault(fileExtension => $".{fileExtension.Extension}" == ext);
+ }
+
+ public static MeshFile? CreateInstance(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.BINARY, FileFormat? slicerFile = null)
+ {
+ var fileExtension = FindFileExtension(filePath);
+ return fileExtension?.FileFormatType.CreateInstance<MeshFile>(filePath, fileMode, fileFormat, slicerFile!);
+ }
+
+ #endregion
+
+ #region Enums
+
+ public enum MeshFileFormat : byte
+ {
+ [Description("Binary")]
+ BINARY,
+ [Description("ASCII")]
+ ASCII
+ }
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets the file format for this mesh
+ /// </summary>
+ public MeshFileFormat FileFormat { get; } = MeshFileFormat.BINARY;
+
+ /// <summary>
+ /// Gets the <see cref="FileFormat"/> from model export
+ /// </summary>
+ public FileFormat? SlicerFile { get; }
+
+ /// <summary>
+ /// Gets the file path of the stream
+ /// </summary>
+ public string FilePath { get; }
+
+ /// <summary>
+ /// Gets the file name with extension from <see cref="FilePath"/>
+ /// </summary>
+ public string Filename => Path.GetFileName(FilePath);
+ public string FilenameWithoutExtension => Path.GetFileNameWithoutExtension(FilePath);
+
+ /// <summary>
+ /// Gets the current file stream
+ /// </summary>
+ public FileStream MeshStream { get; }
+
+ /// <summary>
+ /// Gets the number of vertexes, this is often triangle count * 3
+ /// </summary>
+ public uint VertexCount { get; protected set; }
+
+ /// <summary>
+ /// Gets the number of triangles
+ /// </summary>
+ public uint TriangleCount { get; protected set; }
+ #endregion
- #region Constructor
- protected MeshFile(string filePath, FileMode fileMode, MeshFileFormat meshFileFormat = MeshFileFormat.BINARY, FileFormat slicerFile = null)
- {
- FilePath = filePath;
- FileFormat = meshFileFormat;
- SlicerFile = slicerFile;
- MeshStream = new FileStream(filePath, fileMode);
- }
- #endregion
-
- #region Methods
- /// <summary>
- /// Call once before write content to the file, use this to build up the header if any
- /// </summary>
- public virtual void BeginWrite(){}
-
- /// <summary>
- /// Writes an triangle to the file
- /// </summary>
- /// <param name="p1"></param>
- /// <param name="p2"></param>
- /// <param name="p3"></param>
- /// <param name="normal"></param>
- public abstract void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal);
-
- /// <summary>
- /// Call once before close the file, use this to build up the footer if any
- /// </summary>
- public virtual void EndWrite(){}
-
-
- /// <inheritdoc />
- public void Dispose()
- {
- MeshStream?.Dispose();
- }
- #endregion
+ #region Constructor
+ protected MeshFile(string filePath, FileMode fileMode, MeshFileFormat meshFileFormat = MeshFileFormat.BINARY, FileFormat? slicerFile = null)
+ {
+ FilePath = filePath;
+ FileFormat = meshFileFormat;
+ SlicerFile = slicerFile;
+ MeshStream = new FileStream(filePath, fileMode);
+ }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Call once before write content to the file, use this to build up the header if any
+ /// </summary>
+ public virtual void BeginWrite(){}
+
+ /// <summary>
+ /// Writes an triangle to the file
+ /// </summary>
+ /// <param name="p1"></param>
+ /// <param name="p2"></param>
+ /// <param name="p3"></param>
+ /// <param name="normal"></param>
+ public abstract void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal);
+
+ /// <summary>
+ /// Call once before close the file, use this to build up the footer if any
+ /// </summary>
+ public virtual void EndWrite(){}
+
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ MeshStream?.Dispose();
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/OBJMeshFile.cs b/UVtools.Core/MeshFormats/OBJMeshFile.cs
index 7ea2c12..9b0dc69 100644
--- a/UVtools.Core/MeshFormats/OBJMeshFile.cs
+++ b/UVtools.Core/MeshFormats/OBJMeshFile.cs
@@ -12,92 +12,91 @@ using System.Numerics;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public class OBJMeshFile : MeshFile
{
- public class OBJMeshFile : MeshFile
- {
- #region Constants
- public const string DefaultObjectName = "Object.1";
- #endregion
+ #region Constants
+ public const string DefaultObjectName = "Object.1";
+ #endregion
- #region Members
- private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
- private FileStream _triangleStream;
- #endregion
+ #region Members
+ private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
+ private FileStream _triangleStream = null!;
+ #endregion
- #region Properties
- public static FileExtension FileExtension => new(typeof(OBJMeshFile), "obj", "Wavefront");
+ #region Properties
+ public static FileExtension FileExtension => new(typeof(OBJMeshFile), "obj", "Wavefront");
- public string ObjectName { get; } = DefaultObjectName;
- #endregion
+ public string ObjectName { get; } = DefaultObjectName;
+ #endregion
- #region Constructor
- public OBJMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
- #endregion
+ #region Constructor
+ public OBJMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat? slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
+ #endregion
- #region Methods
- public override void BeginWrite()
- {
- /* Create a stream to store the triangles (faces) as they come through */
- _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
+ #region Methods
+ public override void BeginWrite()
+ {
+ /* Create a stream to store the triangles (faces) as they come through */
+ _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
- MeshStream.WriteLine($"# {HeaderComment}");
- MeshStream.WriteLine($"o {ObjectName}");
- MeshStream.WriteLine();
- MeshStream.WriteLine("# List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0");
+ MeshStream.WriteLine($"# {HeaderComment}");
+ MeshStream.WriteLine($"o {ObjectName}");
+ MeshStream.WriteLine();
+ MeshStream.WriteLine("# List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0");
+ }
+
+ public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ {
+ if (!_vertexCache.ContainsKey(p1))
+ {
+ MeshStream.WriteLine($"v {p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
+ VertexCount++;
+ _vertexCache.Add(p1, VertexCount);
}
- public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ if (!_vertexCache.ContainsKey(p2))
{
- if (!_vertexCache.ContainsKey(p1))
- {
- MeshStream.WriteLine($"v {p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
- VertexCount++;
- _vertexCache.Add(p1, VertexCount);
- }
-
- if (!_vertexCache.ContainsKey(p2))
- {
- MeshStream.WriteLine($"v {p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
- VertexCount++;
- _vertexCache.Add(p2, VertexCount);
- }
-
- if (!_vertexCache.ContainsKey(p3))
- {
- MeshStream.WriteLine($"v {p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
- VertexCount++;
- _vertexCache.Add(p3, VertexCount);
- }
-
- _triangleStream.WriteLine($"f {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]}");
-
- TriangleCount++;
-
- /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
- * an allocation of a bigger buffer and copy of the kvp's */
- if (_vertexCache.Count >= VertexCacheSize - 10)
- {
- _vertexCache.Clear();
- }
+ MeshStream.WriteLine($"v {p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
+ VertexCount++;
+ _vertexCache.Add(p2, VertexCount);
}
- public override void EndWrite()
+ if (!_vertexCache.ContainsKey(p3))
+ {
+ MeshStream.WriteLine($"v {p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
+ VertexCount++;
+ _vertexCache.Add(p3, VertexCount);
+ }
+
+ _triangleStream.WriteLine($"f {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]}");
+
+ TriangleCount++;
+
+ /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
+ * an allocation of a bigger buffer and copy of the kvp's */
+ if (_vertexCache.Count >= VertexCacheSize - 10)
{
_vertexCache.Clear();
+ }
+ }
- MeshStream.WriteLine();
- MeshStream.WriteLine("# Polygonal face elements");
+ public override void EndWrite()
+ {
+ _vertexCache.Clear();
- _triangleStream.Seek(0, SeekOrigin.Begin);
- _triangleStream.CopyTo(MeshStream);
- _triangleStream.Dispose();
+ MeshStream.WriteLine();
+ MeshStream.WriteLine("# Polygonal face elements");
- MeshStream.WriteLine();
- MeshStream.WriteLine($"# Triangles: {TriangleCount}");
- MeshStream.WriteLine($"# Unique Vertexes: {VertexCount}");
- }
+ _triangleStream.Seek(0, SeekOrigin.Begin);
+ _triangleStream.CopyTo(MeshStream);
+ _triangleStream.Dispose();
- #endregion
+ MeshStream.WriteLine();
+ MeshStream.WriteLine($"# Triangles: {TriangleCount}");
+ MeshStream.WriteLine($"# Unique Vertexes: {VertexCount}");
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/OFFMeshFile.cs b/UVtools.Core/MeshFormats/OFFMeshFile.cs
index ea0e985..49b240d 100644
--- a/UVtools.Core/MeshFormats/OFFMeshFile.cs
+++ b/UVtools.Core/MeshFormats/OFFMeshFile.cs
@@ -12,89 +12,88 @@ using System.Numerics;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public class OFFMeshFile : MeshFile
{
- public class OFFMeshFile : MeshFile
- {
- #region Members
- private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
- private FileStream _triangleStream;
- private long _vertexCountWritePosition;
- #endregion
+ #region Members
+ private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
+ private FileStream _triangleStream = null!;
+ private long _vertexCountWritePosition;
+ #endregion
- #region Properties
- public static FileExtension FileExtension => new(typeof(OFFMeshFile), "off", "Object File Format");
- #endregion
+ #region Properties
+ public static FileExtension FileExtension => new(typeof(OFFMeshFile), "off", "Object File Format");
+ #endregion
- #region Constructor
- public OFFMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
- #endregion
+ #region Constructor
+ public OFFMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat? slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
+ #endregion
- #region Methods
- public override void BeginWrite()
- {
- /* Create a stream to store the triangles (faces) as they come through */
- _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
+ #region Methods
+ public override void BeginWrite()
+ {
+ /* Create a stream to store the triangles (faces) as they come through */
+ _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
- MeshStream.WriteLineLF("OFF");
- MeshStream.WriteLineLF($"# {HeaderComment}");
- //MeshStream.WriteLineLF("# vertex_count face_count edge_count");
- _vertexCountWritePosition = MeshStream.Position;
- MeshStream.WriteLineLF("0000000000 0000000000 0");
- }
+ MeshStream.WriteLineLF("OFF");
+ MeshStream.WriteLineLF($"# {HeaderComment}");
+ //MeshStream.WriteLineLF("# vertex_count face_count edge_count");
+ _vertexCountWritePosition = MeshStream.Position;
+ MeshStream.WriteLineLF("0000000000 0000000000 0");
+ }
- public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ {
+ if (!_vertexCache.ContainsKey(p1))
{
- if (!_vertexCache.ContainsKey(p1))
- {
- MeshStream.WriteLineLF($"{p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
- _vertexCache.Add(p1, VertexCount);
- VertexCount++;
- }
+ MeshStream.WriteLineLF($"{p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
+ _vertexCache.Add(p1, VertexCount);
+ VertexCount++;
+ }
- if (!_vertexCache.ContainsKey(p2))
- {
- MeshStream.WriteLineLF($"{p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
- _vertexCache.Add(p2, VertexCount);
- VertexCount++;
- }
+ if (!_vertexCache.ContainsKey(p2))
+ {
+ MeshStream.WriteLineLF($"{p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
+ _vertexCache.Add(p2, VertexCount);
+ VertexCount++;
+ }
- if (!_vertexCache.ContainsKey(p3))
- {
- MeshStream.WriteLineLF($"{p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
- _vertexCache.Add(p3, VertexCount);
- VertexCount++;
- }
+ if (!_vertexCache.ContainsKey(p3))
+ {
+ MeshStream.WriteLineLF($"{p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
+ _vertexCache.Add(p3, VertexCount);
+ VertexCount++;
+ }
- _triangleStream.WriteLineLF($"3 {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]}");
+ _triangleStream.WriteLineLF($"3 {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]}");
- TriangleCount++;
+ TriangleCount++;
- /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
- * an allocation of a bigger buffer and copy of the kvp's */
- if (_vertexCache.Count >= VertexCacheSize - 10)
- {
- _vertexCache.Clear();
- }
- }
-
- public override void EndWrite()
+ /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
+ * an allocation of a bigger buffer and copy of the kvp's */
+ if (_vertexCache.Count >= VertexCacheSize - 10)
{
_vertexCache.Clear();
+ }
+ }
- //MeshStream.WriteLineLF("# Polygonal face elements");
+ public override void EndWrite()
+ {
+ _vertexCache.Clear();
- _triangleStream.Seek(0, SeekOrigin.Begin);
- _triangleStream.CopyTo(MeshStream);
- _triangleStream.Dispose();
+ //MeshStream.WriteLineLF("# Polygonal face elements");
- MeshStream.Seek(_vertexCountWritePosition + 10 - VertexCount.ToString().Length, SeekOrigin.Begin);
- MeshStream.WriteString(VertexCount.ToString());
- MeshStream.Seek(11 - TriangleCount.ToString().Length, SeekOrigin.Current);
- MeshStream.WriteString(TriangleCount.ToString());
- MeshStream.Seek(0, SeekOrigin.End);
- }
+ _triangleStream.Seek(0, SeekOrigin.Begin);
+ _triangleStream.CopyTo(MeshStream);
+ _triangleStream.Dispose();
- #endregion
+ MeshStream.Seek(_vertexCountWritePosition + 10 - VertexCount.ToString().Length, SeekOrigin.Begin);
+ MeshStream.WriteString(VertexCount.ToString());
+ MeshStream.Seek(11 - TriangleCount.ToString().Length, SeekOrigin.Current);
+ MeshStream.WriteString(TriangleCount.ToString());
+ MeshStream.Seek(0, SeekOrigin.End);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/PLYMeshFile.cs b/UVtools.Core/MeshFormats/PLYMeshFile.cs
index 8941100..5733f72 100644
--- a/UVtools.Core/MeshFormats/PLYMeshFile.cs
+++ b/UVtools.Core/MeshFormats/PLYMeshFile.cs
@@ -12,139 +12,138 @@ using System.Numerics;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public class PLYMeshFile : MeshFile
{
- public class PLYMeshFile : MeshFile
- {
- #region Members
- private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
- private FileStream _triangleStream;
- private long _vertexCountWritePosition;
- private long _faceCountWritePosition;
- #endregion
-
- #region Properties
- public static FileExtension FileExtension => new(typeof(PLYMeshFile), "ply", "Polygon File Format");
- #endregion
-
- #region Constructor
- public PLYMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.BINARY, FileFormat slicerFile = null) : base(filePath, fileMode, fileFormat, slicerFile) { }
- #endregion
+ #region Members
+ private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
+ private FileStream _triangleStream = null!;
+ private long _vertexCountWritePosition;
+ private long _faceCountWritePosition;
+ #endregion
+
+ #region Properties
+ public static FileExtension FileExtension => new(typeof(PLYMeshFile), "ply", "Polygon File Format");
+ #endregion
+
+ #region Constructor
+ public PLYMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.BINARY, FileFormat? slicerFile = null) : base(filePath, fileMode, fileFormat, slicerFile) { }
+ #endregion
- #region Methods
- public override void BeginWrite()
- {
- /* Create a stream to store the triangles (faces) as they come through */
- _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
-
- MeshStream.WriteLineLF("ply");
- MeshStream.WriteLineLF(FileFormat == MeshFileFormat.ASCII
- ? "format ascii 1.0"
- : "format binary_little_endian 1.0");
- MeshStream.WriteLineLF($"comment {HeaderComment}");
- MeshStream.WriteString("element vertex 0000000000");
- _vertexCountWritePosition = MeshStream.Position;
- MeshStream.WriteLineLF();
- MeshStream.WriteLineLF("property float x");
- MeshStream.WriteLineLF("property float y");
- MeshStream.WriteLineLF("property float z");
- MeshStream.WriteString("element face 0000000000");
- _faceCountWritePosition = MeshStream.Position;
- MeshStream.WriteLineLF();
- MeshStream.WriteLineLF("property list uint8 uint32 vertex_index");
- MeshStream.WriteLineLF("end_header");
- }
+ #region Methods
+ public override void BeginWrite()
+ {
+ /* Create a stream to store the triangles (faces) as they come through */
+ _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
+
+ MeshStream.WriteLineLF("ply");
+ MeshStream.WriteLineLF(FileFormat == MeshFileFormat.ASCII
+ ? "format ascii 1.0"
+ : "format binary_little_endian 1.0");
+ MeshStream.WriteLineLF($"comment {HeaderComment}");
+ MeshStream.WriteString("element vertex 0000000000");
+ _vertexCountWritePosition = MeshStream.Position;
+ MeshStream.WriteLineLF();
+ MeshStream.WriteLineLF("property float x");
+ MeshStream.WriteLineLF("property float y");
+ MeshStream.WriteLineLF("property float z");
+ MeshStream.WriteString("element face 0000000000");
+ _faceCountWritePosition = MeshStream.Position;
+ MeshStream.WriteLineLF();
+ MeshStream.WriteLineLF("property list uint8 uint32 vertex_index");
+ MeshStream.WriteLineLF("end_header");
+ }
- public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ {
+ if (!_vertexCache.ContainsKey(p1))
{
- if (!_vertexCache.ContainsKey(p1))
+ if (FileFormat == MeshFileFormat.ASCII)
{
- if (FileFormat == MeshFileFormat.ASCII)
- {
- MeshStream.WriteLineLF($"{p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
- }
- else
- {
- MeshStream.WriteFloatLittleEndian(p1.X);
- MeshStream.WriteFloatLittleEndian(p1.Y);
- MeshStream.WriteFloatLittleEndian(p1.Z);
- }
-
- _vertexCache.Add(p1, VertexCount);
- VertexCount++;
+ MeshStream.WriteLineLF($"{p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
}
-
- if (!_vertexCache.ContainsKey(p2))
+ else
{
- if (FileFormat == MeshFileFormat.ASCII)
- {
- MeshStream.WriteLineLF($"{p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
- }
- else
- {
- MeshStream.WriteFloatLittleEndian(p2.X);
- MeshStream.WriteFloatLittleEndian(p2.Y);
- MeshStream.WriteFloatLittleEndian(p2.Z);
- }
-
- _vertexCache.Add(p2, VertexCount);
- VertexCount++;
+ MeshStream.WriteFloatLittleEndian(p1.X);
+ MeshStream.WriteFloatLittleEndian(p1.Y);
+ MeshStream.WriteFloatLittleEndian(p1.Z);
}
+
+ _vertexCache.Add(p1, VertexCount);
+ VertexCount++;
+ }
- if (!_vertexCache.ContainsKey(p3))
+ if (!_vertexCache.ContainsKey(p2))
+ {
+ if (FileFormat == MeshFileFormat.ASCII)
+ {
+ MeshStream.WriteLineLF($"{p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
+ }
+ else
{
- if (FileFormat == MeshFileFormat.ASCII)
- {
- MeshStream.WriteLineLF($"{p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
- }
- else
- {
- MeshStream.WriteFloatLittleEndian(p3.X);
- MeshStream.WriteFloatLittleEndian(p3.Y);
- MeshStream.WriteFloatLittleEndian(p3.Z);
- }
-
- _vertexCache.Add(p3, VertexCount);
- VertexCount++;
+ MeshStream.WriteFloatLittleEndian(p2.X);
+ MeshStream.WriteFloatLittleEndian(p2.Y);
+ MeshStream.WriteFloatLittleEndian(p2.Z);
}
+ _vertexCache.Add(p2, VertexCount);
+ VertexCount++;
+ }
+
+ if (!_vertexCache.ContainsKey(p3))
+ {
if (FileFormat == MeshFileFormat.ASCII)
{
- _triangleStream.WriteLineLF($"3 {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]}");
+ MeshStream.WriteLineLF($"{p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
}
else
{
- _triangleStream.WriteByte(3);
- _triangleStream.WriteUIntLittleEndian(_vertexCache[p1]);
- _triangleStream.WriteUIntLittleEndian(_vertexCache[p2]);
- _triangleStream.WriteUIntLittleEndian(_vertexCache[p3]);
+ MeshStream.WriteFloatLittleEndian(p3.X);
+ MeshStream.WriteFloatLittleEndian(p3.Y);
+ MeshStream.WriteFloatLittleEndian(p3.Z);
}
- TriangleCount++;
-
- /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
- * an allocation of a bigger buffer and copy of the kvp's */
- if (_vertexCache.Count >= VertexCacheSize - 10)
- {
- _vertexCache.Clear();
- }
+ _vertexCache.Add(p3, VertexCount);
+ VertexCount++;
}
- public override void EndWrite()
+ if (FileFormat == MeshFileFormat.ASCII)
+ {
+ _triangleStream.WriteLineLF($"3 {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]}");
+ }
+ else
+ {
+ _triangleStream.WriteByte(3);
+ _triangleStream.WriteUIntLittleEndian(_vertexCache[p1]);
+ _triangleStream.WriteUIntLittleEndian(_vertexCache[p2]);
+ _triangleStream.WriteUIntLittleEndian(_vertexCache[p3]);
+ }
+
+ TriangleCount++;
+
+ /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
+ * an allocation of a bigger buffer and copy of the kvp's */
+ if (_vertexCache.Count >= VertexCacheSize - 10)
{
_vertexCache.Clear();
+ }
+ }
- _triangleStream.Seek(0, SeekOrigin.Begin);
- _triangleStream.CopyTo(MeshStream);
- _triangleStream.Dispose();
+ public override void EndWrite()
+ {
+ _vertexCache.Clear();
- MeshStream.Seek(_vertexCountWritePosition - VertexCount.ToString().Length, SeekOrigin.Begin);
- MeshStream.WriteString(VertexCount.ToString());
- MeshStream.Seek(_faceCountWritePosition - TriangleCount.ToString().Length, SeekOrigin.Begin);
- MeshStream.WriteString(TriangleCount.ToString());
- MeshStream.Seek(0, SeekOrigin.End);
- }
+ _triangleStream.Seek(0, SeekOrigin.Begin);
+ _triangleStream.CopyTo(MeshStream);
+ _triangleStream.Dispose();
- #endregion
+ MeshStream.Seek(_vertexCountWritePosition - VertexCount.ToString().Length, SeekOrigin.Begin);
+ MeshStream.WriteString(VertexCount.ToString());
+ MeshStream.Seek(_faceCountWritePosition - TriangleCount.ToString().Length, SeekOrigin.Begin);
+ MeshStream.WriteString(TriangleCount.ToString());
+ MeshStream.Seek(0, SeekOrigin.End);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/STLMeshFile.cs b/UVtools.Core/MeshFormats/STLMeshFile.cs
index 446beae..7303460 100644
--- a/UVtools.Core/MeshFormats/STLMeshFile.cs
+++ b/UVtools.Core/MeshFormats/STLMeshFile.cs
@@ -12,95 +12,94 @@ using System.Text;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public class STLMeshFile : MeshFile
{
- public class STLMeshFile : MeshFile
- {
- #region Constants
- public const string DefaultObjectName = "UVTools STL Object";
- #endregion
+ #region Constants
+ public const string DefaultObjectName = "UVTools STL Object";
+ #endregion
- #region Properties
- public static FileExtension FileExtension => new(typeof(STLMeshFile), "stl", "Standard Triangle Language");
+ #region Properties
+ public static FileExtension FileExtension => new(typeof(STLMeshFile), "stl", "Standard Triangle Language");
- public string ObjectName { get; } = DefaultObjectName;
- #endregion
+ public string ObjectName { get; } = DefaultObjectName;
+ #endregion
- #region Constructor
- public STLMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.BINARY, FileFormat slicerFile = null) : base(filePath, fileMode, fileFormat, slicerFile)
- { }
+ #region Constructor
+ public STLMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.BINARY, FileFormat? slicerFile = null) : base(filePath, fileMode, fileFormat, slicerFile)
+ { }
- #endregion
+ #endregion
- #region Methods
- public override void BeginWrite()
+ #region Methods
+ public override void BeginWrite()
+ {
+ if (FileFormat == MeshFileFormat.ASCII)
+ {
+ MeshStream.WriteLineLF($"solid \"{ObjectName}\"");
+ }
+ else
{
- if (FileFormat == MeshFileFormat.ASCII)
- {
- MeshStream.WriteLineLF($"solid \"{ObjectName}\"");
- }
- else
- {
- var header = new byte[80];
- var headerText = Encoding.UTF8.GetBytes(HeaderComment);
- Array.Copy(headerText, header, headerText.Length);
- MeshStream.Write(header);
- MeshStream.Seek(4, SeekOrigin.Current);
- }
+ var header = new byte[80];
+ var headerText = Encoding.UTF8.GetBytes(HeaderComment);
+ Array.Copy(headerText, header, headerText.Length);
+ MeshStream.Write(header);
+ MeshStream.Seek(4, SeekOrigin.Current);
}
+ }
- public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ {
+ if (FileFormat == MeshFileFormat.ASCII)
+ {
+ MeshStream.WriteLineLF($" facet normal {normal.X} {normal.Y} {normal.Z}");
+ MeshStream.WriteLineLF(" outer loop");
+ MeshStream.WriteLineLF($" vertex {p1.X:E11} {p1.Y:E11} {p1.Z:E11}");
+ MeshStream.WriteLineLF($" vertex {p2.X:E11} {p2.Y:E11} {p2.Z:E11}");
+ MeshStream.WriteLineLF($" vertex {p3.X:E11} {p3.Y:E11} {p3.Z:E11}");
+ MeshStream.WriteLineLF(" endloop");
+ MeshStream.WriteLineLF(" endfacet");
+ }
+ else
{
- if (FileFormat == MeshFileFormat.ASCII)
- {
- MeshStream.WriteLineLF($" facet normal {normal.X} {normal.Y} {normal.Z}");
- MeshStream.WriteLineLF(" outer loop");
- MeshStream.WriteLineLF($" vertex {p1.X:E11} {p1.Y:E11} {p1.Z:E11}");
- MeshStream.WriteLineLF($" vertex {p2.X:E11} {p2.Y:E11} {p2.Z:E11}");
- MeshStream.WriteLineLF($" vertex {p3.X:E11} {p3.Y:E11} {p3.Z:E11}");
- MeshStream.WriteLineLF(" endloop");
- MeshStream.WriteLineLF(" endfacet");
- }
- else
- {
- MeshStream.WriteFloatLittleEndian(normal.X);
- MeshStream.WriteFloatLittleEndian(normal.Y);
- MeshStream.WriteFloatLittleEndian(normal.Z);
+ MeshStream.WriteFloatLittleEndian(normal.X);
+ MeshStream.WriteFloatLittleEndian(normal.Y);
+ MeshStream.WriteFloatLittleEndian(normal.Z);
- MeshStream.WriteFloatLittleEndian(p1.X);
- MeshStream.WriteFloatLittleEndian(p1.Y);
- MeshStream.WriteFloatLittleEndian(p1.Z);
+ MeshStream.WriteFloatLittleEndian(p1.X);
+ MeshStream.WriteFloatLittleEndian(p1.Y);
+ MeshStream.WriteFloatLittleEndian(p1.Z);
- MeshStream.WriteFloatLittleEndian(p2.X);
- MeshStream.WriteFloatLittleEndian(p2.Y);
- MeshStream.WriteFloatLittleEndian(p2.Z);
+ MeshStream.WriteFloatLittleEndian(p2.X);
+ MeshStream.WriteFloatLittleEndian(p2.Y);
+ MeshStream.WriteFloatLittleEndian(p2.Z);
- MeshStream.WriteFloatLittleEndian(p3.X);
- MeshStream.WriteFloatLittleEndian(p3.Y);
- MeshStream.WriteFloatLittleEndian(p3.Z);
+ MeshStream.WriteFloatLittleEndian(p3.X);
+ MeshStream.WriteFloatLittleEndian(p3.Y);
+ MeshStream.WriteFloatLittleEndian(p3.Z);
- MeshStream.Write(new byte[2]);
- }
-
- TriangleCount++;
- VertexCount += 3;
+ MeshStream.Write(new byte[2]);
}
- public override void EndWrite()
+ TriangleCount++;
+ VertexCount += 3;
+ }
+
+ public override void EndWrite()
+ {
+ if (FileFormat == MeshFileFormat.ASCII)
{
- if (FileFormat == MeshFileFormat.ASCII)
- {
- MeshStream.WriteLineLF($"endsolid \"{ObjectName}\"");
- }
- else
- {
- MeshStream.Seek(80, SeekOrigin.Begin);
- MeshStream.WriteUIntLittleEndian(TriangleCount);
- }
- MeshStream.Flush();
+ MeshStream.WriteLineLF($"endsolid \"{ObjectName}\"");
+ }
+ else
+ {
+ MeshStream.Seek(80, SeekOrigin.Begin);
+ MeshStream.WriteUIntLittleEndian(TriangleCount);
}
-
- #endregion
+ MeshStream.Flush();
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/MeshFormats/WRLMeshFile.cs b/UVtools.Core/MeshFormats/WRLMeshFile.cs
index 2109471..392299d 100644
--- a/UVtools.Core/MeshFormats/WRLMeshFile.cs
+++ b/UVtools.Core/MeshFormats/WRLMeshFile.cs
@@ -12,91 +12,90 @@ using System.Numerics;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.MeshFormats
+namespace UVtools.Core.MeshFormats;
+
+public class WRLMeshFile : MeshFile
{
- public class WRLMeshFile : MeshFile
- {
- #region Members
- private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
- private FileStream _triangleStream;
- #endregion
+ #region Members
+ private readonly Dictionary<Vector3, uint> _vertexCache = new(VertexCacheSize);
+ private FileStream _triangleStream = null!;
+ #endregion
- #region Properties
- public static FileExtension FileExtension => new(typeof(WRLMeshFile), "wrl", "Virtual Reality Modeling Language");
- #endregion
+ #region Properties
+ public static FileExtension FileExtension => new(typeof(WRLMeshFile), "wrl", "Virtual Reality Modeling Language");
+ #endregion
- #region Constructor
- public WRLMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
- #endregion
+ #region Constructor
+ public WRLMeshFile(string filePath, FileMode fileMode, MeshFileFormat fileFormat = MeshFileFormat.ASCII, FileFormat? slicerFile = null) : base(filePath, fileMode, MeshFileFormat.ASCII, slicerFile) { }
+ #endregion
- #region Methods
- public override void BeginWrite()
- {
- /* Create a stream to store the triangles (faces) as they come through */
- _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
+ #region Methods
+ public override void BeginWrite()
+ {
+ /* Create a stream to store the triangles (faces) as they come through */
+ _triangleStream = new FileStream(PathExtensions.GetTempFilePathWithExtension("trig", $"{About.Software}_"), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 81920, FileOptions.DeleteOnClose);
- MeshStream.WriteLineLF("#VRML V2.0 utf8");
- MeshStream.WriteLineLF($"WorldInfo {{ info \"{HeaderComment}\" }}");
- MeshStream.WriteLineLF("Shape {");
- MeshStream.WriteLineLF("\tgeometry IndexedFaceSet {");
- MeshStream.WriteLineLF("\t\tcoord Coordinate {");
- MeshStream.WriteString("\t\t\tpoint [");
- }
+ MeshStream.WriteLineLF("#VRML V2.0 utf8");
+ MeshStream.WriteLineLF($"WorldInfo {{ info \"{HeaderComment}\" }}");
+ MeshStream.WriteLineLF("Shape {");
+ MeshStream.WriteLineLF("\tgeometry IndexedFaceSet {");
+ MeshStream.WriteLineLF("\t\tcoord Coordinate {");
+ MeshStream.WriteString("\t\t\tpoint [");
+ }
- public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ public override void WriteTriangle(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)
+ {
+ if (!_vertexCache.ContainsKey(p1))
{
- if (!_vertexCache.ContainsKey(p1))
- {
- MeshStream.WriteString($" {p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
- _vertexCache.Add(p1, VertexCount);
- VertexCount++;
- }
+ MeshStream.WriteString($" {p1.X:F6} {p1.Y:F6} {p1.Z:F6}");
+ _vertexCache.Add(p1, VertexCount);
+ VertexCount++;
+ }
- if (!_vertexCache.ContainsKey(p2))
- {
- MeshStream.WriteString($" {p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
- _vertexCache.Add(p2, VertexCount);
- VertexCount++;
- }
+ if (!_vertexCache.ContainsKey(p2))
+ {
+ MeshStream.WriteString($" {p2.X:F6} {p2.Y:F6} {p2.Z:F6}");
+ _vertexCache.Add(p2, VertexCount);
+ VertexCount++;
+ }
- if (!_vertexCache.ContainsKey(p3))
- {
- MeshStream.WriteString($" {p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
- _vertexCache.Add(p3, VertexCount);
- VertexCount++;
- }
+ if (!_vertexCache.ContainsKey(p3))
+ {
+ MeshStream.WriteString($" {p3.X:F6} {p3.Y:F6} {p3.Z:F6}");
+ _vertexCache.Add(p3, VertexCount);
+ VertexCount++;
+ }
- _triangleStream.WriteString($" {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]} -1");
+ _triangleStream.WriteString($" {_vertexCache[p1]} {_vertexCache[p2]} {_vertexCache[p3]} -1");
- TriangleCount++;
+ TriangleCount++;
- /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
- * an allocation of a bigger buffer and copy of the kvp's */
- if (_vertexCache.Count >= VertexCacheSize - 10)
- {
- _vertexCache.Clear();
- }
- }
-
- public override void EndWrite()
+ /* If we are getting close to the cache size. we do *not* want to go over the capacity as that will trigger
+ * an allocation of a bigger buffer and copy of the kvp's */
+ if (_vertexCache.Count >= VertexCacheSize - 10)
{
_vertexCache.Clear();
+ }
+ }
- MeshStream.WriteLineLF(" ]");
- MeshStream.WriteLineLF("\t\t}");
- MeshStream.WriteString("\t\tcoordIndex [");
+ public override void EndWrite()
+ {
+ _vertexCache.Clear();
+
+ MeshStream.WriteLineLF(" ]");
+ MeshStream.WriteLineLF("\t\t}");
+ MeshStream.WriteString("\t\tcoordIndex [");
- _triangleStream.Seek(0, SeekOrigin.Begin);
- _triangleStream.CopyTo(MeshStream);
- _triangleStream.Dispose();
+ _triangleStream.Seek(0, SeekOrigin.Begin);
+ _triangleStream.CopyTo(MeshStream);
+ _triangleStream.Dispose();
- MeshStream.WriteLineLF(" ]");
+ MeshStream.WriteLineLF(" ]");
- MeshStream.WriteLineLF("\t}");
- MeshStream.WriteLineLF("}");
- }
-
- #endregion
+ MeshStream.WriteLineLF("\t}");
+ MeshStream.WriteLineLF("}");
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Network/MappedDevice.cs b/UVtools.Core/Network/MappedDevice.cs
index 6a1d88f..87c60cb 100644
--- a/UVtools.Core/Network/MappedDevice.cs
+++ b/UVtools.Core/Network/MappedDevice.cs
@@ -8,103 +8,102 @@
using UVtools.Core.Objects;
-namespace UVtools.Core.Network
+namespace UVtools.Core.Network;
+
+public class MappedDevice : BindableBase
{
- public class MappedDevice : BindableBase
+ #region Members
+
+ private bool _isEnabled = true;
+ private string _path = null!;
+ private string? _name;
+ private string? _compatibleExtensions;
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets if this device is enabled
+ /// </summary>
+ public bool IsEnabled
+ {
+ get => _isEnabled;
+ set => RaiseAndSetIfChanged(ref _isEnabled, value);
+ }
+
+ /// <summary>
+ /// Gets or sets the full path for the location
+ /// </summary>
+ public string Path
+ {
+ get => _path;
+ set => RaiseAndSetIfChanged(ref _path, value);
+ }
+
+ /// <summary>
+ /// Gets or sets the path name alias
+ /// </summary>
+ public string? Name
+ {
+ get => _name;
+ set => RaiseAndSetIfChanged(ref _name, value);
+ }
+
+ /// <summary>
+ /// Gets or sets the compatible extensions with this device.
+ /// Empty or null to be compatible with everything
+ /// </summary>
+ public string? CompatibleExtensions
+ {
+ get => _compatibleExtensions;
+ set => RaiseAndSetIfChanged(ref _compatibleExtensions, value);
+ }
+
+ #endregion
+
+ #region Contructors
+
+ public MappedDevice() { }
+
+ public MappedDevice(string path, string? name = null)
+ {
+ _path = path;
+ _name = name;
+ }
+
+ #endregion
+
+ #region Methods
+
+ public override string ToString()
{
- #region Members
-
- private bool _isEnabled = true;
- private string _path;
- private string _name;
- private string _compatibleExtensions;
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Gets or sets if this device is enabled
- /// </summary>
- public bool IsEnabled
- {
- get => _isEnabled;
- set => RaiseAndSetIfChanged(ref _isEnabled, value);
- }
-
- /// <summary>
- /// Gets or sets the full path for the location
- /// </summary>
- public string Path
- {
- get => _path;
- set => RaiseAndSetIfChanged(ref _path, value);
- }
-
- /// <summary>
- /// Gets or sets the path name alias
- /// </summary>
- public string Name
- {
- get => _name;
- set => RaiseAndSetIfChanged(ref _name, value);
- }
-
- /// <summary>
- /// Gets or sets the compatible extensions with this device.
- /// Empty or null to be compatible with everything
- /// </summary>
- public string CompatibleExtensions
- {
- get => _compatibleExtensions;
- set => RaiseAndSetIfChanged(ref _compatibleExtensions, value);
- }
-
- #endregion
-
- #region Contructors
-
- public MappedDevice() { }
-
- public MappedDevice(string path, string name = null)
- {
- _path = path;
- _name = name;
- }
-
- #endregion
-
- #region Methods
-
- public override string ToString()
- {
- if (!string.IsNullOrWhiteSpace(_name)) return $"{_path} ({_name})";
- return _path;
- }
-
- protected bool Equals(MappedDevice other)
- {
- return _path == other._path;
- }
-
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((MappedDevice)obj);
- }
-
- public override int GetHashCode()
- {
- return (_path != null ? _path.GetHashCode() : 0);
- }
-
- public MappedDevice Clone()
- {
- return MemberwiseClone() as MappedDevice;
- }
-
- #endregion
+ if (!string.IsNullOrWhiteSpace(_name)) return $"{_path} ({_name})";
+ return _path;
}
-}
+
+ protected bool Equals(MappedDevice other)
+ {
+ return _path == other._path;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((MappedDevice)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return (_path != null ? _path.GetHashCode() : 0);
+ }
+
+ public MappedDevice Clone()
+ {
+ return (MemberwiseClone() as MappedDevice)!;
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Network/RemotePrinter.cs b/UVtools.Core/Network/RemotePrinter.cs
index bd18d0a..4d63d76 100644
--- a/UVtools.Core/Network/RemotePrinter.cs
+++ b/UVtools.Core/Network/RemotePrinter.cs
@@ -6,208 +6,206 @@
* of this license document, but changing it is not allowed.
*/
-using System.Diagnostics;
using UVtools.Core.Extensions;
using UVtools.Core.Objects;
-namespace UVtools.Core.Network
+namespace UVtools.Core.Network;
+
+public class RemotePrinter : BindableBase
{
- public class RemotePrinter : BindableBase
- {
- #region Members
- private bool _isEnabled;
- private string _name;
- private string _host = "0.0.0.0";
- private string _compatibleExtensions;
- private ushort _port;
-
- private RemotePrinterRequest _requestUploadFile = new(RemotePrinterRequest.RequestType.UploadFile, RemotePrinterRequest.RequestMethod.PUT);
- private RemotePrinterRequest _requestPrintFile = new(RemotePrinterRequest.RequestType.PrintFile, RemotePrinterRequest.RequestMethod.GET);
- private RemotePrinterRequest _requestDeleteFile = new(RemotePrinterRequest.RequestType.DeleteFile, RemotePrinterRequest.RequestMethod.GET);
- private RemotePrinterRequest _requestPausePrint = new(RemotePrinterRequest.RequestType.PausePrint, RemotePrinterRequest.RequestMethod.GET);
- private RemotePrinterRequest _requestResumePrint = new(RemotePrinterRequest.RequestType.ResumePrint, RemotePrinterRequest.RequestMethod.GET);
- private RemotePrinterRequest _requestStopPrint = new(RemotePrinterRequest.RequestType.StopPrint, RemotePrinterRequest.RequestMethod.GET);
- private RemotePrinterRequest _requestGetFiles = new(RemotePrinterRequest.RequestType.GetFiles, RemotePrinterRequest.RequestMethod.GET);
- private RemotePrinterRequest _requestPrintStatus = new(RemotePrinterRequest.RequestType.PrintStatus, RemotePrinterRequest.RequestMethod.GET);
- private RemotePrinterRequest _requestPrinterInfo = new(RemotePrinterRequest.RequestType.PrinterInfo, RemotePrinterRequest.RequestMethod.GET);
-
- //private List<RemotePrinterRequest> _requests = new();
- #endregion
-
- #region Properties
-
- public bool IsEnabled
- {
- get => _isEnabled;
- set => RaiseAndSetIfChanged(ref _isEnabled, value);
- }
+ #region Members
+ private bool _isEnabled;
+ private string _name = null!;
+ private string _host = "0.0.0.0";
+ private string? _compatibleExtensions;
+ private ushort _port;
+
+ private RemotePrinterRequest _requestUploadFile = new(RemotePrinterRequest.RequestType.UploadFile, RemotePrinterRequest.RequestMethod.PUT);
+ private RemotePrinterRequest _requestPrintFile = new(RemotePrinterRequest.RequestType.PrintFile, RemotePrinterRequest.RequestMethod.GET);
+ private RemotePrinterRequest _requestDeleteFile = new(RemotePrinterRequest.RequestType.DeleteFile, RemotePrinterRequest.RequestMethod.GET);
+ private RemotePrinterRequest _requestPausePrint = new(RemotePrinterRequest.RequestType.PausePrint, RemotePrinterRequest.RequestMethod.GET);
+ private RemotePrinterRequest _requestResumePrint = new(RemotePrinterRequest.RequestType.ResumePrint, RemotePrinterRequest.RequestMethod.GET);
+ private RemotePrinterRequest _requestStopPrint = new(RemotePrinterRequest.RequestType.StopPrint, RemotePrinterRequest.RequestMethod.GET);
+ private RemotePrinterRequest _requestGetFiles = new(RemotePrinterRequest.RequestType.GetFiles, RemotePrinterRequest.RequestMethod.GET);
+ private RemotePrinterRequest _requestPrintStatus = new(RemotePrinterRequest.RequestType.PrintStatus, RemotePrinterRequest.RequestMethod.GET);
+ private RemotePrinterRequest _requestPrinterInfo = new(RemotePrinterRequest.RequestType.PrinterInfo, RemotePrinterRequest.RequestMethod.GET);
+
+ //private List<RemotePrinterRequest> _requests = new();
+ #endregion
+
+ #region Properties
+
+ public bool IsEnabled
+ {
+ get => _isEnabled;
+ set => RaiseAndSetIfChanged(ref _isEnabled, value);
+ }
- /// <summary>
- /// Gets or sets the alias name for this printer.
- /// Not used on requests
- /// </summary>
- public string Name
- {
- get => _name;
- set => RaiseAndSetIfChanged(ref _name, value);
- }
+ /// <summary>
+ /// Gets or sets the alias name for this printer.
+ /// Not used on requests
+ /// </summary>
+ public string Name
+ {
+ get => _name;
+ set => RaiseAndSetIfChanged(ref _name, value);
+ }
- /// <summary>
- /// Gets or sets the host/ip for the requests
- /// </summary>
- public string Host
- {
- get => _host;
- set => RaiseAndSetIfChanged(ref _host, value?.Trim());
- }
+ /// <summary>
+ /// Gets or sets the host/ip for the requests
+ /// </summary>
+ public string Host
+ {
+ get => _host;
+ set => RaiseAndSetIfChanged(ref _host!, value?.Trim());
+ }
- /// <summary>
- /// Gets or sets the host port for the requests.
- /// Use 0 to not use a port
- /// </summary>
- public ushort Port
- {
- get => _port;
- set => RaiseAndSetIfChanged(ref _port, value);
- }
+ /// <summary>
+ /// Gets or sets the host port for the requests.
+ /// Use 0 to not use a port
+ /// </summary>
+ public ushort Port
+ {
+ get => _port;
+ set => RaiseAndSetIfChanged(ref _port, value);
+ }
- public string HostUrl
+ public string HostUrl
+ {
+ get
{
- get
- {
- var result = $"http://{_host}";
- if (_port > 0) result += $":{_port}";
- return result;
- }
+ var result = $"http://{_host}";
+ if (_port > 0) result += $":{_port}";
+ return result;
}
+ }
- /// <summary>
- /// Gets or sets the compatible extensions with this device.
- /// Empty or null to be compatible with everything
- /// </summary>
- public string CompatibleExtensions
- {
- get => _compatibleExtensions;
- set => RaiseAndSetIfChanged(ref _compatibleExtensions, value);
- }
+ /// <summary>
+ /// Gets or sets the compatible extensions with this device.
+ /// Empty or null to be compatible with everything
+ /// </summary>
+ public string? CompatibleExtensions
+ {
+ get => _compatibleExtensions;
+ set => RaiseAndSetIfChanged(ref _compatibleExtensions, value);
+ }
- /// <summary>
- /// Gets if this host is valid
- /// </summary>
- public bool IsValid => !string.IsNullOrEmpty(_host) && _host != "0.0.0.0";
+ /// <summary>
+ /// Gets if this host is valid
+ /// </summary>
+ public bool IsValid => !string.IsNullOrEmpty(_host) && _host != "0.0.0.0";
- public RemotePrinterRequest RequestUploadFile
- {
- get => _requestUploadFile;
- set => RaiseAndSetIfChanged(ref _requestUploadFile, value);
- }
+ public RemotePrinterRequest RequestUploadFile
+ {
+ get => _requestUploadFile;
+ set => RaiseAndSetIfChanged(ref _requestUploadFile, value);
+ }
- public RemotePrinterRequest RequestPrintFile
- {
- get => _requestPrintFile;
- set => RaiseAndSetIfChanged(ref _requestPrintFile, value);
- }
+ public RemotePrinterRequest RequestPrintFile
+ {
+ get => _requestPrintFile;
+ set => RaiseAndSetIfChanged(ref _requestPrintFile, value);
+ }
- public RemotePrinterRequest RequestDeleteFile
- {
- get => _requestDeleteFile;
- set => RaiseAndSetIfChanged(ref _requestDeleteFile, value);
- }
+ public RemotePrinterRequest RequestDeleteFile
+ {
+ get => _requestDeleteFile;
+ set => RaiseAndSetIfChanged(ref _requestDeleteFile, value);
+ }
- public RemotePrinterRequest RequestPausePrint
- {
- get => _requestPausePrint;
- set => RaiseAndSetIfChanged(ref _requestPausePrint, value);
- }
+ public RemotePrinterRequest RequestPausePrint
+ {
+ get => _requestPausePrint;
+ set => RaiseAndSetIfChanged(ref _requestPausePrint, value);
+ }
- public RemotePrinterRequest RequestResumePrint
- {
- get => _requestResumePrint;
- set => RaiseAndSetIfChanged(ref _requestResumePrint, value);
- }
+ public RemotePrinterRequest RequestResumePrint
+ {
+ get => _requestResumePrint;
+ set => RaiseAndSetIfChanged(ref _requestResumePrint, value);
+ }
- public RemotePrinterRequest RequestStopPrint
- {
- get => _requestStopPrint;
- set => RaiseAndSetIfChanged(ref _requestStopPrint, value);
- }
+ public RemotePrinterRequest RequestStopPrint
+ {
+ get => _requestStopPrint;
+ set => RaiseAndSetIfChanged(ref _requestStopPrint, value);
+ }
- public RemotePrinterRequest RequestGetFiles
- {
- get => _requestGetFiles;
- set => RaiseAndSetIfChanged(ref _requestGetFiles, value);
- }
+ public RemotePrinterRequest RequestGetFiles
+ {
+ get => _requestGetFiles;
+ set => RaiseAndSetIfChanged(ref _requestGetFiles, value);
+ }
- public RemotePrinterRequest RequestPrintStatus
- {
- get => _requestPrintStatus;
- set => RaiseAndSetIfChanged(ref _requestPrintStatus, value);
- }
+ public RemotePrinterRequest RequestPrintStatus
+ {
+ get => _requestPrintStatus;
+ set => RaiseAndSetIfChanged(ref _requestPrintStatus, value);
+ }
- public RemotePrinterRequest RequestPrinterInfo
- {
- get => _requestPrinterInfo;
- set => RaiseAndSetIfChanged(ref _requestPrinterInfo, value);
- }
+ public RemotePrinterRequest RequestPrinterInfo
+ {
+ get => _requestPrinterInfo;
+ set => RaiseAndSetIfChanged(ref _requestPrinterInfo, value);
+ }
- public RemotePrinterRequest[] Requests => new []
- {
- _requestUploadFile,
- _requestPrintFile,
- _requestDeleteFile,
- _requestPausePrint,
- _requestResumePrint,
- _requestStopPrint,
- _requestGetFiles,
- _requestPrintStatus,
- _requestPrinterInfo,
- };
-
- /*
- /// <summary>
- /// Gets or sets the available requests for this printer
- /// </summary>
- public List<RemotePrinterRequest> Requests
- {
- get => _requests;
- set => RaiseAndSetIfChanged(ref _requests, value);
- }*/
+ public RemotePrinterRequest[] Requests => new []
+ {
+ _requestUploadFile,
+ _requestPrintFile,
+ _requestDeleteFile,
+ _requestPausePrint,
+ _requestResumePrint,
+ _requestStopPrint,
+ _requestGetFiles,
+ _requestPrintStatus,
+ _requestPrinterInfo,
+ };
+
+ /*
+ /// <summary>
+ /// Gets or sets the available requests for this printer
+ /// </summary>
+ public List<RemotePrinterRequest> Requests
+ {
+ get => _requests;
+ set => RaiseAndSetIfChanged(ref _requests, value);
+ }*/
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public RemotePrinter()
- {
- }
+ public RemotePrinter()
+ {
+ }
- public RemotePrinter(string host = "0.0.0.0", ushort port = 0, string name = "", bool isEnabled = false)
- {
- _isEnabled = isEnabled;
- _name = name;
- _host = host;
- _port = port;
- }
+ public RemotePrinter(string host = "0.0.0.0", ushort port = 0, string name = "", bool isEnabled = false)
+ {
+ _isEnabled = isEnabled;
+ _name = name;
+ _host = host;
+ _port = port;
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- public override string ToString()
- {
- var result = HostUrl;
- if (!string.IsNullOrWhiteSpace(_name)) result += $" ({_name})";
- return result;
- }
+ public override string ToString()
+ {
+ var result = HostUrl;
+ if (!string.IsNullOrWhiteSpace(_name)) result += $" ({_name})";
+ return result;
+ }
- public RemotePrinter Clone()
- {
- return this.CloneByXmlSerialization();
- }
+ public RemotePrinter Clone()
+ {
+ return this.CloneByXmlSerialization();
+ }
- #endregion
+ #endregion
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Network/RemotePrinterRequest.cs b/UVtools.Core/Network/RemotePrinterRequest.cs
index 56aba1a..2894885 100644
--- a/UVtools.Core/Network/RemotePrinterRequest.cs
+++ b/UVtools.Core/Network/RemotePrinterRequest.cs
@@ -7,191 +7,186 @@
*/
using System;
-using System.Collections.Specialized;
using System.ComponentModel;
-using System.Diagnostics;
-using System.IO;
-using System.Net;
using System.Net.Http;
-using System.Threading;
using System.Threading.Tasks;
using UVtools.Core.Extensions;
using UVtools.Core.Objects;
using UVtools.Core.Operations;
-namespace UVtools.Core.Network
+namespace UVtools.Core.Network;
+
+public class RemotePrinterRequest : BindableBase
{
- public class RemotePrinterRequest : BindableBase
- {
- #region Enums
+ #region Enums
- public enum RequestMethod : byte
- {
- [Description("GET")]
- GET,
+ public enum RequestMethod : byte
+ {
+ [Description("GET")]
+ GET,
- [Description("POST")]
- POST,
+ [Description("POST")]
+ POST,
- [Description("PUT")]
- PUT
- }
+ [Description("PUT")]
+ PUT
+ }
- public enum RequestType : byte
- {
- UploadFile,
- PrintFile,
- DeleteFile,
- PausePrint,
- ResumePrint,
- StopPrint,
- GetFiles,
- PrintStatus,
- PrinterInfo,
- }
+ public enum RequestType : byte
+ {
+ UploadFile,
+ PrintFile,
+ DeleteFile,
+ PausePrint,
+ ResumePrint,
+ StopPrint,
+ GetFiles,
+ PrintStatus,
+ PrinterInfo,
+ }
- #endregion
+ #endregion
- #region Members
- private RequestType _type;
- private RequestMethod _method;
- private string _path = "";
- #endregion
+ #region Members
+ private RequestType _type;
+ private RequestMethod _method;
+ private string _path = string.Empty;
+ #endregion
- #region Properties
+ #region Properties
- /// <summary>
- /// Gets or sets this request type
- /// </summary>
- public RequestType Type
- {
- get => _type;
- set => RaiseAndSetIfChanged(ref _type, value);
- }
+ /// <summary>
+ /// Gets or sets this request type
+ /// </summary>
+ public RequestType Type
+ {
+ get => _type;
+ set => RaiseAndSetIfChanged(ref _type, value);
+ }
- /// <summary>
- /// Gets or sets this request method
- /// </summary>
- public RequestMethod Method
- {
- get => _method;
- set => RaiseAndSetIfChanged(ref _method, value);
- }
+ /// <summary>
+ /// Gets or sets this request method
+ /// </summary>
+ public RequestMethod Method
+ {
+ get => _method;
+ set => RaiseAndSetIfChanged(ref _method, value);
+ }
- /// <summary>
- /// Gets or sets the request path, eg: print/file/{0}
- /// </summary>
- public string Path
+ /// <summary>
+ /// Gets or sets the request path, eg: print/file/{0}
+ /// </summary>
+ public string Path
+ {
+ get => _path;
+ set
{
- get => _path;
- set
+ if (!string.IsNullOrWhiteSpace(value))
{
- if (!string.IsNullOrWhiteSpace(value))
- {
- value = value.Trim();
- if (value[0] == '/') value = value.Remove(0, 1);
- if(value[^1] == '/') value = value.Remove(value.Length-1, 1);
- }
- if(!RaiseAndSetIfChanged(ref _path, value)) return;
- RaisePropertyChanged(nameof(IsValid));
+ value = value.Trim();
+ if (value[0] == '/') value = value.Remove(0, 1);
+ if(value[^1] == '/') value = value.Remove(value.Length-1, 1);
}
+ if(!RaiseAndSetIfChanged(ref _path, value)) return;
+ RaisePropertyChanged(nameof(IsValid));
}
+ }
- public bool IsValid => !string.IsNullOrWhiteSpace(_path);
+ public bool IsValid => !string.IsNullOrWhiteSpace(_path);
- #endregion
+ #endregion
- #region Contructors
+ #region Contructors
- public RemotePrinterRequest() { }
+ public RemotePrinterRequest() { }
- public RemotePrinterRequest(RequestType type, RequestMethod method, string path = "")
- {
- _type = type;
- _method = method;
- Path = path;
- }
+ public RemotePrinterRequest(RequestType type, RequestMethod method, string path = "")
+ {
+ _type = type;
+ _method = method;
+ Path = path;
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- /// <summary>
- /// Gets the path with formatted arguments
- /// </summary>
- /// <param name="parameters"></param>
- /// <returns></returns>
- public string GetFormattedPath(params object[] parameters) => _path is null ? null : string.Format(_path, parameters);
+ /// <summary>
+ /// Gets the path with formatted arguments
+ /// </summary>
+ /// <param name="parameters"></param>
+ /// <returns></returns>
+ public string GetFormattedPath(params object?[] parameters) => string.Format(_path, parameters);
- public async Task<HttpResponseMessage> SendRequest(string host, ushort port = 0, OperationProgress progress = null, string param1 = null, HttpContent content = null)
- {
- string url = $"http://{host}";
- if (port > 0) url += $":{port}";
+ public async Task<HttpResponseMessage> SendRequest(string host, ushort port = 0, OperationProgress? progress = null, string? param1 = null, HttpContent? content = null)
+ {
+ string url = $"http://{host}";
+ if (port > 0) url += $":{port}";
- progress ??= new();
- progress.Title = $"Sending {_method} request to: {url}";
- progress.ItemName = "Megabyte(s)";
- progress.CanCancel = true;
+ progress ??= new();
+ progress.Title = $"Sending {_method} request to: {url}";
+ progress.ItemName = "Megabyte(s)";
+ progress.CanCancel = true;
- if (!string.IsNullOrWhiteSpace(_path)) url += $"/{GetFormattedPath(param1)}";
+ if (!string.IsNullOrWhiteSpace(_path)) url += $"/{GetFormattedPath(param1)}";
- if (_method == RequestMethod.GET)
+ switch (_method)
+ {
+ case RequestMethod.GET:
{
using var response = await NetworkExtensions.HttpClient.GetAsync(url, progress.Token);
return response;
}
-
- if (_method == RequestMethod.POST)
+ case RequestMethod.POST:
{
using var response = await NetworkExtensions.HttpClient.PostAsync(url, content, progress.Token);
return response;
}
-
- if (_method == RequestMethod.PUT)
+ case RequestMethod.PUT:
{
using var response = await NetworkExtensions.HttpClient.PutAsync(url, content, progress.Token);
return response;
}
-
- return null;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(Method));
}
+ }
- public async Task<HttpResponseMessage> SendRequest(RemotePrinter remotePrinter,
- OperationProgress progress = null, string param1 = null, HttpContent content = null)
- => await SendRequest(remotePrinter.Host, remotePrinter.Port, progress, param1, content);
+ public async Task<HttpResponseMessage> SendRequest(RemotePrinter remotePrinter,
+ OperationProgress? progress = null, string? param1 = null, HttpContent? content = null)
+ => await SendRequest(remotePrinter.Host, remotePrinter.Port, progress, param1, content);
- public RemotePrinterRequest Clone()
- {
- return MemberwiseClone() as RemotePrinterRequest;
- }
-
- public override string ToString()
- {
- return _path;
- }
+ public RemotePrinterRequest Clone()
+ {
+ return (MemberwiseClone() as RemotePrinterRequest)!;
+ }
- protected bool Equals(RemotePrinterRequest other)
- {
- return _path == other._path;
- }
+ public override string ToString()
+ {
+ return _path;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((RemotePrinterRequest)obj);
- }
+ protected bool Equals(RemotePrinterRequest other)
+ {
+ return _path == other._path;
+ }
- public override int GetHashCode()
- {
- return (_path != null ? _path.GetHashCode() : 0);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((RemotePrinterRequest)obj);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ return (_path != null ? _path.GetHashCode() : 0);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/BindableBase.cs b/UVtools.Core/Objects/BindableBase.cs
index 9e63a1f..4fbfa04 100644
--- a/UVtools.Core/Objects/BindableBase.cs
+++ b/UVtools.Core/Objects/BindableBase.cs
@@ -10,84 +10,83 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+/// <summary>
+/// Implementation of <see cref="INotifyPropertyChanged" /> to simplify models.
+/// </summary>
+public abstract class BindableBase : INotifyPropertyChanged
{
/// <summary>
- /// Implementation of <see cref="INotifyPropertyChanged" /> to simplify models.
+ /// Multicast event for property change notifications.
/// </summary>
- public abstract class BindableBase : INotifyPropertyChanged
+ private PropertyChangedEventHandler? _propertyChanged;
+
+ public event PropertyChangedEventHandler? PropertyChanged
{
- /// <summary>
- /// Multicast event for property change notifications.
- /// </summary>
- private PropertyChangedEventHandler _propertyChanged;
+ add => _propertyChanged += value;
+ remove => _propertyChanged -= value;
+ }
- public event PropertyChangedEventHandler PropertyChanged
+ /// <summary>
+ /// Checks if a property already matches a desired value. Sets the property and
+ /// notifies listeners only when necessary.
+ /// </summary>
+ /// <typeparam name="T">Type of the property.</typeparam>
+ /// <param name="storage">Reference to a property with both getter and setter.</param>
+ /// <param name="value">Desired value for the property.</param>
+ /// <param name="propertyName">
+ /// Name of the property used to notify listeners. This
+ /// value is optional and can be provided automatically when invoked from compilers that
+ /// support CallerMemberName.
+ /// </param>
+ /// <returns>
+ /// True if the value was changed, false if the existing value matched the
+ /// desired value.
+ /// </returns>
+ /*protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (Equals(storage, value))
{
- add => _propertyChanged += value;
- remove => _propertyChanged -= value;
+ return false;
}
- /// <summary>
- /// Checks if a property already matches a desired value. Sets the property and
- /// notifies listeners only when necessary.
- /// </summary>
- /// <typeparam name="T">Type of the property.</typeparam>
- /// <param name="storage">Reference to a property with both getter and setter.</param>
- /// <param name="value">Desired value for the property.</param>
- /// <param name="propertyName">
- /// Name of the property used to notify listeners. This
- /// value is optional and can be provided automatically when invoked from compilers that
- /// support CallerMemberName.
- /// </param>
- /// <returns>
- /// True if the value was changed, false if the existing value matched the
- /// desired value.
- /// </returns>
- /*protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
- {
- if (Equals(storage, value))
- {
- return false;
- }
+ storage = value;
+ RaisePropertyChanged(propertyName);
+ return true;
+ }*/
- storage = value;
- RaisePropertyChanged(propertyName);
- return true;
- }*/
-
- protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
- {
- if (EqualityComparer<T>.Default.Equals(field, value)) return false;
- field = value;
- RaisePropertyChanged(propertyName);
- return true;
- }
+ protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
+ {
+ if (EqualityComparer<T>.Default.Equals(field, value)) return false;
+ field = value;
+ RaisePropertyChanged(propertyName);
+ return true;
+ }
- protected void RaiseAndSet<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
- {
- field = value;
- RaisePropertyChanged(propertyName);
- }
+ protected void RaiseAndSet<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
+ {
+ field = value;
+ RaisePropertyChanged(propertyName);
+ }
- protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
- {
- }
+ protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ }
- /// <summary>
- /// Notifies listeners that a property value has changed.
- /// </summary>
- /// <param name="propertyName">
- /// Name of the property used to notify listeners. This
- /// value is optional and can be provided automatically when invoked from compilers
- /// that support <see cref="CallerMemberNameAttribute" />.
- /// </param>
- protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
- {
- var e = new PropertyChangedEventArgs(propertyName);
- OnPropertyChanged(e);
- _propertyChanged?.Invoke(this, e);
- }
+ /// <summary>
+ /// Notifies listeners that a property value has changed.
+ /// </summary>
+ /// <param name="propertyName">
+ /// Name of the property used to notify listeners. This
+ /// value is optional and can be provided automatically when invoked from compilers
+ /// that support <see cref="CallerMemberNameAttribute" />.
+ /// </param>
+ protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ var e = new PropertyChangedEventArgs(propertyName);
+ OnPropertyChanged(e);
+ _propertyChanged?.Invoke(this, e);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/ExposureItem.cs b/UVtools.Core/Objects/ExposureItem.cs
index fdab730..8c94b17 100644
--- a/UVtools.Core/Objects/ExposureItem.cs
+++ b/UVtools.Core/Objects/ExposureItem.cs
@@ -1,107 +1,106 @@
using System;
using UVtools.Core.Layers;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+[Serializable]
+public sealed class ExposureItem : BindableBase, IComparable<ExposureItem>
{
- [Serializable]
- public sealed class ExposureItem : BindableBase, IComparable<ExposureItem>
+ private decimal _layerHeight;
+ private decimal _bottomExposure;
+ private decimal _exposure;
+ private byte _brightness = byte.MaxValue;
+
+ /// <summary>
+ /// Gets or sets the layer height in millimeters
+ /// </summary>
+ public decimal LayerHeight
{
- private decimal _layerHeight;
- private decimal _bottomExposure;
- private decimal _exposure;
- private byte _brightness = byte.MaxValue;
-
- /// <summary>
- /// Gets or sets the layer height in millimeters
- /// </summary>
- public decimal LayerHeight
- {
- get => _layerHeight;
- set => RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value));
- }
+ get => _layerHeight;
+ set => RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value));
+ }
- /// <summary>
- /// Gets or sets the bottom exposure in seconds
- /// </summary>
- public decimal BottomExposure
- {
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
- }
+ /// <summary>
+ /// Gets or sets the bottom exposure in seconds
+ /// </summary>
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
- /// <summary>
- /// Gets or sets the bottom exposure in seconds
- /// </summary>
- public decimal Exposure
- {
- get => _exposure;
- set => RaiseAndSetIfChanged(ref _exposure, Math.Round(value, 2));
- }
+ /// <summary>
+ /// Gets or sets the bottom exposure in seconds
+ /// </summary>
+ public decimal Exposure
+ {
+ get => _exposure;
+ set => RaiseAndSetIfChanged(ref _exposure, Math.Round(value, 2));
+ }
- /// <summary>
- /// Gets or sets the brightness level
- /// </summary>
- public byte Brightness
+ /// <summary>
+ /// Gets or sets the brightness level
+ /// </summary>
+ public byte Brightness
+ {
+ get => _brightness;
+ set
{
- get => _brightness;
- set
- {
- if(!RaiseAndSetIfChanged(ref _brightness, value)) return;
- RaisePropertyChanged(nameof(BrightnessPercent));
- }
+ if(!RaiseAndSetIfChanged(ref _brightness, value)) return;
+ RaisePropertyChanged(nameof(BrightnessPercent));
}
+ }
- public decimal BrightnessPercent => Math.Round(_brightness * 100m / byte.MaxValue, 2);
+ public decimal BrightnessPercent => Math.Round(_brightness * 100m / byte.MaxValue, 2);
- public bool IsValid => _layerHeight > 0 && _bottomExposure > 0 && _exposure > 0 && _brightness > 0;
+ public bool IsValid => _layerHeight > 0 && _bottomExposure > 0 && _exposure > 0 && _brightness > 0;
- public ExposureItem() { }
+ public ExposureItem() { }
- public ExposureItem(decimal layerHeight, decimal bottomExposure = 0, decimal exposure = 0, byte brightness = 255)
- {
- _layerHeight = Layer.RoundHeight(layerHeight);
- _bottomExposure = Math.Round(bottomExposure, 2);
- _exposure = Math.Round(exposure, 2);
- _brightness = brightness;
- }
+ public ExposureItem(decimal layerHeight, decimal bottomExposure = 0, decimal exposure = 0, byte brightness = 255)
+ {
+ _layerHeight = Layer.RoundHeight(layerHeight);
+ _bottomExposure = Math.Round(bottomExposure, 2);
+ _exposure = Math.Round(exposure, 2);
+ _brightness = brightness;
+ }
- public override string ToString()
- {
- return $"{nameof(LayerHeight)}: {_layerHeight}mm, {nameof(BottomExposure)}: {_bottomExposure}s, {nameof(Exposure)}: {_exposure}s, {nameof(Brightness)}: {_brightness} ({BrightnessPercent} %)";
- }
+ public override string ToString()
+ {
+ return $"{nameof(LayerHeight)}: {_layerHeight}mm, {nameof(BottomExposure)}: {_bottomExposure}s, {nameof(Exposure)}: {_exposure}s, {nameof(Brightness)}: {_brightness} ({BrightnessPercent} %)";
+ }
- private bool Equals(ExposureItem other)
- {
- return _layerHeight == other._layerHeight && _bottomExposure == other._bottomExposure && _exposure == other._exposure && _brightness == other._brightness;
- }
+ private bool Equals(ExposureItem other)
+ {
+ return _layerHeight == other._layerHeight && _bottomExposure == other._bottomExposure && _exposure == other._exposure && _brightness == other._brightness;
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is ExposureItem other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is ExposureItem other && Equals(other);
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_layerHeight, _bottomExposure, _exposure, _brightness);
- }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_layerHeight, _bottomExposure, _exposure, _brightness);
+ }
- public int CompareTo(ExposureItem other)
- {
- if (ReferenceEquals(this, other)) return 0;
- if (ReferenceEquals(null, other)) return 1;
- var layerHeightComparison = _layerHeight.CompareTo(other._layerHeight);
- if (layerHeightComparison != 0) return layerHeightComparison;
- var bottomExposureComparison = _bottomExposure.CompareTo(other._bottomExposure);
- if (bottomExposureComparison != 0) return bottomExposureComparison;
- var exposureComparison = _exposure.CompareTo(other._exposure);
- if (exposureComparison != 0) return exposureComparison;
- return _brightness.CompareTo(other._brightness);
- }
+ public int CompareTo(ExposureItem? other)
+ {
+ if (ReferenceEquals(this, other)) return 0;
+ if (ReferenceEquals(null, other)) return 1;
+ var layerHeightComparison = _layerHeight.CompareTo(other._layerHeight);
+ if (layerHeightComparison != 0) return layerHeightComparison;
+ var bottomExposureComparison = _bottomExposure.CompareTo(other._bottomExposure);
+ if (bottomExposureComparison != 0) return bottomExposureComparison;
+ var exposureComparison = _exposure.CompareTo(other._exposure);
+ if (exposureComparison != 0) return exposureComparison;
+ return _brightness.CompareTo(other._brightness);
+ }
- public ExposureItem Clone()
- {
- return MemberwiseClone() as ExposureItem;
- }
+ public ExposureItem Clone()
+ {
+ return (MemberwiseClone() as ExposureItem)!;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/KernelConfiguration.cs b/UVtools.Core/Objects/KernelConfiguration.cs
index 9e03d82..779450d 100644
--- a/UVtools.Core/Objects/KernelConfiguration.cs
+++ b/UVtools.Core/Objects/KernelConfiguration.cs
@@ -6,236 +6,235 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.Managers;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+[Serializable]
+public sealed class KernelConfiguration : BindableBase, IDisposable
{
- [Serializable]
- public sealed class KernelConfiguration : BindableBase, IDisposable
- {
- #region Members
- private bool _useDynamicKernel;
- private KernelCacheManager _kernelCache = new();
- private ElementShape _kernelShape = ElementShape.Rectangle;
- private uint _matrixWidth = 3;
- private uint _matrixHeight = 3;
- private string _matrixText = "1 1 1\n1 1 1\n1 1 1";
- private int _anchorX = -1;
- private int _anchorY = -1;
- private Mat _kernelMat;
- private object _mutex = new();
- #endregion
-
- #region Properties
- public bool UseDynamicKernel
+ #region Members
+ private bool _useDynamicKernel;
+ private readonly KernelCacheManager _kernelCache = new();
+ private ElementShape _kernelShape = ElementShape.Rectangle;
+ private uint _matrixWidth = 3;
+ private uint _matrixHeight = 3;
+ private string _matrixText = "1 1 1\n1 1 1\n1 1 1";
+ private int _anchorX = -1;
+ private int _anchorY = -1;
+ private Mat? _kernelMat;
+ private readonly object _mutex = new();
+ #endregion
+
+ #region Properties
+ public bool UseDynamicKernel
+ {
+ get => _useDynamicKernel;
+ set
{
- get => _useDynamicKernel;
- set
- {
- if(!RaiseAndSetIfChanged(ref _useDynamicKernel, value)) return;
- _kernelCache.UseDynamicKernel = value;
- }
+ if(!RaiseAndSetIfChanged(ref _useDynamicKernel, value)) return;
+ _kernelCache.UseDynamicKernel = value;
}
+ }
- public int AnchorX
- {
- get => _anchorX;
- set => RaiseAndSetIfChanged(ref _anchorX, value);
- }
+ public int AnchorX
+ {
+ get => _anchorX;
+ set => RaiseAndSetIfChanged(ref _anchorX, value);
+ }
- public int AnchorY
- {
- get => _anchorY;
- set => RaiseAndSetIfChanged(ref _anchorY, value);
- }
+ public int AnchorY
+ {
+ get => _anchorY;
+ set => RaiseAndSetIfChanged(ref _anchorY, value);
+ }
- public string MatrixText
- {
- get => _matrixText;
- set => RaiseAndSetIfChanged(ref _matrixText, value);
- }
+ public string MatrixText
+ {
+ get => _matrixText;
+ set => RaiseAndSetIfChanged(ref _matrixText, value);
+ }
- public uint MatrixWidth
- {
- get => _matrixWidth;
- set => RaiseAndSetIfChanged(ref _matrixWidth, value);
- }
+ public uint MatrixWidth
+ {
+ get => _matrixWidth;
+ set => RaiseAndSetIfChanged(ref _matrixWidth, value);
+ }
- public uint MatrixHeight
- {
- get => _matrixHeight;
- set => RaiseAndSetIfChanged(ref _matrixHeight, value);
- }
+ public uint MatrixHeight
+ {
+ get => _matrixHeight;
+ set => RaiseAndSetIfChanged(ref _matrixHeight, value);
+ }
- public Size MatrixSize => new((int)_matrixWidth, (int)_matrixHeight);
+ public Size MatrixSize => new((int)_matrixWidth, (int)_matrixHeight);
- [XmlIgnore]
- public Mat KernelMat
+ [XmlIgnore]
+ public Mat? KernelMat
+ {
+ get
{
- get
+ lock (_mutex)
{
- lock (_mutex)
+ if (_kernelMat is null)
{
- if (_kernelMat is null)
- {
- if (!string.IsNullOrWhiteSpace(_matrixText))
- {
- GenerateKernelFromText();
- }
- else
- {
- SetKernel(ElementShape.Rectangle);
- }
+ if (!string.IsNullOrWhiteSpace(_matrixText))
+ {
+ GenerateKernelFromText();
+ }
+ else
+ {
+ SetKernel(ElementShape.Rectangle);
}
}
-
- return _kernelMat;
}
- set => _kernelMat = value;
+
+ return _kernelMat;
}
+ set => _kernelMat = value;
+ }
+
+ public ElementShape KernelShape
+ {
+ get => _kernelShape;
+ set => RaiseAndSetIfChanged(ref _kernelShape, value);
+ }
+
+ public IEnumerable<ElementShape> KernelShapes => ((ElementShape[])Enum.GetValues(typeof(ElementShape))).Where(element => element != ElementShape.Custom);
- public ElementShape KernelShape
+ public Point Anchor
+ {
+ get => new(_anchorX, _anchorY);
+ set
{
- get => _kernelShape;
- set => RaiseAndSetIfChanged(ref _kernelShape, value);
+ _anchorX = value.X;
+ _anchorY = value.Y;
}
+ }
+ #endregion
+
+ #region Constructor
+ public KernelConfiguration() { }
+ #endregion
- public IEnumerable<ElementShape> KernelShapes => ((ElementShape[])Enum.GetValues(typeof(ElementShape))).Where(element => element != ElementShape.Custom);
+ #region Methods
+ public void ResetKernel()
+ {
+ KernelShape = ElementShape.Rectangle;
+ MatrixWidth = 3;
+ MatrixHeight = 3;
+ AnchorX = -1;
+ AnchorY = -1;
+ _kernelMat?.Dispose();
+ _kernelMat = null;
+ MatrixText = "1 1 1\n1 1 1\n1 1 1";
+ }
- public Point Anchor
+ public void GenerateKernelText()
+ {
+ using var kernel = CvInvoke.GetStructuringElement(KernelShape, MatrixSize, Anchor);
+ string text = string.Empty;
+ for (int y = 0; y < kernel.Height; y++)
{
- get => new(_anchorX, _anchorY);
- set
+ var span = kernel.GetRowSpan<byte>(y);
+ var line = string.Empty;
+ for (int x = 0; x < span.Length; x++)
{
- _anchorX = value.X;
- _anchorY = value.Y;
+ line += $" {span[x]}";
+ }
+
+ line = line.Remove(0, 1);
+ text += line;
+ if (y < kernel.Height - 1)
+ {
+ text += '\n';
}
}
- #endregion
- #region Constructor
- public KernelConfiguration() { }
- #endregion
+ MatrixText = text;
+ }
- #region Methods
- public void ResetKernel()
+ public void GenerateKernelFromText()
+ {
+ if (string.IsNullOrEmpty(_matrixText))
{
- KernelShape = ElementShape.Rectangle;
- MatrixWidth = 3;
- MatrixHeight = 3;
- AnchorX = -1;
- AnchorY = -1;
- _kernelMat?.Dispose();
- _kernelMat = null;
- MatrixText = "1 1 1\n1 1 1\n1 1 1";
+ throw new InvalidOperationException("Invalid kernel: Kernel can't be empty.");
}
-
- public void GenerateKernelText()
+ Matrix<byte> matrix = null!;
+ var lines = _matrixText.Split('\n');
+ for (var row = 0; row < lines.Length; row++)
{
- using var kernel = CvInvoke.GetStructuringElement(KernelShape, MatrixSize, Anchor);
- string text = string.Empty;
- for (int y = 0; y < kernel.Height; y++)
+ var bytes = lines[row].Trim().Split(' ');
+ if (row == 0)
{
- var span = kernel.GetRowSpan<byte>(y);
- var line = string.Empty;
- for (int x = 0; x < span.Length; x++)
- {
- line += $" {span[x]}";
- }
-
- line = line.Remove(0, 1);
- text += line;
- if (y < kernel.Height - 1)
+ matrix = new Matrix<byte>(lines.Length, bytes.Length);
+ }
+ else
+ {
+ if (matrix.Cols != bytes.Length)
{
- text += '\n';
+ matrix.Dispose();
+ throw new InvalidOperationException($"Invalid kernel: Row {row + 1} have invalid number of columns, the matrix must have equal columns count per line, per defined on line 1");
}
}
- MatrixText = text;
- }
-
- public void GenerateKernelFromText()
- {
- if (string.IsNullOrEmpty(_matrixText))
- {
- throw new InvalidOperationException("Invalid kernel: Kernel can't be empty.");
- }
- Matrix<byte> matrix = null;
- var lines = _matrixText.Split('\n');
- for (var row = 0; row < lines.Length; row++)
+ for (int col = 0; col < bytes.Length; col++)
{
- var bytes = lines[row].Trim().Split(' ');
- if (row == 0)
+ if (byte.TryParse(bytes[col], out var value))
{
- matrix = new Matrix<byte>(lines.Length, bytes.Length);
+ matrix[row, col] = value;
}
else
{
- if (matrix.Cols != bytes.Length)
- {
- matrix.Dispose();
- throw new InvalidOperationException($"Invalid kernel: Row {row + 1} have invalid number of columns, the matrix must have equal columns count per line, per defined on line 1");
- }
- }
-
- for (int col = 0; col < bytes.Length; col++)
- {
- if (byte.TryParse(bytes[col], out var value))
- {
- matrix[row, col] = value;
- }
- else
- {
- matrix.Dispose();
- throw new InvalidOperationException($"Invalid kernel: {bytes[col]} is a invalid number, use values from 0 to 255");
- }
+ matrix.Dispose();
+ throw new InvalidOperationException($"Invalid kernel: {bytes[col]} is a invalid number, use values from 0 to 255");
}
}
- if (matrix.Cols <= _anchorX || matrix.Rows <= _anchorY)
- {
- matrix.Dispose();
- throw new InvalidOperationException("Invalid kernel: Anchor position X/Y can't be higher or equal than kernel columns/rows\nPlease fix the values.");
- }
-
- _kernelMat?.Dispose();
- KernelMat = matrix.Mat;
}
-
-
- public void SetKernel(ElementShape shape, Size size, Point anchor)
+ if (matrix.Cols <= _anchorX || matrix.Rows <= _anchorY)
{
- KernelMat = CvInvoke.GetStructuringElement(shape, size, anchor);
+ matrix.Dispose();
+ throw new InvalidOperationException("Invalid kernel: Anchor position X/Y can't be higher or equal than kernel columns/rows\nPlease fix the values.");
}
- public void SetKernel(ElementShape shape, Size size) => SetKernel(shape, size, new Point(-1, -1));
- public void SetKernel(ElementShape shape) => SetKernel(shape, new Size(3, 3), new Point(-1, -1));
+ _kernelMat?.Dispose();
+ KernelMat = matrix.Mat;
+ }
- public Mat GetKernel()
- {
- return KernelMat;
- }
- public Mat GetKernel(ref int iterations)
- {
- if (!UseDynamicKernel) return KernelMat;
- return _kernelCache.Get(ref iterations);
- }
+ public void SetKernel(ElementShape shape, Size size, Point anchor)
+ {
+ KernelMat = CvInvoke.GetStructuringElement(shape, size, anchor);
+ }
- public void Dispose()
- {
- _kernelMat?.Dispose();
- _kernelCache?.Dispose();
- }
- #endregion
+ public void SetKernel(ElementShape shape, Size size) => SetKernel(shape, size, new Point(-1, -1));
+ public void SetKernel(ElementShape shape) => SetKernel(shape, new Size(3, 3), new Point(-1, -1));
+
+ public Mat? GetKernel()
+ {
+ return KernelMat;
+ }
+
+ public Mat? GetKernel(ref int iterations)
+ {
+ if (!UseDynamicKernel) return KernelMat;
+ return _kernelCache.Get(ref iterations);
+ }
+
+ public void Dispose()
+ {
+ _kernelMat?.Dispose();
+ _kernelCache?.Dispose();
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/MappedProcess.cs b/UVtools.Core/Objects/MappedProcess.cs
index 6060097..addfe3f 100644
--- a/UVtools.Core/Objects/MappedProcess.cs
+++ b/UVtools.Core/Objects/MappedProcess.cs
@@ -12,162 +12,160 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using UVtools.Core.FileFormats;
-using UVtools.Core.Objects;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+public class MappedProcess : BindableBase
{
- public class MappedProcess : BindableBase
- {
- #region Constants
+ #region Constants
- public const string DefaultArgument = "\"{0}\"";
- #endregion
+ public const string DefaultArgument = "\"{0}\"";
+ #endregion
- #region Members
+ #region Members
- private bool _isEnabled = true;
- private string _applicationPath;
- private string _name;
- private string _arguments = DefaultArgument;
- private string _compatibleExtensions;
- private bool _waitForExit;
+ private bool _isEnabled = true;
+ private string _applicationPath = null!;
+ private string? _name;
+ private string _arguments = DefaultArgument;
+ private string? _compatibleExtensions;
+ private bool _waitForExit;
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- /// <summary>
- /// Gets or sets if this device is enabled
- /// </summary>
- public bool IsEnabled
- {
- get => _isEnabled;
- set => RaiseAndSetIfChanged(ref _isEnabled, value);
- }
+ /// <summary>
+ /// Gets or sets if this device is enabled
+ /// </summary>
+ public bool IsEnabled
+ {
+ get => _isEnabled;
+ set => RaiseAndSetIfChanged(ref _isEnabled, value);
+ }
- /// <summary>
- /// Gets or sets the full path for the application
- /// </summary>
- public string ApplicationPath
- {
- get => _applicationPath;
- set => RaiseAndSetIfChanged(ref _applicationPath, value);
- }
+ /// <summary>
+ /// Gets or sets the full path for the application
+ /// </summary>
+ public string ApplicationPath
+ {
+ get => _applicationPath;
+ set => RaiseAndSetIfChanged(ref _applicationPath, value);
+ }
- /// <summary>
- /// Gets or sets the path name alias
- /// </summary>
- public string Name
- {
- get => _name;
- set => RaiseAndSetIfChanged(ref _name, value);
- }
+ /// <summary>
+ /// Gets or sets the path name alias
+ /// </summary>
+ public string? Name
+ {
+ get => _name;
+ set => RaiseAndSetIfChanged(ref _name, value);
+ }
- /// <summary>
- /// Gets or sets the arguments for the application
- /// </summary>
- public string Arguments
- {
- get => _arguments;
- set => RaiseAndSetIfChanged(ref _arguments, value);
- }
+ /// <summary>
+ /// Gets or sets the arguments for the application
+ /// </summary>
+ public string Arguments
+ {
+ get => _arguments;
+ set => RaiseAndSetIfChanged(ref _arguments, value);
+ }
- /// <summary>
- /// Gets or sets the compatible extensions with this device.
- /// Empty or null to be compatible with everything
- /// </summary>
- public string CompatibleExtensions
- {
- get => _compatibleExtensions;
- set => RaiseAndSetIfChanged(ref _compatibleExtensions, value);
- }
+ /// <summary>
+ /// Gets or sets the compatible extensions with this device.
+ /// Empty or null to be compatible with everything
+ /// </summary>
+ public string? CompatibleExtensions
+ {
+ get => _compatibleExtensions;
+ set => RaiseAndSetIfChanged(ref _compatibleExtensions, value);
+ }
- public bool WaitForExit
- {
- get => _waitForExit;
- set => RaiseAndSetIfChanged(ref _waitForExit, value);
- }
+ public bool WaitForExit
+ {
+ get => _waitForExit;
+ set => RaiseAndSetIfChanged(ref _waitForExit, value);
+ }
- #endregion
+ #endregion
- #region Contructors
+ #region Contructors
- public MappedProcess() { }
+ public MappedProcess() { }
- public MappedProcess(bool isEnabled, string applicationPath, string name = null, string arguments = DefaultArgument)
+ public MappedProcess(bool isEnabled, string applicationPath, string? name = null, string arguments = DefaultArgument)
+ {
+ _isEnabled = isEnabled;
+ _applicationPath = applicationPath;
+ if (string.IsNullOrWhiteSpace(name))
{
- _isEnabled = isEnabled;
- _applicationPath = applicationPath;
- if (string.IsNullOrWhiteSpace(name))
- {
- name = Path.GetFileNameWithoutExtension(applicationPath);
- }
- _name = name;
-
- if (string.IsNullOrWhiteSpace(arguments))
- {
- arguments = DefaultArgument;
- }
-
- _arguments = arguments;
+ name = Path.GetFileNameWithoutExtension(applicationPath);
}
+ _name = name;
- public MappedProcess(string applicationPath, string name = null, string arguments = DefaultArgument) : this(true, applicationPath, name, arguments)
- { }
+ if (string.IsNullOrWhiteSpace(arguments))
+ {
+ arguments = DefaultArgument;
+ }
- #endregion
+ _arguments = arguments;
+ }
- #region Methods
+ public MappedProcess(string applicationPath, string? name = null, string arguments = DefaultArgument) : this(true, applicationPath, name, arguments)
+ { }
- public bool IsValid()
- {
- return File.Exists(_applicationPath);
- }
+ #endregion
- public async Task StartProcess(FileFormat slicerFile, CancellationToken cancellationToken = default)
- {
- await StartProcess(slicerFile.FileFullPath, cancellationToken);
- }
+ #region Methods
- public async Task StartProcess(string slicerFile, CancellationToken cancellationToken = default)
- {
- var arguments = string.IsNullOrWhiteSpace(_arguments) ? $"\"{slicerFile}\"" : string.Format(_arguments, slicerFile);
- using var process = Process.Start(_applicationPath, arguments);
- if (process is null) return;
- if (_waitForExit)
- {
- await process.WaitForExitAsync(cancellationToken);
- }
- }
+ public bool IsValid()
+ {
+ return File.Exists(_applicationPath);
+ }
- public override string ToString()
- {
- return $"{_applicationPath} {Arguments}";
- }
+ public async Task StartProcess(FileFormat slicerFile, CancellationToken cancellationToken = default)
+ {
+ await StartProcess(slicerFile.FileFullPath!, cancellationToken);
+ }
- protected bool Equals(MappedProcess other)
+ public async Task StartProcess(string slicerFile, CancellationToken cancellationToken = default)
+ {
+ var arguments = string.IsNullOrWhiteSpace(_arguments) ? $"\"{slicerFile}\"" : string.Format(_arguments, slicerFile);
+ using var process = Process.Start(_applicationPath, arguments);
+ if (process is null) return;
+ if (_waitForExit)
{
- return _applicationPath == other._applicationPath && _name == other._name && _arguments == other._arguments && _compatibleExtensions == other._compatibleExtensions;
+ await process.WaitForExitAsync(cancellationToken);
}
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((MappedProcess)obj);
- }
+ public override string ToString()
+ {
+ return $"{_applicationPath} {Arguments}";
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_applicationPath, _name, _arguments, _compatibleExtensions);
- }
+ protected bool Equals(MappedProcess other)
+ {
+ return _applicationPath == other._applicationPath && _name == other._name && _arguments == other._arguments && _compatibleExtensions == other._compatibleExtensions;
+ }
- public MappedProcess Clone()
- {
- return MemberwiseClone() as MappedProcess;
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((MappedProcess)obj);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_applicationPath, _name, _arguments, _compatibleExtensions);
}
-}
+
+ public MappedProcess Clone()
+ {
+ return (MemberwiseClone() as MappedProcess)!;
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/Material.cs b/UVtools.Core/Objects/Material.cs
index d469c5a..621ac7c 100644
--- a/UVtools.Core/Objects/Material.cs
+++ b/UVtools.Core/Objects/Material.cs
@@ -8,266 +8,265 @@
using System;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+/// <summary>
+/// Represents a material to feed in the printer
+/// </summary>
+public class Material : BindableBase, ICloneable
{
+ #region Members
+ private string _name = null!;
+ private uint _bottleVolume = 1000;
+ private decimal _density = 1;
+ private decimal _bottleCost = 30;
+ private int _bottlesInStock = 1;
+ private decimal _bottleRemainingVolume = 1000;
+ private decimal _consumedVolume;
+ private double _printTime;
+
+ #endregion
+
+ #region Properties
+ public string Name
+ {
+ get => _name;
+ set => RaiseAndSetIfChanged(ref _name, value);
+ }
+
/// <summary>
- /// Represents a material to feed in the printer
+ /// Gets or sets the bottle volume in milliliters
/// </summary>
- public class Material : BindableBase, ICloneable
+ public uint BottleVolume
{
- #region Members
- private string _name;
- private uint _bottleVolume = 1000;
- private decimal _density = 1;
- private decimal _bottleCost = 30;
- private int _bottlesInStock = 1;
- private decimal _bottleRemainingVolume = 1000;
- private decimal _consumedVolume;
- private double _printTime;
-
- #endregion
-
- #region Properties
- public string Name
- {
- get => _name;
- set => RaiseAndSetIfChanged(ref _name, value);
- }
-
- /// <summary>
- /// Gets or sets the bottle volume in milliliters
- /// </summary>
- public uint BottleVolume
+ get => _bottleVolume;
+ set
{
- get => _bottleVolume;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottleVolume, value)) return;
- RaisePropertyChanged(nameof(BottleWeight));
- RaisePropertyChanged(nameof(ConsumedBottles));
- RaisePropertyChanged(nameof(TotalCost));
- RaisePropertyChanged(nameof(VolumeInStock));
- }
+ if(!RaiseAndSetIfChanged(ref _bottleVolume, value)) return;
+ RaisePropertyChanged(nameof(BottleWeight));
+ RaisePropertyChanged(nameof(ConsumedBottles));
+ RaisePropertyChanged(nameof(TotalCost));
+ RaisePropertyChanged(nameof(VolumeInStock));
}
+ }
- /// <summary>
- /// Gets or sets the bottle weight in grams
- /// </summary>
- public decimal BottleWeight => _bottleVolume * _density;
+ /// <summary>
+ /// Gets or sets the bottle weight in grams
+ /// </summary>
+ public decimal BottleWeight => _bottleVolume * _density;
- /// <summary>
- /// Gets or sets the material density in g/ml
- /// </summary>
- public decimal Density
+ /// <summary>
+ /// Gets or sets the material density in g/ml
+ /// </summary>
+ public decimal Density
+ {
+ get => _density;
+ set
{
- get => _density;
- set
- {
- if(!RaiseAndSetIfChanged(ref _density, value)) return;
- RaisePropertyChanged(nameof(BottleWeight));
- }
+ if(!RaiseAndSetIfChanged(ref _density, value)) return;
+ RaisePropertyChanged(nameof(BottleWeight));
}
+ }
- /// <summary>
- /// Gets or sets the bottle cost
- /// </summary>
- public decimal BottleCost
+ /// <summary>
+ /// Gets or sets the bottle cost
+ /// </summary>
+ public decimal BottleCost
+ {
+ get => _bottleCost;
+ set
{
- get => _bottleCost;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottleCost, value)) return;
- RaisePropertyChanged(nameof(TotalCost));
- }
+ if(!RaiseAndSetIfChanged(ref _bottleCost, value)) return;
+ RaisePropertyChanged(nameof(TotalCost));
}
+ }
- public decimal TotalCost => OwnedBottles * _bottleCost;
+ public decimal TotalCost => OwnedBottles * _bottleCost;
- /// <summary>
- /// Gets or sets the number of bottles in stock
- /// </summary>
- public int BottlesInStock
+ /// <summary>
+ /// Gets or sets the number of bottles in stock
+ /// </summary>
+ public int BottlesInStock
+ {
+ get => _bottlesInStock;
+ set
{
- get => _bottlesInStock;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottlesInStock, value)) return;
- RaisePropertyChanged(nameof(OwnedBottles));
- RaisePropertyChanged(nameof(TotalCost));
- RaisePropertyChanged(nameof(VolumeInStock));
- }
+ if(!RaiseAndSetIfChanged(ref _bottlesInStock, value)) return;
+ RaisePropertyChanged(nameof(OwnedBottles));
+ RaisePropertyChanged(nameof(TotalCost));
+ RaisePropertyChanged(nameof(VolumeInStock));
}
+ }
- /// <summary>
- /// Gets or sets the current bottle remaining material in milliliters
- /// </summary>
- public decimal BottleRemainingVolume
+ /// <summary>
+ /// Gets or sets the current bottle remaining material in milliliters
+ /// </summary>
+ public decimal BottleRemainingVolume
+ {
+ get => Math.Round(_bottleRemainingVolume, 2);
+ set
{
- get => Math.Round(_bottleRemainingVolume, 2);
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottleRemainingVolume, value)) return;
- RaisePropertyChanged(nameof(VolumeInStock));
- }
+ if(!RaiseAndSetIfChanged(ref _bottleRemainingVolume, value)) return;
+ RaisePropertyChanged(nameof(VolumeInStock));
}
+ }
- /// <summary>
- /// Gets the total available volume in stock in milliliters
- /// </summary>
- public decimal VolumeInStock => _bottlesInStock * _bottleVolume - (_bottleVolume - _bottleRemainingVolume);
-
- /// <summary>
- /// Gets the number of consumed bottles
- /// </summary>
- public uint ConsumedBottles => (uint)(_consumedVolume / _bottleVolume);
-
- /// <summary>
- /// Gets the total number of owned bottles
- /// </summary>
- public int OwnedBottles => (int) (_bottlesInStock + ConsumedBottles);
-
- /// <summary>
- /// Gets or sets the total number of consumed volume in milliliters
- /// </summary>
- public decimal ConsumedVolume
+ /// <summary>
+ /// Gets the total available volume in stock in milliliters
+ /// </summary>
+ public decimal VolumeInStock => _bottlesInStock * _bottleVolume - (_bottleVolume - _bottleRemainingVolume);
+
+ /// <summary>
+ /// Gets the number of consumed bottles
+ /// </summary>
+ public uint ConsumedBottles => (uint)(_consumedVolume / _bottleVolume);
+
+ /// <summary>
+ /// Gets the total number of owned bottles
+ /// </summary>
+ public int OwnedBottles => (int) (_bottlesInStock + ConsumedBottles);
+
+ /// <summary>
+ /// Gets or sets the total number of consumed volume in milliliters
+ /// </summary>
+ public decimal ConsumedVolume
+ {
+ get => _consumedVolume;
+ set
{
- get => _consumedVolume;
- set
- {
- if(!RaiseAndSetIfChanged(ref _consumedVolume, value)) return;
- RaisePropertyChanged(nameof(ConsumedVolumeLiters));
- }
+ if(!RaiseAndSetIfChanged(ref _consumedVolume, value)) return;
+ RaisePropertyChanged(nameof(ConsumedVolumeLiters));
}
+ }
- /// <summary>
- /// Gets total number of consumed volume in liters
- /// </summary>
- public decimal ConsumedVolumeLiters => ConsumedVolume / 1000;
+ /// <summary>
+ /// Gets total number of consumed volume in liters
+ /// </summary>
+ public decimal ConsumedVolumeLiters => ConsumedVolume / 1000;
- /// <summary>
- /// Gets or sets the total print time using with material in hours
- /// </summary>
- public double PrintTime
+ /// <summary>
+ /// Gets or sets the total print time using with material in hours
+ /// </summary>
+ public double PrintTime
+ {
+ get => _printTime;
+ set
{
- get => _printTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _printTime, value)) return;
- RaisePropertyChanged(nameof(PrintTimeSpan));
- }
+ if(!RaiseAndSetIfChanged(ref _printTime, value)) return;
+ RaisePropertyChanged(nameof(PrintTimeSpan));
}
+ }
- public TimeSpan PrintTimeSpan => TimeSpan.FromHours(_printTime);
+ public TimeSpan PrintTimeSpan => TimeSpan.FromHours(_printTime);
- #endregion
+ #endregion
- #region Constructors
- public Material() { }
+ #region Constructors
+ public Material() { }
- public Material(string name, uint bottleVolume = 1000, decimal density = 1, decimal bottleCost = 30, int bottlesInStock = 1)
- {
- _name = name;
- _bottleVolume = bottleVolume;
- _density = density;
- _bottleCost = bottleCost;
- _bottlesInStock = bottlesInStock;
- _bottleRemainingVolume = bottleVolume;
- }
- #endregion
+ public Material(string name, uint bottleVolume = 1000, decimal density = 1, decimal bottleCost = 30, int bottlesInStock = 1)
+ {
+ _name = name;
+ _bottleVolume = bottleVolume;
+ _density = density;
+ _bottleCost = bottleCost;
+ _bottlesInStock = bottlesInStock;
+ _bottleRemainingVolume = bottleVolume;
+ }
+ #endregion
- #region Overrides
+ #region Overrides
- protected bool Equals(Material other)
- {
- return _name == other._name;
- }
+ protected bool Equals(Material other)
+ {
+ return _name == other._name;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((Material) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((Material) obj);
+ }
- public override int GetHashCode()
- {
- return (_name != null ? _name.GetHashCode() : 0);
- }
+ public override int GetHashCode()
+ {
+ return (_name != null ? _name.GetHashCode() : 0);
+ }
- public override string ToString()
- {
- return $"{_name} ({_bottleRemainingVolume}/{VolumeInStock}ml)";
- }
+ public override string ToString()
+ {
+ return $"{_name} ({_bottleRemainingVolume}/{VolumeInStock}ml)";
+ }
- public object Clone()
- {
- return MemberwiseClone();
- }
+ public object Clone()
+ {
+ return MemberwiseClone();
+ }
- public Material CloneMaterial()
- {
- return (Material)Clone();
- }
+ public Material CloneMaterial()
+ {
+ return (Material)Clone();
+ }
- #endregion
-
- #region Methods
- /// <summary>
- /// Gets the cost for a given volume
- /// </summary>
- /// <param name="volume">Volume in ml</param>
- /// <returns></returns>
- public decimal GetVolumeCost(decimal volume) => _bottleVolume > 0 ? volume * _bottleCost / _bottleVolume : 0;
-
- /// <summary>
- /// Gets the grams for a given volume
- /// </summary>
- /// <param name="volume">Volume in ml</param>
- /// <returns></returns>
- public decimal GetVolumeGrams(decimal volume) => volume * _density;
-
- /// <summary>
- /// Consume material from current bottle and manage stock
- /// </summary>
- /// <param name="volume">Volume to consume in milliliters</param>
- /// <param name="printSeconds">Time in seconds it took to print</param>
- /// <returns>True if still have bottles in stock, otherwise false</returns>
- public bool Consume(decimal volume, double printSeconds = 0)
- {
- if (volume <= 0 || _bottleVolume == 0) return true; // Safe check
- int consumedBottles = (int)(volume / _bottleVolume);
- decimal remainder = volume % _bottleVolume;
+ #endregion
- if (remainder > 0)
+ #region Methods
+ /// <summary>
+ /// Gets the cost for a given volume
+ /// </summary>
+ /// <param name="volume">Volume in ml</param>
+ /// <returns></returns>
+ public decimal GetVolumeCost(decimal volume) => _bottleVolume > 0 ? volume * _bottleCost / _bottleVolume : 0;
+
+ /// <summary>
+ /// Gets the grams for a given volume
+ /// </summary>
+ /// <param name="volume">Volume in ml</param>
+ /// <returns></returns>
+ public decimal GetVolumeGrams(decimal volume) => volume * _density;
+
+ /// <summary>
+ /// Consume material from current bottle and manage stock
+ /// </summary>
+ /// <param name="volume">Volume to consume in milliliters</param>
+ /// <param name="printSeconds">Time in seconds it took to print</param>
+ /// <returns>True if still have bottles in stock, otherwise false</returns>
+ public bool Consume(decimal volume, double printSeconds = 0)
+ {
+ if (volume <= 0 || _bottleVolume == 0) return true; // Safe check
+ int consumedBottles = (int)(volume / _bottleVolume);
+ decimal remainder = volume % _bottleVolume;
+
+ if (remainder > 0)
+ {
+ decimal remainingVolume = _bottleRemainingVolume - remainder;
+ if (remainingVolume < 0)
{
- decimal remainingVolume = _bottleRemainingVolume - remainder;
- if (remainingVolume < 0)
- {
- consumedBottles++;
- remainingVolume += _bottleVolume;
- }
-
- BottleRemainingVolume = remainingVolume;
+ consumedBottles++;
+ remainingVolume += _bottleVolume;
}
- BottlesInStock -= consumedBottles;
- ConsumedVolume += volume;
+ BottleRemainingVolume = remainingVolume;
+ }
- AddPrintTimeSeconds(printSeconds);
+ BottlesInStock -= consumedBottles;
+ ConsumedVolume += volume;
- return _bottlesInStock > 0;
- }
+ AddPrintTimeSeconds(printSeconds);
- /// <summary>
- /// Add print time with this material
- /// </summary>
- /// <param name="seconds">Seconds to add</param>
- public void AddPrintTimeSeconds(double seconds)
- {
- if (seconds <= 0) return;
- PrintTime += seconds / 60 / 60;
- }
- #endregion
+ return _bottlesInStock > 0;
+ }
+
+ /// <summary>
+ /// Add print time with this material
+ /// </summary>
+ /// <param name="seconds">Seconds to add</param>
+ public void AddPrintTimeSeconds(double seconds)
+ {
+ if (seconds <= 0) return;
+ PrintTime += seconds / 60 / 60;
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs b/UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs
index ff71690..6f93276 100644
--- a/UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs
+++ b/UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs
@@ -8,54 +8,53 @@
using BinarySerialization;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+/// <summary>
+/// A string that always end with 0x00 if not null
+/// It contains the string length as uint
+/// </summary>
+public sealed class NullTerminatedUintStringBigEndian
{
- /// <summary>
- /// A string that always end with 0x00 if not null
- /// It contains the string length as uint
- /// </summary>
- public sealed class NullTerminatedUintStringBigEndian
- {
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)]
- public uint SerializedLength { get; set; }
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)]
+ public uint SerializedLength { get; set; }
- [FieldOrder(1)] [FieldLength(nameof(SerializedLength))]
- public string SerializedValue { get; set; }
+ [FieldOrder(1)] [FieldLength(nameof(SerializedLength))]
+ public string? SerializedValue { get; set; }
- [Ignore]
- public string Value
- {
- get => SerializedValue?.TrimEnd(char.MinValue);
- set => SerializedValue = value is null ? null : $"{value}{char.MinValue}";
- }
+ [Ignore]
+ public string? Value
+ {
+ get => SerializedValue?.TrimEnd(char.MinValue);
+ set => SerializedValue = value is null ? null : $"{value}{char.MinValue}";
+ }
- public NullTerminatedUintStringBigEndian() { }
+ public NullTerminatedUintStringBigEndian() { }
- public NullTerminatedUintStringBigEndian(string value)
- {
- Value = value;
- }
+ public NullTerminatedUintStringBigEndian(string value)
+ {
+ Value = value;
+ }
- public override string ToString() => Value;
+ public override string? ToString() => Value;
- private bool Equals(NullTerminatedUintStringBigEndian other)
- {
- return SerializedValue == other.SerializedValue;
- }
-
- public bool Equals(string value)
- {
- return Value == value;
- }
-
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is NullTerminatedUintStringBigEndian other && Equals(other);
- }
-
- public override int GetHashCode()
- {
- return (SerializedValue != null ? SerializedValue.GetHashCode() : 0);
- }
+ private bool Equals(NullTerminatedUintStringBigEndian other)
+ {
+ return SerializedValue == other.SerializedValue;
+ }
+
+ public bool Equals(string value)
+ {
+ return Value == value;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is NullTerminatedUintStringBigEndian other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return (SerializedValue != null ? SerializedValue.GetHashCode() : 0);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/RangeObservableCollection.cs b/UVtools.Core/Objects/RangeObservableCollection.cs
index 0e73004..ececee5 100644
--- a/UVtools.Core/Objects/RangeObservableCollection.cs
+++ b/UVtools.Core/Objects/RangeObservableCollection.cs
@@ -1,713 +1,711 @@
-namespace System.Collections.ObjectModel
+namespace System.Collections.ObjectModel;
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+
+public static class RangeObservableCollectionExtensions
{
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
+ public static RangeObservableCollection<T> ToRangeObservableCollection<T>(this IEnumerable<T> collection)
+ {
+ var list = new RangeObservableCollection<T>();
+ list.AddRange(collection);
+ return list;
+ }
+}
+
+/// <summary>
+/// Implementation of a dynamic data collection based on generic Collection&lt;T&gt;,
+/// implementing INotifyCollectionChanged to notify listeners
+/// when items get added, removed or the whole list is refreshed.
+/// </summary>
+public class RangeObservableCollection<T> : ObservableCollection<T>
+{
+ //------------------------------------------------------
+ //
+ // Private Fields
+ //
+ //------------------------------------------------------
+
+ #region Private Fields
+ [NonSerialized]
+ private DeferredEventsCollection? _deferredEvents;
+ #endregion Private Fields
+
+
+ //------------------------------------------------------
+ //
+ // Constructors
+ //
+ //------------------------------------------------------
+
+ #region Constructors
+ /// <summary>
+ /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity.
+ /// </summary>
+ public RangeObservableCollection() { }
+
+ /// <summary>
+ /// Initializes a new instance of the ObservableCollection class that contains
+ /// elements copied from the specified collection and has sufficient capacity
+ /// to accommodate the number of elements copied.
+ /// </summary>
+ /// <param name="collection">The collection whose elements are copied to the new list.</param>
+ /// <remarks>
+ /// The elements are copied onto the ObservableCollection in the
+ /// same order they are read by the enumerator of the collection.
+ /// </remarks>
+ /// <exception cref="ArgumentNullException"> collection is a null reference </exception>
+ public RangeObservableCollection(IEnumerable<T> collection) : base(collection) { }
+
+ /// <summary>
+ /// Initializes a new instance of the ObservableCollection class
+ /// that contains elements copied from the specified list
+ /// </summary>
+ /// <param name="list">The list whose elements are copied to the new list.</param>
+ /// <remarks>
+ /// The elements are copied onto the ObservableCollection in the
+ /// same order they are read by the enumerator of the list.
+ /// </remarks>
+ /// <exception cref="ArgumentNullException"> list is a null reference </exception>
+ public RangeObservableCollection(List<T> list) : base(list) { }
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Linq;
+ #endregion Constructors
- public static class RangeObservableCollectionExtensions
+ //------------------------------------------------------
+ //
+ // Public Properties
+ //
+ //------------------------------------------------------
+
+ #region Public Properties
+ EqualityComparer<T>? _Comparer;
+ public EqualityComparer<T> Comparer
{
- public static RangeObservableCollection<T> ToRangeObservableCollection<T>(this IEnumerable<T> collection)
- {
- var list = new RangeObservableCollection<T>();
- list.AddRange(collection);
- return list;
- }
+ get => _Comparer ??= EqualityComparer<T>.Default;
+ private set => _Comparer = value;
}
/// <summary>
- /// Implementation of a dynamic data collection based on generic Collection&lt;T&gt;,
- /// implementing INotifyCollectionChanged to notify listeners
- /// when items get added, removed or the whole list is refreshed.
+ /// Gets or sets a value indicating whether this collection acts as a <see cref="HashSet{T}"/>,
+ /// disallowing duplicate items, based on <see cref="Comparer"/>.
+ /// This might indeed consume background performance, but in the other hand,
+ /// it will pay off in UI performance as less required UI updates are required.
/// </summary>
- public class RangeObservableCollection<T> : ObservableCollection<T>
+ public bool AllowDuplicates { get; set; } = true;
+
+ #endregion Public Properties
+
+ //------------------------------------------------------
+ //
+ // Public Methods
+ //
+ //------------------------------------------------------
+
+ #region Public Methods
+
+ /// <summary>
+ /// Clear the list and set a collection in place
+ /// </summary>
+ /// <param name="collection">
+ /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
+ /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
+ /// </param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void ReplaceCollection(IEnumerable<T> collection)
{
- //------------------------------------------------------
- //
- // Private Fields
- //
- //------------------------------------------------------
-
- #region Private Fields
- [NonSerialized]
- private DeferredEventsCollection? _deferredEvents;
- #endregion Private Fields
-
-
- //------------------------------------------------------
- //
- // Constructors
- //
- //------------------------------------------------------
-
- #region Constructors
- /// <summary>
- /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity.
- /// </summary>
- public RangeObservableCollection() { }
-
- /// <summary>
- /// Initializes a new instance of the ObservableCollection class that contains
- /// elements copied from the specified collection and has sufficient capacity
- /// to accommodate the number of elements copied.
- /// </summary>
- /// <param name="collection">The collection whose elements are copied to the new list.</param>
- /// <remarks>
- /// The elements are copied onto the ObservableCollection in the
- /// same order they are read by the enumerator of the collection.
- /// </remarks>
- /// <exception cref="ArgumentNullException"> collection is a null reference </exception>
- public RangeObservableCollection(IEnumerable<T> collection) : base(collection) { }
-
- /// <summary>
- /// Initializes a new instance of the ObservableCollection class
- /// that contains elements copied from the specified list
- /// </summary>
- /// <param name="list">The list whose elements are copied to the new list.</param>
- /// <remarks>
- /// The elements are copied onto the ObservableCollection in the
- /// same order they are read by the enumerator of the list.
- /// </remarks>
- /// <exception cref="ArgumentNullException"> list is a null reference </exception>
- public RangeObservableCollection(List<T> list) : base(list) { }
-
- #endregion Constructors
-
- //------------------------------------------------------
- //
- // Public Properties
- //
- //------------------------------------------------------
-
- #region Public Properties
- EqualityComparer<T>? _Comparer;
- public EqualityComparer<T> Comparer
- {
- get => _Comparer ??= EqualityComparer<T>.Default;
- private set => _Comparer = value;
- }
+ Clear();
+ AddRange(collection);
+ }
- /// <summary>
- /// Gets or sets a value indicating whether this collection acts as a <see cref="HashSet{T}"/>,
- /// disallowing duplicate items, based on <see cref="Comparer"/>.
- /// This might indeed consume background performance, but in the other hand,
- /// it will pay off in UI performance as less required UI updates are required.
- /// </summary>
- public bool AllowDuplicates { get; set; } = true;
-
- #endregion Public Properties
-
- //------------------------------------------------------
- //
- // Public Methods
- //
- //------------------------------------------------------
-
- #region Public Methods
-
- /// <summary>
- /// Clear the list and set a collection in place
- /// </summary>
- /// <param name="collection">
- /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
- /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
- /// </param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public void ReplaceCollection(IEnumerable<T> collection)
- {
- Clear();
- AddRange(collection);
- }
+ /// <summary>
+ /// Adds the elements of the specified collection to the end of the <see cref="ObservableCollection{T}"/>.
+ /// </summary>
+ /// <param name="collection">
+ /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
+ /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
+ /// </param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void AddRange(IEnumerable<T> collection)
+ {
+ InsertRange(Count, collection);
+ }
- /// <summary>
- /// Adds the elements of the specified collection to the end of the <see cref="ObservableCollection{T}"/>.
- /// </summary>
- /// <param name="collection">
- /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
- /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
- /// </param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public void AddRange(IEnumerable<T> collection)
+ /// <summary>
+ /// Inserts the elements of a collection into the <see cref="ObservableCollection{T}"/> at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index at which the new elements should be inserted.</param>
+ /// <param name="collection">The collection whose elements should be inserted into the List<T>.
+ /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is not in the collection range.</exception>
+ public void InsertRange(int index, IEnumerable<T> collection)
+ {
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ if (!AllowDuplicates)
+ collection =
+ collection
+ .Distinct(Comparer)
+ .Where(item => !Items.Contains(item, Comparer))
+ .ToList();
+
+ if (collection is ICollection<T> countable)
{
- InsertRange(Count, collection);
+ if (countable.Count == 0)
+ return;
}
+ else if (!collection.Any())
+ return;
- /// <summary>
- /// Inserts the elements of a collection into the <see cref="ObservableCollection{T}"/> at the specified index.
- /// </summary>
- /// <param name="index">The zero-based index at which the new elements should be inserted.</param>
- /// <param name="collection">The collection whose elements should be inserted into the List<T>.
- /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.</param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is not in the collection range.</exception>
- public void InsertRange(int index, IEnumerable<T> collection)
- {
- if (collection == null)
- throw new ArgumentNullException(nameof(collection));
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index));
- if (index > Count)
- throw new ArgumentOutOfRangeException(nameof(index));
-
- if (!AllowDuplicates)
- collection =
- collection
- .Distinct(Comparer)
- .Where(item => !Items.Contains(item, Comparer))
- .ToList();
-
- if (collection is ICollection<T> countable)
- {
- if (countable.Count == 0)
- return;
- }
- else if (!collection.Any())
- return;
+ CheckReentrancy();
- CheckReentrancy();
+ //expand the following couple of lines when adding more constructors.
+ var target = (List<T>)Items;
+ target.InsertRange(index, collection);
- //expand the following couple of lines when adding more constructors.
- var target = (List<T>)Items;
- target.InsertRange(index, collection);
+ OnEssentialPropertiesChanged();
- OnEssentialPropertiesChanged();
+ /*if (!(collection is IList list))
+ list = new List<T>(collection);
- /*if (!(collection is IList list))
- list = new List<T>(collection);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, list, index));*/
+ OnCollectionReset();
+ }
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, list, index));*/
- OnCollectionReset();
- }
+ /// <summary>
+ /// Removes the first occurence of each item in the specified collection from the <see cref="ObservableCollection{T}"/>.
+ /// </summary>
+ /// <param name="collection">The items to remove.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void RemoveRange(IEnumerable<T> collection)
+ {
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
- /// <summary>
- /// Removes the first occurence of each item in the specified collection from the <see cref="ObservableCollection{T}"/>.
- /// </summary>
- /// <param name="collection">The items to remove.</param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public void RemoveRange(IEnumerable<T> collection)
+ if (Count == 0)
+ return;
+ else if (collection is ICollection<T> countable)
{
- if (collection == null)
- throw new ArgumentNullException(nameof(collection));
-
- if (Count == 0)
- return;
- else if (collection is ICollection<T> countable)
+ switch (countable.Count)
{
- switch (countable.Count)
+ case 0:
+ return;
+ case 1:
{
- case 0:
- return;
- case 1:
- {
- using IEnumerator<T> enumerator = countable.GetEnumerator();
- enumerator.MoveNext();
- Remove(enumerator.Current);
- return;
- }
+ using IEnumerator<T> enumerator = countable.GetEnumerator();
+ enumerator.MoveNext();
+ Remove(enumerator.Current);
+ return;
}
}
- else if (!collection.Any())
- return;
+ }
+ else if (!collection.Any())
+ return;
- CheckReentrancy();
+ CheckReentrancy();
- //var clusters = new Dictionary<int, List<T>>();
- //var lastIndex = -1;
- //List<T>? lastCluster = null;
- foreach (T item in collection)
- {
- var index = IndexOf(item);
- if (index < 0)
- continue;
+ //var clusters = new Dictionary<int, List<T>>();
+ //var lastIndex = -1;
+ //List<T>? lastCluster = null;
+ foreach (T item in collection)
+ {
+ var index = IndexOf(item);
+ if (index < 0)
+ continue;
- Items.RemoveAt(index);
+ Items.RemoveAt(index);
- /*if (lastIndex == index && lastCluster != null)
- lastCluster.Add(item);
- else
- clusters[lastIndex = index] = lastCluster = new List<T> { item };*/
- }
+ /*if (lastIndex == index && lastCluster != null)
+ lastCluster.Add(item);
+ else
+ clusters[lastIndex = index] = lastCluster = new List<T> { item };*/
+ }
- OnEssentialPropertiesChanged();
- OnCollectionReset();
+ OnEssentialPropertiesChanged();
+ OnCollectionReset();
- /*if (Count == 0)
- OnCollectionReset();
- else
- foreach (var cluster in clusters)
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster.Value, cluster.Key));*/
+ /*if (Count == 0)
+ OnCollectionReset();
+ else
+ foreach (var cluster in clusters)
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster.Value, cluster.Key));*/
- }
+ }
- /// <summary>
- /// Iterates over the collection and removes all items that satisfy the specified match.
- /// </summary>
- /// <remarks>The complexity is O(n).</remarks>
- /// <param name="match"></param>
- /// <returns>Returns the number of elements that where </returns>
- /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
- public int RemoveAll(Predicate<T> match)
- {
- return RemoveAll(0, Count, match);
- }
+ /// <summary>
+ /// Iterates over the collection and removes all items that satisfy the specified match.
+ /// </summary>
+ /// <remarks>The complexity is O(n).</remarks>
+ /// <param name="match"></param>
+ /// <returns>Returns the number of elements that where </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
+ public int RemoveAll(Predicate<T> match)
+ {
+ return RemoveAll(0, Count, match);
+ }
- /// <summary>
- /// Iterates over the specified range within the collection and removes all items that satisfy the specified match.
- /// </summary>
- /// <remarks>The complexity is O(n).</remarks>
- /// <param name="index">The index of where to start performing the search.</param>
- /// <param name="count">The number of items to iterate on.</param>
- /// <param name="match"></param>
- /// <returns>Returns the number of elements that where </returns>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
- public int RemoveAll(int index, int count, Predicate<T> match)
+ /// <summary>
+ /// Iterates over the specified range within the collection and removes all items that satisfy the specified match.
+ /// </summary>
+ /// <remarks>The complexity is O(n).</remarks>
+ /// <param name="index">The index of where to start performing the search.</param>
+ /// <param name="count">The number of items to iterate on.</param>
+ /// <param name="match"></param>
+ /// <returns>Returns the number of elements that where </returns>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
+ public int RemoveAll(int index, int count, Predicate<T> match)
+ {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count));
+ if (index + count > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (match == null)
+ throw new ArgumentNullException(nameof(match));
+
+ if (Count == 0)
+ return 0;
+
+ //List<T>? cluster = null;
+ //var clusterIndex = -1;
+ var removedCount = 0;
+
+ using (BlockReentrancy())
+ using (DeferEvents())
{
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index));
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count));
- if (index + count > Count)
- throw new ArgumentOutOfRangeException(nameof(index));
- if (match == null)
- throw new ArgumentNullException(nameof(match));
-
- if (Count == 0)
- return 0;
-
- //List<T>? cluster = null;
- //var clusterIndex = -1;
- var removedCount = 0;
-
- using (BlockReentrancy())
- using (DeferEvents())
+ for (var i = 0; i < count; i++, index++)
{
- for (var i = 0; i < count; i++, index++)
+ T item = Items[index];
+ if (match(item))
{
- T item = Items[index];
- if (match(item))
+ Items.RemoveAt(index);
+ removedCount++;
+
+ /*if (clusterIndex == index)
{
- Items.RemoveAt(index);
- removedCount++;
-
- /*if (clusterIndex == index)
- {
- Debug.Assert(cluster != null);
- cluster!.Add(item);
- }
- else
- {
- cluster = new List<T> { item };
- clusterIndex = index;
- }*/
-
- index--;
+ Debug.Assert(cluster != null);
+ cluster!.Add(item);
}
- /*else if (clusterIndex > -1)
+ else
{
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
- clusterIndex = -1;
- cluster = null;
+ cluster = new List<T> { item };
+ clusterIndex = index;
}*/
- }
- //if (clusterIndex > -1)
- // OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
- }
-
- if (removedCount > 0)
- {
- OnEssentialPropertiesChanged();
- OnCollectionReset();
+ index--;
+ }
+ /*else if (clusterIndex > -1)
+ {
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
+ clusterIndex = -1;
+ cluster = null;
+ }*/
}
- return removedCount;
+ //if (clusterIndex > -1)
+ // OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
}
- /// <summary>
- /// Removes a range of elements from the <see cref="ObservableCollection{T}"/>>.
- /// </summary>
- /// <param name="index">The zero-based starting index of the range of elements to remove.</param>
- /// <param name="count">The number of elements to remove.</param>
- /// <exception cref="ArgumentOutOfRangeException">The specified range is exceeding the collection.</exception>
- public void RemoveRange(int index, int count)
+ if (removedCount > 0)
{
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index));
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count));
- if (index + count > Count)
- throw new ArgumentOutOfRangeException(nameof(index));
+ OnEssentialPropertiesChanged();
+ OnCollectionReset();
+ }
- if (count == 0)
- return;
+ return removedCount;
+ }
- if (count == 1)
- {
- RemoveItem(index);
- return;
- }
+ /// <summary>
+ /// Removes a range of elements from the <see cref="ObservableCollection{T}"/>>.
+ /// </summary>
+ /// <param name="index">The zero-based starting index of the range of elements to remove.</param>
+ /// <param name="count">The number of elements to remove.</param>
+ /// <exception cref="ArgumentOutOfRangeException">The specified range is exceeding the collection.</exception>
+ public void RemoveRange(int index, int count)
+ {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count));
+ if (index + count > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
- //Items will always be List<T>, see constructors
- var items = (List<T>)Items;
- List<T> removedItems = items.GetRange(index, count);
+ if (count == 0)
+ return;
- CheckReentrancy();
+ if (count == 1)
+ {
+ RemoveItem(index);
+ return;
+ }
- items.RemoveRange(index, count);
+ //Items will always be List<T>, see constructors
+ var items = (List<T>)Items;
+ List<T> removedItems = items.GetRange(index, count);
- OnEssentialPropertiesChanged();
+ CheckReentrancy();
- if (Count == 0)
- OnCollectionReset();
- else
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, index));
- }
+ items.RemoveRange(index, count);
- /// <summary>
- /// Clears the current collection and replaces it with the specified collection,
- /// using <see cref="Comparer"/>.
- /// </summary>
- /// <param name="collection">The items to fill the collection with, after clearing it.</param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public void ReplaceRange(IEnumerable<T> collection)
- {
- ReplaceRange(0, Count, collection);
- }
+ OnEssentialPropertiesChanged();
+
+ if (Count == 0)
+ OnCollectionReset();
+ else
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, index));
+ }
+
+ /// <summary>
+ /// Clears the current collection and replaces it with the specified collection,
+ /// using <see cref="Comparer"/>.
+ /// </summary>
+ /// <param name="collection">The items to fill the collection with, after clearing it.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void ReplaceRange(IEnumerable<T> collection)
+ {
+ ReplaceRange(0, Count, collection);
+ }
- /// <summary>
- /// Removes the specified range and inserts the specified collection in its position, leaving equal items in equal positions intact.
- /// </summary>
- /// <param name="index">The index of where to start the replacement.</param>
- /// <param name="count">The number of items to be replaced.</param>
- /// <param name="collection">The collection to insert in that location.</param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
- public void ReplaceRange(int index, int count, IEnumerable<T> collection)
+ /// <summary>
+ /// Removes the specified range and inserts the specified collection in its position, leaving equal items in equal positions intact.
+ /// </summary>
+ /// <param name="index">The index of where to start the replacement.</param>
+ /// <param name="count">The number of items to be replaced.</param>
+ /// <param name="collection">The collection to insert in that location.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
+ public void ReplaceRange(int index, int count, IEnumerable<T> collection)
+ {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count));
+ if (index + count > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+
+ if (!AllowDuplicates)
+ collection =
+ collection
+ .Distinct(Comparer)
+ .ToList();
+
+ if (collection is ICollection<T> countable)
{
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index));
- if (count < 0)
- throw new ArgumentOutOfRangeException(nameof(count));
- if (index + count > Count)
- throw new ArgumentOutOfRangeException(nameof(index));
-
- if (collection == null)
- throw new ArgumentNullException(nameof(collection));
-
- if (!AllowDuplicates)
- collection =
- collection
- .Distinct(Comparer)
- .ToList();
-
- if (collection is ICollection<T> countable)
- {
- if (countable.Count == 0)
- {
- RemoveRange(index, count);
- return;
- }
- }
- else if (!collection.Any())
+ if (countable.Count == 0)
{
RemoveRange(index, count);
return;
}
+ }
+ else if (!collection.Any())
+ {
+ RemoveRange(index, count);
+ return;
+ }
- if (index + count == 0)
- {
- InsertRange(0, collection);
- return;
- }
+ if (index + count == 0)
+ {
+ InsertRange(0, collection);
+ return;
+ }
- if (collection is not IList<T> list)
- list = new List<T>(collection);
+ if (collection is not IList<T> list)
+ list = new List<T>(collection);
- using (BlockReentrancy())
- using (DeferEvents())
- {
- var rangeCount = index + count;
- var addedCount = list.Count;
+ using (BlockReentrancy())
+ using (DeferEvents())
+ {
+ var rangeCount = index + count;
+ var addedCount = list.Count;
- var changesMade = false;
- List<T>?
- newCluster = null,
- oldCluster = null;
+ var changesMade = false;
+ List<T>?
+ newCluster = null,
+ oldCluster = null;
- int i = index;
- for (; i < rangeCount && i - index < addedCount; i++)
+ int i = index;
+ for (; i < rangeCount && i - index < addedCount; i++)
+ {
+ //parallel position
+ T old = this[i], @new = list[i - index];
+ if (Comparer.Equals(old, @new))
{
- //parallel position
- T old = this[i], @new = list[i - index];
- if (Comparer.Equals(old, @new))
+ OnRangeReplaced(i, newCluster!, oldCluster!);
+ continue;
+ }
+ else
+ {
+ Items[i] = @new;
+
+ if (newCluster == null)
{
- OnRangeReplaced(i, newCluster!, oldCluster!);
- continue;
+ Debug.Assert(oldCluster == null);
+ newCluster = new List<T> { @new };
+ oldCluster = new List<T> { old };
}
else
{
- Items[i] = @new;
-
- if (newCluster == null)
- {
- Debug.Assert(oldCluster == null);
- newCluster = new List<T> { @new };
- oldCluster = new List<T> { old };
- }
- else
- {
- newCluster.Add(@new);
- oldCluster!.Add(old);
- }
-
- changesMade = true;
+ newCluster.Add(@new);
+ oldCluster!.Add(old);
}
+
+ changesMade = true;
}
+ }
- OnRangeReplaced(i, newCluster!, oldCluster!);
+ OnRangeReplaced(i, newCluster!, oldCluster!);
- //exceeding position
- if (count != addedCount)
+ //exceeding position
+ if (count != addedCount)
+ {
+ var items = (List<T>)Items;
+ if (count > addedCount)
{
- var items = (List<T>)Items;
- if (count > addedCount)
- {
- var removedCount = rangeCount - addedCount;
- T[] removed = new T[removedCount];
- items.CopyTo(i, removed, 0, removed.Length);
- items.RemoveRange(i, removedCount);
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, i));
- }
- else
- {
- var k = i - index;
- T[] added = new T[addedCount - k];
- for (int j = k; j < addedCount; j++)
- {
- T @new = list[j];
- added[j - k] = @new;
- }
- items.InsertRange(i, added);
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, i));
- }
-
- OnEssentialPropertiesChanged();
+ var removedCount = rangeCount - addedCount;
+ T[] removed = new T[removedCount];
+ items.CopyTo(i, removed, 0, removed.Length);
+ items.RemoveRange(i, removedCount);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, i));
}
- else if (changesMade)
+ else
{
- OnIndexerPropertyChanged();
+ var k = i - index;
+ T[] added = new T[addedCount - k];
+ for (int j = k; j < addedCount; j++)
+ {
+ T @new = list[j];
+ added[j - k] = @new;
+ }
+ items.InsertRange(i, added);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, i));
}
+
+ OnEssentialPropertiesChanged();
+ }
+ else if (changesMade)
+ {
+ OnIndexerPropertyChanged();
}
}
+ }
- public void Sort()
- {
- Sort(null);
- }
+ public void Sort()
+ {
+ Sort(null);
+ }
- public void Sort(Comparison<T> comparison)
+ public void Sort(Comparison<T>? comparison)
+ {
+ if (Count == 0)
{
- if (Count == 0)
- {
- return;
- }
-
- var list = this.ToList();
- list.Sort(comparison);
- ReplaceCollection(list);
+ return;
}
- #endregion Public Methods
+ var list = this.ToList();
+ list.Sort(comparison!);
+ ReplaceCollection(list);
+ }
+ #endregion Public Methods
- //------------------------------------------------------
- //
- // Protected Methods
- //
- //------------------------------------------------------
- #region Protected Methods
+ //------------------------------------------------------
+ //
+ // Protected Methods
+ //
+ //------------------------------------------------------
- /// <summary>
- /// Called by base class Collection&lt;T&gt; when the list is being cleared;
- /// raises a CollectionChanged event to any listeners.
- /// </summary>
- protected override void ClearItems()
- {
- if (Count == 0)
- return;
+ #region Protected Methods
- //CheckReentrancy();
- base.ClearItems();
- //OnEssentialPropertiesChanged();
- //OnCollectionReset();
- }
+ /// <summary>
+ /// Called by base class Collection&lt;T&gt; when the list is being cleared;
+ /// raises a CollectionChanged event to any listeners.
+ /// </summary>
+ protected override void ClearItems()
+ {
+ if (Count == 0)
+ return;
- /// <inheritdoc/>
- protected override void InsertItem(int index, T item)
- {
- if (!AllowDuplicates && Items.Contains(item))
- return;
+ //CheckReentrancy();
+ base.ClearItems();
+ //OnEssentialPropertiesChanged();
+ //OnCollectionReset();
+ }
- base.InsertItem(index, item);
- }
+ /// <inheritdoc/>
+ protected override void InsertItem(int index, T item)
+ {
+ if (!AllowDuplicates && Items.Contains(item))
+ return;
- /// <inheritdoc/>
- protected override void SetItem(int index, T item)
+ base.InsertItem(index, item);
+ }
+
+ /// <inheritdoc/>
+ protected override void SetItem(int index, T item)
+ {
+ if (AllowDuplicates)
{
- if (AllowDuplicates)
- {
- if (Comparer.Equals(this[index], item))
- return;
- }
- else
- if (Items.Contains(item, Comparer))
+ if (Comparer.Equals(this[index], item))
return;
+ }
+ else
+ if (Items.Contains(item, Comparer))
+ return;
- CheckReentrancy();
- T oldItem = this[index];
- base.SetItem(index, item);
+ CheckReentrancy();
+ T oldItem = this[index];
+ base.SetItem(index, item);
- OnIndexerPropertyChanged();
- OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem!, item!, index);
- }
+ OnIndexerPropertyChanged();
+ OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem!, item!, index);
+ }
- /// <summary>
- /// Raise CollectionChanged event to any listeners.
- /// Properties/methods modifying this ObservableCollection will raise
- /// a collection changed event through this virtual method.
- /// </summary>
- /// <remarks>
- /// When overriding this method, either call its base implementation
- /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
- /// </remarks>
- protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ /// <summary>
+ /// Raise CollectionChanged event to any listeners.
+ /// Properties/methods modifying this ObservableCollection will raise
+ /// a collection changed event through this virtual method.
+ /// </summary>
+ /// <remarks>
+ /// When overriding this method, either call its base implementation
+ /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
+ /// </remarks>
+ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ if (_deferredEvents != null)
{
- if (_deferredEvents != null)
- {
- _deferredEvents.Add(e);
- return;
- }
- base.OnCollectionChanged(e);
+ _deferredEvents.Add(e);
+ return;
}
+ base.OnCollectionChanged(e);
+ }
- protected virtual IDisposable DeferEvents() => new DeferredEventsCollection(this);
+ protected virtual IDisposable DeferEvents() => new DeferredEventsCollection(this);
- #endregion Protected Methods
+ #endregion Protected Methods
- //------------------------------------------------------
- //
- // Private Methods
- //
- //------------------------------------------------------
+ //------------------------------------------------------
+ //
+ // Private Methods
+ //
+ //------------------------------------------------------
- #region Private Methods
+ #region Private Methods
- /// <summary>
- /// Helper to raise Count property and the Indexer property.
- /// </summary>
- void OnEssentialPropertiesChanged()
- {
- OnPropertyChanged(EventArgsCache.CountPropertyChanged);
- OnIndexerPropertyChanged();
- }
+ /// <summary>
+ /// Helper to raise Count property and the Indexer property.
+ /// </summary>
+ void OnEssentialPropertiesChanged()
+ {
+ OnPropertyChanged(EventArgsCache.CountPropertyChanged);
+ OnIndexerPropertyChanged();
+ }
+
+ /// <summary>
+ /// /// Helper to raise a PropertyChanged event for the Indexer property
+ /// /// </summary>
+ void OnIndexerPropertyChanged() =>
+ OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
+
+ /// <summary>
+ /// Helper to raise CollectionChanged event to any listeners
+ /// </summary>
+ void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
- /// <summary>
- /// /// Helper to raise a PropertyChanged event for the Indexer property
- /// /// </summary>
- void OnIndexerPropertyChanged() =>
- OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
-
- /// <summary>
- /// Helper to raise CollectionChanged event to any listeners
- /// </summary>
- void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
-
- /// <summary>
- /// Helper to raise CollectionChanged event with action == Reset to any listeners
- /// </summary>
- void OnCollectionReset() =>
- OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
-
- /// <summary>
- /// Helper to raise event for clustered action and clear cluster.
- /// </summary>
- /// <param name="followingItemIndex">The index of the item following the replacement block.</param>
- /// <param name="newCluster"></param>
- /// <param name="oldCluster"></param>
- //TODO should have really been a local method inside ReplaceRange(int index, int count, IEnumerable<T> collection, IEqualityComparer<T> comparer),
- //move when supported language version updated.
- void OnRangeReplaced(int followingItemIndex, ICollection<T> newCluster, ICollection<T> oldCluster)
+ /// <summary>
+ /// Helper to raise CollectionChanged event with action == Reset to any listeners
+ /// </summary>
+ void OnCollectionReset() =>
+ OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
+
+ /// <summary>
+ /// Helper to raise event for clustered action and clear cluster.
+ /// </summary>
+ /// <param name="followingItemIndex">The index of the item following the replacement block.</param>
+ /// <param name="newCluster"></param>
+ /// <param name="oldCluster"></param>
+ //TODO should have really been a local method inside ReplaceRange(int index, int count, IEnumerable<T> collection, IEqualityComparer<T> comparer),
+ //move when supported language version updated.
+ void OnRangeReplaced(int followingItemIndex, ICollection<T> newCluster, ICollection<T> oldCluster)
+ {
+ if (oldCluster == null || oldCluster.Count == 0)
{
- if (oldCluster == null || oldCluster.Count == 0)
- {
- Debug.Assert(newCluster == null || newCluster.Count == 0);
- return;
- }
+ Debug.Assert(newCluster == null || newCluster.Count == 0);
+ return;
+ }
- OnCollectionChanged(
- new NotifyCollectionChangedEventArgs(
+ OnCollectionChanged(
+ new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Replace,
new List<T>(newCluster),
new List<T>(oldCluster),
followingItemIndex - oldCluster.Count));
- oldCluster.Clear();
- newCluster.Clear();
- }
+ oldCluster.Clear();
+ newCluster.Clear();
+ }
- #endregion Private Methods
+ #endregion Private Methods
- //------------------------------------------------------
- //
- // Private Types
- //
- //------------------------------------------------------
+ //------------------------------------------------------
+ //
+ // Private Types
+ //
+ //------------------------------------------------------
- #region Private Types
- sealed class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
+ #region Private Types
+ sealed class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
+ {
+ readonly RangeObservableCollection<T> _collection;
+ public DeferredEventsCollection(RangeObservableCollection<T> collection)
{
- readonly RangeObservableCollection<T> _collection;
- public DeferredEventsCollection(RangeObservableCollection<T> collection)
- {
- Debug.Assert(collection != null);
- Debug.Assert(collection._deferredEvents == null);
- _collection = collection;
- _collection._deferredEvents = this;
- }
+ Debug.Assert(collection != null);
+ Debug.Assert(collection._deferredEvents == null);
+ _collection = collection;
+ _collection._deferredEvents = this;
+ }
- public void Dispose()
- {
- _collection._deferredEvents = null;
- foreach (var args in this)
- _collection.OnCollectionChanged(args);
- }
+ public void Dispose()
+ {
+ _collection._deferredEvents = null;
+ foreach (var args in this)
+ _collection.OnCollectionChanged(args);
}
+ }
- #endregion Private Types
+ #endregion Private Types
- }
+}
- /// <remarks>
- /// To be kept outside <see cref="ObservableCollection{T}"/>, since otherwise, a new instance will be created for each generic type used.
- /// </remarks>
- internal static class EventArgsCache
- {
- internal static readonly PropertyChangedEventArgs CountPropertyChanged = new("Count");
- internal static readonly PropertyChangedEventArgs IndexerPropertyChanged = new("Item[]");
- internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new(NotifyCollectionChangedAction.Reset);
- }
+/// <remarks>
+/// To be kept outside <see cref="ObservableCollection{T}"/>, since otherwise, a new instance will be created for each generic type used.
+/// </remarks>
+internal static class EventArgsCache
+{
+ internal static readonly PropertyChangedEventArgs CountPropertyChanged = new("Count");
+ internal static readonly PropertyChangedEventArgs IndexerPropertyChanged = new("Item[]");
+ internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new(NotifyCollectionChangedAction.Reset);
} \ No newline at end of file
diff --git a/UVtools.Core/Objects/StaticObjects.cs b/UVtools.Core/Objects/StaticObjects.cs
index 4be5b4e..580a547 100644
--- a/UVtools.Core/Objects/StaticObjects.cs
+++ b/UVtools.Core/Objects/StaticObjects.cs
@@ -1,20 +1,18 @@
-using System;
-using System.IO;
+using System.IO;
using System.Security.Cryptography;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+public static class StaticObjects
{
- public static class StaticObjects
- {
- public static readonly SHA256 Sha256 = SHA256.Create();
+ public static readonly SHA256 Sha256 = SHA256.Create();
- public static readonly string[] LineBreakCharacters = {"\r\n", "\r", "\n"};
+ public static readonly string[] LineBreakCharacters = {"\r\n", "\r", "\n"};
- // Compute the file's hash.
- public static byte[] GetHashSha256(string filename)
- {
- using var stream = File.OpenRead(filename);
- return Sha256.ComputeHash(stream);
- }
+ // Compute the file's hash.
+ public static byte[] GetHashSha256(string filename)
+ {
+ using var stream = File.OpenRead(filename);
+ return Sha256.ComputeHash(stream);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Objects/ValueDescription.cs b/UVtools.Core/Objects/ValueDescription.cs
index 84bfe3d..b584cc9 100644
--- a/UVtools.Core/Objects/ValueDescription.cs
+++ b/UVtools.Core/Objects/ValueDescription.cs
@@ -8,65 +8,64 @@
using System;
-namespace UVtools.Core.Objects
+namespace UVtools.Core.Objects;
+
+public class ValueDescription : BindableBase, IEquatable<ValueDescription>
{
- public class ValueDescription : BindableBase, IEquatable<ValueDescription>
- {
- private object _value;
- private string _description;
+ private object? _value;
+ private string? _description;
- public object Value
- {
- get => _value;
- set => RaiseAndSetIfChanged(ref _value, value);
- }
+ public object? Value
+ {
+ get => _value;
+ set => RaiseAndSetIfChanged(ref _value, value);
+ }
- public string Description
- {
- get => _description;
- set => RaiseAndSetIfChanged(ref _description, value);
- }
+ public string? Description
+ {
+ get => _description;
+ set => RaiseAndSetIfChanged(ref _description, value);
+ }
- public string ValueAsString
- {
- get => Value?.ToString();
- set => Value = value;
- }
+ public string ValueAsString
+ {
+ get => Value?.ToString() ?? string.Empty;
+ set => Value = value;
+ }
- public ValueDescription(object value, string description = null)
- {
- Value = value;
- Description = description;
- }
- public ValueDescription(object value, object description = null)
- {
- Value = value;
- Description = description?.ToString();
- }
+ public ValueDescription(object value, string? description = null)
+ {
+ Value = value;
+ Description = description;
+ }
+ public ValueDescription(object value, object? description = null)
+ {
+ Value = value;
+ Description = description?.ToString();
+ }
- public bool Equals(ValueDescription other)
- {
- if (ReferenceEquals(null, other)) return false;
- if (ReferenceEquals(this, other)) return true;
- return Equals(_value, other._value) && _description == other._description;
- }
+ public bool Equals(ValueDescription? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(_value, other._value) && _description == other._description;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((ValueDescription) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((ValueDescription) obj);
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_value, _description);
- }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_value, _description);
+ }
- public override string ToString()
- {
- return Description;
- }
+ public override string? ToString()
+ {
+ return Description;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index ad35b05..5e6dcc2 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -6,6 +6,7 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Drawing;
using System.IO;
@@ -13,634 +14,635 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public abstract class Operation : BindableBase, IDisposable
{
- [Serializable]
- public abstract class Operation : BindableBase, IDisposable
- {
- #region Constants
- public const string NotSupportedMessage = "The current printer and/or file format is not compatible with this tool.";
- public string NotSupportedTitle => $"{Title} - Unable to run";
- #endregion
+ #region Constants
+ public const string NotSupportedMessage = "The current printer and/or file format is not compatible with this tool.";
+ public string NotSupportedTitle => $"{Title} - Unable to run";
+ #endregion
- #region Enums
+ #region Enums
- public enum OperationImportFrom : byte
- {
- None,
- Profile,
- Session,
- Undo,
- }
- #endregion
-
- #region Members
- private FileFormat _slicerFile;
- private Rectangle _originalBoundingRectangle;
- private OperationImportFrom _importedFrom = OperationImportFrom.None;
- private Rectangle _roi = Rectangle.Empty;
- private Point[][] _maskPoints;
- private uint _layerIndexEnd;
- private uint _layerIndexStart;
- private string _profileName;
- private bool _profileIsDefault;
- private Enumerations.LayerRangeSelection _layerRangeSelection = Enumerations.LayerRangeSelection.All;
- public const byte ClassNameLength = 9;
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Gets or sets from where this option got loaded/imported
- /// </summary>
- [XmlIgnore]
- public OperationImportFrom ImportedFrom
- {
- get => _importedFrom;
- set => RaiseAndSetIfChanged(ref _importedFrom, value);
- }
+ public enum OperationImportFrom : byte
+ {
+ None,
+ Profile,
+ Session,
+ Undo,
+ }
+ #endregion
+
+ #region Members
+ private FileFormat _slicerFile = null!;
+ private Rectangle _originalBoundingRectangle;
+ private OperationImportFrom _importedFrom = OperationImportFrom.None;
+ private Rectangle _roi = Rectangle.Empty;
+ private Point[][]? _maskPoints;
+ private uint _layerIndexEnd;
+ private uint _layerIndexStart;
+ private string? _profileName;
+ private bool _profileIsDefault;
+ private Enumerations.LayerRangeSelection _layerRangeSelection = Enumerations.LayerRangeSelection.All;
+ public const byte ClassNameLength = 9;
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets from where this option got loaded/imported
+ /// </summary>
+ [XmlIgnore]
+ public OperationImportFrom ImportedFrom
+ {
+ get => _importedFrom;
+ set => RaiseAndSetIfChanged(ref _importedFrom, value);
+ }
- [XmlIgnore]
- public FileFormat SlicerFile
+ [XmlIgnore]
+ public FileFormat SlicerFile
+ {
+ get => _slicerFile;
+ set
{
- get => _slicerFile;
- set
- {
- if(!RaiseAndSetIfChanged(ref _slicerFile, value)) return;
- OriginalBoundingRectangle = _slicerFile.BoundingRectangle;
- InitWithSlicerFile();
- }
+ if(!RaiseAndSetIfChanged(ref _slicerFile, value)) return;
+ OriginalBoundingRectangle = _slicerFile.BoundingRectangle;
+ InitWithSlicerFile();
}
+ }
- [XmlIgnore]
- public Rectangle OriginalBoundingRectangle
- {
- get => _originalBoundingRectangle;
- private set => RaiseAndSetIfChanged(ref _originalBoundingRectangle, value);
- }
+ [XmlIgnore]
+ public Rectangle OriginalBoundingRectangle
+ {
+ get => _originalBoundingRectangle;
+ private set => RaiseAndSetIfChanged(ref _originalBoundingRectangle, value);
+ }
- [XmlIgnore]
- public object Tag { get; set; }
+ [XmlIgnore]
+ public object? Tag { get; set; }
- /// <summary>
- /// Gets the ID name of this operation, this comes from class name with "Operation" removed
- /// </summary>
- public string Id => GetType().Name.Remove(0, ClassNameLength);
+ /// <summary>
+ /// Gets the ID name of this operation, this comes from class name with "Operation" removed
+ /// </summary>
+ public string Id => GetType().Name.Remove(0, ClassNameLength);
- public virtual Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.All;
+ public virtual Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.All;
- /// <summary>
- /// Gets the last used layer range selection, returns none if custom
- /// </summary>
- public Enumerations.LayerRangeSelection LayerRangeSelection
- {
- get => _layerRangeSelection;
- set => RaiseAndSetIfChanged(ref _layerRangeSelection, value);
- }
+ /// <summary>
+ /// Gets the last used layer range selection, returns none if custom
+ /// </summary>
+ public Enumerations.LayerRangeSelection LayerRangeSelection
+ {
+ get => _layerRangeSelection;
+ set => RaiseAndSetIfChanged(ref _layerRangeSelection, value);
+ }
- public virtual string LayerRangeString
+ public virtual string LayerRangeString
+ {
+ get
{
- get
+ if (LayerRangeSelection == Enumerations.LayerRangeSelection.None)
{
- if (LayerRangeSelection == Enumerations.LayerRangeSelection.None)
- {
- return $" [Layers: {LayerIndexStart}-{LayerIndexEnd}]";
- }
-
- return $" [Layers: {LayerRangeSelection}]";
+ return $" [Layers: {LayerIndexStart}-{LayerIndexEnd}]";
}
+
+ return $" [Layers: {LayerRangeSelection}]";
}
+ }
- /// <summary>
- /// Gets if the LayerIndexEnd selector is enabled
- /// </summary>
- public virtual bool LayerIndexEndEnabled => true;
-
- /// <summary>
- /// Gets if this operation should set layer range to the actual layer index on layer preview
- /// </summary>
- public virtual bool PassActualLayerIndex => false;
-
- /// <summary>
- /// Gets if this operation can run in a file open as partial mode
- /// </summary>
- public virtual bool CanRunInPartialMode => false;
-
- /// <summary>
- /// Gets if this operation can make use of ROI
- /// </summary>
- public virtual bool CanROI => true;
-
- /// <summary>
- /// Gets if this operation can make use maskable areas
- /// </summary>
- public virtual bool CanMask => CanROI;
-
- /// <summary>
- /// Gets if this operation can store profiles
- /// </summary>
- public virtual bool CanHaveProfiles => true;
-
- /// <summary>
- /// Gets if this operation supports cancellation
- /// </summary>
- public virtual bool CanCancel => true;
-
- /// <summary>
- /// Gets the title of this operation
- /// </summary>
- public virtual string Title => Id;
-
- /// <summary>
- /// Gets a descriptive text of this operation
- /// </summary>
- public virtual string Description => Id;
-
- /// <summary>
- /// Gets the Ok button text
- /// </summary>
- public virtual string ButtonOkText => Title;
-
- /// <summary>
- /// Gets the confirmation text for the operation
- /// </summary>
- public virtual string ConfirmationText => $"Are you sure you want to {Id}";
-
- /// <summary>
- /// Gets the progress window title
- /// </summary>
- public virtual string ProgressTitle => "Processing items";
-
- /// <summary>
- /// Gets the progress action name
- /// </summary>
- public virtual string ProgressAction => Id;
-
- /// <summary>
- /// Gets if this operation have a action text
- /// </summary>
- public bool HaveAction => !string.IsNullOrEmpty(ProgressAction);
-
- /// <summary>
- /// Gets the start layer index where operation will starts in
- /// </summary>
- public virtual uint LayerIndexStart
+ /// <summary>
+ /// Gets if the LayerIndexEnd selector is enabled
+ /// </summary>
+ public virtual bool LayerIndexEndEnabled => true;
+
+ /// <summary>
+ /// Gets if this operation should set layer range to the actual layer index on layer preview
+ /// </summary>
+ public virtual bool PassActualLayerIndex => false;
+
+ /// <summary>
+ /// Gets if this operation can run in a file open as partial mode
+ /// </summary>
+ public virtual bool CanRunInPartialMode => false;
+
+ /// <summary>
+ /// Gets if this operation can make use of ROI
+ /// </summary>
+ public virtual bool CanROI => true;
+
+ /// <summary>
+ /// Gets if this operation can make use maskable areas
+ /// </summary>
+ public virtual bool CanMask => CanROI;
+
+ /// <summary>
+ /// Gets if this operation can store profiles
+ /// </summary>
+ public virtual bool CanHaveProfiles => true;
+
+ /// <summary>
+ /// Gets if this operation supports cancellation
+ /// </summary>
+ public virtual bool CanCancel => true;
+
+ /// <summary>
+ /// Gets the icon class to show on the UI
+ /// </summary>
+ public virtual string IconClass => null!;
+
+ /// <summary>
+ /// Gets the title of this operation
+ /// </summary>
+ public virtual string Title => Id;
+
+ /// <summary>
+ /// Gets a descriptive text of this operation
+ /// </summary>
+ public virtual string Description => Id;
+
+ /// <summary>
+ /// Gets the Ok button text
+ /// </summary>
+ public virtual string ButtonOkText => Title;
+
+ /// <summary>
+ /// Gets the confirmation text for the operation
+ /// </summary>
+ public virtual string ConfirmationText => $"Are you sure you want to {Id}";
+
+ /// <summary>
+ /// Gets the progress window title
+ /// </summary>
+ public virtual string ProgressTitle => "Processing items";
+
+ /// <summary>
+ /// Gets the progress action name
+ /// </summary>
+ public virtual string ProgressAction => Id;
+
+ /// <summary>
+ /// Gets if this operation have a action text
+ /// </summary>
+ public bool HaveAction => !string.IsNullOrEmpty(ProgressAction);
+
+ /// <summary>
+ /// Gets the start layer index where operation will starts in
+ /// </summary>
+ public virtual uint LayerIndexStart
+ {
+ get => _layerIndexStart;
+ set
{
- get => _layerIndexStart;
- set
+ if (SlicerFile is not null)
{
- if (SlicerFile is not null)
- {
- value = Math.Min(value, SlicerFile.LastLayerIndex);
- }
- if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return;
- RaisePropertyChanged(nameof(LayerRangeCount));
- RaisePropertyChanged(nameof(LayerRangeHaveBottoms));
+ value = Math.Min(value, SlicerFile.LastLayerIndex);
}
+ if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return;
+ RaisePropertyChanged(nameof(LayerRangeCount));
+ RaisePropertyChanged(nameof(LayerRangeHaveBottoms));
}
+ }
- /// <summary>
- /// Gets the end layer index where operation will ends in
- /// </summary>
- public virtual uint LayerIndexEnd
+ /// <summary>
+ /// Gets the end layer index where operation will ends in
+ /// </summary>
+ public virtual uint LayerIndexEnd
+ {
+ get => _layerIndexEnd;
+ set
{
- get => _layerIndexEnd;
- set
+ if (SlicerFile is not null)
{
- if (SlicerFile is not null)
- {
- value = Math.Min(value, SlicerFile.LastLayerIndex);
- }
- if(!RaiseAndSetIfChanged(ref _layerIndexEnd, value)) return;
- RaisePropertyChanged(nameof(LayerRangeCount));
- RaisePropertyChanged(nameof(LayerRangeHaveNormals));
+ value = Math.Min(value, SlicerFile.LastLayerIndex);
}
+ if(!RaiseAndSetIfChanged(ref _layerIndexEnd, value)) return;
+ RaisePropertyChanged(nameof(LayerRangeCount));
+ RaisePropertyChanged(nameof(LayerRangeHaveNormals));
}
+ }
- public bool LayerRangeHaveBottoms => LayerIndexStart < (SlicerFile.FirstNormalLayer?.Index ?? 0);
- public bool LayerRangeHaveNormals => LayerIndexEnd >= (SlicerFile.FirstNormalLayer?.Index ?? 0);
+ public bool LayerRangeHaveBottoms => LayerIndexStart < (SlicerFile.FirstNormalLayer?.Index ?? 0);
+ public bool LayerRangeHaveNormals => LayerIndexEnd >= (SlicerFile.FirstNormalLayer?.Index ?? 0);
- public uint LayerRangeCount => LayerIndexEnd - LayerIndexStart + 1;
+ public uint LayerRangeCount => (uint)Math.Max(0, (int)LayerIndexEnd - LayerIndexStart + 1);
- /// <summary>
- /// Gets the name for this profile
- /// </summary>
- public string ProfileName
- {
- get => _profileName;
- set => RaiseAndSetIfChanged(ref _profileName, value);
- }
+ /// <summary>
+ /// Gets the name for this profile
+ /// </summary>
+ public string? ProfileName
+ {
+ get => _profileName;
+ set => RaiseAndSetIfChanged(ref _profileName, value);
+ }
- public bool ProfileIsDefault
- {
- get => _profileIsDefault;
- set => RaiseAndSetIfChanged(ref _profileIsDefault, value);
- }
+ public bool ProfileIsDefault
+ {
+ get => _profileIsDefault;
+ set => RaiseAndSetIfChanged(ref _profileIsDefault, value);
+ }
- /// <summary>
- /// Gets or sets an ROI to process this operation
- /// </summary>
- [XmlIgnore]
- public Rectangle ROI
+ /// <summary>
+ /// Gets or sets an ROI to process this operation
+ /// </summary>
+ [XmlIgnore]
+ public Rectangle ROI
+ {
+ get => _roi;
+ set
{
- get => _roi;
- set
- {
- if (!CanROI) return;
- RaiseAndSetIfChanged(ref _roi, value);
- }
+ if (!CanROI) return;
+ RaiseAndSetIfChanged(ref _roi, value);
}
+ }
- public bool HaveROI => !ROI.IsEmpty;
+ public bool HaveROI => !ROI.IsEmpty;
- /// <summary>
- /// Gets or sets an Mask to process this operation
- /// </summary>
- [XmlIgnore]
- public Point[][] MaskPoints
+ /// <summary>
+ /// Gets or sets an Mask to process this operation
+ /// </summary>
+ [XmlIgnore]
+ public Point[][]? MaskPoints
+ {
+ get => _maskPoints;
+ set
{
- get => _maskPoints;
- set
- {
- if (!CanMask) return;
- if(!RaiseAndSetIfChanged(ref _maskPoints, value)) return;
- //if(HaveMask) ROI = Rectangle.Empty;
- }
+ if (!CanMask) return;
+ if(!RaiseAndSetIfChanged(ref _maskPoints, value)) return;
+ //if(HaveMask) ROI = Rectangle.Empty;
}
+ }
- public bool HaveMask => _maskPoints is not null && _maskPoints.Length > 0;
+ public bool HaveMask => _maskPoints is not null && _maskPoints.Length > 0;
- public bool HaveROIorMask => HaveROI || HaveMask;
+ public bool HaveROIorMask => HaveROI || HaveMask;
- /// <summary>
- /// Gets if this operation have been executed once
- /// </summary>
- [XmlIgnore]
- public bool HaveExecuted { get; private set; }
+ /// <summary>
+ /// Gets if this operation have been executed once
+ /// </summary>
+ [XmlIgnore]
+ public bool HaveExecuted { get; private set; }
- /// <summary>
- /// Gets if this operation have validated at least once
- /// </summary>
- [XmlIgnore]
- public bool IsValidated { get; private set; }
- #endregion
+ /// <summary>
+ /// Gets if this operation have validated at least once
+ /// </summary>
+ [XmlIgnore]
+ public bool IsValidated { get; private set; }
+ #endregion
- #region Constructor
- protected Operation() { }
+ #region Constructor
+ protected Operation() { }
- protected Operation(FileFormat slicerFile) : this()
- {
- _slicerFile = slicerFile;
- OriginalBoundingRectangle = _slicerFile.BoundingRectangle;
- SelectAllLayers();
- InitWithSlicerFile();
- }
- #endregion
+ protected Operation(FileFormat slicerFile) : this()
+ {
+ _slicerFile = slicerFile;
+ OriginalBoundingRectangle = _slicerFile.BoundingRectangle;
+ SelectAllLayers();
+ InitWithSlicerFile();
+ }
+ #endregion
- #region Methods
+ #region Methods
- public bool CanSpawn => string.IsNullOrWhiteSpace(ValidateSpawn());
+ public bool CanSpawn => string.IsNullOrWhiteSpace(ValidateSpawn());
- /// <summary>
- /// Gets if this operation can spawn under the <see cref="SlicerFile"/>
- /// </summary>
- public virtual string ValidateSpawn() => null;
+ /// <summary>
+ /// Gets if this operation can spawn under the <see cref="SlicerFile"/>
+ /// </summary>
+ public virtual string? ValidateSpawn() => null;
- /// <summary>
- /// Gets if this operation can spawn under the <see cref="SlicerFile"/>
- /// </summary>
- public bool ValidateSpawn(out string message)
- {
- message = ValidateSpawn();
- return string.IsNullOrWhiteSpace(message);
- }
+ /// <summary>
+ /// Gets if this operation can spawn under the <see cref="SlicerFile"/>
+ /// </summary>
+ public bool ValidateSpawn(out string? message)
+ {
+ message = ValidateSpawn();
+ return string.IsNullOrWhiteSpace(message);
+ }
- public virtual string ValidateInternally()
+ public virtual string? ValidateInternally()
+ {
+ if (!ValidateSpawn(out var message))
{
- if (!ValidateSpawn(out var message))
- {
- return message;
- }
- return null;
+ return message;
}
+ return null;
+ }
- /// <summary>
- /// Validates the operation
- /// </summary>
- /// <returns>null or empty if validates, otherwise return a string with error message</returns>
- public string Validate()
- {
- IsValidated = true;
- return ValidateInternally();
- }
+ /// <summary>
+ /// Validates the operation
+ /// </summary>
+ /// <returns>null or empty if validates, otherwise return a string with error message</returns>
+ public string? Validate()
+ {
+ IsValidated = true;
+ return ValidateInternally();
+ }
- public bool CanValidate()
- {
- return string.IsNullOrWhiteSpace(Validate());
- }
+ public bool CanValidate()
+ {
+ return string.IsNullOrWhiteSpace(Validate());
+ }
- public void SelectAllLayers()
- {
- LayerIndexStart = 0;
- LayerIndexEnd = SlicerFile.LastLayerIndex;
- LayerRangeSelection = Enumerations.LayerRangeSelection.All;
- }
+ public void SelectAllLayers()
+ {
+ LayerIndexStart = 0;
+ LayerIndexEnd = SlicerFile.LastLayerIndex;
+ LayerRangeSelection = Enumerations.LayerRangeSelection.All;
+ }
- public void SelectCurrentLayer(uint layerIndex)
- {
- LayerIndexStart = LayerIndexEnd = layerIndex;
- LayerRangeSelection = Enumerations.LayerRangeSelection.Current;
- }
+ public void SelectCurrentLayer(uint layerIndex)
+ {
+ LayerIndexStart = LayerIndexEnd = layerIndex;
+ LayerRangeSelection = Enumerations.LayerRangeSelection.Current;
+ }
- public void SelectBottomLayers()
- {
- LayerIndexStart = 0;
- LayerIndexEnd = Math.Max(1, SlicerFile.FirstNormalLayer?.Index ?? 1) - 1u;
- LayerRangeSelection = Enumerations.LayerRangeSelection.Bottom;
- }
+ public void SelectBottomLayers()
+ {
+ LayerIndexStart = 0;
+ LayerIndexEnd = Math.Max(1, SlicerFile.FirstNormalLayer?.Index ?? 1) - 1u;
+ LayerRangeSelection = Enumerations.LayerRangeSelection.Bottom;
+ }
- public void SelectNormalLayers()
- {
- LayerIndexStart = SlicerFile.FirstNormalLayer?.Index ?? 0;
- LayerIndexEnd = SlicerFile.LastLayerIndex;
- LayerRangeSelection = Enumerations.LayerRangeSelection.Normal;
- }
+ public void SelectNormalLayers()
+ {
+ LayerIndexStart = SlicerFile.FirstNormalLayer?.Index ?? 0;
+ LayerIndexEnd = SlicerFile.LastLayerIndex;
+ LayerRangeSelection = Enumerations.LayerRangeSelection.Normal;
+ }
- public void SelectFirstLayer()
- {
- LayerIndexStart = LayerIndexEnd = 0;
- LayerRangeSelection = Enumerations.LayerRangeSelection.First;
- }
+ public void SelectFirstLayer()
+ {
+ LayerIndexStart = LayerIndexEnd = 0;
+ LayerRangeSelection = Enumerations.LayerRangeSelection.First;
+ }
- public void SelectLastLayer()
- {
- LayerIndexStart = LayerIndexEnd = SlicerFile.LastLayerIndex;
- LayerRangeSelection = Enumerations.LayerRangeSelection.Last;
- }
+ public void SelectLastLayer()
+ {
+ LayerIndexStart = LayerIndexEnd = SlicerFile.LastLayerIndex;
+ LayerRangeSelection = Enumerations.LayerRangeSelection.Last;
+ }
- public void SelectFirstToCurrentLayer(uint currentLayerIndex)
- {
- LayerIndexStart = 0;
- LayerIndexEnd = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
- }
+ public void SelectFirstToCurrentLayer(uint currentLayerIndex)
+ {
+ LayerIndexStart = 0;
+ LayerIndexEnd = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
+ }
- public void SelectCurrentToLastLayer(uint currentLayerIndex)
- {
- LayerIndexStart = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
- LayerIndexEnd = SlicerFile.LastLayerIndex;
- }
+ public void SelectCurrentToLastLayer(uint currentLayerIndex)
+ {
+ LayerIndexStart = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
+ LayerIndexEnd = SlicerFile.LastLayerIndex;
+ }
- public void SelectLayers(Enumerations.LayerRangeSelection range)
- {
- switch (range)
- {
- case Enumerations.LayerRangeSelection.None:
- break;
- case Enumerations.LayerRangeSelection.All:
- SelectAllLayers();
- break;
- case Enumerations.LayerRangeSelection.Current:
- //SelectCurrentLayer();
- break;
- case Enumerations.LayerRangeSelection.Bottom:
- SelectBottomLayers();
- break;
- case Enumerations.LayerRangeSelection.Normal:
- SelectNormalLayers();
- break;
- case Enumerations.LayerRangeSelection.First:
- SelectFirstLayer();
- break;
- case Enumerations.LayerRangeSelection.Last:
- SelectLastLayer();
- break;
- default:
- throw new NotImplementedException();
- }
+ public void SelectLayers(Enumerations.LayerRangeSelection range)
+ {
+ switch (range)
+ {
+ case Enumerations.LayerRangeSelection.None:
+ break;
+ case Enumerations.LayerRangeSelection.All:
+ SelectAllLayers();
+ break;
+ case Enumerations.LayerRangeSelection.Current:
+ //SelectCurrentLayer();
+ break;
+ case Enumerations.LayerRangeSelection.Bottom:
+ SelectBottomLayers();
+ break;
+ case Enumerations.LayerRangeSelection.Normal:
+ SelectNormalLayers();
+ break;
+ case Enumerations.LayerRangeSelection.First:
+ SelectFirstLayer();
+ break;
+ case Enumerations.LayerRangeSelection.Last:
+ SelectLastLayer();
+ break;
+ default:
+ throw new NotImplementedException();
}
+ }
- /// <summary>
- /// Called to init the object when <see cref="SlicerFile"/> changes
- /// </summary>
- public virtual void InitWithSlicerFile() { }
+ /// <summary>
+ /// Called to init the object when <see cref="SlicerFile"/> changes
+ /// </summary>
+ public virtual void InitWithSlicerFile() { }
- public void ClearROI()
- {
- ROI = Rectangle.Empty;
- }
+ public void ClearROI()
+ {
+ ROI = Rectangle.Empty;
+ }
- public void ClearROIandMasks()
- {
- ClearROI();
- ClearMasks();
- }
+ public void ClearROIandMasks()
+ {
+ ClearROI();
+ ClearMasks();
+ }
- public void SetROIIfEmpty(Rectangle roi)
- {
- if (HaveROI) return;
- ROI = roi;
- }
+ public void SetROIIfEmpty(Rectangle roi)
+ {
+ if (HaveROI) return;
+ ROI = roi;
+ }
- public Size GetRoiSizeOrDefault() => GetRoiSizeOrDefault(SlicerFile.Resolution);
- public Size GetRoiSizeOrDefault(Mat defaultMat) => defaultMat is null ? GetRoiSizeOrDefault() : GetRoiSizeOrDefault(defaultMat.Size);
- public Size GetRoiSizeOrDefault(Rectangle fallbackRectangle) => GetRoiSizeOrDefault(fallbackRectangle.Size);
- public Size GetRoiSizeOrDefault(Size fallbackSize)
- {
- return HaveROI ? _roi.Size : fallbackSize;
- }
+ public Size GetRoiSizeOrDefault() => GetRoiSizeOrDefault(SlicerFile.Resolution);
+ public Size GetRoiSizeOrDefault(Mat? defaultMat) => defaultMat is null ? GetRoiSizeOrDefault() : GetRoiSizeOrDefault(defaultMat.Size);
+ public Size GetRoiSizeOrDefault(Rectangle fallbackRectangle) => GetRoiSizeOrDefault(fallbackRectangle.Size);
+ public Size GetRoiSizeOrDefault(Size fallbackSize)
+ {
+ return HaveROI ? _roi.Size : fallbackSize;
+ }
- public Mat GetRoiOrDefault(Mat defaultMat)
- {
- return HaveROI && defaultMat.Size != _roi.Size ? defaultMat.Roi(_roi) : defaultMat;
- }
+ public Mat GetRoiOrDefault(Mat defaultMat)
+ {
+ return HaveROI && defaultMat.Size != _roi.Size ? defaultMat.Roi(_roi) : defaultMat;
+ }
- public Mat GetRoiOrDefault(Mat defaultMat, Rectangle fallbackRoi)
- {
- if (HaveROI && defaultMat.Size != _roi.Size) return defaultMat.Roi(_roi);
- if (fallbackRoi.IsEmpty) return defaultMat;
- return defaultMat.Size != fallbackRoi.Size ? defaultMat.Roi(fallbackRoi) : defaultMat;
- }
+ public Mat GetRoiOrDefault(Mat defaultMat, Rectangle fallbackRoi)
+ {
+ if (HaveROI && defaultMat.Size != _roi.Size) return defaultMat.Roi(_roi);
+ if (fallbackRoi.IsEmpty) return defaultMat;
+ return defaultMat.Size != fallbackRoi.Size ? defaultMat.Roi(fallbackRoi) : defaultMat;
+ }
- public Mat GetRoiOrVolumeBounds(Mat defaultMat)
- {
- return GetRoiOrDefault(defaultMat, _originalBoundingRectangle);
- }
+ public Mat GetRoiOrVolumeBounds(Mat defaultMat)
+ {
+ return GetRoiOrDefault(defaultMat, _originalBoundingRectangle);
+ }
- public void ClearMasks()
- {
- MaskPoints = null;
- }
+ public void ClearMasks()
+ {
+ MaskPoints = null;
+ }
- public void SetMasksIfEmpty(Point[][] points)
- {
- if (HaveMask) return;
- MaskPoints = points;
- }
+ public void SetMasksIfEmpty(Point[][] points)
+ {
+ if (HaveMask) return;
+ MaskPoints = points;
+ }
- public Mat GetMask(Mat mat) => GetMask(_maskPoints, mat);
+ public Mat? GetMask(Mat mat) => GetMask(_maskPoints, mat);
- public Mat GetMask(Point[][] points, Mat mat)
- {
- if (!HaveMask) return null;
+ public Mat? GetMask(Point[][]? points, Mat mat)
+ {
+ if (!HaveMask) return null;
- var mask = mat.CreateMask(points);
- return GetRoiOrDefault(mask);
- }
+ var mask = mat.CreateMask(points!);
+ return GetRoiOrDefault(mask);
+ }
- public void ApplyMask(Mat original, Mat result, Mat mask)
+ public void ApplyMask(Mat original, Mat result, Mat? mask)
+ {
+ if (mask is null) return;
+ var originalRoi = GetRoiOrDefault(original);
+ var resultRoi = result;
+ if (originalRoi.Size != result.Size) // Accept a ROI mat
{
- if (mask is null) return;
- var originalRoi = GetRoiOrDefault(original);
- var resultRoi = result;
- if (originalRoi.Size != result.Size) // Accept a ROI mat
- {
- resultRoi = GetRoiOrDefault(result);
- }
-
- if (mask.Size != resultRoi.Size) // Accept a full size mask
- {
- mask = GetRoiOrDefault(mask);
- }
-
- using var tempMat = originalRoi.Clone();
- resultRoi.CopyTo(tempMat, mask);
- tempMat.CopyTo(resultRoi);
+ resultRoi = GetRoiOrDefault(result);
}
- /// <summary>
- /// Gets a mask and apply it
- /// </summary>
- /// <param name="original">Original unmodified image</param>
- /// <param name="result">Result image which will also be modified</param>
- public void ApplyMask(Mat original, Mat result)
+ if (mask.Size != resultRoi.Size) // Accept a full size mask
{
- using var mask = GetMask(original);
- ApplyMask(original, result, mask);
+ mask = GetRoiOrDefault(mask);
}
+ using var tempMat = originalRoi.Clone();
+ resultRoi.CopyTo(tempMat, mask);
+ tempMat.CopyTo(resultRoi);
+ }
- protected virtual bool ExecuteInternally(OperationProgress progress)
- {
- throw new NotImplementedException();
- }
+ /// <summary>
+ /// Gets a mask and apply it
+ /// </summary>
+ /// <param name="original">Original unmodified image</param>
+ /// <param name="result">Result image which will also be modified</param>
+ public void ApplyMask(Mat original, Mat result)
+ {
+ using var mask = GetMask(original);
+ ApplyMask(original, result, mask);
+ }
- public bool Execute(OperationProgress progress = null)
- {
- if (_slicerFile is null) throw new InvalidOperationException($"{Title} can't execute due the lacking of a file parent.");
- if (_slicerFile.DecodeType == FileFormat.FileDecodeType.Partial && !CanRunInPartialMode) throw new InvalidOperationException($"The file was open in partial mode and the tool \"{Title}\" is unable to run in this mode.\nPlease reload the file in full mode in order to use this tool.");
- if (!IsValidated)
- {
- var msg = Validate();
- if(!string.IsNullOrWhiteSpace(msg)) throw new InvalidOperationException($"{Title} can't execute due some errors:\n{msg}");
- }
-
- progress ??= new OperationProgress();
- progress.Reset(ProgressAction, LayerRangeCount);
- HaveExecuted = true;
- var result = ExecuteInternally(progress);
+ protected virtual bool ExecuteInternally(OperationProgress progress)
+ {
+ throw new NotImplementedException();
+ }
- progress.Token.ThrowIfCancellationRequested();
- return result;
+ public bool Execute(OperationProgress? progress = null)
+ {
+ if (_slicerFile is null) throw new InvalidOperationException($"{Title} can't execute due the lacking of a file parent.");
+ if (_slicerFile.DecodeType == FileFormat.FileDecodeType.Partial && !CanRunInPartialMode) throw new InvalidOperationException($"The file was open in partial mode and the tool \"{Title}\" is unable to run in this mode.\nPlease reload the file in full mode in order to use this tool.");
+ if (!IsValidated)
+ {
+ var msg = Validate();
+ if(!string.IsNullOrWhiteSpace(msg)) throw new InvalidOperationException($"{Title} can't execute due some errors:\n{msg}");
}
+
+ progress ??= new OperationProgress();
+ progress.Reset(ProgressAction, LayerRangeCount);
+ HaveExecuted = true;
- public async Task<bool> ExecuteAsync(OperationProgress progress = null) => await new Task<bool>(() => Execute(progress));
+ var result = ExecuteInternally(progress);
- public virtual bool Execute(Mat mat, params object[] arguments)
- {
- throw new NotImplementedException();
- }
+ progress.Token.ThrowIfCancellationRequested();
+ return result;
+ }
- public async Task<bool> ExecuteAsync(Mat mat, params object[] arguments) => await new Task<bool>(() => Execute(mat, arguments));
+ public async Task<bool> ExecuteAsync(OperationProgress? progress = null) => await new Task<bool>(() => Execute(progress));
- /// <summary>
- /// Get the selected layer range in a new array, array index will not match layer index when a range is selected
- /// </summary>
- /// <returns></returns>
- public Layer[] GetSelectedLayerRange()
- {
- return LayerRangeCount == SlicerFile.LayerCount
- ? SlicerFile.ToArray()
- : SlicerFile.Where((_, layerIndex) => layerIndex >= _layerIndexStart && layerIndex <= _layerIndexEnd).ToArray();
- }
+ public virtual bool Execute(Mat mat, params object[]? arguments)
+ {
+ throw new NotImplementedException();
+ }
- /// <summary>
- /// Copy this operation base configuration to another operation.
- /// Layer range, ROI, Masks
- /// </summary>
- /// <param name="operation"></param>
- public void CopyConfigurationTo(Operation operation)
- {
- operation.LayerIndexStart = LayerIndexStart;
- operation.LayerIndexEnd = LayerIndexEnd;
- operation.ROI = ROI;
- operation.MaskPoints = MaskPoints;
- }
+ public async Task<bool> ExecuteAsync(Mat mat, params object[]? arguments) => await new Task<bool>(() => Execute(mat, arguments));
- public void Serialize(string path)
- {
- XmlSerializer serializer = new(GetType());
- using StreamWriter writer = new(path);
- serializer.Serialize(writer, this);
- }
+ /// <summary>
+ /// Get the selected layer range in a new array, array index will not match layer index when a range is selected
+ /// </summary>
+ /// <returns></returns>
+ public Layer[] GetSelectedLayerRange()
+ {
+ return LayerRangeCount == SlicerFile.LayerCount
+ ? SlicerFile.ToArray()
+ : SlicerFile.Where((_, layerIndex) => layerIndex >= _layerIndexStart && layerIndex <= _layerIndexEnd).ToArray();
+ }
- public virtual Operation Clone()
- {
- var operation = MemberwiseClone() as Operation;
- operation.SlicerFile = _slicerFile;
- return operation;
- }
+ /// <summary>
+ /// Copy this operation base configuration to another operation.
+ /// Layer range, ROI, Masks
+ /// </summary>
+ /// <param name="operation"></param>
+ public void CopyConfigurationTo(Operation operation)
+ {
+ operation.LayerIndexStart = LayerIndexStart;
+ operation.LayerIndexEnd = LayerIndexEnd;
+ operation.ROI = ROI;
+ operation.MaskPoints = MaskPoints;
+ }
- public override string ToString()
- {
- if (!string.IsNullOrEmpty(ProfileName)) return ProfileName;
+ public void Serialize(string path)
+ {
+ XmlExtensions.SerializeToFile(this, path);
+ }
- var result = $"{Title}: {LayerRangeString}";
- return result;
- }
+ public virtual Operation Clone()
+ {
+ var operation = MemberwiseClone() as Operation;
+ operation!.SlicerFile = _slicerFile;
+ return operation;
+ }
- public virtual void Dispose() { GC.SuppressFinalize(this); }
- #endregion
+ public override string ToString()
+ {
+ if (!string.IsNullOrEmpty(ProfileName)) return ProfileName;
- #region Static Methods
+ var result = $"{Title}: {LayerRangeString}";
+ return result;
+ }
- public static Operation Deserialize(string path)
- {
- if (!File.Exists(path)) return null;
-
- var fileText = File.ReadAllText(path);
- var match = Regex.Match(fileText, @"(?:<\/\s*Operation)([a-zA-Z0-9_]+)(?:\s*>)");
- if (!match.Success) return null;
- if (match.Groups.Count < 1) return null;
- var operationName = match.Groups[1].Value;
- var baseType = typeof(Operation).FullName;
- if (string.IsNullOrWhiteSpace(baseType)) return null;
- var classname = baseType + operationName + ", UVtools.Core";
- var type = Type.GetType(classname);
- if (type is null) return null;
-
- return Deserialize(path, type);
- }
+ public virtual void Dispose() { GC.SuppressFinalize(this); }
+ #endregion
- public static Operation Deserialize(string path, Type type)
- {
- XmlSerializer serializer = new(type);
- using var stream = File.OpenRead(path);
- var operation = (Operation)serializer.Deserialize(stream);
- operation.ImportedFrom = OperationImportFrom.Session;
- return operation;
- }
+ #region Static Methods
- public static Operation Deserialize(string path, Operation operation) => Deserialize(path, operation.GetType());
+ public static Operation? Deserialize(string path)
+ {
+ if (!File.Exists(path)) return null;
+
+ var fileText = File.ReadAllText(path);
+ var match = Regex.Match(fileText, @"(?:<\/\s*Operation)([a-zA-Z0-9_]+)(?:\s*>)");
+ if (!match.Success) return null;
+ if (match.Groups.Count < 1) return null;
+ var operationName = match.Groups[1].Value;
+ var baseType = typeof(Operation).FullName;
+ if (string.IsNullOrWhiteSpace(baseType)) return null;
+ var classname = baseType + operationName + ", UVtools.Core";
+ var type = Type.GetType(classname);
+ if (type is null) return null;
+
+ return Deserialize(path, type);
+ }
- #endregion
+ public static Operation Deserialize(string path, Type type)
+ {
+ var serializer = new XmlSerializer(type);
+ using var stream = File.OpenRead(path);
+ var operation = serializer.Deserialize(stream) as Operation;
+ operation!.ImportedFrom = OperationImportFrom.Session;
+ return operation;
}
-}
+
+ public static Operation Deserialize(string path, Operation operation) => Deserialize(path, operation.GetType());
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs
index dde9ecf..f558e40 100644
--- a/UVtools.Core/Operations/OperationBlur.cs
+++ b/UVtools.Core/Operations/OperationBlur.cs
@@ -6,201 +6,200 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationBlur : Operation
{
- [Serializable]
- public sealed class OperationBlur : Operation
- {
- #region Members
- private BlurAlgorithm _blurOperation = BlurAlgorithm.Blur;
- private uint _size = 1;
- #endregion
+ #region Members
+ private BlurAlgorithm _blurOperation = BlurAlgorithm.Blur;
+ private uint _size = 1;
+ #endregion
- #region Overrides
+ #region Overrides
- public override string Title => "Blur";
- public override string Description =>
- $"Blur layer images by applying a low pass filter.\n\n" +
- "NOTE: Target printer must support AntiAliasing in order to use this function.\n" +
- "See https://docs.opencv.org/master/d4/d13/tutorial_py_filtering.html";
+ public override string IconClass => "fas fa-burn";
+ public override string Title => "Blur";
+ public override string Description =>
+ $"Blur layer images by applying a low pass filter.\n\n" +
+ "NOTE: Target printer must support AntiAliasing in order to use this function.\n" +
+ "See https://docs.opencv.org/master/d4/d13/tutorial_py_filtering.html";
- public override string ConfirmationText =>
- $"blur model with {BlurOperation} from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ConfirmationText =>
+ $"blur model with {BlurOperation} from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressTitle =>
- $"Bluring model with {BlurOperation} from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Bluring model with {BlurOperation} from layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => "Blured layers";
+ public override string ProgressAction => "Blured layers";
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- if (BlurOperation == BlurAlgorithm.GaussianBlur ||
- BlurOperation == BlurAlgorithm.MedianBlur)
+ if (BlurOperation is BlurAlgorithm.GaussianBlur or BlurAlgorithm.MedianBlur)
+ {
+ if (Size % 2 != 1)
{
- if (Size % 2 != 1)
- {
- sb.AppendLine("Size must be a odd number.");
- }
+ sb.AppendLine("Size must be a odd number.");
}
+ }
- if (BlurOperation == BlurAlgorithm.Filter2D)
+ if (BlurOperation == BlurAlgorithm.Filter2D)
+ {
+ if (Kernel is null)
{
- if (Kernel is null)
- {
- sb.AppendLine("Kernel can not be empty.");
- }
+ sb.AppendLine("Kernel can not be empty.");
}
-
- return sb.ToString();
}
- #endregion
+ return sb.ToString();
+ }
- #region Enums
- public enum BlurAlgorithm
- {
- [Description("Blur: Normalized box filter")]
- Blur,
- [Description("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition")]
- Pyramid,
- [Description("Median Blur: Each pixel becomes the median of its surrounding pixels")]
- MedianBlur,
- [Description("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood")]
- GaussianBlur,
- [Description("Filter 2D: Applies an arbitrary linear filter to an image")]
- Filter2D
- }
- #endregion
+ #endregion
- #region Properties
+ #region Enums
+ public enum BlurAlgorithm
+ {
+ [Description("Blur: Normalized box filter")]
+ Blur,
+ [Description("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition")]
+ Pyramid,
+ [Description("Median Blur: Each pixel becomes the median of its surrounding pixels")]
+ MedianBlur,
+ [Description("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood")]
+ GaussianBlur,
+ [Description("Filter 2D: Applies an arbitrary linear filter to an image")]
+ Filter2D
+ }
+ #endregion
- public BlurAlgorithm BlurOperation
- {
- get => _blurOperation;
- set
- {
- if(!RaiseAndSetIfChanged(ref _blurOperation, value)) return;
- RaisePropertyChanged(nameof(IsSizeEnabled));
- RaisePropertyChanged(nameof(IsKernelVisible));
- }
- }
+ #region Properties
- public uint Size
+ public BlurAlgorithm BlurOperation
+ {
+ get => _blurOperation;
+ set
{
- get => _size;
- set => RaiseAndSetIfChanged(ref _size, value);
+ if(!RaiseAndSetIfChanged(ref _blurOperation, value)) return;
+ RaisePropertyChanged(nameof(IsSizeEnabled));
+ RaisePropertyChanged(nameof(IsKernelVisible));
}
+ }
- public bool IsSizeEnabled => BlurOperation != BlurAlgorithm.Pyramid &&
- BlurOperation != BlurAlgorithm.Filter2D;
+ public uint Size
+ {
+ get => _size;
+ set => RaiseAndSetIfChanged(ref _size, value);
+ }
- public bool IsKernelVisible => BlurOperation == BlurAlgorithm.Filter2D;
+ public bool IsSizeEnabled => BlurOperation != BlurAlgorithm.Pyramid &&
+ BlurOperation != BlurAlgorithm.Filter2D;
- public KernelConfiguration Kernel { get; set; } = new ();
+ public bool IsKernelVisible => BlurOperation == BlurAlgorithm.Filter2D;
- public override string ToString()
- {
- var result = $"[{_blurOperation}] [Size: {_size}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ public KernelConfiguration Kernel { get; set; } = new ();
- #endregion
+ public override string ToString()
+ {
+ var result = $"[{_blurOperation}] [Size: {_size}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #region Constructor
+ #endregion
- public OperationBlur() { }
+ #region Constructor
- public OperationBlur(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationBlur() { }
- #endregion
+ public OperationBlur(FileFormat slicerFile) : base(slicerFile) { }
- #region Methods
+ #endregion
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = SlicerFile[layerIndex].LayerMat)
- {
- Execute(mat);
- SlicerFile[layerIndex].LayerMat = mat;
- }
- progress.LockAndIncrement();
- });
-
- return !progress.Token.IsCancellationRequested;
- }
+ #region Methods
- public override bool Execute(Mat mat, params object[] arguments)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- Size size = new((int)Size, (int)Size);
- Point anchor = Kernel.Anchor;
- if (anchor.IsEmpty) anchor = new Point(-1, -1);
- //if (size.IsEmpty) size = new Size(3, 3);
- //if (anchor.IsEmpty) anchor = new Point(-1, -1);
- var target = GetRoiOrDefault(mat);
- using var original = mat.Clone();
- switch (BlurOperation)
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = SlicerFile[layerIndex].LayerMat)
{
- case BlurAlgorithm.Blur:
- CvInvoke.Blur(target, target, size, Kernel.Anchor);
- break;
- case BlurAlgorithm.Pyramid:
- CvInvoke.PyrDown(target, target);
- CvInvoke.PyrUp(target, target);
- break;
- case BlurAlgorithm.MedianBlur:
- CvInvoke.MedianBlur(target, target, (int)Size);
- break;
- case BlurAlgorithm.GaussianBlur:
- CvInvoke.GaussianBlur(target, target, size, 0);
- break;
- case BlurAlgorithm.Filter2D:
- CvInvoke.Filter2D(target, target, Kernel.GetKernel(), anchor);
- break;
- default:
- throw new ArgumentOutOfRangeException();
+ Execute(mat);
+ SlicerFile[layerIndex].LayerMat = mat;
}
+ progress.LockAndIncrement();
+ });
- ApplyMask(original, target);
+ return !progress.Token.IsCancellationRequested;
+ }
- return true;
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ Size size = new((int)Size, (int)Size);
+ Point anchor = Kernel.Anchor;
+ if (anchor.IsEmpty) anchor = new Point(-1, -1);
+ //if (size.IsEmpty) size = new Size(3, 3);
+ //if (anchor.IsEmpty) anchor = new Point(-1, -1);
+ var target = GetRoiOrDefault(mat);
+ using var original = mat.Clone();
+ switch (BlurOperation)
+ {
+ case BlurAlgorithm.Blur:
+ CvInvoke.Blur(target, target, size, Kernel.Anchor);
+ break;
+ case BlurAlgorithm.Pyramid:
+ CvInvoke.PyrDown(target, target);
+ CvInvoke.PyrUp(target, target);
+ break;
+ case BlurAlgorithm.MedianBlur:
+ CvInvoke.MedianBlur(target, target, (int)Size);
+ break;
+ case BlurAlgorithm.GaussianBlur:
+ CvInvoke.GaussianBlur(target, target, size, 0);
+ break;
+ case BlurAlgorithm.Filter2D:
+ CvInvoke.Filter2D(target, target, Kernel.GetKernel(), anchor);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
}
+ ApplyMask(original, target);
- #endregion
+ return true;
+ }
- #region Equality
- private bool Equals(OperationBlur other)
- {
- return _blurOperation == other._blurOperation && _size == other._size;
- }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationBlur other && Equals(other);
- }
+ #endregion
+
+ #region Equality
+ private bool Equals(OperationBlur other)
+ {
+ return _blurOperation == other._blurOperation && _size == other._size;
+ }
- public override int GetHashCode()
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationBlur other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
{
- unchecked
- {
- return ((int) _blurOperation * 397) ^ (int) _size;
- }
+ return ((int) _blurOperation * 397) ^ (int) _size;
}
- #endregion
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalculator.cs b/UVtools.Core/Operations/OperationCalculator.cs
index 8c63c06..b6395e3 100644
--- a/UVtools.Core/Operations/OperationCalculator.cs
+++ b/UVtools.Core/Operations/OperationCalculator.cs
@@ -12,433 +12,435 @@ using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationCalculator : Operation
{
- [Serializable]
- public class OperationCalculator : Operation
- {
- #region Overrides
+ #region Overrides
- public override bool CanRunInPartialMode => true;
+ public override bool CanRunInPartialMode => true;
- public override string Title => "Calculator";
- public override string Description => null;
+ public override string IconClass => "fas fa-calculator";
- public override string ConfirmationText => null;
+ public override string Title => "Calculator";
+ public override string Description => null!;
- public override string ProgressTitle => null;
+ public override string ConfirmationText => null!;
- public override string ProgressAction => null;
+ public override string ProgressTitle => null!;
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI => false;
+ public override string ProgressAction => null!;
- public override bool CanHaveProfiles => false;
- #endregion
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
- #region Properties
- public MillimetersToPixels CalcMillimetersToPixels { get; set; }
- public LightOffDelayC CalcLightOffDelay { get; set; }
- public OptimalModelTilt CalcOptimalModelTilt { get; set; }
- #endregion
+ public override bool CanHaveProfiles => false;
+ #endregion
- #region Constructor
+ #region Properties
- public OperationCalculator() { }
+ public MillimetersToPixels CalcMillimetersToPixels { get; set; } = null!;
+ public LightOffDelayC CalcLightOffDelay { get; set; } = null!;
+ public OptimalModelTilt CalcOptimalModelTilt { get; set; } = null!;
+ #endregion
- public OperationCalculator(FileFormat slicerFile) : base(slicerFile)
- { }
+ #region Constructor
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- CalcMillimetersToPixels = new MillimetersToPixels(SlicerFile.Resolution, SlicerFile.Display);
- CalcLightOffDelay = new LightOffDelayC(
- (decimal)SlicerFile.LiftHeight, (decimal)SlicerFile.BottomLiftHeight,
- (decimal)SlicerFile.LiftSpeed, (decimal)SlicerFile.BottomLiftSpeed,
- (decimal)SlicerFile.RetractSpeed, (decimal)SlicerFile.RetractSpeed);
- CalcOptimalModelTilt = new OptimalModelTilt(SlicerFile.Resolution, SlicerFile.Display,
- (decimal)SlicerFile.LayerHeight);
- }
+ public OperationCalculator() { }
- #endregion
+ public OperationCalculator(FileFormat slicerFile) : base(slicerFile)
+ { }
- public abstract class Calculation : BindableBase
- {
- public abstract string Description { get; }
- public abstract string Formula { get; }
- }
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ CalcMillimetersToPixels = new MillimetersToPixels(SlicerFile.Resolution, SlicerFile.Display);
+ CalcLightOffDelay = new LightOffDelayC(
+ (decimal)SlicerFile.LiftHeight, (decimal)SlicerFile.BottomLiftHeight,
+ (decimal)SlicerFile.LiftSpeed, (decimal)SlicerFile.BottomLiftSpeed,
+ (decimal)SlicerFile.RetractSpeed, (decimal)SlicerFile.RetractSpeed);
+ CalcOptimalModelTilt = new OptimalModelTilt(SlicerFile.Resolution, SlicerFile.Display,
+ (decimal)SlicerFile.LayerHeight);
+ }
- public sealed class MillimetersToPixels : Calculation
- {
- private uint _resolutionX;
- private uint _resolutionY;
- private decimal _displayWidth;
- private decimal _displayHeight;
- private decimal _millimeters = 1;
+ #endregion
+
+ public abstract class Calculation : BindableBase
+ {
+ public abstract string Description { get; }
+ public abstract string Formula { get; }
+ }
- public override string Description => "Converts from Millimeters to Pixels";
- public override string Formula => "Pixels = Resolution / Display * Millimeters";
+ public sealed class MillimetersToPixels : Calculation
+ {
+ private uint _resolutionX;
+ private uint _resolutionY;
+ private decimal _displayWidth;
+ private decimal _displayHeight;
+ private decimal _millimeters = 1;
- public MillimetersToPixels(Size resolution, SizeF display, decimal millimeters = 1)
- {
- _resolutionX = (uint) resolution.Width;
- _resolutionY = (uint) resolution.Height;
+ public override string Description => "Converts from Millimeters to Pixels";
+ public override string Formula => "Pixels = Resolution / Display * Millimeters";
- _displayWidth = (decimal) display.Width;
- _displayHeight = (decimal) display.Height;
+ public MillimetersToPixels(Size resolution, SizeF display, decimal millimeters = 1)
+ {
+ _resolutionX = (uint) resolution.Width;
+ _resolutionY = (uint) resolution.Height;
- _millimeters = millimeters;
- }
+ _displayWidth = (decimal) display.Width;
+ _displayHeight = (decimal) display.Height;
- public uint ResolutionX
+ _millimeters = millimeters;
+ }
+
+ public uint ResolutionX
+ {
+ get => _resolutionX;
+ set
{
- get => _resolutionX;
- set
- {
- if(!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
- RaisePropertyChanged(nameof(PixelsPerMillimeterX));
- RaisePropertyChanged(nameof(PixelsX));
- }
+ if(!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
+ RaisePropertyChanged(nameof(PixelsPerMillimeterX));
+ RaisePropertyChanged(nameof(PixelsX));
}
+ }
- public uint ResolutionY
+ public uint ResolutionY
+ {
+ get => _resolutionY;
+ set
{
- get => _resolutionY;
- set
- {
- if(!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
- RaisePropertyChanged(nameof(PixelsPerMillimeterY));
- RaisePropertyChanged(nameof(PixelsY));
- }
+ if(!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
+ RaisePropertyChanged(nameof(PixelsPerMillimeterY));
+ RaisePropertyChanged(nameof(PixelsY));
}
+ }
- public decimal DisplayWidth
+ public decimal DisplayWidth
+ {
+ get => _displayWidth;
+ set
{
- get => _displayWidth;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayWidth, value)) return;
- RaisePropertyChanged(nameof(PixelsPerMillimeterX));
- RaisePropertyChanged(nameof(PixelsX));
- }
+ if(!RaiseAndSetIfChanged(ref _displayWidth, value)) return;
+ RaisePropertyChanged(nameof(PixelsPerMillimeterX));
+ RaisePropertyChanged(nameof(PixelsX));
}
+ }
- public decimal DisplayHeight
+ public decimal DisplayHeight
+ {
+ get => _displayHeight;
+ set
{
- get => _displayHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayHeight, value)) return;
- RaisePropertyChanged(nameof(PixelsPerMillimeterY));
- RaisePropertyChanged(nameof(PixelsY));
- }
+ if(!RaiseAndSetIfChanged(ref _displayHeight, value)) return;
+ RaisePropertyChanged(nameof(PixelsPerMillimeterY));
+ RaisePropertyChanged(nameof(PixelsY));
}
+ }
- public decimal Millimeters
+ public decimal Millimeters
+ {
+ get => _millimeters;
+ set
{
- get => _millimeters;
- set
- {
- if(!RaiseAndSetIfChanged(ref _millimeters, value)) return;
- RaisePropertyChanged(nameof(PixelsX));
- RaisePropertyChanged(nameof(PixelsY));
- }
+ if(!RaiseAndSetIfChanged(ref _millimeters, value)) return;
+ RaisePropertyChanged(nameof(PixelsX));
+ RaisePropertyChanged(nameof(PixelsY));
}
+ }
- public decimal PixelsPerMillimeterX => DisplayWidth > 0 ? Math.Round(ResolutionX / DisplayWidth, 2) : 0;
- public decimal PixelsPerMillimeterY => DisplayHeight > 0 ? Math.Round(ResolutionY / DisplayHeight, 2) : 0;
+ public decimal PixelsPerMillimeterX => DisplayWidth > 0 ? Math.Round(ResolutionX / DisplayWidth, 2) : 0;
+ public decimal PixelsPerMillimeterY => DisplayHeight > 0 ? Math.Round(ResolutionY / DisplayHeight, 2) : 0;
- public decimal PixelsX => Math.Round(PixelsPerMillimeterX * Millimeters, 2);
- public decimal PixelsY => Math.Round(PixelsPerMillimeterY * Millimeters, 2);
+ public decimal PixelsX => Math.Round(PixelsPerMillimeterX * Millimeters, 2);
+ public decimal PixelsY => Math.Round(PixelsPerMillimeterY * Millimeters, 2);
- }
+ }
- public sealed class LightOffDelayC : Calculation
+ public sealed class LightOffDelayC : Calculation
+ {
+ private decimal _liftHeight;
+ private decimal _bottomLiftHeight;
+ private decimal _liftSpeed;
+ private decimal _bottomLiftSpeed;
+ private decimal _retractSpeed;
+ private decimal _bottomRetractSpeed;
+ private decimal _waitTime = 2.5m;
+ private decimal _bottomWaitTime = 3m;
+
+ public override string Description =>
+ "Calculates the required light-off delay (Moving time from the build plate + additional time for resin to stabilize) given the lifting height, speed and retract to wait x seconds before cure a new layer.\n" +
+ "Light-off delay is crucial for gaining higher-resolution and sharper prints.\n" +
+ "When the build plate retracts, it is important to allow enough time for the resin to stabilize before the UV lights turn on. This would ideally be around 2-3s.";
+
+ public override string Formula => "Light-off delay = Lifting height / (Lifting speed / 60) + Lifting height / (Retract speed / 60) + Desired wait seconds";
+
+ public decimal LiftHeight
{
- private decimal _liftHeight;
- private decimal _bottomLiftHeight;
- private decimal _liftSpeed;
- private decimal _bottomLiftSpeed;
- private decimal _retractSpeed;
- private decimal _bottomRetractSpeed;
- private decimal _waitTime = 2.5m;
- private decimal _bottomWaitTime = 3m;
-
- public override string Description =>
- "Calculates the required light-off delay (Moving time from the build plate + additional time for resin to stabilize) given the lifting height, speed and retract to wait x seconds before cure a new layer.\n" +
- "Light-off delay is crucial for gaining higher-resolution and sharper prints.\n" +
- "When the build plate retracts, it is important to allow enough time for the resin to stabilize before the UV lights turn on. This would ideally be around 2-3s.";
-
- public override string Formula => "Light-off delay = Lifting height / (Lifting speed / 60) + Lifting height / (Retract speed / 60) + Desired wait seconds";
-
- public decimal LiftHeight
+ get => _liftHeight;
+ set
{
- get => _liftHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _liftHeight, value)) return;
- RaisePropertyChanged(nameof(LightOffDelay));
- }
+ if(!RaiseAndSetIfChanged(ref _liftHeight, value)) return;
+ RaisePropertyChanged(nameof(LightOffDelay));
}
+ }
- public decimal BottomLiftHeight
+ public decimal BottomLiftHeight
+ {
+ get => _bottomLiftHeight;
+ set
{
- get => _bottomLiftHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLiftHeight, value)) return;
- RaisePropertyChanged(nameof(BottomLightOffDelay));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLiftHeight, value)) return;
+ RaisePropertyChanged(nameof(BottomLightOffDelay));
}
+ }
- public decimal LiftSpeed
+ public decimal LiftSpeed
+ {
+ get => _liftSpeed;
+ set
{
- get => _liftSpeed;
- set
- {
- if(!RaiseAndSetIfChanged(ref _liftSpeed, value)) return;
- RaisePropertyChanged(nameof(LightOffDelay));
- }
+ if(!RaiseAndSetIfChanged(ref _liftSpeed, value)) return;
+ RaisePropertyChanged(nameof(LightOffDelay));
}
+ }
- public decimal BottomLiftSpeed
+ public decimal BottomLiftSpeed
+ {
+ get => _bottomLiftSpeed;
+ set
{
- get => _bottomLiftSpeed;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLiftSpeed, value)) return;
- RaisePropertyChanged(nameof(BottomLightOffDelay));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLiftSpeed, value)) return;
+ RaisePropertyChanged(nameof(BottomLightOffDelay));
}
+ }
- public decimal RetractSpeed
+ public decimal RetractSpeed
+ {
+ get => _retractSpeed;
+ set
{
- get => _retractSpeed;
- set
- {
- if(!RaiseAndSetIfChanged(ref _retractSpeed, value)) return;
- RaisePropertyChanged(nameof(LightOffDelay));
-
- BottomRetractSpeed = _retractSpeed;
- }
+ if(!RaiseAndSetIfChanged(ref _retractSpeed, value)) return;
+ RaisePropertyChanged(nameof(LightOffDelay));
+
+ BottomRetractSpeed = _retractSpeed;
}
+ }
- public decimal BottomRetractSpeed
+ public decimal BottomRetractSpeed
+ {
+ get => _bottomRetractSpeed;
+ set
{
- get => _bottomRetractSpeed;
- set
- {
- if (!RaiseAndSetIfChanged(ref _bottomRetractSpeed, value)) return;
- RaisePropertyChanged(nameof(BottomLightOffDelay));
- }
+ if (!RaiseAndSetIfChanged(ref _bottomRetractSpeed, value)) return;
+ RaisePropertyChanged(nameof(BottomLightOffDelay));
}
+ }
- public decimal WaitTime
+ public decimal WaitTime
+ {
+ get => _waitTime;
+ set
{
- get => _waitTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _waitTime, value)) return;
- RaisePropertyChanged(nameof(LightOffDelay));
- }
+ if(!RaiseAndSetIfChanged(ref _waitTime, value)) return;
+ RaisePropertyChanged(nameof(LightOffDelay));
}
+ }
- public decimal BottomWaitTime
+ public decimal BottomWaitTime
+ {
+ get => _bottomWaitTime;
+ set
{
- get => _bottomWaitTime;
- set
- {
- if (!RaiseAndSetIfChanged(ref _bottomWaitTime, value)) return;
- RaisePropertyChanged(nameof(BottomLightOffDelay));
- }
+ if (!RaiseAndSetIfChanged(ref _bottomWaitTime, value)) return;
+ RaisePropertyChanged(nameof(BottomLightOffDelay));
}
+ }
- public decimal LightOffDelay => CalculateSeconds(_liftHeight, _liftSpeed, _retractSpeed, _waitTime);
+ public decimal LightOffDelay => CalculateSeconds(_liftHeight, _liftSpeed, _retractSpeed, _waitTime);
- public decimal BottomLightOffDelay => CalculateSeconds(_bottomLiftHeight, _bottomLiftSpeed, _bottomRetractSpeed, _bottomWaitTime);
+ public decimal BottomLightOffDelay => CalculateSeconds(_bottomLiftHeight, _bottomLiftSpeed, _bottomRetractSpeed, _bottomWaitTime);
- public LightOffDelayC()
- {
- }
+ public LightOffDelayC()
+ {
+ }
- public LightOffDelayC(decimal liftHeight, decimal bottomLiftHeight, decimal liftSpeed, decimal bottomLiftSpeed, decimal retractSpeed, decimal bottomRetractSpeed, decimal waitTime = 2.5m, decimal bottomWaitTime = 3m)
+ public LightOffDelayC(decimal liftHeight, decimal bottomLiftHeight, decimal liftSpeed, decimal bottomLiftSpeed, decimal retractSpeed, decimal bottomRetractSpeed, decimal waitTime = 2.5m, decimal bottomWaitTime = 3m)
+ {
+ _liftHeight = liftHeight;
+ _bottomLiftHeight = bottomLiftHeight;
+ _liftSpeed = liftSpeed;
+ _bottomLiftSpeed = bottomLiftSpeed;
+ _retractSpeed = retractSpeed;
+ _bottomRetractSpeed = bottomRetractSpeed;
+ _waitTime = waitTime;
+ _bottomWaitTime = bottomWaitTime;
+ }
+
+ public static decimal CalculateSeconds(decimal liftHeight, decimal liftSpeed, decimal retractSpeed, decimal extraWaitTime = 0)
+ {
+ var time = extraWaitTime;
+ if (liftSpeed > 0)
{
- _liftHeight = liftHeight;
- _bottomLiftHeight = bottomLiftHeight;
- _liftSpeed = liftSpeed;
- _bottomLiftSpeed = bottomLiftSpeed;
- _retractSpeed = retractSpeed;
- _bottomRetractSpeed = bottomRetractSpeed;
- _waitTime = waitTime;
- _bottomWaitTime = bottomWaitTime;
+ time += liftHeight / (liftSpeed / 60m);
}
-
- public static decimal CalculateSeconds(decimal liftHeight, decimal liftSpeed, decimal retractSpeed, decimal extraWaitTime = 0)
+ if (retractSpeed > 0)
{
- var time = extraWaitTime;
- if (liftSpeed > 0)
- {
- time += liftHeight / (liftSpeed / 60m);
- }
- if (retractSpeed > 0)
- {
- time += liftHeight / (retractSpeed / 60m);
- }
-
- return Math.Round(time, 2);
+ time += liftHeight / (retractSpeed / 60m);
}
+
+ return Math.Round(time, 2);
+ }
- public static float CalculateSeconds(float liftHeight, float liftSpeed, float retractSpeed, float extraWaitTime = 0,
- float liftHeight2 = 0, float liftSpeed2 = 0, float retractHeight2 = 0, float retractSpeed2 = 0)
- {
- var time = extraWaitTime;
- if (liftHeight > 0 && liftSpeed > 0)
- time += liftHeight / (liftSpeed / 60f);
+ public static float CalculateSeconds(float liftHeight, float liftSpeed, float retractSpeed, float extraWaitTime = 0,
+ float liftHeight2 = 0, float liftSpeed2 = 0, float retractHeight2 = 0, float retractSpeed2 = 0)
+ {
+ var time = extraWaitTime;
+ if (liftHeight > 0 && liftSpeed > 0)
+ time += liftHeight / (liftSpeed / 60f);
- if (liftHeight2 > 0 && liftSpeed2 > 0)
- time += liftHeight2 / (liftSpeed2 / 60f);
+ if (liftHeight2 > 0 && liftSpeed2 > 0)
+ time += liftHeight2 / (liftSpeed2 / 60f);
- if (retractHeight2 > 0 && retractSpeed2 > 0)
- time += retractHeight2 / (retractSpeed2 / 60f);
+ if (retractHeight2 > 0 && retractSpeed2 > 0)
+ time += retractHeight2 / (retractSpeed2 / 60f);
- var remainingRetractHeight = liftHeight + liftHeight2 - retractHeight2;
+ var remainingRetractHeight = liftHeight + liftHeight2 - retractHeight2;
- if (remainingRetractHeight > 0 && retractSpeed > 0)
- {
- time += remainingRetractHeight / (retractSpeed / 60f);
- }
+ if (remainingRetractHeight > 0 && retractSpeed > 0)
+ {
+ time += remainingRetractHeight / (retractSpeed / 60f);
+ }
- return (float)Math.Round(time, 2);
+ return (float)Math.Round(time, 2);
- }
+ }
- public static float CalculateSeconds(Layer layer, float extraTime)
- => CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime,
- layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2);
+ public static float CalculateSeconds(Layer layer, float extraTime)
+ => CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime,
+ layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2);
- public static uint CalculateMilliseconds(Layer layer, float extraTime)
- => (uint)(CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime,
- layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2) * 1000);
+ public static uint CalculateMilliseconds(Layer layer, float extraTime)
+ => (uint)(CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime,
+ layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2) * 1000);
- public static uint CalculateMilliseconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0) =>
- (uint)(CalculateSeconds(liftHeight, liftSpeed, retract, extraWaitTime) * 1000);
+ public static uint CalculateMilliseconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0) =>
+ (uint)(CalculateSeconds(liftHeight, liftSpeed, retract, extraWaitTime) * 1000);
- public static float CalculateSecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0)
- {
- var time = extraWaitTime;
- if (liftHeight > 0 && liftSpeed > 0) time += (float)Math.Round(liftHeight / (liftSpeed / 60f) + extraWaitTime, 2);
- if (liftHeight2 > 0 && liftSpeed2 > 0) time += (float)Math.Round(liftHeight2 / (liftSpeed2 / 60f) + extraWaitTime, 2);
- return time;
- }
+ public static float CalculateSecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0)
+ {
+ var time = extraWaitTime;
+ if (liftHeight > 0 && liftSpeed > 0) time += (float)Math.Round(liftHeight / (liftSpeed / 60f) + extraWaitTime, 2);
+ if (liftHeight2 > 0 && liftSpeed2 > 0) time += (float)Math.Round(liftHeight2 / (liftSpeed2 / 60f) + extraWaitTime, 2);
+ return time;
+ }
- public static uint CalculateMillisecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0) =>
- (uint)(CalculateSecondsLiftOnly(liftHeight, liftSpeed, liftHeight2, liftSpeed2, extraWaitTime) * 1000);
+ public static uint CalculateMillisecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0) =>
+ (uint)(CalculateSecondsLiftOnly(liftHeight, liftSpeed, liftHeight2, liftSpeed2, extraWaitTime) * 1000);
- public static float CalculateSecondsLiftOnly(Layer layer, float extraWaitTime = 0) =>
- CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime);
+ public static float CalculateSecondsLiftOnly(Layer layer, float extraWaitTime = 0) =>
+ CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime);
- public static uint CalculateMillisecondsLiftOnly(Layer layer, float extraWaitTime = 0) =>
- (uint)(CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime) * 1000);
+ public static uint CalculateMillisecondsLiftOnly(Layer layer, float extraWaitTime = 0) =>
+ (uint)(CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime) * 1000);
- }
+ }
- public sealed class OptimalModelTilt : Calculation
- {
- private uint _resolutionX;
- private uint _resolutionY;
- private decimal _displayWidth;
- private decimal _displayHeight;
- private decimal _layerHeight = 0.05m;
+ public sealed class OptimalModelTilt : Calculation
+ {
+ private uint _resolutionX;
+ private uint _resolutionY;
+ private decimal _displayWidth;
+ private decimal _displayHeight;
+ private decimal _layerHeight = 0.05m;
- public override string Description => "Calculates the optimal model tilt angle for printing and to minimize the visual layer effect.";
- public override string Formula => "Angleº = arctan(Layer height / XYResolution) * (180 / PI)";
+ public override string Description => "Calculates the optimal model tilt angle for printing and to minimize the visual layer effect.";
+ public override string Formula => "Angleº = arctan(Layer height / XYResolution) * (180 / PI)";
- public OptimalModelTilt(Size resolution, SizeF display, decimal layerHeight = 0.05m)
- {
- _resolutionX = (uint)resolution.Width;
- _resolutionY = (uint)resolution.Height;
+ public OptimalModelTilt(Size resolution, SizeF display, decimal layerHeight = 0.05m)
+ {
+ _resolutionX = (uint)resolution.Width;
+ _resolutionY = (uint)resolution.Height;
- _displayWidth = (decimal)display.Width;
- _displayHeight = (decimal)display.Height;
+ _displayWidth = (decimal)display.Width;
+ _displayHeight = (decimal)display.Height;
- _layerHeight = layerHeight;
- }
+ _layerHeight = layerHeight;
+ }
- public uint ResolutionX
+ public uint ResolutionX
+ {
+ get => _resolutionX;
+ set
{
- get => _resolutionX;
- set
- {
- if (!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
- RaisePropertyChanged(nameof(XYResolution));
- RaisePropertyChanged(nameof(XYResolutionUm));
- RaisePropertyChanged(nameof(TiltAngleDegrees));
- }
+ if (!RaiseAndSetIfChanged(ref _resolutionX, value)) return;
+ RaisePropertyChanged(nameof(XYResolution));
+ RaisePropertyChanged(nameof(XYResolutionUm));
+ RaisePropertyChanged(nameof(TiltAngleDegrees));
}
+ }
- public uint ResolutionY
+ public uint ResolutionY
+ {
+ get => _resolutionY;
+ set
{
- get => _resolutionY;
- set
- {
- if (!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
- RaisePropertyChanged(nameof(XYResolution));
- RaisePropertyChanged(nameof(XYResolutionUm));
- RaisePropertyChanged(nameof(TiltAngleDegrees));
- }
+ if (!RaiseAndSetIfChanged(ref _resolutionY, value)) return;
+ RaisePropertyChanged(nameof(XYResolution));
+ RaisePropertyChanged(nameof(XYResolutionUm));
+ RaisePropertyChanged(nameof(TiltAngleDegrees));
}
+ }
- public decimal DisplayWidth
+ public decimal DisplayWidth
+ {
+ get => _displayWidth;
+ set
{
- get => _displayWidth;
- set
- {
- if (!RaiseAndSetIfChanged(ref _displayWidth, value)) return;
- RaisePropertyChanged(nameof(XYResolution));
- RaisePropertyChanged(nameof(XYResolutionUm));
- RaisePropertyChanged(nameof(TiltAngleDegrees));
- }
+ if (!RaiseAndSetIfChanged(ref _displayWidth, value)) return;
+ RaisePropertyChanged(nameof(XYResolution));
+ RaisePropertyChanged(nameof(XYResolutionUm));
+ RaisePropertyChanged(nameof(TiltAngleDegrees));
}
+ }
- public decimal DisplayHeight
+ public decimal DisplayHeight
+ {
+ get => _displayHeight;
+ set
{
- get => _displayHeight;
- set
- {
- if (!RaiseAndSetIfChanged(ref _displayHeight, value)) return;
- RaisePropertyChanged(nameof(XYResolution));
- RaisePropertyChanged(nameof(XYResolutionUm));
- RaisePropertyChanged(nameof(TiltAngleDegrees));
- }
+ if (!RaiseAndSetIfChanged(ref _displayHeight, value)) return;
+ RaisePropertyChanged(nameof(XYResolution));
+ RaisePropertyChanged(nameof(XYResolutionUm));
+ RaisePropertyChanged(nameof(TiltAngleDegrees));
}
+ }
- public decimal LayerHeight
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if (!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(XYResolution));
- RaisePropertyChanged(nameof(XYResolutionUm));
- RaisePropertyChanged(nameof(TiltAngleDegrees));
- }
+ if (!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(XYResolution));
+ RaisePropertyChanged(nameof(XYResolutionUm));
+ RaisePropertyChanged(nameof(TiltAngleDegrees));
}
+ }
- public decimal XYResolution => DisplayWidth > 0 || DisplayHeight > 0 ?
- Math.Max(
- DisplayWidth / ResolutionX,
- DisplayHeight / ResolutionY
- )
- : 0;
+ public decimal XYResolution => DisplayWidth > 0 || DisplayHeight > 0 ?
+ Math.Max(
+ DisplayWidth / ResolutionX,
+ DisplayHeight / ResolutionY
+ )
+ : 0;
- public decimal XYResolutionUm => Math.Round(XYResolution * 1000, 2);
+ public decimal XYResolutionUm => Math.Round(XYResolution * 1000, 2);
- public decimal TiltAngleDegrees =>
- XYResolution > 0 ? (decimal) Math.Round(Math.Atan((double) (_layerHeight / XYResolution)) * (180 / Math.PI), 3) : 0;
+ public decimal TiltAngleDegrees =>
+ XYResolution > 0 ? (decimal) Math.Round(Math.Atan((double) (_layerHeight / XYResolution)) * (180 / Math.PI), 3) : 0;
- }
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
index f84c3b0..b8e17aa 100644
--- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
+++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
@@ -6,783 +6,782 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using Emgu.CV.Util;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
-using Emgu.CV.Util;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationCalibrateElephantFoot : Operation
{
- [Serializable]
- public sealed class OperationCalibrateElephantFoot : Operation
+ #region Members
+ private decimal _layerHeight;
+ private bool _syncLayers;
+ private ushort _bottomLayers;
+ private ushort _normalLayers;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+ private decimal _partScale = 1;
+ private byte _margin = 30;
+ private bool _extrudeText = true;
+ private decimal _textHeight = 1;
+ private bool _enableAntiAliasing = true;
+ private bool _mirrorOutput;
+ private bool _isErodeEnabled = true;
+ private byte _erodeStartIteration = 2;
+ private byte _erodeEndIteration = 6;
+ private byte _erodeIterationSteps = 1;
+ private bool _isDimmingEnabled = true;
+ private byte _dimmingWallThickness = 20;
+ private byte _dimmingStartBrightness = 140;
+ private byte _dimmingEndBrightness = 200;
+ private byte _dimmingBrightnessSteps = 20;
+ private bool _outputOriginalPart = true;
+
+ #endregion
+
+ #region Overrides
+
+ public override bool CanROI => false;
+
+ public override bool CanCancel => false;
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "mdi-elephant";
+ public override string Title => "Elephant foot";
+ public override string Description =>
+ "Generates test models with various strategies and increments to verify the best method/values to remove the elephant foot.\n" +
+ "Elephant foot is removed when bottom layers are flush and aligned with normal layers.\n" +
+ "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
+ "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
+
+ public override string ConfirmationText =>
+ $"generate the elephant foot test?";
+
+ public override string ProgressTitle =>
+ $"Generating the elephant foot test";
+
+ public override string ProgressAction => "Generated";
+
+ public override string? ValidateInternally()
{
- #region Members
- private decimal _layerHeight;
- private bool _syncLayers;
- private ushort _bottomLayers;
- private ushort _normalLayers;
- private decimal _bottomExposure;
- private decimal _normalExposure;
- private decimal _partScale = 1;
- private byte _margin = 30;
- private bool _extrudeText = true;
- private decimal _textHeight = 1;
- private bool _enableAntiAliasing = true;
- private bool _mirrorOutput;
- private bool _isErodeEnabled = true;
- private byte _erodeStartIteration = 2;
- private byte _erodeEndIteration = 6;
- private byte _erodeIterationSteps = 1;
- private bool _isDimmingEnabled = true;
- private byte _dimmingWallThickness = 20;
- private byte _dimmingStartBrightness = 140;
- private byte _dimmingEndBrightness = 200;
- private byte _dimmingBrightnessSteps = 20;
- private bool _outputOriginalPart = true;
-
- #endregion
-
- #region Overrides
-
- public override bool CanROI => false;
-
- public override bool CanCancel => false;
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
-
- public override string Title => "Elephant foot";
- public override string Description =>
- "Generates test models with various strategies and increments to verify the best method/values to remove the elephant foot.\n" +
- "Elephant foot is removed when bottom layers are flush and aligned with normal layers.\n" +
- "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
- "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
-
- public override string ConfirmationText =>
- $"generate the elephant foot test?";
-
- public override string ProgressTitle =>
- $"Generating the elephant foot test";
-
- public override string ProgressAction => "Generated";
-
- public override string ValidateInternally()
+ var sb = new StringBuilder();
+
+ if (_isErodeEnabled && _erodeStartIteration > _erodeEndIteration)
{
- var sb = new StringBuilder();
+ sb.AppendLine("Erode start iterations can't be higher than end iterations.");
+ }
- if (_isErodeEnabled && _erodeStartIteration > _erodeEndIteration)
+ if (_isDimmingEnabled)
+ {
+ if (_dimmingStartBrightness > _dimmingEndBrightness)
{
- sb.AppendLine("Erode start iterations can't be higher than end iterations.");
+ sb.AppendLine("Wall dimming start brightness can't be higher than end brightness.");
}
- if (_isDimmingEnabled)
+ if (SlicerFile.IsAntiAliasingEmulated && SlicerFile.AntiAliasing < 2)
{
- if (_dimmingStartBrightness > _dimmingEndBrightness)
- {
- sb.AppendLine("Wall dimming start brightness can't be higher than end brightness.");
- }
-
- if (SlicerFile.IsAntiAliasingEmulated && SlicerFile.AntiAliasing < 2)
- {
- sb.AppendLine($"With a emulated anti-aliasing of {SlicerFile.AntiAliasing}x, is not possible to run the dimming method, use the erode instead.");
- sb.AppendLine("As alternative, re-slice the file with a AntiAliasing level greater than 1 and run this tool again.");
- }
+ sb.AppendLine($"With a emulated anti-aliasing of {SlicerFile.AntiAliasing}x, is not possible to run the dimming method, use the erode instead.");
+ sb.AppendLine("As alternative, re-slice the file with a AntiAliasing level greater than 1 and run this tool again.");
}
-
-
- if (ObjectCount <= 0)
- {
- sb.AppendLine("No objects to output, please adjust the settings.");
- }
-
- return sb.ToString();
}
+
- public override string ToString()
+ if (ObjectCount <= 0)
{
- var result = $"[Layer Height: {_layerHeight}] " +
- $"[Layers: {_bottomLayers}/{_normalLayers}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
- $"[Extrude: {_extrudeText} {_textHeight}mm]" +
- $"[Scale: {_partScale}] [Margin: {_margin}] [ORI: {_outputOriginalPart}]" +
- $"[E: {_erodeStartIteration}-{_erodeEndIteration} S{_erodeIterationSteps}] " +
- $"[D: W{_dimmingWallThickness} B{_dimmingStartBrightness}-{_dimmingEndBrightness} S{_dimmingBrightnessSteps}] " +
- $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("No objects to output, please adjust the settings.");
}
- #endregion
+ return sb.ToString();
+ }
- #region Properties
+ public override string ToString()
+ {
+ var result = $"[Layer Height: {_layerHeight}] " +
+ $"[Layers: {_bottomLayers}/{_normalLayers}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
+ $"[Extrude: {_extrudeText} {_textHeight}mm]" +
+ $"[Scale: {_partScale}] [Margin: {_margin}] [ORI: {_outputOriginalPart}]" +
+ $"[E: {_erodeStartIteration}-{_erodeEndIteration} S{_erodeIterationSteps}] " +
+ $"[D: W{_dimmingWallThickness} B{_dimmingStartBrightness}-{_dimmingEndBrightness} S{_dimmingBrightnessSteps}] " +
+ $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- public decimal LayerHeight
+ #endregion
+
+ #region Properties
+
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(BottomHeight));
- RaisePropertyChanged(nameof(NormalHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- }
+ if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(BottomHeight));
+ RaisePropertyChanged(nameof(NormalHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
}
+ }
- public ushort Microns => (ushort) (LayerHeight * 1000);
+ public ushort Microns => (ushort) (LayerHeight * 1000);
- public bool SyncLayers
+ public bool SyncLayers
+ {
+ get => _syncLayers;
+ set
{
- get => _syncLayers;
- set
+ if(!RaiseAndSetIfChanged(ref _syncLayers, value)) return;
+ if (_syncLayers)
{
- if(!RaiseAndSetIfChanged(ref _syncLayers, value)) return;
- if (_syncLayers)
- {
- NormalLayers = _bottomLayers;
- }
+ NormalLayers = _bottomLayers;
}
}
+ }
- public ushort BottomLayers
+ public ushort BottomLayers
+ {
+ get => _bottomLayers;
+ set
{
- get => _bottomLayers;
- set
+ if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
+ if (_syncLayers)
{
- if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
- if (_syncLayers)
- {
- NormalLayers = _bottomLayers;
- }
- RaisePropertyChanged(nameof(BottomHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
+ NormalLayers = _bottomLayers;
}
+ RaisePropertyChanged(nameof(BottomHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public ushort NormalLayers
+ public ushort NormalLayers
+ {
+ get => _normalLayers;
+ set
{
- get => _normalLayers;
- set
+ if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return;
+ if (_syncLayers)
{
- if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return;
- if (_syncLayers)
- {
- BottomLayers = _normalLayers;
- }
- RaisePropertyChanged(nameof(NormalHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
+ BottomLayers = _normalLayers;
}
+ RaisePropertyChanged(nameof(NormalHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public uint LayerCount => (uint)(_bottomLayers + _normalLayers + (_extrudeText ? Math.Floor(_textHeight / _layerHeight) : 0));
+ public uint LayerCount => (uint)(_bottomLayers + _normalLayers + (_extrudeText ? Math.Floor(_textHeight / _layerHeight) : 0));
- public decimal BottomHeight => Layer.RoundHeight(LayerHeight * BottomLayers);
- public decimal NormalHeight => Layer.RoundHeight(LayerHeight * NormalLayers);
+ public decimal BottomHeight => Layer.RoundHeight(LayerHeight * BottomLayers);
+ public decimal NormalHeight => Layer.RoundHeight(LayerHeight * NormalLayers);
- public decimal TotalHeight => BottomHeight + NormalHeight;
+ public decimal TotalHeight => BottomHeight + NormalHeight;
- public decimal BottomExposure
- {
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
- }
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
- public decimal NormalExposure
- {
- get => _normalExposure;
- set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
- }
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
+ }
- public decimal PartScale
- {
- get => _partScale;
- set => RaiseAndSetIfChanged(ref _partScale, Math.Round(value, 2));
- }
+ public decimal PartScale
+ {
+ get => _partScale;
+ set => RaiseAndSetIfChanged(ref _partScale, Math.Round(value, 2));
+ }
- public byte Margin
- {
- get => _margin;
- set => RaiseAndSetIfChanged(ref _margin, value);
- }
+ public byte Margin
+ {
+ get => _margin;
+ set => RaiseAndSetIfChanged(ref _margin, value);
+ }
- public bool ExtrudeText
- {
- get => _extrudeText;
- set => RaiseAndSetIfChanged(ref _extrudeText, value);
- }
+ public bool ExtrudeText
+ {
+ get => _extrudeText;
+ set => RaiseAndSetIfChanged(ref _extrudeText, value);
+ }
- public decimal TextHeight
- {
- get => _textHeight;
- set => RaiseAndSetIfChanged(ref _textHeight, value);
- }
+ public decimal TextHeight
+ {
+ get => _textHeight;
+ set => RaiseAndSetIfChanged(ref _textHeight, value);
+ }
- public bool OutputOriginalPart
+ public bool OutputOriginalPart
+ {
+ get => _outputOriginalPart;
+ set
{
- get => _outputOriginalPart;
- set
- {
- if(!RaiseAndSetIfChanged(ref _outputOriginalPart, value)) return;
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _outputOriginalPart, value)) return;
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public bool EnableAntiAliasing
- {
- get => _enableAntiAliasing;
- set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
- }
+ public bool EnableAntiAliasing
+ {
+ get => _enableAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
+ }
- public bool MirrorOutput
- {
- get => _mirrorOutput;
- set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
- }
+ public bool MirrorOutput
+ {
+ get => _mirrorOutput;
+ set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
+ }
- public bool IsErodeEnabled
+ public bool IsErodeEnabled
+ {
+ get => _isErodeEnabled;
+ set
{
- get => _isErodeEnabled;
- set
- {
- if(!RaiseAndSetIfChanged(ref _isErodeEnabled, value)) return;
- RaisePropertyChanged(nameof(ErodeObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _isErodeEnabled, value)) return;
+ RaisePropertyChanged(nameof(ErodeObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public byte ErodeStartIteration
+ public byte ErodeStartIteration
+ {
+ get => _erodeStartIteration;
+ set
{
- get => _erodeStartIteration;
- set
- {
- if(!RaiseAndSetIfChanged(ref _erodeStartIteration, value)) return;
- RaisePropertyChanged(nameof(ErodeObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _erodeStartIteration, value)) return;
+ RaisePropertyChanged(nameof(ErodeObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public byte ErodeEndIteration
+ public byte ErodeEndIteration
+ {
+ get => _erodeEndIteration;
+ set
{
- get => _erodeEndIteration;
- set
- {
- if(!RaiseAndSetIfChanged(ref _erodeEndIteration, value)) return;
- RaisePropertyChanged(nameof(ErodeObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _erodeEndIteration, value)) return;
+ RaisePropertyChanged(nameof(ErodeObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public byte ErodeIterationSteps
+ public byte ErodeIterationSteps
+ {
+ get => _erodeIterationSteps;
+ set
{
- get => _erodeIterationSteps;
- set
- {
- if(!RaiseAndSetIfChanged(ref _erodeIterationSteps, value)) return;
- RaisePropertyChanged(nameof(ErodeObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _erodeIterationSteps, value)) return;
+ RaisePropertyChanged(nameof(ErodeObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public KernelConfiguration ErodeKernel { get; set; } = new();
+ public KernelConfiguration ErodeKernel { get; set; } = new();
- public bool IsDimmingEnabled
+ public bool IsDimmingEnabled
+ {
+ get => _isDimmingEnabled;
+ set
{
- get => _isDimmingEnabled;
- set
- {
- if(!RaiseAndSetIfChanged(ref _isDimmingEnabled, value)) return;
- RaisePropertyChanged(nameof(DimmingObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _isDimmingEnabled, value)) return;
+ RaisePropertyChanged(nameof(DimmingObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public byte DimmingWallThickness
- {
- get => _dimmingWallThickness;
- set => RaiseAndSetIfChanged(ref _dimmingWallThickness, value);
- }
+ public byte DimmingWallThickness
+ {
+ get => _dimmingWallThickness;
+ set => RaiseAndSetIfChanged(ref _dimmingWallThickness, value);
+ }
- public byte DimmingStartBrightness
+ public byte DimmingStartBrightness
+ {
+ get => _dimmingStartBrightness;
+ set
{
- get => _dimmingStartBrightness;
- set
- {
- if(!RaiseAndSetIfChanged(ref _dimmingStartBrightness, value)) return;
- RaisePropertyChanged(nameof(DimmingStartBrightnessPercent));
- RaisePropertyChanged(nameof(DimmingObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _dimmingStartBrightness, value)) return;
+ RaisePropertyChanged(nameof(DimmingStartBrightnessPercent));
+ RaisePropertyChanged(nameof(DimmingObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public float DimmingStartBrightnessPercent => (float) Math.Round(_dimmingStartBrightness * 100 / 255M, 2);
+ public float DimmingStartBrightnessPercent => (float) Math.Round(_dimmingStartBrightness * 100 / 255M, 2);
- public byte DimmingEndBrightness
+ public byte DimmingEndBrightness
+ {
+ get => _dimmingEndBrightness;
+ set
{
- get => _dimmingEndBrightness;
- set
- {
- if(!RaiseAndSetIfChanged(ref _dimmingEndBrightness, value)) return;
- RaisePropertyChanged(nameof(DimmingEndBrightnessPercent));
- RaisePropertyChanged(nameof(DimmingObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _dimmingEndBrightness, value)) return;
+ RaisePropertyChanged(nameof(DimmingEndBrightnessPercent));
+ RaisePropertyChanged(nameof(DimmingObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public float DimmingEndBrightnessPercent => (float)Math.Round(_dimmingEndBrightness * 100 / 255M, 2);
+ public float DimmingEndBrightnessPercent => (float)Math.Round(_dimmingEndBrightness * 100 / 255M, 2);
- public byte DimmingBrightnessSteps
+ public byte DimmingBrightnessSteps
+ {
+ get => _dimmingBrightnessSteps;
+ set
{
- get => _dimmingBrightnessSteps;
- set
- {
- if(!RaiseAndSetIfChanged(ref _dimmingBrightnessSteps, value)) return;
- RaisePropertyChanged(nameof(DimmingObjects));
- RaisePropertyChanged(nameof(ObjectCount));
- }
+ if(!RaiseAndSetIfChanged(ref _dimmingBrightnessSteps, value)) return;
+ RaisePropertyChanged(nameof(DimmingObjects));
+ RaisePropertyChanged(nameof(ObjectCount));
}
+ }
- public KernelConfiguration DimmingKernel { get; set; } = new();
+ public KernelConfiguration DimmingKernel { get; set; } = new();
- public uint ErodeObjects => _isErodeEnabled ?
- (uint)((_erodeEndIteration - _erodeStartIteration) / (decimal)_erodeIterationSteps) + 1
- : 0;
+ public uint ErodeObjects => _isErodeEnabled ?
+ (uint)((_erodeEndIteration - _erodeStartIteration) / (decimal)_erodeIterationSteps) + 1
+ : 0;
- public uint DimmingObjects => _isDimmingEnabled ?
- (uint)((_dimmingEndBrightness - _dimmingStartBrightness) / (decimal) _dimmingBrightnessSteps) + 1
- : 0;
+ public uint DimmingObjects => _isDimmingEnabled ?
+ (uint)((_dimmingEndBrightness - _dimmingStartBrightness) / (decimal) _dimmingBrightnessSteps) + 1
+ : 0;
- public uint ObjectCount => (_outputOriginalPart ? 1u : 0) + ErodeObjects + DimmingObjects;
+ public uint ObjectCount => (_outputOriginalPart ? 1u : 0) + ErodeObjects + DimmingObjects;
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationCalibrateElephantFoot() { }
+ public OperationCalibrateElephantFoot() { }
- public OperationCalibrateElephantFoot(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationCalibrateElephantFoot(FileFormat slicerFile) : base(slicerFile)
+ { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
- if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
- if (_bottomLayers <= 0) _bottomLayers = (ushort) Slicer.Slicer.MillimetersToLayers(1M, _layerHeight);
- if (_normalLayers <= 0) _normalLayers = (ushort) Slicer.Slicer.MillimetersToLayers(3.5M, _layerHeight);
-
- _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
- }
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
+ if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ if (_bottomLayers <= 0) _bottomLayers = (ushort) Slicer.Slicer.MillimetersToLayers(1M, _layerHeight);
+ if (_normalLayers <= 0) _normalLayers = (ushort) Slicer.Slicer.MillimetersToLayers(3.5M, _layerHeight);
+
+ _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- private bool Equals(OperationCalibrateElephantFoot other)
- {
- return _layerHeight == other._layerHeight && _syncLayers == other._syncLayers && _bottomLayers == other._bottomLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _partScale == other._partScale && _margin == other._margin && _extrudeText == other._extrudeText && _textHeight == other._textHeight && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _isErodeEnabled == other._isErodeEnabled && _erodeStartIteration == other._erodeStartIteration && _erodeEndIteration == other._erodeEndIteration && _erodeIterationSteps == other._erodeIterationSteps && _isDimmingEnabled == other._isDimmingEnabled && _dimmingWallThickness == other._dimmingWallThickness && _dimmingStartBrightness == other._dimmingStartBrightness && _dimmingEndBrightness == other._dimmingEndBrightness && _dimmingBrightnessSteps == other._dimmingBrightnessSteps && _outputOriginalPart == other._outputOriginalPart;
- }
+ private bool Equals(OperationCalibrateElephantFoot other)
+ {
+ return _layerHeight == other._layerHeight && _syncLayers == other._syncLayers && _bottomLayers == other._bottomLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _partScale == other._partScale && _margin == other._margin && _extrudeText == other._extrudeText && _textHeight == other._textHeight && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _isErodeEnabled == other._isErodeEnabled && _erodeStartIteration == other._erodeStartIteration && _erodeEndIteration == other._erodeEndIteration && _erodeIterationSteps == other._erodeIterationSteps && _isDimmingEnabled == other._isDimmingEnabled && _dimmingWallThickness == other._dimmingWallThickness && _dimmingStartBrightness == other._dimmingStartBrightness && _dimmingEndBrightness == other._dimmingEndBrightness && _dimmingBrightnessSteps == other._dimmingBrightnessSteps && _outputOriginalPart == other._outputOriginalPart;
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationCalibrateElephantFoot other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationCalibrateElephantFoot other && Equals(other);
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_layerHeight);
- hashCode.Add(_syncLayers);
- hashCode.Add(_bottomLayers);
- hashCode.Add(_normalLayers);
- hashCode.Add(_bottomExposure);
- hashCode.Add(_normalExposure);
- hashCode.Add(_partScale);
- hashCode.Add(_margin);
- hashCode.Add(_extrudeText);
- hashCode.Add(_textHeight);
- hashCode.Add(_enableAntiAliasing);
- hashCode.Add(_mirrorOutput);
- hashCode.Add(_isErodeEnabled);
- hashCode.Add(_erodeStartIteration);
- hashCode.Add(_erodeEndIteration);
- hashCode.Add(_erodeIterationSteps);
- hashCode.Add(_isDimmingEnabled);
- hashCode.Add(_dimmingWallThickness);
- hashCode.Add(_dimmingStartBrightness);
- hashCode.Add(_dimmingEndBrightness);
- hashCode.Add(_dimmingBrightnessSteps);
- hashCode.Add(_outputOriginalPart);
- return hashCode.ToHashCode();
- }
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_layerHeight);
+ hashCode.Add(_syncLayers);
+ hashCode.Add(_bottomLayers);
+ hashCode.Add(_normalLayers);
+ hashCode.Add(_bottomExposure);
+ hashCode.Add(_normalExposure);
+ hashCode.Add(_partScale);
+ hashCode.Add(_margin);
+ hashCode.Add(_extrudeText);
+ hashCode.Add(_textHeight);
+ hashCode.Add(_enableAntiAliasing);
+ hashCode.Add(_mirrorOutput);
+ hashCode.Add(_isErodeEnabled);
+ hashCode.Add(_erodeStartIteration);
+ hashCode.Add(_erodeEndIteration);
+ hashCode.Add(_erodeIterationSteps);
+ hashCode.Add(_isDimmingEnabled);
+ hashCode.Add(_dimmingWallThickness);
+ hashCode.Add(_dimmingStartBrightness);
+ hashCode.Add(_dimmingEndBrightness);
+ hashCode.Add(_dimmingBrightnessSteps);
+ hashCode.Add(_outputOriginalPart);
+ return hashCode.ToHashCode();
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- /// <summary>
- /// Gets the bottom and normal layers, 0 = bottom | 1 = normal
- /// </summary>
- /// <returns></returns>
- public Mat[] GetLayers()
- {
- var layers = new Mat[3];
- var anchor = new Point(-1, -1);
+ /// <summary>
+ /// Gets the bottom and normal layers, 0 = bottom | 1 = normal
+ /// </summary>
+ /// <returns></returns>
+ public Mat[] GetLayers()
+ {
+ var layers = new Mat[3];
+ var anchor = new Point(-1, -1);
- layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution);
- layers[2] = layers[0].Clone();
- LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
- int length = (int) (250 * _partScale);
- int triangleLength = (int) (50 * _partScale);
- const byte startX = 2;
- const byte startY = 2;
- int x = startX;
- int y = startY;
-
- int maxX = x;
- int maxY = y;
-
- var pointList = new List<Point> { new(x, y) };
-
- void addPoint()
- {
- maxX = Math.Max(maxX, x);
- maxY = Math.Max(maxY, y);
- pointList.Add(new Point(x, y));
- }
+ layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution);
+ layers[2] = layers[0].Clone();
+ LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
+ int length = (int) (250 * _partScale);
+ int triangleLength = (int) (50 * _partScale);
+ const byte startX = 2;
+ const byte startY = 2;
+ int x = startX;
+ int y = startY;
+
+ int maxX = x;
+ int maxY = y;
+
+ var pointList = new List<Point> { new(x, y) };
+
+ void addPoint()
+ {
+ maxX = Math.Max(maxX, x);
+ maxY = Math.Max(maxY, y);
+ pointList.Add(new Point(x, y));
+ }
- x += length;
- addPoint();
+ x += length;
+ addPoint();
- x -= triangleLength;
- y += triangleLength;
- addPoint();
+ x -= triangleLength;
+ y += triangleLength;
+ addPoint();
- y += triangleLength;
- addPoint();
+ y += triangleLength;
+ addPoint();
- x += triangleLength;
- y += triangleLength;
- addPoint();
- x -= triangleLength;
- y += triangleLength;
- addPoint();
+ x += triangleLength;
+ y += triangleLength;
+ addPoint();
+ x -= triangleLength;
+ y += triangleLength;
+ addPoint();
- x += triangleLength;
- y += triangleLength;
- addPoint();
- x -= triangleLength;
- y += triangleLength;
- addPoint();
+ x += triangleLength;
+ y += triangleLength;
+ addPoint();
+ x -= triangleLength;
+ y += triangleLength;
+ addPoint();
- x += triangleLength;
- addPoint();
- y += triangleLength;
- addPoint();
- x -= triangleLength;
- y += triangleLength;
- addPoint();
+ x += triangleLength;
+ addPoint();
+ y += triangleLength;
+ addPoint();
+ x -= triangleLength;
+ y += triangleLength;
+ addPoint();
- x = startX;
- addPoint();
+ x = startX;
+ addPoint();
- int ellipseHeight = (int) (50 * _partScale);
+ int ellipseHeight = (int) (50 * _partScale);
- maxY += ellipseHeight;
- using Mat shape = EmguExtensions.InitMat(new Size(maxX + startX, maxY + startY));
- CvInvoke.FillPoly(shape, new VectorOfPoint(pointList.ToArray()), EmguExtensions.WhiteColor, lineType);
- CvInvoke.Circle(shape, new Point(0, 0), length / 4, EmguExtensions.BlackColor, -1, lineType);
- CvInvoke.Ellipse(shape, new Point(maxX / 2, maxY - ellipseHeight), new Size(maxX / 3, ellipseHeight), 0, 0, 360,
- EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Circle(shape, new Point(length / 2, (int) (maxY - 100 * _partScale)), length / 5, EmguExtensions.BlackColor, -1, lineType);
-
- int currentX = 0;
- int currentY = 0;
-
- maxX = 0;
-
- const FontFace font = FontFace.HersheyDuplex;
- int fontMargin = (int)(42 * _partScale);
- int fontStartX = (int) (30 * _partScale);
- int fontStartY = length / 4 + fontMargin;
- double fontScale = 1.3 * (double) _partScale;
- int fontThickness = (int) (3 * _partScale);
+ maxY += ellipseHeight;
+ using Mat shape = EmguExtensions.InitMat(new Size(maxX + startX, maxY + startY));
+ CvInvoke.FillPoly(shape, new VectorOfPoint(pointList.ToArray()), EmguExtensions.WhiteColor, lineType);
+ CvInvoke.Circle(shape, new Point(0, 0), length / 4, EmguExtensions.BlackColor, -1, lineType);
+ CvInvoke.Ellipse(shape, new Point(maxX / 2, maxY - ellipseHeight), new Size(maxX / 3, ellipseHeight), 0, 0, 360,
+ EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Circle(shape, new Point(length / 2, (int) (maxY - 100 * _partScale)), length / 5, EmguExtensions.BlackColor, -1, lineType);
+
+ int currentX = 0;
+ int currentY = 0;
+
+ maxX = 0;
+
+ const FontFace font = FontFace.HersheyDuplex;
+ int fontMargin = (int)(42 * _partScale);
+ int fontStartX = (int) (30 * _partScale);
+ int fontStartY = length / 4 + fontMargin;
+ double fontScale = 1.3 * (double) _partScale;
+ int fontThickness = (int) (3 * _partScale);
- void addText(Mat mat, ushort number, params string[] text)
+ void addText(Mat mat, ushort number, params string[]? text)
+ {
+ var color = _extrudeText ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor;
+ CvInvoke.PutText(mat, number.ToString(), new Point((int) (100 * _partScale), (int) (55 * _partScale)), font, 1.5 * (double) _partScale, color, (int) (4 * _partScale), lineType);
+ CvInvoke.PutText(mat, "UVtools EP", new Point(fontStartX, fontStartY), font, 0.8 * (double) _partScale, color, (int) (2 * _partScale), lineType);
+ CvInvoke.PutText(mat, $"{Microns}um", new Point(fontStartX, fontStartY + fontMargin), font, fontScale, color, fontThickness, lineType);
+ CvInvoke.PutText(mat, $"{BottomExposure}|{NormalExposure}s", new Point(fontStartX, fontStartY + fontMargin * 2), font, fontScale, color, fontThickness, lineType);
+ if (text is null) return;
+ for (var i = 0; i < text.Length; i++)
{
- var color = _extrudeText ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor;
- CvInvoke.PutText(mat, number.ToString(), new Point((int) (100 * _partScale), (int) (55 * _partScale)), font, 1.5 * (double) _partScale, color, (int) (4 * _partScale), lineType);
- CvInvoke.PutText(mat, "UVtools EP", new Point(fontStartX, fontStartY), font, 0.8 * (double) _partScale, color, (int) (2 * _partScale), lineType);
- CvInvoke.PutText(mat, $"{Microns}um", new Point(fontStartX, fontStartY + fontMargin), font, fontScale, color, fontThickness, lineType);
- CvInvoke.PutText(mat, $"{BottomExposure}|{NormalExposure}s", new Point(fontStartX, fontStartY + fontMargin * 2), font, fontScale, color, fontThickness, lineType);
- if (text is null) return;
- for (var i = 0; i < text.Length; i++)
- {
- CvInvoke.PutText(mat, text[i], new Point(fontStartX, fontStartY + fontMargin * (i + 3)), font,
- fontScale, color, fontThickness, lineType);
- }
-
+ CvInvoke.PutText(mat, text[i], new Point(fontStartX, fontStartY + fontMargin * (i + 3)), font,
+ fontScale, color, fontThickness, lineType);
}
- ushort count = 0;
+ }
- layers[1] = layers[0].Clone();
+ ushort count = 0;
- if (OutputOriginalPart)
- {
- using var roi0 = new Mat(layers[0], new Rectangle(new Point(currentX, currentY), shape.Size));
- shape.CopyTo(roi0);
+ layers[1] = layers[0].Clone();
+
+ if (OutputOriginalPart)
+ {
+ using var roi0 = new Mat(layers[0], new Rectangle(new Point(currentX, currentY), shape.Size));
+ shape.CopyTo(roi0);
- using var roi1 = new Mat(layers[1], new Rectangle(new Point(currentX, currentY), shape.Size));
- shape.CopyTo(roi1);
+ using var roi1 = new Mat(layers[1], new Rectangle(new Point(currentX, currentY), shape.Size));
+ shape.CopyTo(roi1);
- if (_extrudeText)
- {
- using var roi2 = new Mat(layers[2], new Rectangle(new Point(currentX, currentY), shape.Size));
- addText(roi2, ++count, "ORI");
- }
- else
- {
- addText(roi1, ++count, "ORI");
- }
+ if (_extrudeText)
+ {
+ using var roi2 = new Mat(layers[2], new Rectangle(new Point(currentX, currentY), shape.Size));
+ addText(roi2, ++count, "ORI");
}
else
{
- currentX -= shape.Width + Margin;
+ addText(roi1, ++count, "ORI");
}
+ }
+ else
+ {
+ currentX -= shape.Width + Margin;
+ }
- if (IsErodeEnabled)
+ if (IsErodeEnabled)
+ {
+ for (int iteration = ErodeStartIteration;
+ iteration <= ErodeEndIteration;
+ iteration += ErodeIterationSteps)
{
- for (int iteration = ErodeStartIteration;
- iteration <= ErodeEndIteration;
- iteration += ErodeIterationSteps)
+ currentX += shape.Width + Margin;
+ maxX = Math.Max(maxX, currentX);
+
+ if (currentX + shape.Width >= layers[0].Width)
{
- currentX += shape.Width + Margin;
- maxX = Math.Max(maxX, currentX);
+ currentX = startX;
+ currentY += shape.Height + Margin;
+ }
- if (currentX + shape.Width >= layers[0].Width)
- {
- currentX = startX;
- currentY += shape.Height + Margin;
- }
+ if (currentY + shape.Height >= layers[0].Height)
+ {
+ break; // Insufficient size
+ }
- if (currentY + shape.Height >= layers[0].Height)
+ count++;
+ using (var roi = new Mat(layers[1], new Rectangle(new Point(currentX, currentY), shape.Size)))
+ {
+ shape.CopyTo(roi);
+ if (_extrudeText)
{
- break; // Insufficient size
+ using var roi1 = new Mat(layers[2], new Rectangle(new Point(currentX, currentY), shape.Size));
+ addText(roi1, count, $"E: {iteration}i");
}
-
- count++;
- using (var roi = new Mat(layers[1], new Rectangle(new Point(currentX, currentY), shape.Size)))
+ else
{
- shape.CopyTo(roi);
- if (_extrudeText)
- {
- using var roi1 = new Mat(layers[2], new Rectangle(new Point(currentX, currentY), shape.Size));
- addText(roi1, count, $"E: {iteration}i");
- }
- else
- {
- addText(roi, count, $"E: {iteration}i");
- }
-
+ addText(roi, count, $"E: {iteration}i");
}
+
+ }
- using (var roi = layers[0].Roi(new Rectangle(new Point(currentX, currentY), shape.Size)))
- using (var erode = new Mat())
- {
- var tempIterations = iteration;
- var kernel = ErodeKernel.GetKernel(ref tempIterations);
- CvInvoke.Erode(shape, erode, kernel, ErodeKernel.Anchor, tempIterations, BorderType.Reflect101, default);
- erode.CopyTo(roi);
- //addText(roi, count, $"E: {iteration}i");
- }
+ using (var roi = layers[0].Roi(new Rectangle(new Point(currentX, currentY), shape.Size)))
+ using (var erode = new Mat())
+ {
+ var tempIterations = iteration;
+ var kernel = ErodeKernel.GetKernel(ref tempIterations);
+ CvInvoke.Erode(shape, erode, kernel, ErodeKernel.Anchor, tempIterations, BorderType.Reflect101, default);
+ erode.CopyTo(roi);
+ //addText(roi, count, $"E: {iteration}i");
}
}
+ }
- if (IsDimmingEnabled)
+ if (IsDimmingEnabled)
+ {
+ for (int brightness = DimmingStartBrightness;
+ brightness <= DimmingEndBrightness;
+ brightness += DimmingBrightnessSteps)
{
- for (int brightness = DimmingStartBrightness;
- brightness <= DimmingEndBrightness;
- brightness += DimmingBrightnessSteps)
- {
- currentX += shape.Width + Margin;
+ currentX += shape.Width + Margin;
- if (currentX + shape.Width >= layers[0].Width)
- {
- currentX = 0;
- currentY += shape.Height + Margin;
- }
+ if (currentX + shape.Width >= layers[0].Width)
+ {
+ currentX = 0;
+ currentY += shape.Height + Margin;
+ }
- if (currentY + shape.Height >= layers[0].Height)
- {
- break; // Insufficient size
- }
+ if (currentY + shape.Height >= layers[0].Height)
+ {
+ break; // Insufficient size
+ }
- count++;
- using (var roi = layers[1].Roi(new Rectangle(new Point(currentX, currentY), shape.Size)))
+ count++;
+ using (var roi = layers[1].Roi(new Rectangle(new Point(currentX, currentY), shape.Size)))
+ {
+ shape.CopyTo(roi);
+ if (_extrudeText)
{
- shape.CopyTo(roi);
- if (_extrudeText)
- {
- using var roi1 = new Mat(layers[2], new Rectangle(new Point(currentX, currentY), shape.Size));
- addText(roi1, count, $"W: {DimmingWallThickness}", $"B: {brightness}");
- }
- else
- {
- addText(roi, count, $"W: {DimmingWallThickness}", $"B: {brightness}");
- }
+ using var roi1 = new Mat(layers[2], new Rectangle(new Point(currentX, currentY), shape.Size));
+ addText(roi1, count, $"W: {DimmingWallThickness}", $"B: {brightness}");
}
-
- using (var roi = new Mat(layers[0], new Rectangle(new Point(currentX, currentY), shape.Size)))
- using (var erode = new Mat())
- using (var target = new Mat())
- using (var mask = shape.NewBlank())
+ else
{
- mask.SetTo(new MCvScalar(byte.MaxValue-brightness));
- int tempIterations = DimmingWallThickness;
- var kernel = DimmingKernel.GetKernel(ref tempIterations);
- CvInvoke.Erode(shape, erode, kernel, anchor, tempIterations, BorderType.Reflect101, default);
- //CvInvoke.Subtract(shape, erode, diff);
- //CvInvoke.BitwiseAnd(diff, mask, target);
- //CvInvoke.Add(erode, target, target);
- CvInvoke.Subtract(shape, mask, target);
- CvInvoke.Add(erode, target, target);
- target.CopyTo(roi);
- //addText(roi, count, $"W: {DimmingWallThickness}", $"B: {brightness}");
+ addText(roi, count, $"W: {DimmingWallThickness}", $"B: {brightness}");
}
}
- }
- if (_mirrorOutput)
- {
- var flip = SlicerFile.DisplayMirror;
- if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
- Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
+ using (var roi = new Mat(layers[0], new Rectangle(new Point(currentX, currentY), shape.Size)))
+ using (var erode = new Mat())
+ using (var target = new Mat())
+ using (var mask = shape.NewBlank())
+ {
+ mask.SetTo(new MCvScalar(byte.MaxValue-brightness));
+ int tempIterations = DimmingWallThickness;
+ var kernel = DimmingKernel.GetKernel(ref tempIterations);
+ CvInvoke.Erode(shape, erode, kernel, anchor, tempIterations, BorderType.Reflect101, default);
+ //CvInvoke.Subtract(shape, erode, diff);
+ //CvInvoke.BitwiseAnd(diff, mask, target);
+ //CvInvoke.Add(erode, target, target);
+ CvInvoke.Subtract(shape, mask, target);
+ CvInvoke.Add(erode, target, target);
+ target.CopyTo(roi);
+ //addText(roi, count, $"W: {DimmingWallThickness}", $"B: {brightness}");
+ }
}
-
- // Preview
- //layers[2] = new Mat(layers[0], new Rectangle(0, 0, Math.Min(layers[0].Width, maxX), Math.Min(layers[0].Height, currentY)));
-
- return layers;
}
- public Mat GetThumbnail()
+ if (_mirrorOutput)
{
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "Elephant Foot Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
-
- /*thumbnail.SetTo(EmguExtensions.Black3Byte);
-
- CvInvoke.Circle(thumbnail, new Point(400/2, 200/2), 200/2, EmguExtensions.White3Byte, -1);
- for (int angle = 0; angle < 360; angle+=20)
- {
- CvInvoke.Line(thumbnail, new Point(400 / 2, 200 / 2), new Point((int)(400 / 2 + 100 * Math.Cos(angle * Math.PI / 180)), (int)(200 / 2 + 100 * Math.Sin(angle * Math.PI / 180))), new MCvScalar(255, 27, 245), 3);
- }
-
- thumbnail.Save("D:\\Thumbnail.png");*/
- return thumbnail;
+ var flip = SlicerFile.DisplayMirror;
+ if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
+ Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
}
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- progress.ItemCount = 3;
+ // Preview
+ //layers[2] = new Mat(layers[0], new Rectangle(0, 0, Math.Min(layers[0].Width, maxX), Math.Min(layers[0].Height, currentY)));
+
+ return layers;
+ }
+
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "Elephant Foot Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
+ CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+
+ /*thumbnail.SetTo(EmguExtensions.Black3Byte);
+
+ CvInvoke.Circle(thumbnail, new Point(400/2, 200/2), 200/2, EmguExtensions.White3Byte, -1);
+ for (int angle = 0; angle < 360; angle+=20)
+ {
+ CvInvoke.Line(thumbnail, new Point(400 / 2, 200 / 2), new Point((int)(400 / 2 + 100 * Math.Cos(angle * Math.PI / 180)), (int)(200 / 2 + 100 * Math.Sin(angle * Math.PI / 180))), new MCvScalar(255, 27, 245), 3);
+ }
+
+ thumbnail.Save("D:\\Thumbnail.png");*/
+ return thumbnail;
+ }
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = 3;
- var newLayers = new Layer[LayerCount];
+ var newLayers = new Layer[LayerCount];
- var layers = GetLayers();
- progress++;
+ var layers = GetLayers();
+ progress++;
- var bottomLayer = new Layer(0, layers[0], SlicerFile.LayerManager);
+ var bottomLayer = new Layer(0, layers[0], SlicerFile);
- Layer extrudeLayer = null;
- var moveOp = new OperationMove(SlicerFile, bottomLayer.BoundingRectangle);
- moveOp.Execute(layers[0]);
- moveOp.Execute(layers[1]);
+ Layer? extrudeLayer = null;
+ var moveOp = new OperationMove(SlicerFile, bottomLayer.BoundingRectangle);
+ moveOp.Execute(layers[0]);
+ moveOp.Execute(layers[1]);
+
+ var layer = new Layer(0, layers[1], SlicerFile)
+ {
+ IsModified = true
+ };
- var layer = new Layer(0, layers[1], SlicerFile.LayerManager)
+ if (_extrudeText)
+ {
+ moveOp.Execute(layers[2]);
+ extrudeLayer = new Layer(0, layers[2], SlicerFile)
{
IsModified = true
};
-
- if (_extrudeText)
- {
- moveOp.Execute(layers[2]);
- extrudeLayer = new Layer(0, layers[2], SlicerFile.LayerManager)
- {
- IsModified = true
- };
- }
- bottomLayer.LayerMat = layers[0];
+ }
+ bottomLayer.LayerMat = layers[0];
- progress++;
+ progress++;
- for (uint layerIndex = 0;
- layerIndex < _bottomLayers + _normalLayers;
- layerIndex++)
- {
- newLayers[layerIndex] = SlicerFile.GetBottomOrNormalValue(layerIndex, bottomLayer.Clone(), layer.Clone());
- }
+ for (uint layerIndex = 0;
+ layerIndex < _bottomLayers + _normalLayers;
+ layerIndex++)
+ {
+ newLayers[layerIndex] = SlicerFile.GetBottomOrNormalValue(layerIndex, bottomLayer.Clone(), layer.Clone());
+ }
- if (_extrudeText)
+ if (_extrudeText)
+ {
+ for (uint layerIndex = (uint) (_bottomLayers + _normalLayers); layerIndex < LayerCount; layerIndex++)
{
- for (uint layerIndex = (uint) (_bottomLayers + _normalLayers); layerIndex < LayerCount; layerIndex++)
- {
- newLayers[layerIndex] = extrudeLayer.Clone();
- }
+ newLayers[layerIndex] = extrudeLayer!.Clone();
}
+ }
- foreach (var mat in layers)
- {
- mat.Dispose();
- }
+ foreach (var mat in layers)
+ {
+ mat.Dispose();
+ }
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(GetThumbnail());
+ if (SlicerFile.ThumbnailsCount > 0)
+ SlicerFile.SetThumbnails(GetThumbnail());
- progress++;
+ progress++;
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)LayerHeight;
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.BottomLayerCount = BottomLayers;
- SlicerFile.TransitionLayerCount = 0;
-
- SlicerFile.LayerManager.Layers = newLayers;
- }, true);
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerHeight = (float)LayerHeight;
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.BottomLayerCount = BottomLayers;
+ SlicerFile.TransitionLayerCount = 0;
+
+ SlicerFile.Layers = newLayers;
+ }, true);
- return !progress.Token.IsCancellationRequested;
- }
-
- #endregion
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
index 2785828..26f7b47 100644
--- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
@@ -6,6 +6,10 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using Emgu.CV.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -15,1520 +19,1478 @@ using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
-using Emgu.CV.Util;
-using MoreLinq;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationCalibrateExposureFinder : Operation
{
- [Serializable]
- public sealed class OperationCalibrateExposureFinder : Operation
+ #region Enums
+ public enum CalibrateExposureFinderShapes : byte
{
- #region Enums
- public enum CalibrateExposureFinderShapes : byte
- {
- Square,
- Circle
- }
- public static Array ShapesItems => Enum.GetValues(typeof(CalibrateExposureFinderShapes));
+ Square,
+ Circle
+ }
+ public static Array ShapesItems => Enum.GetValues(typeof(CalibrateExposureFinderShapes));
- public enum CalibrateExposureFinderMeasures : byte
- {
- Pixels,
- Millimeters,
- }
+ public enum CalibrateExposureFinderMeasures : byte
+ {
+ Pixels,
+ Millimeters,
+ }
- public static Array MeasuresItems => Enum.GetValues(typeof(CalibrateExposureFinderMeasures));
+ public static Array MeasuresItems => Enum.GetValues(typeof(CalibrateExposureFinderMeasures));
- public enum CalibrateExposureFinderMultipleBrightnessExcludeFrom : byte
- {
- None,
- Bottom,
- BottomAndBase
- }
- public static Array MultipleBrightnessExcludeFromItems => Enum.GetValues(typeof(CalibrateExposureFinderMultipleBrightnessExcludeFrom));
+ public enum CalibrateExposureFinderMultipleBrightnessExcludeFrom : byte
+ {
+ None,
+ Bottom,
+ BottomAndBase
+ }
+ public static Array MultipleBrightnessExcludeFromItems => Enum.GetValues(typeof(CalibrateExposureFinderMultipleBrightnessExcludeFrom));
- public enum CalibrateExposureFinderExposureGenTypes : byte
- {
- Linear,
- Multiplier
- }
+ public enum CalibrateExposureFinderExposureGenTypes : byte
+ {
+ Linear,
+ Multiplier
+ }
- public static Array ExposureGenTypeItems => Enum.GetValues(typeof(CalibrateExposureFinderExposureGenTypes));
+ public static Array ExposureGenTypeItems => Enum.GetValues(typeof(CalibrateExposureFinderExposureGenTypes));
- public enum CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes : byte
- {
- [Description("Iterative exposure: Print each base layer at it own exposure time")]
- Iterative,
+ public enum CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes : byte
+ {
+ [Description("Iterative exposure: Print each base layer at it own exposure time")]
+ Iterative,
- [Description("Lowest exposure: Base layers will print at the lowest defined exposure time")]
- UseLowest,
+ [Description("Lowest exposure: Base layers will print at the lowest defined exposure time")]
+ UseLowest,
- [Description("Middle exposure: Base layers will print at the middle defined exposure time")]
- UseMiddle,
+ [Description("Middle exposure: Base layers will print at the middle defined exposure time")]
+ UseMiddle,
- [Description("Highest exposure: Base layers will print at the highest defined exposure time")]
- UseHighest,
+ [Description("Highest exposure: Base layers will print at the highest defined exposure time")]
+ UseHighest,
- [Description("Custom exposure: Base layers will print at a custom defined exposure time")]
- Custom
- }
- #endregion
+ [Description("Custom exposure: Base layers will print at a custom defined exposure time")]
+ Custom
+ }
+ #endregion
- #region Subclasses
+ #region Subclasses
- public sealed class BullsEyeCircle
- {
- public ushort Diameter { get; set; }
- public ushort Radius => (ushort) (Diameter / 2);
- public ushort Thickness { get; set; } = 10;
+ public sealed class BullsEyeCircle
+ {
+ public ushort Diameter { get; set; }
+ public ushort Radius => (ushort) (Diameter / 2);
+ public ushort Thickness { get; set; } = 10;
- public BullsEyeCircle() {}
+ public BullsEyeCircle() {}
- public BullsEyeCircle(ushort diameter, ushort thickness)
- {
- Diameter = diameter;
- Thickness = thickness;
- }
+ public BullsEyeCircle(ushort diameter, ushort thickness)
+ {
+ Diameter = diameter;
+ Thickness = thickness;
}
- #endregion
-
- #region Constants
-
- const byte TextMarkingSpacing = 60;
- const byte TextMarkingLineBreak = 30;
- const FontFace TextMarkingFontFace = Emgu.CV.CvEnum.FontFace.HersheyDuplex;
- const byte TextMarkingStartX = 10;
- //const byte TextStartY = 50;
- const double TextMarkingScale = 0.8;
- const byte TextMarkingThickness = 2;
-
- #endregion
-
- #region Members
- private decimal _displayWidth;
- private decimal _displayHeight;
- private decimal _layerHeight;
- private ushort _bottomLayers;
- private decimal _bottomExposure;
- private decimal _normalExposure;
- private decimal _topBottomMargin = 5;
- private decimal _leftRightMargin = 10;
- private byte _chamferLayers = 0;
- private byte _erodeBottomIterations = 0;
- private decimal _partMargin = 0;
- private bool _enableAntiAliasing = false;
- private bool _mirrorOutput;
- private decimal _baseHeight = 1;
- private decimal _featuresHeight = 1;
- private decimal _featuresMargin = 2m;
+ }
+ #endregion
+
+ #region Constants
+
+ const byte TextMarkingSpacing = 60;
+ const byte TextMarkingLineBreak = 30;
+ const FontFace TextMarkingFontFace = Emgu.CV.CvEnum.FontFace.HersheyDuplex;
+ const byte TextMarkingStartX = 10;
+ //const byte TextStartY = 50;
+ const double TextMarkingScale = 0.8;
+ const byte TextMarkingThickness = 2;
+
+ #endregion
+
+ #region Members
+ private decimal _displayWidth;
+ private decimal _displayHeight;
+ private decimal _layerHeight;
+ private ushort _bottomLayers;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+ private decimal _topBottomMargin = 5;
+ private decimal _leftRightMargin = 10;
+ private byte _chamferLayers = 0;
+ private byte _erodeBottomIterations = 0;
+ private decimal _partMargin = 0;
+ private bool _enableAntiAliasing = false;
+ private bool _mirrorOutput;
+ private decimal _baseHeight = 1;
+ private decimal _featuresHeight = 1;
+ private decimal _featuresMargin = 2m;
- private ushort _staircaseThicknessPx = 40;
- private decimal _staircaseThicknessMm = 2;
+ private ushort _staircaseThicknessPx = 40;
+ private decimal _staircaseThicknessMm = 2;
- private bool _holesEnabled = false;
- private CalibrateExposureFinderShapes _holeShape = CalibrateExposureFinderShapes.Square;
- private CalibrateExposureFinderMeasures _unitOfMeasure = CalibrateExposureFinderMeasures.Pixels;
- private string _holeDiametersPx = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11";
- private string _holeDiametersMm = "0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2";
-
- private bool _barsEnabled = true;
- private decimal _barSpacing = 1.5m;
- private decimal _barLength = 4;
- private sbyte _barVerticalSplitter = 0;
- private byte _barFenceThickness = 10;
- private sbyte _barFenceOffset = 4;
- private string _barThicknessesPx = "4, 6, 8, 60"; //"4, 6, 8, 10, 12, 14, 16, 18, 20";
- private string _barThicknessesMm = "0.2, 0.3, 0.4, 3"; //"0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2";
-
- private bool _textEnabled = true;
- private FontFace _textFont = TextMarkingFontFace;
- private double _textScale = 1;
- private byte _textThickness = 2;
- private string _text = "ABHJQRWZ%&#"; //"ABGHJKLMQRSTUVWXZ%&#";
-
- private bool _multipleBrightness;
- private CalibrateExposureFinderMultipleBrightnessExcludeFrom _multipleBrightnessExcludeFrom = CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase;
- private string _multipleBrightnessValues;
- private decimal _multipleBrightnessGenExposureTime;
- private byte _multipleBrightnessGenEmulatedAALevel = FileFormat.MaximumAntiAliasing;
- private byte _multipleBrightnessGenExposureFractions = 8;
-
- private bool _multipleLayerHeight;
- private decimal _multipleLayerHeightMaximum = 0.1m;
- private decimal _multipleLayerHeightStep = 0.01m;
-
- private CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes _multipleExposuresBaseLayersPrintMode;
- private decimal _multipleExposuresBaseLayersCustomExposure;
- private bool _differentSettingsForSamePositionedLayers;
- private bool _samePositionedLayersLiftHeightEnabled = true;
- private decimal _samePositionedLayersLiftHeight;
- private bool _samePositionedLayersWaitTimeBeforeCureEnabled = true;
- private decimal _samePositionedLayersWaitTimeBeforeCure;
- private bool _multipleExposures;
- private CalibrateExposureFinderExposureGenTypes _exposureGenType = CalibrateExposureFinderExposureGenTypes.Linear;
- private bool _exposureGenIgnoreBaseExposure;
- private decimal _exposureGenBottomStep = 0;
- private decimal _exposureGenNormalStep = 0.2m;
- private byte _exposureGenTests = 4;
- private decimal _exposureGenManualLayerHeight;
- private decimal _exposureGenManualBottom;
- private decimal _exposureGenManualNormal;
- private RangeObservableCollection<ExposureItem> _exposureTable = new();
-
- private bool _bullsEyeEnabled = true;
- private string _bullsEyeConfigurationPx = "26:5, 60:10, 116:15, 190:20";
- private string _bullsEyeConfigurationMm = "1.3:0.25, 3:0.5, 5.8:0.75, 9.5:1";
- private bool _bullsEyeInvertQuadrants = true;
-
- private bool _counterTrianglesEnabled = true;
- private sbyte _counterTrianglesTipOffset = 3;
- private bool _counterTrianglesFence = false;
-
- private bool _patternModel;
- private byte _bullsEyeFenceThickness = 10;
- private sbyte _bullsEyeFenceOffset;
- private bool _patternModelGlueBottomLayers = true;
- private bool _patternModelTextEnabled = true;
-
- #endregion
-
- #region Overrides
-
- public override bool CanROI => false;
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
-
- public override string Title => "Exposure time finder";
- public override string Description =>
- "Generates test models with various strategies and increments to verify the best exposure time for a given layer height.\n" +
- "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
- "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
-
- public override string ConfirmationText =>
- $"generate the exposure time finder test?";
-
- public override string ProgressTitle =>
- $"Generating the exposure time finder test";
-
- public override string ProgressAction => "Generated layers";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ private bool _holesEnabled = false;
+ private CalibrateExposureFinderShapes _holeShape = CalibrateExposureFinderShapes.Square;
+ private CalibrateExposureFinderMeasures _unitOfMeasure = CalibrateExposureFinderMeasures.Pixels;
+ private string _holeDiametersPx = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11";
+ private string _holeDiametersMm = "0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2";
+
+ private bool _barsEnabled = true;
+ private decimal _barSpacing = 1.5m;
+ private decimal _barLength = 4;
+ private sbyte _barVerticalSplitter = 0;
+ private byte _barFenceThickness = 10;
+ private sbyte _barFenceOffset = 4;
+ private string _barThicknessesPx = "4, 6, 8, 60"; //"4, 6, 8, 10, 12, 14, 16, 18, 20";
+ private string _barThicknessesMm = "0.2, 0.3, 0.4, 3"; //"0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2";
+
+ private bool _textEnabled = true;
+ private FontFace _textFont = TextMarkingFontFace;
+ private double _textScale = 1;
+ private byte _textThickness = 2;
+ private string _text = "ABHJQRWZ%&#"; //"ABGHJKLMQRSTUVWXZ%&#";
+
+ private bool _multipleBrightness;
+ private CalibrateExposureFinderMultipleBrightnessExcludeFrom _multipleBrightnessExcludeFrom = CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase;
+ private string _multipleBrightnessValues = null!;
+ private decimal _multipleBrightnessGenExposureTime;
+ private byte _multipleBrightnessGenEmulatedAALevel = FileFormat.MaximumAntiAliasing;
+ private byte _multipleBrightnessGenExposureFractions = 8;
+
+ private bool _multipleLayerHeight;
+ private decimal _multipleLayerHeightMaximum = 0.1m;
+ private decimal _multipleLayerHeightStep = 0.01m;
+
+ private CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes _multipleExposuresBaseLayersPrintMode;
+ private decimal _multipleExposuresBaseLayersCustomExposure;
+ private bool _differentSettingsForSamePositionedLayers;
+ private bool _samePositionedLayersLiftHeightEnabled = true;
+ private decimal _samePositionedLayersLiftHeight;
+ private bool _samePositionedLayersWaitTimeBeforeCureEnabled = true;
+ private decimal _samePositionedLayersWaitTimeBeforeCure;
+ private bool _multipleExposures;
+ private CalibrateExposureFinderExposureGenTypes _exposureGenType = CalibrateExposureFinderExposureGenTypes.Linear;
+ private bool _exposureGenIgnoreBaseExposure;
+ private decimal _exposureGenBottomStep = 0;
+ private decimal _exposureGenNormalStep = 0.2m;
+ private byte _exposureGenTests = 4;
+ private decimal _exposureGenManualLayerHeight;
+ private decimal _exposureGenManualBottom;
+ private decimal _exposureGenManualNormal;
+ private RangeObservableCollection<ExposureItem> _exposureTable = new();
+
+ private bool _bullsEyeEnabled = true;
+ private string _bullsEyeConfigurationPx = "26:5, 60:10, 116:15, 190:20";
+ private string _bullsEyeConfigurationMm = "1.3:0.25, 3:0.5, 5.8:0.75, 9.5:1";
+ private bool _bullsEyeInvertQuadrants = true;
+
+ private bool _counterTrianglesEnabled = true;
+ private sbyte _counterTrianglesTipOffset = 3;
+ private bool _counterTrianglesFence = false;
+
+ private bool _patternModel;
+ private byte _bullsEyeFenceThickness = 10;
+ private sbyte _bullsEyeFenceOffset;
+ private bool _patternModelGlueBottomLayers = true;
+ private bool _patternModelTextEnabled = true;
+
+ #endregion
+
+ #region Overrides
+
+ public override bool CanROI => false;
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "mdi-timer-cog";
+ public override string Title => "Exposure time finder";
+ public override string Description =>
+ "Generates test models with various strategies and increments to verify the best exposure time for a given layer height.\n" +
+ "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
+ "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
+
+ public override string ConfirmationText =>
+ $"generate the exposure time finder test?";
+
+ public override string ProgressTitle =>
+ $"Generating the exposure time finder test";
+
+ public override string ProgressAction => "Generated layers";
+
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- if (_displayWidth <= 0)
- {
- sb.AppendLine("Display width must be a positive value.");
- }
+ if (_displayWidth <= 0)
+ {
+ sb.AppendLine("Display width must be a positive value.");
+ }
- if (_displayHeight <= 0)
- {
- sb.AppendLine("Display height must be a positive value.");
- }
+ if (_displayHeight <= 0)
+ {
+ sb.AppendLine("Display height must be a positive value.");
+ }
- if (_chamferLayers * _layerHeight > _baseHeight)
- {
- sb.AppendLine("The chamfer can't be higher than the base height, lower the chamfer layer count.");
- }
+ if (_chamferLayers * _layerHeight > _baseHeight)
+ {
+ sb.AppendLine("The chamfer can't be higher than the base height, lower the chamfer layer count.");
+ }
- if (_multipleExposures)
+ if (_multipleExposures)
+ {
+ var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
+ for (decimal layerHeight = _layerHeight;
+ layerHeight <= endLayerHeight;
+ layerHeight += _multipleLayerHeightStep)
{
- var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
- for (decimal layerHeight = _layerHeight;
- layerHeight <= endLayerHeight;
- layerHeight += _multipleLayerHeightStep)
+ bool found = false;
+ foreach (var exposureItem in _exposureTable)
{
- bool found = false;
- foreach (var exposureItem in _exposureTable)
+ if (exposureItem.LayerHeight == layerHeight && exposureItem.IsValid)
{
- if (exposureItem.LayerHeight == layerHeight && exposureItem.IsValid)
- {
- found = true;
- break;
- }
+ found = true;
+ break;
}
- if(!found)
- sb.AppendLine($"[ME]: The {Layer.ShowHeight(layerHeight)}mm layer height have no set exposure(s).");
}
+ if(!found)
+ sb.AppendLine($"[ME]: The {Layer.ShowHeight(layerHeight)}mm layer height have no set exposure(s).");
}
+ }
- if (_multipleBrightness)
+ if (_multipleBrightness)
+ {
+ var brightnessValues = MultipleBrightnessValuesArray;
+ if (brightnessValues.Length == 0)
{
- var brightnessValues = MultipleBrightnessValuesArray;
- if (brightnessValues.Length == 0)
- {
- sb.AppendLine($"Multiple brightness tests are enabled but no valid values are set, use from 1 to 255.");
- }
- else
+ sb.AppendLine($"Multiple brightness tests are enabled but no valid values are set, use from 1 to 255.");
+ }
+ else
+ {
+ if (SlicerFile.IsAntiAliasingEmulated)
{
- if (SlicerFile.IsAntiAliasingEmulated)
+ if (brightnessValues.Length > 16)
{
- if (brightnessValues.Length > 16)
- {
- sb.AppendLine(
- "[ME] This format uses time fractions to emulate AntiAliasing, only up 16 levels of greys/brightness are permitted.");
- }
- else
+ sb.AppendLine(
+ "[ME] This format uses time fractions to emulate AntiAliasing, only up 16 levels of greys/brightness are permitted.");
+ }
+ else
+ {
+ /*byte aalevel = brightnessValues.Length switch
{
- /*byte aalevel = brightnessValues.Length switch
- {
- <= 2 => 2,
- <= 4 => 4,
- <= 8 => 8,
- <= 16 => 16,
- _ => 2
- };*/
+ <= 2 => 2,
+ <= 4 => 4,
+ <= 8 => 8,
+ <= 16 => 16,
+ _ => 2
+ };*/
- var increment = 255f / _multipleBrightnessGenEmulatedAALevel;
+ var increment = 255f / _multipleBrightnessGenEmulatedAALevel;
- byte[] validAA = new byte[_multipleBrightnessGenEmulatedAALevel];
+ byte[] validAA = new byte[_multipleBrightnessGenEmulatedAALevel];
- for (byte frac = 0; frac < _multipleBrightnessGenEmulatedAALevel; frac++)
- {
- validAA[frac] = (byte)(byte.MaxValue - increment * frac);
- }
+ for (byte frac = 0; frac < _multipleBrightnessGenEmulatedAALevel; frac++)
+ {
+ validAA[frac] = (byte)(byte.MaxValue - increment * frac);
+ }
- string invalidAA = string.Empty;
+ string invalidAA = string.Empty;
- foreach (var brightness in brightnessValues)
- {
- if (!validAA.Contains(brightness))
- invalidAA += $"{brightness}, ";
- }
+ foreach (var brightness in brightnessValues)
+ {
+ if (!validAA.Contains(brightness))
+ invalidAA += $"{brightness}, ";
+ }
- invalidAA = invalidAA.Trim().TrimEnd(',');
+ invalidAA = invalidAA.Trim().TrimEnd(',');
- if (!string.IsNullOrWhiteSpace(invalidAA))
- {
- sb.AppendLine($"[ME] This format uses time fractions to emulate AntiAliasing, only some levels greys/brightness are permitted, and everything outside that is thresholded.");
- sb.AppendLine($" - your input have the following wrong levels: {invalidAA}");
- sb.AppendLine($" - AntiAliasing level: {_multipleBrightnessGenEmulatedAALevel} with usable values of: {string.Join(", ", validAA)}");
- }
+ if (!string.IsNullOrWhiteSpace(invalidAA))
+ {
+ sb.AppendLine($"[ME] This format uses time fractions to emulate AntiAliasing, only some levels greys/brightness are permitted, and everything outside that is thresholded.");
+ sb.AppendLine($" - your input have the following wrong levels: {invalidAA}");
+ sb.AppendLine($" - AntiAliasing level: {_multipleBrightnessGenEmulatedAALevel} with usable values of: {string.Join(", ", validAA)}");
}
}
}
}
+ }
- if (_patternModel)
+ if (_patternModel)
+ {
+ if (!CanPatternModel)
{
- if (!CanPatternModel)
- {
- sb.AppendLine($"Unable to pattern the loaded model within the available space.");
- }
-
- if (!_multipleBrightness && !_multipleExposures)
- {
- sb.AppendLine($"Pattern the loaded model requires either multiple brightness or multiple exposures to use with.");
- }
+ sb.AppendLine($"Unable to pattern the loaded model within the available space.");
}
- else
+
+ if (!_multipleBrightness && !_multipleExposures)
{
- if (Bars.Length <= 0 && Holes.Length <= 0 && BullsEyes.Length <= 0 && TextSize.IsEmpty)
- {
- sb.AppendLine("No objects to output, enable at least 1 feature.");
- }
+ sb.AppendLine($"Pattern the loaded model requires either multiple brightness or multiple exposures to use with.");
}
-
- return sb.ToString();
}
-
- public override string ToString()
+ else
{
- var result = $"[Layer Height: {_layerHeight}] " +
- $"[Bottom layers: {_bottomLayers}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
- $"[TB:{_topBottomMargin} LR:{_leftRightMargin} PM:{_partMargin} FM:{_featuresMargin}] " +
- $"[Chamfer: {_chamferLayers}] [Erode: {_erodeBottomIterations}] " +
- $"[Obj height: {_featuresHeight}] " +
- $"[Holes: {Holes.Length}] [Bars: {Bars.Length}] [BE: {BullsEyes.Length}] [Text: {!string.IsNullOrWhiteSpace(_text)}]" +
- $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ if (Bars.Length <= 0 && Holes.Length <= 0 && BullsEyes.Length <= 0 && TextSize.IsEmpty)
+ {
+ sb.AppendLine("No objects to output, enable at least 1 feature.");
+ }
}
- #endregion
+ return sb.ToString();
+ }
+
+ public override string ToString()
+ {
+ var result = $"[Layer Height: {_layerHeight}] " +
+ $"[Bottom layers: {_bottomLayers}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
+ $"[TB:{_topBottomMargin} LR:{_leftRightMargin} PM:{_partMargin} FM:{_featuresMargin}] " +
+ $"[Chamfer: {_chamferLayers}] [Erode: {_erodeBottomIterations}] " +
+ $"[Obj height: {_featuresHeight}] " +
+ $"[Holes: {Holes.Length}] [Bars: {Bars.Length}] [BE: {BullsEyes.Length}] [Text: {!string.IsNullOrWhiteSpace(_text)}]" +
+ $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #endregion
- #region Properties
+ #region Properties
- public decimal DisplayWidth
+ public decimal DisplayWidth
+ {
+ get => _displayWidth;
+ set
{
- get => _displayWidth;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(Xppmm));
- }
+ if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(Xppmm));
}
+ }
- public decimal DisplayHeight
+ public decimal DisplayHeight
+ {
+ get => _displayHeight;
+ set
{
- get => _displayHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(Yppmm));
- }
+ if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(Yppmm));
}
+ }
- public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0;
- public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0;
- public decimal Ppmm => Math.Max(Xppmm, Yppmm);
+ public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0;
+ public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0;
+ public decimal Ppmm => Math.Max(Xppmm, Yppmm);
- public decimal LayerHeight
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- RaisePropertyChanged(nameof(AvailableLayerHeights));
- }
+ if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
+ RaisePropertyChanged(nameof(AvailableLayerHeights));
}
+ }
- public ushort Microns => (ushort)(LayerHeight * 1000);
+ public ushort Microns => (ushort)(LayerHeight * 1000);
- public ushort BottomLayers
+ public ushort BottomLayers
+ {
+ get => _bottomLayers;
+ set
{
- get => _bottomLayers;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
}
+ }
- public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
+ public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
- public decimal BottomExposure
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set
{
- get => _bottomExposure;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(MultipleBrightnessTable));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(MultipleBrightnessTable));
}
+ }
- public decimal NormalExposure
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set
{
- get => _normalExposure;
- set
- {
- if(!RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(MultipleBrightnessTable));
- }
+ if(!RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(MultipleBrightnessTable));
}
+ }
- public decimal TopBottomMargin
- {
- get => _topBottomMargin;
- set => RaiseAndSetIfChanged(ref _topBottomMargin, Math.Round(value, 2));
- }
+ public decimal TopBottomMargin
+ {
+ get => _topBottomMargin;
+ set => RaiseAndSetIfChanged(ref _topBottomMargin, Math.Round(value, 2));
+ }
- public decimal LeftRightMargin
- {
- get => _leftRightMargin;
- set => RaiseAndSetIfChanged(ref _leftRightMargin, Math.Round(value, 2));
- }
+ public decimal LeftRightMargin
+ {
+ get => _leftRightMargin;
+ set => RaiseAndSetIfChanged(ref _leftRightMargin, Math.Round(value, 2));
+ }
- public byte ChamferLayers
- {
- get => _chamferLayers;
- set => RaiseAndSetIfChanged(ref _chamferLayers, value);
- }
+ public byte ChamferLayers
+ {
+ get => _chamferLayers;
+ set => RaiseAndSetIfChanged(ref _chamferLayers, value);
+ }
- public byte ErodeBottomIterations
- {
- get => _erodeBottomIterations;
- set => RaiseAndSetIfChanged(ref _erodeBottomIterations, value);
- }
+ public byte ErodeBottomIterations
+ {
+ get => _erodeBottomIterations;
+ set => RaiseAndSetIfChanged(ref _erodeBottomIterations, value);
+ }
- public decimal PartMargin
- {
- get => _partMargin;
- set => RaiseAndSetIfChanged(ref _partMargin, Math.Round(value, 2));
- }
+ public decimal PartMargin
+ {
+ get => _partMargin;
+ set => RaiseAndSetIfChanged(ref _partMargin, Math.Round(value, 2));
+ }
- public bool EnableAntiAliasing
- {
- get => _enableAntiAliasing;
- set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
- }
+ public bool EnableAntiAliasing
+ {
+ get => _enableAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
+ }
- public bool MirrorOutput
- {
- get => _mirrorOutput;
- set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
- }
+ public bool MirrorOutput
+ {
+ get => _mirrorOutput;
+ set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
+ }
- public decimal BaseHeight
- {
- get => _baseHeight;
- set => RaiseAndSetIfChanged(ref _baseHeight, Math.Round(value, 2));
- }
+ public decimal BaseHeight
+ {
+ get => _baseHeight;
+ set => RaiseAndSetIfChanged(ref _baseHeight, Math.Round(value, 2));
+ }
- public decimal FeaturesHeight
- {
- get => _featuresHeight;
- set => RaiseAndSetIfChanged(ref _featuresHeight, Math.Round(value, 2));
- }
+ public decimal FeaturesHeight
+ {
+ get => _featuresHeight;
+ set => RaiseAndSetIfChanged(ref _featuresHeight, Math.Round(value, 2));
+ }
- public decimal TotalHeight => _baseHeight + _featuresHeight;
+ public decimal TotalHeight => _baseHeight + _featuresHeight;
- public decimal FeaturesMargin
- {
- get => _featuresMargin;
- set => RaiseAndSetIfChanged(ref _featuresMargin, Math.Round(value, 2));
- }
+ public decimal FeaturesMargin
+ {
+ get => _featuresMargin;
+ set => RaiseAndSetIfChanged(ref _featuresMargin, Math.Round(value, 2));
+ }
- public ushort StaircaseThicknessPx
- {
- get => _staircaseThicknessPx;
- set => RaiseAndSetIfChanged(ref _staircaseThicknessPx, value);
- }
+ public ushort StaircaseThicknessPx
+ {
+ get => _staircaseThicknessPx;
+ set => RaiseAndSetIfChanged(ref _staircaseThicknessPx, value);
+ }
- public decimal StaircaseThicknessMm
- {
- get => _staircaseThicknessMm;
- set => RaiseAndSetIfChanged(ref _staircaseThicknessMm, value);
- }
+ public decimal StaircaseThicknessMm
+ {
+ get => _staircaseThicknessMm;
+ set => RaiseAndSetIfChanged(ref _staircaseThicknessMm, value);
+ }
- public ushort StaircaseThickness => _unitOfMeasure == CalibrateExposureFinderMeasures.Pixels
- ? _staircaseThicknessPx
- : (ushort)(_staircaseThicknessMm * Yppmm);
+ public ushort StaircaseThickness => _unitOfMeasure == CalibrateExposureFinderMeasures.Pixels
+ ? _staircaseThicknessPx
+ : (ushort)(_staircaseThicknessMm * Yppmm);
- public bool CounterTrianglesEnabled
- {
- get => _counterTrianglesEnabled;
- set => RaiseAndSetIfChanged(ref _counterTrianglesEnabled, value);
- }
+ public bool CounterTrianglesEnabled
+ {
+ get => _counterTrianglesEnabled;
+ set => RaiseAndSetIfChanged(ref _counterTrianglesEnabled, value);
+ }
- public sbyte CounterTrianglesTipOffset
- {
- get => _counterTrianglesTipOffset;
- set => RaiseAndSetIfChanged(ref _counterTrianglesTipOffset, value);
- }
+ public sbyte CounterTrianglesTipOffset
+ {
+ get => _counterTrianglesTipOffset;
+ set => RaiseAndSetIfChanged(ref _counterTrianglesTipOffset, value);
+ }
- public bool CounterTrianglesFence
- {
- get => _counterTrianglesFence;
- set => RaiseAndSetIfChanged(ref _counterTrianglesFence, value);
- }
+ public bool CounterTrianglesFence
+ {
+ get => _counterTrianglesFence;
+ set => RaiseAndSetIfChanged(ref _counterTrianglesFence, value);
+ }
- public bool HolesEnabled
- {
- get => _holesEnabled;
- set => RaiseAndSetIfChanged(ref _holesEnabled, value);
- }
+ public bool HolesEnabled
+ {
+ get => _holesEnabled;
+ set => RaiseAndSetIfChanged(ref _holesEnabled, value);
+ }
- public CalibrateExposureFinderShapes HoleShape
- {
- get => _holeShape;
- set => RaiseAndSetIfChanged(ref _holeShape, value);
- }
+ public CalibrateExposureFinderShapes HoleShape
+ {
+ get => _holeShape;
+ set => RaiseAndSetIfChanged(ref _holeShape, value);
+ }
- public CalibrateExposureFinderMeasures UnitOfMeasure
+ public CalibrateExposureFinderMeasures UnitOfMeasure
+ {
+ get => _unitOfMeasure;
+ set
{
- get => _unitOfMeasure;
- set
- {
- if (!RaiseAndSetIfChanged(ref _unitOfMeasure, value)) return;
- RaisePropertyChanged(nameof(IsUnitOfMeasureMm));
- }
+ if (!RaiseAndSetIfChanged(ref _unitOfMeasure, value)) return;
+ RaisePropertyChanged(nameof(IsUnitOfMeasureMm));
}
+ }
- public bool IsUnitOfMeasureMm => _unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters;
+ public bool IsUnitOfMeasureMm => _unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters;
- public string HoleDiametersMm
- {
- get => _holeDiametersMm;
- set => RaiseAndSetIfChanged(ref _holeDiametersMm, value);
- }
+ public string HoleDiametersMm
+ {
+ get => _holeDiametersMm;
+ set => RaiseAndSetIfChanged(ref _holeDiametersMm, value);
+ }
- public string HoleDiametersPx
- {
- get => _holeDiametersPx;
- set => RaiseAndSetIfChanged(ref _holeDiametersPx, value);
- }
+ public string HoleDiametersPx
+ {
+ get => _holeDiametersPx;
+ set => RaiseAndSetIfChanged(ref _holeDiametersPx, value);
+ }
- /// <summary>
- /// Gets all holes in pixels and ordered
- /// </summary>
- public int[] Holes
+ /// <summary>
+ /// Gets all holes in pixels and ordered
+ /// </summary>
+ public int[] Holes
+ {
+ get
{
- get
+ if (!_holesEnabled)
{
- if (!_holesEnabled)
- {
- return Array.Empty<int>();
- }
+ return Array.Empty<int>();
+ }
- List<int> holes = new();
+ List<int> holes = new();
- if (_unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters)
+ if (_unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters)
+ {
+ var split = _holeDiametersMm.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var mmStr in split)
{
- var split = _holeDiametersMm.Split(',', StringSplitOptions.TrimEntries);
- foreach (var mmStr in split)
- {
- if (string.IsNullOrWhiteSpace(mmStr)) continue;
- if (!decimal.TryParse(mmStr, out var mm)) continue;
- var mmPx = (int)(mm * Ppmm);
- if (mmPx is <= 0 or > 500) continue;
- if(holes.Contains(mmPx)) continue;
- holes.Add(mmPx);
- }
+ if (string.IsNullOrWhiteSpace(mmStr)) continue;
+ if (!decimal.TryParse(mmStr, out var mm)) continue;
+ var mmPx = (int)(mm * Ppmm);
+ if (mmPx is <= 0 or > 500) continue;
+ if(holes.Contains(mmPx)) continue;
+ holes.Add(mmPx);
}
- else
+ }
+ else
+ {
+ var split = _holeDiametersPx.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var pxStr in split)
{
- var split = _holeDiametersPx.Split(',', StringSplitOptions.TrimEntries);
- foreach (var pxStr in split)
- {
- if (string.IsNullOrWhiteSpace(pxStr)) continue;
- if (!int.TryParse(pxStr, out var px)) continue;
- if (px is <= 0 or > 500) continue;
- if (holes.Contains(px)) continue;
- holes.Add(px);
- }
+ if (string.IsNullOrWhiteSpace(pxStr)) continue;
+ if (!int.TryParse(pxStr, out var px)) continue;
+ if (px is <= 0 or > 500) continue;
+ if (holes.Contains(px)) continue;
+ holes.Add(px);
}
-
- return holes.OrderBy(pixels => pixels).ToArray();
}
- }
- public int GetHolesHeight(int[] holes)
- {
- if (holes.Length == 0) return 0;
- return (int) (holes.Sum() + (holes.Length-1) * _featuresMargin * Yppmm);
+ return holes.OrderBy(pixels => pixels).ToArray();
}
+ }
- public bool BarsEnabled
- {
- get => _barsEnabled;
- set => RaiseAndSetIfChanged(ref _barsEnabled, value);
- }
+ public int GetHolesHeight(int[] holes)
+ {
+ if (holes.Length == 0) return 0;
+ return (int) (holes.Sum() + (holes.Length-1) * _featuresMargin * Yppmm);
+ }
- public decimal BarSpacing
- {
- get => _barSpacing;
- set => RaiseAndSetIfChanged(ref _barSpacing, value);
- }
+ public bool BarsEnabled
+ {
+ get => _barsEnabled;
+ set => RaiseAndSetIfChanged(ref _barsEnabled, value);
+ }
- public decimal BarLength
- {
- get => _barLength;
- set => RaiseAndSetIfChanged(ref _barLength, value);
- }
+ public decimal BarSpacing
+ {
+ get => _barSpacing;
+ set => RaiseAndSetIfChanged(ref _barSpacing, value);
+ }
- public sbyte BarVerticalSplitter
- {
- get => _barVerticalSplitter;
- set => RaiseAndSetIfChanged(ref _barVerticalSplitter, value);
- }
+ public decimal BarLength
+ {
+ get => _barLength;
+ set => RaiseAndSetIfChanged(ref _barLength, value);
+ }
- public byte BarFenceThickness
- {
- get => _barFenceThickness;
- set => RaiseAndSetIfChanged(ref _barFenceThickness, value);
- }
+ public sbyte BarVerticalSplitter
+ {
+ get => _barVerticalSplitter;
+ set => RaiseAndSetIfChanged(ref _barVerticalSplitter, value);
+ }
- public sbyte BarFenceOffset
- {
- get => _barFenceOffset;
- set => RaiseAndSetIfChanged(ref _barFenceOffset, value);
- }
+ public byte BarFenceThickness
+ {
+ get => _barFenceThickness;
+ set => RaiseAndSetIfChanged(ref _barFenceThickness, value);
+ }
- public string BarThicknessesPx
- {
- get => _barThicknessesPx;
- set => RaiseAndSetIfChanged(ref _barThicknessesPx, value);
- }
+ public sbyte BarFenceOffset
+ {
+ get => _barFenceOffset;
+ set => RaiseAndSetIfChanged(ref _barFenceOffset, value);
+ }
- public string BarThicknessesMm
- {
- get => _barThicknessesMm;
- set => RaiseAndSetIfChanged(ref _barThicknessesMm, value);
- }
+ public string BarThicknessesPx
+ {
+ get => _barThicknessesPx;
+ set => RaiseAndSetIfChanged(ref _barThicknessesPx, value);
+ }
- /// <summary>
- /// Gets all holes in pixels and ordered
- /// </summary>
- public int[] Bars
+ public string BarThicknessesMm
+ {
+ get => _barThicknessesMm;
+ set => RaiseAndSetIfChanged(ref _barThicknessesMm, value);
+ }
+
+ /// <summary>
+ /// Gets all holes in pixels and ordered
+ /// </summary>
+ public int[] Bars
+ {
+ get
{
- get
+ if (!_barsEnabled)
{
- if (!_barsEnabled)
- {
- return Array.Empty<int>();
- }
+ return Array.Empty<int>();
+ }
- List<int> bars = new();
+ List<int> bars = new();
- if (_unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters)
+ if (_unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters)
+ {
+ var split = _barThicknessesMm.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var mmStr in split)
{
- var split = _barThicknessesMm.Split(',', StringSplitOptions.TrimEntries);
- foreach (var mmStr in split)
- {
- if (string.IsNullOrWhiteSpace(mmStr)) continue;
- if (!decimal.TryParse(mmStr, out var mm)) continue;
- var mmPx = (int)(mm * Yppmm);
- if (mmPx is <= 0 or > 500) continue;
- if (bars.Contains(mmPx)) continue;
- bars.Add(mmPx);
- }
+ if (string.IsNullOrWhiteSpace(mmStr)) continue;
+ if (!decimal.TryParse(mmStr, out var mm)) continue;
+ var mmPx = (int)(mm * Yppmm);
+ if (mmPx is <= 0 or > 500) continue;
+ if (bars.Contains(mmPx)) continue;
+ bars.Add(mmPx);
}
- else
+ }
+ else
+ {
+ var split = _barThicknessesPx.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var pxStr in split)
{
- var split = _barThicknessesPx.Split(',', StringSplitOptions.TrimEntries);
- foreach (var pxStr in split)
- {
- if (string.IsNullOrWhiteSpace(pxStr)) continue;
- if (!int.TryParse(pxStr, out var px)) continue;
- if (px is <= 0 or > 500) continue;
- if (bars.Contains(px)) continue;
- bars.Add(px);
- }
+ if (string.IsNullOrWhiteSpace(pxStr)) continue;
+ if (!int.TryParse(pxStr, out var px)) continue;
+ if (px is <= 0 or > 500) continue;
+ if (bars.Contains(px)) continue;
+ bars.Add(px);
}
-
- return bars.OrderBy(pixels => pixels).ToArray();
}
- }
- public int GetBarsLength(int[] bars)
- {
- if (bars.Length == 0) return 0;
- int len = (int) (bars.Sum() + (bars.Length + 1) * _barSpacing * Yppmm);
- if (_barFenceThickness > 0)
- {
- len = Math.Max(len, len + _barFenceThickness * 2 + _barFenceOffset * 2);
- }
- return len;
+ return bars.OrderBy(pixels => pixels).ToArray();
}
+ }
- public bool TextEnabled
+ public int GetBarsLength(int[] bars)
+ {
+ if (bars.Length == 0) return 0;
+ int len = (int) (bars.Sum() + (bars.Length + 1) * _barSpacing * Yppmm);
+ if (_barFenceThickness > 0)
{
- get => _textEnabled;
- set => RaiseAndSetIfChanged(ref _textEnabled, value);
+ len = Math.Max(len, len + _barFenceThickness * 2 + _barFenceOffset * 2);
}
+ return len;
+ }
- public static Array TextFonts => Enum.GetValues(typeof(FontFace));
+ public bool TextEnabled
+ {
+ get => _textEnabled;
+ set => RaiseAndSetIfChanged(ref _textEnabled, value);
+ }
- public FontFace TextFont
- {
- get => _textFont;
- set => RaiseAndSetIfChanged(ref _textFont, value);
- }
+ public static Array TextFonts => Enum.GetValues(typeof(FontFace));
- public double TextScale
- {
- get => _textScale;
- set => RaiseAndSetIfChanged(ref _textScale, Math.Round(value, 2));
- }
+ public FontFace TextFont
+ {
+ get => _textFont;
+ set => RaiseAndSetIfChanged(ref _textFont, value);
+ }
- public byte TextThickness
- {
- get => _textThickness;
- set => RaiseAndSetIfChanged(ref _textThickness, value);
- }
+ public double TextScale
+ {
+ get => _textScale;
+ set => RaiseAndSetIfChanged(ref _textScale, Math.Round(value, 2));
+ }
- public string Text
- {
- get => _text;
- set => RaiseAndSetIfChanged(ref _text, value);
- }
+ public byte TextThickness
+ {
+ get => _textThickness;
+ set => RaiseAndSetIfChanged(ref _textThickness, value);
+ }
- public Size TextSize
- {
- get
- {
- if (!_textEnabled || string.IsNullOrWhiteSpace(_text)) return Size.Empty;
- int baseline = 0;
- return CvInvoke.GetTextSize(_text, _textFont, _textScale, _textThickness, ref baseline);
- }
- }
+ public string Text
+ {
+ get => _text;
+ set => RaiseAndSetIfChanged(ref _text, value);
+ }
- public bool MultipleBrightness
+ public Size TextSize
+ {
+ get
{
- get => _multipleBrightness;
- set => RaiseAndSetIfChanged(ref _multipleBrightness, value);
+ if (!_textEnabled || string.IsNullOrWhiteSpace(_text)) return Size.Empty;
+ int baseline = 0;
+ return CvInvoke.GetTextSize(_text, _textFont, _textScale, _textThickness, ref baseline);
}
+ }
- public CalibrateExposureFinderMultipleBrightnessExcludeFrom MultipleBrightnessExcludeFrom
- {
- get => _multipleBrightnessExcludeFrom;
- set => RaiseAndSetIfChanged(ref _multipleBrightnessExcludeFrom, value);
- }
+ public bool MultipleBrightness
+ {
+ get => _multipleBrightness;
+ set => RaiseAndSetIfChanged(ref _multipleBrightness, value);
+ }
- public string MultipleBrightnessValues
- {
- get => _multipleBrightnessValues;
- set
- {
- if(!RaiseAndSetIfChanged(ref _multipleBrightnessValues, value)) return;
- RaisePropertyChanged(nameof(MultipleBrightnessTable));
- }
- }
+ public CalibrateExposureFinderMultipleBrightnessExcludeFrom MultipleBrightnessExcludeFrom
+ {
+ get => _multipleBrightnessExcludeFrom;
+ set => RaiseAndSetIfChanged(ref _multipleBrightnessExcludeFrom, value);
+ }
- public List<ExposureItem> MultipleBrightnessTable
+ public string MultipleBrightnessValues
+ {
+ get => _multipleBrightnessValues;
+ set
{
- get
- {
- var brightnesses = MultipleBrightnessValuesArray;
- return brightnesses.Select(brightness => (ExposureItem)
- new(
- _layerHeight,
- Math.Round(brightness * _bottomExposure / byte.MaxValue, 2),
- Math.Round(brightness * _normalExposure / byte.MaxValue, 2),
- brightness)).ToList();
- }
+ if(!RaiseAndSetIfChanged(ref _multipleBrightnessValues, value)) return;
+ RaisePropertyChanged(nameof(MultipleBrightnessTable));
}
+ }
- public decimal MultipleBrightnessGenExposureTime
+ public List<ExposureItem> MultipleBrightnessTable
+ {
+ get
{
- get => _multipleBrightnessGenExposureTime;
- set => RaiseAndSetIfChanged(ref _multipleBrightnessGenExposureTime, value);
+ var brightnesses = MultipleBrightnessValuesArray;
+ return brightnesses.Select(brightness => (ExposureItem)
+ new(
+ _layerHeight,
+ Math.Round(brightness * _bottomExposure / byte.MaxValue, 2),
+ Math.Round(brightness * _normalExposure / byte.MaxValue, 2),
+ brightness)).ToList();
}
+ }
+
+ public decimal MultipleBrightnessGenExposureTime
+ {
+ get => _multipleBrightnessGenExposureTime;
+ set => RaiseAndSetIfChanged(ref _multipleBrightnessGenExposureTime, value);
+ }
- public byte MaximumAntiAliasing => FileFormat.MaximumAntiAliasing;
+ public byte MaximumAntiAliasing => FileFormat.MaximumAntiAliasing;
- public byte MultipleBrightnessGenEmulatedAALevel
+ public byte MultipleBrightnessGenEmulatedAALevel
+ {
+ get => _multipleBrightnessGenEmulatedAALevel;
+ set
{
- get => _multipleBrightnessGenEmulatedAALevel;
- set
- {
- if(!RaiseAndSetIfChanged(ref _multipleBrightnessGenEmulatedAALevel, value)) return;
- GenerateBrightnessExposureFractions();
- }
+ if(!RaiseAndSetIfChanged(ref _multipleBrightnessGenEmulatedAALevel, value)) return;
+ GenerateBrightnessExposureFractions();
}
+ }
- public byte MultipleBrightnessGenExposureFractions
+ public byte MultipleBrightnessGenExposureFractions
+ {
+ get => _multipleBrightnessGenExposureFractions;
+ set
{
- get => _multipleBrightnessGenExposureFractions;
- set
- {
- if(!RaiseAndSetIfChanged(ref _multipleBrightnessGenExposureFractions, value)) return;
- GenerateBrightnessExposureFractions();
- }
+ if(!RaiseAndSetIfChanged(ref _multipleBrightnessGenExposureFractions, value)) return;
+ GenerateBrightnessExposureFractions();
}
+ }
- /// <summary>
- /// Gets all holes in pixels and ordered
- /// </summary>
- public byte[] MultipleBrightnessValuesArray
+ /// <summary>
+ /// Gets all holes in pixels and ordered
+ /// </summary>
+ public byte[] MultipleBrightnessValuesArray
+ {
+ get
{
- get
+ List<byte> values = new();
+ if (!string.IsNullOrWhiteSpace(_multipleBrightnessValues))
{
- List<byte> values = new();
- if (!string.IsNullOrWhiteSpace(_multipleBrightnessValues))
+ var split = _multipleBrightnessValues.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var brightnessStr in split)
{
- var split = _multipleBrightnessValues.Split(',', StringSplitOptions.TrimEntries);
- foreach (var brightnessStr in split)
- {
- if (string.IsNullOrWhiteSpace(brightnessStr)) continue;
- if (!byte.TryParse(brightnessStr, out var brightness)) continue;
- if (brightness is <= 0 or > 255) continue;
- if (values.Contains(brightness)) continue;
- values.Add(brightness);
- }
+ if (string.IsNullOrWhiteSpace(brightnessStr)) continue;
+ if (!byte.TryParse(brightnessStr, out var brightness)) continue;
+ if (brightness is <= 0 or > 255) continue;
+ if (values.Contains(brightness)) continue;
+ values.Add(brightness);
}
-
- return values.OrderByDescending(brightness => brightness).ToArray();
}
+
+ return values.OrderByDescending(brightness => brightness).ToArray();
}
+ }
- public bool MultipleLayerHeight
+ public bool MultipleLayerHeight
+ {
+ get => _multipleLayerHeight;
+ set
{
- get => _multipleLayerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _multipleLayerHeight, value)) return;
- RaisePropertyChanged(nameof(AvailableLayerHeights));
- }
+ if(!RaiseAndSetIfChanged(ref _multipleLayerHeight, value)) return;
+ RaisePropertyChanged(nameof(AvailableLayerHeights));
}
+ }
- public decimal MultipleLayerHeightMaximum
+ public decimal MultipleLayerHeightMaximum
+ {
+ get => _multipleLayerHeightMaximum;
+ set
{
- get => _multipleLayerHeightMaximum;
- set
- {
- if(!RaiseAndSetIfChanged(ref _multipleLayerHeightMaximum, value)) return;
- RaisePropertyChanged(nameof(AvailableLayerHeights));
- }
+ if(!RaiseAndSetIfChanged(ref _multipleLayerHeightMaximum, value)) return;
+ RaisePropertyChanged(nameof(AvailableLayerHeights));
}
+ }
- public decimal MultipleLayerHeightStep
+ public decimal MultipleLayerHeightStep
+ {
+ get => _multipleLayerHeightStep;
+ set
{
- get => _multipleLayerHeightStep;
- set
- {
- if(!RaiseAndSetIfChanged(ref _multipleLayerHeightStep, value)) return;
- RaisePropertyChanged(nameof(AvailableLayerHeights));
- }
+ if(!RaiseAndSetIfChanged(ref _multipleLayerHeightStep, value)) return;
+ RaisePropertyChanged(nameof(AvailableLayerHeights));
}
+ }
- public CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes MultipleExposuresBaseLayersPrintMode
+ public CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes MultipleExposuresBaseLayersPrintMode
+ {
+ get => _multipleExposuresBaseLayersPrintMode;
+ set
{
- get => _multipleExposuresBaseLayersPrintMode;
- set
- {
- if(!RaiseAndSetIfChanged(ref _multipleExposuresBaseLayersPrintMode, value)) return;
- RaisePropertyChanged(nameof(IsMultipleExposuresBaseLayersPrintModeCustom));
- }
+ if(!RaiseAndSetIfChanged(ref _multipleExposuresBaseLayersPrintMode, value)) return;
+ RaisePropertyChanged(nameof(IsMultipleExposuresBaseLayersPrintModeCustom));
}
+ }
- public bool IsMultipleExposuresBaseLayersPrintModeCustom => _multipleExposuresBaseLayersPrintMode == CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.Custom;
+ public bool IsMultipleExposuresBaseLayersPrintModeCustom => _multipleExposuresBaseLayersPrintMode == CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.Custom;
- public decimal MultipleExposuresBaseLayersCustomExposure
- {
- get => _multipleExposuresBaseLayersCustomExposure;
- set => RaiseAndSetIfChanged(ref _multipleExposuresBaseLayersCustomExposure, value);
- }
+ public decimal MultipleExposuresBaseLayersCustomExposure
+ {
+ get => _multipleExposuresBaseLayersCustomExposure;
+ set => RaiseAndSetIfChanged(ref _multipleExposuresBaseLayersCustomExposure, value);
+ }
- public bool DifferentSettingsForSamePositionedLayers
- {
- get => _differentSettingsForSamePositionedLayers;
- set => RaiseAndSetIfChanged(ref _differentSettingsForSamePositionedLayers, value);
- }
+ public bool DifferentSettingsForSamePositionedLayers
+ {
+ get => _differentSettingsForSamePositionedLayers;
+ set => RaiseAndSetIfChanged(ref _differentSettingsForSamePositionedLayers, value);
+ }
- public bool SamePositionedLayersLiftHeightEnabled
- {
- get => _samePositionedLayersLiftHeightEnabled;
- set => RaiseAndSetIfChanged(ref _samePositionedLayersLiftHeightEnabled, value);
- }
+ public bool SamePositionedLayersLiftHeightEnabled
+ {
+ get => _samePositionedLayersLiftHeightEnabled;
+ set => RaiseAndSetIfChanged(ref _samePositionedLayersLiftHeightEnabled, value);
+ }
- public decimal SamePositionedLayersLiftHeight
- {
- get => _samePositionedLayersLiftHeight;
- set => RaiseAndSetIfChanged(ref _samePositionedLayersLiftHeight, Math.Round(value, 2));
- }
+ public decimal SamePositionedLayersLiftHeight
+ {
+ get => _samePositionedLayersLiftHeight;
+ set => RaiseAndSetIfChanged(ref _samePositionedLayersLiftHeight, Math.Round(value, 2));
+ }
- public bool SamePositionedLayersWaitTimeBeforeCureEnabled
- {
- get => _samePositionedLayersWaitTimeBeforeCureEnabled;
- set => RaiseAndSetIfChanged(ref _samePositionedLayersWaitTimeBeforeCureEnabled, value);
- }
+ public bool SamePositionedLayersWaitTimeBeforeCureEnabled
+ {
+ get => _samePositionedLayersWaitTimeBeforeCureEnabled;
+ set => RaiseAndSetIfChanged(ref _samePositionedLayersWaitTimeBeforeCureEnabled, value);
+ }
- public decimal SamePositionedLayersWaitTimeBeforeCure
- {
- get => _samePositionedLayersWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _samePositionedLayersWaitTimeBeforeCure, Math.Round(value, 2));
- }
+ public decimal SamePositionedLayersWaitTimeBeforeCure
+ {
+ get => _samePositionedLayersWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _samePositionedLayersWaitTimeBeforeCure, Math.Round(value, 2));
+ }
- public bool MultipleExposures
- {
- get => _multipleExposures;
- set => RaiseAndSetIfChanged(ref _multipleExposures, value);
- }
+ public bool MultipleExposures
+ {
+ get => _multipleExposures;
+ set => RaiseAndSetIfChanged(ref _multipleExposures, value);
+ }
- public CalibrateExposureFinderExposureGenTypes ExposureGenType
- {
- get => _exposureGenType;
- set => RaiseAndSetIfChanged(ref _exposureGenType, value);
- }
+ public CalibrateExposureFinderExposureGenTypes ExposureGenType
+ {
+ get => _exposureGenType;
+ set => RaiseAndSetIfChanged(ref _exposureGenType, value);
+ }
- public bool ExposureGenIgnoreBaseExposure
- {
- get => _exposureGenIgnoreBaseExposure;
- set => RaiseAndSetIfChanged(ref _exposureGenIgnoreBaseExposure, value);
- }
+ public bool ExposureGenIgnoreBaseExposure
+ {
+ get => _exposureGenIgnoreBaseExposure;
+ set => RaiseAndSetIfChanged(ref _exposureGenIgnoreBaseExposure, value);
+ }
- public decimal ExposureGenBottomStep
- {
- get => _exposureGenBottomStep;
- set => RaiseAndSetIfChanged(ref _exposureGenBottomStep, Math.Round(value, 2));
- }
+ public decimal ExposureGenBottomStep
+ {
+ get => _exposureGenBottomStep;
+ set => RaiseAndSetIfChanged(ref _exposureGenBottomStep, Math.Round(value, 2));
+ }
- public decimal ExposureGenNormalStep
- {
- get => _exposureGenNormalStep;
- set => RaiseAndSetIfChanged(ref _exposureGenNormalStep, Math.Round(value, 2));
- }
+ public decimal ExposureGenNormalStep
+ {
+ get => _exposureGenNormalStep;
+ set => RaiseAndSetIfChanged(ref _exposureGenNormalStep, Math.Round(value, 2));
+ }
- public byte ExposureGenTests
- {
- get => _exposureGenTests;
- set => RaiseAndSetIfChanged(ref _exposureGenTests, value);
- }
+ public byte ExposureGenTests
+ {
+ get => _exposureGenTests;
+ set => RaiseAndSetIfChanged(ref _exposureGenTests, value);
+ }
- public decimal ExposureGenManualLayerHeight
- {
- get => _exposureGenManualLayerHeight;
- set => RaiseAndSetIfChanged(ref _exposureGenManualLayerHeight, value);
- }
+ public decimal ExposureGenManualLayerHeight
+ {
+ get => _exposureGenManualLayerHeight;
+ set => RaiseAndSetIfChanged(ref _exposureGenManualLayerHeight, value);
+ }
- public decimal[] AvailableLayerHeights
+ public decimal[] AvailableLayerHeights
+ {
+ get
{
- get
+ List<decimal> layerHeights = new();
+ var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
+ for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep)
{
- List<decimal> layerHeights = new();
- var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
- for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep)
- {
- layerHeights.Add(Layer.RoundHeight(layerHeight));
- }
-
- return layerHeights.ToArray();
+ layerHeights.Add(Layer.RoundHeight(layerHeight));
}
- }
- public decimal ExposureGenManualBottom
- {
- get => _exposureGenManualBottom;
- set => RaiseAndSetIfChanged(ref _exposureGenManualBottom, value);
+ return layerHeights.ToArray();
}
+ }
- public decimal ExposureGenManualNormal
- {
- get => _exposureGenManualNormal;
- set => RaiseAndSetIfChanged(ref _exposureGenManualNormal, value);
- }
+ public decimal ExposureGenManualBottom
+ {
+ get => _exposureGenManualBottom;
+ set => RaiseAndSetIfChanged(ref _exposureGenManualBottom, value);
+ }
- public ExposureItem ExposureManualEntry => new (_exposureGenManualLayerHeight, _exposureGenManualBottom, _exposureGenManualNormal);
+ public decimal ExposureGenManualNormal
+ {
+ get => _exposureGenManualNormal;
+ set => RaiseAndSetIfChanged(ref _exposureGenManualNormal, value);
+ }
+ public ExposureItem ExposureManualEntry => new (_exposureGenManualLayerHeight, _exposureGenManualBottom, _exposureGenManualNormal);
- public RangeObservableCollection<ExposureItem> ExposureTable
- {
- get => _exposureTable;
- set => RaiseAndSetIfChanged(ref _exposureTable, value);
- }
- public bool BullsEyeEnabled
- {
- get => _bullsEyeEnabled;
- set => RaiseAndSetIfChanged(ref _bullsEyeEnabled, value);
- }
+ public RangeObservableCollection<ExposureItem> ExposureTable
+ {
+ get => _exposureTable;
+ set => RaiseAndSetIfChanged(ref _exposureTable, value);
+ }
- public string BullsEyeConfigurationPx
- {
- get => _bullsEyeConfigurationPx;
- set => RaiseAndSetIfChanged(ref _bullsEyeConfigurationPx, value);
- }
+ public bool BullsEyeEnabled
+ {
+ get => _bullsEyeEnabled;
+ set => RaiseAndSetIfChanged(ref _bullsEyeEnabled, value);
+ }
- public string BullsEyeConfigurationMm
- {
- get => _bullsEyeConfigurationMm;
- set => RaiseAndSetIfChanged(ref _bullsEyeConfigurationMm, value);
- }
+ public string BullsEyeConfigurationPx
+ {
+ get => _bullsEyeConfigurationPx;
+ set => RaiseAndSetIfChanged(ref _bullsEyeConfigurationPx, value);
+ }
- public byte BullsEyeFenceThickness
- {
- get => _bullsEyeFenceThickness;
- set => RaiseAndSetIfChanged(ref _bullsEyeFenceThickness, value);
- }
+ public string BullsEyeConfigurationMm
+ {
+ get => _bullsEyeConfigurationMm;
+ set => RaiseAndSetIfChanged(ref _bullsEyeConfigurationMm, value);
+ }
- public sbyte BullsEyeFenceOffset
- {
- get => _bullsEyeFenceOffset;
- set => RaiseAndSetIfChanged(ref _bullsEyeFenceOffset, value);
- }
+ public byte BullsEyeFenceThickness
+ {
+ get => _bullsEyeFenceThickness;
+ set => RaiseAndSetIfChanged(ref _bullsEyeFenceThickness, value);
+ }
- public bool BullsEyeInvertQuadrants
- {
- get => _bullsEyeInvertQuadrants;
- set => RaiseAndSetIfChanged(ref _bullsEyeInvertQuadrants, value);
- }
+ public sbyte BullsEyeFenceOffset
+ {
+ get => _bullsEyeFenceOffset;
+ set => RaiseAndSetIfChanged(ref _bullsEyeFenceOffset, value);
+ }
- /// <summary>
- /// Gets all holes in pixels and ordered
- /// </summary>
- public BullsEyeCircle[] BullsEyes
+ public bool BullsEyeInvertQuadrants
+ {
+ get => _bullsEyeInvertQuadrants;
+ set => RaiseAndSetIfChanged(ref _bullsEyeInvertQuadrants, value);
+ }
+
+ /// <summary>
+ /// Gets all holes in pixels and ordered
+ /// </summary>
+ public BullsEyeCircle[] BullsEyes
+ {
+ get
{
- get
+ if (!_bullsEyeEnabled)
{
- if (!_bullsEyeEnabled)
- {
- return Array.Empty<BullsEyeCircle>();
- }
+ return Array.Empty<BullsEyeCircle>();
+ }
- List<BullsEyeCircle> bulleyes = new();
+ List<BullsEyeCircle> bulleyes = new();
- if (_unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters)
+ if (_unitOfMeasure == CalibrateExposureFinderMeasures.Millimeters)
+ {
+ var splitGroup = _bullsEyeConfigurationMm.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var group in splitGroup)
{
- var splitGroup = _bullsEyeConfigurationMm.Split(',', StringSplitOptions.TrimEntries);
- foreach (var group in splitGroup)
- {
- var splitDiameterThickness = group.Split(':', StringSplitOptions.TrimEntries);
- if (splitDiameterThickness.Length < 2) continue;
-
- if (string.IsNullOrWhiteSpace(splitDiameterThickness[0]) ||
- string.IsNullOrWhiteSpace(splitDiameterThickness[1])) continue;
- if (!decimal.TryParse(splitDiameterThickness[0], out var diameterMm)) continue;
- if (!decimal.TryParse(splitDiameterThickness[1], out var thicknessMm)) continue;
- var diameter = (int)(diameterMm * Ppmm);
- if (diameterMm is <= 0 or > 500) continue;
- var thickness = (int)(thicknessMm * Ppmm);
- if (thickness is <= 0 or > 500) continue;
- if (bulleyes.Exists(circle => circle.Diameter == diameter)) continue;
- bulleyes.Add(new BullsEyeCircle((ushort)diameter, (ushort)thickness));
- }
+ var splitDiameterThickness = group.Split(':', StringSplitOptions.TrimEntries);
+ if (splitDiameterThickness.Length < 2) continue;
+
+ if (string.IsNullOrWhiteSpace(splitDiameterThickness[0]) ||
+ string.IsNullOrWhiteSpace(splitDiameterThickness[1])) continue;
+ if (!decimal.TryParse(splitDiameterThickness[0], out var diameterMm)) continue;
+ if (!decimal.TryParse(splitDiameterThickness[1], out var thicknessMm)) continue;
+ var diameter = (int)(diameterMm * Ppmm);
+ if (diameterMm is <= 0 or > 500) continue;
+ var thickness = (int)(thicknessMm * Ppmm);
+ if (thickness is <= 0 or > 500) continue;
+ if (bulleyes.Exists(circle => circle.Diameter == diameter)) continue;
+ bulleyes.Add(new BullsEyeCircle((ushort)diameter, (ushort)thickness));
}
- else
+ }
+ else
+ {
+ var splitGroup = _bullsEyeConfigurationPx.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var group in splitGroup)
{
- var splitGroup = _bullsEyeConfigurationPx.Split(',', StringSplitOptions.TrimEntries);
- foreach (var group in splitGroup)
- {
- var splitDiameterThickness = group.Split(':', StringSplitOptions.TrimEntries);
- if (splitDiameterThickness.Length < 2) continue;
-
- if (string.IsNullOrWhiteSpace(splitDiameterThickness[0]) ||
- string.IsNullOrWhiteSpace(splitDiameterThickness[1])) continue;
- if (!int.TryParse(splitDiameterThickness[0], out var diameter)) continue;
- if (!int.TryParse(splitDiameterThickness[1], out var thickness)) continue;
- if (diameter is <= 0 or > 500) continue;
- if (thickness is <= 0 or > 500) continue;
- if (bulleyes.Exists(circle => circle.Diameter == diameter)) continue;
- bulleyes.Add(new BullsEyeCircle((ushort) diameter, (ushort) thickness));
- }
+ var splitDiameterThickness = group.Split(':', StringSplitOptions.TrimEntries);
+ if (splitDiameterThickness.Length < 2) continue;
+
+ if (string.IsNullOrWhiteSpace(splitDiameterThickness[0]) ||
+ string.IsNullOrWhiteSpace(splitDiameterThickness[1])) continue;
+ if (!int.TryParse(splitDiameterThickness[0], out var diameter)) continue;
+ if (!int.TryParse(splitDiameterThickness[1], out var thickness)) continue;
+ if (diameter is <= 0 or > 500) continue;
+ if (thickness is <= 0 or > 500) continue;
+ if (bulleyes.Exists(circle => circle.Diameter == diameter)) continue;
+ bulleyes.Add(new BullsEyeCircle((ushort) diameter, (ushort) thickness));
}
-
- return bulleyes.OrderBy(circle => circle.Diameter).DistinctBy(circle => circle.Diameter).ToArray();
}
+
+ return bulleyes.OrderBy(circle => circle.Diameter).DistinctBy(circle => circle.Diameter).ToArray();
}
- public int GetBullsEyeMaxPanelDiameter(BullsEyeCircle[] bullseyes)
- {
- if (!_bullsEyeEnabled || bullseyes.Length == 0) return 0;
- var diameter = GetBullsEyeMaxDiameter(bullseyes);
- return Math.Max(diameter, diameter + _bullsEyeFenceThickness + _bullsEyeFenceOffset * 2);
- }
+ }
+ public int GetBullsEyeMaxPanelDiameter(BullsEyeCircle[] bullseyes)
+ {
+ if (!_bullsEyeEnabled || bullseyes.Length == 0) return 0;
+ var diameter = GetBullsEyeMaxDiameter(bullseyes);
+ return Math.Max(diameter, diameter + _bullsEyeFenceThickness + _bullsEyeFenceOffset * 2);
+ }
- public int GetBullsEyeMaxDiameter(BullsEyeCircle[] bullseyes)
- {
- if (!_bullsEyeEnabled || bullseyes.Length == 0) return 0;
- return bullseyes[^1].Diameter + bullseyes[^1].Thickness / 2;
- }
+ public int GetBullsEyeMaxDiameter(BullsEyeCircle[] bullseyes)
+ {
+ if (!_bullsEyeEnabled || bullseyes.Length == 0) return 0;
+ return bullseyes[^1].Diameter + bullseyes[^1].Thickness / 2;
+ }
- public bool PatternModel
+ public bool PatternModel
+ {
+ get => _patternModel;
+ set
{
- get => _patternModel;
- set
+ if(!RaiseAndSetIfChanged(ref _patternModel, value)) return;
+ if (_patternModel)
{
- if(!RaiseAndSetIfChanged(ref _patternModel, value)) return;
- if (_patternModel)
- {
- if(SlicerFile is not null) LayerHeight = (decimal) SlicerFile.LayerHeight;
- MultipleLayerHeight = false;
- }
+ if(SlicerFile is not null) LayerHeight = (decimal) SlicerFile.LayerHeight;
+ MultipleLayerHeight = false;
}
}
+ }
- public bool PatternModelGlueBottomLayers
- {
- get => _patternModelGlueBottomLayers;
- set => RaiseAndSetIfChanged(ref _patternModelGlueBottomLayers, value);
- }
+ public bool PatternModelGlueBottomLayers
+ {
+ get => _patternModelGlueBottomLayers;
+ set => RaiseAndSetIfChanged(ref _patternModelGlueBottomLayers, value);
+ }
- public bool PatternModelTextEnabled
- {
- get => _patternModelTextEnabled;
- set => RaiseAndSetIfChanged(ref _patternModelTextEnabled, value);
- }
+ public bool PatternModelTextEnabled
+ {
+ get => _patternModelTextEnabled;
+ set => RaiseAndSetIfChanged(ref _patternModelTextEnabled, value);
+ }
- public bool CanPatternModel => SlicerFile.BoundingRectangle.Width * 2 + _leftRightMargin * 2 + _partMargin * Xppmm < SlicerFile.ResolutionX ||
- SlicerFile.BoundingRectangle.Height * 2 + _topBottomMargin * 2 + _partMargin * Yppmm < SlicerFile.ResolutionY;
+ public bool CanPatternModel => SlicerFile.BoundingRectangle.Width * 2 + _leftRightMargin * 2 + _partMargin * Xppmm < SlicerFile.ResolutionX ||
+ SlicerFile.BoundingRectangle.Height * 2 + _topBottomMargin * 2 + _partMargin * Yppmm < SlicerFile.ResolutionY;
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationCalibrateExposureFinder() { }
+ public OperationCalibrateExposureFinder() { }
- public OperationCalibrateExposureFinder(FileFormat slicerFile) : base(slicerFile)
+ public OperationCalibrateExposureFinder(FileFormat slicerFile) : base(slicerFile)
+ {
+ if (SlicerFile.SupportPerLayerSettings)
{
- if (SlicerFile.SupportPerLayerSettings)
+ _differentSettingsForSamePositionedLayers = true;
+ if (SlicerFile.SupportsGCode)
{
- _differentSettingsForSamePositionedLayers = true;
- if (SlicerFile.SupportsGCode)
- {
- _samePositionedLayersLiftHeight = 0;
- _samePositionedLayersWaitTimeBeforeCure = 2;
- }
- else
- {
- _samePositionedLayersLiftHeight = 0.1m;
- _samePositionedLayersWaitTimeBeforeCure = 0;
- }
+ _samePositionedLayersLiftHeight = 0;
+ _samePositionedLayersWaitTimeBeforeCure = 2;
+ }
+ else
+ {
+ _samePositionedLayersLiftHeight = 0.1m;
+ _samePositionedLayersWaitTimeBeforeCure = 0;
}
-
}
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
+ }
+
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
- _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
+ _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
- if (SlicerFile.DisplayWidth > 0)
- DisplayWidth = (decimal)SlicerFile.DisplayWidth;
- if (SlicerFile.DisplayHeight > 0)
- DisplayHeight = (decimal)SlicerFile.DisplayHeight;
+ if (SlicerFile.DisplayWidth > 0)
+ DisplayWidth = (decimal)SlicerFile.DisplayWidth;
+ if (SlicerFile.DisplayHeight > 0)
+ DisplayHeight = (decimal)SlicerFile.DisplayHeight;
- if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
- if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
- if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
+ if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
+ if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
- if (_exposureGenManualBottom == 0)
- _exposureGenManualBottom = (decimal) SlicerFile.BottomExposureTime;
- if (_exposureGenManualNormal == 0)
- _exposureGenManualNormal = (decimal)SlicerFile.ExposureTime;
- if (_multipleBrightnessGenExposureTime == 0)
- _multipleBrightnessGenExposureTime = (decimal)SlicerFile.ExposureTime;
+ if (_exposureGenManualBottom == 0)
+ _exposureGenManualBottom = (decimal) SlicerFile.BottomExposureTime;
+ if (_exposureGenManualNormal == 0)
+ _exposureGenManualNormal = (decimal)SlicerFile.ExposureTime;
+ if (_multipleBrightnessGenExposureTime == 0)
+ _multipleBrightnessGenExposureTime = (decimal)SlicerFile.ExposureTime;
- if (_multipleExposuresBaseLayersCustomExposure <= 0) _multipleExposuresBaseLayersCustomExposure = (decimal)SlicerFile.ExposureTime;
+ if (_multipleExposuresBaseLayersCustomExposure <= 0) _multipleExposuresBaseLayersCustomExposure = (decimal)SlicerFile.ExposureTime;
- if (!SlicerFile.CanUseLayerExposureTime)
- {
- _multipleLayerHeight = false;
- _multipleExposures = false;
- }
-
- if (string.IsNullOrWhiteSpace(_multipleBrightnessValues))
- {
- _multipleBrightnessValues =
- SlicerFile.IsAntiAliasingEmulated
- ? "255, 239, 223, 207, 191, 175, 159, 143"
- : "255, 242, 230, 217, 204, 191";
- }
- }
-
- #endregion
-
- #region Equality
-
- private bool Equals(OperationCalibrateExposureFinder other)
+ if (!SlicerFile.CanUseLayerExposureTime)
{
- return _displayWidth == other._displayWidth && _displayHeight == other._displayHeight && _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _chamferLayers == other._chamferLayers && _erodeBottomIterations == other._erodeBottomIterations && _partMargin == other._partMargin && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _baseHeight == other._baseHeight && _featuresHeight == other._featuresHeight && _featuresMargin == other._featuresMargin && _staircaseThicknessPx == other._staircaseThicknessPx && _staircaseThicknessMm == other._staircaseThicknessMm && _holesEnabled == other._holesEnabled && _holeShape == other._holeShape && _unitOfMeasure == other._unitOfMeasure && _holeDiametersPx == other._holeDiametersPx && _holeDiametersMm == other._holeDiametersMm && _barsEnabled == other._barsEnabled && _barSpacing == other._barSpacing && _barLength == other._barLength && _barVerticalSplitter == other._barVerticalSplitter && _barFenceThickness == other._barFenceThickness && _barFenceOffset == other._barFenceOffset && _barThicknessesPx == other._barThicknessesPx && _barThicknessesMm == other._barThicknessesMm && _textEnabled == other._textEnabled && _textFont == other._textFont && _textScale.Equals(other._textScale) && _textThickness == other._textThickness && _text == other._text && _multipleBrightness == other._multipleBrightness && _multipleBrightnessExcludeFrom == other._multipleBrightnessExcludeFrom && _multipleBrightnessValues == other._multipleBrightnessValues && _multipleBrightnessGenExposureTime == other._multipleBrightnessGenExposureTime && _multipleBrightnessGenEmulatedAALevel == other._multipleBrightnessGenEmulatedAALevel && _multipleBrightnessGenExposureFractions == other._multipleBrightnessGenExposureFractions && _multipleLayerHeight == other._multipleLayerHeight && _multipleLayerHeightMaximum == other._multipleLayerHeightMaximum && _multipleLayerHeightStep == other._multipleLayerHeightStep && _multipleExposuresBaseLayersPrintMode == other._multipleExposuresBaseLayersPrintMode && _multipleExposuresBaseLayersCustomExposure == other._multipleExposuresBaseLayersCustomExposure && _differentSettingsForSamePositionedLayers == other._differentSettingsForSamePositionedLayers && _samePositionedLayersLiftHeightEnabled == other._samePositionedLayersLiftHeightEnabled && _samePositionedLayersLiftHeight == other._samePositionedLayersLiftHeight && _samePositionedLayersWaitTimeBeforeCureEnabled == other._samePositionedLayersWaitTimeBeforeCureEnabled && _samePositionedLayersWaitTimeBeforeCure == other._samePositionedLayersWaitTimeBeforeCure && _multipleExposures == other._multipleExposures && _exposureGenType == other._exposureGenType && _exposureGenIgnoreBaseExposure == other._exposureGenIgnoreBaseExposure && _exposureGenBottomStep == other._exposureGenBottomStep && _exposureGenNormalStep == other._exposureGenNormalStep && _exposureGenTests == other._exposureGenTests && _exposureGenManualLayerHeight == other._exposureGenManualLayerHeight && _exposureGenManualBottom == other._exposureGenManualBottom && _exposureGenManualNormal == other._exposureGenManualNormal && Equals(_exposureTable, other._exposureTable) && _bullsEyeEnabled == other._bullsEyeEnabled && _bullsEyeConfigurationPx == other._bullsEyeConfigurationPx && _bullsEyeConfigurationMm == other._bullsEyeConfigurationMm && _bullsEyeInvertQuadrants == other._bullsEyeInvertQuadrants && _counterTrianglesEnabled == other._counterTrianglesEnabled && _counterTrianglesTipOffset == other._counterTrianglesTipOffset && _counterTrianglesFence == other._counterTrianglesFence && _patternModel == other._patternModel && _bullsEyeFenceThickness == other._bullsEyeFenceThickness && _bullsEyeFenceOffset == other._bullsEyeFenceOffset && _patternModelGlueBottomLayers == other._patternModelGlueBottomLayers && _patternModelTextEnabled == other._patternModelTextEnabled;
+ _multipleLayerHeight = false;
+ _multipleExposures = false;
}
- public override bool Equals(object obj)
+ if (string.IsNullOrWhiteSpace(_multipleBrightnessValues))
{
- return ReferenceEquals(this, obj) || obj is OperationCalibrateExposureFinder other && Equals(other);
+ _multipleBrightnessValues =
+ SlicerFile.IsAntiAliasingEmulated
+ ? "255, 239, 223, 207, 191, 175, 159, 143"
+ : "255, 242, 230, 217, 204, 191";
}
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_displayWidth);
- hashCode.Add(_displayHeight);
- hashCode.Add(_layerHeight);
- hashCode.Add(_bottomLayers);
- hashCode.Add(_bottomExposure);
- hashCode.Add(_normalExposure);
- hashCode.Add(_topBottomMargin);
- hashCode.Add(_leftRightMargin);
- hashCode.Add(_chamferLayers);
- hashCode.Add(_erodeBottomIterations);
- hashCode.Add(_partMargin);
- hashCode.Add(_enableAntiAliasing);
- hashCode.Add(_mirrorOutput);
- hashCode.Add(_baseHeight);
- hashCode.Add(_featuresHeight);
- hashCode.Add(_featuresMargin);
- hashCode.Add(_staircaseThicknessPx);
- hashCode.Add(_staircaseThicknessMm);
- hashCode.Add(_holesEnabled);
- hashCode.Add((int)_holeShape);
- hashCode.Add((int)_unitOfMeasure);
- hashCode.Add(_holeDiametersPx);
- hashCode.Add(_holeDiametersMm);
- hashCode.Add(_barsEnabled);
- hashCode.Add(_barSpacing);
- hashCode.Add(_barLength);
- hashCode.Add(_barVerticalSplitter);
- hashCode.Add(_barFenceThickness);
- hashCode.Add(_barFenceOffset);
- hashCode.Add(_barThicknessesPx);
- hashCode.Add(_barThicknessesMm);
- hashCode.Add(_textEnabled);
- hashCode.Add((int)_textFont);
- hashCode.Add(_textScale);
- hashCode.Add(_textThickness);
- hashCode.Add(_text);
- hashCode.Add(_multipleBrightness);
- hashCode.Add((int)_multipleBrightnessExcludeFrom);
- hashCode.Add(_multipleBrightnessValues);
- hashCode.Add(_multipleBrightnessGenExposureTime);
- hashCode.Add(_multipleBrightnessGenEmulatedAALevel);
- hashCode.Add(_multipleBrightnessGenExposureFractions);
- hashCode.Add(_multipleLayerHeight);
- hashCode.Add(_multipleLayerHeightMaximum);
- hashCode.Add(_multipleLayerHeightStep);
- hashCode.Add((int)_multipleExposuresBaseLayersPrintMode);
- hashCode.Add(_multipleExposuresBaseLayersCustomExposure);
- hashCode.Add(_differentSettingsForSamePositionedLayers);
- hashCode.Add(_samePositionedLayersLiftHeightEnabled);
- hashCode.Add(_samePositionedLayersLiftHeight);
- hashCode.Add(_samePositionedLayersWaitTimeBeforeCureEnabled);
- hashCode.Add(_samePositionedLayersWaitTimeBeforeCure);
- hashCode.Add(_multipleExposures);
- hashCode.Add((int)_exposureGenType);
- hashCode.Add(_exposureGenIgnoreBaseExposure);
- hashCode.Add(_exposureGenBottomStep);
- hashCode.Add(_exposureGenNormalStep);
- hashCode.Add(_exposureGenTests);
- hashCode.Add(_exposureGenManualLayerHeight);
- hashCode.Add(_exposureGenManualBottom);
- hashCode.Add(_exposureGenManualNormal);
- hashCode.Add(_exposureTable);
- hashCode.Add(_bullsEyeEnabled);
- hashCode.Add(_bullsEyeConfigurationPx);
- hashCode.Add(_bullsEyeConfigurationMm);
- hashCode.Add(_bullsEyeInvertQuadrants);
- hashCode.Add(_counterTrianglesEnabled);
- hashCode.Add(_counterTrianglesTipOffset);
- hashCode.Add(_counterTrianglesFence);
- hashCode.Add(_patternModel);
- hashCode.Add(_bullsEyeFenceThickness);
- hashCode.Add(_bullsEyeFenceOffset);
- hashCode.Add(_patternModelGlueBottomLayers);
- hashCode.Add(_patternModelTextEnabled);
- return hashCode.ToHashCode();
- }
+ #endregion
+
+ #region Equality
+
+ private bool Equals(OperationCalibrateExposureFinder other)
+ {
+ return _displayWidth == other._displayWidth && _displayHeight == other._displayHeight && _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _chamferLayers == other._chamferLayers && _erodeBottomIterations == other._erodeBottomIterations && _partMargin == other._partMargin && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _baseHeight == other._baseHeight && _featuresHeight == other._featuresHeight && _featuresMargin == other._featuresMargin && _staircaseThicknessPx == other._staircaseThicknessPx && _staircaseThicknessMm == other._staircaseThicknessMm && _holesEnabled == other._holesEnabled && _holeShape == other._holeShape && _unitOfMeasure == other._unitOfMeasure && _holeDiametersPx == other._holeDiametersPx && _holeDiametersMm == other._holeDiametersMm && _barsEnabled == other._barsEnabled && _barSpacing == other._barSpacing && _barLength == other._barLength && _barVerticalSplitter == other._barVerticalSplitter && _barFenceThickness == other._barFenceThickness && _barFenceOffset == other._barFenceOffset && _barThicknessesPx == other._barThicknessesPx && _barThicknessesMm == other._barThicknessesMm && _textEnabled == other._textEnabled && _textFont == other._textFont && _textScale.Equals(other._textScale) && _textThickness == other._textThickness && _text == other._text && _multipleBrightness == other._multipleBrightness && _multipleBrightnessExcludeFrom == other._multipleBrightnessExcludeFrom && _multipleBrightnessValues == other._multipleBrightnessValues && _multipleBrightnessGenExposureTime == other._multipleBrightnessGenExposureTime && _multipleBrightnessGenEmulatedAALevel == other._multipleBrightnessGenEmulatedAALevel && _multipleBrightnessGenExposureFractions == other._multipleBrightnessGenExposureFractions && _multipleLayerHeight == other._multipleLayerHeight && _multipleLayerHeightMaximum == other._multipleLayerHeightMaximum && _multipleLayerHeightStep == other._multipleLayerHeightStep && _multipleExposuresBaseLayersPrintMode == other._multipleExposuresBaseLayersPrintMode && _multipleExposuresBaseLayersCustomExposure == other._multipleExposuresBaseLayersCustomExposure && _differentSettingsForSamePositionedLayers == other._differentSettingsForSamePositionedLayers && _samePositionedLayersLiftHeightEnabled == other._samePositionedLayersLiftHeightEnabled && _samePositionedLayersLiftHeight == other._samePositionedLayersLiftHeight && _samePositionedLayersWaitTimeBeforeCureEnabled == other._samePositionedLayersWaitTimeBeforeCureEnabled && _samePositionedLayersWaitTimeBeforeCure == other._samePositionedLayersWaitTimeBeforeCure && _multipleExposures == other._multipleExposures && _exposureGenType == other._exposureGenType && _exposureGenIgnoreBaseExposure == other._exposureGenIgnoreBaseExposure && _exposureGenBottomStep == other._exposureGenBottomStep && _exposureGenNormalStep == other._exposureGenNormalStep && _exposureGenTests == other._exposureGenTests && _exposureGenManualLayerHeight == other._exposureGenManualLayerHeight && _exposureGenManualBottom == other._exposureGenManualBottom && _exposureGenManualNormal == other._exposureGenManualNormal && Equals(_exposureTable, other._exposureTable) && _bullsEyeEnabled == other._bullsEyeEnabled && _bullsEyeConfigurationPx == other._bullsEyeConfigurationPx && _bullsEyeConfigurationMm == other._bullsEyeConfigurationMm && _bullsEyeInvertQuadrants == other._bullsEyeInvertQuadrants && _counterTrianglesEnabled == other._counterTrianglesEnabled && _counterTrianglesTipOffset == other._counterTrianglesTipOffset && _counterTrianglesFence == other._counterTrianglesFence && _patternModel == other._patternModel && _bullsEyeFenceThickness == other._bullsEyeFenceThickness && _bullsEyeFenceOffset == other._bullsEyeFenceOffset && _patternModelGlueBottomLayers == other._patternModelGlueBottomLayers && _patternModelTextEnabled == other._patternModelTextEnabled;
+ }
- #endregion
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationCalibrateExposureFinder other && Equals(other);
+ }
- #region Methods
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_displayWidth);
+ hashCode.Add(_displayHeight);
+ hashCode.Add(_layerHeight);
+ hashCode.Add(_bottomLayers);
+ hashCode.Add(_bottomExposure);
+ hashCode.Add(_normalExposure);
+ hashCode.Add(_topBottomMargin);
+ hashCode.Add(_leftRightMargin);
+ hashCode.Add(_chamferLayers);
+ hashCode.Add(_erodeBottomIterations);
+ hashCode.Add(_partMargin);
+ hashCode.Add(_enableAntiAliasing);
+ hashCode.Add(_mirrorOutput);
+ hashCode.Add(_baseHeight);
+ hashCode.Add(_featuresHeight);
+ hashCode.Add(_featuresMargin);
+ hashCode.Add(_staircaseThicknessPx);
+ hashCode.Add(_staircaseThicknessMm);
+ hashCode.Add(_holesEnabled);
+ hashCode.Add((int)_holeShape);
+ hashCode.Add((int)_unitOfMeasure);
+ hashCode.Add(_holeDiametersPx);
+ hashCode.Add(_holeDiametersMm);
+ hashCode.Add(_barsEnabled);
+ hashCode.Add(_barSpacing);
+ hashCode.Add(_barLength);
+ hashCode.Add(_barVerticalSplitter);
+ hashCode.Add(_barFenceThickness);
+ hashCode.Add(_barFenceOffset);
+ hashCode.Add(_barThicknessesPx);
+ hashCode.Add(_barThicknessesMm);
+ hashCode.Add(_textEnabled);
+ hashCode.Add((int)_textFont);
+ hashCode.Add(_textScale);
+ hashCode.Add(_textThickness);
+ hashCode.Add(_text);
+ hashCode.Add(_multipleBrightness);
+ hashCode.Add((int)_multipleBrightnessExcludeFrom);
+ hashCode.Add(_multipleBrightnessValues);
+ hashCode.Add(_multipleBrightnessGenExposureTime);
+ hashCode.Add(_multipleBrightnessGenEmulatedAALevel);
+ hashCode.Add(_multipleBrightnessGenExposureFractions);
+ hashCode.Add(_multipleLayerHeight);
+ hashCode.Add(_multipleLayerHeightMaximum);
+ hashCode.Add(_multipleLayerHeightStep);
+ hashCode.Add((int)_multipleExposuresBaseLayersPrintMode);
+ hashCode.Add(_multipleExposuresBaseLayersCustomExposure);
+ hashCode.Add(_differentSettingsForSamePositionedLayers);
+ hashCode.Add(_samePositionedLayersLiftHeightEnabled);
+ hashCode.Add(_samePositionedLayersLiftHeight);
+ hashCode.Add(_samePositionedLayersWaitTimeBeforeCureEnabled);
+ hashCode.Add(_samePositionedLayersWaitTimeBeforeCure);
+ hashCode.Add(_multipleExposures);
+ hashCode.Add((int)_exposureGenType);
+ hashCode.Add(_exposureGenIgnoreBaseExposure);
+ hashCode.Add(_exposureGenBottomStep);
+ hashCode.Add(_exposureGenNormalStep);
+ hashCode.Add(_exposureGenTests);
+ hashCode.Add(_exposureGenManualLayerHeight);
+ hashCode.Add(_exposureGenManualBottom);
+ hashCode.Add(_exposureGenManualNormal);
+ hashCode.Add(_exposureTable);
+ hashCode.Add(_bullsEyeEnabled);
+ hashCode.Add(_bullsEyeConfigurationPx);
+ hashCode.Add(_bullsEyeConfigurationMm);
+ hashCode.Add(_bullsEyeInvertQuadrants);
+ hashCode.Add(_counterTrianglesEnabled);
+ hashCode.Add(_counterTrianglesTipOffset);
+ hashCode.Add(_counterTrianglesFence);
+ hashCode.Add(_patternModel);
+ hashCode.Add(_bullsEyeFenceThickness);
+ hashCode.Add(_bullsEyeFenceOffset);
+ hashCode.Add(_patternModelGlueBottomLayers);
+ hashCode.Add(_patternModelTextEnabled);
+ return hashCode.ToHashCode();
+ }
- public void SortExposureTable()
- {
- _exposureTable.Sort();
- }
+ #endregion
- public void SanitizeExposureTable()
- {
- _exposureTable.ReplaceCollection(GetSanitizedExposureTable());
- }
+ #region Methods
- public List<ExposureItem> GetSanitizedExposureTable()
- {
- var list = _exposureTable.ToList().Distinct().ToList();
- list.Sort();
- return list;
- }
+ public void SortExposureTable()
+ {
+ _exposureTable.Sort();
+ }
- public void GenerateBrightnessExposureFractions()
- {
- var fractions = _multipleBrightnessGenExposureFractions;
- var increment = 255f / _multipleBrightnessGenEmulatedAALevel;
+ public void SanitizeExposureTable()
+ {
+ _exposureTable.ReplaceCollection(GetSanitizedExposureTable());
+ }
- byte[] validAA = new byte[fractions];
+ public List<ExposureItem> GetSanitizedExposureTable()
+ {
+ var list = _exposureTable.ToList().Distinct().ToList();
+ list.Sort();
+ return list;
+ }
- for (byte frac = 0; frac < fractions; frac++)
- {
- validAA[frac] = (byte)(byte.MaxValue - increment * frac);
- }
+ public void GenerateBrightnessExposureFractions()
+ {
+ var fractions = _multipleBrightnessGenExposureFractions;
+ var increment = 255f / _multipleBrightnessGenEmulatedAALevel;
- MultipleBrightnessValues = string.Join(", ", validAA);
- }
+ byte[] validAA = new byte[fractions];
- public void GenerateExposureTable()
+ for (byte frac = 0; frac < fractions; frac++)
{
- var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
- List<ExposureItem> list = new();
- for (decimal layerHeight = _layerHeight;
- layerHeight <= endLayerHeight;
- layerHeight += _multipleLayerHeightStep)
- {
- if(!_exposureGenIgnoreBaseExposure)
- list.Add(new ExposureItem(layerHeight, _bottomExposure, _normalExposure));
- for (ushort testN = 1; testN <= _exposureGenTests; testN++)
- {
- decimal bottomExposureTime = 0;
- decimal exposureTime = 0;
+ validAA[frac] = (byte)(byte.MaxValue - increment * frac);
+ }
- switch (_exposureGenType)
- {
- case CalibrateExposureFinderExposureGenTypes.Linear:
- bottomExposureTime = _bottomExposure + _exposureGenBottomStep * testN;
- exposureTime = _normalExposure + _exposureGenNormalStep * testN;
- break;
- case CalibrateExposureFinderExposureGenTypes.Multiplier:
- bottomExposureTime = _bottomExposure + _bottomExposure * layerHeight * _exposureGenBottomStep * testN;
- exposureTime = _normalExposure + _normalExposure * layerHeight * _exposureGenNormalStep * testN;
- break;
- }
+ MultipleBrightnessValues = string.Join(", ", validAA);
+ }
+
+ public void GenerateExposureTable()
+ {
+ var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
+ List<ExposureItem> list = new();
+ for (decimal layerHeight = _layerHeight;
+ layerHeight <= endLayerHeight;
+ layerHeight += _multipleLayerHeightStep)
+ {
+ if(!_exposureGenIgnoreBaseExposure)
+ list.Add(new ExposureItem(layerHeight, _bottomExposure, _normalExposure));
+ for (ushort testN = 1; testN <= _exposureGenTests; testN++)
+ {
+ decimal bottomExposureTime = 0;
+ decimal exposureTime = 0;
- ExposureItem item = new(layerHeight, bottomExposureTime, exposureTime);
- if(list.Contains(item)) continue; // Already on list, skip
- list.Add(item);
+ switch (_exposureGenType)
+ {
+ case CalibrateExposureFinderExposureGenTypes.Linear:
+ bottomExposureTime = _bottomExposure + _exposureGenBottomStep * testN;
+ exposureTime = _normalExposure + _exposureGenNormalStep * testN;
+ break;
+ case CalibrateExposureFinderExposureGenTypes.Multiplier:
+ bottomExposureTime = _bottomExposure + _bottomExposure * layerHeight * _exposureGenBottomStep * testN;
+ exposureTime = _normalExposure + _normalExposure * layerHeight * _exposureGenNormalStep * testN;
+ break;
}
- }
- ExposureTable = new(list);
+ ExposureItem item = new(layerHeight, bottomExposureTime, exposureTime);
+ if(list.Contains(item)) continue; // Already on list, skip
+ list.Add(item);
+ }
}
- public Mat[] GetLayers(bool isPreview = false)
- {
- var holes = Holes;
- var bars = Bars;
- var bulleyes = BullsEyes;
- var textSize = TextSize;
-
- int featuresMarginX = (int)(Xppmm * _featuresMargin);
- int featuresMarginY = (int)(Yppmm * _featuresMargin);
- ushort startCaseThickness = StaircaseThickness;
+ ExposureTable = new(list);
+ }
- int holePanelWidth = holes.Length > 0 ? featuresMarginX * 2 + holes[^1] : 0;
- int holePanelHeight = GetHolesHeight(holes);
- int barsPanelHeight = GetBarsLength(bars);
- int bulleyesDiameter = GetBullsEyeMaxDiameter(bulleyes);
- int bulleyesPanelDiameter = GetBullsEyeMaxPanelDiameter(bulleyes);
- int bulleyesRadius = bulleyesDiameter / 2;
- int yLeftMaxSize = startCaseThickness + featuresMarginY + Math.Max(barsPanelHeight, textSize.Width) + bulleyesPanelDiameter;
- int yRightMaxSize = startCaseThickness + holePanelHeight + featuresMarginY * 2;
+ public Mat[] GetLayers(bool isPreview = false)
+ {
+ var holes = Holes;
+ var bars = Bars;
+ var bulleyes = BullsEyes;
+ var textSize = TextSize;
+
+ int featuresMarginX = (int)(Xppmm * _featuresMargin);
+ int featuresMarginY = (int)(Yppmm * _featuresMargin);
+ ushort startCaseThickness = StaircaseThickness;
+
+ int holePanelWidth = holes.Length > 0 ? featuresMarginX * 2 + holes[^1] : 0;
+ int holePanelHeight = GetHolesHeight(holes);
+ int barsPanelHeight = GetBarsLength(bars);
+ int bulleyesDiameter = GetBullsEyeMaxDiameter(bulleyes);
+ int bulleyesPanelDiameter = GetBullsEyeMaxPanelDiameter(bulleyes);
+ int bulleyesRadius = bulleyesDiameter / 2;
+ int yLeftMaxSize = startCaseThickness + featuresMarginY + Math.Max(barsPanelHeight, textSize.Width) + bulleyesPanelDiameter;
+ int yRightMaxSize = startCaseThickness + holePanelHeight + featuresMarginY * 2;
- int xSize = featuresMarginX;
- int ySize = TextMarkingSpacing + featuresMarginY;
+ int xSize = featuresMarginX;
+ int ySize = TextMarkingSpacing + featuresMarginY;
- if (barsPanelHeight > 0 || textSize.Width > 0)
- {
- yLeftMaxSize += featuresMarginY;
- }
+ if (barsPanelHeight > 0 || textSize.Width > 0)
+ {
+ yLeftMaxSize += featuresMarginY;
+ }
- int barLengthPx = (int) (_barLength * Xppmm);
- int barSpacingPx = (int) (_barSpacing * Yppmm);
- int barsPanelWidth = 0;
+ int barLengthPx = (int) (_barLength * Xppmm);
+ int barSpacingPx = (int) (_barSpacing * Yppmm);
+ int barsPanelWidth = 0;
- if (bars.Length > 0)
+ if (bars.Length > 0)
+ {
+ barsPanelWidth = barLengthPx * 2 + _barVerticalSplitter;
+ if (_barFenceThickness > 0)
{
- barsPanelWidth = barLengthPx * 2 + _barVerticalSplitter;
- if (_barFenceThickness > 0)
- {
- barsPanelWidth = Math.Max(barsPanelWidth, barsPanelWidth + _barFenceThickness * 2 + _barFenceOffset * 2);
- }
- xSize += barsPanelWidth + featuresMarginX;
+ barsPanelWidth = Math.Max(barsPanelWidth, barsPanelWidth + _barFenceThickness * 2 + _barFenceOffset * 2);
}
+ xSize += barsPanelWidth + featuresMarginX;
+ }
- if (!textSize.IsEmpty)
- {
- xSize += textSize.Height + featuresMarginX;
- }
+ if (!textSize.IsEmpty)
+ {
+ xSize += textSize.Height + featuresMarginX;
+ }
- int bullseyeYPos = yLeftMaxSize - bulleyesPanelDiameter / 2;
+ int bullseyeYPos = yLeftMaxSize - bulleyesPanelDiameter / 2;
- if (bulleyes.Length > 0)
- {
- xSize = Math.Max(xSize, bulleyesPanelDiameter + featuresMarginX * 2);
- yLeftMaxSize += featuresMarginY + 24;
- }
+ if (bulleyes.Length > 0)
+ {
+ xSize = Math.Max(xSize, bulleyesPanelDiameter + featuresMarginX * 2);
+ yLeftMaxSize += featuresMarginY + 24;
+ }
- int bullseyeXPos = xSize / 2;
+ int bullseyeXPos = xSize / 2;
- if (holePanelWidth > 0)
- {
- xSize -= featuresMarginX;
- }
+ if (holePanelWidth > 0)
+ {
+ xSize -= featuresMarginX;
+ }
- xSize += holePanelWidth;
- int negativeSideWidth = xSize;
- xSize += holePanelWidth;
+ xSize += holePanelWidth;
+ int negativeSideWidth = xSize;
+ xSize += holePanelWidth;
- int positiveSideWidth = xSize - holePanelWidth;
+ int positiveSideWidth = xSize - holePanelWidth;
- ySize += Math.Max(yLeftMaxSize, yRightMaxSize+10);
+ ySize += Math.Max(yLeftMaxSize, yRightMaxSize+10);
- Rectangle rect = new(new Point(0, 0), new Size(xSize, ySize));
- var layers = new Mat[2];
- layers[0] = EmguExtensions.InitMat(rect.Size);
+ Rectangle rect = new(new Point(0, 0), new Size(xSize, ySize));
+ var layers = new Mat[2];
+ layers[0] = EmguExtensions.InitMat(rect.Size);
- CvInvoke.Rectangle(layers[0], rect, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- layers[1] = layers[0].NewBlank();
- if (holes.Length > 0)
- {
- CvInvoke.Rectangle(layers[1],
- new Rectangle(rect.Size.Width - holePanelWidth, 0, rect.Size.Width, layers[0].Height),
- EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ CvInvoke.Rectangle(layers[0], rect, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ layers[1] = layers[0].NewBlank();
+ if (holes.Length > 0)
+ {
+ CvInvoke.Rectangle(layers[1],
+ new Rectangle(rect.Size.Width - holePanelWidth, 0, rect.Size.Width, layers[0].Height),
+ EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
- int xPos = 0;
- int yPos = 0;
+ int xPos = 0;
+ int yPos = 0;
- // Print staircase
- if (isPreview && startCaseThickness > 0)
- {
- CvInvoke.Rectangle(layers[1],
- new Rectangle(0, 0, layers[1].Size.Width-holePanelWidth, startCaseThickness),
- EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ // Print staircase
+ if (isPreview && startCaseThickness > 0)
+ {
+ CvInvoke.Rectangle(layers[1],
+ new Rectangle(0, 0, layers[1].Size.Width-holePanelWidth, startCaseThickness),
+ EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
- // Print holes
- for (var layerIndex = 0; layerIndex < layers.Length; layerIndex++)
+ // Print holes
+ for (var layerIndex = 0; layerIndex < layers.Length; layerIndex++)
+ {
+ var layer = layers[layerIndex];
+ yPos = featuresMarginY + startCaseThickness;
+ for (int i = 0; i < holes.Length; i++)
{
- var layer = layers[layerIndex];
- yPos = featuresMarginY + startCaseThickness;
- for (int i = 0; i < holes.Length; i++)
- {
- var diameter = holes[i];
- var radius = diameter / 2;
- xPos = layers[0].Width - holePanelWidth - featuresMarginX;
+ var diameter = holes[i];
+ var radius = diameter / 2;
+ xPos = layers[0].Width - holePanelWidth - featuresMarginX;
- CalibrateExposureFinderShapes effectiveShape = _holeShape == CalibrateExposureFinderShapes.Square || diameter < 6 ?
- CalibrateExposureFinderShapes.Square : CalibrateExposureFinderShapes.Circle;
+ CalibrateExposureFinderShapes effectiveShape = _holeShape == CalibrateExposureFinderShapes.Square || diameter < 6 ?
+ CalibrateExposureFinderShapes.Square : CalibrateExposureFinderShapes.Circle;
- switch (effectiveShape)
- {
- case CalibrateExposureFinderShapes.Square:
- xPos -= diameter;
- break;
- case CalibrateExposureFinderShapes.Circle:
- xPos -= radius;
- yPos += radius;
- break;
- }
-
-
- // Left side
- if (layerIndex == 1)
- {
- if (diameter == 1)
- {
- layer.SetByte(xPos, yPos, 255);
- }
- else
- {
- switch (effectiveShape)
- {
- case CalibrateExposureFinderShapes.Square:
- CvInvoke.Rectangle(layers[layerIndex],
- new Rectangle(new Point(xPos, yPos), new Size(diameter-1, diameter-1)),
- EmguExtensions.WhiteColor, -1,
- _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- break;
- case CalibrateExposureFinderShapes.Circle:
- CvInvoke.Circle(layers[layerIndex],
- new Point(xPos, yPos),
- radius, EmguExtensions.WhiteColor, -1,
- _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- break;
- }
-
- }
- }
+ switch (effectiveShape)
+ {
+ case CalibrateExposureFinderShapes.Square:
+ xPos -= diameter;
+ break;
+ case CalibrateExposureFinderShapes.Circle:
+ xPos -= radius;
+ yPos += radius;
+ break;
+ }
- //holeXPos = layers[0].Width - holeXPos;
- switch (effectiveShape)
- {
- case CalibrateExposureFinderShapes.Square:
- xPos = layers[0].Width - rect.X - featuresMarginX - holes[^1];
- break;
- case CalibrateExposureFinderShapes.Circle:
- xPos = layers[0].Width - rect.X - featuresMarginX - holes[^1] + radius;
- break;
- }
- // Right side
+ // Left side
+ if (layerIndex == 1)
+ {
if (diameter == 1)
{
- layer.SetByte(xPos, yPos, 0);
+ layer.SetByte(xPos, yPos, 255);
}
else
{
@@ -1537,752 +1499,781 @@ namespace UVtools.Core.Operations
case CalibrateExposureFinderShapes.Square:
CvInvoke.Rectangle(layers[layerIndex],
new Rectangle(new Point(xPos, yPos), new Size(diameter-1, diameter-1)),
- EmguExtensions.BlackColor, -1,
+ EmguExtensions.WhiteColor, -1,
_enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
case CalibrateExposureFinderShapes.Circle:
CvInvoke.Circle(layers[layerIndex],
new Point(xPos, yPos),
- radius, EmguExtensions.BlackColor, -1,
+ radius, EmguExtensions.WhiteColor, -1,
_enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
}
- }
+ }
+ }
- yPos += featuresMarginY;
+ //holeXPos = layers[0].Width - holeXPos;
+ switch (effectiveShape)
+ {
+ case CalibrateExposureFinderShapes.Square:
+ xPos = layers[0].Width - rect.X - featuresMarginX - holes[^1];
+ break;
+ case CalibrateExposureFinderShapes.Circle:
+ xPos = layers[0].Width - rect.X - featuresMarginX - holes[^1] + radius;
+ break;
+ }
+ // Right side
+ if (diameter == 1)
+ {
+ layer.SetByte(xPos, yPos, 0);
+ }
+ else
+ {
switch (effectiveShape)
{
case CalibrateExposureFinderShapes.Square:
- yPos += diameter;
+ CvInvoke.Rectangle(layers[layerIndex],
+ new Rectangle(new Point(xPos, yPos), new Size(diameter-1, diameter-1)),
+ EmguExtensions.BlackColor, -1,
+ _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
case CalibrateExposureFinderShapes.Circle:
- yPos += radius;
+ CvInvoke.Circle(layers[layerIndex],
+ new Point(xPos, yPos),
+ radius, EmguExtensions.BlackColor, -1,
+ _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
break;
}
}
- }
- xPos = featuresMarginX;
-
- // Print Zebra bars
- if (bars.Length > 0)
- {
- int yStartPos = startCaseThickness + featuresMarginY;
- int xStartPos = xPos;
- yPos = yStartPos + _barFenceThickness / 2 + _barFenceOffset;
- xPos += _barFenceThickness / 2 + _barFenceOffset;
- for (int i = 0; i < bars.Length; i++)
+
+ yPos += featuresMarginY;
+
+ switch (effectiveShape)
{
- // Print positive bottom
- CvInvoke.Rectangle(layers[1], new Rectangle(xPos, yPos, barLengthPx - 1, barSpacingPx - 1),
- EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- // Print positive top
- yPos += barSpacingPx;
- CvInvoke.Rectangle(layers[1], new Rectangle(xPos + barLengthPx + _barVerticalSplitter, yPos, barLengthPx - 1, bars[i] - 1),
- EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- yPos += bars[i];
+ case CalibrateExposureFinderShapes.Square:
+ yPos += diameter;
+ break;
+ case CalibrateExposureFinderShapes.Circle:
+ yPos += radius;
+ break;
}
+ }
+ }
- // Left over
+ xPos = featuresMarginX;
+
+ // Print Zebra bars
+ if (bars.Length > 0)
+ {
+ int yStartPos = startCaseThickness + featuresMarginY;
+ int xStartPos = xPos;
+ yPos = yStartPos + _barFenceThickness / 2 + _barFenceOffset;
+ xPos += _barFenceThickness / 2 + _barFenceOffset;
+ for (int i = 0; i < bars.Length; i++)
+ {
+ // Print positive bottom
CvInvoke.Rectangle(layers[1], new Rectangle(xPos, yPos, barLengthPx - 1, barSpacingPx - 1),
EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
-
+ // Print positive top
yPos += barSpacingPx;
+ CvInvoke.Rectangle(layers[1], new Rectangle(xPos + barLengthPx + _barVerticalSplitter, yPos, barLengthPx - 1, bars[i] - 1),
+ EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ yPos += bars[i];
+ }
- if (_barFenceThickness > 0)
- {
- CvInvoke.Rectangle(layers[1], new Rectangle(
- xStartPos - 1,
- yStartPos - 1,
- barsPanelWidth - _barFenceThickness + 1,
- yPos - yStartPos + _barFenceThickness / 2 + _barFenceOffset + 1),
- EmguExtensions.WhiteColor, _barFenceThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ // Left over
+ CvInvoke.Rectangle(layers[1], new Rectangle(xPos, yPos, barLengthPx - 1, barSpacingPx - 1),
+ EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- yPos += _barFenceThickness * 2 + _barFenceOffset * 2;
- }
+ yPos += barSpacingPx;
- xPos += featuresMarginX;
+ if (_barFenceThickness > 0)
+ {
+ CvInvoke.Rectangle(layers[1], new Rectangle(
+ xStartPos - 1,
+ yStartPos - 1,
+ barsPanelWidth - _barFenceThickness + 1,
+ yPos - yStartPos + _barFenceThickness / 2 + _barFenceOffset + 1),
+ EmguExtensions.WhiteColor, _barFenceThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+
+ yPos += _barFenceThickness * 2 + _barFenceOffset * 2;
}
- if (!textSize.IsEmpty)
+ xPos += featuresMarginX;
+ }
+
+ if (!textSize.IsEmpty)
+ {
+ CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90CounterClockwise);
+ CvInvoke.PutText(layers[1], _text, new Point(startCaseThickness + featuresMarginY, layers[1].Height - barsPanelWidth - featuresMarginX * (barsPanelWidth > 0 ? 2 : 1)), _textFont, _textScale, EmguExtensions.WhiteColor, _textThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90Clockwise);
+ }
+
+ // Print bullseye
+ if (bulleyes.Length > 0)
+ {
+ yPos = bullseyeYPos;
+ foreach (var circle in bulleyes)
{
- CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90CounterClockwise);
- CvInvoke.PutText(layers[1], _text, new Point(startCaseThickness + featuresMarginY, layers[1].Height - barsPanelWidth - featuresMarginX * (barsPanelWidth > 0 ? 2 : 1)), _textFont, _textScale, EmguExtensions.WhiteColor, _textThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.Rotate(layers[1], layers[1], RotateFlags.Rotate90Clockwise);
+ CvInvoke.Circle(layers[1], new Point(bullseyeXPos, yPos), circle.Radius, EmguExtensions.WhiteColor, circle.Thickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
}
- // Print bullseye
- if (bulleyes.Length > 0)
+ if (_bullsEyeInvertQuadrants)
{
- yPos = bullseyeYPos;
- foreach (var circle in bulleyes)
- {
- CvInvoke.Circle(layers[1], new Point(bullseyeXPos, yPos), circle.Radius, EmguExtensions.WhiteColor, circle.Thickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
-
- if (_bullsEyeInvertQuadrants)
- {
- var matRoi1 = new Mat(layers[1], new Rectangle(bullseyeXPos, yPos - bulleyesRadius - 5, bulleyesRadius + 6, bulleyesRadius + 5));
- var matRoi2 = new Mat(layers[1], new Rectangle(bullseyeXPos - bulleyesRadius - 5, yPos, bulleyesRadius + 5, bulleyesRadius + 6));
- //using var mask = matRoi1.CloneBlank();
+ var matRoi1 = new Mat(layers[1], new Rectangle(bullseyeXPos, yPos - bulleyesRadius - 5, bulleyesRadius + 6, bulleyesRadius + 5));
+ var matRoi2 = new Mat(layers[1], new Rectangle(bullseyeXPos - bulleyesRadius - 5, yPos, bulleyesRadius + 5, bulleyesRadius + 6));
+ //using var mask = matRoi1.CloneBlank();
- //CvInvoke.Circle(mask, new Point(mask.Width / 2, mask.Height / 2), bulleyesRadius, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- //CvInvoke.Circle(mask, new Point(mask.Width / 2, mask.Height / 2), BullsEyes[^1].Radius, EmguExtensions.WhiteByte, BullsEyes[^1].Thickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ //CvInvoke.Circle(mask, new Point(mask.Width / 2, mask.Height / 2), bulleyesRadius, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ //CvInvoke.Circle(mask, new Point(mask.Width / 2, mask.Height / 2), BullsEyes[^1].Radius, EmguExtensions.WhiteByte, BullsEyes[^1].Thickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.BitwiseNot(matRoi1, matRoi1);
- CvInvoke.BitwiseNot(matRoi2, matRoi2);
- }
+ CvInvoke.BitwiseNot(matRoi1, matRoi1);
+ CvInvoke.BitwiseNot(matRoi2, matRoi2);
+ }
- if (_bullsEyeFenceThickness > 0)
- {
- CvInvoke.Rectangle(layers[1],
- new Rectangle(
- new Point(
- bullseyeXPos - bulleyesRadius - 5 - _bullsEyeFenceOffset - _bullsEyeFenceThickness / 2,
- yPos - bulleyesRadius - 5 - _bullsEyeFenceOffset - _bullsEyeFenceThickness / 2),
- new Size(
- bulleyesDiameter + 10 + _bullsEyeFenceOffset*2 + _bullsEyeFenceThickness,
- bulleyesDiameter + 10 + _bullsEyeFenceOffset*2 + _bullsEyeFenceThickness)),
- EmguExtensions.WhiteColor,
- _bullsEyeFenceThickness,
- _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ if (_bullsEyeFenceThickness > 0)
+ {
+ CvInvoke.Rectangle(layers[1],
+ new Rectangle(
+ new Point(
+ bullseyeXPos - bulleyesRadius - 5 - _bullsEyeFenceOffset - _bullsEyeFenceThickness / 2,
+ yPos - bulleyesRadius - 5 - _bullsEyeFenceOffset - _bullsEyeFenceThickness / 2),
+ new Size(
+ bulleyesDiameter + 10 + _bullsEyeFenceOffset*2 + _bullsEyeFenceThickness,
+ bulleyesDiameter + 10 + _bullsEyeFenceOffset*2 + _bullsEyeFenceThickness)),
+ EmguExtensions.WhiteColor,
+ _bullsEyeFenceThickness,
+ _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
- yPos += bulleyesRadius;
- }
+ yPos += bulleyesRadius;
+ }
- if (isPreview)
+ if (isPreview)
+ {
+ var textHeightStart = layers[1].Height - featuresMarginY - TextMarkingSpacing;
+ CvInvoke.PutText(layers[1], $"{Microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ if (holes.Length > 0)
{
- var textHeightStart = layers[1].Height - featuresMarginY - TextMarkingSpacing;
- CvInvoke.PutText(layers[1], $"{Microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- if (holes.Length > 0)
- {
- CvInvoke.PutText(layers[1], $"{Microns}u", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ CvInvoke.PutText(layers[1], $"{Microns}u", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(layers[1], $"{_bottomExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(layers[1], $"{_normalExposure}s", new Point(layers[1].Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
}
+ }
- if (negativeSideWidth >= 200 && _counterTrianglesEnabled)
- {
- xPos = 120;
- int triangleHeight = TextMarkingSpacing + 19;
- int triangleWidth = (negativeSideWidth - xPos - featuresMarginX) / 2;
- int triangleWidthQuarter = triangleWidth / 4;
+ if (negativeSideWidth >= 200 && _counterTrianglesEnabled)
+ {
+ xPos = 120;
+ int triangleHeight = TextMarkingSpacing + 19;
+ int triangleWidth = (negativeSideWidth - xPos - featuresMarginX) / 2;
+ int triangleWidthQuarter = triangleWidth / 4;
- if (triangleWidth > 5)
- {
- yPos = layers[1].Height - featuresMarginY - triangleHeight + 1;
- int yHalfPos = yPos + triangleHeight / 2;
- int yPosEnd = layers[1].Height - featuresMarginY + 1;
+ if (triangleWidth > 5)
+ {
+ yPos = layers[1].Height - featuresMarginY - triangleHeight + 1;
+ int yHalfPos = yPos + triangleHeight / 2;
+ int yPosEnd = layers[1].Height - featuresMarginY + 1;
- var triangles = new Point[4][];
+ var triangles = new Point[4][];
- triangles[0] = new Point[] // Left
- {
- new(xPos, yPos), // Top Left
- new(xPos + triangleWidth, yHalfPos), // Middle
- new(xPos, yPosEnd), // Bottom Left
- };
- triangles[1] = new Point[] // Right
- {
- new(xPos + triangleWidth * 2, yPos), // Top Right
- new(xPos + triangleWidth, yHalfPos), // Middle
- new(xPos + triangleWidth * 2, yPosEnd), // Bottom Right
- };
- triangles[2] = new Point[] // Top
- {
- new(xPos + triangleWidth - triangleWidthQuarter, yPos), // Top Left
- new(xPos + triangleWidth + triangleWidthQuarter, yPos), // Top Right
- new(xPos + triangleWidth, yHalfPos - _counterTrianglesTipOffset), // Middle
- };
- triangles[3] = new Point[] // Bottom
- {
- new(xPos + triangleWidth - triangleWidthQuarter, yPosEnd), // Bottom Left
- new(xPos + triangleWidth + triangleWidthQuarter, yPosEnd), // Bottom Right
- new(xPos + triangleWidth, yHalfPos + _counterTrianglesTipOffset), // Middle
- };
+ triangles[0] = new Point[] // Left
+ {
+ new(xPos, yPos), // Top Left
+ new(xPos + triangleWidth, yHalfPos), // Middle
+ new(xPos, yPosEnd), // Bottom Left
+ };
+ triangles[1] = new Point[] // Right
+ {
+ new(xPos + triangleWidth * 2, yPos), // Top Right
+ new(xPos + triangleWidth, yHalfPos), // Middle
+ new(xPos + triangleWidth * 2, yPosEnd), // Bottom Right
+ };
+ triangles[2] = new Point[] // Top
+ {
+ new(xPos + triangleWidth - triangleWidthQuarter, yPos), // Top Left
+ new(xPos + triangleWidth + triangleWidthQuarter, yPos), // Top Right
+ new(xPos + triangleWidth, yHalfPos - _counterTrianglesTipOffset), // Middle
+ };
+ triangles[3] = new Point[] // Bottom
+ {
+ new(xPos + triangleWidth - triangleWidthQuarter, yPosEnd), // Bottom Left
+ new(xPos + triangleWidth + triangleWidthQuarter, yPosEnd), // Bottom Right
+ new(xPos + triangleWidth, yHalfPos + _counterTrianglesTipOffset), // Middle
+ };
- foreach (var triangle in triangles)
- {
- using var vec = new VectorOfPoint(triangle);
- CvInvoke.FillPoly(layers[1], vec, EmguExtensions.WhiteColor,
- _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ foreach (var triangle in triangles)
+ {
+ using var vec = new VectorOfPoint(triangle);
+ CvInvoke.FillPoly(layers[1], vec, EmguExtensions.WhiteColor,
+ _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
- /*byte size = 60;
- var matRoi = new Mat(layers[1], new Rectangle(
- new Point(xPos + triangleWidth - size / 2, yHalfPos - size / 2),
- new Size(size, size)));
+ /*byte size = 60;
+ var matRoi = new Mat(layers[1], new Rectangle(
+ new Point(xPos + triangleWidth - size / 2, yHalfPos - size / 2),
+ new Size(size, size)));
- CvInvoke.BitwiseNot(matRoi, matRoi);*/
+ CvInvoke.BitwiseNot(matRoi, matRoi);*/
- if (_counterTrianglesFence)
- {
- byte outlineThickness = 8;
- //byte outlineThicknessHalf = (byte)(outlineThickness / 2);
-
- CvInvoke.Rectangle(layers[1], new Rectangle(
- new Point(triangles[0][0].X - 0, triangles[0][0].Y - 0),
- new Size(triangleWidth * 2 + 0, triangleHeight + 0)
- ), EmguExtensions.WhiteColor, outlineThickness,
- _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ if (_counterTrianglesFence)
+ {
+ byte outlineThickness = 8;
+ //byte outlineThicknessHalf = (byte)(outlineThickness / 2);
+
+ CvInvoke.Rectangle(layers[1], new Rectangle(
+ new Point(triangles[0][0].X - 0, triangles[0][0].Y - 0),
+ new Size(triangleWidth * 2 + 0, triangleHeight + 0)
+ ), EmguExtensions.WhiteColor, outlineThickness,
+ _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
}
}
- // Print a hardcoded spiral if have space
- /*if (positiveSideWidth >= 250000)
+ }
+ // Print a hardcoded spiral if have space
+ /*if (positiveSideWidth >= 250000)
+ {
+ var mat = layers[0].CloneBlank();
+ var matMask = layers[0].CloneBlank();
+ xPos = (int) ((layers[0].Width - holePanelWidth) / 1.8);
+ yPos = layers[0].Height - featuresMarginY - TextMarkingSpacing / 2;
+ byte circleThickness = 5;
+ byte radiusStep = 13;
+ int count = -1;
+ int maxRadius = 0;
+ //bool white = true;
+
+ for (int radius = radiusStep;radius <= 100; radius += (radiusStep + count))
{
- var mat = layers[0].CloneBlank();
- var matMask = layers[0].CloneBlank();
- xPos = (int) ((layers[0].Width - holePanelWidth) / 1.8);
- yPos = layers[0].Height - featuresMarginY - TextMarkingSpacing / 2;
- byte circleThickness = 5;
- byte radiusStep = 13;
- int count = -1;
- int maxRadius = 0;
- //bool white = true;
-
- for (int radius = radiusStep;radius <= 100; radius += (radiusStep + count))
- {
- count++;
- CvInvoke.Circle(mat, new Point(xPos, yPos), radius, EmguExtensions.WhiteByte, circleThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- maxRadius = radius;
- }
+ count++;
+ CvInvoke.Circle(mat, new Point(xPos, yPos), radius, EmguExtensions.WhiteByte, circleThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ maxRadius = radius;
+ }
- CvInvoke.Circle(mat, new Point(xPos, yPos), 5, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.Circle(matMask, new Point(xPos, yPos), maxRadius+2, EmguExtensions.WhiteByte, -1);
+ CvInvoke.Circle(mat, new Point(xPos, yPos), 5, EmguExtensions.WhiteByte, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.Circle(matMask, new Point(xPos, yPos), maxRadius+2, EmguExtensions.WhiteByte, -1);
- var matRoi1 = new Mat(mat, new Rectangle(xPos, yPos - maxRadius-1, maxRadius+2, maxRadius+1));
- var matRoi2 = new Mat(mat, new Rectangle(xPos-maxRadius-1, yPos, maxRadius+1, Math.Min(mat.Height- yPos, maxRadius)));
+ var matRoi1 = new Mat(mat, new Rectangle(xPos, yPos - maxRadius-1, maxRadius+2, maxRadius+1));
+ var matRoi2 = new Mat(mat, new Rectangle(xPos-maxRadius-1, yPos, maxRadius+1, Math.Min(mat.Height- yPos, maxRadius)));
- CvInvoke.BitwiseNot(matRoi1, matRoi1);
- CvInvoke.BitwiseNot(matRoi2, matRoi2);
+ CvInvoke.BitwiseNot(matRoi1, matRoi1);
+ CvInvoke.BitwiseNot(matRoi2, matRoi2);
- CvInvoke.BitwiseAnd(layers[0], mat, layers[1], matMask);
+ CvInvoke.BitwiseAnd(layers[0], mat, layers[1], matMask);
- Point anchor = new Point(-1, -1);
- //CvInvoke.MorphologyEx(layers[1], layers[1], MorphOp.Open, CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3,3), anchor), anchor, 1, BorderType.Reflect101, default);
-
- mat.Dispose();
- matMask.Dispose();
- }*/
+ Point anchor = new Point(-1, -1);
+ //CvInvoke.MorphologyEx(layers[1], layers[1], MorphOp.Open, CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3,3), anchor), anchor, 1, BorderType.Reflect101, default);
+
+ mat.Dispose();
+ matMask.Dispose();
+ }*/
- return layers;
- }
+ return layers;
+ }
- public Mat GetThumbnail()
- {
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "Exposure Time Cal.", new Point(xSpacing, ySpacing * 2 - 10), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
-
-
- string text = string.Empty;
-
- if (_multipleLayerHeight)
- {
- text += $"{Microns}um-{(ushort)(_multipleLayerHeightMaximum *1000)}um/{(ushort)(_multipleLayerHeightStep *1000)}um\n";
- }
- else
- {
- text += $"Layer height: {Microns}um\n";
- }
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "Exposure Time Cal.", new Point(xSpacing, ySpacing * 2 - 10), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- if (_multipleExposures)
- {
- text += $"{_exposureTable[0].Exposure}s-{_exposureTable[^1].Exposure}s/{_exposureGenNormalStep}s";
- if (!_patternModel)
- {
- text += $"\nObjects: {_exposureTable.Count}";
- }
- }
- else
- {
- text += $"{_bottomExposure}s/{_normalExposure}s";
- }
- if (_patternModel)
+ string text = string.Empty;
+
+ if (_multipleLayerHeight)
+ {
+ text += $"{Microns}um-{(ushort)(_multipleLayerHeightMaximum *1000)}um/{(ushort)(_multipleLayerHeightStep *1000)}um\n";
+ }
+ else
+ {
+ text += $"Layer height: {Microns}um\n";
+ }
+
+ if (_multipleExposures)
+ {
+ text += $"{_exposureTable[0].Exposure}s-{_exposureTable[^1].Exposure}s/{_exposureGenNormalStep}s";
+ if (!_patternModel)
{
- text += "\nPatterned Model";
+ text += $"\nObjects: {_exposureTable.Count}";
}
+ }
+ else
+ {
+ text += $"{_bottomExposure}s/{_normalExposure}s";
+ }
+ if (_patternModel)
+ {
+ text += "\nPatterned Model";
+ }
- thumbnail.PutTextExtended(text, new Point(xSpacing, ySpacing * 3 - 20), fontFace, 0.8, EmguExtensions.WhiteColor, 2, 10);
+ thumbnail.PutTextExtended(text, new Point(xSpacing, ySpacing * 3 - 20), fontFace, 0.8, EmguExtensions.WhiteColor, 2, 10);
- /*CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- if (_patternModel)
- {
- CvInvoke.PutText(thumbnail, $"Patterned Model", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- }
- else
- {
- CvInvoke.PutText(thumbnail, $"Features: {(_staircaseThickness > 0 ? 1 : 0) + Holes.Length + Bars.Length + BullsEyes.Length + (_counterTrianglesEnabled ? 1 : 0)}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- }*/
-
- return thumbnail;
+ /*CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ if (_patternModel)
+ {
+ CvInvoke.PutText(thumbnail, $"Patterned Model", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
}
-
- protected override bool ExecuteInternally(OperationProgress progress)
+ else
{
- int sideMarginPx = (int)(_leftRightMargin * Xppmm);
- int topBottomMarginPx = (int)(_topBottomMargin * Yppmm);
- int partMarginXPx = (int)(_partMargin * Xppmm);
- int partMarginYPx = (int)(_partMargin * Yppmm);
+ CvInvoke.PutText(thumbnail, $"Features: {(_staircaseThickness > 0 ? 1 : 0) + Holes.Length + Bars.Length + BullsEyes.Length + (_counterTrianglesEnabled ? 1 : 0)}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ }*/
+
- var anchor = new Point(-1, -1);
- var kernel = EmguExtensions.Kernel3x3Rectangle;
+ return thumbnail;
+ }
- if (_patternModel)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ int sideMarginPx = (int)(_leftRightMargin * Xppmm);
+ int topBottomMarginPx = (int)(_topBottomMargin * Yppmm);
+ int partMarginXPx = (int)(_partMargin * Xppmm);
+ int partMarginYPx = (int)(_partMargin * Yppmm);
+
+ var anchor = new Point(-1, -1);
+ var kernel = EmguExtensions.Kernel3x3Rectangle;
+
+ if (_patternModel)
+ {
+ ConcurrentBag<Layer> parallelLayers = new();
+ Dictionary<ExposureItem, Point> table = new();
+ var boundingRectangle = SlicerFile.BoundingRectangle;
+ int xHalf = boundingRectangle.Width / 2;
+ int yHalf = boundingRectangle.Height / 2;
+
+ var brightnesses = MultipleBrightnessValuesArray;
+ var multipleExposures = _exposureTable.Where(item => item.IsValid && item.LayerHeight == (decimal) SlicerFile.LayerHeight).ToArray();
+ if (brightnesses.Length == 0 || !_multipleBrightness) brightnesses = new[] { byte.MaxValue };
+ if (multipleExposures.Length == 0 || !_multipleExposures) multipleExposures = new[] { new ExposureItem((decimal)SlicerFile.LayerHeight, _bottomExposure, _normalExposure) };
+
+ int currentX = sideMarginPx;
+ int currentY = topBottomMarginPx;
+ Rectangle glueBottomLayerRectangle = new(new Point(currentX, currentY), Size.Empty);
+ foreach (var multipleExposure in multipleExposures)
{
- ConcurrentBag<Layer> parallelLayers = new();
- Dictionary<ExposureItem, Point> table = new();
- var boundingRectangle = SlicerFile.BoundingRectangle;
- int xHalf = boundingRectangle.Width / 2;
- int yHalf = boundingRectangle.Height / 2;
-
- var brightnesses = MultipleBrightnessValuesArray;
- var multipleExposures = _exposureTable.Where(item => item.IsValid && item.LayerHeight == (decimal) SlicerFile.LayerHeight).ToArray();
- if (brightnesses.Length == 0 || !_multipleBrightness) brightnesses = new[] { byte.MaxValue };
- if (multipleExposures.Length == 0 || !_multipleExposures) multipleExposures = new[] { new ExposureItem((decimal)SlicerFile.LayerHeight, _bottomExposure, _normalExposure) };
-
- int currentX = sideMarginPx;
- int currentY = topBottomMarginPx;
- Rectangle glueBottomLayerRectangle = new(new Point(currentX, currentY), Size.Empty);
- foreach (var multipleExposure in multipleExposures)
+ foreach (var brightness in brightnesses)
{
- foreach (var brightness in brightnesses)
+ if (currentX + boundingRectangle.Width + sideMarginPx >= SlicerFile.ResolutionX)
{
- if (currentX + boundingRectangle.Width + sideMarginPx >= SlicerFile.ResolutionX)
- {
- currentX = sideMarginPx;
- currentY += boundingRectangle.Height + partMarginYPx;
- }
+ currentX = sideMarginPx;
+ currentY += boundingRectangle.Height + partMarginYPx;
+ }
- if (currentY + boundingRectangle.Height + topBottomMarginPx >= SlicerFile.ResolutionY) break;
+ if (currentY + boundingRectangle.Height + topBottomMarginPx >= SlicerFile.ResolutionY) break;
- var item = multipleExposure.Clone();
- item.Brightness = brightness;
- table.Add(item, new Point(currentX, currentY));
+ var item = multipleExposure.Clone();
+ item.Brightness = brightness;
+ table.Add(item, new Point(currentX, currentY));
- glueBottomLayerRectangle.Size = new Size(currentX + boundingRectangle.Width, currentY + boundingRectangle.Height);
+ glueBottomLayerRectangle.Size = new Size(currentX + boundingRectangle.Width, currentY + boundingRectangle.Height);
- currentX += boundingRectangle.Width + partMarginXPx;
- }
+ currentX += boundingRectangle.Width + partMarginXPx;
}
+ }
- if (table.Count <= 1) return false;
- ushort microns = SlicerFile.LayerHeightUm;
+ if (table.Count <= 1) return false;
+ ushort microns = SlicerFile.LayerHeightUm;
- var tableGrouped = table.GroupBy(pair => new {pair.Key.LayerHeight, pair.Key.BottomExposure, pair.Key.Exposure}).Distinct();
- SlicerFile.BottomLayerCount = _bottomLayers;
- progress.ItemCount = (uint) (SlicerFile.LayerCount * table.Count);
- Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ var tableGrouped = table.GroupBy(pair => new {pair.Key.LayerHeight, pair.Key.BottomExposure, pair.Key.Exposure}).Distinct();
+ SlicerFile.BottomLayerCount = _bottomLayers;
+ progress.ItemCount = (uint) (SlicerFile.LayerCount * table.Count);
+ Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = SlicerFile[layerIndex];
+ using var mat = layer.LayerMat;
+ var matRoi = new Mat(mat, boundingRectangle);
+ int layerCountOnHeight = (int)(layer.PositionZ / SlicerFile.LayerHeight);
+ foreach (var group in tableGrouped)
{
- if (progress.Token.IsCancellationRequested) return;
- var layer = SlicerFile[layerIndex];
- using var mat = layer.LayerMat;
- var matRoi = new Mat(mat, boundingRectangle);
- int layerCountOnHeight = (int)(layer.PositionZ / SlicerFile.LayerHeight);
- foreach (var group in tableGrouped)
+ var newLayer = layer.Clone();
+ newLayer.ExposureTime = (float)(newLayer.IsBottomLayer ? group.Key.BottomExposure : group.Key.Exposure);
+ using var newMat = mat.NewBlank();
+ foreach (var brightness in brightnesses)
{
- var newLayer = layer.Clone();
- newLayer.ExposureTime = (float)(newLayer.IsBottomLayer ? group.Key.BottomExposure : group.Key.Exposure);
- using var newMat = mat.NewBlank();
- foreach (var brightness in brightnesses)
- {
- ExposureItem item = new(group.Key.LayerHeight, group.Key.BottomExposure, group.Key.Exposure, brightness);
- if(!table.TryGetValue(item, out var point)) continue;
+ ExposureItem item = new(group.Key.LayerHeight, group.Key.BottomExposure, group.Key.Exposure, brightness);
+ if(!table.TryGetValue(item, out var point)) continue;
- var newMatRoi = new Mat(newMat, new Rectangle(point, matRoi.Size));
- matRoi.CopyTo(newMatRoi);
+ var newMatRoi = new Mat(newMat, new Rectangle(point, matRoi.Size));
+ matRoi.CopyTo(newMatRoi);
- if (layer.IsBottomLayer)
+ if (layer.IsBottomLayer)
+ {
+ if (_patternModelGlueBottomLayers)
{
- if (_patternModelGlueBottomLayers)
- {
- newMatRoi.SetTo(EmguExtensions.WhiteColor);
- }
+ newMatRoi.SetTo(EmguExtensions.WhiteColor);
}
+ }
- if (layerCountOnHeight < _chamferLayers)
+ if (layerCountOnHeight < _chamferLayers)
+ {
+ CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default);
+ }
+
+ if (layer.IsBottomLayer)
+ {
+ if (_erodeBottomIterations > 0)
{
- CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default);
+ CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default);
}
- if (layer.IsBottomLayer)
+ if(_patternModelTextEnabled)
{
- if (_erodeBottomIterations > 0)
+ if (_multipleBrightness)
{
- CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default);
- }
-
- if(_patternModelTextEnabled)
- {
- if (_multipleBrightness)
- {
- CvInvoke.PutText(newMatRoi, brightness.ToString(), new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 4), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
- CvInvoke.PutText(newMatRoi, $"{microns}u", new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(newMatRoi, $"{group.Key.BottomExposure}s", new(xHalf - 60, yHalf + 20), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(newMatRoi, $"{group.Key.Exposure}s", new(xHalf - 60, yHalf + 20 + TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(newMatRoi, brightness.ToString(), new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 4), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
}
+ CvInvoke.PutText(newMatRoi, $"{microns}u", new(xHalf - 60, yHalf + 20 - TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(newMatRoi, $"{group.Key.BottomExposure}s", new(xHalf - 60, yHalf + 20), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(newMatRoi, $"{group.Key.Exposure}s", new(xHalf - 60, yHalf + 20 + TextMarkingLineBreak * 2), TextMarkingFontFace, 2, EmguExtensions.BlackColor, 3, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
}
+ }
- if (brightness < 255)
+ if (brightness < 255)
+ {
+ if (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !layer.IsBottomLayer ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase && !layer.IsBottomLayer)
{
- if (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None ||
- _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !layer.IsBottomLayer ||
- _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase && !layer.IsBottomLayer)
- {
- using var pattern = matRoi.New();
- pattern.SetTo(new MCvScalar(byte.MaxValue - brightness));
- CvInvoke.Subtract(newMatRoi, pattern, newMatRoi);
- }
+ using var pattern = matRoi.New();
+ pattern.SetTo(new MCvScalar(byte.MaxValue - brightness));
+ CvInvoke.Subtract(newMatRoi, pattern, newMatRoi);
}
}
-
-
- newLayer.LayerMat = newMat;
- parallelLayers.Add(newLayer);
- progress.LockAndIncrement();
- }
- });
-
- progress.Token.ThrowIfCancellationRequested();
- if (parallelLayers.IsEmpty) return false;
- var layers = parallelLayers.OrderBy(layer => layer.PositionZ).ThenBy(layer => layer.ExposureTime).ToList();
-
- progress.ResetNameAndProcessed("Optimized layers");
-
- Layer currentLayer = layers[0];
- for (var layerIndex = 1; layerIndex < layers.Count; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- progress++;
- var layer = layers[layerIndex];
- if (currentLayer.PositionZ != layer.PositionZ ||
- currentLayer.ExposureTime != layer.ExposureTime) // Different layers, cache and continue
- {
- currentLayer = layer;
- continue;
}
+
- using var matCurrent = currentLayer.LayerMat;
- using var mat = layer.LayerMat;
-
- CvInvoke.Add(matCurrent, mat, matCurrent); // Sum layers
- currentLayer.LayerMat = matCurrent;
-
- layers[layerIndex] = null; // Discard
+ newLayer.LayerMat = newMat;
+ parallelLayers.Add(newLayer);
+ progress.LockAndIncrement();
}
+ });
- layers.RemoveAll(layer => layer is null); // Discard equal layers
+ progress.Token.ThrowIfCancellationRequested();
+ if (parallelLayers.IsEmpty) return false;
+ var layers = parallelLayers.OrderBy(layer => layer.PositionZ).ThenBy(layer => layer.ExposureTime).ToList();
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.LayerManager.Layers = layers.ToArray();
- });
- }
- else // No patterned
+ progress.ResetNameAndProcessed("Optimized layers");
+
+ Layer currentLayer = layers[0];
+ for (var layerIndex = 1; layerIndex < layers.Count; layerIndex++)
{
- var layers = GetLayers();
- if (layers is null) return false;
- progress.ItemCount = 0;
- //SanitizeExposureTable();
- if (layers[0].Width > SlicerFile.ResolutionX || layers[0].Height > SlicerFile.ResolutionY)
+ progress.Token.ThrowIfCancellationRequested();
+ progress++;
+ var layer = layers[layerIndex];
+ if (currentLayer.PositionZ != layer.PositionZ ||
+ currentLayer.ExposureTime != layer.ExposureTime) // Different layers, cache and continue
{
- return false;
+ currentLayer = layer;
+ continue;
}
- List<Layer> newLayers = new();
+ using var matCurrent = currentLayer.LayerMat;
+ using var mat = layer.LayerMat;
- Dictionary<ExposureItem, Point> table = new();
- var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
- var totalHeight = TotalHeight;
- uint layerIndex = 0;
- int currentX = sideMarginPx;
- int currentY = topBottomMarginPx;
- int featuresMarginX = (int)(Xppmm * _featuresMargin);
- int featuresMarginY = (int)(Yppmm * _featuresMargin);
- ushort startCaseThickness = StaircaseThickness;
+ CvInvoke.Add(matCurrent, mat, matCurrent); // Sum layers
+ currentLayer.LayerMat = matCurrent;
- var holes = Holes;
- int holePanelWidth = holes.Length > 0 ? featuresMarginX * 2 + holes[^1] : 0;
- int staircaseWidth = layers[0].Width - holePanelWidth;
+ layers[layerIndex] = null!; // Discard
+ }
- var brightnesses = MultipleBrightnessValuesArray;
- if (brightnesses.Length == 0 || !_multipleBrightness) brightnesses = new[] { byte.MaxValue };
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+ layers.RemoveAll(layer => layer is null); // Discard equal layers
- ExposureItem lastExposureItem = null;
- decimal lastcurrentHeight = 0;
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.Layers = layers.ToArray();
+ });
+ }
+ else // No patterned
+ {
+ var layers = GetLayers();
+ progress.ItemCount = 0;
+ //SanitizeExposureTable();
+ if (layers[0].Width+sideMarginPx > SlicerFile.ResolutionX || layers[0].Height+topBottomMarginPx > SlicerFile.ResolutionY)
+ {
+ throw new InvalidOperationException("The used configuration can not produce a test due insufficient space.\n" +
+ "Try to adjust sides and/or top/bottom margins to gain space or object features to shorten the test size.");
+ //return false;
+ }
- void AddLayer(decimal currentHeight, decimal layerHeight, decimal bottomExposure, decimal normalExposure)
- {
- var layerDifference = currentHeight / layerHeight;
+ List<Layer> newLayers = new();
- if (!layerDifference.IsInteger()) return; // Not at right height to process with layer height
- //Debug.WriteLine($"{currentHeight} / {layerHeight} = {layerDifference}, Floor={Math.Floor(layerDifference)}");
+ Dictionary<ExposureItem, Point> table = new();
+ var endLayerHeight = _multipleLayerHeight ? _multipleLayerHeightMaximum : _layerHeight;
+ var totalHeight = TotalHeight;
+ uint layerIndex = 0;
+ int currentX = sideMarginPx;
+ int currentY = topBottomMarginPx;
+ int featuresMarginX = (int)(Xppmm * _featuresMargin);
+ int featuresMarginY = (int)(Yppmm * _featuresMargin);
+ ushort startCaseThickness = StaircaseThickness;
- int firstFeatureLayer = (int)(_baseHeight / layerHeight);
- int lastLayer = (int)((_baseHeight + _featuresHeight) / layerHeight);
- int layerCountOnHeight = (int)(currentHeight / layerHeight);
- bool isBottomLayer = layerCountOnHeight <= _bottomLayers;
- bool isBaseLayer = currentHeight <= _baseHeight;
- ushort microns = (ushort)(layerHeight * 1000);
- Point position;
- bool addSomething = false;
+ var holes = Holes;
+ int holePanelWidth = holes.Length > 0 ? featuresMarginX * 2 + holes[^1] : 0;
+ int staircaseWidth = layers[0].Width - holePanelWidth;
- bool reUseLastLayer =
- lastExposureItem is not null &&
- lastcurrentHeight == currentHeight &&
- lastExposureItem.LayerHeight == layerHeight &&
- (
- (isBottomLayer && lastExposureItem.BottomExposure == bottomExposure || !isBottomLayer && lastExposureItem.Exposure == normalExposure) ||
- (!isBottomLayer && isBaseLayer && _multipleExposuresBaseLayersPrintMode != CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.Iterative)
- );
+ var brightnesses = MultipleBrightnessValuesArray;
+ if (brightnesses.Length == 0 || !_multipleBrightness) brightnesses = new[] { byte.MaxValue };
- using var mat = reUseLastLayer ? newLayers[^1].LayerMat : EmguExtensions.InitMat(SlicerFile.Resolution);
+ ExposureItem? lastExposureItem = null;
+ decimal lastcurrentHeight = 0;
- lastcurrentHeight = currentHeight;
+ void AddLayer(decimal currentHeight, decimal layerHeight, decimal bottomExposure, decimal normalExposure)
+ {
+ var layerDifference = currentHeight / layerHeight;
+
+ if (!layerDifference.IsInteger()) return; // Not at right height to process with layer height
+ //Debug.WriteLine($"{currentHeight} / {layerHeight} = {layerDifference}, Floor={Math.Floor(layerDifference)}");
+
+ int firstFeatureLayer = (int)(_baseHeight / layerHeight);
+ int lastLayer = (int)((_baseHeight + _featuresHeight) / layerHeight);
+ int layerCountOnHeight = (int)(currentHeight / layerHeight);
+ bool isBottomLayer = layerCountOnHeight <= _bottomLayers;
+ bool isBaseLayer = currentHeight <= _baseHeight;
+ ushort microns = (ushort)(layerHeight * 1000);
+ Point position;
+ bool addSomething = false;
+
+ bool reUseLastLayer =
+ lastExposureItem is not null &&
+ lastcurrentHeight == currentHeight &&
+ lastExposureItem.LayerHeight == layerHeight &&
+ (
+ (isBottomLayer && lastExposureItem.BottomExposure == bottomExposure || !isBottomLayer && lastExposureItem.Exposure == normalExposure) ||
+ (!isBottomLayer && isBaseLayer && _multipleExposuresBaseLayersPrintMode != CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.Iterative)
+ );
+
+ using var mat = reUseLastLayer ? newLayers[^1].LayerMat : EmguExtensions.InitMat(SlicerFile.Resolution);
+
+ lastcurrentHeight = currentHeight;
+
+ foreach (var brightness in brightnesses)
+ {
+ var bottomExposureTemp = bottomExposure;
+ var normalExposureTemp = normalExposure;
+ ExposureItem key = new(layerHeight, bottomExposure, normalExposure, brightness);
+ lastExposureItem = key;
- foreach (var brightness in brightnesses)
+ if (table.TryGetValue(key, out var pos))
{
- var bottomExposureTemp = bottomExposure;
- var normalExposureTemp = normalExposure;
- ExposureItem key = new(layerHeight, bottomExposure, normalExposure, brightness);
- lastExposureItem = key;
-
- if (table.TryGetValue(key, out var pos))
+ position = pos;
+ }
+ else
+ {
+ if (currentX + layers[0].Width + sideMarginPx > SlicerFile.ResolutionX)
{
- position = pos;
+ currentX = sideMarginPx;
+ currentY += layers[0].Height + partMarginYPx;
}
- else
- {
- if (currentX + layers[0].Width + sideMarginPx > SlicerFile.ResolutionX)
- {
- currentX = sideMarginPx;
- currentY += layers[0].Height + partMarginYPx;
- }
- if (currentY + layers[0].Height + topBottomMarginPx > SlicerFile.ResolutionY)
- {
- break; // Reach the end
- }
+ if (currentY + layers[0].Height + topBottomMarginPx > SlicerFile.ResolutionY)
+ {
+ break; // Reach the end
+ }
- position = new Point(currentX, currentY);
- table.Add(key, new Point(currentX, currentY));
+ position = new Point(currentX, currentY);
+ table.Add(key, new Point(currentX, currentY));
- currentX += layers[0].Width + partMarginXPx;
- }
+ currentX += layers[0].Width + partMarginXPx;
+ }
- Mat matRoi = new(mat, new Rectangle(position, layers[0].Size));
+ Mat matRoi = new(mat, new Rectangle(position, layers[0].Size));
- layers[isBaseLayer ? 0 : 1].CopyTo(matRoi);
+ layers[isBaseLayer ? 0 : 1].CopyTo(matRoi);
- if (!isBaseLayer && startCaseThickness > 0)
+ if (!isBaseLayer && startCaseThickness > 0)
+ {
+ int staircaseWidthIncrement = (int) Math.Ceiling(staircaseWidth / (_featuresHeight / layerHeight-1));
+ int staircaseLayer = layerCountOnHeight - firstFeatureLayer - 1;
+ int staircaseWidthForLayer = staircaseWidth - staircaseWidthIncrement * staircaseLayer;
+ if (staircaseWidthForLayer >= 0 && layerCountOnHeight != lastLayer)
{
- int staircaseWidthIncrement = (int) Math.Ceiling(staircaseWidth / (_featuresHeight / layerHeight-1));
- int staircaseLayer = layerCountOnHeight - firstFeatureLayer - 1;
- int staircaseWidthForLayer = staircaseWidth - staircaseWidthIncrement * staircaseLayer;
- if (staircaseWidthForLayer >= 0 && layerCountOnHeight != lastLayer)
- {
- CvInvoke.Rectangle(matRoi,
- new Rectangle(staircaseWidth - staircaseWidthForLayer, 0, staircaseWidthForLayer, startCaseThickness),
- EmguExtensions.WhiteColor, -1,
- _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ CvInvoke.Rectangle(matRoi,
+ new Rectangle(staircaseWidth - staircaseWidthForLayer, 0, staircaseWidthForLayer, startCaseThickness),
+ EmguExtensions.WhiteColor, -1,
+ _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
}
+ }
- if (isBottomLayer && _erodeBottomIterations > 0)
- {
- CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default);
- }
+ if (isBottomLayer && _erodeBottomIterations > 0)
+ {
+ CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default);
+ }
- if (layerCountOnHeight < _chamferLayers)
- {
- CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default);
- }
+ if (layerCountOnHeight < _chamferLayers)
+ {
+ CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default);
+ }
- if (_multipleBrightness && brightness < 255)
+ if (_multipleBrightness && brightness < 255)
+ {
+ // normalExposure - 255
+ // x - brightness
+ normalExposureTemp = Math.Round(normalExposure * brightness / byte.MaxValue, 2);
+ if (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None)
{
- // normalExposure - 255
- // x - brightness
- normalExposureTemp = Math.Round(normalExposure * brightness / byte.MaxValue, 2);
- if (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None)
- {
- bottomExposureTemp = Math.Round(bottomExposure * brightness / byte.MaxValue, 2);
- }
+ bottomExposureTemp = Math.Round(bottomExposure * brightness / byte.MaxValue, 2);
}
+ }
- var textHeightStart = matRoi.Height - featuresMarginY - TextMarkingSpacing;
- CvInvoke.PutText(matRoi, $"{microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- if (holes.Length > 0)
- {
- CvInvoke.PutText(matRoi, $"{microns}u", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ var textHeightStart = matRoi.Height - featuresMarginY - TextMarkingSpacing;
+ CvInvoke.PutText(matRoi, $"{microns}u", new Point(TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ if (holes.Length > 0)
+ {
+ CvInvoke.PutText(matRoi, $"{microns}u", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{bottomExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.PutText(matRoi, $"{normalExposureTemp}s", new Point(matRoi.Width - featuresMarginX * 2 - holes[^1] + TextMarkingStartX, textHeightStart + TextMarkingLineBreak * 2), TextMarkingFontFace, TextMarkingScale, EmguExtensions.BlackColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ }
- if (_multipleBrightness)
+ if (_multipleBrightness)
+ {
+ CvInvoke.PutText(matRoi, brightness.ToString(), new Point(matRoi.Width / 3, 35), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ if (brightness < 255 &&
+ (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !isBottomLayer ||
+ _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase && !isBottomLayer && !isBaseLayer)
+ )
{
- CvInvoke.PutText(matRoi, brightness.ToString(), new Point(matRoi.Width / 3, 35), TextMarkingFontFace, TextMarkingScale, EmguExtensions.WhiteColor, TextMarkingThickness, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- if (brightness < 255 &&
- (_multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.None ||
- _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.Bottom && !isBottomLayer ||
- _multipleBrightnessExcludeFrom == CalibrateExposureFinderMultipleBrightnessExcludeFrom.BottomAndBase && !isBottomLayer && !isBaseLayer)
- )
- {
- using var pattern = matRoi.New();
- //pattern.SetTo(new MCvScalar(brightness)); OLD
- //CvInvoke.BitwiseAnd(matRoi, pattern, matRoi, matRoi); OLD
+ using var pattern = matRoi.New();
+ //pattern.SetTo(new MCvScalar(brightness)); OLD
+ //CvInvoke.BitwiseAnd(matRoi, pattern, matRoi, matRoi); OLD
- pattern.SetTo(new MCvScalar(byte.MaxValue - brightness));
- CvInvoke.Subtract(matRoi, pattern, matRoi);
- }
+ pattern.SetTo(new MCvScalar(byte.MaxValue - brightness));
+ CvInvoke.Subtract(matRoi, pattern, matRoi);
}
-
- addSomething = true;
}
- if (!addSomething) return;
+ addSomething = true;
+ }
- if (reUseLastLayer)
- {
- var layer = newLayers[^1];
- layer.LayerMat = mat;
+ if (!addSomething) return;
- if (!isBottomLayer && isBaseLayer)
- {
- switch (_multipleExposuresBaseLayersPrintMode)
- {
- case CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.UseLowest:
- layer.ExposureTime = (float)_exposureTable[0].Exposure;
- break;
- case CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.UseMiddle:
- layer.ExposureTime = (float)_exposureTable[(int)Math.Ceiling((_exposureTable.Count - 1) / 2.0)].Exposure;
- break;
- case CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.UseHighest:
- layer.ExposureTime = (float)_exposureTable[^1].Exposure;
- break;
- case CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.Custom:
- layer.ExposureTime = (float)_multipleExposuresBaseLayersCustomExposure;
- break;
- default:
- throw new ArgumentOutOfRangeException($"Unhandled type for {_multipleExposuresBaseLayersCustomExposure}");
- }
- }
- }
- else
+ if (reUseLastLayer)
+ {
+ var layer = newLayers[^1];
+ layer.LayerMat = mat;
+
+ if (!isBottomLayer && isBaseLayer)
{
- var layer = new Layer(layerIndex++, mat, SlicerFile)
+ layer.ExposureTime = _multipleExposuresBaseLayersPrintMode switch
{
- PositionZ = (float)currentHeight,
- ExposureTime = isBottomLayer ? (float)bottomExposure : (float)normalExposure,
- IsModified = true
+ CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.UseLowest => (float) _exposureTable[0].Exposure,
+ CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.UseMiddle => (float) _exposureTable[(int) Math.Ceiling((_exposureTable.Count - 1) / 2.0)].Exposure,
+ CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.UseHighest => (float) _exposureTable[^1].Exposure,
+ CalibrateExposureFinderMultipleExposuresBaseLayersPrintModes.Custom => (float) _multipleExposuresBaseLayersCustomExposure,
+ _ => throw new ArgumentOutOfRangeException($"Unhandled type for {_multipleExposuresBaseLayersCustomExposure}")
};
- newLayers.Add(layer);
}
+ }
+ else
+ {
+ var layer = new Layer(layerIndex++, mat, SlicerFile)
+ {
+ PositionZ = (float)currentHeight,
+ ExposureTime = isBottomLayer ? (float)bottomExposure : (float)normalExposure,
+ IsModified = true
+ };
+ newLayers.Add(layer);
+ }
- progress++;
- }
+ progress++;
+ }
- for (decimal currentHeight = _layerHeight; currentHeight <= totalHeight; currentHeight += Layer.HeightPrecisionIncrement)
+ for (decimal currentHeight = _layerHeight; currentHeight <= totalHeight; currentHeight += Layer.HeightPrecisionIncrement)
+ {
+ currentHeight = Layer.RoundHeight(currentHeight);
+ for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep)
{
- currentHeight = Layer.RoundHeight(currentHeight);
- for (decimal layerHeight = _layerHeight; layerHeight <= endLayerHeight; layerHeight += _multipleLayerHeightStep)
- {
- progress.Token.ThrowIfCancellationRequested();
- layerHeight = Layer.RoundHeight(layerHeight);
+ progress.Token.ThrowIfCancellationRequested();
+ layerHeight = Layer.RoundHeight(layerHeight);
- if (_multipleExposures)
+ if (_multipleExposures)
+ {
+ foreach (var exposureItem in _exposureTable)
{
- foreach (var exposureItem in _exposureTable)
+ if (exposureItem.IsValid && exposureItem.LayerHeight == layerHeight)
{
- if (exposureItem.IsValid && exposureItem.LayerHeight == layerHeight)
- {
- AddLayer(currentHeight, layerHeight, exposureItem.BottomExposure, exposureItem.Exposure);
- }
+ AddLayer(currentHeight, layerHeight, exposureItem.BottomExposure, exposureItem.Exposure);
}
}
- else
- {
- AddLayer(currentHeight, layerHeight, _bottomExposure, _normalExposure);
- }
}
- }
-
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)LayerHeight;
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.BottomLayerCount = BottomLayers;
- SlicerFile.TransitionLayerCount = 0;
- SlicerFile.LayerManager.Layers = newLayers.ToArray();
- });
-
- if (_mirrorOutput)
- {
- var flip = SlicerFile.DisplayMirror;
- if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
- new OperationFlip(SlicerFile) { FlipDirection = Enumerations.ToOpenCVFlipType(flip) }.Execute(progress);
+ else
+ {
+ AddLayer(currentHeight, layerHeight, _bottomExposure, _normalExposure);
+ }
}
}
- if (_multipleBrightness && SlicerFile.IsAntiAliasingEmulated)
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
{
- /*SlicerFile.AntiAliasing = MultipleBrightnessValuesArray.Length switch
- {
- <= 2 => 2,
- <= 4 => 4,
- <= 8 => 8,
- <= 16 => 16,
- _ => 16
- };*/
- SlicerFile.AntiAliasing = _multipleBrightnessGenEmulatedAALevel;
+ SlicerFile.LayerHeight = (float)LayerHeight;
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.BottomLayerCount = BottomLayers;
+ SlicerFile.TransitionLayerCount = 0;
+ SlicerFile.Layers = newLayers.ToArray();
+ });
+
+ if (_mirrorOutput)
+ {
+ var flip = SlicerFile.DisplayMirror;
+ if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
+ new OperationFlip(SlicerFile) { FlipDirection = Enumerations.ToOpenCVFlipType(flip) }.Execute(progress);
}
+ }
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(GetThumbnail());
-
- if (_differentSettingsForSamePositionedLayers)
+ if (_multipleBrightness && SlicerFile.IsAntiAliasingEmulated)
+ {
+ /*SlicerFile.AntiAliasing = MultipleBrightnessValuesArray.Length switch
{
- var layers = SlicerFile.LayerManager.GetSamePositionedLayers();
- foreach (var layer in layers)
- {
- if(_samePositionedLayersLiftHeightEnabled) layer.LiftHeightTotal = (float) _samePositionedLayersLiftHeight;
- if(_samePositionedLayersWaitTimeBeforeCureEnabled) layer.SetWaitTimeBeforeCureOrLightOffDelay((float) _samePositionedLayersWaitTimeBeforeCure);
- }
- }
+ <= 2 => 2,
+ <= 4 => 4,
+ <= 8 => 8,
+ <= 16 => 16,
+ _ => 16
+ };*/
+ SlicerFile.AntiAliasing = _multipleBrightnessGenEmulatedAALevel;
+ }
- new OperationMove(SlicerFile).Execute(progress);
+ if (SlicerFile.ThumbnailsCount > 0)
+ SlicerFile.SetThumbnails(GetThumbnail());
- return !progress.Token.IsCancellationRequested;
+ if (_differentSettingsForSamePositionedLayers)
+ {
+ var layers = SlicerFile.SamePositionedLayers;
+ foreach (var layer in layers)
+ {
+ if(_samePositionedLayersLiftHeightEnabled) layer.LiftHeightTotal = (float) _samePositionedLayersLiftHeight;
+ if(_samePositionedLayersWaitTimeBeforeCureEnabled) layer.SetWaitTimeBeforeCureOrLightOffDelay((float) _samePositionedLayersWaitTimeBeforeCure);
+ }
}
- #endregion
+ new OperationMove(SlicerFile).Execute(progress);
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateExternalTests.cs b/UVtools.Core/Operations/OperationCalibrateExternalTests.cs
index 99fa3fd..bebef99 100644
--- a/UVtools.Core/Operations/OperationCalibrateExternalTests.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExternalTests.cs
@@ -9,49 +9,49 @@
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+public class OperationCalibrateExternalTests : Operation
{
- public class OperationCalibrateExternalTests : Operation
- {
- #region Members
- #endregion
+ #region Members
+ #endregion
- #region Overrides
+ #region Overrides
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI => false;
- public override bool CanHaveProfiles => false;
- public override string ButtonOkText => null;
- public override string Title => "External tests";
- public override string Description =>
- "A set of useful external tests to run within your slicer.\nClick on a button to open website and instructions.";
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
+ public override bool CanHaveProfiles => false;
+ public override string ButtonOkText => null!;
+ public override string IconClass => "fas fa-bookmark";
+ public override string Title => "External tests";
+ public override string Description =>
+ "A set of useful external tests to run within your slicer.\nClick on a button to open website and instructions.";
- public override string ConfirmationText => null;
+ public override string ConfirmationText => null!;
- public override string ProgressTitle => null;
+ public override string ProgressTitle => null!;
- public override string ProgressAction => null;
+ public override string ProgressAction => null!;
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationCalibrateExternalTests() { }
+ public OperationCalibrateExternalTests() { }
- public OperationCalibrateExternalTests(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationCalibrateExternalTests(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- #endregion
- }
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
index bc8610a..49bc53e 100644
--- a/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
+++ b/UVtools.Core/Operations/OperationCalibrateGrayscale.cs
@@ -6,558 +6,557 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Drawing;
-using System.Text;
-using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
+using System;
+using System.Drawing;
+using System.Text;
+using System.Threading.Tasks;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationCalibrateGrayscale : Operation
{
- [Serializable]
- public sealed class OperationCalibrateGrayscale : Operation
+ #region Members
+ private decimal _layerHeight;
+ private ushort _bottomLayers;
+ private ushort _interfaceLayers = 20;
+ private ushort _normalLayers = 20;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+ private ushort _outerMargin = 200;
+ private ushort _innerMargin = 50;
+ private bool _enableAntiAliasing = false;
+ private bool _mirrorOutput;
+ private byte _startBrightness = 175;
+ private byte _endBrightness = 255;
+ private byte _brightnessSteps = 10;
+ private bool _enableCenterHoleRelief = true;
+ private ushort _centerHoleDiameter = 200;
+ private bool _textEnabled = true;
+ private bool _convertBrightnessToExposureTime;
+ private bool _enableLineDivisions = true;
+ private byte _lineDivisionThickness = 30;
+ private byte _lineDivisionBrightness = 255;
+ private short _textXOffset;
+
+ #endregion
+
+ #region Overrides
+
+ public override bool CanROI => false;
+
+ public override bool CanCancel => false;
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "fas fa-chart-pie";
+ public override string Title => "Grayscale";
+ public override string Description =>
+ "Generates test models with various strategies and increments to verify the LED power against the grayscale levels.\n" +
+ "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
+ "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
+
+ public override string ConfirmationText =>
+ $"generate the grayscale test?";
+
+ public override string ProgressTitle =>
+ $"Generating the grayscale test";
+
+ public override string ProgressAction => "Generated";
+
+ public override string? ValidateInternally()
{
- #region Members
- private decimal _layerHeight;
- private ushort _bottomLayers;
- private ushort _interfaceLayers = 20;
- private ushort _normalLayers = 20;
- private decimal _bottomExposure;
- private decimal _normalExposure;
- private ushort _outerMargin = 200;
- private ushort _innerMargin = 50;
- private bool _enableAntiAliasing = false;
- private bool _mirrorOutput;
- private byte _startBrightness = 175;
- private byte _endBrightness = 255;
- private byte _brightnessSteps = 10;
- private bool _enableCenterHoleRelief = true;
- private ushort _centerHoleDiameter = 200;
- private bool _textEnabled = true;
- private bool _convertBrightnessToExposureTime;
- private bool _enableLineDivisions = true;
- private byte _lineDivisionThickness = 30;
- private byte _lineDivisionBrightness = 255;
- private short _textXOffset;
-
- #endregion
-
- #region Overrides
-
- public override bool CanROI => false;
-
- public override bool CanCancel => false;
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
-
- public override string Title => "Grayscale";
- public override string Description =>
- "Generates test models with various strategies and increments to verify the LED power against the grayscale levels.\n" +
- "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
- "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
-
- public override string ConfirmationText =>
- $"generate the grayscale test?";
-
- public override string ProgressTitle =>
- $"Generating the grayscale test";
-
- public override string ProgressAction => "Generated";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ var sb = new StringBuilder();
- if (_startBrightness > _endBrightness)
- {
- sb.AppendLine("Start brightness must be lower or equal to end brightness.");
- }
- if (Divisions <= 0)
- {
- sb.AppendLine("No divisions to output.");
- }
-
- return sb.ToString();
+ if (_startBrightness > _endBrightness)
+ {
+ sb.AppendLine("Start brightness must be lower or equal to end brightness.");
}
-
- public override string ToString()
+ if (Divisions <= 0)
{
- var result = $"[Layer Height: {_layerHeight}] " +
- $"[Layers: {_bottomLayers}/{_interfaceLayers}/{_normalLayers}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
- $"[Margin: {_outerMargin}/{_innerMargin}] " +
- $"[B: {_startBrightness}-{_endBrightness} S{_brightnessSteps}] " +
- $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("No divisions to output.");
}
- #endregion
+ return sb.ToString();
+ }
- #region Constructor
+ public override string ToString()
+ {
+ var result = $"[Layer Height: {_layerHeight}] " +
+ $"[Layers: {_bottomLayers}/{_interfaceLayers}/{_normalLayers}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
+ $"[Margin: {_outerMargin}/{_innerMargin}] " +
+ $"[B: {_startBrightness}-{_endBrightness} S{_brightnessSteps}] " +
+ $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- public OperationCalibrateGrayscale() { }
+ #endregion
- public OperationCalibrateGrayscale(FileFormat slicerFile) : base(slicerFile)
- { }
+ #region Constructor
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
- if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
- if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
- _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
- }
+ public OperationCalibrateGrayscale() { }
+
+ public OperationCalibrateGrayscale(FileFormat slicerFile) : base(slicerFile)
+ { }
- #endregion
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
+ if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
+ if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
+ }
+
+ #endregion
- #region Properties
+ #region Properties
- public decimal LayerHeight
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(BottomHeight));
- RaisePropertyChanged(nameof(InterfaceHeight));
- RaisePropertyChanged(nameof(NormalHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- }
+ if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(BottomHeight));
+ RaisePropertyChanged(nameof(InterfaceHeight));
+ RaisePropertyChanged(nameof(NormalHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
}
+ }
- public ushort Microns => (ushort) (LayerHeight * 1000);
+ public ushort Microns => (ushort) (LayerHeight * 1000);
- public ushort BottomLayers
+ public ushort BottomLayers
+ {
+ get => _bottomLayers;
+ set
{
- get => _bottomLayers;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
- RaisePropertyChanged(nameof(BottomHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
+ RaisePropertyChanged(nameof(BottomHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public ushort InterfaceLayers
+ public ushort InterfaceLayers
+ {
+ get => _interfaceLayers;
+ set
{
- get => _interfaceLayers;
- set
- {
- if(!RaiseAndSetIfChanged(ref _interfaceLayers, value)) return;
- RaisePropertyChanged(nameof(InterfaceHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ if(!RaiseAndSetIfChanged(ref _interfaceLayers, value)) return;
+ RaisePropertyChanged(nameof(InterfaceHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public ushort NormalLayers
+ public ushort NormalLayers
+ {
+ get => _normalLayers;
+ set
{
- get => _normalLayers;
- set
- {
- if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return;
- RaisePropertyChanged(nameof(NormalHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return;
+ RaisePropertyChanged(nameof(NormalHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public uint LayerCount => (uint) (_bottomLayers + _interfaceLayers + _normalLayers);
+ public uint LayerCount => (uint) (_bottomLayers + _interfaceLayers + _normalLayers);
- public decimal BottomHeight => Layer.RoundHeight(LayerHeight * _bottomLayers);
- public decimal InterfaceHeight => Layer.RoundHeight(LayerHeight * _interfaceLayers);
- public decimal NormalHeight => Layer.RoundHeight(LayerHeight * _normalLayers);
+ public decimal BottomHeight => Layer.RoundHeight(LayerHeight * _bottomLayers);
+ public decimal InterfaceHeight => Layer.RoundHeight(LayerHeight * _interfaceLayers);
+ public decimal NormalHeight => Layer.RoundHeight(LayerHeight * _normalLayers);
- public decimal TotalHeight => BottomHeight + InterfaceHeight + NormalHeight;
+ public decimal TotalHeight => BottomHeight + InterfaceHeight + NormalHeight;
- public decimal BottomExposure
- {
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
- }
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
- public decimal NormalExposure
- {
- get => _normalExposure;
- set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
- }
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
+ }
- public ushort OuterMargin
- {
- get => _outerMargin;
- set => RaiseAndSetIfChanged(ref _outerMargin, value);
- }
+ public ushort OuterMargin
+ {
+ get => _outerMargin;
+ set => RaiseAndSetIfChanged(ref _outerMargin, value);
+ }
- public ushort InnerMargin
- {
- get => _innerMargin;
- set => RaiseAndSetIfChanged(ref _innerMargin, value);
- }
+ public ushort InnerMargin
+ {
+ get => _innerMargin;
+ set => RaiseAndSetIfChanged(ref _innerMargin, value);
+ }
- public bool EnableAntiAliasing
- {
- get => _enableAntiAliasing;
- set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
- }
+ public bool EnableAntiAliasing
+ {
+ get => _enableAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
+ }
- public bool MirrorOutput
- {
- get => _mirrorOutput;
- set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
- }
+ public bool MirrorOutput
+ {
+ get => _mirrorOutput;
+ set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
+ }
- public byte StartBrightness
+ public byte StartBrightness
+ {
+ get => _startBrightness;
+ set
{
- get => _startBrightness;
- set
- {
- if (!RaiseAndSetIfChanged(ref _startBrightness, value)) return;
- RaisePropertyChanged(nameof(StartBrightnessPercent));
- RaisePropertyChanged(nameof(Divisions));
- RaisePropertyChanged(nameof(AngleStep));
- }
+ if (!RaiseAndSetIfChanged(ref _startBrightness, value)) return;
+ RaisePropertyChanged(nameof(StartBrightnessPercent));
+ RaisePropertyChanged(nameof(Divisions));
+ RaisePropertyChanged(nameof(AngleStep));
}
+ }
- public float StartBrightnessPercent => (float)Math.Round(_startBrightness * 100 / 255M, 2);
+ public float StartBrightnessPercent => (float)Math.Round(_startBrightness * 100 / 255M, 2);
- public byte EndBrightness
+ public byte EndBrightness
+ {
+ get => _endBrightness;
+ set
{
- get => _endBrightness;
- set
- {
- if (!RaiseAndSetIfChanged(ref _endBrightness, value)) return;
- RaisePropertyChanged(nameof(EndBrightnessPercent));
- RaisePropertyChanged(nameof(Divisions));
- RaisePropertyChanged(nameof(AngleStep));
- }
+ if (!RaiseAndSetIfChanged(ref _endBrightness, value)) return;
+ RaisePropertyChanged(nameof(EndBrightnessPercent));
+ RaisePropertyChanged(nameof(Divisions));
+ RaisePropertyChanged(nameof(AngleStep));
}
+ }
- public float EndBrightnessPercent => (float)Math.Round(_endBrightness * 100 / 255M, 2);
+ public float EndBrightnessPercent => (float)Math.Round(_endBrightness * 100 / 255M, 2);
- public byte BrightnessSteps
+ public byte BrightnessSteps
+ {
+ get => _brightnessSteps;
+ set
{
- get => _brightnessSteps;
- set
- {
- if (!RaiseAndSetIfChanged(ref _brightnessSteps, value)) return;
- RaisePropertyChanged(nameof(Divisions));
- RaisePropertyChanged(nameof(AngleStep));
- }
+ if (!RaiseAndSetIfChanged(ref _brightnessSteps, value)) return;
+ RaisePropertyChanged(nameof(Divisions));
+ RaisePropertyChanged(nameof(AngleStep));
}
+ }
- public int Divisions => (int)((_endBrightness - _startBrightness) / (decimal)_brightnessSteps) + 1;
- public float AngleStep => 360f / Divisions;
+ public int Divisions => (int)((_endBrightness - _startBrightness) / (decimal)_brightnessSteps) + 1;
+ public float AngleStep => 360f / Divisions;
- public bool EnableCenterHoleRelief
- {
- get => _enableCenterHoleRelief;
- set => RaiseAndSetIfChanged(ref _enableCenterHoleRelief, value);
- }
+ public bool EnableCenterHoleRelief
+ {
+ get => _enableCenterHoleRelief;
+ set => RaiseAndSetIfChanged(ref _enableCenterHoleRelief, value);
+ }
- public ushort CenterHoleDiameter
- {
- get => _centerHoleDiameter;
- set => RaiseAndSetIfChanged(ref _centerHoleDiameter, value);
- }
+ public ushort CenterHoleDiameter
+ {
+ get => _centerHoleDiameter;
+ set => RaiseAndSetIfChanged(ref _centerHoleDiameter, value);
+ }
- public bool TextEnabled
- {
- get => _textEnabled;
- set => RaiseAndSetIfChanged(ref _textEnabled, value);
- }
+ public bool TextEnabled
+ {
+ get => _textEnabled;
+ set => RaiseAndSetIfChanged(ref _textEnabled, value);
+ }
- public bool ConvertBrightnessToExposureTime
- {
- get => _convertBrightnessToExposureTime;
- set => RaiseAndSetIfChanged(ref _convertBrightnessToExposureTime, value);
- }
+ public bool ConvertBrightnessToExposureTime
+ {
+ get => _convertBrightnessToExposureTime;
+ set => RaiseAndSetIfChanged(ref _convertBrightnessToExposureTime, value);
+ }
- public bool EnableLineDivisions
- {
- get => _enableLineDivisions;
- set => RaiseAndSetIfChanged(ref _enableLineDivisions, value);
- }
+ public bool EnableLineDivisions
+ {
+ get => _enableLineDivisions;
+ set => RaiseAndSetIfChanged(ref _enableLineDivisions, value);
+ }
- public byte LineDivisionThickness
- {
- get => _lineDivisionThickness;
- set => RaiseAndSetIfChanged(ref _lineDivisionThickness, value);
- }
+ public byte LineDivisionThickness
+ {
+ get => _lineDivisionThickness;
+ set => RaiseAndSetIfChanged(ref _lineDivisionThickness, value);
+ }
- public byte LineDivisionBrightness
+ public byte LineDivisionBrightness
+ {
+ get => _lineDivisionBrightness;
+ set
{
- get => _lineDivisionBrightness;
- set
- {
- if(!RaiseAndSetIfChanged(ref _lineDivisionBrightness, value)) return;
- RaisePropertyChanged(nameof(LineDivisionBrightnessPercent));
- }
+ if(!RaiseAndSetIfChanged(ref _lineDivisionBrightness, value)) return;
+ RaisePropertyChanged(nameof(LineDivisionBrightnessPercent));
}
+ }
- public float LineDivisionBrightnessPercent => (float)Math.Round(_lineDivisionBrightness * 100 / 255M, 2);
+ public float LineDivisionBrightnessPercent => (float)Math.Round(_lineDivisionBrightness * 100 / 255M, 2);
- public short TextXOffset
- {
- get => _textXOffset;
- set => RaiseAndSetIfChanged(ref _textXOffset, value);
- }
+ public short TextXOffset
+ {
+ get => _textXOffset;
+ set => RaiseAndSetIfChanged(ref _textXOffset, value);
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- private bool Equals(OperationCalibrateGrayscale other)
- {
- return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _interfaceLayers == other._interfaceLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _outerMargin == other._outerMargin && _innerMargin == other._innerMargin && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _startBrightness == other._startBrightness && _endBrightness == other._endBrightness && _brightnessSteps == other._brightnessSteps && _enableCenterHoleRelief == other._enableCenterHoleRelief && _centerHoleDiameter == other._centerHoleDiameter && _textEnabled == other._textEnabled && _convertBrightnessToExposureTime == other._convertBrightnessToExposureTime && _enableLineDivisions == other._enableLineDivisions && _lineDivisionThickness == other._lineDivisionThickness && _lineDivisionBrightness == other._lineDivisionBrightness && _textXOffset == other._textXOffset;
- }
+ private bool Equals(OperationCalibrateGrayscale other)
+ {
+ return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _interfaceLayers == other._interfaceLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _outerMargin == other._outerMargin && _innerMargin == other._innerMargin && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _startBrightness == other._startBrightness && _endBrightness == other._endBrightness && _brightnessSteps == other._brightnessSteps && _enableCenterHoleRelief == other._enableCenterHoleRelief && _centerHoleDiameter == other._centerHoleDiameter && _textEnabled == other._textEnabled && _convertBrightnessToExposureTime == other._convertBrightnessToExposureTime && _enableLineDivisions == other._enableLineDivisions && _lineDivisionThickness == other._lineDivisionThickness && _lineDivisionBrightness == other._lineDivisionBrightness && _textXOffset == other._textXOffset;
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationCalibrateGrayscale other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationCalibrateGrayscale other && Equals(other);
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_layerHeight);
- hashCode.Add(_bottomLayers);
- hashCode.Add(_interfaceLayers);
- hashCode.Add(_normalLayers);
- hashCode.Add(_bottomExposure);
- hashCode.Add(_normalExposure);
- hashCode.Add(_outerMargin);
- hashCode.Add(_innerMargin);
- hashCode.Add(_enableAntiAliasing);
- hashCode.Add(_mirrorOutput);
- hashCode.Add(_startBrightness);
- hashCode.Add(_endBrightness);
- hashCode.Add(_brightnessSteps);
- hashCode.Add(_enableCenterHoleRelief);
- hashCode.Add(_centerHoleDiameter);
- hashCode.Add(_textEnabled);
- hashCode.Add(_convertBrightnessToExposureTime);
- hashCode.Add(_enableLineDivisions);
- hashCode.Add(_lineDivisionThickness);
- hashCode.Add(_lineDivisionBrightness);
- hashCode.Add(_textXOffset);
- return hashCode.ToHashCode();
- }
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_layerHeight);
+ hashCode.Add(_bottomLayers);
+ hashCode.Add(_interfaceLayers);
+ hashCode.Add(_normalLayers);
+ hashCode.Add(_bottomExposure);
+ hashCode.Add(_normalExposure);
+ hashCode.Add(_outerMargin);
+ hashCode.Add(_innerMargin);
+ hashCode.Add(_enableAntiAliasing);
+ hashCode.Add(_mirrorOutput);
+ hashCode.Add(_startBrightness);
+ hashCode.Add(_endBrightness);
+ hashCode.Add(_brightnessSteps);
+ hashCode.Add(_enableCenterHoleRelief);
+ hashCode.Add(_centerHoleDiameter);
+ hashCode.Add(_textEnabled);
+ hashCode.Add(_convertBrightnessToExposureTime);
+ hashCode.Add(_enableLineDivisions);
+ hashCode.Add(_lineDivisionThickness);
+ hashCode.Add(_lineDivisionBrightness);
+ hashCode.Add(_textXOffset);
+ return hashCode.ToHashCode();
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- /// <summary>
- /// Gets the bottom and normal layers, 0 = bottom | 1 = normal
- /// </summary>
- /// <returns></returns>
- public Mat[] GetLayers()
- {
- Mat[] layers = new Mat[3];
+ /// <summary>
+ /// Gets the bottom and normal layers, 0 = bottom | 1 = normal
+ /// </summary>
+ /// <returns></returns>
+ public Mat[] GetLayers()
+ {
+ Mat[] layers = new Mat[3];
- layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution);
+ layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution);
- int radius = Math.Max(100, Math.Min(SlicerFile.Resolution.Width, SlicerFile.Resolution.Height) - _outerMargin * 2) / 2 ;
- Point center = new(SlicerFile.Resolution.Width / 2, SlicerFile.Resolution.Height / 2);
- int innerRadius = Math.Max(100, radius - _innerMargin);
- double topLineLength = 0;
+ int radius = Math.Max(100, Math.Min(SlicerFile.Resolution.Width, SlicerFile.Resolution.Height) - _outerMargin * 2) / 2 ;
+ Point center = new(SlicerFile.Resolution.Width / 2, SlicerFile.Resolution.Height / 2);
+ int innerRadius = Math.Max(100, radius - _innerMargin);
+ double topLineLength = 0;
- LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
+ LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
- CvInvoke.Circle(layers[0], center, radius, EmguExtensions.WhiteColor, -1, lineType);
- layers[1] = layers[0].Clone();
- layers[2] = layers[0].Clone();
+ CvInvoke.Circle(layers[0], center, radius, EmguExtensions.WhiteColor, -1, lineType);
+ layers[1] = layers[0].Clone();
+ layers[2] = layers[0].Clone();
- int i = 0;
- for (ushort brightness = _startBrightness; brightness <= _endBrightness; brightness += _brightnessSteps)
+ int i = 0;
+ for (ushort brightness = _startBrightness; brightness <= _endBrightness; brightness += _brightnessSteps)
+ {
+ var radians = new float[2];
+ var degrees = new SizeF[2];
+ for (int n = 0; n < 2; n++)
{
- var radians = new float[2];
- var degrees = new SizeF[2];
- for (int n = 0; n < 2; n++)
- {
- radians[n] = -AngleStep * (i + n);
- degrees[n] = new SizeF((float) Math.Cos(radians[n] * Math.PI / 180), (float) Math.Sin(radians[n] * Math.PI / 180));
- }
+ radians[n] = -AngleStep * (i + n);
+ degrees[n] = new SizeF((float) Math.Cos(radians[n] * Math.PI / 180), (float) Math.Sin(radians[n] * Math.PI / 180));
+ }
- Point[] points = new Point[3];
- points[0] = center;
- points[1] = new(center.X + (int) (innerRadius * degrees[0].Width), center.Y + (int) (innerRadius * degrees[0].Height));
- points[2] = new(center.X + (int) (innerRadius * degrees[1].Width), center.Y + (int) (innerRadius * degrees[1].Height));
- using var vec = new VectorOfPoint(points);
+ Point[] points = new Point[3];
+ points[0] = center;
+ points[1] = new(center.X + (int) (innerRadius * degrees[0].Width), center.Y + (int) (innerRadius * degrees[0].Height));
+ points[2] = new(center.X + (int) (innerRadius * degrees[1].Width), center.Y + (int) (innerRadius * degrees[1].Height));
+ using var vec = new VectorOfPoint(points);
- if (topLineLength == 0) topLineLength = PointExtensions.FindLength(points[1], points[2]);
+ if (topLineLength == 0) topLineLength = PointExtensions.FindLength(points[1], points[2]);
- CvInvoke.FillPoly(layers[2], vec, new MCvScalar(brightness), lineType);
+ CvInvoke.FillPoly(layers[2], vec, new MCvScalar(brightness), lineType);
- if (_enableLineDivisions && _lineDivisionThickness > 0)
- {
- CvInvoke.Polylines(layers[2], vec, false, new MCvScalar(_lineDivisionBrightness), _lineDivisionThickness, lineType);
- }
-
- i++;
+ if (_enableLineDivisions && _lineDivisionThickness > 0)
+ {
+ CvInvoke.Polylines(layers[2], vec, false, new MCvScalar(_lineDivisionBrightness), _lineDivisionThickness, lineType);
}
+ i++;
+ }
- FontFace fontFace = FontFace.HersheyDuplex;
- double fontScale = 2;
- int fontThickness = 5;
-
- if (_textEnabled)
- {
- Point fontPoint = new((int)(center.X + radius / 2.5f + _textXOffset), (int)(center.Y + AngleStep / 1.5));
- var halfAngleStep = AngleStep / 2;
- var rotatedAngle = halfAngleStep;
+ FontFace fontFace = FontFace.HersheyDuplex;
+ double fontScale = 2;
+ int fontThickness = 5;
+ if (_textEnabled)
+ {
+ Point fontPoint = new((int)(center.X + radius / 2.5f + _textXOffset), (int)(center.Y + AngleStep / 1.5));
- layers[2].Rotate(halfAngleStep);
- for (ushort brightness = _startBrightness; brightness <= _endBrightness; brightness += _brightnessSteps)
- {
- var text = brightness.ToString();
- if (_convertBrightnessToExposureTime)
- {
- text = $"{Math.Round(brightness * _normalExposure / byte.MaxValue, 2)}s";
- }
-
- CvInvoke.PutText(layers[2], text, fontPoint, fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType);
- rotatedAngle += AngleStep;
- layers[2].Rotate(AngleStep);
- }
+ var halfAngleStep = AngleStep / 2;
+ var rotatedAngle = halfAngleStep;
- layers[2].Rotate(-rotatedAngle);
- }
- if (_enableCenterHoleRelief && _centerHoleDiameter > 1)
+ layers[2].Rotate(halfAngleStep);
+ for (ushort brightness = _startBrightness; brightness <= _endBrightness; brightness += _brightnessSteps)
{
- var holeRadius = Math.Min(radius, _centerHoleDiameter) / 2;
- if (_innerMargin > 0)
+ var text = brightness.ToString();
+ if (_convertBrightnessToExposureTime)
{
- CvInvoke.Circle(layers[2], center, holeRadius + _innerMargin, EmguExtensions.WhiteColor, -1, lineType);
+ text = $"{Math.Round(brightness * _normalExposure / byte.MaxValue, 2)}s";
}
- foreach (var layer in layers)
- {
- CvInvoke.Circle(layer, center, holeRadius, EmguExtensions.BlackColor, -1, lineType);
- }
+ CvInvoke.PutText(layers[2], text, fontPoint, fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType);
+ rotatedAngle += AngleStep;
+ layers[2].Rotate(AngleStep);
}
- fontScale = 1.5;
- fontThickness = 3;
- CvInvoke.PutText(layers[0], $"{Microns}um at {_bottomExposure}s/{_normalExposure}s",
- new Point(center.X - radius / 2, center.Y + radius / 2 +40),
- fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType, true);
-
- CvInvoke.PutText(layers[0], $"{_startBrightness}-{_endBrightness} S:{_brightnessSteps}",
- new Point(center.X - radius / 2, center.Y + radius / 2 - 40),
- fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType, true);
+ layers[2].Rotate(-rotatedAngle);
+ }
- if (_mirrorOutput)
+ if (_enableCenterHoleRelief && _centerHoleDiameter > 1)
+ {
+ var holeRadius = Math.Min(radius, _centerHoleDiameter) / 2;
+ if (_innerMargin > 0)
{
- var flip = SlicerFile.DisplayMirror;
- if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
- Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
+ CvInvoke.Circle(layers[2], center, holeRadius + _innerMargin, EmguExtensions.WhiteColor, -1, lineType);
}
- return layers;
+ foreach (var layer in layers)
+ {
+ CvInvoke.Circle(layer, center, holeRadius, EmguExtensions.BlackColor, -1, lineType);
+ }
}
- public Mat GetThumbnail()
- {
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "Grayscale Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- CvInvoke.PutText(thumbnail, $"Divs:{Divisions} Angle:{AngleStep}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
-
- return thumbnail;
- }
+ fontScale = 1.5;
+ fontThickness = 3;
+ CvInvoke.PutText(layers[0], $"{Microns}um at {_bottomExposure}s/{_normalExposure}s",
+ new Point(center.X - radius / 2, center.Y + radius / 2 +40),
+ fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType, true);
- protected override bool ExecuteInternally(OperationProgress progress)
+ CvInvoke.PutText(layers[0], $"{_startBrightness}-{_endBrightness} S:{_brightnessSteps}",
+ new Point(center.X - radius / 2, center.Y + radius / 2 - 40),
+ fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType, true);
+
+ if (_mirrorOutput)
{
- progress.ItemCount = LayerCount;
- var newLayers = new Layer[LayerCount];
+ var flip = SlicerFile.DisplayMirror;
+ if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
+ Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
+ }
- var layers = GetLayers();
+ return layers;
+ }
- var bottomLayer = new Layer(0, layers[0], SlicerFile.LayerManager)
- {
- IsModified = true
- };
- var interfaceLayer = InterfaceLayers > 0 && layers[1] is not null ? new Layer(0, layers[1], SlicerFile.LayerManager)
- {
- IsModified = true
- } : null;
- var layer = new Layer(0, layers[2], SlicerFile.LayerManager)
- {
- IsModified = true
- };
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "Grayscale Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
+ CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ CvInvoke.PutText(thumbnail, $"Divs:{Divisions} Angle:{AngleStep}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+
+ return thumbnail;
+ }
- uint layerIndex = 0;
- for (uint i = 0; i < BottomLayers; i++)
- {
- newLayers[layerIndex] = bottomLayer.Clone();
- progress++;
- layerIndex++;
- }
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = LayerCount;
+ var newLayers = new Layer[LayerCount];
- for (uint i = 0; i < InterfaceLayers; i++)
- {
- newLayers[layerIndex] = interfaceLayer.Clone();
- progress++;
- layerIndex++;
- }
+ var layers = GetLayers();
+ var bottomLayer = new Layer(0, layers[0], SlicerFile)
+ {
+ IsModified = true
+ };
+ var interfaceLayer = InterfaceLayers > 0 ? new Layer(0, layers[1], SlicerFile)
+ {
+ IsModified = true
+ } : null;
+ var layer = new Layer(0, layers[2], SlicerFile)
+ {
+ IsModified = true
+ };
- for (uint i = 0; i < NormalLayers; i++)
- {
- newLayers[layerIndex] = layer.Clone();
- progress++;
- layerIndex++;
- }
+ uint layerIndex = 0;
+ for (uint i = 0; i < BottomLayers; i++)
+ {
+ newLayers[layerIndex] = bottomLayer.Clone();
+ progress++;
+ layerIndex++;
+ }
- foreach (var mat in layers)
- {
- mat?.Dispose();
- }
+ for (uint i = 0; i < InterfaceLayers; i++)
+ {
+ newLayers[layerIndex] = interfaceLayer!.Clone();
+ progress++;
+ layerIndex++;
+ }
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(GetThumbnail());
+ for (uint i = 0; i < NormalLayers; i++)
+ {
+ newLayers[layerIndex] = layer.Clone();
+ progress++;
+ layerIndex++;
+ }
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)LayerHeight;
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.BottomLayerCount = BottomLayers;
- SlicerFile.TransitionLayerCount = 0;
-
- SlicerFile.LayerManager.Layers = newLayers;
- }, true);
-
- return !progress.Token.IsCancellationRequested;
+ foreach (var mat in layers)
+ {
+ mat?.Dispose();
}
- #endregion
+
+ if (SlicerFile.ThumbnailsCount > 0)
+ SlicerFile.SetThumbnails(GetThumbnail());
+
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerHeight = (float)LayerHeight;
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.BottomLayerCount = BottomLayers;
+ SlicerFile.TransitionLayerCount = 0;
+
+ SlicerFile.Layers = newLayers;
+ }, true);
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs b/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs
index c979a23..db74a36 100644
--- a/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs
+++ b/UVtools.Core/Operations/OperationCalibrateLiftHeight.cs
@@ -6,439 +6,438 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Drawing;
-using System.Text;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
+using System;
+using System.Drawing;
+using System.Text;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationCalibrateLiftHeight : Operation
{
- [Serializable]
- public sealed class OperationCalibrateLiftHeight : Operation
+ #region Members
+ private decimal _layerHeight;
+ private ushort _bottomLayers = 3;
+ private ushort _normalLayers = 2;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+ private decimal _bottomLiftHeight;
+ private decimal _liftHeight;
+ private decimal _bottomLiftSpeed;
+ private decimal _liftSpeed;
+ private decimal _retractSpeed;
+ private ushort _leftRightMargin = 200;
+ private ushort _topBottomMargin = 200;
+ private bool _decreaseImage = true;
+ private byte _decreaseImageFactor = 10;
+ private byte _minimumImageFactor = 10;
+
+ #endregion
+
+ #region Overrides
+
+ public override bool CanROI => false;
+
+ public override bool CanCancel => false;
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "mdi-arrow-expand-up";
+ public override string Title => "Lift height";
+ public override string Description =>
+ "Generates test models with various strategies and increments to measure the optimal lift height or peel forces for layers given the printed area.\n" +
+ "You must have a tool to measure the lift height / forces as it moves up, record the values to determine the lowest safe value for the lift.\n" +
+ "After find the height where it peels, you must give from 1mm to 2mm more for safeness.\n" +
+ "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
+ "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
+
+ public override string ConfirmationText =>
+ $"generate the lift height test?";
+
+ public override string ProgressTitle =>
+ $"Generating the lift height test";
+
+ public override string ProgressAction => "Generated";
+
+ public override string? ValidateInternally()
{
- #region Members
- private decimal _layerHeight;
- private ushort _bottomLayers = 3;
- private ushort _normalLayers = 2;
- private decimal _bottomExposure;
- private decimal _normalExposure;
- private decimal _bottomLiftHeight;
- private decimal _liftHeight;
- private decimal _bottomLiftSpeed;
- private decimal _liftSpeed;
- private decimal _retractSpeed;
- private ushort _leftRightMargin = 200;
- private ushort _topBottomMargin = 200;
- private bool _decreaseImage = true;
- private byte _decreaseImageFactor = 10;
- private byte _minimumImageFactor = 10;
-
- #endregion
-
- #region Overrides
-
- public override bool CanROI => false;
-
- public override bool CanCancel => false;
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
-
- public override string Title => "Lift height";
- public override string Description =>
- "Generates test models with various strategies and increments to measure the optimal lift height or peel forces for layers given the printed area.\n" +
- "You must have a tool to measure the lift height / forces as it moves up, record the values to determine the lowest safe value for the lift.\n" +
- "After find the height where it peels, you must give from 1mm to 2mm more for safeness.\n" +
- "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
- "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
-
- public override string ConfirmationText =>
- $"generate the lift height test?";
-
- public override string ProgressTitle =>
- $"Generating the lift height test";
-
- public override string ProgressAction => "Generated";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ var sb = new StringBuilder();
- if (SlicerFile.ResolutionX - _leftRightMargin * 2 <= 0)
- sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution.");
+ if (SlicerFile.ResolutionX - _leftRightMargin * 2 <= 0)
+ sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution.");
- if (SlicerFile.ResolutionY - _topBottomMargin * 2 <= 0)
- sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution.");
+ if (SlicerFile.ResolutionY - _topBottomMargin * 2 <= 0)
+ sb.AppendLine("The top/bottom margin is too big, it overlaps the screen resolution.");
- if (_decreaseImage)
- {
- if(_decreaseImageFactor is 0 or >= 100)
- sb.AppendLine("The image decrease factor must be between 1 and 99%");
+ if (_decreaseImage)
+ {
+ if(_decreaseImageFactor is 0 or >= 100)
+ sb.AppendLine("The image decrease factor must be between 1 and 99%");
- if(_minimumImageFactor is 0 or >= 100)
- sb.AppendLine("The minimum image decrease factor must be between 1 and 99%");
- }
-
- return sb.ToString();
+ if(_minimumImageFactor is 0 or >= 100)
+ sb.AppendLine("The minimum image decrease factor must be between 1 and 99%");
}
+
+ return sb.ToString();
+ }
- public override string ToString()
- {
- var result = $"[Layer Height: {_layerHeight}] " +
- $"[Layers: {_bottomLayers}/{_normalLayers}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}s] " +
- $"[Lift: {_bottomLiftHeight}/{_liftHeight}mm @ {_bottomLiftSpeed}/{_liftSpeed}mm/min]" +
- $"[Retract speed: {_retractSpeed}mm/min]" +
- $"[Decrease image: {_decreaseImage} @ {_decreaseImageFactor}-{_minimumImageFactor}%]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ public override string ToString()
+ {
+ var result = $"[Layer Height: {_layerHeight}] " +
+ $"[Layers: {_bottomLayers}/{_normalLayers}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}s] " +
+ $"[Lift: {_bottomLiftHeight}/{_liftHeight}mm @ {_bottomLiftSpeed}/{_liftSpeed}mm/min]" +
+ $"[Retract speed: {_retractSpeed}mm/min]" +
+ $"[Decrease image: {_decreaseImage} @ {_decreaseImageFactor}-{_minimumImageFactor}%]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public decimal LayerHeight
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(BottomHeight));
- RaisePropertyChanged(nameof(NormalHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- }
+ if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(BottomHeight));
+ RaisePropertyChanged(nameof(NormalHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
}
+ }
- public ushort Microns => (ushort) (LayerHeight * 1000);
+ public ushort Microns => (ushort) (LayerHeight * 1000);
- public ushort BottomLayers
+ public ushort BottomLayers
+ {
+ get => _bottomLayers;
+ set
{
- get => _bottomLayers;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
- RaisePropertyChanged(nameof(BottomHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
+ RaisePropertyChanged(nameof(BottomHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public ushort NormalLayers
+ public ushort NormalLayers
+ {
+ get => _normalLayers;
+ set
{
- get => _normalLayers;
- set
- {
- if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return;
- RaisePropertyChanged(nameof(NormalHeight));
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ if (!RaiseAndSetIfChanged(ref _normalLayers, value)) return;
+ RaisePropertyChanged(nameof(NormalHeight));
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public uint LayerCount
+ public uint LayerCount
+ {
+ get
{
- get
+ uint layerCount = (uint)(_bottomLayers + _normalLayers);
+ if (_decreaseImage)
{
- uint layerCount = (uint)(_bottomLayers + _normalLayers);
- if (_decreaseImage)
- {
- layerCount += (100u - _minimumImageFactor) / _decreaseImageFactor;
- //layerCount += (uint)Math.Ceiling((100.0 - _minimumImageFactor - _decreaseImageFactor) / _decreaseImageFactor);
- //for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor)
- // layerCount++;
- }
- return layerCount;
+ layerCount += (100u - _minimumImageFactor) / _decreaseImageFactor;
+ //layerCount += (uint)Math.Ceiling((100.0 - _minimumImageFactor - _decreaseImageFactor) / _decreaseImageFactor);
+ //for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor)
+ // layerCount++;
}
+ return layerCount;
}
+ }
- public decimal BottomHeight => Layer.RoundHeight(_layerHeight * _bottomLayers);
- public decimal NormalHeight => Layer.RoundHeight(_layerHeight * (LayerCount - _bottomLayers));
+ public decimal BottomHeight => Layer.RoundHeight(_layerHeight * _bottomLayers);
+ public decimal NormalHeight => Layer.RoundHeight(_layerHeight * (LayerCount - _bottomLayers));
- public decimal TotalHeight => BottomHeight + NormalHeight;
+ public decimal TotalHeight => BottomHeight + NormalHeight;
- public decimal BottomExposure
- {
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
- }
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
- public decimal NormalExposure
- {
- get => _normalExposure;
- set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
- }
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
+ }
- public decimal BottomLiftHeight
- {
- get => _bottomLiftHeight;
- set => RaiseAndSetIfChanged(ref _bottomLiftHeight, value);
- }
+ public decimal BottomLiftHeight
+ {
+ get => _bottomLiftHeight;
+ set => RaiseAndSetIfChanged(ref _bottomLiftHeight, value);
+ }
- public decimal LiftHeight
- {
- get => _liftHeight;
- set => RaiseAndSetIfChanged(ref _liftHeight, value);
- }
+ public decimal LiftHeight
+ {
+ get => _liftHeight;
+ set => RaiseAndSetIfChanged(ref _liftHeight, value);
+ }
- public decimal BottomLiftSpeed
- {
- get => _bottomLiftSpeed;
- set => RaiseAndSetIfChanged(ref _bottomLiftSpeed, value);
- }
+ public decimal BottomLiftSpeed
+ {
+ get => _bottomLiftSpeed;
+ set => RaiseAndSetIfChanged(ref _bottomLiftSpeed, value);
+ }
- public decimal LiftSpeed
- {
- get => _liftSpeed;
- set => RaiseAndSetIfChanged(ref _liftSpeed, value);
- }
+ public decimal LiftSpeed
+ {
+ get => _liftSpeed;
+ set => RaiseAndSetIfChanged(ref _liftSpeed, value);
+ }
- public decimal RetractSpeed
- {
- get => _retractSpeed;
- set => RaiseAndSetIfChanged(ref _retractSpeed, value);
- }
+ public decimal RetractSpeed
+ {
+ get => _retractSpeed;
+ set => RaiseAndSetIfChanged(ref _retractSpeed, value);
+ }
- public ushort LeftRightMargin
- {
- get => _leftRightMargin;
- set => RaiseAndSetIfChanged(ref _leftRightMargin, value);
- }
+ public ushort LeftRightMargin
+ {
+ get => _leftRightMargin;
+ set => RaiseAndSetIfChanged(ref _leftRightMargin, value);
+ }
- public ushort MaxLeftRightMargin => (ushort)((SlicerFile.ResolutionX - 100) / 2);
+ public ushort MaxLeftRightMargin => (ushort)((SlicerFile.ResolutionX - 100) / 2);
- public ushort TopBottomMargin
- {
- get => _topBottomMargin;
- set => RaiseAndSetIfChanged(ref _topBottomMargin, value);
- }
+ public ushort TopBottomMargin
+ {
+ get => _topBottomMargin;
+ set => RaiseAndSetIfChanged(ref _topBottomMargin, value);
+ }
- public ushort MaxTopBottomMargin => (ushort) ((SlicerFile.ResolutionY - 100) / 2);
+ public ushort MaxTopBottomMargin => (ushort) ((SlicerFile.ResolutionY - 100) / 2);
- public bool DecreaseImage
+ public bool DecreaseImage
+ {
+ get => _decreaseImage;
+ set
{
- get => _decreaseImage;
- set
- {
- RaiseAndSetIfChanged(ref _decreaseImage, value);
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ RaiseAndSetIfChanged(ref _decreaseImage, value);
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public byte DecreaseImageFactor
+ public byte DecreaseImageFactor
+ {
+ get => _decreaseImageFactor;
+ set
{
- get => _decreaseImageFactor;
- set
- {
- RaiseAndSetIfChanged(ref _decreaseImageFactor, value);
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ RaiseAndSetIfChanged(ref _decreaseImageFactor, value);
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public byte MinimumImageFactor
+ public byte MinimumImageFactor
+ {
+ get => _minimumImageFactor;
+ set
{
- get => _minimumImageFactor;
- set
- {
- RaiseAndSetIfChanged(ref _minimumImageFactor, value);
- RaisePropertyChanged(nameof(TotalHeight));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ RaiseAndSetIfChanged(ref _minimumImageFactor, value);
+ RaisePropertyChanged(nameof(TotalHeight));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public Rectangle WhiteBlock
+ public Rectangle WhiteBlock
+ {
+ get
{
- get
- {
- int width = (int) (SlicerFile.ResolutionX - _leftRightMargin * 2);
- int height = (int) (SlicerFile.ResolutionY - _topBottomMargin * 2);
- int x = (int) (SlicerFile.ResolutionX - width) / 2;
- int y = (int) (SlicerFile.ResolutionY - height) / 2;
+ int width = (int) (SlicerFile.ResolutionX - _leftRightMargin * 2);
+ int height = (int) (SlicerFile.ResolutionY - _topBottomMargin * 2);
+ int x = (int) (SlicerFile.ResolutionX - width) / 2;
+ int y = (int) (SlicerFile.ResolutionY - height) / 2;
- return new Rectangle(x, y, width, height);
- }
+ return new Rectangle(x, y, width, height);
}
+ }
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationCalibrateLiftHeight() { }
+ public OperationCalibrateLiftHeight() { }
- public OperationCalibrateLiftHeight(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationCalibrateLiftHeight(FileFormat slicerFile) : base(slicerFile)
+ { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
- if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
- if (_bottomLiftHeight <= 0) _bottomLiftHeight = (decimal)SlicerFile.BottomLiftHeight;
- if (_liftHeight <= 0) _liftHeight = (decimal)SlicerFile.LiftHeight;
- if (_bottomLiftSpeed <= 0) _bottomLiftSpeed = (decimal) SlicerFile.BottomLiftSpeed;
- if (_liftSpeed <= 0) _liftSpeed = (decimal) SlicerFile.LiftSpeed;
- if (_retractSpeed <= 0) _retractSpeed = (decimal) SlicerFile.RetractSpeed;
- }
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
+ if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ if (_bottomLiftHeight <= 0) _bottomLiftHeight = (decimal)SlicerFile.BottomLiftHeight;
+ if (_liftHeight <= 0) _liftHeight = (decimal)SlicerFile.LiftHeight;
+ if (_bottomLiftSpeed <= 0) _bottomLiftSpeed = (decimal) SlicerFile.BottomLiftSpeed;
+ if (_liftSpeed <= 0) _liftSpeed = (decimal) SlicerFile.LiftSpeed;
+ if (_retractSpeed <= 0) _retractSpeed = (decimal) SlicerFile.RetractSpeed;
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- private bool Equals(OperationCalibrateLiftHeight other)
- {
- return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _bottomLiftHeight == other._bottomLiftHeight && _liftHeight == other._liftHeight && _bottomLiftSpeed == other._bottomLiftSpeed && _liftSpeed == other._liftSpeed && _retractSpeed == other._retractSpeed && _leftRightMargin == other._leftRightMargin && _topBottomMargin == other._topBottomMargin && _decreaseImage == other._decreaseImage && _decreaseImageFactor == other._decreaseImageFactor && _minimumImageFactor == other._minimumImageFactor;
- }
+ private bool Equals(OperationCalibrateLiftHeight other)
+ {
+ return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _normalLayers == other._normalLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _bottomLiftHeight == other._bottomLiftHeight && _liftHeight == other._liftHeight && _bottomLiftSpeed == other._bottomLiftSpeed && _liftSpeed == other._liftSpeed && _retractSpeed == other._retractSpeed && _leftRightMargin == other._leftRightMargin && _topBottomMargin == other._topBottomMargin && _decreaseImage == other._decreaseImage && _decreaseImageFactor == other._decreaseImageFactor && _minimumImageFactor == other._minimumImageFactor;
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationCalibrateLiftHeight other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationCalibrateLiftHeight other && Equals(other);
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_layerHeight);
- hashCode.Add(_bottomLayers);
- hashCode.Add(_normalLayers);
- hashCode.Add(_bottomExposure);
- hashCode.Add(_normalExposure);
- hashCode.Add(_bottomLiftHeight);
- hashCode.Add(_liftHeight);
- hashCode.Add(_bottomLiftSpeed);
- hashCode.Add(_liftSpeed);
- hashCode.Add(_retractSpeed);
- hashCode.Add(_leftRightMargin);
- hashCode.Add(_topBottomMargin);
- hashCode.Add(_decreaseImage);
- hashCode.Add(_decreaseImageFactor);
- hashCode.Add(_minimumImageFactor);
- return hashCode.ToHashCode();
- }
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_layerHeight);
+ hashCode.Add(_bottomLayers);
+ hashCode.Add(_normalLayers);
+ hashCode.Add(_bottomExposure);
+ hashCode.Add(_normalExposure);
+ hashCode.Add(_bottomLiftHeight);
+ hashCode.Add(_liftHeight);
+ hashCode.Add(_bottomLiftSpeed);
+ hashCode.Add(_liftSpeed);
+ hashCode.Add(_retractSpeed);
+ hashCode.Add(_leftRightMargin);
+ hashCode.Add(_topBottomMargin);
+ hashCode.Add(_decreaseImage);
+ hashCode.Add(_decreaseImageFactor);
+ hashCode.Add(_minimumImageFactor);
+ return hashCode.ToHashCode();
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- /// <summary>
- /// Gets the bottom and normal layers, 0 = bottom | 1 = normal
- /// </summary>
- /// <returns></returns>
- public Mat[] GetLayers()
- {
- var layers = new Mat[1];
+ /// <summary>
+ /// Gets the bottom and normal layers, 0 = bottom | 1 = normal
+ /// </summary>
+ /// <returns></returns>
+ public Mat[] GetLayers()
+ {
+ var layers = new Mat[1];
- layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution);
- CvInvoke.Rectangle(layers[0], WhiteBlock, EmguExtensions.WhiteColor, -1);
+ layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution);
+ CvInvoke.Rectangle(layers[0], WhiteBlock, EmguExtensions.WhiteColor, -1);
- return layers;
- }
+ return layers;
+ }
- public Mat GetThumbnail()
- {
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "Lift Height Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- //CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "Lift Height Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
+ CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ //CvInvoke.PutText(thumbnail, $"{ObjectCount} Objects", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- return thumbnail;
- }
+ return thumbnail;
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- progress.ItemCount = LayerCount;
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = LayerCount;
- var newLayers = new Layer[LayerCount];
+ var newLayers = new Layer[LayerCount];
- var layers = GetLayers();
- progress++;
+ var layers = GetLayers();
+ progress++;
- var layer = new Layer(0, layers[0], SlicerFile.LayerManager)
- {
- IsModified = true
- };
+ var layer = new Layer(0, layers[0], SlicerFile)
+ {
+ IsModified = true
+ };
- uint layerIndex = 0;
- for (; layerIndex < _bottomLayers + _normalLayers; layerIndex++)
- {
- newLayers[layerIndex] = layer.Clone();
- progress++;
- }
+ uint layerIndex = 0;
+ for (; layerIndex < _bottomLayers + _normalLayers; layerIndex++)
+ {
+ newLayers[layerIndex] = layer.Clone();
+ progress++;
+ }
- if (_decreaseImage)
+ if (_decreaseImage)
+ {
+ var rect = WhiteBlock;
+ for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor)
{
- var rect = WhiteBlock;
- for (int factor = 100 - _decreaseImageFactor; factor >= _minimumImageFactor; factor -= _decreaseImageFactor)
- {
- using var mat = layers[0].NewBlank();
+ using var mat = layers[0].NewBlank();
- // size - 100
- // x - factor
+ // size - 100
+ // x - factor
- int width = rect.Width * factor / 100;
- int height = rect.Height * factor / 100;
- int x = (int)(SlicerFile.ResolutionX - width) / 2;
- int y = (int)(SlicerFile.ResolutionY - height) / 2;
+ int width = rect.Width * factor / 100;
+ int height = rect.Height * factor / 100;
+ int x = (int)(SlicerFile.ResolutionX - width) / 2;
+ int y = (int)(SlicerFile.ResolutionY - height) / 2;
- CvInvoke.Rectangle(mat,
- new Rectangle(x, y, width, height),
- EmguExtensions.WhiteColor, -1);
-
- newLayers[layerIndex] = new Layer(0, mat, SlicerFile.LayerManager)
- {
- IsModified = true
- };
-
- layerIndex++;
- progress++;
- }
- }
+ CvInvoke.Rectangle(mat,
+ new Rectangle(x, y, width, height),
+ EmguExtensions.WhiteColor, -1);
- foreach (var mat in layers)
- {
- mat.Dispose();
+ newLayers[layerIndex] = new Layer(0, mat, SlicerFile)
+ {
+ IsModified = true
+ };
+
+ layerIndex++;
+ progress++;
}
+ }
+
+ foreach (var mat in layers)
+ {
+ mat.Dispose();
+ }
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(GetThumbnail());
+ if (SlicerFile.ThumbnailsCount > 0)
+ SlicerFile.SetThumbnails(GetThumbnail());
- progress++;
+ progress++;
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)_layerHeight;
- SlicerFile.BottomExposureTime = (float)_bottomExposure;
- SlicerFile.ExposureTime = (float)_normalExposure;
- SlicerFile.BottomLiftHeight = (float)_bottomLiftHeight;
- SlicerFile.LiftHeight = (float)_liftHeight;
- SlicerFile.BottomLiftSpeed = (float)_bottomLiftSpeed;
- SlicerFile.LiftSpeed = (float)_liftSpeed;
- SlicerFile.RetractSpeed = (float)_retractSpeed;
- SlicerFile.BottomLayerCount = _bottomLayers;
- SlicerFile.TransitionLayerCount = 0;
-
- SlicerFile.LayerManager.Layers = newLayers;
- }, true);
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerHeight = (float)_layerHeight;
+ SlicerFile.BottomExposureTime = (float)_bottomExposure;
+ SlicerFile.ExposureTime = (float)_normalExposure;
+ SlicerFile.BottomLiftHeight = (float)_bottomLiftHeight;
+ SlicerFile.LiftHeight = (float)_liftHeight;
+ SlicerFile.BottomLiftSpeed = (float)_bottomLiftSpeed;
+ SlicerFile.LiftSpeed = (float)_liftSpeed;
+ SlicerFile.RetractSpeed = (float)_retractSpeed;
+ SlicerFile.BottomLayerCount = _bottomLayers;
+ SlicerFile.TransitionLayerCount = 0;
+
+ SlicerFile.Layers = newLayers;
+ }, true);
- return !progress.Token.IsCancellationRequested;
- }
-
- #endregion
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs
index e7824a1..7794d41 100644
--- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs
+++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs
@@ -6,441 +6,440 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationCalibrateStressTower : Operation
{
- [Serializable]
- public sealed class OperationCalibrateStressTower : Operation
+ #region Members
+ private decimal _displayWidth;
+ private decimal _displayHeight;
+ private decimal _layerHeight;
+ private ushort _bottomLayers;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+ private decimal _baseDiameter = 30;
+ private decimal _baseHeight = 3;
+ private decimal _bodyHeight = 50;
+ private decimal _ceilHeight = 3;
+ private byte _chamferLayers = 6;
+ private bool _enableAntiAliasing = true;
+ private bool _mirrorOutput;
+ private byte _spirals = 2;
+ private decimal _spiralDiameter = 2;
+ private SpiralDirections _spiralDirection = SpiralDirections.Both;
+ private decimal _spiralAngleStepPerLayer = 1;
+
+ #endregion
+
+ #region Overrides
+
+ public override bool CanROI => false;
+
+ public override bool CanCancel => false;
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "fas fa-chess-rook";
+ public override string Title => "Stress tower";
+ public override string Description =>
+ "Generates a stress tower to test the printer capabilities.\n" +
+ "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
+
+ public override string ConfirmationText =>
+ $"generate the stress tower?";
+
+ public override string ProgressTitle =>
+ $"Generating the stress tower";
+
+ public override string ProgressAction => "Generated";
+
+ public override string? ValidateInternally()
{
- #region Members
- private decimal _displayWidth;
- private decimal _displayHeight;
- private decimal _layerHeight;
- private ushort _bottomLayers;
- private decimal _bottomExposure;
- private decimal _normalExposure;
- private decimal _baseDiameter = 30;
- private decimal _baseHeight = 3;
- private decimal _bodyHeight = 50;
- private decimal _ceilHeight = 3;
- private byte _chamferLayers = 6;
- private bool _enableAntiAliasing = true;
- private bool _mirrorOutput;
- private byte _spirals = 2;
- private decimal _spiralDiameter = 2;
- private SpiralDirections _spiralDirection = SpiralDirections.Both;
- private decimal _spiralAngleStepPerLayer = 1;
-
- #endregion
-
- #region Overrides
-
- public override bool CanROI => false;
+ var sb = new StringBuilder();
- public override bool CanCancel => false;
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
-
- public override string Title => "Stress tower";
- public override string Description =>
- "Generates a stress tower to test the printer capabilities.\n" +
- "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
-
- public override string ConfirmationText =>
- $"generate the stress tower?";
-
- public override string ProgressTitle =>
- $"Generating the stress tower";
-
- public override string ProgressAction => "Generated";
-
- public override string ValidateInternally()
+ if (_displayWidth <= 0)
{
- var sb = new StringBuilder();
-
- if (_displayWidth <= 0)
- {
- sb.AppendLine("Display width must be a positive value.");
- }
-
- if (_displayHeight <= 0)
- {
- sb.AppendLine("Display height must be a positive value.");
- }
-
- return sb.ToString();
+ sb.AppendLine("Display width must be a positive value.");
}
- public override string ToString()
+ if (_displayHeight <= 0)
{
- var result = $"[Layer Height: {_layerHeight}] " +
- $"[Bottom layers: {_bottomLayers}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
- $"[Base: H:{_baseHeight} D:{_baseDiameter}] " +
- $"[Ceil: {_ceilHeight}] [Body: {_bodyHeight}] " +
- $"[Chamfer: {_chamferLayers}] " +
- $"[Spirals: {_spirals} Dir: {_spiralDirection} D:{_spiralDiameter} Angle: {_spiralAngleStepPerLayer}º]" +
- $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("Display height must be a positive value.");
}
+
+ return sb.ToString();
+ }
- #endregion
+ public override string ToString()
+ {
+ var result = $"[Layer Height: {_layerHeight}] " +
+ $"[Bottom layers: {_bottomLayers}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
+ $"[Base: H:{_baseHeight} D:{_baseDiameter}] " +
+ $"[Ceil: {_ceilHeight}] [Body: {_bodyHeight}] " +
+ $"[Chamfer: {_chamferLayers}] " +
+ $"[Spirals: {_spirals} Dir: {_spiralDirection} D:{_spiralDiameter} Angle: {_spiralAngleStepPerLayer}º]" +
+ $"[AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #region Constructor
+ #endregion
- public OperationCalibrateStressTower() { }
+ #region Constructor
- public OperationCalibrateStressTower(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationCalibrateStressTower() { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
- if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
- if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
- _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
-
- if (SlicerFile.DisplayWidth > 0)
- DisplayWidth = (decimal)SlicerFile.DisplayWidth;
- if (SlicerFile.DisplayHeight > 0)
- DisplayHeight = (decimal)SlicerFile.DisplayHeight;
- }
+ public OperationCalibrateStressTower(FileFormat slicerFile) : base(slicerFile)
+ { }
- #endregion
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if(_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
+ if(_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
+ if(_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if(_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
+
+ if (SlicerFile.DisplayWidth > 0)
+ DisplayWidth = (decimal)SlicerFile.DisplayWidth;
+ if (SlicerFile.DisplayHeight > 0)
+ DisplayHeight = (decimal)SlicerFile.DisplayHeight;
+ }
+
+ #endregion
- #region Properties
+ #region Properties
- public decimal DisplayWidth
+ public decimal DisplayWidth
+ {
+ get => _displayWidth;
+ set
{
- get => _displayWidth;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
- }
+ if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
}
+ }
- public decimal DisplayHeight
+ public decimal DisplayHeight
+ {
+ get => _displayHeight;
+ set
{
- get => _displayHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
- }
+ if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
}
+ }
- public decimal LayerHeight
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- RaisePropertyChanged(nameof(LayerCount));
- }
+ if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
+ RaisePropertyChanged(nameof(LayerCount));
}
+ }
- public ushort Microns => (ushort)(LayerHeight * 1000);
+ public ushort Microns => (ushort)(LayerHeight * 1000);
- public ushort BottomLayers
+ public ushort BottomLayers
+ {
+ get => _bottomLayers;
+ set
{
- get => _bottomLayers;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
}
+ }
- public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
+ public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
- public decimal BottomExposure
- {
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
- }
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
- public decimal NormalExposure
- {
- get => _normalExposure;
- set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
- }
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
+ }
- public uint LayerCount => (uint)((_baseHeight + _bodyHeight + _ceilHeight) / LayerHeight);
+ public uint LayerCount => (uint)((_baseHeight + _bodyHeight + _ceilHeight) / LayerHeight);
- public decimal TotalHeight => _baseHeight + _bodyHeight + _ceilHeight;
+ public decimal TotalHeight => _baseHeight + _bodyHeight + _ceilHeight;
- public decimal BaseDiameter
- {
- get => _baseDiameter;
- set => RaiseAndSetIfChanged(ref _baseDiameter, value);
- }
+ public decimal BaseDiameter
+ {
+ get => _baseDiameter;
+ set => RaiseAndSetIfChanged(ref _baseDiameter, value);
+ }
- public decimal BaseHeight
+ public decimal BaseHeight
+ {
+ get => _baseHeight;
+ set
{
- get => _baseHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _baseHeight, value)) return;
- RaisePropertyChanged(nameof(TotalHeight));
- }
+ if(!RaiseAndSetIfChanged(ref _baseHeight, value)) return;
+ RaisePropertyChanged(nameof(TotalHeight));
}
+ }
- public decimal BodyHeight
+ public decimal BodyHeight
+ {
+ get => _bodyHeight;
+ set
{
- get => _bodyHeight;
- set
- {
- if (!RaiseAndSetIfChanged(ref _bodyHeight, value)) return;
- RaisePropertyChanged(nameof(TotalHeight));
- }
+ if (!RaiseAndSetIfChanged(ref _bodyHeight, value)) return;
+ RaisePropertyChanged(nameof(TotalHeight));
}
+ }
- public decimal CeilHeight
+ public decimal CeilHeight
+ {
+ get => _ceilHeight;
+ set
{
- get => _ceilHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _ceilHeight, value)) return;
- RaisePropertyChanged(nameof(TotalHeight));
- }
+ if(!RaiseAndSetIfChanged(ref _ceilHeight, value)) return;
+ RaisePropertyChanged(nameof(TotalHeight));
}
+ }
- public byte ChamferLayers
- {
- get => _chamferLayers;
- set => RaiseAndSetIfChanged(ref _chamferLayers, value);
- }
+ public byte ChamferLayers
+ {
+ get => _chamferLayers;
+ set => RaiseAndSetIfChanged(ref _chamferLayers, value);
+ }
- public bool EnableAntiAliasing
- {
- get => _enableAntiAliasing;
- set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
- }
+ public bool EnableAntiAliasing
+ {
+ get => _enableAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
+ }
- public bool MirrorOutput
- {
- get => _mirrorOutput;
- set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
- }
+ public bool MirrorOutput
+ {
+ get => _mirrorOutput;
+ set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
+ }
- public byte Spirals
- {
- get => _spirals;
- set => RaiseAndSetIfChanged(ref _spirals, value);
- }
+ public byte Spirals
+ {
+ get => _spirals;
+ set => RaiseAndSetIfChanged(ref _spirals, value);
+ }
- public decimal SpiralDiameter
- {
- get => _spiralDiameter;
- set => RaiseAndSetIfChanged(ref _spiralDiameter, value);
- }
+ public decimal SpiralDiameter
+ {
+ get => _spiralDiameter;
+ set => RaiseAndSetIfChanged(ref _spiralDiameter, value);
+ }
- public SpiralDirections SpiralDirection
- {
- get => _spiralDirection;
- set => RaiseAndSetIfChanged(ref _spiralDirection, value);
- }
+ public SpiralDirections SpiralDirection
+ {
+ get => _spiralDirection;
+ set => RaiseAndSetIfChanged(ref _spiralDirection, value);
+ }
- public decimal SpiralAngleStepPerLayer
- {
- get => _spiralAngleStepPerLayer;
- set => RaiseAndSetIfChanged(ref _spiralAngleStepPerLayer, value);
- }
+ public decimal SpiralAngleStepPerLayer
+ {
+ get => _spiralAngleStepPerLayer;
+ set => RaiseAndSetIfChanged(ref _spiralAngleStepPerLayer, value);
+ }
- #endregion
+ #endregion
- #region Enums
+ #region Enums
- public enum SpiralDirections : byte
- {
- Clockwise,
- Alternate,
- Both
- }
+ public enum SpiralDirections : byte
+ {
+ Clockwise,
+ Alternate,
+ Both
+ }
- public static Array SpiralDirectionsItems => Enum.GetValues(typeof(SpiralDirections));
- #endregion
+ public static Array SpiralDirectionsItems => Enum.GetValues(typeof(SpiralDirections));
+ #endregion
- #region Equality
+ #region Equality
- private bool Equals(OperationCalibrateStressTower other)
- {
- return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _baseDiameter == other._baseDiameter && _baseHeight == other._baseHeight && _bodyHeight == other._bodyHeight && _ceilHeight == other._ceilHeight && _chamferLayers == other._chamferLayers && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _spirals == other._spirals && _spiralDiameter == other._spiralDiameter && _spiralDirection == other._spiralDirection && _spiralAngleStepPerLayer == other._spiralAngleStepPerLayer;
- }
+ private bool Equals(OperationCalibrateStressTower other)
+ {
+ return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _baseDiameter == other._baseDiameter && _baseHeight == other._baseHeight && _bodyHeight == other._bodyHeight && _ceilHeight == other._ceilHeight && _chamferLayers == other._chamferLayers && _enableAntiAliasing == other._enableAntiAliasing && _mirrorOutput == other._mirrorOutput && _spirals == other._spirals && _spiralDiameter == other._spiralDiameter && _spiralDirection == other._spiralDirection && _spiralAngleStepPerLayer == other._spiralAngleStepPerLayer;
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationCalibrateStressTower other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationCalibrateStressTower other && Equals(other);
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_layerHeight);
- hashCode.Add(_bottomLayers);
- hashCode.Add(_bottomExposure);
- hashCode.Add(_normalExposure);
- hashCode.Add(_baseDiameter);
- hashCode.Add(_baseHeight);
- hashCode.Add(_bodyHeight);
- hashCode.Add(_ceilHeight);
- hashCode.Add(_chamferLayers);
- hashCode.Add(_enableAntiAliasing);
- hashCode.Add(_mirrorOutput);
- hashCode.Add(_spirals);
- hashCode.Add(_spiralDiameter);
- hashCode.Add((int) _spiralDirection);
- hashCode.Add(_spiralAngleStepPerLayer);
- return hashCode.ToHashCode();
- }
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_layerHeight);
+ hashCode.Add(_bottomLayers);
+ hashCode.Add(_bottomExposure);
+ hashCode.Add(_normalExposure);
+ hashCode.Add(_baseDiameter);
+ hashCode.Add(_baseHeight);
+ hashCode.Add(_bodyHeight);
+ hashCode.Add(_ceilHeight);
+ hashCode.Add(_chamferLayers);
+ hashCode.Add(_enableAntiAliasing);
+ hashCode.Add(_mirrorOutput);
+ hashCode.Add(_spirals);
+ hashCode.Add(_spiralDiameter);
+ hashCode.Add((int) _spiralDirection);
+ hashCode.Add(_spiralAngleStepPerLayer);
+ return hashCode.ToHashCode();
+ }
- #endregion
+ #endregion
- #region Methods
- public Mat[] GetLayers()
- {
- var layers = new Mat[LayerCount];
+ #region Methods
+ public Mat[] GetLayers()
+ {
+ var layers = new Mat[LayerCount];
- Slicer.Slicer slicer = new(SlicerFile.Resolution, new SizeF((float) DisplayWidth, (float) DisplayHeight));
- Point center = new(SlicerFile.Resolution.Width / 2, SlicerFile.Resolution.Height / 2);
- uint baseRadius = slicer.PixelsFromMillimeters(_baseDiameter) / 2;
- uint baseLayers = (ushort)(_baseHeight / _layerHeight);
- uint bodyLayers = (ushort)(_bodyHeight / _layerHeight);
- uint spiralLayers = (uint)(_spiralDiameter / _layerHeight);
- uint ceilLayers = (ushort)(_ceilHeight / _layerHeight);
- uint currrentlayer = baseLayers;
-
- decimal spiralOffsetAngle = 360m / _spirals;
- uint spiralRadius = slicer.PixelsFromMillimeters(_spiralDiameter) / 2;
-
- /*const FontFace fontFace = FontFace.HersheyDuplex;
- const double fontScale = 1;
- const byte fontThickness = 2;
- LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
-
- var anchor = new Point(-1, -1);
- var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);*/
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
- {
- layers[layerIndex] = EmguExtensions.InitMat(SlicerFile.Resolution);
- });
+ Slicer.Slicer slicer = new(SlicerFile.Resolution, new SizeF((float) DisplayWidth, (float) DisplayHeight));
+ Point center = new(SlicerFile.Resolution.Width / 2, SlicerFile.Resolution.Height / 2);
+ uint baseRadius = slicer.PixelsFromMillimeters(_baseDiameter) / 2;
+ uint baseLayers = (ushort)(_baseHeight / _layerHeight);
+ uint bodyLayers = (ushort)(_bodyHeight / _layerHeight);
+ uint spiralLayers = (uint)(_spiralDiameter / _layerHeight);
+ uint ceilLayers = (ushort)(_ceilHeight / _layerHeight);
+ uint currrentlayer = baseLayers;
+
+ decimal spiralOffsetAngle = 360m / _spirals;
+ uint spiralRadius = slicer.PixelsFromMillimeters(_spiralDiameter) / 2;
+
+ /*const FontFace fontFace = FontFace.HersheyDuplex;
+ const double fontScale = 1;
+ const byte fontThickness = 2;
+ LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
+
+ var anchor = new Point(-1, -1);
+ var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);*/
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ layers[layerIndex] = EmguExtensions.InitMat(SlicerFile.Resolution);
+ });
- Parallel.For(0, baseLayers, CoreSettings.ParallelOptions, layerIndex =>
- {
- int chamferOffset = (int) Math.Max(0, _chamferLayers - layerIndex);
- CvInvoke.Circle(layers[layerIndex], center, (int) baseRadius - chamferOffset, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- });
+ Parallel.For(0, baseLayers, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ int chamferOffset = (int) Math.Max(0, _chamferLayers - layerIndex);
+ CvInvoke.Circle(layers[layerIndex], center, (int) baseRadius - chamferOffset, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ });
- Parallel.For(0, baseLayers+bodyLayers, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.For(0, baseLayers+bodyLayers, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ decimal angle = (layerIndex * _spiralAngleStepPerLayer) % 360m;
+ for (byte spiral = 0; spiral < _spirals; spiral++)
{
- decimal angle = (layerIndex * _spiralAngleStepPerLayer) % 360m;
- for (byte spiral = 0; spiral < _spirals; spiral++)
+ decimal spiralAngle = (spiralOffsetAngle * spiral + angle) % 360;
+ if (_spiralDirection == SpiralDirections.Alternate && spiral % 2 == 0)
{
- decimal spiralAngle = (spiralOffsetAngle * spiral + angle) % 360;
- if (_spiralDirection == SpiralDirections.Alternate && spiral % 2 == 0)
- {
- spiralAngle = -spiralAngle;
- }
- Point location = new((int) (center.X - baseRadius + spiralRadius), center.Y);
- var locationCW = location.Rotate((double) spiralAngle, center);
- var locationCCW = location.Rotate((double) -spiralAngle, center);
+ spiralAngle = -spiralAngle;
+ }
+ Point location = new((int) (center.X - baseRadius + spiralRadius), center.Y);
+ var locationCW = location.Rotate((double) spiralAngle, center);
+ var locationCCW = location.Rotate((double) -spiralAngle, center);
- uint maxLayer = (uint) Math.Min(layerIndex + spiralLayers, baseLayers + bodyLayers);
+ uint maxLayer = (uint) Math.Min(layerIndex + spiralLayers, baseLayers + bodyLayers);
- for (uint spiralLayerIndex = (uint) layerIndex; spiralLayerIndex < maxLayer; spiralLayerIndex++)
+ for (uint spiralLayerIndex = (uint) layerIndex; spiralLayerIndex < maxLayer; spiralLayerIndex++)
+ {
+ CvInvoke.Circle(layers[spiralLayerIndex], locationCW, (int)spiralRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ if (_spiralDirection == SpiralDirections.Both)
{
- CvInvoke.Circle(layers[spiralLayerIndex], locationCW, (int)spiralRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- if (_spiralDirection == SpiralDirections.Both)
- {
- spiralAngle = -spiralAngle;
- CvInvoke.Circle(layers[spiralLayerIndex], locationCCW, (int)spiralRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- }
+ spiralAngle = -spiralAngle;
+ CvInvoke.Circle(layers[spiralLayerIndex], locationCCW, (int)spiralRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
}
}
- });
-
- currrentlayer += bodyLayers;
-
- Parallel.For(0, ceilLayers, CoreSettings.ParallelOptions, i =>
- {
- uint layerIndex = (uint)(currrentlayer + i);
- CvInvoke.Circle(layers[layerIndex], center, (int)baseRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- });
+ }
+ });
+ currrentlayer += bodyLayers;
+ Parallel.For(0, ceilLayers, CoreSettings.ParallelOptions, i =>
+ {
+ uint layerIndex = (uint)(currrentlayer + i);
+ CvInvoke.Circle(layers[layerIndex], center, (int)baseRadius, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
+ });
- if (_mirrorOutput)
- {
- var flip = SlicerFile.DisplayMirror;
- if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
- Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
- }
- return layers;
- }
- public Mat GetThumbnail()
+ if (_mirrorOutput)
{
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "Stress Tower", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- CvInvoke.PutText(thumbnail, $"{_spirals} Spirals @ {_spiralAngleStepPerLayer}deg", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- return thumbnail;
+ var flip = SlicerFile.DisplayMirror;
+ if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
+ Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
}
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- progress.ItemCount = LayerCount;
- var newLayers = new Layer[LayerCount];
+ return layers;
+ }
- var layers = GetLayers();
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "Stress Tower", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
+ CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ CvInvoke.PutText(thumbnail, $"{_spirals} Spirals @ {_spiralAngleStepPerLayer}deg", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ return thumbnail;
+ }
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
- {
- newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile.LayerManager) {IsModified = true};
- layers[layerIndex].Dispose();
- progress.LockAndIncrement();
- });
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = LayerCount;
+ var newLayers = new Layer[LayerCount];
-
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(GetThumbnail());
+ var layers = GetLayers();
+
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile) {IsModified = true};
+ layers[layerIndex].Dispose();
+ progress.LockAndIncrement();
+ });
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)LayerHeight;
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.BottomLayerCount = BottomLayers;
- SlicerFile.LayerManager.Layers = newLayers;
- }, true);
- return !progress.Token.IsCancellationRequested;
- }
+ if (SlicerFile.ThumbnailsCount > 0)
+ SlicerFile.SetThumbnails(GetThumbnail());
- #endregion
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerHeight = (float)LayerHeight;
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.BottomLayerCount = BottomLayers;
+ SlicerFile.Layers = newLayers;
+ }, true);
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs
index 8b43115..6b62762 100644
--- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs
+++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs
@@ -6,756 +6,755 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public sealed class OperationCalibrateTolerance : Operation
- {
- #region Members
- private decimal _displayWidth;
- private decimal _displayHeight;
- private decimal _layerHeight;
- private ushort _bottomLayers;
- private decimal _bottomExposure;
- private decimal _normalExposure;
- private decimal _zSize = 10;
- private ushort _topBottomMargin = 100;
- private ushort _leftRightMargin = 100;
- private byte _chamferLayers = 4;
- private byte _erodeBottomIterations = 4;
- private Shapes _shape = Shapes.Circle;
- private ushort _partMargin = 50;
- private bool _outputSameDiameterPart = true;
- private bool _fuseParts;
- private bool _enableAntiAliasing = true;
- private bool _mirrorOutput;
-
- private decimal _femaleDiameter = 16;
- private decimal _femaleHoleDiameter = 10;
-
- private ushort _maleThinnerModels = 5;
- private decimal _maleThinnerOffset;
- private decimal _maleThinnerStep = -0.1M;
- private ushort _maleThickerModels;
- private decimal _maleThickerOffset;
- private decimal _maleThickerStep = 0.1M;
- #endregion
-
- #region Overrides
-
- public override bool CanROI => false;
-
- public override bool CanCancel => false;
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
-
- public override string Title => "Tolerance";
- public override string Description =>
- "Generates test models with various strategies and increments to verify the part tolerances.\n" +
- "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
- "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
-
- public override string ConfirmationText =>
- $"generate the tolerance test?";
-
- public override string ProgressTitle =>
- $"Generating the tolerance test";
-
- public override string ProgressAction => "Generated";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+namespace UVtools.Core.Operations;
- if (_displayWidth <= 0)
- {
- sb.AppendLine("Display width must be a positive value.");
- }
+[Serializable]
+public sealed class OperationCalibrateTolerance : Operation
+{
+ #region Members
+ private decimal _displayWidth;
+ private decimal _displayHeight;
+ private decimal _layerHeight;
+ private ushort _bottomLayers;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+ private decimal _zSize = 10;
+ private ushort _topBottomMargin = 100;
+ private ushort _leftRightMargin = 100;
+ private byte _chamferLayers = 4;
+ private byte _erodeBottomIterations = 4;
+ private Shapes _shape = Shapes.Circle;
+ private ushort _partMargin = 50;
+ private bool _outputSameDiameterPart = true;
+ private bool _fuseParts;
+ private bool _enableAntiAliasing = true;
+ private bool _mirrorOutput;
+
+ private decimal _femaleDiameter = 16;
+ private decimal _femaleHoleDiameter = 10;
+
+ private ushort _maleThinnerModels = 5;
+ private decimal _maleThinnerOffset;
+ private decimal _maleThinnerStep = -0.1M;
+ private ushort _maleThickerModels;
+ private decimal _maleThickerOffset;
+ private decimal _maleThickerStep = 0.1M;
+ #endregion
+
+ #region Overrides
+
+ public override bool CanROI => false;
+
+ public override bool CanCancel => false;
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "fas fa-dot-circle";
+ public override string Title => "Tolerance";
+ public override string Description =>
+ "Generates test models with various strategies and increments to verify the part tolerances.\n" +
+ "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
+ "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
+
+ public override string ConfirmationText =>
+ $"generate the tolerance test?";
+
+ public override string ProgressTitle =>
+ $"Generating the tolerance test";
+
+ public override string ProgressAction => "Generated";
+
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- if (_displayHeight <= 0)
- {
- sb.AppendLine("Display height must be a positive value.");
- }
+ if (_displayWidth <= 0)
+ {
+ sb.AppendLine("Display width must be a positive value.");
+ }
- if (_femaleHoleDiameter >= _femaleDiameter)
- {
- sb.AppendLine("Hole diameter must be smaller than female diameter.");
- }
-
- if (OutputObjects <= 0)
- {
- sb.AppendLine("No objects to output.");
- }
-
- return sb.ToString();
+ if (_displayHeight <= 0)
+ {
+ sb.AppendLine("Display height must be a positive value.");
}
- public override string ToString()
+ if (_femaleHoleDiameter >= _femaleDiameter)
+ {
+ sb.AppendLine("Hole diameter must be smaller than female diameter.");
+ }
+
+ if (OutputObjects <= 0)
{
- var result = $"[Layer Height: {_layerHeight}] " +
- $"[Bottom layers: {_bottomLayers}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
- $"[Z: {_zSize}] " +
- $"[TB:{_topBottomMargin} LR:{_leftRightMargin} PM:{_partMargin}] " +
- $"[Chamfer: {_chamferLayers}] [Erode: {_erodeBottomIterations}] " +
- $"[OSHD: {_outputSameDiameterPart}] [Fuse: {_fuseParts}] [AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]" +
- $"[{_shape}, {_femaleDiameter}/{_femaleHoleDiameter}] " +
- $"[tM: {_maleThinnerModels} O:{_maleThinnerOffset} S:{_maleThinnerStep}] " +
- $"[TM: {_maleThickerModels} O:{_maleThickerOffset} S:{_maleThickerStep}] ";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("No objects to output.");
}
+
+ return sb.ToString();
+ }
+
+ public override string ToString()
+ {
+ var result = $"[Layer Height: {_layerHeight}] " +
+ $"[Bottom layers: {_bottomLayers}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
+ $"[Z: {_zSize}] " +
+ $"[TB:{_topBottomMargin} LR:{_leftRightMargin} PM:{_partMargin}] " +
+ $"[Chamfer: {_chamferLayers}] [Erode: {_erodeBottomIterations}] " +
+ $"[OSHD: {_outputSameDiameterPart}] [Fuse: {_fuseParts}] [AA: {_enableAntiAliasing}] [Mirror: {_mirrorOutput}]" +
+ $"[{_shape}, {_femaleDiameter}/{_femaleHoleDiameter}] " +
+ $"[tM: {_maleThinnerModels} O:{_maleThinnerOffset} S:{_maleThinnerStep}] " +
+ $"[TM: {_maleThickerModels} O:{_maleThickerOffset} S:{_maleThickerStep}] ";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public decimal DisplayWidth
+ public decimal DisplayWidth
+ {
+ get => _displayWidth;
+ set
{
- get => _displayWidth;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(Xppmm));
- }
+ if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(Xppmm));
}
+ }
- public decimal DisplayHeight
+ public decimal DisplayHeight
+ {
+ get => _displayHeight;
+ set
{
- get => _displayHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(Yppmm));
- }
+ if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(Yppmm));
}
+ }
- public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0;
- public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0;
+ public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0;
+ public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0;
- public decimal LayerHeight
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- RaisePropertyChanged(nameof(LayerCount));
- RaisePropertyChanged(nameof(RealZSize));
- //RaisePropertyChanged(nameof(ObservedZSize));
- }
+ if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
+ RaisePropertyChanged(nameof(LayerCount));
+ RaisePropertyChanged(nameof(RealZSize));
+ //RaisePropertyChanged(nameof(ObservedZSize));
}
+ }
- public ushort Microns => (ushort)(LayerHeight * 1000);
+ public ushort Microns => (ushort)(LayerHeight * 1000);
- public ushort BottomLayers
+ public ushort BottomLayers
+ {
+ get => _bottomLayers;
+ set
{
- get => _bottomLayers;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
}
+ }
- public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
+ public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
- public decimal BottomExposure
- {
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
- }
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
- public decimal NormalExposure
- {
- get => _normalExposure;
- set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
- }
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
+ }
- public decimal ZSize
+ public decimal ZSize
+ {
+ get => _zSize;
+ set
{
- get => _zSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _zSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(LayerCount));
- RaisePropertyChanged(nameof(RealZSize));
- //RaisePropertyChanged(nameof(ObservedZSize));
- }
+ if(!RaiseAndSetIfChanged(ref _zSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(LayerCount));
+ RaisePropertyChanged(nameof(RealZSize));
+ //RaisePropertyChanged(nameof(ObservedZSize));
}
+ }
- public uint LayerCount => (uint)(ZSize / LayerHeight);
+ public uint LayerCount => (uint)(ZSize / LayerHeight);
- public decimal RealZSize => LayerCount * _layerHeight;
+ public decimal RealZSize => LayerCount * _layerHeight;
- public ushort TopBottomMargin
- {
- get => _topBottomMargin;
- set => RaiseAndSetIfChanged(ref _topBottomMargin, value);
- }
+ public ushort TopBottomMargin
+ {
+ get => _topBottomMargin;
+ set => RaiseAndSetIfChanged(ref _topBottomMargin, value);
+ }
- public ushort LeftRightMargin
- {
- get => _leftRightMargin;
- set => RaiseAndSetIfChanged(ref _leftRightMargin, value);
- }
+ public ushort LeftRightMargin
+ {
+ get => _leftRightMargin;
+ set => RaiseAndSetIfChanged(ref _leftRightMargin, value);
+ }
- public byte ChamferLayers
- {
- get => _chamferLayers;
- set => RaiseAndSetIfChanged(ref _chamferLayers, value);
- }
+ public byte ChamferLayers
+ {
+ get => _chamferLayers;
+ set => RaiseAndSetIfChanged(ref _chamferLayers, value);
+ }
- public byte ErodeBottomIterations
- {
- get => _erodeBottomIterations;
- set => RaiseAndSetIfChanged(ref _erodeBottomIterations, value);
- }
+ public byte ErodeBottomIterations
+ {
+ get => _erodeBottomIterations;
+ set => RaiseAndSetIfChanged(ref _erodeBottomIterations, value);
+ }
- public Shapes Shape
- {
- get => _shape;
- set => RaiseAndSetIfChanged(ref _shape, value);
- }
+ public Shapes Shape
+ {
+ get => _shape;
+ set => RaiseAndSetIfChanged(ref _shape, value);
+ }
- public ushort PartMargin
- {
- get => _partMargin;
- set => RaiseAndSetIfChanged(ref _partMargin, value);
- }
+ public ushort PartMargin
+ {
+ get => _partMargin;
+ set => RaiseAndSetIfChanged(ref _partMargin, value);
+ }
- public bool OutputSameDiameterPart
- {
- get => _outputSameDiameterPart;
- set => RaiseAndSetIfChanged(ref _outputSameDiameterPart, value);
- }
+ public bool OutputSameDiameterPart
+ {
+ get => _outputSameDiameterPart;
+ set => RaiseAndSetIfChanged(ref _outputSameDiameterPart, value);
+ }
- public bool FuseParts
+ public bool FuseParts
+ {
+ get => _fuseParts;
+ set
{
- get => _fuseParts;
- set
+ if(!RaiseAndSetIfChanged(ref _fuseParts, value)) return;
+ if (value)
{
- if(!RaiseAndSetIfChanged(ref _fuseParts, value)) return;
- if (value)
- {
- OutputSameDiameterPart = false;
- MaleThickerModels = 0;
- }
+ OutputSameDiameterPart = false;
+ MaleThickerModels = 0;
}
}
+ }
- public bool EnableAntiAliasing
- {
- get => _enableAntiAliasing;
- set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
- }
+ public bool EnableAntiAliasing
+ {
+ get => _enableAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _enableAntiAliasing, value);
+ }
- public bool MirrorOutput
- {
- get => _mirrorOutput;
- set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
- }
+ public bool MirrorOutput
+ {
+ get => _mirrorOutput;
+ set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
+ }
- public decimal FemaleDiameter
+ public decimal FemaleDiameter
+ {
+ get => _femaleDiameter;
+ set
{
- get => _femaleDiameter;
- set
- {
- if(!RaiseAndSetIfChanged(ref _femaleDiameter, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(FemaleDiameterXPixels));
- RaisePropertyChanged(nameof(FemaleDiameterYPixels));
- RaisePropertyChanged(nameof(FemaleDiameterRealXSize));
- RaisePropertyChanged(nameof(FemaleDiameterRealYSize));
- }
+ if(!RaiseAndSetIfChanged(ref _femaleDiameter, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(FemaleDiameterXPixels));
+ RaisePropertyChanged(nameof(FemaleDiameterYPixels));
+ RaisePropertyChanged(nameof(FemaleDiameterRealXSize));
+ RaisePropertyChanged(nameof(FemaleDiameterRealYSize));
}
+ }
- public uint FemaleDiameterXPixels => (uint)(_femaleDiameter * Xppmm);
- public uint FemaleDiameterYPixels => (uint)(_femaleDiameter * Yppmm);
+ public uint FemaleDiameterXPixels => (uint)(_femaleDiameter * Xppmm);
+ public uint FemaleDiameterYPixels => (uint)(_femaleDiameter * Yppmm);
- public decimal FemaleDiameterRealXSize
+ public decimal FemaleDiameterRealXSize
+ {
+ get
{
- get
- {
- decimal pixels = _femaleDiameter * Xppmm;
- return pixels <= 0 ? 0 : Math.Round(_femaleDiameter - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
- }
+ decimal pixels = _femaleDiameter * Xppmm;
+ return pixels <= 0 ? 0 : Math.Round(_femaleDiameter - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
}
+ }
- public decimal FemaleDiameterRealYSize
+ public decimal FemaleDiameterRealYSize
+ {
+ get
{
- get
- {
- decimal pixels = _femaleDiameter * Yppmm;
- return pixels <= 0 ? 0 : Math.Round(_femaleDiameter - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
- }
+ decimal pixels = _femaleDiameter * Yppmm;
+ return pixels <= 0 ? 0 : Math.Round(_femaleDiameter - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
}
+ }
- public decimal FemaleHoleDiameter
+ public decimal FemaleHoleDiameter
+ {
+ get => _femaleHoleDiameter;
+ set
{
- get => _femaleHoleDiameter;
- set
- {
- if(!RaiseAndSetIfChanged(ref _femaleHoleDiameter, value)) return;
- RaisePropertyChanged(nameof(FemaleHoleDiameterXPixels));
- RaisePropertyChanged(nameof(FemaleHoleDiameterYPixels));
- RaisePropertyChanged(nameof(FemaleHoleDiameterRealXSize));
- RaisePropertyChanged(nameof(FemaleHoleDiameterRealYSize));
- }
+ if(!RaiseAndSetIfChanged(ref _femaleHoleDiameter, value)) return;
+ RaisePropertyChanged(nameof(FemaleHoleDiameterXPixels));
+ RaisePropertyChanged(nameof(FemaleHoleDiameterYPixels));
+ RaisePropertyChanged(nameof(FemaleHoleDiameterRealXSize));
+ RaisePropertyChanged(nameof(FemaleHoleDiameterRealYSize));
}
+ }
- public uint FemaleHoleDiameterXPixels => (uint)(_femaleHoleDiameter * Xppmm);
- public uint FemaleHoleDiameterYPixels => (uint)(_femaleHoleDiameter * Yppmm);
+ public uint FemaleHoleDiameterXPixels => (uint)(_femaleHoleDiameter * Xppmm);
+ public uint FemaleHoleDiameterYPixels => (uint)(_femaleHoleDiameter * Yppmm);
- public decimal FemaleHoleDiameterRealXSize
+ public decimal FemaleHoleDiameterRealXSize
+ {
+ get
{
- get
- {
- decimal pixels = _femaleHoleDiameter * Xppmm;
- return pixels <= 0 ? 0 : Math.Round(_femaleHoleDiameter - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
- }
+ decimal pixels = _femaleHoleDiameter * Xppmm;
+ return pixels <= 0 ? 0 : Math.Round(_femaleHoleDiameter - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
}
+ }
- public decimal FemaleHoleDiameterRealYSize
+ public decimal FemaleHoleDiameterRealYSize
+ {
+ get
{
- get
- {
- decimal pixels = _femaleHoleDiameter * Yppmm;
- return pixels <= 0 ? 0 : Math.Round(_femaleHoleDiameter - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
- }
+ decimal pixels = _femaleHoleDiameter * Yppmm;
+ return pixels <= 0 ? 0 : Math.Round(_femaleHoleDiameter - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
}
+ }
- public ushort MaleThinnerModels
- {
- get => _maleThinnerModels;
- set => RaiseAndSetIfChanged(ref _maleThinnerModels, value);
- }
+ public ushort MaleThinnerModels
+ {
+ get => _maleThinnerModels;
+ set => RaiseAndSetIfChanged(ref _maleThinnerModels, value);
+ }
- public decimal MaleThinnerOffset
- {
- get => _maleThinnerOffset;
- set => RaiseAndSetIfChanged(ref _maleThinnerOffset, Math.Round(value, 2));
- }
+ public decimal MaleThinnerOffset
+ {
+ get => _maleThinnerOffset;
+ set => RaiseAndSetIfChanged(ref _maleThinnerOffset, Math.Round(value, 2));
+ }
- public decimal MaleThinnerStep
- {
- get => _maleThinnerStep;
- set => RaiseAndSetIfChanged(ref _maleThinnerStep, Math.Round(value, 2));
- }
+ public decimal MaleThinnerStep
+ {
+ get => _maleThinnerStep;
+ set => RaiseAndSetIfChanged(ref _maleThinnerStep, Math.Round(value, 2));
+ }
- public ushort MaleThickerModels
- {
- get => _maleThickerModels;
- set => RaiseAndSetIfChanged(ref _maleThickerModels, value);
- }
+ public ushort MaleThickerModels
+ {
+ get => _maleThickerModels;
+ set => RaiseAndSetIfChanged(ref _maleThickerModels, value);
+ }
- public decimal MaleThickerOffset
- {
- get => _maleThickerOffset;
- set => RaiseAndSetIfChanged(ref _maleThickerOffset, Math.Round(value, 2));
- }
+ public decimal MaleThickerOffset
+ {
+ get => _maleThickerOffset;
+ set => RaiseAndSetIfChanged(ref _maleThickerOffset, Math.Round(value, 2));
+ }
- public decimal MaleThickerStep
- {
- get => _maleThickerStep;
- set => RaiseAndSetIfChanged(ref _maleThickerStep, Math.Round(value, 2));
- }
+ public decimal MaleThickerStep
+ {
+ get => _maleThickerStep;
+ set => RaiseAndSetIfChanged(ref _maleThickerStep, Math.Round(value, 2));
+ }
- public uint OutputObjects =>
- (_outputSameDiameterPart ? 1u : 0) +
- _maleThinnerModels +
- _maleThickerModels +
- (_fuseParts ? 0 : 1u);
+ public uint OutputObjects =>
+ (_outputSameDiameterPart ? 1u : 0) +
+ _maleThinnerModels +
+ _maleThickerModels +
+ (_fuseParts ? 0 : 1u);
- /*public decimal ObservedXSize
+ /*public decimal ObservedXSize
+ {
+ get => _observedXSize;
+ set
{
- get => _observedXSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _observedXSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(ScaleXFactor));
- }
+ if(!RaiseAndSetIfChanged(ref _observedXSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(ScaleXFactor));
}
+ }
- public decimal ObservedYSize
+ public decimal ObservedYSize
+ {
+ get => _observedYSize;
+ set
{
- get => _observedYSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _observedYSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(ScaleYFactor));
- }
+ if(!RaiseAndSetIfChanged(ref _observedYSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(ScaleYFactor));
}
+ }
- public decimal ObservedZSize
+ public decimal ObservedZSize
+ {
+ get => _observedZSize;
+ set
{
- get => _observedZSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _observedZSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(ScaleZFactor));
- }
+ if(!RaiseAndSetIfChanged(ref _observedZSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(ScaleZFactor));
}
+ }
- public decimal ScaleXFactor => ObservedXSize > 0 && RealXSize > 0 ? Math.Round(RealXSize * 100 / ObservedXSize, 2) : 100;
- public decimal ScaleYFactor => ObservedYSize > 0 && RealYSize > 0 ? Math.Round(RealYSize * 100 / ObservedYSize, 2) : 100;
- public decimal ScaleZFactor => ObservedZSize > 0 && RealZSize > 0 ? Math.Round(RealZSize * 100 / ObservedZSize, 2) : 100;
- */
- #endregion
+ public decimal ScaleXFactor => ObservedXSize > 0 && RealXSize > 0 ? Math.Round(RealXSize * 100 / ObservedXSize, 2) : 100;
+ public decimal ScaleYFactor => ObservedYSize > 0 && RealYSize > 0 ? Math.Round(RealYSize * 100 / ObservedYSize, 2) : 100;
+ public decimal ScaleZFactor => ObservedZSize > 0 && RealZSize > 0 ? Math.Round(RealZSize * 100 / ObservedZSize, 2) : 100;
+ */
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationCalibrateTolerance() { }
+ public OperationCalibrateTolerance() { }
- public OperationCalibrateTolerance(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationCalibrateTolerance(FileFormat slicerFile) : base(slicerFile)
+ { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if (_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
- if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
- if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
- _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
-
- if (SlicerFile.DisplayWidth > 0)
- DisplayWidth = (decimal)SlicerFile.DisplayWidth;
- if (SlicerFile.DisplayHeight > 0)
- DisplayHeight = (decimal)SlicerFile.DisplayHeight;
- }
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if (_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
+ if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
+ if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
+
+ if (SlicerFile.DisplayWidth > 0)
+ DisplayWidth = (decimal)SlicerFile.DisplayWidth;
+ if (SlicerFile.DisplayHeight > 0)
+ DisplayHeight = (decimal)SlicerFile.DisplayHeight;
+ }
- #endregion
+ #endregion
- #region Enums
+ #region Enums
- public enum Shapes : byte
- {
- Circle,
- Square
- }
+ public enum Shapes : byte
+ {
+ Circle,
+ Square
+ }
- public static Array ShapesItems => Enum.GetValues(typeof(Shapes));
- #endregion
+ public static Array ShapesItems => Enum.GetValues(typeof(Shapes));
+ #endregion
- #region Equality
+ #region Equality
- private bool Equals(OperationCalibrateTolerance other)
- {
- return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _zSize == other._zSize && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _chamferLayers == other._chamferLayers && _erodeBottomIterations == other._erodeBottomIterations && _shape == other._shape && _partMargin == other._partMargin && _outputSameDiameterPart == other._outputSameDiameterPart && _fuseParts == other._fuseParts && _enableAntiAliasing == other._enableAntiAliasing && _femaleDiameter == other._femaleDiameter && _femaleHoleDiameter == other._femaleHoleDiameter && _maleThinnerModels == other._maleThinnerModels && _maleThinnerOffset == other._maleThinnerOffset && _maleThinnerStep == other._maleThinnerStep && _maleThickerModels == other._maleThickerModels && _maleThickerOffset == other._maleThickerOffset && _maleThickerStep == other._maleThickerStep && _mirrorOutput == other._mirrorOutput;
- }
+ private bool Equals(OperationCalibrateTolerance other)
+ {
+ return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _zSize == other._zSize && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _chamferLayers == other._chamferLayers && _erodeBottomIterations == other._erodeBottomIterations && _shape == other._shape && _partMargin == other._partMargin && _outputSameDiameterPart == other._outputSameDiameterPart && _fuseParts == other._fuseParts && _enableAntiAliasing == other._enableAntiAliasing && _femaleDiameter == other._femaleDiameter && _femaleHoleDiameter == other._femaleHoleDiameter && _maleThinnerModels == other._maleThinnerModels && _maleThinnerOffset == other._maleThinnerOffset && _maleThinnerStep == other._maleThinnerStep && _maleThickerModels == other._maleThickerModels && _maleThickerOffset == other._maleThickerOffset && _maleThickerStep == other._maleThickerStep && _mirrorOutput == other._mirrorOutput;
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationCalibrateTolerance other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationCalibrateTolerance other && Equals(other);
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_layerHeight);
- hashCode.Add(_bottomLayers);
- hashCode.Add(_bottomExposure);
- hashCode.Add(_normalExposure);
- hashCode.Add(_zSize);
- hashCode.Add(_topBottomMargin);
- hashCode.Add(_leftRightMargin);
- hashCode.Add(_chamferLayers);
- hashCode.Add(_erodeBottomIterations);
- hashCode.Add((int) _shape);
- hashCode.Add(_partMargin);
- hashCode.Add(_outputSameDiameterPart);
- hashCode.Add(_fuseParts);
- hashCode.Add(_enableAntiAliasing);
- hashCode.Add(_mirrorOutput);
- hashCode.Add(_femaleDiameter);
- hashCode.Add(_femaleHoleDiameter);
- hashCode.Add(_maleThinnerModels);
- hashCode.Add(_maleThinnerOffset);
- hashCode.Add(_maleThinnerStep);
- hashCode.Add(_maleThickerModels);
- hashCode.Add(_maleThickerOffset);
- hashCode.Add(_maleThickerStep);
- return hashCode.ToHashCode();
- }
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_layerHeight);
+ hashCode.Add(_bottomLayers);
+ hashCode.Add(_bottomExposure);
+ hashCode.Add(_normalExposure);
+ hashCode.Add(_zSize);
+ hashCode.Add(_topBottomMargin);
+ hashCode.Add(_leftRightMargin);
+ hashCode.Add(_chamferLayers);
+ hashCode.Add(_erodeBottomIterations);
+ hashCode.Add((int) _shape);
+ hashCode.Add(_partMargin);
+ hashCode.Add(_outputSameDiameterPart);
+ hashCode.Add(_fuseParts);
+ hashCode.Add(_enableAntiAliasing);
+ hashCode.Add(_mirrorOutput);
+ hashCode.Add(_femaleDiameter);
+ hashCode.Add(_femaleHoleDiameter);
+ hashCode.Add(_maleThinnerModels);
+ hashCode.Add(_maleThinnerOffset);
+ hashCode.Add(_maleThinnerStep);
+ hashCode.Add(_maleThickerModels);
+ hashCode.Add(_maleThickerOffset);
+ hashCode.Add(_maleThickerStep);
+ return hashCode.ToHashCode();
+ }
- #endregion
+ #endregion
- #region Methods
- public Mat[] GetLayers()
- {
- var layers = new Mat[LayerCount];
- var layer = EmguExtensions.InitMat(SlicerFile.Resolution);
+ #region Methods
+ public Mat[] GetLayers()
+ {
+ var layers = new Mat[LayerCount];
+ var layer = EmguExtensions.InitMat(SlicerFile.Resolution);
- ushort startX = Math.Max((ushort)2, _leftRightMargin);
- ushort startY = Math.Max((ushort)2, _topBottomMargin);
- int currentX = startX;
- int currentY = startY;
+ ushort startX = Math.Max((ushort)2, _leftRightMargin);
+ ushort startY = Math.Max((ushort)2, _topBottomMargin);
+ int currentX = startX;
+ int currentY = startY;
- const FontFace fontFace = FontFace.HersheyDuplex;
- const double fontScale = 1;
- const byte fontThickness = 2;
- LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
+ const FontFace fontFace = FontFace.HersheyDuplex;
+ const double fontScale = 1;
+ const byte fontThickness = 2;
+ LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected;
- var anchor = new Point(-1, -1);
- var kernel = EmguExtensions.Kernel3x3Rectangle;
+ var anchor = new Point(-1, -1);
+ var kernel = EmguExtensions.Kernel3x3Rectangle;
- var pointTextList = new List<KeyValuePair<Point, string>>();
+ var pointTextList = new List<KeyValuePair<Point, string>>();
- if (!_fuseParts)
+ if (!_fuseParts)
+ {
+ switch (Shape)
{
+ case Shapes.Circle:
+ currentX += (int) FemaleDiameterXPixels / 2;
+ currentY += (int) FemaleDiameterXPixels / 2;
+ CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleDiameterXPixels / 2), EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackColor, -1, lineType);
+ currentX += (int) FemaleDiameterXPixels / 2 + PartMargin;
+
+ break;
+ case Shapes.Square:
+ int offsetX = (int) ((FemaleDiameterXPixels - FemaleHoleDiameterXPixels) / 2);
+ int offsetY = (int) ((FemaleDiameterYPixels - FemaleHoleDiameterYPixels) / 2);
+ CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int) FemaleDiameterXPixels, (int) FemaleDiameterXPixels), EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int) FemaleHoleDiameterXPixels, (int) FemaleHoleDiameterYPixels), EmguExtensions.BlackColor, -1, lineType);
+ currentX += (int)FemaleDiameterXPixels + PartMargin;
+ currentY = startY + (int) FemaleDiameterYPixels / 2;
+ break;
+ }
+ }
+
+ bool addPart(decimal step)
+ {
+ decimal millimeters = Math.Round(_femaleHoleDiameter + step, 2);
+ if (millimeters <= 0) return false;
+ int xPixels = (int)(millimeters * Xppmm);
+ int yPixels = (int)(millimeters * Yppmm);
+ Point partCenterText;
+
+ if (_fuseParts)
+ {
+ if (xPixels >= FemaleHoleDiameterXPixels || yPixels >= FemaleHoleDiameterYPixels) return false;
+ if (currentX + FemaleDiameterXPixels + _leftRightMargin >= SlicerFile.Resolution.Width)
+ {
+ currentX = startX;
+ currentY += (int)FemaleDiameterYPixels + PartMargin;
+ }
+
+ if (currentY + FemaleDiameterYPixels + _topBottomMargin >= SlicerFile.Resolution.Height)
+ {
+ return false; // Insufficient size
+ }
+
+ int halfDiameterX = (int)(FemaleDiameterXPixels / 2);
+ int halfDiameterY = (int)(FemaleDiameterYPixels / 2);
+
switch (Shape)
{
case Shapes.Circle:
- currentX += (int) FemaleDiameterXPixels / 2;
- currentY += (int) FemaleDiameterXPixels / 2;
- CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleDiameterXPixels / 2), EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Circle(layer, new Point(currentX, currentY), (int) (FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackColor, -1, lineType);
- currentX += (int) FemaleDiameterXPixels / 2 + PartMargin;
-
+ CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleDiameterXPixels / 2), EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackColor, -1, lineType);
+ CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), xPixels / 2, EmguExtensions.WhiteColor, -1, lineType);
break;
case Shapes.Square:
- int offsetX = (int) ((FemaleDiameterXPixels - FemaleHoleDiameterXPixels) / 2);
- int offsetY = (int) ((FemaleDiameterYPixels - FemaleHoleDiameterYPixels) / 2);
- CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int) FemaleDiameterXPixels, (int) FemaleDiameterXPixels), EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int) FemaleHoleDiameterXPixels, (int) FemaleHoleDiameterYPixels), EmguExtensions.BlackColor, -1, lineType);
- currentX += (int)FemaleDiameterXPixels + PartMargin;
- currentY = startY + (int) FemaleDiameterYPixels / 2;
+ int offsetX = (int)((FemaleDiameterXPixels - FemaleHoleDiameterXPixels) / 2);
+ int offsetY = (int)((FemaleDiameterYPixels - FemaleHoleDiameterYPixels) / 2);
+ CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int)FemaleDiameterXPixels, (int)FemaleDiameterXPixels), EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int)FemaleHoleDiameterXPixels, (int)FemaleHoleDiameterYPixels), EmguExtensions.BlackColor, -1, lineType);
+ offsetX = (int)((FemaleDiameterXPixels - xPixels) / 2);
+ offsetY = (int)((FemaleDiameterYPixels - yPixels) / 2);
+ CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, xPixels, yPixels), EmguExtensions.WhiteColor, -1, lineType);
break;
}
- }
- bool addPart(decimal step)
+ partCenterText = new Point(currentX + halfDiameterX - 60, currentY + halfDiameterY + 10);
+ currentX += (int)FemaleDiameterXPixels + PartMargin;
+ }
+ else
{
- decimal millimeters = Math.Round(_femaleHoleDiameter + step, 2);
- if (millimeters <= 0) return false;
- int xPixels = (int)(millimeters * Xppmm);
- int yPixels = (int)(millimeters * Yppmm);
- Point partCenterText;
+ if (currentX + xPixels + _leftRightMargin >= SlicerFile.Resolution.Width)
+ {
+ currentX = startX;
+ currentY += yPixels + PartMargin;
+ }
- if (_fuseParts)
+ if (currentY + yPixels + _topBottomMargin >= SlicerFile.Resolution.Height)
{
- if (xPixels >= FemaleHoleDiameterXPixels || yPixels >= FemaleHoleDiameterYPixels) return false;
- if (currentX + FemaleDiameterXPixels + _leftRightMargin >= SlicerFile.Resolution.Width)
- {
- currentX = startX;
- currentY += (int)FemaleDiameterYPixels + PartMargin;
- }
-
- if (currentY + FemaleDiameterYPixels + _topBottomMargin >= SlicerFile.Resolution.Height)
- {
- return false; // Insufficient size
- }
-
- int halfDiameterX = (int)(FemaleDiameterXPixels / 2);
- int halfDiameterY = (int)(FemaleDiameterYPixels / 2);
-
- switch (Shape)
- {
- case Shapes.Circle:
- CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleDiameterXPixels / 2), EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), (int)(FemaleHoleDiameterXPixels / 2), EmguExtensions.BlackColor, -1, lineType);
- CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), xPixels / 2, EmguExtensions.WhiteColor, -1, lineType);
- break;
- case Shapes.Square:
- int offsetX = (int)((FemaleDiameterXPixels - FemaleHoleDiameterXPixels) / 2);
- int offsetY = (int)((FemaleDiameterYPixels - FemaleHoleDiameterYPixels) / 2);
- CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, (int)FemaleDiameterXPixels, (int)FemaleDiameterXPixels), EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, (int)FemaleHoleDiameterXPixels, (int)FemaleHoleDiameterYPixels), EmguExtensions.BlackColor, -1, lineType);
- offsetX = (int)((FemaleDiameterXPixels - xPixels) / 2);
- offsetY = (int)((FemaleDiameterYPixels - yPixels) / 2);
- CvInvoke.Rectangle(layer, new Rectangle(currentX + offsetX, currentY + offsetY, xPixels, yPixels), EmguExtensions.WhiteColor, -1, lineType);
- break;
- }
-
- partCenterText = new Point(currentX + halfDiameterX - 60, currentY + halfDiameterY + 10);
- currentX += (int)FemaleDiameterXPixels + PartMargin;
+ return false; // Insufficient size
}
- else
+
+ int halfDiameterX = xPixels / 2;
+ int halfDiameterY = yPixels / 2;
+
+ switch (Shape)
{
- if (currentX + xPixels + _leftRightMargin >= SlicerFile.Resolution.Width)
- {
- currentX = startX;
- currentY += yPixels + PartMargin;
- }
-
- if (currentY + yPixels + _topBottomMargin >= SlicerFile.Resolution.Height)
- {
- return false; // Insufficient size
- }
-
- int halfDiameterX = xPixels / 2;
- int halfDiameterY = yPixels / 2;
-
- switch (Shape)
- {
- case Shapes.Circle:
- CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), halfDiameterX, EmguExtensions.WhiteColor, -1, lineType);
- break;
- case Shapes.Square:
- CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, xPixels, yPixels), EmguExtensions.WhiteColor, -1, lineType);
- break;
- }
-
- partCenterText = new Point(currentX + halfDiameterX - 60, currentY + halfDiameterY + 10);
-
- currentX += xPixels + PartMargin;
+ case Shapes.Circle:
+ CvInvoke.Circle(layer, new Point(currentX + halfDiameterX, currentY + halfDiameterY), halfDiameterX, EmguExtensions.WhiteColor, -1, lineType);
+ break;
+ case Shapes.Square:
+ CvInvoke.Rectangle(layer, new Rectangle(currentX, currentY, xPixels, yPixels), EmguExtensions.WhiteColor, -1, lineType);
+ break;
}
- pointTextList.Add(new KeyValuePair<Point, string>(partCenterText, step > 0 ? $"+{step:F2}" : $"{step:F2}"));
+ partCenterText = new Point(currentX + halfDiameterX - 60, currentY + halfDiameterY + 10);
- return true;
+ currentX += xPixels + PartMargin;
}
- if (!_fuseParts && _outputSameDiameterPart)
- {
- addPart(0);
- }
+ pointTextList.Add(new KeyValuePair<Point, string>(partCenterText, step > 0 ? $"+{step:F2}" : $"{step:F2}"));
- for (int i = 1; i <= _maleThinnerModels; i++)
- {
- var step = _maleThinnerOffset + _maleThinnerStep * i;
- if (!addPart(step)) break;
- }
+ return true;
+ }
- for (int i = 1; i <= _maleThickerModels; i++)
- {
- var step = _maleThickerOffset + _maleThickerStep * i;
- if (!addPart(step)) break;
- }
+ if (!_fuseParts && _outputSameDiameterPart)
+ {
+ addPart(0);
+ }
- Parallel.For(0, layers.Length, CoreSettings.ParallelOptions, layerIndex =>
- //for (var i = 0; i < layers.Length; i++)
- {
- layers[layerIndex] = layer.Clone();
+ for (int i = 1; i <= _maleThinnerModels; i++)
+ {
+ var step = _maleThinnerOffset + _maleThinnerStep * i;
+ if (!addPart(step)) break;
+ }
+
+ for (int i = 1; i <= _maleThickerModels; i++)
+ {
+ var step = _maleThickerOffset + _maleThickerStep * i;
+ if (!addPart(step)) break;
+ }
+
+ Parallel.For(0, layers.Length, CoreSettings.ParallelOptions, layerIndex =>
+ //for (var i = 0; i < layers.Length; i++)
+ {
+ layers[layerIndex] = layer.Clone();
+ });
+
+ if (_erodeBottomIterations > 0)
+ {
+ Parallel.For(0, _bottomLayers, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default);
});
+ }
- if (_erodeBottomIterations > 0)
+ if (_chamferLayers > 0)
+ {
+ Parallel.For(0, _chamferLayers, CoreSettings.ParallelOptions, layerIndexOffset =>
{
- Parallel.For(0, _bottomLayers, CoreSettings.ParallelOptions, layerIndex =>
- {
- CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default);
- });
- }
+ var iteration = _chamferLayers - layerIndexOffset;
+ CvInvoke.Erode(layers[layerIndexOffset], layers[layerIndexOffset], kernel, anchor, iteration, BorderType.Reflect101, default);
- if (_chamferLayers > 0)
+ var layerIndex = layers.Length - 1 - layerIndexOffset;
+ CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, iteration, BorderType.Reflect101, default);
+ });
+ /*byte iterations = _chamferLayers;
+ var layerIndex = 0;
+ for (; layerIndex < LayerCount && iterations > 0; layerIndex++)
{
- Parallel.For(0, _chamferLayers, CoreSettings.ParallelOptions, layerIndexOffset =>
- {
- var iteration = _chamferLayers - layerIndexOffset;
- CvInvoke.Erode(layers[layerIndexOffset], layers[layerIndexOffset], kernel, anchor, iteration, BorderType.Reflect101, default);
-
- var layerIndex = layers.Length - 1 - layerIndexOffset;
- CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, iteration, BorderType.Reflect101, default);
- });
- /*byte iterations = _chamferLayers;
- var layerIndex = 0;
- for (; layerIndex < LayerCount && iterations > 0; layerIndex++)
- {
- CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, iterations--, BorderType.Reflect101, default);
- }
-
- iterations = _chamferLayers;
- for (int i = (int) (LayerCount - 1); i >= 0 && i > layerIndex && iterations > 0; i--)
- {
- CvInvoke.Erode(layers[i], layers[i], kernel, anchor, iterations--, BorderType.Reflect101, default);
- }*/
+ CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, iterations--, BorderType.Reflect101, default);
}
- Parallel.For(Math.Max(0u, LayerCount - 15), LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ iterations = _chamferLayers;
+ for (int i = (int) (LayerCount - 1); i >= 0 && i > layerIndex && iterations > 0; i--)
{
- foreach (var keyValuePair in pointTextList)
- {
- CvInvoke.PutText(layers[layerIndex], keyValuePair.Value, keyValuePair.Key, fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType);
- }
- });
+ CvInvoke.Erode(layers[i], layers[i], kernel, anchor, iterations--, BorderType.Reflect101, default);
+ }*/
+ }
- if (_mirrorOutput)
+ Parallel.For(Math.Max(0u, LayerCount - 15), LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ foreach (var keyValuePair in pointTextList)
{
- var flip = SlicerFile.DisplayMirror;
- if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
- Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
+ CvInvoke.PutText(layers[layerIndex], keyValuePair.Value, keyValuePair.Key, fontFace, fontScale, EmguExtensions.BlackColor, fontThickness, lineType);
}
+ });
- return layers;
- }
-
- public Mat GetThumbnail()
+ if (_mirrorOutput)
{
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "Tolerance Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- CvInvoke.PutText(thumbnail, $"Objects: {OutputObjects}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
-
- /*thumbnail.SetTo(EmguExtensions.Black3Byte);
-
- CvInvoke.Circle(thumbnail, new Point(400/2, 200/2), 200/2, EmguExtensions.White3Byte, -1);
- for (int angle = 0; angle < 360; angle+=20)
- {
- CvInvoke.Line(thumbnail, new Point(400 / 2, 200 / 2), new Point((int)(400 / 2 + 100 * Math.Cos(angle * Math.PI / 180)), (int)(200 / 2 + 100 * Math.Sin(angle * Math.PI / 180))), new MCvScalar(255, 27, 245), 3);
- }
-
- thumbnail.Save("D:\\Thumbnail.png");*/
- return thumbnail;
+ var flip = SlicerFile.DisplayMirror;
+ if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
+ Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
}
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- progress.ItemCount = LayerCount;
+ return layers;
+ }
- var newLayers = new Layer[LayerCount];
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "Tolerance Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
+ CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ CvInvoke.PutText(thumbnail, $"Objects: {OutputObjects}", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+
+ /*thumbnail.SetTo(EmguExtensions.Black3Byte);
+
+ CvInvoke.Circle(thumbnail, new Point(400/2, 200/2), 200/2, EmguExtensions.White3Byte, -1);
+ for (int angle = 0; angle < 360; angle+=20)
+ {
+ CvInvoke.Line(thumbnail, new Point(400 / 2, 200 / 2), new Point((int)(400 / 2 + 100 * Math.Cos(angle * Math.PI / 180)), (int)(200 / 2 + 100 * Math.Sin(angle * Math.PI / 180))), new MCvScalar(255, 27, 245), 3);
+ }
+
+ thumbnail.Save("D:\\Thumbnail.png");*/
+ return thumbnail;
+ }
- var layers = GetLayers();
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = LayerCount;
- Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
- {
- newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile.LayerManager) {IsModified = true};
- layers[layerIndex].Dispose();
- progress.LockAndIncrement();
- });
+ var newLayers = new Layer[LayerCount];
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(GetThumbnail());
+ var layers = GetLayers();
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)LayerHeight;
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.BottomLayerCount = BottomLayers;
- SlicerFile.TransitionLayerCount = 0;
- SlicerFile.LayerManager.Layers = newLayers;
- }, true);
-
- var moveOp = new OperationMove(SlicerFile);
- moveOp.Execute(progress);
+ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile) {IsModified = true};
+ layers[layerIndex].Dispose();
+ progress.LockAndIncrement();
+ });
+ if (SlicerFile.ThumbnailsCount > 0)
+ SlicerFile.SetThumbnails(GetThumbnail());
- return !progress.Token.IsCancellationRequested;
- }
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerHeight = (float)LayerHeight;
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.BottomLayerCount = BottomLayers;
+ SlicerFile.TransitionLayerCount = 0;
+ SlicerFile.Layers = newLayers;
+ }, true);
+
+ var moveOp = new OperationMove(SlicerFile);
+ moveOp.Execute(progress);
- #endregion
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
index a475515..77b6a1d 100644
--- a/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
+++ b/UVtools.Core/Operations/OperationCalibrateXYZAccuracy.cs
@@ -6,809 +6,808 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationCalibrateXYZAccuracy : Operation
{
- [Serializable]
- public sealed class OperationCalibrateXYZAccuracy : Operation
- {
- #region Members
- private decimal _layerHeight;
- private ushort _bottomLayers;
- private decimal _bottomExposure;
- private decimal _normalExposure;
- private ushort _topBottomMargin = 100;
- private ushort _leftRightMargin = 100;
- private decimal _displayWidth;
- private decimal _displayHeight;
- private decimal _xSize = 15;
- private decimal _ySize = 15;
- private decimal _zSize = 15;
- private bool _centerHoleRelief = true;
- private bool _hollowModel = true;
- private bool _mirrorOutput;
- private decimal _wallThickness = 3.0M;
- private decimal _observedXSize;
- private decimal _observedYSize;
- private decimal _observedZSize;
- private bool _outputTLObject;
- private bool _outputTCObject;
- private bool _outputTRObject;
- private bool _outputMLObject;
- private bool _outputMCObject = true;
- private bool _outputMRObject;
- private bool _outputBLObject;
- private bool _outputBCObject;
- private bool _outputBRObject;
- private decimal _drainHoleArea = 3;
-
- #endregion
-
- #region Overrides
-
- public override bool CanROI => false;
-
- public override bool CanCancel => false;
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
-
- public override string Title => "XYZ Accuracy";
- public override string Description =>
- "Generates test models with various strategies and increments to verify the XYZ accuracy.\n" +
- "XYZ are accurate when the printed model match the expected size.\n" +
- "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
- "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
-
- public override string ConfirmationText =>
- $"generate the XYZ accuracy test?";
-
- public override string ProgressTitle =>
- $"Generating the XYZ accuracy test";
-
- public override string ProgressAction => "Generated";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
-
- if (DisplayWidth <= 0)
- {
- sb.AppendLine("Display width must be a positive value.");
- }
+ #region Members
+ private decimal _layerHeight;
+ private ushort _bottomLayers;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+ private ushort _topBottomMargin = 100;
+ private ushort _leftRightMargin = 100;
+ private decimal _displayWidth;
+ private decimal _displayHeight;
+ private decimal _xSize = 15;
+ private decimal _ySize = 15;
+ private decimal _zSize = 15;
+ private bool _centerHoleRelief = true;
+ private bool _hollowModel = true;
+ private bool _mirrorOutput;
+ private decimal _wallThickness = 3.0M;
+ private decimal _observedXSize;
+ private decimal _observedYSize;
+ private decimal _observedZSize;
+ private bool _outputTLObject;
+ private bool _outputTCObject;
+ private bool _outputTRObject;
+ private bool _outputMLObject;
+ private bool _outputMCObject = true;
+ private bool _outputMRObject;
+ private bool _outputBLObject;
+ private bool _outputBCObject;
+ private bool _outputBRObject;
+ private decimal _drainHoleArea = 3;
+
+ #endregion
+
+ #region Overrides
+
+ public override bool CanROI => false;
+
+ public override bool CanCancel => false;
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "fas fa-cubes";
+ public override string Title => "XYZ Accuracy";
+ public override string Description =>
+ "Generates test models with various strategies and increments to verify the XYZ accuracy.\n" +
+ "XYZ are accurate when the printed model match the expected size.\n" +
+ "You must repeat this test when change any of the following: printer, LEDs, resin and exposure times.\n" +
+ "Note: The current opened file will be overwritten with this test, use a dummy or a not needed file.";
+
+ public override string ConfirmationText =>
+ $"generate the XYZ accuracy test?";
+
+ public override string ProgressTitle =>
+ $"Generating the XYZ accuracy test";
+
+ public override string ProgressAction => "Generated";
+
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- if (DisplayHeight <= 0)
- {
- sb.AppendLine("Display height must be a positive value.");
- }
-
- if (OutputObjects <= 0)
- {
- sb.AppendLine("No objects to output.");
- }
-
- return sb.ToString();
+ if (DisplayWidth <= 0)
+ {
+ sb.AppendLine("Display width must be a positive value.");
}
- public override string ToString()
+ if (DisplayHeight <= 0)
+ {
+ sb.AppendLine("Display height must be a positive value.");
+ }
+
+ if (OutputObjects <= 0)
{
- var result = $"[Layer Height: {_layerHeight}] " +
- $"[Bottom layers: {_bottomLayers}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
- $"[X: {_xSize} Y:{_ySize} Z:{_zSize}] " +
- $"[TB:{_topBottomMargin} LR:{_leftRightMargin}] " +
- $"[Model: {_outputTLObject.ToByte()}{_outputTCObject.ToByte()}{_outputTRObject.ToByte()}" +
- $"|{_outputMLObject.ToByte()}{_outputMCObject.ToByte()}{_outputMRObject.ToByte()}" +
- $"|{_outputBLObject.ToByte()}{_outputBCObject.ToByte()}{_outputBRObject.ToByte()}] " +
- $"[Hollow: {_hollowModel} @ {_wallThickness}mm] [Relief: {_centerHoleRelief}] [Mirror: {_mirrorOutput}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("No objects to output.");
}
+
+ return sb.ToString();
+ }
- #endregion
+ public override string ToString()
+ {
+ var result = $"[Layer Height: {_layerHeight}] " +
+ $"[Bottom layers: {_bottomLayers}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}] " +
+ $"[X: {_xSize} Y:{_ySize} Z:{_zSize}] " +
+ $"[TB:{_topBottomMargin} LR:{_leftRightMargin}] " +
+ $"[Model: {_outputTLObject.ToByte()}{_outputTCObject.ToByte()}{_outputTRObject.ToByte()}" +
+ $"|{_outputMLObject.ToByte()}{_outputMCObject.ToByte()}{_outputMRObject.ToByte()}" +
+ $"|{_outputBLObject.ToByte()}{_outputBCObject.ToByte()}{_outputBRObject.ToByte()}] " +
+ $"[Hollow: {_hollowModel} @ {_wallThickness}mm] [Relief: {_centerHoleRelief}] [Mirror: {_mirrorOutput}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #region Properties
+ #endregion
- public decimal DisplayWidth
+ #region Properties
+
+ public decimal DisplayWidth
+ {
+ get => _displayWidth;
+ set
{
- get => _displayWidth;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(Xppmm));
- }
+ if(!RaiseAndSetIfChanged(ref _displayWidth, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(Xppmm));
}
+ }
- public decimal DisplayHeight
+ public decimal DisplayHeight
+ {
+ get => _displayHeight;
+ set
{
- get => _displayHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(Yppmm));
- }
+ if(!RaiseAndSetIfChanged(ref _displayHeight, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(Yppmm));
}
+ }
- public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0;
- public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0;
+ public decimal Xppmm => DisplayWidth > 0 ? Math.Round(SlicerFile.Resolution.Width / DisplayWidth, 2) : 0;
+ public decimal Yppmm => DisplayHeight > 0 ? Math.Round(SlicerFile.Resolution.Height / DisplayHeight, 2) : 0;
- public decimal LayerHeight
+ public decimal LayerHeight
+ {
+ get => _layerHeight;
+ set
{
- get => _layerHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- RaisePropertyChanged(nameof(LayerCount));
- RaisePropertyChanged(nameof(RealZSize));
- RaisePropertyChanged(nameof(ObservedZSize));
- }
+ if(!RaiseAndSetIfChanged(ref _layerHeight, Layer.RoundHeight(value))) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
+ RaisePropertyChanged(nameof(LayerCount));
+ RaisePropertyChanged(nameof(RealZSize));
+ RaisePropertyChanged(nameof(ObservedZSize));
}
+ }
- public ushort Microns => (ushort)(LayerHeight * 1000);
+ public ushort Microns => (ushort)(LayerHeight * 1000);
- public ushort BottomLayers
+ public ushort BottomLayers
+ {
+ get => _bottomLayers;
+ set
{
- get => _bottomLayers;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
- RaisePropertyChanged(nameof(BottomLayersMM));
- }
+ if(!RaiseAndSetIfChanged(ref _bottomLayers, value)) return;
+ RaisePropertyChanged(nameof(BottomLayersMM));
}
+ }
- public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
+ public decimal BottomLayersMM => Layer.RoundHeight(LayerHeight * BottomLayers);
- public decimal BottomExposure
- {
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
- }
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
- public decimal NormalExposure
- {
- get => _normalExposure;
- set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
- }
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
+ }
- public ushort TopBottomMargin
- {
- get => _topBottomMargin;
- set => RaiseAndSetIfChanged(ref _topBottomMargin, value);
- }
+ public ushort TopBottomMargin
+ {
+ get => _topBottomMargin;
+ set => RaiseAndSetIfChanged(ref _topBottomMargin, value);
+ }
- public ushort LeftRightMargin
- {
- get => _leftRightMargin;
- set => RaiseAndSetIfChanged(ref _leftRightMargin, value);
- }
+ public ushort LeftRightMargin
+ {
+ get => _leftRightMargin;
+ set => RaiseAndSetIfChanged(ref _leftRightMargin, value);
+ }
- public decimal XSize
+ public decimal XSize
+ {
+ get => _xSize;
+ set
{
- get => _xSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _xSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(RealXSize));
- RaisePropertyChanged(nameof(ObservedXSize));
- }
+ if(!RaiseAndSetIfChanged(ref _xSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(RealXSize));
+ RaisePropertyChanged(nameof(ObservedXSize));
}
+ }
- public decimal YSize
+ public decimal YSize
+ {
+ get => _ySize;
+ set
{
- get => _ySize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _ySize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(RealYSize));
- RaisePropertyChanged(nameof(ObservedYSize));
- }
+ if(!RaiseAndSetIfChanged(ref _ySize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(RealYSize));
+ RaisePropertyChanged(nameof(ObservedYSize));
}
+ }
- public decimal ZSize
+ public decimal ZSize
+ {
+ get => _zSize;
+ set
{
- get => _zSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _zSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(LayerCount));
- RaisePropertyChanged(nameof(RealZSize));
- RaisePropertyChanged(nameof(ObservedZSize));
- }
+ if(!RaiseAndSetIfChanged(ref _zSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(LayerCount));
+ RaisePropertyChanged(nameof(RealZSize));
+ RaisePropertyChanged(nameof(ObservedZSize));
}
+ }
- public decimal RealXSize
+ public decimal RealXSize
+ {
+ get
{
- get
- {
- decimal pixels = _xSize * Xppmm;
- if (pixels <= 0) return 0;
- return Math.Round(_xSize - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
- }
-
+ decimal pixels = _xSize * Xppmm;
+ if (pixels <= 0) return 0;
+ return Math.Round(_xSize - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
}
- public decimal RealYSize
+ }
+
+ public decimal RealYSize
+ {
+ get
{
- get
- {
- decimal pixels = _ySize * Yppmm;
- if (pixels <= 0) return 0;
- return Math.Round(_ySize - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
- }
+ decimal pixels = _ySize * Yppmm;
+ if (pixels <= 0) return 0;
+ return Math.Round(_ySize - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
}
+ }
- public decimal RealZSize => LayerCount * _layerHeight;
+ public decimal RealZSize => LayerCount * _layerHeight;
- public uint XPixels => (uint)(XSize * Xppmm);
- public uint YPixels => (uint)(YSize * Yppmm);
+ public uint XPixels => (uint)(XSize * Xppmm);
+ public uint YPixels => (uint)(YSize * Yppmm);
- public uint LayerCount => (uint)(ZSize / LayerHeight);
+ public uint LayerCount => (uint)(ZSize / LayerHeight);
- public decimal DrainHoleArea
- {
- get => _drainHoleArea;
- set => RaiseAndSetIfChanged(ref _drainHoleArea, value);
- }
+ public decimal DrainHoleArea
+ {
+ get => _drainHoleArea;
+ set => RaiseAndSetIfChanged(ref _drainHoleArea, value);
+ }
- public bool CenterHoleRelief
- {
- get => _centerHoleRelief;
- set => RaiseAndSetIfChanged(ref _centerHoleRelief, value);
- }
+ public bool CenterHoleRelief
+ {
+ get => _centerHoleRelief;
+ set => RaiseAndSetIfChanged(ref _centerHoleRelief, value);
+ }
- public bool HollowModel
- {
- get => _hollowModel;
- set => RaiseAndSetIfChanged(ref _hollowModel, value);
- }
+ public bool HollowModel
+ {
+ get => _hollowModel;
+ set => RaiseAndSetIfChanged(ref _hollowModel, value);
+ }
- public bool MirrorOutput
- {
- get => _mirrorOutput;
- set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
- }
+ public bool MirrorOutput
+ {
+ get => _mirrorOutput;
+ set => RaiseAndSetIfChanged(ref _mirrorOutput, value);
+ }
- public decimal WallThickness
+ public decimal WallThickness
+ {
+ get => _wallThickness;
+ set
{
- get => _wallThickness;
- set
- {
- if(!RaiseAndSetIfChanged(ref _wallThickness, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(WallThicknessRealXSize));
- RaisePropertyChanged(nameof(WallThicknessRealYSize));
- }
+ if(!RaiseAndSetIfChanged(ref _wallThickness, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(WallThicknessRealXSize));
+ RaisePropertyChanged(nameof(WallThicknessRealYSize));
}
+ }
- public decimal WallThicknessRealXSize
+ public decimal WallThicknessRealXSize
+ {
+ get
{
- get
- {
- decimal pixels = _wallThickness * Xppmm;
- if (pixels <= 0) return 0;
- return Math.Round(_wallThickness - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
- }
+ decimal pixels = _wallThickness * Xppmm;
+ if (pixels <= 0) return 0;
+ return Math.Round(_wallThickness - (pixels - Math.Truncate(pixels)) / Xppmm, 2);
}
+ }
- public decimal WallThicknessRealYSize
+ public decimal WallThicknessRealYSize
+ {
+ get
{
- get
- {
- decimal pixels = _wallThickness * Yppmm;
- if (pixels <= 0) return 0;
- return Math.Round(_wallThickness - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
- }
+ decimal pixels = _wallThickness * Yppmm;
+ if (pixels <= 0) return 0;
+ return Math.Round(_wallThickness - (pixels - Math.Truncate(pixels)) / Yppmm, 2);
}
+ }
- public uint WallThicknessXPixels => (uint)(WallThickness * Xppmm);
- public uint WallThicknessYPixels => (uint)(WallThickness * Yppmm);
+ public uint WallThicknessXPixels => (uint)(WallThickness * Xppmm);
+ public uint WallThicknessYPixels => (uint)(WallThickness * Yppmm);
- public bool OutputTLObject
+ public bool OutputTLObject
+ {
+ get => _outputTLObject;
+ set
{
- get => _outputTLObject;
- set
- {
- if(!RaiseAndSetIfChanged(ref _outputTLObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if(!RaiseAndSetIfChanged(ref _outputTLObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputTCObject
+ public bool OutputTCObject
+ {
+ get => _outputTCObject;
+ set
{
- get => _outputTCObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputTCObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputTCObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputTRObject
+ public bool OutputTRObject
+ {
+ get => _outputTRObject;
+ set
{
- get => _outputTRObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputTRObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputTRObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputMLObject
+ public bool OutputMLObject
+ {
+ get => _outputMLObject;
+ set
{
- get => _outputMLObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputMLObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputMLObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputMCObject
+ public bool OutputMCObject
+ {
+ get => _outputMCObject;
+ set
{
- get => _outputMCObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputMCObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputMCObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputMRObject
+ public bool OutputMRObject
+ {
+ get => _outputMRObject;
+ set
{
- get => _outputMRObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputMRObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputMRObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputBLObject
+ public bool OutputBLObject
+ {
+ get => _outputBLObject;
+ set
{
- get => _outputBLObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputBLObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputBLObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputBCObject
+ public bool OutputBCObject
+ {
+ get => _outputBCObject;
+ set
{
- get => _outputBCObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputBCObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputBCObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public bool OutputBRObject
+ public bool OutputBRObject
+ {
+ get => _outputBRObject;
+ set
{
- get => _outputBRObject;
- set
- {
- if (!RaiseAndSetIfChanged(ref _outputBRObject, value)) return;
- RaisePropertyChanged(nameof(OutputObjects));
- }
+ if (!RaiseAndSetIfChanged(ref _outputBRObject, value)) return;
+ RaisePropertyChanged(nameof(OutputObjects));
}
+ }
- public byte OutputObjects => (byte) (_outputTLObject.ToByte() +
- _outputTCObject.ToByte() +
- _outputTRObject.ToByte() +
- _outputMLObject.ToByte() +
- _outputMCObject.ToByte() +
- _outputMRObject.ToByte() +
- _outputBLObject.ToByte() +
- _outputBCObject.ToByte() +
- _outputBRObject.ToByte());
-
- public decimal ObservedXSize
- {
- get => _observedXSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _observedXSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(ScaleXFactor));
- }
+ public byte OutputObjects => (byte) (_outputTLObject.ToByte() +
+ _outputTCObject.ToByte() +
+ _outputTRObject.ToByte() +
+ _outputMLObject.ToByte() +
+ _outputMCObject.ToByte() +
+ _outputMRObject.ToByte() +
+ _outputBLObject.ToByte() +
+ _outputBCObject.ToByte() +
+ _outputBRObject.ToByte());
+
+ public decimal ObservedXSize
+ {
+ get => _observedXSize;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _observedXSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(ScaleXFactor));
}
+ }
- public decimal ObservedYSize
+ public decimal ObservedYSize
+ {
+ get => _observedYSize;
+ set
{
- get => _observedYSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _observedYSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(ScaleYFactor));
- }
+ if(!RaiseAndSetIfChanged(ref _observedYSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(ScaleYFactor));
}
+ }
- public decimal ObservedZSize
+ public decimal ObservedZSize
+ {
+ get => _observedZSize;
+ set
{
- get => _observedZSize;
- set
- {
- if(!RaiseAndSetIfChanged(ref _observedZSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(ScaleZFactor));
- }
+ if(!RaiseAndSetIfChanged(ref _observedZSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(ScaleZFactor));
}
+ }
- // 15 - x
- // 14 - 100
- public decimal ScaleXFactor => ObservedXSize > 0 && RealXSize > 0 ? Math.Round(RealXSize * 100 / ObservedXSize, 2) : 100;
- public decimal ScaleYFactor => ObservedYSize > 0 && RealYSize > 0 ? Math.Round(RealYSize * 100 / ObservedYSize, 2) : 100;
- public decimal ScaleZFactor => ObservedZSize > 0 && RealZSize > 0 ? Math.Round(RealZSize * 100 / ObservedZSize, 2) : 100;
+ // 15 - x
+ // 14 - 100
+ public decimal ScaleXFactor => ObservedXSize > 0 && RealXSize > 0 ? Math.Round(RealXSize * 100 / ObservedXSize, 2) : 100;
+ public decimal ScaleYFactor => ObservedYSize > 0 && RealYSize > 0 ? Math.Round(RealYSize * 100 / ObservedYSize, 2) : 100;
+ public decimal ScaleZFactor => ObservedZSize > 0 && RealZSize > 0 ? Math.Round(RealZSize * 100 / ObservedZSize, 2) : 100;
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationCalibrateXYZAccuracy() { }
+ public OperationCalibrateXYZAccuracy() { }
- public OperationCalibrateXYZAccuracy(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationCalibrateXYZAccuracy(FileFormat slicerFile) : base(slicerFile)
+ { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if (_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
- if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
- if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
- _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if (_layerHeight <= 0) _layerHeight = (decimal)SlicerFile.LayerHeight;
+ if (_bottomLayers <= 0) _bottomLayers = SlicerFile.BottomLayerCount;
+ if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ _mirrorOutput = SlicerFile.DisplayMirror != Enumerations.FlipDirection.None;
+
+ if (SlicerFile.DisplayWidth > 0)
+ DisplayWidth = (decimal)SlicerFile.DisplayWidth;
+ if (SlicerFile.DisplayHeight > 0)
+ DisplayHeight = (decimal)SlicerFile.DisplayHeight;
+ }
- if (SlicerFile.DisplayWidth > 0)
- DisplayWidth = (decimal)SlicerFile.DisplayWidth;
- if (SlicerFile.DisplayHeight > 0)
- DisplayHeight = (decimal)SlicerFile.DisplayHeight;
- }
+ #endregion
- #endregion
+ #region Enums
- #region Enums
+ #endregion
- #endregion
+ #region Properties
- #region Properties
+ /*public override string ToString()
+ {
+ var result = $"[{_blurOperation}] [Size: {_size}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }*/
- /*public override string ToString()
- {
- var result = $"[{_blurOperation}] [Size: {_size}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }*/
+ #endregion
- #endregion
+ #region Equality
- #region Equality
+ private bool Equals(OperationCalibrateXYZAccuracy other)
+ {
+ return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _xSize == other._xSize && _ySize == other._ySize && _zSize == other._zSize && _centerHoleRelief == other._centerHoleRelief && _hollowModel == other._hollowModel && _wallThickness == other._wallThickness && _observedXSize == other._observedXSize && _observedYSize == other._observedYSize && _observedZSize == other._observedZSize && _outputTLObject == other._outputTLObject && _outputTCObject == other._outputTCObject && _outputTRObject == other._outputTRObject && _outputMLObject == other._outputMLObject && _outputMCObject == other._outputMCObject && _outputMRObject == other._outputMRObject && _outputBLObject == other._outputBLObject && _outputBCObject == other._outputBCObject && _outputBRObject == other._outputBRObject && _mirrorOutput == other._mirrorOutput;
+ }
- private bool Equals(OperationCalibrateXYZAccuracy other)
- {
- return _layerHeight == other._layerHeight && _bottomLayers == other._bottomLayers && _bottomExposure == other._bottomExposure && _normalExposure == other._normalExposure && _topBottomMargin == other._topBottomMargin && _leftRightMargin == other._leftRightMargin && _xSize == other._xSize && _ySize == other._ySize && _zSize == other._zSize && _centerHoleRelief == other._centerHoleRelief && _hollowModel == other._hollowModel && _wallThickness == other._wallThickness && _observedXSize == other._observedXSize && _observedYSize == other._observedYSize && _observedZSize == other._observedZSize && _outputTLObject == other._outputTLObject && _outputTCObject == other._outputTCObject && _outputTRObject == other._outputTRObject && _outputMLObject == other._outputMLObject && _outputMCObject == other._outputMCObject && _outputMRObject == other._outputMRObject && _outputBLObject == other._outputBLObject && _outputBCObject == other._outputBCObject && _outputBRObject == other._outputBRObject && _mirrorOutput == other._mirrorOutput;
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationCalibrateXYZAccuracy other && Equals(other);
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationCalibrateXYZAccuracy other && Equals(other);
- }
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_layerHeight);
+ hashCode.Add(_bottomLayers);
+ hashCode.Add(_bottomExposure);
+ hashCode.Add(_normalExposure);
+ hashCode.Add(_topBottomMargin);
+ hashCode.Add(_leftRightMargin);
+ hashCode.Add(_xSize);
+ hashCode.Add(_ySize);
+ hashCode.Add(_zSize);
+ hashCode.Add(_centerHoleRelief);
+ hashCode.Add(_hollowModel);
+ hashCode.Add(_mirrorOutput);
+ hashCode.Add(_wallThickness);
+ hashCode.Add(_observedXSize);
+ hashCode.Add(_observedYSize);
+ hashCode.Add(_observedZSize);
+ hashCode.Add(_outputTLObject);
+ hashCode.Add(_outputTCObject);
+ hashCode.Add(_outputTRObject);
+ hashCode.Add(_outputMLObject);
+ hashCode.Add(_outputMCObject);
+ hashCode.Add(_outputMRObject);
+ hashCode.Add(_outputBLObject);
+ hashCode.Add(_outputBCObject);
+ hashCode.Add(_outputBRObject);
+ return hashCode.ToHashCode();
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_layerHeight);
- hashCode.Add(_bottomLayers);
- hashCode.Add(_bottomExposure);
- hashCode.Add(_normalExposure);
- hashCode.Add(_topBottomMargin);
- hashCode.Add(_leftRightMargin);
- hashCode.Add(_xSize);
- hashCode.Add(_ySize);
- hashCode.Add(_zSize);
- hashCode.Add(_centerHoleRelief);
- hashCode.Add(_hollowModel);
- hashCode.Add(_mirrorOutput);
- hashCode.Add(_wallThickness);
- hashCode.Add(_observedXSize);
- hashCode.Add(_observedYSize);
- hashCode.Add(_observedZSize);
- hashCode.Add(_outputTLObject);
- hashCode.Add(_outputTCObject);
- hashCode.Add(_outputTRObject);
- hashCode.Add(_outputMLObject);
- hashCode.Add(_outputMCObject);
- hashCode.Add(_outputMRObject);
- hashCode.Add(_outputBLObject);
- hashCode.Add(_outputBCObject);
- hashCode.Add(_outputBRObject);
- return hashCode.ToHashCode();
- }
+ #endregion
- #endregion
+ #region Methods
- #region Methods
+ public void SelectNoneObjects()
+ {
+ OutputTLObject = false;
+ OutputTCObject = false;
+ OutputTRObject = false;
+ OutputMLObject = false;
+ OutputMCObject = false;
+ OutputMRObject = false;
+ OutputBLObject = false;
+ OutputBCObject = false;
+ OutputBRObject = false;
+ }
- public void SelectNoneObjects()
- {
- OutputTLObject = false;
- OutputTCObject = false;
- OutputTRObject = false;
- OutputMLObject = false;
- OutputMCObject = false;
- OutputMRObject = false;
- OutputBLObject = false;
- OutputBCObject = false;
- OutputBRObject = false;
- }
+ public void SelectAllObjects()
+ {
+ OutputTLObject = true;
+ OutputTCObject = true;
+ OutputTRObject = true;
+ OutputMLObject = true;
+ OutputMCObject = true;
+ OutputMRObject = true;
+ OutputBLObject = true;
+ OutputBCObject = true;
+ OutputBRObject = true;
+ }
- public void SelectAllObjects()
- {
- OutputTLObject = true;
- OutputTCObject = true;
- OutputTRObject = true;
- OutputMLObject = true;
- OutputMCObject = true;
- OutputMRObject = true;
- OutputBLObject = true;
- OutputBCObject = true;
- OutputBRObject = true;
- }
+ public void SelectCrossedObjects()
+ {
+ OutputTLObject = false;
+ OutputTCObject = true;
+ OutputTRObject = false;
+ OutputMLObject = true;
+ OutputMCObject = true;
+ OutputMRObject = true;
+ OutputBLObject = false;
+ OutputBCObject = true;
+ OutputBRObject = false;
+ }
+
+ public void SelectCenterObject()
+ {
+ OutputTLObject = false;
+ OutputTCObject = false;
+ OutputTRObject = false;
+ OutputMLObject = false;
+ OutputMCObject = true;
+ OutputMRObject = false;
+ OutputBLObject = false;
+ OutputBCObject = false;
+ OutputBRObject = false;
+ }
- public void SelectCrossedObjects()
+ public void Sanitize()
+ {
+ for (ushort i = 0; i < 10000 && (_xSize * Xppmm).DecimalDigits() >= 1; i++)
{
- OutputTLObject = false;
- OutputTCObject = true;
- OutputTRObject = false;
- OutputMLObject = true;
- OutputMCObject = true;
- OutputMRObject = true;
- OutputBLObject = false;
- OutputBCObject = true;
- OutputBRObject = false;
+ XSize += 0.01M;
}
- public void SelectCenterObject()
+ for (ushort i = 0; i < 10000 && (_ySize * Yppmm).DecimalDigits() >= 1; i++)
{
- OutputTLObject = false;
- OutputTCObject = false;
- OutputTRObject = false;
- OutputMLObject = false;
- OutputMCObject = true;
- OutputMRObject = false;
- OutputBLObject = false;
- OutputBCObject = false;
- OutputBRObject = false;
+ YSize += 0.01M;
}
- public void Sanitize()
+ for (ushort i = 0; i < 10000 && (_zSize / LayerHeight).DecimalDigits() >= 1; i++)
{
- for (ushort i = 0; i < 10000 && (_xSize * Xppmm).DecimalDigits() >= 1; i++)
- {
- XSize += 0.01M;
- }
-
- for (ushort i = 0; i < 10000 && (_ySize * Yppmm).DecimalDigits() >= 1; i++)
- {
- YSize += 0.01M;
- }
-
- for (ushort i = 0; i < 10000 && (_zSize / LayerHeight).DecimalDigits() >= 1; i++)
- {
- ZSize += 0.01M;
- }
+ ZSize += 0.01M;
}
+ }
- public Mat[] GetLayers()
+ public Mat[] GetLayers()
+ {
+ var layers = new Mat[3];
+ for (byte i = 0; i < layers.Length; i++)
{
- var layers = new Mat[3];
- for (byte i = 0; i < layers.Length; i++)
- {
- layers[i] = EmguExtensions.InitMat(SlicerFile.Resolution);
- }
+ layers[i] = EmguExtensions.InitMat(SlicerFile.Resolution);
+ }
- int currentX = 0;
- int currentY = 0;
- string positionYStr = string.Empty;
- string positionStr = string.Empty;
-
- const FontFace fontFace = FontFace.HersheyDuplex;
- const byte fontStartX = 30;
- const byte fontStartY = 50;
- const double fontScale = 1.3;
- const byte fontThickness = 3;
-
- var xPixels = XPixels;
- var yPixels = YPixels;
-
- for (int y = 0; y < 3; y++)
- {
- switch (y)
+ int currentX = 0;
+ int currentY = 0;
+ string positionYStr = string.Empty;
+ string positionStr = string.Empty;
+
+ const FontFace fontFace = FontFace.HersheyDuplex;
+ const byte fontStartX = 30;
+ const byte fontStartY = 50;
+ const double fontScale = 1.3;
+ const byte fontThickness = 3;
+
+ var xPixels = XPixels;
+ var yPixels = YPixels;
+
+ for (int y = 0; y < 3; y++)
+ {
+ switch (y)
+ {
+ case 0:
+ currentY = _topBottomMargin;
+ positionYStr = "T";
+ break;
+ case 1:
+ currentY = (int)(SlicerFile.Resolution.Height / 2 - yPixels / 2);
+ positionYStr = "M";
+ break;
+ case 2:
+ currentY = (int)(SlicerFile.Resolution.Height - yPixels - _topBottomMargin);
+ positionYStr = "B";
+ break;
+ }
+ for (int x = 0; x < 3; x++)
+ {
+ switch (x)
{
case 0:
- currentY = _topBottomMargin;
- positionYStr = "T";
+ currentX = _leftRightMargin;
+ positionStr = $"{positionYStr}L";
break;
case 1:
- currentY = (int)(SlicerFile.Resolution.Height / 2 - yPixels / 2);
- positionYStr = "M";
+ currentX = (int)(SlicerFile.Resolution.Width / 2 - xPixels / 2);
+ positionStr = $"{positionYStr}C";
break;
case 2:
- currentY = (int)(SlicerFile.Resolution.Height - yPixels - _topBottomMargin);
- positionYStr = "B";
+ currentX = (int)(SlicerFile.Resolution.Width - xPixels - _leftRightMargin);
+ positionStr = $"{positionYStr}R";
break;
}
- for (int x = 0; x < 3; x++)
+
+
+ for (var i = 0; i < layers.Length; i++)
{
- switch (x)
+ if(y == 0 && x == 0 && !_outputTLObject) continue;
+ if(y == 0 && x == 1 && !_outputTCObject) continue;
+ if(y == 0 && x == 2 && !_outputTRObject) continue;
+ if(y == 1 && x == 0 && !_outputMLObject) continue;
+ if(y == 1 && x == 1 && !_outputMCObject) continue;
+ if(y == 1 && x == 2 && !_outputMRObject) continue;
+ if(y == 2 && x == 0 && !_outputBLObject) continue;
+ if(y == 2 && x == 1 && !_outputBCObject) continue;
+ if(y == 2 && x == 2 && !_outputBRObject) continue;
+ var layer = layers[i];
+ CvInvoke.Rectangle(layer,
+ new Rectangle(currentX, currentY, (int)xPixels, (int) yPixels),
+ EmguExtensions.WhiteColor, -1);
+
+ CvInvoke.PutText(layer, positionStr,
+ new Point(currentX + fontStartX, currentY + fontStartY), fontFace, fontScale,
+ EmguExtensions.BlackColor, fontThickness);
+
+ CvInvoke.PutText(layer, $"{XSize},{YSize},{ZSize}",
+ new Point(currentX + fontStartX, (int) (currentY + yPixels - fontStartY + 25)), fontFace, fontScale,
+ EmguExtensions.BlackColor, fontThickness);
+
+ if (CenterHoleRelief)
{
- case 0:
- currentX = _leftRightMargin;
- positionStr = $"{positionYStr}L";
- break;
- case 1:
- currentX = (int)(SlicerFile.Resolution.Width / 2 - xPixels / 2);
- positionStr = $"{positionYStr}C";
- break;
- case 2:
- currentX = (int)(SlicerFile.Resolution.Width - xPixels - _leftRightMargin);
- positionStr = $"{positionYStr}R";
- break;
+ CvInvoke.Circle(layer,
+ new Point((int) (currentX + xPixels / 2), (int) (currentY + yPixels / 2)),
+ (int) (Math.Min(xPixels, yPixels) / 4),
+ EmguExtensions.BlackColor, -1);
}
+ if (_hollowModel && i > 0 && _wallThickness > 0)
+ {
+ Size rectSize = new((int) (xPixels - WallThicknessXPixels * 2), (int) (yPixels - WallThicknessYPixels * 2));
+ Point rectLocation = new((int) (currentX + WallThicknessXPixels), (int) (currentY + WallThicknessYPixels));
+ CvInvoke.Rectangle(layers[i], new Rectangle(rectLocation, rectSize),
+ EmguExtensions.BlackColor, -1);
+ }
- for (var i = 0; i < layers.Length; i++)
+ if (i == 2 && _drainHoleArea > 0)
{
- if(y == 0 && x == 0 && !_outputTLObject) continue;
- if(y == 0 && x == 1 && !_outputTCObject) continue;
- if(y == 0 && x == 2 && !_outputTRObject) continue;
- if(y == 1 && x == 0 && !_outputMLObject) continue;
- if(y == 1 && x == 1 && !_outputMCObject) continue;
- if(y == 1 && x == 2 && !_outputMRObject) continue;
- if(y == 2 && x == 0 && !_outputBLObject) continue;
- if(y == 2 && x == 1 && !_outputBCObject) continue;
- if(y == 2 && x == 2 && !_outputBRObject) continue;
- var layer = layers[i];
- CvInvoke.Rectangle(layer,
- new Rectangle(currentX, currentY, (int)xPixels, (int) yPixels),
- EmguExtensions.WhiteColor, -1);
-
- CvInvoke.PutText(layer, positionStr,
- new Point(currentX + fontStartX, currentY + fontStartY), fontFace, fontScale,
- EmguExtensions.BlackColor, fontThickness);
-
- CvInvoke.PutText(layer, $"{XSize},{YSize},{ZSize}",
- new Point(currentX + fontStartX, (int) (currentY + yPixels - fontStartY + 25)), fontFace, fontScale,
- EmguExtensions.BlackColor, fontThickness);
-
- if (CenterHoleRelief)
- {
- CvInvoke.Circle(layer,
- new Point((int) (currentX + xPixels / 2), (int) (currentY + yPixels / 2)),
- (int) (Math.Min(xPixels, yPixels) / 4),
- EmguExtensions.BlackColor, -1);
- }
-
- if (_hollowModel && i > 0 && _wallThickness > 0)
- {
- Size rectSize = new((int) (xPixels - WallThicknessXPixels * 2), (int) (yPixels - WallThicknessYPixels * 2));
- Point rectLocation = new((int) (currentX + WallThicknessXPixels), (int) (currentY + WallThicknessYPixels));
- CvInvoke.Rectangle(layers[i], new Rectangle(rectLocation, rectSize),
- EmguExtensions.BlackColor, -1);
- }
-
- if (i == 2 && _drainHoleArea > 0)
- {
- Size rectSize = new((int)xPixels, (int)(Yppmm * _drainHoleArea));
- Point rectLocation = new(currentX, (int)(currentY + xPixels / 2 - rectSize.Height / 2));
- CvInvoke.Rectangle(layers[i], new Rectangle(rectLocation, rectSize),
- EmguExtensions.BlackColor, -1);
- }
+ Size rectSize = new((int)xPixels, (int)(Yppmm * _drainHoleArea));
+ Point rectLocation = new(currentX, (int)(currentY + xPixels / 2 - rectSize.Height / 2));
+ CvInvoke.Rectangle(layers[i], new Rectangle(rectLocation, rectSize),
+ EmguExtensions.BlackColor, -1);
}
}
}
-
- if (_mirrorOutput)
- {
- var flip = SlicerFile.DisplayMirror;
- if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
- Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
- }
-
- return layers;
- }
-
- public Mat GetThumbnail()
- {
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "XYZ Accuracy Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- CvInvoke.PutText(thumbnail, $"{XSize} x {YSize} x {ZSize} mm", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
-
- /*thumbnail.SetTo(EmguExtensions.Black3Byte);
-
- CvInvoke.Circle(thumbnail, new Point(400/2, 200/2), 200/2, EmguExtensions.White3Byte, -1);
- for (int angle = 0; angle < 360; angle+=20)
- {
- CvInvoke.Line(thumbnail, new Point(400 / 2, 200 / 2), new Point((int)(400 / 2 + 100 * Math.Cos(angle * Math.PI / 180)), (int)(200 / 2 + 100 * Math.Sin(angle * Math.PI / 180))), new MCvScalar(255, 27, 245), 3);
- }
-
- thumbnail.Save("D:\\Thumbnail.png");*/
- return thumbnail;
}
- protected override bool ExecuteInternally(OperationProgress progress)
+ if (_mirrorOutput)
{
- progress.ItemCount = LayerCount;
+ var flip = SlicerFile.DisplayMirror;
+ if (flip == Enumerations.FlipDirection.None) flip = Enumerations.FlipDirection.Horizontally;
+ Parallel.ForEach(layers, CoreSettings.ParallelOptions, mat => CvInvoke.Flip(mat, mat, Enumerations.ToOpenCVFlipType(flip)));
+ }
+ return layers;
+ }
- var newLayers = new Layer[LayerCount];
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "XYZ Accuracy Cal.", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
+ CvInvoke.PutText(thumbnail, $"{Microns}um @ {BottomExposure}s/{NormalExposure}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ CvInvoke.PutText(thumbnail, $"{XSize} x {YSize} x {ZSize} mm", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+
+ /*thumbnail.SetTo(EmguExtensions.Black3Byte);
+
+ CvInvoke.Circle(thumbnail, new Point(400/2, 200/2), 200/2, EmguExtensions.White3Byte, -1);
+ for (int angle = 0; angle < 360; angle+=20)
+ {
+ CvInvoke.Line(thumbnail, new Point(400 / 2, 200 / 2), new Point((int)(400 / 2 + 100 * Math.Cos(angle * Math.PI / 180)), (int)(200 / 2 + 100 * Math.Sin(angle * Math.PI / 180))), new MCvScalar(255, 27, 245), 3);
+ }
+
+ thumbnail.Save("D:\\Thumbnail.png");*/
+ return thumbnail;
+ }
- var layers = GetLayers();
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = LayerCount;
- var bottomLayer = new Layer(0, layers[0], SlicerFile.LayerManager)
- {
- IsModified = true
- };
- var layer = new Layer(0, layers[1], SlicerFile.LayerManager)
- {
- IsModified = true
- };
- var ventLayer = new Layer(0, layers[2], SlicerFile.LayerManager)
- {
- IsModified = true
- };
+ var newLayers = new Layer[LayerCount];
- for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
- {
- newLayers[layerIndex] = SlicerFile.GetBottomOrNormalValue(layerIndex, bottomLayer.Clone(),
- (_hollowModel || _centerHoleRelief) && _drainHoleArea > 0 && layerIndex <= _bottomLayers + (int)(_drainHoleArea / _layerHeight)
- ? ventLayer.Clone() : layer.Clone());
+ var layers = GetLayers();
- progress++;
- }
+ var bottomLayer = new Layer(0, layers[0], SlicerFile)
+ {
+ IsModified = true
+ };
+ var layer = new Layer(0, layers[1], SlicerFile)
+ {
+ IsModified = true
+ };
+ var ventLayer = new Layer(0, layers[2], SlicerFile)
+ {
+ IsModified = true
+ };
- foreach (var mat in layers)
- {
- mat.Dispose();
- }
- if (SlicerFile.ThumbnailsCount > 0)
- SlicerFile.SetThumbnails(GetThumbnail());
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ newLayers[layerIndex] = SlicerFile.GetBottomOrNormalValue(layerIndex, bottomLayer.Clone(),
+ (_hollowModel || _centerHoleRelief) && _drainHoleArea > 0 && layerIndex <= _bottomLayers + (int)(_drainHoleArea / _layerHeight)
+ ? ventLayer.Clone() : layer.Clone());
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerHeight = (float)LayerHeight;
- SlicerFile.BottomExposureTime = (float)BottomExposure;
- SlicerFile.ExposureTime = (float)NormalExposure;
- SlicerFile.BottomLayerCount = BottomLayers;
- SlicerFile.TransitionLayerCount = 0;
- SlicerFile.LayerManager.Layers = newLayers;
- }, true);
-
- return !progress.Token.IsCancellationRequested;
+ progress++;
}
- #endregion
+ foreach (var mat in layers)
+ {
+ mat.Dispose();
+ }
+
+ if (SlicerFile.ThumbnailsCount > 0)
+ SlicerFile.SetThumbnails(GetThumbnail());
+
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.LayerHeight = (float)LayerHeight;
+ SlicerFile.BottomExposureTime = (float)BottomExposure;
+ SlicerFile.ExposureTime = (float)NormalExposure;
+ SlicerFile.BottomLayerCount = BottomLayers;
+ SlicerFile.TransitionLayerCount = 0;
+ SlicerFile.Layers = newLayers;
+ }, true);
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs
index e2b88c9..2e23079 100644
--- a/UVtools.Core/Operations/OperationChangeResolution.cs
+++ b/UVtools.Core/Operations/OperationChangeResolution.cs
@@ -6,271 +6,271 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationChangeResolution : Operation
{
- [Serializable]
- public sealed class OperationChangeResolution : Operation
- {
- #region Members
- private uint _newResolutionX;
- private uint _newResolutionY;
- private bool _fixRatio;
+ #region Members
+ private uint _newResolutionX;
+ private uint _newResolutionY;
+ private bool _fixRatio;
- #endregion
+ #endregion
- #region Subclasses
- public class Resolution
- {
- public uint ResolutionX { get; }
- public uint ResolutionY { get; }
- public string Name { get; }
- public bool IsEmpty => ResolutionX == 0 && ResolutionY == 0;
+ #region Subclasses
+ public class Resolution
+ {
+ public uint ResolutionX { get; }
+ public uint ResolutionY { get; }
+ public string? Name { get; }
+ public bool IsEmpty => ResolutionX == 0 && ResolutionY == 0;
- public Resolution(uint resolutionX, uint resolutionY, string name = null)
- {
- ResolutionX = resolutionX;
- ResolutionY = resolutionY;
- Name = name;
- }
+ public Resolution(uint resolutionX, uint resolutionY, string? name = null)
+ {
+ ResolutionX = resolutionX;
+ ResolutionY = resolutionY;
+ Name = name;
+ }
- public override string ToString()
+ public override string ToString()
+ {
+ if(IsEmpty) return string.Empty;
+ var str = $"{ResolutionX} x {ResolutionY}";
+ if (!string.IsNullOrEmpty(Name))
{
- if(IsEmpty) return string.Empty;
- var str = $"{ResolutionX} x {ResolutionY}";
- if (!string.IsNullOrEmpty(Name))
- {
- str += $" ({Name})";
- }
- return str;
+ str += $" ({Name})";
}
+ return str;
}
- #endregion
+ }
+ #endregion
- #region Overrides
+ #region Overrides
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI => false;
- public override string Title => "Change print resolution";
- public override string Description =>
- "Crops or resizes all layer images to fit an alternate print resolution.\n" +
- "Useful to make files printable on a different printer than they were originally sliced for without the need to re-slice.\n\n" +
- "NOTE: Please ensure that the actual model will fit within the new print resolution. The operation will be aborted if it will result in any of the actual model being clipped.\n" +
- "Only use this tool if both source and target printer have the same pixel pitch spec, otherwise the model size will be invalidated and result in a different size than the originally sliced for. " +
- "As alternative is possible to resize the model to match the new pixel pitch.";
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
+ public override string IconClass => "mdi-resize";
+ public override string Title => "Change print resolution";
+ public override string Description =>
+ "Crops or resizes all layer images to fit an alternate print resolution.\n" +
+ "Useful to make files printable on a different printer than they were originally sliced for without the need to re-slice.\n\n" +
+ "NOTE: Please ensure that the actual model will fit within the new print resolution. The operation will be aborted if it will result in any of the actual model being clipped.\n" +
+ "Only use this tool if both source and target printer have the same pixel pitch spec, otherwise the model size will be invalidated and result in a different size than the originally sliced for. " +
+ "As alternative is possible to resize the model to match the new pixel pitch.";
- public override string ConfirmationText =>
- "change print resolution " +
- $"from {SlicerFile.ResolutionX}x{SlicerFile.ResolutionY} " +
- $"to {NewResolutionX}x{NewResolutionY}?";
+ public override string ConfirmationText =>
+ "change print resolution " +
+ $"from {SlicerFile.ResolutionX}x{SlicerFile.ResolutionY} " +
+ $"to {NewResolutionX}x{NewResolutionY}?";
- public override string ProgressTitle =>
- $"Changing print resolution from ({SlicerFile.ResolutionX}x{SlicerFile.ResolutionY}) to ({NewResolutionX}x{NewResolutionY})";
+ public override string ProgressTitle =>
+ $"Changing print resolution from ({SlicerFile.ResolutionX}x{SlicerFile.ResolutionY}) to ({NewResolutionX}x{NewResolutionY})";
- public override string ProgressAction => "Changed layers";
+ public override string ProgressAction => "Changed layers";
- public override string ValidateInternally()
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ if (SlicerFile.ResolutionX == NewResolutionX && SlicerFile.ResolutionY == NewResolutionY)
{
- var sb = new StringBuilder();
- if (SlicerFile.ResolutionX == NewResolutionX && SlicerFile.ResolutionY == NewResolutionY)
- {
- sb.AppendLine($"The new resolution must be different from current resolution ({SlicerFile.ResolutionX} x {SlicerFile.ResolutionY}).");
- }
-
- var finalBoundsWidth = FinalBoundsWidth;
- var finalBoundsHeight = FinalBoundsHeight;
- if (NewResolutionX < finalBoundsWidth || NewResolutionY < finalBoundsHeight)
- {
- sb.AppendLine($"The new resolution ({NewResolutionX} x {NewResolutionY}) is not large enough to hold the model volume ({finalBoundsWidth} x {finalBoundsHeight}), continuing operation would clip the model.");
- sb.AppendLine("To fix this, try to rotate the object and/or resize to fit on this new resolution.");
- }
-
- return sb.ToString();
+ sb.AppendLine($"The new resolution must be different from current resolution ({SlicerFile.ResolutionX} x {SlicerFile.ResolutionY}).");
}
- public override string ToString()
+ var finalBoundsWidth = FinalBoundsWidth;
+ var finalBoundsHeight = FinalBoundsHeight;
+ if (NewResolutionX < finalBoundsWidth || NewResolutionY < finalBoundsHeight)
{
- var result = $"{_newResolutionX} x {_newResolutionY} [Fix ratio: {_fixRatio}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine($"The new resolution ({NewResolutionX} x {NewResolutionY}) is not large enough to hold the model volume ({finalBoundsWidth} x {finalBoundsHeight}), continuing operation would clip the model.");
+ sb.AppendLine("To fix this, try to rotate the object and/or resize to fit on this new resolution.");
}
- #endregion
+ return sb.ToString();
+ }
+
+ public override string ToString()
+ {
+ var result = $"{_newResolutionX} x {_newResolutionY} [Fix ratio: {_fixRatio}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ #endregion
- #region Properties
- public uint NewResolutionX
+ #region Properties
+ public uint NewResolutionX
+ {
+ get => _newResolutionX;
+ set
{
- get => _newResolutionX;
- set
- {
- if(!RaiseAndSetIfChanged(ref _newResolutionX, value)) return;
- RaisePropertyChanged(nameof(NewRatioX));
- RaisePropertyChanged(nameof(NewFixedRatioX));
- RaisePropertyChanged(nameof(FinalBoundsWidth));
- }
+ if(!RaiseAndSetIfChanged(ref _newResolutionX, value)) return;
+ RaisePropertyChanged(nameof(NewRatioX));
+ RaisePropertyChanged(nameof(NewFixedRatioX));
+ RaisePropertyChanged(nameof(FinalBoundsWidth));
}
+ }
- public uint NewResolutionY
+ public uint NewResolutionY
+ {
+ get => _newResolutionY;
+ set
{
- get => _newResolutionY;
- set
- {
- RaiseAndSetIfChanged(ref _newResolutionY, value);
- RaisePropertyChanged(nameof(NewRatioY));
- RaisePropertyChanged(nameof(NewFixedRatioY));
- RaisePropertyChanged(nameof(FinalBoundsHeight));
- }
+ RaiseAndSetIfChanged(ref _newResolutionY, value);
+ RaisePropertyChanged(nameof(NewRatioY));
+ RaisePropertyChanged(nameof(NewFixedRatioY));
+ RaisePropertyChanged(nameof(FinalBoundsHeight));
}
+ }
- public double NewRatioX => Math.Round((double)SlicerFile.ResolutionX / _newResolutionX, 2);
- public double NewRatioY => Math.Round((double)SlicerFile.ResolutionY / _newResolutionY, 2);
+ public double NewRatioX => Math.Round((double)SlicerFile.ResolutionX / _newResolutionX, 2);
+ public double NewRatioY => Math.Round((double)SlicerFile.ResolutionY / _newResolutionY, 2);
- public double NewFixedRatioX => Math.Round((double)_newResolutionX / SlicerFile.ResolutionX, 2);
- public double NewFixedRatioY => Math.Round((double)_newResolutionY / SlicerFile.ResolutionY, 2);
+ public double NewFixedRatioX => Math.Round((double)_newResolutionX / SlicerFile.ResolutionX, 2);
+ public double NewFixedRatioY => Math.Round((double)_newResolutionY / SlicerFile.ResolutionY, 2);
- public bool FixRatio
+ public bool FixRatio
+ {
+ get => _fixRatio;
+ set
{
- get => _fixRatio;
- set
- {
- if(!RaiseAndSetIfChanged(ref _fixRatio, value)) return;
- RaisePropertyChanged(nameof(FinalBoundsWidth));
- RaisePropertyChanged(nameof(FinalBoundsHeight));
- }
+ if(!RaiseAndSetIfChanged(ref _fixRatio, value)) return;
+ RaisePropertyChanged(nameof(FinalBoundsWidth));
+ RaisePropertyChanged(nameof(FinalBoundsHeight));
}
+ }
- public uint FinalBoundsWidth => (uint)(_fixRatio ? SlicerFile.BoundingRectangle.Width * NewFixedRatioX : SlicerFile.BoundingRectangle.Width);
- public uint FinalBoundsHeight => (uint)(_fixRatio ? SlicerFile.BoundingRectangle.Height * NewFixedRatioY : SlicerFile.BoundingRectangle.Height);
+ public uint FinalBoundsWidth => (uint)(_fixRatio ? SlicerFile.BoundingRectangle.Width * NewFixedRatioX : SlicerFile.BoundingRectangle.Width);
+ public uint FinalBoundsHeight => (uint)(_fixRatio ? SlicerFile.BoundingRectangle.Height * NewFixedRatioY : SlicerFile.BoundingRectangle.Height);
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationChangeResolution() { }
+ public OperationChangeResolution() { }
- public OperationChangeResolution(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationChangeResolution(FileFormat slicerFile) : base(slicerFile)
+ { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if(_newResolutionX <= 0) _newResolutionX = SlicerFile.ResolutionX;
- if(_newResolutionY <= 0) _newResolutionY = SlicerFile.ResolutionY;
- }
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if(_newResolutionX <= 0) _newResolutionX = SlicerFile.ResolutionX;
+ if(_newResolutionY <= 0) _newResolutionY = SlicerFile.ResolutionY;
+ }
- #endregion
+ #endregion
- #region Methods
- public static Resolution[] GetResolutions()
- {
- return new [] {
- //new Resolution(0, 0, string.Empty),
- new Resolution(854, 480, "FWVGA"),
- new Resolution(960, 1708),
- new Resolution(1080, 1920, "FHD"),
- new Resolution(1440, 2560, "QHD"),
- new Resolution(1600, 2560, "WQXGA"),
- new Resolution(1620, 2560, "WQXGA"),
- new Resolution(1920, 1080, "FHD"),
- new Resolution(2160, 3840, "4K UHD"),
- new Resolution(2531, 1410, "QHD"),
- new Resolution(2560, 1440, "QHD"),
- new Resolution(2560, 1600, "WQXGA"),
- new Resolution(2560, 1620, "WQXGA"),
- new Resolution(3840, 2160, "4K UHD"),
- new Resolution(3840, 2400, "WQUXGA"),
- new Resolution(4920, 2880, "5K UHD"),
- new Resolution(5448, 3064, "6K"),
- new Resolution(7680, 4320, "8K UHD"),
- };
- }
+ #region Methods
+ public static Resolution[] GetResolutions()
+ {
+ return new [] {
+ //new Resolution(0, 0, string.Empty),
+ new Resolution(854, 480, "FWVGA"),
+ new Resolution(960, 1708),
+ new Resolution(1080, 1920, "FHD"),
+ new Resolution(1440, 2560, "QHD"),
+ new Resolution(1600, 2560, "WQXGA"),
+ new Resolution(1620, 2560, "WQXGA"),
+ new Resolution(1920, 1080, "FHD"),
+ new Resolution(2160, 3840, "4K UHD"),
+ new Resolution(2531, 1410, "QHD"),
+ new Resolution(2560, 1440, "QHD"),
+ new Resolution(2560, 1600, "WQXGA"),
+ new Resolution(2560, 1620, "WQXGA"),
+ new Resolution(3840, 2160, "4K UHD"),
+ new Resolution(3840, 2400, "WQUXGA"),
+ new Resolution(4920, 2880, "5K UHD"),
+ new Resolution(5448, 3064, "6K"),
+ new Resolution(7680, 4320, "8K UHD"),
+ };
+ }
+
+ public static Resolution[] Presets => GetResolutions();
- public static Resolution[] Presets => GetResolutions();
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = SlicerFile.LayerCount;
+ var newSize = new Size((int) NewResolutionX, (int) NewResolutionY);
+ var finalBoundsWidth = FinalBoundsWidth;
+ var finalBoundsHeight = FinalBoundsHeight;
+
+ var finalBounds = new Size((int)finalBoundsWidth, (int)finalBoundsHeight);
- protected override bool ExecuteInternally(OperationProgress progress)
+ Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
{
- progress.ItemCount = SlicerFile.LayerCount;
- var newSize = new Size((int) NewResolutionX, (int) NewResolutionY);
- var finalBoundsWidth = FinalBoundsWidth;
- var finalBoundsHeight = FinalBoundsHeight;
+ if (progress.Token.IsCancellationRequested) return;
- var finalBounds = new Size((int)finalBoundsWidth, (int)finalBoundsHeight);
+ using var mat = SlicerFile[layerIndex].LayerMat;
- Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ if (mat.Size != newSize)
{
- if (progress.Token.IsCancellationRequested) return;
+ using var matDst = EmguExtensions.InitMat(newSize);
- using var mat = SlicerFile[layerIndex].LayerMat;
-
- if (mat.Size != newSize)
+ var newFixedRatioX = NewFixedRatioX;
+ var newFixedRatioY = NewFixedRatioY;
+ if (_fixRatio && (newFixedRatioX != 1.0 || newFixedRatioY != 1.0))
{
- using var matDst = EmguExtensions.InitMat(newSize);
-
- var newFixedRatioX = NewFixedRatioX;
- var newFixedRatioY = NewFixedRatioY;
- if (_fixRatio && (newFixedRatioX != 1.0 || newFixedRatioY != 1.0))
- {
- CvInvoke.Resize(mat, mat, SlicerFile.Resolution.Multiply(newFixedRatioX, newFixedRatioY));
- }
-
- mat.CopyCenterToCenter(finalBounds, matDst);
-
- SlicerFile[layerIndex].LayerMat = matDst;
+ CvInvoke.Resize(mat, mat, SlicerFile.Resolution.Multiply(newFixedRatioX, newFixedRatioY));
}
- progress.LockAndIncrement();
- });
+ mat.CopyCenterToCenter(finalBounds, matDst);
- progress.Token.ThrowIfCancellationRequested();
+ SlicerFile[layerIndex].LayerMat = matDst;
+ }
- SlicerFile.ResolutionX = NewResolutionX;
- SlicerFile.ResolutionY = NewResolutionY;
+ progress.LockAndIncrement();
+ });
- return !progress.Token.IsCancellationRequested;
- }
+ progress.Token.ThrowIfCancellationRequested();
- /*public override bool Execute(Mat mat, params object[] arguments)
- {
- //mat.Transform(1, 1, newResolutionX-mat.Width+roi.Width, newResolutionY-mat.Height - roi.Height/2, new Size((int) newResolutionX, (int) newResolutionY));
- using var matRoi = new Mat(mat, VolumeBonds);
- using var matDst = new Mat(new Size((int)NewResolutionX, (int)NewResolutionY), mat.Depth, mat.NumberOfChannels);
- using var matDstRoi = new Mat(matDst,
- new Rectangle((int)(NewResolutionX / 2 - VolumeBonds.Width / 2),
- (int)NewResolutionY / 2 - VolumeBonds.Height / 2,
- VolumeBonds.Width, VolumeBonds.Height));
- matRoi.CopyTo(matDstRoi);
+ SlicerFile.ResolutionX = NewResolutionX;
+ SlicerFile.ResolutionY = NewResolutionY;
- return true;
- }*/
+ return !progress.Token.IsCancellationRequested;
+ }
- #endregion
+ /*public override bool Execute(Mat mat, params object[] arguments)
+ {
+ //mat.Transform(1, 1, newResolutionX-mat.Width+roi.Width, newResolutionY-mat.Height - roi.Height/2, new Size((int) newResolutionX, (int) newResolutionY));
+ using var matRoi = new Mat(mat, VolumeBonds);
+ using var matDst = new Mat(new Size((int)NewResolutionX, (int)NewResolutionY), mat.Depth, mat.NumberOfChannels);
+ using var matDstRoi = new Mat(matDst,
+ new Rectangle((int)(NewResolutionX / 2 - VolumeBonds.Width / 2),
+ (int)NewResolutionY / 2 - VolumeBonds.Height / 2,
+ VolumeBonds.Width, VolumeBonds.Height));
+ matRoi.CopyTo(matDstRoi);
- #region Equality
+ return true;
+ }*/
- private bool Equals(OperationChangeResolution other)
- {
- return _newResolutionX == other._newResolutionX && _newResolutionY == other._newResolutionY && _fixRatio == other._fixRatio;
- }
+ #endregion
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationChangeResolution other && Equals(other);
- }
+ #region Equality
- public override int GetHashCode()
- {
- return HashCode.Combine(_newResolutionX, _newResolutionY, _fixRatio);
- }
+ private bool Equals(OperationChangeResolution other)
+ {
+ return _newResolutionX == other._newResolutionX && _newResolutionY == other._newResolutionY && _fixRatio == other._fixRatio;
+ }
- #endregion
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationChangeResolution other && Equals(other);
+ }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_newResolutionX, _newResolutionY, _fixRatio);
}
-}
+
+ #endregion
+
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationDoubleExposure.cs b/UVtools.Core/Operations/OperationDoubleExposure.cs
index dae57bf..25b7416 100644
--- a/UVtools.Core/Operations/OperationDoubleExposure.cs
+++ b/UVtools.Core/Operations/OperationDoubleExposure.cs
@@ -6,398 +6,398 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationDoubleExposure : Operation
{
- [Serializable]
- public class OperationDoubleExposure : Operation
+ #region Members
+ private decimal _firstBottomExposure;
+ private decimal _firstNormalExposure;
+ private decimal _secondBottomExposure;
+ private decimal _secondNormalExposure;
+ private byte _firstBottomErodeIterations = 4;
+ private byte _secondBottomErodeIterations;
+ private byte _firstNormalErodeIterations = 1;
+ private byte _secondNormalErodeIterations;
+ private bool _secondLayerDifference = true;
+ private byte _secondLayerDifferenceOverlapErodeIterations = 10;
+ private bool _differentSettingsForSecondLayer;
+ private bool _secondLayerLiftHeightEnabled = true;
+ private decimal _secondLayerLiftHeight;
+ private bool _secondLayerWaitTimeBeforeCureEnabled = true;
+ private decimal _secondLayerWaitTimeBeforeCure;
+
+ #endregion
+
+ #region Overrides
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Bottom;
+ public override string IconClass => "fas fa-grip-lines";
+ public override string Title => "Double exposure";
+ public override string Description =>
+ "The double exposure method clones the selected layer range and print the same layer twice with different exposure times and strategies.\n" +
+ "Can be used to eliminate the elephant foot effect or to harden a layer in two steps.\n" +
+ "After this, do not apply any modification which reconstruct the z positions of the layers.\n" +
+ "Note: To eliminate the elephant foot effect, the use of wall dimming method is recommended.";
+
+ public override string ConfirmationText =>
+ $"double exposure model layers {LayerIndexStart} through {LayerIndexEnd}";
+
+ public override string ProgressTitle =>
+ $"Double exposure from layers {LayerIndexStart} to {LayerIndexEnd}";
+
+ public override string ProgressAction => "Cloned layers";
+
+ public override string? ValidateSpawn()
{
- #region Members
- private decimal _firstBottomExposure;
- private decimal _firstNormalExposure;
- private decimal _secondBottomExposure;
- private decimal _secondNormalExposure;
- private byte _firstBottomErodeIterations = 4;
- private byte _secondBottomErodeIterations;
- private byte _firstNormalErodeIterations = 1;
- private byte _secondNormalErodeIterations;
- private bool _secondLayerDifference = true;
- private byte _secondLayerDifferenceOverlapErodeIterations = 10;
- private bool _differentSettingsForSecondLayer;
- private bool _secondLayerLiftHeightEnabled = true;
- private decimal _secondLayerLiftHeight;
- private bool _secondLayerWaitTimeBeforeCureEnabled = true;
- private decimal _secondLayerWaitTimeBeforeCure;
-
- #endregion
-
- #region Overrides
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Bottom;
- public override string Title => "Double exposure";
- public override string Description =>
- "The double exposure method clones the selected layer range and print the same layer twice with different exposure times and strategies.\n" +
- "Can be used to eliminate the elephant foot effect or to harden a layer in two steps.\n" +
- "After this, do not apply any modification which reconstruct the z positions of the layers.\n" +
- "Note: To eliminate the elephant foot effect, the use of wall dimming method is recommended.";
-
- public override string ConfirmationText =>
- $"double exposure model layers {LayerIndexStart} through {LayerIndexEnd}";
-
- public override string ProgressTitle =>
- $"Double exposure from layers {LayerIndexStart} to {LayerIndexEnd}";
-
- public override string ProgressAction => "Cloned layers";
-
- public override string ValidateSpawn()
+ if (!SlicerFile.CanUseLayerPositionZ || !SlicerFile.CanUseLayerLiftHeight || !SlicerFile.CanUseLayerExposureTime)
{
- if (!SlicerFile.CanUseLayerPositionZ || !SlicerFile.CanUseLayerLiftHeight || !SlicerFile.CanUseLayerExposureTime)
- {
- return NotSupportedMessage;
- }
-
- return null;
+ return NotSupportedMessage;
}
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ return null;
+ }
- //if (LayerRangeHaveBottoms && _firstBottomExposure == _secondBottomExposure && _firstBottomErodeIterations == _secondBottomErodeIterations)
- // sb.AppendLine("The settings for bottoms layers will produce exactly to equal layers");
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ //if (LayerRangeHaveBottoms && _firstBottomExposure == _secondBottomExposure && _firstBottomErodeIterations == _secondBottomErodeIterations)
+ // sb.AppendLine("The settings for bottoms layers will produce exactly to equal layers");
- float lastPositionZ = SlicerFile[LayerIndexStart].PositionZ;
- for (uint layerIndex = LayerIndexStart + 1; layerIndex <= LayerIndexEnd; layerIndex++)
+
+ float lastPositionZ = SlicerFile[LayerIndexStart].PositionZ;
+ for (uint layerIndex = LayerIndexStart + 1; layerIndex <= LayerIndexEnd; layerIndex++)
+ {
+ if (lastPositionZ == SlicerFile[layerIndex].PositionZ)
{
- if (lastPositionZ == SlicerFile[layerIndex].PositionZ)
- {
- sb.AppendLine($"The selected layer range already have modified layers with same z position, starting at layer {layerIndex}. Not safe to continue.");
- break;
- }
- lastPositionZ = SlicerFile[layerIndex].PositionZ;
+ sb.AppendLine($"The selected layer range already have modified layers with same z position, starting at layer {layerIndex}. Not safe to continue.");
+ break;
}
+ lastPositionZ = SlicerFile[layerIndex].PositionZ;
+ }
- return sb.ToString();
- }
+ return sb.ToString();
+ }
- public override string ToString()
- {
- var result = $"[1º exp: {_firstBottomExposure}/{_firstNormalExposure}s erode: {_firstBottomErodeIterations}/{_firstNormalErodeIterations}px] " +
- $"[2º exp: {_secondBottomExposure}/{_secondNormalExposure}s erode: {_secondBottomErodeIterations}/{_secondNormalErodeIterations}px] " +
- $"[Diff: {_secondLayerDifference} Overlap: {_secondLayerDifferenceOverlapErodeIterations}px]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
+ public override string ToString()
+ {
+ var result = $"[1º exp: {_firstBottomExposure}/{_firstNormalExposure}s erode: {_firstBottomErodeIterations}/{_firstNormalErodeIterations}px] " +
+ $"[2º exp: {_secondBottomExposure}/{_secondNormalExposure}s erode: {_secondBottomErodeIterations}/{_secondNormalErodeIterations}px] " +
+ $"[Diff: {_secondLayerDifference} Overlap: {_secondLayerDifferenceOverlapErodeIterations}px]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- #region Properties
+ #region Properties
- public decimal FirstBottomExposure
- {
- get => _firstBottomExposure;
- set => RaiseAndSetIfChanged(ref _firstBottomExposure, Math.Round(value, 2));
- }
+ public decimal FirstBottomExposure
+ {
+ get => _firstBottomExposure;
+ set => RaiseAndSetIfChanged(ref _firstBottomExposure, Math.Round(value, 2));
+ }
- public decimal FirstNormalExposure
- {
- get => _firstNormalExposure;
- set => RaiseAndSetIfChanged(ref _firstNormalExposure, Math.Round(value, 2));
- }
+ public decimal FirstNormalExposure
+ {
+ get => _firstNormalExposure;
+ set => RaiseAndSetIfChanged(ref _firstNormalExposure, Math.Round(value, 2));
+ }
- public decimal SecondBottomExposure
- {
- get => _secondBottomExposure;
- set => RaiseAndSetIfChanged(ref _secondBottomExposure, Math.Round(value, 2));
- }
+ public decimal SecondBottomExposure
+ {
+ get => _secondBottomExposure;
+ set => RaiseAndSetIfChanged(ref _secondBottomExposure, Math.Round(value, 2));
+ }
- public decimal SecondNormalExposure
- {
- get => _secondNormalExposure;
- set => RaiseAndSetIfChanged(ref _secondNormalExposure, Math.Round(value, 2));
- }
+ public decimal SecondNormalExposure
+ {
+ get => _secondNormalExposure;
+ set => RaiseAndSetIfChanged(ref _secondNormalExposure, Math.Round(value, 2));
+ }
- public byte FirstBottomErodeIterations
- {
- get => _firstBottomErodeIterations;
- set => RaiseAndSetIfChanged(ref _firstBottomErodeIterations, value);
- }
+ public byte FirstBottomErodeIterations
+ {
+ get => _firstBottomErodeIterations;
+ set => RaiseAndSetIfChanged(ref _firstBottomErodeIterations, value);
+ }
- public byte SecondBottomErodeIterations
- {
- get => _secondBottomErodeIterations;
- set => RaiseAndSetIfChanged(ref _secondBottomErodeIterations, value);
- }
+ public byte SecondBottomErodeIterations
+ {
+ get => _secondBottomErodeIterations;
+ set => RaiseAndSetIfChanged(ref _secondBottomErodeIterations, value);
+ }
- public byte FirstNormalErodeIterations
- {
- get => _firstNormalErodeIterations;
- set => RaiseAndSetIfChanged(ref _firstNormalErodeIterations, value);
- }
+ public byte FirstNormalErodeIterations
+ {
+ get => _firstNormalErodeIterations;
+ set => RaiseAndSetIfChanged(ref _firstNormalErodeIterations, value);
+ }
- public byte SecondNormalErodeIterations
- {
- get => _secondNormalErodeIterations;
- set => RaiseAndSetIfChanged(ref _secondNormalErodeIterations, value);
- }
+ public byte SecondNormalErodeIterations
+ {
+ get => _secondNormalErodeIterations;
+ set => RaiseAndSetIfChanged(ref _secondNormalErodeIterations, value);
+ }
- public bool SecondLayerDifference
- {
- get => _secondLayerDifference;
- set => RaiseAndSetIfChanged(ref _secondLayerDifference, value);
- }
+ public bool SecondLayerDifference
+ {
+ get => _secondLayerDifference;
+ set => RaiseAndSetIfChanged(ref _secondLayerDifference, value);
+ }
- public byte SecondLayerDifferenceOverlapErodeIterations
- {
- get => _secondLayerDifferenceOverlapErodeIterations;
- set => RaiseAndSetIfChanged(ref _secondLayerDifferenceOverlapErodeIterations, value);
- }
+ public byte SecondLayerDifferenceOverlapErodeIterations
+ {
+ get => _secondLayerDifferenceOverlapErodeIterations;
+ set => RaiseAndSetIfChanged(ref _secondLayerDifferenceOverlapErodeIterations, value);
+ }
- public bool DifferentSettingsForSecondLayer
- {
- get => _differentSettingsForSecondLayer;
- set => RaiseAndSetIfChanged(ref _differentSettingsForSecondLayer, value);
- }
+ public bool DifferentSettingsForSecondLayer
+ {
+ get => _differentSettingsForSecondLayer;
+ set => RaiseAndSetIfChanged(ref _differentSettingsForSecondLayer, value);
+ }
- public bool SecondLayerLiftHeightEnabled
- {
- get => _secondLayerLiftHeightEnabled;
- set => RaiseAndSetIfChanged(ref _secondLayerLiftHeightEnabled, value);
- }
+ public bool SecondLayerLiftHeightEnabled
+ {
+ get => _secondLayerLiftHeightEnabled;
+ set => RaiseAndSetIfChanged(ref _secondLayerLiftHeightEnabled, value);
+ }
- public decimal SecondLayerLiftHeight
- {
- get => _secondLayerLiftHeight;
- set => RaiseAndSetIfChanged(ref _secondLayerLiftHeight, value);
- }
+ public decimal SecondLayerLiftHeight
+ {
+ get => _secondLayerLiftHeight;
+ set => RaiseAndSetIfChanged(ref _secondLayerLiftHeight, value);
+ }
- public bool SecondLayerWaitTimeBeforeCureEnabled
- {
- get => _secondLayerWaitTimeBeforeCureEnabled;
- set => RaiseAndSetIfChanged(ref _secondLayerWaitTimeBeforeCureEnabled, value);
- }
+ public bool SecondLayerWaitTimeBeforeCureEnabled
+ {
+ get => _secondLayerWaitTimeBeforeCureEnabled;
+ set => RaiseAndSetIfChanged(ref _secondLayerWaitTimeBeforeCureEnabled, value);
+ }
- public decimal SecondLayerWaitTimeBeforeCure
- {
- get => _secondLayerWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _secondLayerWaitTimeBeforeCure, value);
- }
+ public decimal SecondLayerWaitTimeBeforeCure
+ {
+ get => _secondLayerWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _secondLayerWaitTimeBeforeCure, value);
+ }
- public KernelConfiguration Kernel { get; set; } = new();
+ public KernelConfiguration Kernel { get; set; } = new();
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationDoubleExposure() { }
+ public OperationDoubleExposure() { }
- public OperationDoubleExposure(FileFormat slicerFile) : base(slicerFile)
+ public OperationDoubleExposure(FileFormat slicerFile) : base(slicerFile)
+ {
+ if (SlicerFile.SupportPerLayerSettings)
{
- if (SlicerFile.SupportPerLayerSettings)
+ _differentSettingsForSecondLayer = true;
+ if (SlicerFile.SupportsGCode)
{
- _differentSettingsForSecondLayer = true;
- if (SlicerFile.SupportsGCode)
- {
- _secondLayerLiftHeight = 0;
- _secondLayerWaitTimeBeforeCure = 2;
- }
- else
- {
- _secondLayerLiftHeight = 0.1m;
- _secondLayerWaitTimeBeforeCure = 0;
- }
+ _secondLayerLiftHeight = 0;
+ _secondLayerWaitTimeBeforeCure = 2;
+ }
+ else
+ {
+ _secondLayerLiftHeight = 0.1m;
+ _secondLayerWaitTimeBeforeCure = 0;
}
}
+ }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if (_firstBottomExposure <= 0) _firstBottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if (_firstNormalExposure <= 0) _firstNormalExposure = (decimal)SlicerFile.ExposureTime;
- if (_secondBottomExposure <= 0) _secondBottomExposure = (decimal)SlicerFile.ExposureTime;
- if (_secondNormalExposure <= 0) _secondNormalExposure = (decimal)SlicerFile.ExposureTime;
- }
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if (_firstBottomExposure <= 0) _firstBottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if (_firstNormalExposure <= 0) _firstNormalExposure = (decimal)SlicerFile.ExposureTime;
+ if (_secondBottomExposure <= 0) _secondBottomExposure = (decimal)SlicerFile.ExposureTime;
+ if (_secondNormalExposure <= 0) _secondNormalExposure = (decimal)SlicerFile.ExposureTime;
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationDoubleExposure other)
- {
- return _firstBottomExposure == other._firstBottomExposure && _firstNormalExposure == other._firstNormalExposure && _secondBottomExposure == other._secondBottomExposure && _secondNormalExposure == other._secondNormalExposure && _firstBottomErodeIterations == other._firstBottomErodeIterations && _secondBottomErodeIterations == other._secondBottomErodeIterations && _firstNormalErodeIterations == other._firstNormalErodeIterations && _secondNormalErodeIterations == other._secondNormalErodeIterations && _secondLayerDifference == other._secondLayerDifference && _secondLayerDifferenceOverlapErodeIterations == other._secondLayerDifferenceOverlapErodeIterations && _differentSettingsForSecondLayer == other._differentSettingsForSecondLayer && _secondLayerLiftHeightEnabled == other._secondLayerLiftHeightEnabled && _secondLayerLiftHeight == other._secondLayerLiftHeight && _secondLayerWaitTimeBeforeCureEnabled == other._secondLayerWaitTimeBeforeCureEnabled && _secondLayerWaitTimeBeforeCure == other._secondLayerWaitTimeBeforeCure;
- }
+ protected bool Equals(OperationDoubleExposure other)
+ {
+ return _firstBottomExposure == other._firstBottomExposure && _firstNormalExposure == other._firstNormalExposure && _secondBottomExposure == other._secondBottomExposure && _secondNormalExposure == other._secondNormalExposure && _firstBottomErodeIterations == other._firstBottomErodeIterations && _secondBottomErodeIterations == other._secondBottomErodeIterations && _firstNormalErodeIterations == other._firstNormalErodeIterations && _secondNormalErodeIterations == other._secondNormalErodeIterations && _secondLayerDifference == other._secondLayerDifference && _secondLayerDifferenceOverlapErodeIterations == other._secondLayerDifferenceOverlapErodeIterations && _differentSettingsForSecondLayer == other._differentSettingsForSecondLayer && _secondLayerLiftHeightEnabled == other._secondLayerLiftHeightEnabled && _secondLayerLiftHeight == other._secondLayerLiftHeight && _secondLayerWaitTimeBeforeCureEnabled == other._secondLayerWaitTimeBeforeCureEnabled && _secondLayerWaitTimeBeforeCure == other._secondLayerWaitTimeBeforeCure;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationDoubleExposure)obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationDoubleExposure)obj);
+ }
- public override int GetHashCode()
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_firstBottomExposure);
+ hashCode.Add(_firstNormalExposure);
+ hashCode.Add(_secondBottomExposure);
+ hashCode.Add(_secondNormalExposure);
+ hashCode.Add(_firstBottomErodeIterations);
+ hashCode.Add(_secondBottomErodeIterations);
+ hashCode.Add(_firstNormalErodeIterations);
+ hashCode.Add(_secondNormalErodeIterations);
+ hashCode.Add(_secondLayerDifference);
+ hashCode.Add(_secondLayerDifferenceOverlapErodeIterations);
+ hashCode.Add(_differentSettingsForSecondLayer);
+ hashCode.Add(_secondLayerLiftHeightEnabled);
+ hashCode.Add(_secondLayerLiftHeight);
+ hashCode.Add(_secondLayerWaitTimeBeforeCureEnabled);
+ hashCode.Add(_secondLayerWaitTimeBeforeCure);
+ return hashCode.ToHashCode();
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var anchor = new Point(-1, -1);
+
+ var layers = new Layer[SlicerFile.LayerCount+LayerRangeCount];
+
+ // Untouched
+ for (uint i = 0; i < LayerIndexStart; i++)
{
- var hashCode = new HashCode();
- hashCode.Add(_firstBottomExposure);
- hashCode.Add(_firstNormalExposure);
- hashCode.Add(_secondBottomExposure);
- hashCode.Add(_secondNormalExposure);
- hashCode.Add(_firstBottomErodeIterations);
- hashCode.Add(_secondBottomErodeIterations);
- hashCode.Add(_firstNormalErodeIterations);
- hashCode.Add(_secondNormalErodeIterations);
- hashCode.Add(_secondLayerDifference);
- hashCode.Add(_secondLayerDifferenceOverlapErodeIterations);
- hashCode.Add(_differentSettingsForSecondLayer);
- hashCode.Add(_secondLayerLiftHeightEnabled);
- hashCode.Add(_secondLayerLiftHeight);
- hashCode.Add(_secondLayerWaitTimeBeforeCureEnabled);
- hashCode.Add(_secondLayerWaitTimeBeforeCure);
- return hashCode.ToHashCode();
+ layers[i] = SlicerFile[i];
}
- #endregion
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
- #region Methods
+ var firstLayer = SlicerFile[layerIndex];
+ var secondLayer = firstLayer.Clone();
+ var isBottomLayer = firstLayer.IsBottomLayer;
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- var anchor = new Point(-1, -1);
-
- var layers = new Layer[SlicerFile.LayerCount+LayerRangeCount];
+ firstLayer.ExposureTime = (float)( isBottomLayer ? _firstBottomExposure : _firstNormalExposure);
+ secondLayer.ExposureTime = (float)(isBottomLayer ? _secondBottomExposure : _secondNormalExposure);
- // Untouched
- for (uint i = 0; i < LayerIndexStart; i++)
+ if (_differentSettingsForSecondLayer)
{
- layers[i] = SlicerFile[i];
+ if (_secondLayerLiftHeightEnabled) secondLayer.LiftHeightTotal = (float)_secondLayerLiftHeight;
+ if (_secondLayerWaitTimeBeforeCureEnabled) secondLayer.SetWaitTimeBeforeCureOrLightOffDelay((float)_secondLayerWaitTimeBeforeCure);
}
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- var firstLayer = SlicerFile[layerIndex];
- var secondLayer = firstLayer.Clone();
- var isBottomLayer = firstLayer.IsBottomLayer;
+ byte firstErodeIterations = isBottomLayer ? _firstBottomErodeIterations : _firstNormalErodeIterations;
+ byte secondErodeIterations = isBottomLayer ? _secondBottomErodeIterations : _secondNormalErodeIterations;
- firstLayer.ExposureTime = (float)( isBottomLayer ? _firstBottomExposure : _firstNormalExposure);
- secondLayer.ExposureTime = (float)(isBottomLayer ? _secondBottomExposure : _secondNormalExposure);
-
- if (_differentSettingsForSecondLayer)
+ using (var mat = firstLayer.LayerMat)
+ {
+ //using Mat matOriginal = _secondExposureLayerDifference ? mat.Clone() : null;
+ if (firstErodeIterations > 0 && firstErodeIterations == secondErodeIterations)
{
- if (_secondLayerLiftHeightEnabled) secondLayer.LiftHeightTotal = (float)_secondLayerLiftHeight;
- if (_secondLayerWaitTimeBeforeCureEnabled) secondLayer.SetWaitTimeBeforeCureOrLightOffDelay((float)_secondLayerWaitTimeBeforeCure);
- }
-
- byte firstErodeIterations = isBottomLayer ? _firstBottomErodeIterations : _firstNormalErodeIterations;
- byte secondErodeIterations = isBottomLayer ? _secondBottomErodeIterations : _secondNormalErodeIterations;
+ int tempIterations = firstErodeIterations;
+ var kernel = Kernel.GetKernel(ref tempIterations);
+ CvInvoke.Erode(mat, mat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
+ firstLayer.LayerMat = mat;
+ firstLayer.CopyImageTo(secondLayer);
- using (var mat = firstLayer.LayerMat)
+ if (_secondLayerDifference && _secondLayerDifferenceOverlapErodeIterations > 0)
+ {
+ tempIterations = _secondLayerDifferenceOverlapErodeIterations;
+ kernel = Kernel.GetKernel(ref tempIterations);
+ using var matErode = new Mat();
+ CvInvoke.Erode(mat, matErode, kernel, anchor, tempIterations, BorderType.Reflect101, default);
+ //CvInvoke.Threshold(matErode, matErode, 127, 255, ThresholdType.Binary);
+ CvInvoke.Subtract(mat, matErode, mat);
+ secondLayer.LayerMat = mat;
+ }
+ else
+ {
+ firstLayer.CopyImageTo(secondLayer);
+ }
+ }
+ else
{
- //using Mat matOriginal = _secondExposureLayerDifference ? mat.Clone() : null;
- if (firstErodeIterations > 0 && firstErodeIterations == secondErodeIterations)
+ Mat? firstMat = null;
+ Mat? secondMat = null;
+ if (firstErodeIterations > 0)
{
int tempIterations = firstErodeIterations;
var kernel = Kernel.GetKernel(ref tempIterations);
- CvInvoke.Erode(mat, mat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
- firstLayer.LayerMat = mat;
- firstLayer.CopyImageTo(secondLayer);
-
- if (_secondLayerDifference && _secondLayerDifferenceOverlapErodeIterations > 0)
- {
- tempIterations = _secondLayerDifferenceOverlapErodeIterations;
- kernel = Kernel.GetKernel(ref tempIterations);
- using var matErode = new Mat();
- CvInvoke.Erode(mat, matErode, kernel, anchor, tempIterations, BorderType.Reflect101, default);
- //CvInvoke.Threshold(matErode, matErode, 127, 255, ThresholdType.Binary);
- CvInvoke.Subtract(mat, matErode, mat);
- secondLayer.LayerMat = mat;
- }
- else
- {
- firstLayer.CopyImageTo(secondLayer);
- }
+ firstMat = new Mat();
+ CvInvoke.Erode(mat, firstMat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
+ firstLayer.LayerMat = firstMat;
}
- else
- {
- Mat firstMat = null;
- Mat secondMat = null;
- if (firstErodeIterations > 0)
- {
- int tempIterations = firstErodeIterations;
- var kernel = Kernel.GetKernel(ref tempIterations);
- firstMat = new Mat();
- CvInvoke.Erode(mat, firstMat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
- firstLayer.LayerMat = firstMat;
- }
- if (secondErodeIterations > 0)
- {
- int tempIterations = secondErodeIterations;
- var kernel = Kernel.GetKernel(ref tempIterations);
- secondMat = new Mat();
- CvInvoke.Erode(mat, secondMat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
- }
+ if (secondErodeIterations > 0)
+ {
+ int tempIterations = secondErodeIterations;
+ var kernel = Kernel.GetKernel(ref tempIterations);
+ secondMat = new Mat();
+ CvInvoke.Erode(mat, secondMat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
+ }
- if(firstMat is not null && _secondLayerDifference)
+ if(firstMat is not null && _secondLayerDifference)
+ {
+ if (firstErodeIterations + _secondLayerDifferenceOverlapErodeIterations != secondErodeIterations)
{
- if (firstErodeIterations + _secondLayerDifferenceOverlapErodeIterations != secondErodeIterations)
+ if (_secondLayerDifferenceOverlapErodeIterations > 0 &&
+ firstErodeIterations + _secondLayerDifferenceOverlapErodeIterations != secondErodeIterations)
{
- if (_secondLayerDifferenceOverlapErodeIterations > 0 &&
- firstErodeIterations + _secondLayerDifferenceOverlapErodeIterations != secondErodeIterations)
- {
- int tempIterations = _secondLayerDifferenceOverlapErodeIterations;
- var kernel = Kernel.GetKernel(ref tempIterations);
- CvInvoke.Erode(firstMat, firstMat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
- //CvInvoke.Threshold(firstMat, firstMat, 127, 255, ThresholdType.Binary);
- }
-
- CvInvoke.AbsDiff(firstMat, secondMat ?? mat, mat);
- secondLayer.LayerMat = mat;
+ int tempIterations = _secondLayerDifferenceOverlapErodeIterations;
+ var kernel = Kernel.GetKernel(ref tempIterations);
+ CvInvoke.Erode(firstMat, firstMat, kernel, anchor, tempIterations, BorderType.Reflect101, default);
+ //CvInvoke.Threshold(firstMat, firstMat, 127, 255, ThresholdType.Binary);
}
- }
- else if (secondMat is not null)
- {
- secondLayer.LayerMat = secondMat;
- }
- firstMat?.Dispose();
- secondMat?.Dispose();
+ CvInvoke.AbsDiff(firstMat, secondMat ?? mat, mat);
+ secondLayer.LayerMat = mat;
+ }
}
+ else if (secondMat is not null)
+ {
+ secondLayer.LayerMat = secondMat;
+ }
+
+ firstMat?.Dispose();
+ secondMat?.Dispose();
}
+ }
- uint index = LayerIndexStart + (uint)(layerIndex - LayerIndexStart) * 2;
+ uint index = LayerIndexStart + (uint)(layerIndex - LayerIndexStart) * 2;
- layers[index] = firstLayer;
- layers[index + 1] = secondLayer;
-
- progress.LockAndIncrement();
- });
-
- // Untouched
- for (uint i = LayerIndexEnd+1; i < SlicerFile.LayerCount; i++)
- {
- layers[i + LayerRangeCount] = SlicerFile[i];
- }
+ layers[index] = firstLayer;
+ layers[index + 1] = secondLayer;
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerManager.Layers = layers;
- });
+ progress.LockAndIncrement();
+ });
- return !progress.Token.IsCancellationRequested;
+ // Untouched
+ for (uint i = LayerIndexEnd+1; i < SlicerFile.LayerCount; i++)
+ {
+ layers[i + LayerRangeCount] = SlicerFile[i];
}
- #endregion
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.Layers = layers;
+ });
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
index f761640..ccd22d2 100644
--- a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
+++ b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
@@ -6,529 +6,409 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Text;
-using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Managers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationDynamicLayerHeight : Operation
{
- [Serializable]
- public sealed class OperationDynamicLayerHeight : Operation
+ #region Sub Classes
+ public sealed class Report
{
- #region Sub Classes
- public sealed class Report
+ private uint _oldLayerCount;
+ private uint _newLayerCount;
+ private uint _stackedLayers;
+ private float _maximumLayerHeight1;
+ private float _oldPrintTime;
+ private float _newPrintTime;
+
+ public uint OldLayerCount
{
- private uint _oldLayerCount;
- private uint _newLayerCount;
- private uint _stackedLayers;
- private float _maximumLayerHeight1;
- private float _oldPrintTime;
- private float _newPrintTime;
-
- public uint OldLayerCount
- {
- get => _oldLayerCount;
- set => _oldLayerCount = value;
- }
+ get => _oldLayerCount;
+ set => _oldLayerCount = value;
+ }
- public uint NewLayerCount
- {
- get => _newLayerCount;
- set => _newLayerCount = value;
- }
+ public uint NewLayerCount
+ {
+ get => _newLayerCount;
+ set => _newLayerCount = value;
+ }
- public uint StackedLayers
- {
- get => _stackedLayers;
- set => _stackedLayers = value;
- }
+ public uint StackedLayers
+ {
+ get => _stackedLayers;
+ set => _stackedLayers = value;
+ }
- public uint ReusedLayers => OldLayerCount - StackedLayers;
+ public uint ReusedLayers => OldLayerCount - StackedLayers;
- public float MaximumLayerHeight
- {
- get => _maximumLayerHeight1;
- set => _maximumLayerHeight1 = value;
- }
+ public float MaximumLayerHeight
+ {
+ get => _maximumLayerHeight1;
+ set => _maximumLayerHeight1 = value;
+ }
- public float OldPrintTime
- {
- get => _oldPrintTime;
- set => _oldPrintTime = value;
- }
+ public float OldPrintTime
+ {
+ get => _oldPrintTime;
+ set => _oldPrintTime = value;
+ }
- public float NewPrintTime
- {
- get => _newPrintTime;
- set => _newPrintTime = value;
- }
+ public float NewPrintTime
+ {
+ get => _newPrintTime;
+ set => _newPrintTime = value;
+ }
- public double SparedPrintTime => Math.Round(OldPrintTime - NewPrintTime, 2);
+ public double SparedPrintTime => Math.Round(OldPrintTime - NewPrintTime, 2);
- public double CompressionRatio => Math.Round((double)OldLayerCount / NewLayerCount * 100.0, 2);
+ public double CompressionRatio => Math.Round((double)OldLayerCount / NewLayerCount * 100.0, 2);
- public override string ToString()
- {
- var oldTime = TimeSpan.FromSeconds(OldPrintTime);
- var newTime = TimeSpan.FromSeconds(NewPrintTime);
- var sparedTime = TimeSpan.FromSeconds(SparedPrintTime);
- return
- $"From {OldLayerCount} layers, {ReusedLayers} got reused, {StackedLayers} got stacked and optimized with dynamic layer height's\n" +
- $"Resultant layers: {NewLayerCount}\n" +
- $"Compression ratio: {CompressionRatio}%\n" +
- $"Maximum layer height reached: {MaximumLayerHeight}mm\n" +
- $"Print time: {oldTime.Hours}h{oldTime.Minutes}m{oldTime.Seconds}s -> {newTime.Hours}h{newTime.Minutes}m{newTime.Seconds}s (- {sparedTime.Hours}h{sparedTime.Minutes}m{sparedTime.Seconds}s)";
- }
+ public override string ToString()
+ {
+ var oldTime = TimeSpan.FromSeconds(OldPrintTime);
+ var newTime = TimeSpan.FromSeconds(NewPrintTime);
+ var sparedTime = TimeSpan.FromSeconds(SparedPrintTime);
+ return
+ $"From {OldLayerCount} layers, {ReusedLayers} got reused, {StackedLayers} got stacked and optimized with dynamic layer height's\n" +
+ $"Resultant layers: {NewLayerCount}\n" +
+ $"Compression ratio: {CompressionRatio}%\n" +
+ $"Maximum layer height reached: {MaximumLayerHeight}mm\n" +
+ $"Print time: {oldTime.Hours}h{oldTime.Minutes}m{oldTime.Seconds}s -> {newTime.Hours}h{newTime.Minutes}m{newTime.Seconds}s (- {sparedTime.Hours}h{sparedTime.Minutes}m{sparedTime.Seconds}s)";
}
+ }
- #endregion
-
- #region Constants
- public const byte ObjectsPerCache = 2;
- #endregion
+ #endregion
- #region Members
+ #region Constants
+ public const byte ObjectsPerCache = 2;
+ #endregion
- private decimal _cacheRamSize = 1.5m;
- private decimal _minimumLayerHeight = 0.03m;
- private decimal _maximumLayerHeight = 0.10m;
- private bool _stripAntiAliasing;
- private bool _reconstructAntiAliasing;
- private byte _maximumErodes = 10;
+ #region Members
- private ExposureSetTypes _exposureSetType = ExposureSetTypes.Linear;
- private bool _iterateBottomExposureTime;
- private decimal _bottomExposureTime;
- private decimal _exposureTime;
- private decimal _bottomExposureStep = 0.5m;
- private decimal _exposureStep = 0.2m;
- private RangeObservableCollection<ExposureItem> _automaticExposureTable = new();
- private RangeObservableCollection<ExposureItem> _manualExposureTable = new();
+ private decimal _cacheRamSize = 1.5m;
+ private decimal _minimumLayerHeight = 0.03m;
+ private decimal _maximumLayerHeight = 0.10m;
+ private bool _stripAntiAliasing;
+ private bool _reconstructAntiAliasing;
+ private byte _maximumErodes = 10;
- #endregion
+ private ExposureSetTypes _exposureSetType = ExposureSetTypes.Linear;
+ private bool _iterateBottomExposureTime;
+ private decimal _bottomExposureTime;
+ private decimal _exposureTime;
+ private decimal _bottomExposureStep = 0.5m;
+ private decimal _exposureStep = 0.2m;
+ private RangeObservableCollection<ExposureItem> _automaticExposureTable = new();
+ private RangeObservableCollection<ExposureItem> _manualExposureTable = new();
- #region Overrides
+ #endregion
- public override bool CanROI => false;
+ #region Overrides
- public override string Title => "Dynamic layer height";
+ public override bool CanROI => false;
+ public override string IconClass => "mdi-format-line-style";
+ public override string Title => "Dynamic layer height";
- public override string Description =>
- "Analyze and optimize the model with dynamic layer heights, larger angles will slice at lower layer height" +
- " while more straight angles will slice larger layer height.\n" +
- "Note: The model should be sliced at the lowest layer height possible (0.01mm).\n" +
- "After this, do not apply any modification which reconstruct the z positions of the layers. " +
- "Only few printers support this, make sure your is supported or else it will print a malformed model.";
+ public override string Description =>
+ "Analyze and optimize the model with dynamic layer heights, larger angles will slice at lower layer height" +
+ " while more straight angles will slice larger layer height.\n" +
+ "Note: The model should be sliced at the lowest layer height possible (0.01mm).\n" +
+ "After this, do not apply any modification which reconstruct the z positions of the layers. " +
+ "Only few printers support this, make sure your is supported or else it will print a malformed model.";
- public override string ConfirmationText =>
- $"dynamic layers from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ConfirmationText =>
+ $"dynamic layers from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressTitle =>
- $"Analyzing and optimizing layers height from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Analyzing and optimizing layers height from layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => "Processed layers";
+ public override string ProgressAction => "Processed layers";
- public override string ValidateSpawn()
+ public override string? ValidateSpawn()
+ {
+ if (!SlicerFile.CanUseLayerPositionZ || !SlicerFile.CanUseLayerExposureTime)
{
- if (!SlicerFile.CanUseLayerPositionZ || !SlicerFile.CanUseLayerExposureTime)
- {
- return NotSupportedMessage;
- }
-
- if (SlicerFile.LayerHeight * 2 > FileFormat.MaximumLayerHeight)
- {
- return $"This file already uses the maximum layer height possible ({SlicerFile.LayerHeight}mm).\n" +
- "Layers can not be stacked, please re-slice your file with the lowest layer height of 0.01mm.";
- }
-
- for (uint layerIndex = 1; layerIndex < SlicerFile.LayerCount; layerIndex++)
- {
- if ((decimal)Layer.RoundHeight(SlicerFile[layerIndex].PositionZ - SlicerFile[layerIndex - 1].PositionZ) ==
- (decimal)SlicerFile.LayerHeight) continue;
- return $"This file contain layer(s) with modified positions, starting at layer {layerIndex}.\n" +
- $"This tool requires sequential layers with equal height.\n" +
- $"If you run this tool before, you cant re-run.";
- }
-
- return null;
+ return NotSupportedMessage;
}
- public override string ValidateInternally()
+ if (SlicerFile.LayerHeight * 2 > FileFormat.MaximumLayerHeight)
{
- var sb = new StringBuilder();
-
- /*if (XYResolutionUm <= 0)
- {
- sb.AppendLine($"Display width and height must be a positive value.");
- }*/
-
- decimal layerHeight = (decimal) SlicerFile.LayerHeight;
- if (_minimumLayerHeight < layerHeight)
- {
- sb.AppendLine(
- $"Minimum layer height ({_minimumLayerHeight}mm) must be equal or higher than file layer height ({layerHeight}mm)");
- }
- if (_minimumLayerHeight > _maximumLayerHeight)
- {
- sb.AppendLine(
- $"Minimum layer height ({_minimumLayerHeight}mm) can't be higher than maximum layer height ({_maximumLayerHeight}mm)");
- }
- if (layerHeight >= _maximumLayerHeight)
- {
- sb.AppendLine(
- $"Maximum layer height ({_maximumLayerHeight}mm) can't be the same or less than current file layer height ({SlicerFile.LayerHeight}mm)");
- }
-
- var exposureTable = ExposureTableDictionary;
-
- for (layerHeight = (decimal) SlicerFile.LayerHeight;
- layerHeight <= _maximumLayerHeight;
- layerHeight = layerHeight + (decimal) SlicerFile.LayerHeight)
- {
- layerHeight = Layer.RoundHeight(layerHeight);
- if (exposureTable.TryGetValue(layerHeight, out var exposure))
- {
- if (exposure.BottomExposure <= 0 || exposure.Exposure <= 0)
- {
- sb.AppendLine($"Layer height {layerHeight}mm exposures must be a positive value, current: {exposure.BottomExposure}s/{exposure.Exposure}s");
- }
- }
- else
- {
- sb.AppendLine($"Layer height {layerHeight}mm exposures are missing.");
- }
- }
-
- return sb.ToString();
+ return $"This file already uses the maximum layer height possible ({SlicerFile.LayerHeight}mm).\n" +
+ "Layers can not be stacked, please re-slice your file with the lowest layer height of 0.01mm.";
}
- public override string ToString()
+ for (uint layerIndex = 1; layerIndex < SlicerFile.LayerCount; layerIndex++)
{
- var result = $"[RAM: {_cacheRamSize}Gb] " +
- $"[Layer Height: Min: {_minimumLayerHeight}mm Max: {_maximumLayerHeight}mm] " +
- $"[Strip AA: {_stripAntiAliasing} Reconstruct AA: {_reconstructAntiAliasing}] " +
- $"[Difference: {_maximumErodes}px] " +
- $"[Bottom Exposure: {_bottomExposureTime}s Normal Exposure: {_exposureTime}s] " +
- $"[Exposure type: {_exposureSetType}, Steps: {_bottomExposureStep}s/{_exposureStep}s]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ if ((decimal)Layer.RoundHeight(SlicerFile[layerIndex].PositionZ - SlicerFile[layerIndex - 1].PositionZ) ==
+ (decimal)SlicerFile.LayerHeight) continue;
+ return $"This file contain layer(s) with modified positions, starting at layer {layerIndex}.\n" +
+ $"This tool requires sequential layers with equal height.\n" +
+ $"If you run this tool before, you cant re-run.";
}
+ return null;
+ }
- #endregion
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- #region Enums
+ /*if (XYResolutionUm <= 0)
+ {
+ sb.AppendLine($"Display width and height must be a positive value.");
+ }*/
- public enum ExposureSetTypes: byte
+ decimal layerHeight = (decimal) SlicerFile.LayerHeight;
+ if (_minimumLayerHeight < layerHeight)
{
- Linear,
- Multiplier,
- Manual
+ sb.AppendLine(
+ $"Minimum layer height ({_minimumLayerHeight}mm) must be equal or higher than file layer height ({layerHeight}mm)");
}
-
- public static Array ExposureSetTypeItems => Enum.GetValues(typeof(ExposureSetTypes));
-
- #endregion
-
- #region Properties
-
- public decimal CacheRAMSize
+ if (_minimumLayerHeight > _maximumLayerHeight)
{
- get => _cacheRamSize;
- set
- {
- if (!RaiseAndSetIfChanged(ref _cacheRamSize, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(CacheObjectCount));
- }
+ sb.AppendLine(
+ $"Minimum layer height ({_minimumLayerHeight}mm) can't be higher than maximum layer height ({_maximumLayerHeight}mm)");
+ }
+ if (layerHeight >= _maximumLayerHeight)
+ {
+ sb.AppendLine(
+ $"Maximum layer height ({_maximumLayerHeight}mm) can't be the same or less than current file layer height ({SlicerFile.LayerHeight}mm)");
}
- public uint CacheObjectCount => (uint)(_cacheRamSize * 1000000000L / SlicerFile.Resolution.Area() / ObjectsPerCache);
+ var exposureTable = ExposureTableDictionary;
- public decimal MinimumLayerHeight
+ for (layerHeight = (decimal) SlicerFile.LayerHeight;
+ layerHeight <= _maximumLayerHeight;
+ layerHeight += (decimal) SlicerFile.LayerHeight)
{
- get => _minimumLayerHeight;
- set
+ layerHeight = Layer.RoundHeight(layerHeight);
+ if (exposureTable.TryGetValue(layerHeight, out var exposure))
{
- if (!RaiseAndSetIfChanged(ref _minimumLayerHeight, Layer.RoundHeight(value))) return;
- //RaisePropertyChanged(nameof(ExposureData));
- //if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
+ if (exposure.BottomExposure <= 0 || exposure.Exposure <= 0)
+ {
+ sb.AppendLine($"Layer height {layerHeight}mm exposures must be a positive value, current: {exposure.BottomExposure}s/{exposure.Exposure}s");
+ }
}
- }
-
- public decimal MaximumLayerHeight
- {
- get => _maximumLayerHeight;
- set
+ else
{
- if(!RaiseAndSetIfChanged(ref _maximumLayerHeight, Layer.RoundHeight(value))) return;
- //RaisePropertyChanged(nameof(ExposureData));
- if(!IsExposureSetTypeManual) RebuildAutoExposureTable();
+ sb.AppendLine($"Layer height {layerHeight}mm exposures are missing.");
}
}
- public bool StripAntiAliasing
- {
- get => _stripAntiAliasing;
- set => RaiseAndSetIfChanged(ref _stripAntiAliasing, value);
- }
+ return sb.ToString();
+ }
- public bool ReconstructAntiAliasing
- {
- get => _reconstructAntiAliasing;
- set => RaiseAndSetIfChanged(ref _reconstructAntiAliasing, value);
- }
+ public override string ToString()
+ {
+ var result = $"[RAM: {_cacheRamSize}Gb] " +
+ $"[Layer Height: Min: {_minimumLayerHeight}mm Max: {_maximumLayerHeight}mm] " +
+ $"[Strip AA: {_stripAntiAliasing} Reconstruct AA: {_reconstructAntiAliasing}] " +
+ $"[Difference: {_maximumErodes}px] " +
+ $"[Bottom Exposure: {_bottomExposureTime}s Normal Exposure: {_exposureTime}s] " +
+ $"[Exposure type: {_exposureSetType}, Steps: {_bottomExposureStep}s/{_exposureStep}s]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- public byte MaximumErodes
- {
- get => _maximumErodes;
- set => RaiseAndSetIfChanged(ref _maximumErodes, value);
- }
- public ExposureSetTypes ExposureSetType
- {
- get => _exposureSetType;
- set
- {
- if(!RaiseAndSetIfChanged(ref _exposureSetType, value)) return;
- RaisePropertyChanged(nameof(IsExposureSetTypeManual));
- RaisePropertyChanged(nameof(ExposureTable));
- RaisePropertyChanged(nameof(ExposureTableDictionary));
- //RaisePropertyChanged(nameof(ExposureData));
- if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
- }
- }
+ #endregion
- public bool IsExposureSetTypeManual => _exposureSetType == ExposureSetTypes.Manual;
+ #region Enums
- public bool IterateBottomExposureTime
- {
- get => _iterateBottomExposureTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _iterateBottomExposureTime, value)) return;
- if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
- }
- }
+ public enum ExposureSetTypes: byte
+ {
+ Linear,
+ Multiplier,
+ Manual
+ }
- public decimal BottomExposureTime
- {
- get => _bottomExposureTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomExposureTime, value)) return;
- if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
- }
- }
+ public static Array ExposureSetTypeItems => Enum.GetValues(typeof(ExposureSetTypes));
- public decimal ExposureTime
- {
- get => _exposureTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _exposureTime, value)) return;
- if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
- }
-
- }
+ #endregion
- public decimal BottomExposureStep
- {
- get => _bottomExposureStep;
- set
- {
- if(!RaiseAndSetIfChanged(ref _bottomExposureStep, value)) return;
- //RaisePropertyChanged(nameof(ExposureData));
- if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
- }
- }
+ #region Properties
- public decimal ExposureStep
+ public decimal CacheRAMSize
+ {
+ get => _cacheRamSize;
+ set
{
- get => _exposureStep;
- set
- {
- if(!RaiseAndSetIfChanged(ref _exposureStep, value)) return;
- //RaisePropertyChanged(nameof(ExposureData));
- if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
- }
+ if (!RaiseAndSetIfChanged(ref _cacheRamSize, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(CacheObjectCount));
}
+ }
+
+ public uint CacheObjectCount => (uint)(_cacheRamSize * 1000000000L / SlicerFile.Resolution.Area() / ObjectsPerCache);
- [XmlIgnore]
- public RangeObservableCollection<ExposureItem> AutomaticExposureTable
+ public decimal MinimumLayerHeight
+ {
+ get => _minimumLayerHeight;
+ set
{
- get
- {
- if(_automaticExposureTable.Count == 0) RebuildAutoExposureTable();
- return _automaticExposureTable;
- }
- set => RaiseAndSetIfChanged(ref _automaticExposureTable, value);
+ if (!RaiseAndSetIfChanged(ref _minimumLayerHeight, Layer.RoundHeight(value))) return;
+ //RaisePropertyChanged(nameof(ExposureData));
+ //if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+ }
- public RangeObservableCollection<ExposureItem> ManualExposureTable
+ public decimal MaximumLayerHeight
+ {
+ get => _maximumLayerHeight;
+ set
{
- get => _manualExposureTable;
- set => RaiseAndSetIfChanged(ref _manualExposureTable, value);
+ if(!RaiseAndSetIfChanged(ref _maximumLayerHeight, Layer.RoundHeight(value))) return;
+ //RaisePropertyChanged(nameof(ExposureData));
+ if(!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+ }
- [XmlIgnore]
- public RangeObservableCollection<ExposureItem> ExposureTable => IsExposureSetTypeManual ? _manualExposureTable : AutomaticExposureTable;
+ public bool StripAntiAliasing
+ {
+ get => _stripAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _stripAntiAliasing, value);
+ }
- /// <summary>
- /// Gets the exposure table into a dictionary where key is the layer height
- /// </summary>
- [XmlIgnore]
- public Dictionary<decimal, ExposureItem> ExposureTableDictionary
- {
- get
- {
- Dictionary<decimal, ExposureItem> dictionary = new();
- foreach (var exposure in ExposureTable)
- {
- dictionary.TryAdd(exposure.LayerHeight, exposure);
- }
+ public bool ReconstructAntiAliasing
+ {
+ get => _reconstructAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _reconstructAntiAliasing, value);
+ }
- return dictionary;
- }
- }
+ public byte MaximumErodes
+ {
+ get => _maximumErodes;
+ set => RaiseAndSetIfChanged(ref _maximumErodes, value);
+ }
- public string ExposureData
+ public ExposureSetTypes ExposureSetType
+ {
+ get => _exposureSetType;
+ set
{
- get
- {
- StringBuilder sb = new();
- byte count = 0;
- for (decimal layerHeight = (decimal) SlicerFile.LayerHeight; layerHeight <= _maximumLayerHeight; layerHeight+= (decimal)SlicerFile.LayerHeight)
- {
- decimal bottomExposure = 0;
- decimal exposure = 0;
- switch (_exposureSetType)
- {
- case ExposureSetTypes.Linear:
- bottomExposure = _iterateBottomExposureTime ? _bottomExposureTime + count * _bottomExposureStep : _bottomExposureTime;
- exposure = _exposureTime + count * _exposureStep;
- break;
- case ExposureSetTypes.Multiplier:
- bottomExposure = _iterateBottomExposureTime ? _bottomExposureTime + _bottomExposureTime * count * layerHeight * _bottomExposureStep : _bottomExposureTime;
- exposure = _exposureTime + _exposureTime * count * layerHeight * _exposureStep;
- break;
- case ExposureSetTypes.Manual:
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- sb.AppendLine($"{layerHeight:F2}mm: {bottomExposure:F2}s / {exposure:F2}s");
- count++;
- }
- return sb.ToString();
- }
+ if(!RaiseAndSetIfChanged(ref _exposureSetType, value)) return;
+ RaisePropertyChanged(nameof(IsExposureSetTypeManual));
+ RaisePropertyChanged(nameof(ExposureTable));
+ RaisePropertyChanged(nameof(ExposureTableDictionary));
+ //RaisePropertyChanged(nameof(ExposureData));
+ if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+ }
- #endregion
-
- #region Constructor
+ public bool IsExposureSetTypeManual => _exposureSetType == ExposureSetTypes.Manual;
- public OperationDynamicLayerHeight()
+ public bool IterateBottomExposureTime
+ {
+ get => _iterateBottomExposureTime;
+ set
{
- //InitManualTable();
+ if(!RaiseAndSetIfChanged(ref _iterateBottomExposureTime, value)) return;
+ if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+ }
- public OperationDynamicLayerHeight(FileFormat slicerFile) : base(slicerFile)
+ public decimal BottomExposureTime
+ {
+ get => _bottomExposureTime;
+ set
{
- InitManualTable();
+ if(!RaiseAndSetIfChanged(ref _bottomExposureTime, value)) return;
+ if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+ }
- public override void InitWithSlicerFile()
+ public decimal ExposureTime
+ {
+ get => _exposureTime;
+ set
{
- base.InitWithSlicerFile();
-
- var layerHeight = (decimal)SlicerFile.LayerHeight;
- if (_minimumLayerHeight < layerHeight)
- {
- _minimumLayerHeight = layerHeight;
- }
- if (layerHeight * 2 > _maximumLayerHeight)
- {
- _maximumLayerHeight = Math.Min((decimal) FileFormat.MaximumLayerHeight, _maximumLayerHeight*2);
- }
- if (_bottomExposureTime <= 0)
- _bottomExposureTime = (decimal)SlicerFile.BottomExposureTime;
- if (_exposureTime <= 0)
- _exposureTime = (decimal)SlicerFile.ExposureTime;
-
+ if(!RaiseAndSetIfChanged(ref _exposureTime, value)) return;
+ if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+
+ }
- public void InitManualTable()
+ public decimal BottomExposureStep
+ {
+ get => _bottomExposureStep;
+ set
{
- for (decimal layerHeight = (decimal)FileFormat.MinimumLayerHeight;
- layerHeight <= (decimal) FileFormat.MaximumLayerHeight;
- layerHeight += (decimal)FileFormat.MinimumLayerHeight)
- {
- var item = new ExposureItem(layerHeight, _bottomExposureTime, _exposureTime);
- //item.BottomExposure = _bottomExposureTime;
- //item.Exposure = _exposureTime;
- /*if (layerHeight == (decimal) SlicerFile.LayerHeight)
- {
-
- }*/
- _manualExposureTable.Add(item);
- }
+ if(!RaiseAndSetIfChanged(ref _bottomExposureStep, value)) return;
+ //RaisePropertyChanged(nameof(ExposureData));
+ if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+ }
- #endregion
-
- #region Equality
-
- private bool Equals(OperationDynamicLayerHeight other)
+ public decimal ExposureStep
+ {
+ get => _exposureStep;
+ set
{
- return _cacheRamSize == other._cacheRamSize && _minimumLayerHeight == other._minimumLayerHeight && _maximumLayerHeight == other._maximumLayerHeight && _stripAntiAliasing == other._stripAntiAliasing && _reconstructAntiAliasing == other._reconstructAntiAliasing && _maximumErodes == other._maximumErodes && _exposureSetType == other._exposureSetType && _iterateBottomExposureTime == other._iterateBottomExposureTime && _bottomExposureTime == other._bottomExposureTime && _exposureTime == other._exposureTime && _bottomExposureStep == other._bottomExposureStep && _exposureStep == other._exposureStep && Equals(_manualExposureTable, other._manualExposureTable);
+ if(!RaiseAndSetIfChanged(ref _exposureStep, value)) return;
+ //RaisePropertyChanged(nameof(ExposureData));
+ if (!IsExposureSetTypeManual) RebuildAutoExposureTable();
}
+ }
- public override bool Equals(object obj)
+ [XmlIgnore]
+ public RangeObservableCollection<ExposureItem> AutomaticExposureTable
+ {
+ get
{
- return ReferenceEquals(this, obj) || obj is OperationDynamicLayerHeight other && Equals(other);
+ if(_automaticExposureTable.Count == 0) RebuildAutoExposureTable();
+ return _automaticExposureTable;
}
+ set => RaiseAndSetIfChanged(ref _automaticExposureTable, value);
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_cacheRamSize);
- hashCode.Add(_minimumLayerHeight);
- hashCode.Add(_maximumLayerHeight);
- hashCode.Add(_stripAntiAliasing);
- hashCode.Add(_reconstructAntiAliasing);
- hashCode.Add(_maximumErodes);
- hashCode.Add((int)_exposureSetType);
- hashCode.Add(_iterateBottomExposureTime);
- hashCode.Add(_bottomExposureTime);
- hashCode.Add(_exposureTime);
- hashCode.Add(_bottomExposureStep);
- hashCode.Add(_exposureStep);
- hashCode.Add(_manualExposureTable);
- return hashCode.ToHashCode();
- }
+ public RangeObservableCollection<ExposureItem> ManualExposureTable
+ {
+ get => _manualExposureTable;
+ set => RaiseAndSetIfChanged(ref _manualExposureTable, value);
+ }
- #endregion
+ [XmlIgnore]
+ public RangeObservableCollection<ExposureItem> ExposureTable => IsExposureSetTypeManual ? _manualExposureTable : AutomaticExposureTable;
- #region Methods
+ /// <summary>
+ /// Gets the exposure table into a dictionary where key is the layer height
+ /// </summary>
+ [XmlIgnore]
+ public Dictionary<decimal, ExposureItem> ExposureTableDictionary
+ {
+ get
+ {
+ Dictionary<decimal, ExposureItem> dictionary = new();
+ foreach (var exposure in ExposureTable)
+ {
+ dictionary.TryAdd(exposure.LayerHeight, exposure);
+ }
- public void RebuildAutoExposureTable()
+ return dictionary;
+ }
+ }
+
+ public string ExposureData
+ {
+ get
{
- if (SlicerFile is null) return;
- _automaticExposureTable.Clear();
+ StringBuilder sb = new();
byte count = 0;
- for (decimal layerHeight = (decimal)SlicerFile.LayerHeight; layerHeight <= _maximumLayerHeight; layerHeight += (decimal)SlicerFile.LayerHeight)
+ for (decimal layerHeight = (decimal) SlicerFile.LayerHeight; layerHeight <= _maximumLayerHeight; layerHeight+= (decimal)SlicerFile.LayerHeight)
{
decimal bottomExposure = 0;
decimal exposure = 0;
@@ -547,293 +427,411 @@ namespace UVtools.Core.Operations
default:
throw new ArgumentOutOfRangeException();
}
- _automaticExposureTable.Add(new ExposureItem(layerHeight, Math.Round(bottomExposure, 2), Math.Round(exposure, 2)));
+ sb.AppendLine($"{layerHeight:F2}mm: {bottomExposure:F2}s / {exposure:F2}s");
count++;
}
+ return sb.ToString();
}
+ }
+
+ #endregion
+
+ #region Constructor
+
+ public OperationDynamicLayerHeight()
+ {
+ //InitManualTable();
+ }
+
+ public OperationDynamicLayerHeight(FileFormat slicerFile) : base(slicerFile)
+ {
+ InitManualTable();
+ }
- public void CopyAutomaticTableToManual()
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+
+ var layerHeight = (decimal)SlicerFile.LayerHeight;
+ if (_minimumLayerHeight < layerHeight)
{
- ManualExposureTable.Clear();
- ManualExposureTable.AddRange(_automaticExposureTable);
- ExposureSetType = ExposureSetTypes.Manual;
+ _minimumLayerHeight = layerHeight;
}
+ if (layerHeight * 2 > _maximumLayerHeight)
+ {
+ _maximumLayerHeight = Math.Min((decimal) FileFormat.MaximumLayerHeight, _maximumLayerHeight*2);
+ }
+ if (_bottomExposureTime <= 0)
+ _bottomExposureTime = (decimal)SlicerFile.BottomExposureTime;
+ if (_exposureTime <= 0)
+ _exposureTime = (decimal)SlicerFile.ExposureTime;
- protected override bool ExecuteInternally(OperationProgress progress)
+ }
+
+ public void InitManualTable()
+ {
+ for (decimal layerHeight = (decimal)FileFormat.MinimumLayerHeight;
+ layerHeight <= (decimal) FileFormat.MaximumLayerHeight;
+ layerHeight += (decimal)FileFormat.MinimumLayerHeight)
{
- Report report = new()
+ var item = new ExposureItem(layerHeight, _bottomExposureTime, _exposureTime);
+ //item.BottomExposure = _bottomExposureTime;
+ //item.Exposure = _exposureTime;
+ /*if (layerHeight == (decimal) SlicerFile.LayerHeight)
{
- OldLayerCount = SlicerFile.LayerCount,
- OldPrintTime = SlicerFile.PrintTime
- };
+
+ }*/
+ _manualExposureTable.Add(item);
+ }
+ }
- var anchor = new Point(-1, -1);
- var kernel = EmguExtensions.Kernel3x3Rectangle;
+ #endregion
- var matCache = new MatCacheManager(this, (ushort)CacheObjectCount, ObjectsPerCache)
- {
- AutoDispose = true,
- AutoDisposeKeepLast = 1,
- AfterCacheAction = mats =>
- {
- mats[1] = new Mat();
- // Clean AA
- CvInvoke.Threshold(mats[0], mats[1], 127, 255, ThresholdType.Binary);
+ #region Equality
- if (_stripAntiAliasing)
- {
- mats[0].Dispose();
- mats[0] = mats[1];
- }
- }
- };
+ private bool Equals(OperationDynamicLayerHeight other)
+ {
+ return _cacheRamSize == other._cacheRamSize && _minimumLayerHeight == other._minimumLayerHeight && _maximumLayerHeight == other._maximumLayerHeight && _stripAntiAliasing == other._stripAntiAliasing && _reconstructAntiAliasing == other._reconstructAntiAliasing && _maximumErodes == other._maximumErodes && _exposureSetType == other._exposureSetType && _iterateBottomExposureTime == other._iterateBottomExposureTime && _bottomExposureTime == other._bottomExposureTime && _exposureTime == other._exposureTime && _bottomExposureStep == other._bottomExposureStep && _exposureStep == other._exposureStep && Equals(_manualExposureTable, other._manualExposureTable);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationDynamicLayerHeight other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_cacheRamSize);
+ hashCode.Add(_minimumLayerHeight);
+ hashCode.Add(_maximumLayerHeight);
+ hashCode.Add(_stripAntiAliasing);
+ hashCode.Add(_reconstructAntiAliasing);
+ hashCode.Add(_maximumErodes);
+ hashCode.Add((int)_exposureSetType);
+ hashCode.Add(_iterateBottomExposureTime);
+ hashCode.Add(_bottomExposureTime);
+ hashCode.Add(_exposureTime);
+ hashCode.Add(_bottomExposureStep);
+ hashCode.Add(_exposureStep);
+ hashCode.Add(_manualExposureTable);
+ return hashCode.ToHashCode();
+ }
- List<Layer> layers = new();
+ #endregion
- using Mat matXor = new();
- Mat matXorSum = null;
- Mat matSum = null;
+ #region Methods
- //float xyResolutionUm = SlicerFile.PixelSizeMicronsMax;
- //if (xyResolutionUm == 0) xyResolutionUm = 35;
- //const double xyRes = 35;
- //var stepAngle = Math.Atan(SlicerFile.LayerHeight*1000 / xyRes) * (180 / Math.PI);
- //byte maximumErodes = (byte) (_maximumLayerHeight * 100 - (decimal) (SlicerFile.LayerHeight * 100f));
+ public void RebuildAutoExposureTable()
+ {
+ if (SlicerFile is null) return;
+ _automaticExposureTable.Clear();
+ byte count = 0;
+ for (decimal layerHeight = (decimal)SlicerFile.LayerHeight; layerHeight <= _maximumLayerHeight; layerHeight += (decimal)SlicerFile.LayerHeight)
+ {
+ decimal bottomExposure = 0;
+ decimal exposure = 0;
+ switch (_exposureSetType)
+ {
+ case ExposureSetTypes.Linear:
+ bottomExposure = _iterateBottomExposureTime ? _bottomExposureTime + count * _bottomExposureStep : _bottomExposureTime;
+ exposure = _exposureTime + count * _exposureStep;
+ break;
+ case ExposureSetTypes.Multiplier:
+ bottomExposure = _iterateBottomExposureTime ? _bottomExposureTime + _bottomExposureTime * count * layerHeight * _bottomExposureStep : _bottomExposureTime;
+ exposure = _exposureTime + _exposureTime * count * layerHeight * _exposureStep;
+ break;
+ case ExposureSetTypes.Manual:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ _automaticExposureTable.Add(new ExposureItem(layerHeight, Math.Round(bottomExposure, 2), Math.Round(exposure, 2)));
+ count++;
+ }
+ }
+
+ public void CopyAutomaticTableToManual()
+ {
+ ManualExposureTable.Clear();
+ ManualExposureTable.AddRange(_automaticExposureTable);
+ ExposureSetType = ExposureSetTypes.Manual;
+ }
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Report report = new()
+ {
+ OldLayerCount = SlicerFile.LayerCount,
+ OldPrintTime = SlicerFile.PrintTime
+ };
- float GetLastPositionZ(float layerHeight) => layers.Count > 0 ? Layer.RoundHeight(layers[^1].PositionZ + layerHeight) : layerHeight;
+ var anchor = new Point(-1, -1);
+ var kernel = EmguExtensions.Kernel3x3Rectangle;
- void AddNewLayer(Mat mat, float layerHeight)
+ var matCache = new MatCacheManager(this, (ushort)CacheObjectCount, ObjectsPerCache)
+ {
+ AutoDispose = true,
+ AutoDisposeKeepLast = 1,
+ AfterCacheAction = mats =>
{
- if (_stripAntiAliasing && _reconstructAntiAliasing)
+ mats[1] = new Mat();
+ // Clean AA
+ CvInvoke.Threshold(mats[0], mats[1], 127, 255, ThresholdType.Binary);
+
+ if (_stripAntiAliasing)
{
- CvInvoke.GaussianBlur(mat, mat, new Size(3, 3), 0);
+ mats[0].Dispose();
+ mats[0] = mats[1];
}
+ }
+ };
- report.MaximumLayerHeight = Math.Max(report.MaximumLayerHeight, layerHeight);
- var positionZ = GetLastPositionZ(layerHeight);
- var layer = new Layer((uint) layers.Count, mat, SlicerFile)
- {
- IsModified = true,
- PositionZ = positionZ
+ List<Layer> layers = new();
- };
- layers.Add(layer);
+ using Mat matXor = new();
+ Mat? matXorSum = null;
+ Mat? matSum = null;
+
+ //float xyResolutionUm = SlicerFile.PixelSizeMicronsMax;
+ //if (xyResolutionUm == 0) xyResolutionUm = 35;
+ //const double xyRes = 35;
+ //var stepAngle = Math.Atan(SlicerFile.LayerHeight*1000 / xyRes) * (180 / Math.PI);
+ //byte maximumErodes = (byte) (_maximumLayerHeight * 100 - (decimal) (SlicerFile.LayerHeight * 100f));
+
+ float GetLastPositionZ(float layerHeight) => layers.Count > 0 ? Layer.RoundHeight(layers[^1].PositionZ + layerHeight) : layerHeight;
+
+ void AddNewLayer(Mat mat, float layerHeight)
+ {
+ if (_stripAntiAliasing && _reconstructAntiAliasing)
+ {
+ CvInvoke.GaussianBlur(mat, mat, new Size(3, 3), 0);
}
- void ReUseLayer(uint layerIndex)
+ report.MaximumLayerHeight = Math.Max(report.MaximumLayerHeight, layerHeight);
+ var positionZ = GetLastPositionZ(layerHeight);
+ var layer = new Layer((uint) layers.Count, mat, SlicerFile)
{
- var layer = SlicerFile[layerIndex];
- layer.PositionZ = GetLastPositionZ(SlicerFile.LayerHeight);
- layer.Index = (uint) layers.Count;
- layer.IsModified = true;
- if (_stripAntiAliasing)
+ IsModified = true,
+ PositionZ = positionZ
+
+ };
+ layers.Add(layer);
+ }
+
+ void ReUseLayer(uint layerIndex)
+ {
+ var layer = SlicerFile[layerIndex];
+ layer.PositionZ = GetLastPositionZ(SlicerFile.LayerHeight);
+ layer.Index = (uint) layers.Count;
+ layer.IsModified = true;
+ if (_stripAntiAliasing)
+ {
+ var matThreshold = matCache.Get(layerIndex, 1);
+ if (_reconstructAntiAliasing)
{
- var matThreshold = matCache.Get(layerIndex, 1);
- if (_reconstructAntiAliasing)
- {
- var blurMat = new Mat();
- CvInvoke.GaussianBlur(matThreshold, blurMat, new Size(3, 3), 0);
- layer.LayerMat = blurMat;
- }
- else
- {
- layer.LayerMat = matThreshold;
- }
+ var blurMat = new Mat();
+ CvInvoke.GaussianBlur(matThreshold, blurMat, new Size(3, 3), 0);
+ layer.LayerMat = blurMat;
+ }
+ else
+ {
+ layer.LayerMat = matThreshold;
}
- layers.Add(layer);
}
+ layers.Add(layer);
+ }
- for (uint layerIndex = 0; layerIndex < LayerIndexStart; layerIndex++) // Skip layers and re-use layers
+ for (uint layerIndex = 0; layerIndex < LayerIndexStart; layerIndex++) // Skip layers and re-use layers
+ {
+ ReUseLayer(layerIndex);
+ }
+
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; )
+ {
+ Debug.WriteLine($"Head Layer: {layerIndex} ({SlicerFile.LayerHeight}mm)");
+
+ if (layerIndex == LayerIndexEnd)
{
ReUseLayer(layerIndex);
+ break;
}
- for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; )
+ var currentLayerHeight = SlicerFile.LayerHeight;
+ byte layerSum = 1;
+ byte erodeCount = 0;
+ //byte maxErodeCount = 0;
+ //double maxSlop = 90;
+ matSum?.Dispose();
+ matSum = null;
+ matXorSum?.Dispose();
+ matXorSum = null;
+
+ while (true) // In a stack
{
- Debug.WriteLine($"Head Layer: {layerIndex} ({SlicerFile.LayerHeight}mm)");
+ progress.Token.ThrowIfCancellationRequested();
+ progress.ProcessedItems = layerIndex - LayerIndexStart;
- if (layerIndex == LayerIndexEnd)
+ if (currentLayerHeight >= (float)_maximumLayerHeight || layerIndex == LayerIndexEnd)
{
- ReUseLayer(layerIndex);
+ // Cant perform any additional stack. Maximum layer height reached!
+ // Break this cycle and restart from same layer
+ matSum ??= matCache.Get(layerIndex, 1).Clone(); // This only happen when layer height is already the maximum supported layer height
+ layerIndex++;
break;
}
- var currentLayerHeight = SlicerFile.LayerHeight;
- byte layerSum = 1;
- byte erodeCount = 0;
- //byte maxErodeCount = 0;
- //double maxSlop = 90;
- matSum?.Dispose();
- matSum = null;
- matXorSum?.Dispose();
- matXorSum = null;
-
- while (true) // In a stack
- {
- progress.Token.ThrowIfCancellationRequested();
- progress.ProcessedItems = layerIndex - LayerIndexStart;
-
- if (currentLayerHeight >= (float)_maximumLayerHeight || layerIndex == LayerIndexEnd)
- {
- // Cant perform any additional stack. Maximum layer height reached!
- // Break this cycle and restart from same layer
- matSum ??= matCache.Get(layerIndex, 1).Clone(); // This only happen when layer height is already the maximum supported layer height
- layerIndex++;
- break;
- }
-
- var previousLayerHeight = currentLayerHeight;
- currentLayerHeight = Layer.RoundHeight(currentLayerHeight + SlicerFile.LayerHeight);
- //var currentLayerHeightUm = currentLayerHeight * 1000;
+ var previousLayerHeight = currentLayerHeight;
+ currentLayerHeight = Layer.RoundHeight(currentLayerHeight + SlicerFile.LayerHeight);
+ //var currentLayerHeightUm = currentLayerHeight * 1000;
- var (mat1, mat1Threshold) = matCache.Get2(layerIndex);
- var (mat2, mat2Threshold) = matCache.Get2(++layerIndex);
+ var (mat1, mat1Threshold) = matCache.Get2(layerIndex);
+ var (mat2, mat2Threshold) = matCache.Get2(++layerIndex);
- Debug.Write($" Stacking layer: {layerIndex} ({currentLayerHeight}mm)");
+ Debug.Write($" Stacking layer: {layerIndex} ({currentLayerHeight}mm)");
- matSum ??= mat1.Clone();
+ matSum ??= mat1.Clone();
- CvInvoke.BitwiseXor(mat1Threshold, mat2Threshold, matXor);
- if (matXorSum is null)
- {
- matXorSum = matXor.Clone();
- }
- else
- {
- CvInvoke.Max(matXorSum, matXor, matXorSum);
- }
+ CvInvoke.BitwiseXor(mat1Threshold, mat2Threshold, matXor);
+ if (matXorSum is null)
+ {
+ matXorSum = matXor.Clone();
+ }
+ else
+ {
+ CvInvoke.Max(matXorSum, matXor, matXorSum);
+ }
- //var currentLayerHeigthUm = currentLayerHeight * 1000.0;
- //CvInvoke.Imshow("test", matXorSum);
- //CvInvoke.WaitKey();
- if (CvInvoke.CountNonZero(matXorSum) > 0) // Layers are different
+ //var currentLayerHeigthUm = currentLayerHeight * 1000.0;
+ //CvInvoke.Imshow("test", matXorSum);
+ //CvInvoke.WaitKey();
+ if (CvInvoke.CountNonZero(matXorSum) > 0) // Layers are different
//if (!matXorSum.IsZeroed(0, startPos, endPos + 1)) // Layers are different
+ {
+ //byte innerErodeCount = 0;
+ bool meetRequirement = false;
+ //using var erodeMatXor = matXorSum.Clone();
+ //Debug.WriteLine($"\n\n{layerIndex} - 0");
+ //CvInvoke.Imshow("Render", erodeMatXor.Roi(SlicerFile.BoundingRectangle));
+ //CvInvoke.WaitKey();
+ while (erodeCount < _maximumErodes)
{
- //byte innerErodeCount = 0;
- bool meetRequirement = false;
- //using var erodeMatXor = matXorSum.Clone();
- //Debug.WriteLine($"\n\n{layerIndex} - 0");
+ //innerErodeCount++;
+ erodeCount++;
+ //maxErodeCount = Math.Max(maxErodeCount, erodeCount);
+
+ /*var slope = Math.Atan(currentLayerHeightUm / (double) (xyResolutionUm * erodeCount)) * (180 / Math.PI);
+ var stepover = Math.Round(currentLayerHeightUm / Math.Tan(slope * (Math.PI / 180)));
+ Debug.Write($" [Slope: {slope:F2} Stepover: {stepover} <= {xyResolutionUm} = {stepover <= xyResolutionUm}]");
+
+ if (stepover > xyResolutionUm)
+ {
+ break;
+ }*/
+ //Debug.WriteLine($"{layerIndex} - {erodeCount}");
+ CvInvoke.Erode(matXorSum, matXor, kernel, anchor, 1, BorderType.Reflect101, default);
//CvInvoke.Imshow("Render", erodeMatXor.Roi(SlicerFile.BoundingRectangle));
//CvInvoke.WaitKey();
- while (erodeCount < _maximumErodes)
- {
- //innerErodeCount++;
- erodeCount++;
- //maxErodeCount = Math.Max(maxErodeCount, erodeCount);
-
- /*var slope = Math.Atan(currentLayerHeightUm / (double) (xyResolutionUm * erodeCount)) * (180 / Math.PI);
- var stepover = Math.Round(currentLayerHeightUm / Math.Tan(slope * (Math.PI / 180)));
- Debug.Write($" [Slope: {slope:F2} Stepover: {stepover} <= {xyResolutionUm} = {stepover <= xyResolutionUm}]");
-
- if (stepover > xyResolutionUm)
- {
- break;
- }*/
- //Debug.WriteLine($"{layerIndex} - {erodeCount}");
- CvInvoke.Erode(matXorSum, matXor, kernel, anchor, 1, BorderType.Reflect101, default);
- //CvInvoke.Imshow("Render", erodeMatXor.Roi(SlicerFile.BoundingRectangle));
- //CvInvoke.WaitKey();
- if (CvInvoke.CountNonZero(matXor) == 0)
+ if (CvInvoke.CountNonZero(matXor) == 0)
//if (erodeMatXor.IsZeroed(0, startPos, endPos+1)) // Image pixels exhausted and got empty image, can pack and go next
- {
- meetRequirement = true;
- break;
- }
- }
-
- //if ((!meetRequirement || erodeCount >= _maximumErodes) && _minimumLayerHeight < (decimal) currentLayerHeight
- // To many pixels, image still not blank, pack the previous group and start again from current height
- if (!meetRequirement && _minimumLayerHeight < (decimal) currentLayerHeight)
{
- currentLayerHeight = previousLayerHeight;
- Debug.WriteLine(string.Empty);
+ meetRequirement = true;
break;
- }
-
- if (erodeCount > 0) // Sum only if layers are different from the stack
- {
- CvInvoke.Max(matSum, mat2, matSum);
- }
+ }
}
- else
+
+ //if ((!meetRequirement || erodeCount >= _maximumErodes) && _minimumLayerHeight < (decimal) currentLayerHeight
+ // To many pixels, image still not blank, pack the previous group and start again from current height
+ if (!meetRequirement && _minimumLayerHeight < (decimal) currentLayerHeight)
{
- //erodeCount++; // Safe check
- Debug.Write(" [Equal layer]");
+ currentLayerHeight = previousLayerHeight;
+ Debug.WriteLine(string.Empty);
+ break;
}
- layerSum++;
-
- Debug.WriteLine(string.Empty);
+ if (erodeCount > 0) // Sum only if layers are different from the stack
+ {
+ CvInvoke.Max(matSum, mat2, matSum);
+ }
}
-
- if (layerSum > 1) report.StackedLayers += layerSum;
- Debug.WriteLine($" Packing {layerSum} layers with {currentLayerHeight}mm");
- // Add the result
-
-
- var positionZ = GetLastPositionZ(currentLayerHeight);
- if ((decimal)positionZ != (decimal)SlicerFile[layerIndex-1].PositionZ)
+ else
{
- Debug.WriteLine($"{layerIndex}: ({positionZ}mm != {SlicerFile[layerIndex-1].PositionZ}mm) Height mismatch!!");
- throw new InvalidOperationException($"Model height integrity has been violated at layer {layerIndex}/{layers.Count} ({positionZ}mm != {SlicerFile[layerIndex - 1].PositionZ}mm), this operation will not proceed.");
+ //erodeCount++; // Safe check
+ Debug.Write(" [Equal layer]");
}
- AddNewLayer(matSum, currentLayerHeight);
+
+ layerSum++;
+
+ Debug.WriteLine(string.Empty);
}
- for (uint layerIndex = LayerIndexEnd+1; layerIndex < SlicerFile.LayerCount; layerIndex++) // Add left-overs
+ if (layerSum > 1) report.StackedLayers += layerSum;
+ Debug.WriteLine($" Packing {layerSum} layers with {currentLayerHeight}mm");
+ // Add the result
+
+
+ var positionZ = GetLastPositionZ(currentLayerHeight);
+ if ((decimal)positionZ != (decimal)SlicerFile[layerIndex-1].PositionZ)
{
- ReUseLayer(layerIndex);
+ Debug.WriteLine($"{layerIndex}: ({positionZ}mm != {SlicerFile[layerIndex-1].PositionZ}mm) Height mismatch!!");
+ throw new InvalidOperationException($"Model height integrity has been violated at layer {layerIndex}/{layers.Count} ({positionZ}mm != {SlicerFile[layerIndex - 1].PositionZ}mm), this operation will not proceed.");
}
+ AddNewLayer(matSum, currentLayerHeight);
+ }
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.BottomExposureTime = (float)_bottomExposureTime;
- SlicerFile.ExposureTime = (float)_exposureTime;
- SlicerFile.LayerManager.Layers = layers.ToArray();
- }, true, false);
-
- // Set exposures times per layer
- var exposureDictionary = ExposureTableDictionary;
- for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++)
- {
- var layer = SlicerFile[layerIndex];
- var bottomExposure = _bottomExposureTime;
- var exposure = _exposureTime;
- if(exposureDictionary.TryGetValue((decimal)layer.LayerHeight, out var item))
- {
- bottomExposure = item.BottomExposure;
- exposure = item.Exposure;
- }
+ for (uint layerIndex = LayerIndexEnd+1; layerIndex < SlicerFile.LayerCount; layerIndex++) // Add left-overs
+ {
+ ReUseLayer(layerIndex);
+ }
- layer.ExposureTime = (float)SlicerFile.GetBottomOrNormalValue(layer, bottomExposure, exposure);
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.BottomExposureTime = (float)_bottomExposureTime;
+ SlicerFile.ExposureTime = (float)_exposureTime;
+ SlicerFile.Layers = layers.ToArray();
+ }, true, false);
+
+ // Set exposures times per layer
+ var exposureDictionary = ExposureTableDictionary;
+ for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++)
+ {
+ var layer = SlicerFile[layerIndex];
+ var bottomExposure = _bottomExposureTime;
+ var exposure = _exposureTime;
+ if(exposureDictionary.TryGetValue((decimal)layer.LayerHeight, out var item))
+ {
+ bottomExposure = item.BottomExposure;
+ exposure = item.Exposure;
}
- //var layer = slicerFile.LayerManager.Layers[^1];
- /*Debug.WriteLine(layer.ExposureTime);
+ layer.ExposureTime = (float)SlicerFile.GetBottomOrNormalValue(layer, bottomExposure, exposure);
+ }
+ //var layer = slicerFile.LayerManager.Layers[^1];
+
+ /*Debug.WriteLine(layer.ExposureTime);
+ Debug.WriteLine(layer.Index);
+ Debug.WriteLine(layer.Filename);
+ Debug.WriteLine(layer.IsNormalLayer);
+ Debug.WriteLine(layer.IsBottomLayer);
+ Debug.WriteLine(layer.LiftHeight);
+ Debug.WriteLine(layer.LiftSpeed);
+ Debug.WriteLine(layer.LightOffDelay);
+ Debug.WriteLine(layer.LightPWM);
+ Debug.WriteLine(layer.BoundingRectangle);*/
+ //Debug.WriteLine(layer.LayerHeight);
+ //Debug.WriteLine(layer);
+ /*Debug.WriteLine(slicerFile.LayerManager);
+ foreach (var layer in slicerFile)
+ {
Debug.WriteLine(layer.Index);
- Debug.WriteLine(layer.Filename);
- Debug.WriteLine(layer.IsNormalLayer);
- Debug.WriteLine(layer.IsBottomLayer);
- Debug.WriteLine(layer.LiftHeight);
- Debug.WriteLine(layer.LiftSpeed);
- Debug.WriteLine(layer.LightOffDelay);
- Debug.WriteLine(layer.LightPWM);
- Debug.WriteLine(layer.BoundingRectangle);*/
- //Debug.WriteLine(layer.LayerHeight);
- //Debug.WriteLine(layer);
- /*Debug.WriteLine(slicerFile.LayerManager);
- foreach (var layer in slicerFile)
- {
- Debug.WriteLine(layer.Index);
- }*/
+ }*/
- report.NewLayerCount = SlicerFile.LayerCount;
- report.NewPrintTime = SlicerFile.PrintTime;
- Tag = report;
+ report.NewLayerCount = SlicerFile.LayerCount;
+ report.NewPrintTime = SlicerFile.PrintTime;
+ Tag = report;
- return true;
- }
-
- #endregion
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationDynamicLifts.cs b/UVtools.Core/Operations/OperationDynamicLifts.cs
index 53d4f6d..509e6fc 100644
--- a/UVtools.Core/Operations/OperationDynamicLifts.cs
+++ b/UVtools.Core/Operations/OperationDynamicLifts.cs
@@ -10,434 +10,432 @@ using System;
using System.ComponentModel;
using System.Linq;
using System.Text;
-using MoreLinq.Extensions;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationDynamicLifts : Operation
{
- [Serializable]
- public sealed class OperationDynamicLifts : Operation
+ #region Enums
+
+ public enum DynamicLiftsSetMethod : byte
{
- #region Enums
+ // Reduces maximal lift height with the number of pixels in layer divided by maximal number of pixels in any layer. Increases the minimal speed with the same ratio.
+ [Description("Traditional: Reduces maximal lift height with the surface area divided by the maximal of all layers")]
+ Traditional,
+ //Squeezes lift height and lift speed within full range of min/max values. E.g. the layer with the least pixels gets minimal lift height and maximal lift speed. The layer with the most pixels gets maximal lift height and minimal lift speed.
+ [Description("Full Range: Squeezes lift height and lift speed within full range of smallest/largest values")]
+ FullRange
+ }
- public enum DynamicLiftsSetMethod : byte
- {
- // Reduces maximal lift height with the number of pixels in layer divided by maximal number of pixels in any layer. Increases the minimal speed with the same ratio.
- [Description("Traditional: Reduces maximal lift height with the surface area divided by the maximal of all layers")]
- Traditional,
- //Squeezes lift height and lift speed within full range of min/max values. E.g. the layer with the least pixels gets minimal lift height and maximal lift speed. The layer with the most pixels gets maximal lift height and minimal lift speed.
- [Description("Full Range: Squeezes lift height and lift speed within full range of smallest/largest values")]
- FullRange
- }
+ public enum DynamicLiftsLightOffDelaySetMode : byte
+ {
+ [Description("Set the light-off with an extra delay")]
+ UpdateWithExtraDelay,
- public enum DynamicLiftsLightOffDelaySetMode : byte
- {
- [Description("Set the light-off with an extra delay")]
- UpdateWithExtraDelay,
+ [Description("Set the light-off without an extra delay")]
+ UpdateWithoutExtraDelay,
- [Description("Set the light-off without an extra delay")]
- UpdateWithoutExtraDelay,
+ [Description("Set the light-off to zero")]
+ SetToZero,
+ }
+ #endregion
- [Description("Set the light-off to zero")]
- SetToZero,
- }
- #endregion
+ #region Members
+
+ private DynamicLiftsSetMethod _setMethod = DynamicLiftsSetMethod.Traditional;
+ private float _smallestBottomLiftHeight;
+ private float _largestBottomLiftHeight;
+ private float _smallestLiftHeight;
+ private float _largestLiftHeight;
+ private float _slowestBottomLiftSpeed;
+ private float _fastestBottomLiftSpeed;
+ private float _slowestLiftSpeed;
+ private float _fastestLiftSpeed;
- #region Members
+ private float _lightOffDelayBottomExtraTime = 3;
+ private float _lightOffDelayExtraTime = 2.5f;
+ private DynamicLiftsLightOffDelaySetMode _lightOffDelaySetMode = DynamicLiftsLightOffDelaySetMode.UpdateWithExtraDelay;
- private DynamicLiftsSetMethod _setMethod = DynamicLiftsSetMethod.Traditional;
- private float _smallestBottomLiftHeight;
- private float _largestBottomLiftHeight;
- private float _smallestLiftHeight;
- private float _largestLiftHeight;
- private float _slowestBottomLiftSpeed;
- private float _fastestBottomLiftSpeed;
- private float _slowestLiftSpeed;
- private float _fastestLiftSpeed;
+ #endregion
- private float _lightOffDelayBottomExtraTime = 3;
- private float _lightOffDelayExtraTime = 2.5f;
- private DynamicLiftsLightOffDelaySetMode _lightOffDelaySetMode = DynamicLiftsLightOffDelaySetMode.UpdateWithExtraDelay;
+ #region Overrides
- #endregion
+ public override bool CanRunInPartialMode => true;
- #region Overrides
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
+ public override string IconClass => "fas fa-chart-line";
+ public override string Title => "Dynamic lifts";
- public override bool CanRunInPartialMode => true;
+ public override string Description =>
+ "Generate dynamic lift height and speeds for each layer given it surface area.\n" +
+ "Larger surface areas requires more lift height and less speed while smaller surface areas can go with shorter lift height and more speed.\n" +
+ "If you have a raft, start after it layer number to not influence the calculations.\n" +
+ "Note: Only few printers support this. Running this on an unsupported printer will cause no harm.";
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
+ public override string ConfirmationText =>
+ $"generate dynamic lifts from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string Title => "Dynamic lifts";
+ public override string ProgressTitle =>
+ $"Generating dynamic lifts from layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string Description =>
- "Generate dynamic lift height and speeds for each layer given it surface area.\n" +
- "Larger surface areas requires more lift height and less speed while smaller surface areas can go with shorter lift height and more speed.\n" +
- "If you have a raft, start after it layer number to not influence the calculations.\n" +
- "Note: Only few printers support this. Running this on an unsupported printer will cause no harm.";
+ public override string ProgressAction => "Generated lifts";
- public override string ConfirmationText =>
- $"generate dynamic lifts from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string? ValidateSpawn()
+ {
+ if (!SlicerFile.CanUseLayerLiftHeight || !SlicerFile.CanUseLayerLiftSpeed)
+ {
+ return NotSupportedMessage;
+ }
- public override string ProgressTitle =>
- $"Generating dynamic lifts from layers {LayerIndexStart} through {LayerIndexEnd}";
+ return null;
+ }
- public override string ProgressAction => "Generated lifts";
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ValidateSpawn()
+ if (_smallestBottomLiftHeight > _largestBottomLiftHeight)
{
- if (!SlicerFile.CanUseLayerLiftHeight || !SlicerFile.CanUseLayerLiftSpeed)
- {
- return NotSupportedMessage;
- }
+ sb.AppendLine("Smallest bottom lift height can't be higher than the largest.");
+ }
+ if (_slowestBottomLiftSpeed > _fastestBottomLiftSpeed)
+ {
+ sb.AppendLine("Slowest bottom lift speed can't be higher than the fastest.");
+ }
- return null;
+ if (_smallestLiftHeight > _largestLiftHeight)
+ {
+ sb.AppendLine("Smallest lift height can't be higher than the largest.");
+ }
+ if (_slowestLiftSpeed > _fastestLiftSpeed)
+ {
+ sb.AppendLine("Slowest lift speed can't be higher than the fastest.");
}
- public override string ValidateInternally()
+ if (_smallestBottomLiftHeight == _largestBottomLiftHeight &&
+ _slowestBottomLiftSpeed == _fastestBottomLiftSpeed &&
+ _smallestLiftHeight == _largestLiftHeight &&
+ _slowestLiftSpeed == _fastestLiftSpeed)
{
- var sb = new StringBuilder();
+ sb.AppendLine("The selected smallest/largest settings are all equal and will not produce a change.");
+ }
- if (_smallestBottomLiftHeight > _largestBottomLiftHeight)
- {
- sb.AppendLine("Smallest bottom lift height can't be higher than the largest.");
- }
- if (_slowestBottomLiftSpeed > _fastestBottomLiftSpeed)
- {
- sb.AppendLine("Slowest bottom lift speed can't be higher than the fastest.");
- }
+ return sb.ToString();
+ }
- if (_smallestLiftHeight > _largestLiftHeight)
- {
- sb.AppendLine("Smallest lift height can't be higher than the largest.");
- }
- if (_slowestLiftSpeed > _fastestLiftSpeed)
- {
- sb.AppendLine("Slowest lift speed can't be higher than the fastest.");
- }
+ public override string ToString()
+ {
+ var result =
+ $"[Method: {_setMethod}]" +
+ $" [Bottom height: {_smallestBottomLiftHeight}/{_largestBottomLiftHeight}mm]" +
+ $" [Bottom speed: {_slowestBottomLiftSpeed}/{_fastestBottomLiftSpeed}mm/min]" +
+ $" [Height: {_smallestLiftHeight}/{_largestLiftHeight}mm]" +
+ $" [Speed: {_slowestLiftSpeed}/{_fastestLiftSpeed}mm/min]" +
+ $" [Light-off: {_lightOffDelaySetMode} {_lightOffDelayBottomExtraTime}/{_lightOffDelayExtraTime}s]" +
+ LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- if (_smallestBottomLiftHeight == _largestBottomLiftHeight &&
- _slowestBottomLiftSpeed == _fastestBottomLiftSpeed &&
- _smallestLiftHeight == _largestLiftHeight &&
- _slowestLiftSpeed == _fastestLiftSpeed)
- {
- sb.AppendLine("The selected smallest/largest settings are all equal and will not produce a change.");
- }
+ #endregion
- return sb.ToString();
- }
+ #region Properties
- public override string ToString()
- {
- var result =
- $"[Method: {_setMethod}]" +
- $" [Bottom height: {_smallestBottomLiftHeight}/{_largestBottomLiftHeight}mm]" +
- $" [Bottom speed: {_slowestBottomLiftSpeed}/{_fastestBottomLiftSpeed}mm/min]" +
- $" [Height: {_smallestLiftHeight}/{_largestLiftHeight}mm]" +
- $" [Speed: {_slowestLiftSpeed}/{_fastestLiftSpeed}mm/min]" +
- $" [Light-off: {_lightOffDelaySetMode} {_lightOffDelayBottomExtraTime}/{_lightOffDelayExtraTime}s]" +
- LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ public DynamicLiftsSetMethod SetMethod
+ {
+ get => _setMethod;
+ set => RaiseAndSetIfChanged(ref _setMethod, value);
+ }
- #endregion
+ public float SmallestBottomLiftHeight
+ {
+ get => _smallestBottomLiftHeight;
+ set => RaiseAndSetIfChanged(ref _smallestBottomLiftHeight, (float)Math.Round(value, 2));
+ }
- #region Properties
+ public float LargestBottomLiftHeight
+ {
+ get => _largestBottomLiftHeight;
+ set => RaiseAndSetIfChanged(ref _largestBottomLiftHeight, (float)Math.Round(value, 2));
+ }
- public DynamicLiftsSetMethod SetMethod
- {
- get => _setMethod;
- set => RaiseAndSetIfChanged(ref _setMethod, value);
- }
+ public float SmallestLiftHeight
+ {
+ get => _smallestLiftHeight;
+ set => RaiseAndSetIfChanged(ref _smallestLiftHeight, (float)Math.Round(value, 2));
+ }
- public float SmallestBottomLiftHeight
- {
- get => _smallestBottomLiftHeight;
- set => RaiseAndSetIfChanged(ref _smallestBottomLiftHeight, (float)Math.Round(value, 2));
- }
+ public float LargestLiftHeight
+ {
+ get => _largestLiftHeight;
+ set => RaiseAndSetIfChanged(ref _largestLiftHeight, (float)Math.Round(value, 2));
+ }
- public float LargestBottomLiftHeight
- {
- get => _largestBottomLiftHeight;
- set => RaiseAndSetIfChanged(ref _largestBottomLiftHeight, (float)Math.Round(value, 2));
- }
+ public float SlowestBottomLiftSpeed
+ {
+ get => _slowestBottomLiftSpeed;
+ set => RaiseAndSetIfChanged(ref _slowestBottomLiftSpeed, (float)Math.Round(value, 2));
+ }
- public float SmallestLiftHeight
- {
- get => _smallestLiftHeight;
- set => RaiseAndSetIfChanged(ref _smallestLiftHeight, (float)Math.Round(value, 2));
- }
+ public float FastestBottomLiftSpeed
+ {
+ get => _fastestBottomLiftSpeed;
+ set => RaiseAndSetIfChanged(ref _fastestBottomLiftSpeed, (float)Math.Round(value, 2));
+ }
- public float LargestLiftHeight
- {
- get => _largestLiftHeight;
- set => RaiseAndSetIfChanged(ref _largestLiftHeight, (float)Math.Round(value, 2));
- }
+ public float SlowestLiftSpeed
+ {
+ get => _slowestLiftSpeed;
+ set => RaiseAndSetIfChanged(ref _slowestLiftSpeed, (float)Math.Round(value, 2));
+ }
+
+ public float FastestLiftSpeed
+ {
+ get => _fastestLiftSpeed;
+ set => RaiseAndSetIfChanged(ref _fastestLiftSpeed, (float)Math.Round(value, 2));
+ }
+
+ public DynamicLiftsLightOffDelaySetMode LightOffDelaySetMode
+ {
+ get => _lightOffDelaySetMode;
+ set => RaiseAndSetIfChanged(ref _lightOffDelaySetMode, value);
+ }
+
+ public float LightOffDelayBottomExtraTime
+ {
+ get => _lightOffDelayBottomExtraTime;
+ set => RaiseAndSetIfChanged(ref _lightOffDelayBottomExtraTime, (float)Math.Round(value, 2));
+ }
+
+ public float LightOffDelayExtraTime
+ {
+ get => _lightOffDelayExtraTime;
+ set => RaiseAndSetIfChanged(ref _lightOffDelayExtraTime, (float)Math.Round(value, 2));
+ }
+
+ //public uint MinBottomLayerPixels => SlicerFile.Where(layer => layer.IsBottomLayer && !layer.IsEmpty && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
+ public uint MinBottomLayerPixels => (from layer in SlicerFile
+ where layer.IsBottomLayer
+ where !layer.IsEmpty
+ where layer.Index >= LayerIndexStart
+ where layer.Index <= LayerIndexEnd
+ select layer.NonZeroPixelCount).Min();
+
+ //public uint MinNormalLayerPixels => SlicerFile.Where(layer => layer.IsNormalLayer && !layer.IsEmpty && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
+ public uint MinNormalLayerPixels => (from layer in SlicerFile
+ where layer.IsNormalLayer
+ where !layer.IsEmpty
+ where layer.Index >= LayerIndexStart
+ where layer.Index <= LayerIndexEnd
+ select layer.NonZeroPixelCount).Min();
+
+ //public uint MaxBottomLayerPixels => SlicerFile.Where(layer => layer.IsBottomLayer && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
+ public uint MaxBottomLayerPixels => (from layer in SlicerFile
+ where layer.IsBottomLayer
+ where !layer.IsEmpty
+ where layer.Index >= LayerIndexStart
+ where layer.Index <= LayerIndexEnd
+ select layer.NonZeroPixelCount).Max();
+ //public uint MaxNormalLayerPixels => SlicerFile.Where(layer => layer.IsNormalLayer && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
+ public uint MaxNormalLayerPixels => (from layer in SlicerFile
+ where layer.IsNormalLayer
+ where !layer.IsEmpty
+ where layer.Index >= LayerIndexStart
+ where layer.Index <= LayerIndexEnd
+ select layer.NonZeroPixelCount).Max();
+
+ #endregion
+
+ #region Constructor
+
+ public OperationDynamicLifts()
+ { }
+
+ public OperationDynamicLifts(FileFormat slicerFile) : base(slicerFile)
+ { }
+
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
- public float SlowestBottomLiftSpeed
+ if(_smallestBottomLiftHeight <= 0) _smallestBottomLiftHeight = SlicerFile.BottomLiftHeightTotal;
+ if (_largestBottomLiftHeight <= 0 || _largestBottomLiftHeight < _smallestBottomLiftHeight) _largestBottomLiftHeight = _smallestBottomLiftHeight;
+
+ if (_smallestLiftHeight <= 0) _smallestLiftHeight = SlicerFile.LiftHeightTotal;
+ if (_largestLiftHeight <= 0 || _largestLiftHeight < _smallestLiftHeight) _largestLiftHeight = _smallestLiftHeight;
+
+ if (_slowestBottomLiftSpeed <= 0) _slowestBottomLiftSpeed = SlicerFile.BottomLiftSpeed;
+ if (_fastestBottomLiftSpeed <= 0 || _fastestBottomLiftSpeed < _slowestBottomLiftSpeed) _fastestBottomLiftSpeed = _slowestBottomLiftSpeed;
+
+ if(_slowestLiftSpeed <= 0) _slowestLiftSpeed = SlicerFile.LiftSpeed;
+ if (_fastestLiftSpeed <= 0 || _fastestLiftSpeed < _slowestLiftSpeed) _fastestLiftSpeed = _slowestLiftSpeed;
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ uint minBottomPixels = 0;
+ uint minNormalPixels = 0;
+ uint maxBottomPixels = 0;
+ uint maxNormalPixels = 0;
+
+ try
{
- get => _slowestBottomLiftSpeed;
- set => RaiseAndSetIfChanged(ref _slowestBottomLiftSpeed, (float)Math.Round(value, 2));
+ minBottomPixels = MinBottomLayerPixels;
}
-
- public float FastestBottomLiftSpeed
+ catch
{
- get => _fastestBottomLiftSpeed;
- set => RaiseAndSetIfChanged(ref _fastestBottomLiftSpeed, (float)Math.Round(value, 2));
}
- public float SlowestLiftSpeed
+ try
{
- get => _slowestLiftSpeed;
- set => RaiseAndSetIfChanged(ref _slowestLiftSpeed, (float)Math.Round(value, 2));
+ minNormalPixels = MinNormalLayerPixels;
}
-
- public float FastestLiftSpeed
+ catch
{
- get => _fastestLiftSpeed;
- set => RaiseAndSetIfChanged(ref _fastestLiftSpeed, (float)Math.Round(value, 2));
}
- public DynamicLiftsLightOffDelaySetMode LightOffDelaySetMode
+ try
{
- get => _lightOffDelaySetMode;
- set => RaiseAndSetIfChanged(ref _lightOffDelaySetMode, value);
+ maxBottomPixels = MaxBottomLayerPixels;
}
-
- public float LightOffDelayBottomExtraTime
+ catch
{
- get => _lightOffDelayBottomExtraTime;
- set => RaiseAndSetIfChanged(ref _lightOffDelayBottomExtraTime, (float)Math.Round(value, 2));
}
- public float LightOffDelayExtraTime
+ try
{
- get => _lightOffDelayExtraTime;
- set => RaiseAndSetIfChanged(ref _lightOffDelayExtraTime, (float)Math.Round(value, 2));
+ maxNormalPixels = MaxNormalLayerPixels;
}
-
- //public uint MinBottomLayerPixels => SlicerFile.Where(layer => layer.IsBottomLayer && !layer.IsEmpty && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
- public uint MinBottomLayerPixels => (from layer in SlicerFile
- where layer.IsBottomLayer
- where !layer.IsEmpty
- where layer.Index >= LayerIndexStart
- where layer.Index <= LayerIndexEnd
- select layer.NonZeroPixelCount).Min();
-
- //public uint MinNormalLayerPixels => SlicerFile.Where(layer => layer.IsNormalLayer && !layer.IsEmpty && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
- public uint MinNormalLayerPixels => (from layer in SlicerFile
- where layer.IsNormalLayer
- where !layer.IsEmpty
- where layer.Index >= LayerIndexStart
- where layer.Index <= LayerIndexEnd
- select layer.NonZeroPixelCount).Min();
-
- //public uint MaxBottomLayerPixels => SlicerFile.Where(layer => layer.IsBottomLayer && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
- public uint MaxBottomLayerPixels => (from layer in SlicerFile
- where layer.IsBottomLayer
- where !layer.IsEmpty
- where layer.Index >= LayerIndexStart
- where layer.Index <= LayerIndexEnd
- select layer.NonZeroPixelCount).Max();
- //public uint MaxNormalLayerPixels => SlicerFile.Where(layer => layer.IsNormalLayer && layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).Max(layer => layer.NonZeroPixelCount);
- public uint MaxNormalLayerPixels => (from layer in SlicerFile
- where layer.IsNormalLayer
- where !layer.IsEmpty
- where layer.Index >= LayerIndexStart
- where layer.Index <= LayerIndexEnd
- select layer.NonZeroPixelCount).Max();
-
- #endregion
-
- #region Constructor
-
- public OperationDynamicLifts()
- { }
-
- public OperationDynamicLifts(FileFormat slicerFile) : base(slicerFile)
- { }
-
- public override void InitWithSlicerFile()
+ catch
{
- base.InitWithSlicerFile();
-
- if(_smallestBottomLiftHeight <= 0) _smallestBottomLiftHeight = SlicerFile.BottomLiftHeightTotal;
- if (_largestBottomLiftHeight <= 0 || _largestBottomLiftHeight < _smallestBottomLiftHeight) _largestBottomLiftHeight = _smallestBottomLiftHeight;
-
- if (_smallestLiftHeight <= 0) _smallestLiftHeight = SlicerFile.LiftHeightTotal;
- if (_largestLiftHeight <= 0 || _largestLiftHeight < _smallestLiftHeight) _largestLiftHeight = _smallestLiftHeight;
-
- if (_slowestBottomLiftSpeed <= 0) _slowestBottomLiftSpeed = SlicerFile.BottomLiftSpeed;
- if (_fastestBottomLiftSpeed <= 0 || _fastestBottomLiftSpeed < _slowestBottomLiftSpeed) _fastestBottomLiftSpeed = _slowestBottomLiftSpeed;
-
- if(_slowestLiftSpeed <= 0) _slowestLiftSpeed = SlicerFile.LiftSpeed;
- if (_fastestLiftSpeed <= 0 || _fastestLiftSpeed < _slowestLiftSpeed) _fastestLiftSpeed = _slowestLiftSpeed;
}
- #endregion
+ float liftHeight = 0;
+ float liftSpeed = 0;
- #region Methods
+ //uint max = (from layer in SlicerFile where !layer.IsBottomLayer where !layer.IsEmpty where layer.Index >= LayerIndexStart where layer.Index <= LayerIndexEnd select layer).Aggregate<Layer, uint>(0, (current, layer) => Math.Max(layer.NonZeroPixelCount, current));
- protected override bool ExecuteInternally(OperationProgress progress)
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
{
- uint minBottomPixels = 0;
- uint minNormalPixels = 0;
- uint maxBottomPixels = 0;
- uint maxNormalPixels = 0;
-
- try
- {
- minBottomPixels = MinBottomLayerPixels;
- }
- catch
- {
- }
-
- try
- {
- minNormalPixels = MinNormalLayerPixels;
- }
- catch
- {
- }
-
- try
- {
- maxBottomPixels = MaxBottomLayerPixels;
- }
- catch
- {
- }
-
- try
- {
- maxNormalPixels = MaxNormalLayerPixels;
- }
- catch
- {
- }
-
- float liftHeight = 0;
- float liftSpeed = 0;
-
- //uint max = (from layer in SlicerFile where !layer.IsBottomLayer where !layer.IsEmpty where layer.Index >= LayerIndexStart where layer.Index <= LayerIndexEnd select layer).Aggregate<Layer, uint>(0, (current, layer) => Math.Max(layer.NonZeroPixelCount, current));
-
- for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- var layer = SlicerFile[layerIndex];
+ progress.Token.ThrowIfCancellationRequested();
+ var layer = SlicerFile[layerIndex];
- // Height
- // min - largestpixelcount
- // x - pixelcount
+ // Height
+ // min - largestpixelcount
+ // x - pixelcount
- // Speed
- // max - minpixelCount
- // x - pixelcount
+ // Speed
+ // max - minpixelCount
+ // x - pixelcount
- if (layer.IsBottomLayer)
- {
- switch (_setMethod)
- {
- case DynamicLiftsSetMethod.Traditional:
- liftHeight = (_largestBottomLiftHeight * layer.NonZeroPixelCount / maxBottomPixels).Clamp(_smallestBottomLiftHeight, _largestBottomLiftHeight);
- liftSpeed = (_fastestBottomLiftSpeed - (_fastestBottomLiftSpeed * layer.NonZeroPixelCount / maxBottomPixels)).Clamp(_slowestBottomLiftSpeed, _fastestBottomLiftSpeed);
- break;
- case DynamicLiftsSetMethod.FullRange:
- var pixelRatio = (layer.NonZeroPixelCount - minBottomPixels) / (float)(maxBottomPixels - minBottomPixels); // pixel_ratio is between 0 and 1
- liftHeight = (_smallestBottomLiftHeight + ((_largestBottomLiftHeight - _smallestBottomLiftHeight) * pixelRatio)).Clamp(_smallestBottomLiftHeight, _largestBottomLiftHeight);
- liftSpeed = (_fastestBottomLiftSpeed - ((_fastestBottomLiftSpeed - _slowestBottomLiftSpeed) * pixelRatio)).Clamp(_slowestBottomLiftSpeed, _fastestBottomLiftSpeed);
- break;
- }
-
- }
- else
+ if (layer.IsBottomLayer)
+ {
+ switch (_setMethod)
{
- switch (_setMethod)
- {
- case DynamicLiftsSetMethod.Traditional:
- liftHeight = (_largestLiftHeight * layer.NonZeroPixelCount / maxNormalPixels).Clamp(_smallestLiftHeight, _largestLiftHeight);
- liftSpeed = (_fastestLiftSpeed - (_fastestLiftSpeed * layer.NonZeroPixelCount / maxNormalPixels)).Clamp(_slowestLiftSpeed, _fastestLiftSpeed);
- break;
- case DynamicLiftsSetMethod.FullRange:
- var pixelRatio = (layer.NonZeroPixelCount - minNormalPixels) / (float)(maxNormalPixels - minNormalPixels); // pixel_ratio is between 0 and 1
- liftHeight = (_smallestLiftHeight + ((_largestLiftHeight - _smallestLiftHeight) * pixelRatio)).Clamp(_smallestLiftHeight, _largestLiftHeight);
- liftSpeed = (_fastestLiftSpeed - ((_fastestLiftSpeed - _slowestLiftSpeed) * pixelRatio)).Clamp(_slowestLiftSpeed, _fastestLiftSpeed);
- break;
- }
+ case DynamicLiftsSetMethod.Traditional:
+ liftHeight = (_largestBottomLiftHeight * layer.NonZeroPixelCount / maxBottomPixels).Clamp(_smallestBottomLiftHeight, _largestBottomLiftHeight);
+ liftSpeed = (_fastestBottomLiftSpeed - (_fastestBottomLiftSpeed * layer.NonZeroPixelCount / maxBottomPixels)).Clamp(_slowestBottomLiftSpeed, _fastestBottomLiftSpeed);
+ break;
+ case DynamicLiftsSetMethod.FullRange:
+ var pixelRatio = (layer.NonZeroPixelCount - minBottomPixels) / (float)(maxBottomPixels - minBottomPixels); // pixel_ratio is between 0 and 1
+ liftHeight = (_smallestBottomLiftHeight + ((_largestBottomLiftHeight - _smallestBottomLiftHeight) * pixelRatio)).Clamp(_smallestBottomLiftHeight, _largestBottomLiftHeight);
+ liftSpeed = (_fastestBottomLiftSpeed - ((_fastestBottomLiftSpeed - _slowestBottomLiftSpeed) * pixelRatio)).Clamp(_slowestBottomLiftSpeed, _fastestBottomLiftSpeed);
+ break;
}
-
- layer.RetractHeight2 = 0;
- layer.LiftHeightTotal = (float) Math.Round(liftHeight, 1);
- layer.LiftSpeed = (float) Math.Round(liftSpeed, 1);
-
- switch (_lightOffDelaySetMode)
+
+ }
+ else
+ {
+ switch (_setMethod)
{
- case DynamicLiftsLightOffDelaySetMode.UpdateWithExtraDelay:
- layer.SetLightOffDelay(layer.IsBottomLayer ? _lightOffDelayBottomExtraTime : _lightOffDelayExtraTime);
- break;
- case DynamicLiftsLightOffDelaySetMode.UpdateWithoutExtraDelay:
- layer.SetLightOffDelay();
+ case DynamicLiftsSetMethod.Traditional:
+ liftHeight = (_largestLiftHeight * layer.NonZeroPixelCount / maxNormalPixels).Clamp(_smallestLiftHeight, _largestLiftHeight);
+ liftSpeed = (_fastestLiftSpeed - (_fastestLiftSpeed * layer.NonZeroPixelCount / maxNormalPixels)).Clamp(_slowestLiftSpeed, _fastestLiftSpeed);
break;
- case DynamicLiftsLightOffDelaySetMode.SetToZero:
- layer.LightOffDelay = 0;
+ case DynamicLiftsSetMethod.FullRange:
+ var pixelRatio = (layer.NonZeroPixelCount - minNormalPixels) / (float)(maxNormalPixels - minNormalPixels); // pixel_ratio is between 0 and 1
+ liftHeight = (_smallestLiftHeight + ((_largestLiftHeight - _smallestLiftHeight) * pixelRatio)).Clamp(_smallestLiftHeight, _largestLiftHeight);
+ liftSpeed = (_fastestLiftSpeed - ((_fastestLiftSpeed - _slowestLiftSpeed) * pixelRatio)).Clamp(_slowestLiftSpeed, _fastestLiftSpeed);
break;
- default:
- throw new NotImplementedException();
}
-
+ }
- progress++;
+ layer.RetractHeight2 = 0;
+ layer.LiftHeightTotal = (float) Math.Round(liftHeight, 1);
+ layer.LiftSpeed = (float) Math.Round(liftSpeed, 1);
+
+ switch (_lightOffDelaySetMode)
+ {
+ case DynamicLiftsLightOffDelaySetMode.UpdateWithExtraDelay:
+ layer.SetLightOffDelay(layer.IsBottomLayer ? _lightOffDelayBottomExtraTime : _lightOffDelayExtraTime);
+ break;
+ case DynamicLiftsLightOffDelaySetMode.UpdateWithoutExtraDelay:
+ layer.SetLightOffDelay();
+ break;
+ case DynamicLiftsLightOffDelaySetMode.SetToZero:
+ layer.LightOffDelay = 0;
+ break;
+ default:
+ throw new NotImplementedException();
}
+
- return !progress.Token.IsCancellationRequested;
+ progress++;
}
- public Layer GetSmallestLayer(bool isBottom)
- {
- return SlicerFile.Where((layer, index) => !layer.IsEmpty && layer.IsBottomLayer == isBottom && index >= LayerIndexStart && index <= LayerIndexEnd).MinBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
- }
+ return !progress.Token.IsCancellationRequested;
+ }
- public Layer GetLargestLayer(bool isBottom)
- {
- return SlicerFile.Where((layer, index) => !layer.IsEmpty && layer.IsBottomLayer == isBottom && index >= LayerIndexStart && index <= LayerIndexEnd).MaxBy(layer => layer.NonZeroPixelCount).FirstOrDefault();
- }
+ public Layer? GetSmallestLayer(bool isBottom)
+ {
+ return SlicerFile.Where((layer, index) => !layer.IsEmpty && layer.IsBottomLayer == isBottom && index >= LayerIndexStart && index <= LayerIndexEnd).MinBy(layer => layer.NonZeroPixelCount);
+ }
- #endregion
+ public Layer? GetLargestLayer(bool isBottom)
+ {
+ return SlicerFile.Where((layer, index) => !layer.IsEmpty && layer.IsBottomLayer == isBottom && index >= LayerIndexStart && index <= LayerIndexEnd).MaxBy(layer => layer.NonZeroPixelCount);
+ }
- #region Equality
+ #endregion
+ #region Equality
- private bool Equals(OperationDynamicLifts other)
- {
- return _setMethod == other._setMethod && _smallestBottomLiftHeight.Equals(other._smallestBottomLiftHeight) && _largestBottomLiftHeight.Equals(other._largestBottomLiftHeight) && _smallestLiftHeight.Equals(other._smallestLiftHeight) && _largestLiftHeight.Equals(other._largestLiftHeight) && _slowestBottomLiftSpeed.Equals(other._slowestBottomLiftSpeed) && _fastestBottomLiftSpeed.Equals(other._fastestBottomLiftSpeed) && _slowestLiftSpeed.Equals(other._slowestLiftSpeed) && _fastestLiftSpeed.Equals(other._fastestLiftSpeed) && _lightOffDelayBottomExtraTime.Equals(other._lightOffDelayBottomExtraTime) && _lightOffDelayExtraTime.Equals(other._lightOffDelayExtraTime) && _lightOffDelaySetMode == other._lightOffDelaySetMode;
- }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationDynamicLifts other && Equals(other);
- }
+ private bool Equals(OperationDynamicLifts other)
+ {
+ return _setMethod == other._setMethod && _smallestBottomLiftHeight.Equals(other._smallestBottomLiftHeight) && _largestBottomLiftHeight.Equals(other._largestBottomLiftHeight) && _smallestLiftHeight.Equals(other._smallestLiftHeight) && _largestLiftHeight.Equals(other._largestLiftHeight) && _slowestBottomLiftSpeed.Equals(other._slowestBottomLiftSpeed) && _fastestBottomLiftSpeed.Equals(other._fastestBottomLiftSpeed) && _slowestLiftSpeed.Equals(other._slowestLiftSpeed) && _fastestLiftSpeed.Equals(other._fastestLiftSpeed) && _lightOffDelayBottomExtraTime.Equals(other._lightOffDelayBottomExtraTime) && _lightOffDelayExtraTime.Equals(other._lightOffDelayExtraTime) && _lightOffDelaySetMode == other._lightOffDelaySetMode;
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add((int)_setMethod);
- hashCode.Add(_smallestBottomLiftHeight);
- hashCode.Add(_largestBottomLiftHeight);
- hashCode.Add(_smallestLiftHeight);
- hashCode.Add(_largestLiftHeight);
- hashCode.Add(_slowestBottomLiftSpeed);
- hashCode.Add(_fastestBottomLiftSpeed);
- hashCode.Add(_slowestLiftSpeed);
- hashCode.Add(_fastestLiftSpeed);
- hashCode.Add(_lightOffDelayBottomExtraTime);
- hashCode.Add(_lightOffDelayExtraTime);
- hashCode.Add((int)_lightOffDelaySetMode);
- return hashCode.ToHashCode();
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationDynamicLifts other && Equals(other);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add((int)_setMethod);
+ hashCode.Add(_smallestBottomLiftHeight);
+ hashCode.Add(_largestBottomLiftHeight);
+ hashCode.Add(_smallestLiftHeight);
+ hashCode.Add(_largestLiftHeight);
+ hashCode.Add(_slowestBottomLiftSpeed);
+ hashCode.Add(_fastestBottomLiftSpeed);
+ hashCode.Add(_slowestLiftSpeed);
+ hashCode.Add(_fastestLiftSpeed);
+ hashCode.Add(_lightOffDelayBottomExtraTime);
+ hashCode.Add(_lightOffDelayExtraTime);
+ hashCode.Add((int)_lightOffDelaySetMode);
+ return hashCode.ToHashCode();
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationEditParameters.cs b/UVtools.Core/Operations/OperationEditParameters.cs
index 99d1b8a..b3a9cdd 100644
--- a/UVtools.Core/Operations/OperationEditParameters.cs
+++ b/UVtools.Core/Operations/OperationEditParameters.cs
@@ -12,199 +12,204 @@ using System.Text;
using System.Xml.Serialization;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public class OperationEditParameters : Operation
- {
- #region Members
+namespace UVtools.Core.Operations;
- private bool _propagateModificationsToLayers = true;
- private bool _perLayerOverride;
- private uint _setNumberOfLayer = 1;
- private uint _skipNumberOfLayer;
+[Serializable]
+public class OperationEditParameters : Operation
+{
+ #region Members
- #endregion
+ private bool _propagateModificationsToLayers = true;
+ private bool _perLayerOverride;
+ private uint _setNumberOfLayer = 1;
+ private uint _skipNumberOfLayer;
- #region Overrides
+ #endregion
- public override bool CanRunInPartialMode => true;
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ #region Overrides
- public override bool CanROI => false;
+ public override bool CanRunInPartialMode => true;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override string Title => "Edit print parameters";
+ public override bool CanROI => false;
+ public override string IconClass => "fas fa-edit";
+ public override string Title => "Edit print parameters";
- public override string Description =>
- "Edits the available print parameters.\n" +
- "Note: Set global parameters will override all per layer settings when they are available.";
+ public override string Description =>
+ "Edits the available print parameters.\n" +
+ "Note: Set global parameters will override all per layer settings when they are available.";
- public override string ConfirmationText
+ public override string ConfirmationText
+ {
+ get
{
- get
+ var sb = new StringBuilder();
+ foreach (var modifier in Modifiers!)
{
- var sb = new StringBuilder();
- foreach (var modifier in Modifiers)
+ if(!modifier.HasChanged) continue;
+ sb.AppendLine($"{modifier.Name}: {modifier.OldValue}{modifier.ValueUnit} » {modifier.NewValue}{modifier.ValueUnit}");
+ }
+ var text = "commit print parameter changes";
+ if (_perLayerOverride)
+ {
+ if (LayerRangeCount == 1)
{
- if(!modifier.HasChanged) continue;
- sb.AppendLine($"{modifier.Name}: {modifier.OldValue}{modifier.ValueUnit} » {modifier.NewValue}{modifier.ValueUnit}");
+ text += $" to layer {LayerIndexStart}";
}
- var text = "commit print parameter changes";
- if (_perLayerOverride)
+ else
{
- if (LayerRangeCount == 1)
- {
- text += $" to layer {LayerIndexStart}";
- }
- else
- {
- text += $" from layer {LayerIndexStart} to {LayerIndexEnd}";
- }
+ text += $" from layer {LayerIndexStart} to {LayerIndexEnd}";
}
-
- return $"{text}?\n{sb}";
}
+
+ return $"{text}?\n{sb}";
}
+ }
- public override string ProgressTitle => "Change print parameters";
+ public override string ProgressTitle => "Change print parameters";
- public override string ProgressAction => "Changing print parameters";
+ public override string ProgressAction => "Changing print parameters";
- public override bool CanHaveProfiles => false;
+ public override bool CanHaveProfiles => false;
- public override string ValidateSpawn()
+ public override string? ValidateSpawn()
+ {
+ if (Modifiers is null || Modifiers.Length == 0)
{
- if (Modifiers is null || Modifiers.Length == 0)
- {
- return "No available properties to edit on this file format.";
- }
-
- return null;
+ return "No available properties to edit on this file format.";
}
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
- var changed = Modifiers.Any(modifier => modifier.HasChanged);
-
- if (!changed)
- {
- sb.AppendLine("Nothing changed\nDo some changes or cancel the operation.");
- }
-
- if (Modifiers.Contains(FileFormat.PrintParameterModifier.PositionZ)
- && FileFormat.PrintParameterModifier.PositionZ.HasChanged
- && _skipNumberOfLayer > 0
- && LayerRangeCount > 1
- && _setNumberOfLayer + _skipNumberOfLayer < LayerRangeCount)
- {
- sb.AppendLine("Can not change the PositionZ in layers with an active alternating pattern.");
- }
-
+ return null;
+ }
- return sb.ToString();
+ public override string? ValidateInternally()
+ {
+ if (Modifiers is null)
+ {
+ return "Modifiers does not exists, can't validate.";
}
- #endregion
- #region Propertiers
- [XmlIgnore]
- public FileFormat.PrintParameterModifier[] Modifiers { get; set; }
+ var sb = new StringBuilder();
+ var changed = Modifiers.Any(modifier => modifier.HasChanged);
- public bool PropagateModificationsToLayers
+ if (!changed)
{
- get => _propagateModificationsToLayers;
- set => RaiseAndSetIfChanged(ref _propagateModificationsToLayers, value);
+ sb.AppendLine("Nothing changed\nDo some changes or cancel the operation.");
}
- /// <summary>
- /// Gets or sets if parameters are global or per layer inside a layer range
- /// </summary>
- public bool PerLayerOverride
+ if (Modifiers.Contains(FileFormat.PrintParameterModifier.PositionZ)
+ && FileFormat.PrintParameterModifier.PositionZ.HasChanged
+ && _skipNumberOfLayer > 0
+ && LayerRangeCount > 1
+ && _setNumberOfLayer + _skipNumberOfLayer < LayerRangeCount)
{
- get => _perLayerOverride;
- set => RaiseAndSetIfChanged(ref _perLayerOverride, value);
+ sb.AppendLine("Can not change the PositionZ in layers with an active alternating pattern.");
}
- /// <summary>
- /// Gets or sets the number of sequential layers to set the parameters
- /// </summary>
- public uint SetNumberOfLayer
- {
- get => _setNumberOfLayer;
- set => RaiseAndSetIfChanged(ref _setNumberOfLayer, value);
- }
- /// <summary>
- /// Gets or sets the number of sequential layers to skip after set a layer
- /// </summary>
- public uint SkipNumberOfLayer
- {
- get => _skipNumberOfLayer;
- set => RaiseAndSetIfChanged(ref _skipNumberOfLayer, value);
- }
+ return sb.ToString();
+ }
+ #endregion
+
+ #region Propertiers
+
+ [XmlIgnore]
+ public FileFormat.PrintParameterModifier[]? Modifiers { get; set; }
+
+ public bool PropagateModificationsToLayers
+ {
+ get => _propagateModificationsToLayers;
+ set => RaiseAndSetIfChanged(ref _propagateModificationsToLayers, value);
+ }
- #endregion
+ /// <summary>
+ /// Gets or sets if parameters are global or per layer inside a layer range
+ /// </summary>
+ public bool PerLayerOverride
+ {
+ get => _perLayerOverride;
+ set => RaiseAndSetIfChanged(ref _perLayerOverride, value);
+ }
- #region Constructor
+ /// <summary>
+ /// Gets or sets the number of sequential layers to set the parameters
+ /// </summary>
+ public uint SetNumberOfLayer
+ {
+ get => _setNumberOfLayer;
+ set => RaiseAndSetIfChanged(ref _setNumberOfLayer, value);
+ }
- public OperationEditParameters() { }
+ /// <summary>
+ /// Gets or sets the number of sequential layers to skip after set a layer
+ /// </summary>
+ public uint SkipNumberOfLayer
+ {
+ get => _skipNumberOfLayer;
+ set => RaiseAndSetIfChanged(ref _skipNumberOfLayer, value);
+ }
- public OperationEditParameters(FileFormat slicerFile) : base(slicerFile) { }
+ #endregion
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- SlicerFile.RefreshPrintParametersModifiersValues();
- Modifiers = SlicerFile.PrintParameterModifiers;
- }
+ #region Constructor
+
+ public OperationEditParameters() { }
+
+ public OperationEditParameters(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ SlicerFile.RefreshPrintParametersModifiersValues();
+ Modifiers = SlicerFile.PrintParameterModifiers;
+ }
- #region Methods
+ #endregion
- protected override bool ExecuteInternally(OperationProgress progress)
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (_perLayerOverride)
{
- if (_perLayerOverride)
+ uint setLayers = 0;
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
{
- uint setLayers = 0;
- for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
+ SlicerFile[layerIndex].SetValuesFromPrintParametersModifiers(Modifiers);
+ if (_skipNumberOfLayer == 0) continue;
+ setLayers++;
+ if (setLayers >= _setNumberOfLayer)
{
- SlicerFile[layerIndex].SetValuesFromPrintParametersModifiers(Modifiers);
- if (_skipNumberOfLayer == 0) continue;
- setLayers++;
- if (setLayers >= _setNumberOfLayer)
- {
- setLayers = 0;
- layerIndex += _skipNumberOfLayer;
- }
+ setLayers = 0;
+ layerIndex += _skipNumberOfLayer;
}
+ }
- foreach (var modifier in Modifiers)
- {
- modifier.OldValue = modifier.NewValue;
- }
- SlicerFile.RebuildGCode();
+ foreach (var modifier in Modifiers!)
+ {
+ modifier.OldValue = modifier.NewValue;
}
- else
+ SlicerFile.RebuildGCode();
+ }
+ else
+ {
+ if (!_propagateModificationsToLayers)
{
- if (!_propagateModificationsToLayers)
- {
- SlicerFile.SuppressRebuildProperties = true;
- }
- SlicerFile.SetValuesFromPrintParametersModifiers();
- if (!_propagateModificationsToLayers)
- {
- SlicerFile.SuppressRebuildProperties = false;
- SlicerFile.RebuildGCode();
- }
+ SlicerFile.SuppressRebuildProperties = true;
+ }
+ SlicerFile.SetValuesFromPrintParametersModifiers();
+ if (!_propagateModificationsToLayers)
+ {
+ SlicerFile.SuppressRebuildProperties = false;
+ SlicerFile.RebuildGCode();
}
-
- SlicerFile.RefreshPrintParametersModifiersValues();
-
- return !progress.Token.IsCancellationRequested;
}
- #endregion
+ SlicerFile.RefreshPrintParametersModifiersValues();
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationFadeExposureTime.cs b/UVtools.Core/Operations/OperationFadeExposureTime.cs
index 345cfbf..8a3afb3 100644
--- a/UVtools.Core/Operations/OperationFadeExposureTime.cs
+++ b/UVtools.Core/Operations/OperationFadeExposureTime.cs
@@ -11,193 +11,193 @@ using System.ComponentModel;
using System.Text;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationFadeExposureTime : Operation
{
- [Serializable]
- public class OperationFadeExposureTime : Operation
- {
- #region Members
+ #region Members
- private uint _layerCount = 10;
- private decimal _fromExposureTime;
- private decimal _toExposureTime;
- private bool _disableFirmwareTransitionLayers = true;
+ private uint _layerCount = 10;
+ private decimal _fromExposureTime;
+ private decimal _toExposureTime;
+ private bool _disableFirmwareTransitionLayers = true;
- #endregion
+ #endregion
- #region Overrides
- public override bool CanRunInPartialMode => true;
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
- public override bool LayerIndexEndEnabled => false;
- public override string Title => "Fade exposure time";
+ #region Overrides
+ public override bool CanRunInPartialMode => true;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
+ public override bool LayerIndexEndEnabled => false;
+ public override string IconClass => "fas fa-history";
+ public override string Title => "Fade exposure time";
- public override string Description =>
- "Fade the exposure time in increments from a start to a end value on the selected layer range.";
+ public override string Description =>
+ "Fade the exposure time in increments from a start to a end value on the selected layer range.";
- public override string ConfirmationText =>
- $"fade exposure time model layers {LayerIndexStart} through {LayerIndexEnd} with increments of {IncrementValue}s";
+ public override string ConfirmationText =>
+ $"fade exposure time model layers {LayerIndexStart} through {LayerIndexEnd} with increments of {IncrementValue}s";
- public override string ProgressTitle =>
- $"Fading exposure time from layers {LayerIndexStart} to {LayerIndexEnd} with increments of {IncrementValue}s";
+ public override string ProgressTitle =>
+ $"Fading exposure time from layers {LayerIndexStart} to {LayerIndexEnd} with increments of {IncrementValue}s";
- public override string ProgressAction => "Faded layers";
+ public override string ProgressAction => "Faded layers";
- public override string ValidateSpawn()
+ public override string? ValidateSpawn()
+ {
+ if (!SlicerFile.CanUseLayerExposureTime)
{
- if (!SlicerFile.CanUseLayerExposureTime)
- {
- return NotSupportedMessage;
- }
-
- return null;
+ return NotSupportedMessage;
}
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ return null;
+ }
- if (_layerCount == 0) sb.AppendLine("The layer count must be higher than 0.");
- if(_fromExposureTime == _toExposureTime) sb.AppendLine("The starting exposure time can't be the same as the ending exposure time.");
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- return sb.ToString();
- }
+ if (_layerCount == 0) sb.AppendLine("The layer count must be higher than 0.");
+ if(_fromExposureTime == _toExposureTime) sb.AppendLine("The starting exposure time can't be the same as the ending exposure time.");
- public override string ToString()
- {
- var result = $"[Layers: {LayerRangeCount} From: {_fromExposureTime}s To: {_toExposureTime}s @ {IncrementValue}s] " + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ return sb.ToString();
+ }
- protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ public override string ToString()
+ {
+ var result = $"[Layers: {LayerRangeCount} From: {_fromExposureTime}s To: {_toExposureTime}s @ {IncrementValue}s] " + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(LayerIndexStart))
{
- if (e.PropertyName == nameof(LayerIndexStart))
- {
- LayerCount = _layerCount; // Sanitize
- LayerIndexEnd = LayerIndexStart + _layerCount - 1 ; // Sync
- RaisePropertyChanged(nameof(MaximumLayerCount));
- RaisePropertyChanged(nameof(IncrementValue));
- }
- /*else if (e.PropertyName == nameof(LayerIndexEnd))
- {
- LayerCount = LayerRangeCount;
- RaisePropertyChanged(nameof(IncrementValue));
- }*/
-
- base.OnPropertyChanged(e);
+ LayerCount = _layerCount; // Sanitize
+ LayerIndexEnd = LayerIndexStart + _layerCount - 1 ; // Sync
+ RaisePropertyChanged(nameof(MaximumLayerCount));
+ RaisePropertyChanged(nameof(IncrementValue));
}
+ /*else if (e.PropertyName == nameof(LayerIndexEnd))
+ {
+ LayerCount = LayerRangeCount;
+ RaisePropertyChanged(nameof(IncrementValue));
+ }*/
- #endregion
+ base.OnPropertyChanged(e);
+ }
+
+ #endregion
- #region Properties
+ #region Properties
- public uint LayerCount
+ public uint LayerCount
+ {
+ get => _layerCount;
+ set
{
- get => _layerCount;
- set
- {
- if (!RaiseAndSetIfChanged(ref _layerCount, Math.Min(value, SlicerFile.LayerCount - LayerIndexStart))) return;
- LayerIndexEnd = LayerIndexStart + _layerCount - 1;
- RaisePropertyChanged(nameof(MaximumLayerCount));
- RaisePropertyChanged(nameof(IncrementValue));
- }
+ if (!RaiseAndSetIfChanged(ref _layerCount, Math.Min(value, SlicerFile.LayerCount - LayerIndexStart))) return;
+ LayerIndexEnd = LayerIndexStart + _layerCount - 1;
+ RaisePropertyChanged(nameof(MaximumLayerCount));
+ RaisePropertyChanged(nameof(IncrementValue));
}
+ }
- public uint MaximumLayerCount => Math.Max(LayerCount, SlicerFile.LayerCount - LayerIndexStart);
+ public uint MaximumLayerCount => Math.Max(LayerCount, SlicerFile.LayerCount - LayerIndexStart);
- public decimal FromExposureTime
+ public decimal FromExposureTime
+ {
+ get => _fromExposureTime;
+ set
{
- get => _fromExposureTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _fromExposureTime, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(IncrementValue));
- }
+ if(!RaiseAndSetIfChanged(ref _fromExposureTime, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(IncrementValue));
}
+ }
- public decimal ToExposureTime
+ public decimal ToExposureTime
+ {
+ get => _toExposureTime;
+ set
{
- get => _toExposureTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _toExposureTime, Math.Round(value, 2))) return;
- RaisePropertyChanged(nameof(IncrementValue));
- }
+ if(!RaiseAndSetIfChanged(ref _toExposureTime, Math.Round(value, 2))) return;
+ RaisePropertyChanged(nameof(IncrementValue));
}
+ }
- public bool DisableFirmwareTransitionLayers
- {
- get => _disableFirmwareTransitionLayers;
- set => RaiseAndSetIfChanged(ref _disableFirmwareTransitionLayers, value);
- }
+ public bool DisableFirmwareTransitionLayers
+ {
+ get => _disableFirmwareTransitionLayers;
+ set => RaiseAndSetIfChanged(ref _disableFirmwareTransitionLayers, value);
+ }
- public decimal IncrementValue => Math.Round(IncrementValueRaw, 2);
- public decimal IncrementValueRaw => (_toExposureTime - _fromExposureTime) / (LayerRangeCount + 1);
+ public decimal IncrementValue => Math.Round(IncrementValueRaw, 2);
+ public decimal IncrementValueRaw => (_toExposureTime - _fromExposureTime) / (LayerRangeCount + 1);
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationFadeExposureTime() { }
+ public OperationFadeExposureTime() { }
- public OperationFadeExposureTime(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationFadeExposureTime(FileFormat slicerFile) : base(slicerFile) { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- if (_fromExposureTime <= 0) _fromExposureTime = (decimal)SlicerFile.BottomExposureTime;
- if (_toExposureTime <= 0) _toExposureTime = (decimal)SlicerFile.ExposureTime;
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ if (_fromExposureTime <= 0) _fromExposureTime = (decimal)SlicerFile.BottomExposureTime;
+ if (_toExposureTime <= 0) _toExposureTime = (decimal)SlicerFile.ExposureTime;
- LayerIndexEnd = LayerIndexStart + _layerCount - 1; // Sync
- }
+ LayerIndexEnd = LayerIndexStart + _layerCount - 1; // Sync
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationFadeExposureTime other)
- {
- return _layerCount == other._layerCount && _fromExposureTime == other._fromExposureTime && _toExposureTime == other._toExposureTime && _disableFirmwareTransitionLayers == other._disableFirmwareTransitionLayers;
- }
+ protected bool Equals(OperationFadeExposureTime other)
+ {
+ return _layerCount == other._layerCount && _fromExposureTime == other._fromExposureTime && _toExposureTime == other._toExposureTime && _disableFirmwareTransitionLayers == other._disableFirmwareTransitionLayers;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationFadeExposureTime) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationFadeExposureTime) obj);
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_layerCount, _fromExposureTime, _toExposureTime, _disableFirmwareTransitionLayers);
- }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_layerCount, _fromExposureTime, _toExposureTime, _disableFirmwareTransitionLayers);
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ LayerIndexEnd = LayerIndexStart + _layerCount - 1; // Sanitize
+
+ if (_disableFirmwareTransitionLayers)
+ {
+ SlicerFile.TransitionLayerCount = 0;
+ }
+
+ var increment = IncrementValueRaw;
+ var exposure = _fromExposureTime;
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
{
- LayerIndexEnd = LayerIndexStart + _layerCount - 1; // Sanitize
-
- if (_disableFirmwareTransitionLayers)
- {
- SlicerFile.TransitionLayerCount = 0;
- }
-
- var increment = IncrementValueRaw;
- var exposure = _fromExposureTime;
- for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
- {
- progress.Token.ThrowIfCancellationRequested();
- exposure += increment;
- SlicerFile[layerIndex].ExposureTime = (float)exposure;
- }
-
- return !progress.Token.IsCancellationRequested;
+ progress.Token.ThrowIfCancellationRequested();
+ exposure += increment;
+ SlicerFile[layerIndex].ExposureTime = (float)exposure;
}
- #endregion
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs
index e9c2a43..dc4261b 100644
--- a/UVtools.Core/Operations/OperationFlip.cs
+++ b/UVtools.Core/Operations/OperationFlip.cs
@@ -6,133 +6,133 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
+using System;
+using System.Threading.Tasks;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationFlip : Operation
{
- [Serializable]
- public class OperationFlip : Operation
+ #region Members
+ private bool _makeCopy;
+ private FlipType _flipDirection = FlipType.Horizontal;
+ #endregion
+
+ #region Overrides
+ public override string IconClass => "mdi-flip-horizontal";
+ public override string Title => "Flip";
+ public override string Description =>
+ "Flip the layers of the model vertically and/or horizontally.\n" +
+ "Note: Before perform this operation, un-rotate the layer preview to see the real orientation.";
+
+ public override string ConfirmationText =>
+ FlipDirection == FlipType.Both
+ ? $"flip {(_makeCopy ? "and blend ":"")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically?"
+ : $"flip {(_makeCopy ? "and blend " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}?";
+
+ public override string ProgressTitle =>
+ FlipDirection == FlipType.Both
+ ? $"Flipping {(_makeCopy ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically"
+ : $"Flipping {(_makeCopy ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}";
+
+ public override string ProgressAction => "Flipped layers";
+
+ public override string ToString()
{
- #region Members
- private bool _makeCopy;
- private FlipType _flipDirection = FlipType.Horizontal;
- #endregion
-
- #region Overrides
- public override string Title => "Flip";
- public override string Description =>
- "Flip the layers of the model vertically and/or horizontally.\n" +
- "Note: Before perform this operation, un-rotate the layer preview to see the real orientation.";
-
- public override string ConfirmationText =>
- FlipDirection == FlipType.Both
- ? $"flip {(_makeCopy ? "and blend ":"")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically?"
- : $"flip {(_makeCopy ? "and blend " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}?";
-
- public override string ProgressTitle =>
- FlipDirection == FlipType.Both
- ? $"Flipping {(_makeCopy ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} Horizontally and Vertically"
- : $"Flipping {(_makeCopy ? "and blending " : "")}layers {LayerIndexStart} through {LayerIndexEnd} {FlipDirection}";
-
- public override string ProgressAction => "Flipped layers";
-
- public override string ToString()
- {
- var result = $"[{_flipDirection}] [Blend: {_makeCopy}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
+ var result = $"[{_flipDirection}] [Blend: {_makeCopy}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- #region Properties
+ #region Properties
- public FlipType FlipDirection
- {
- get => _flipDirection;
- set => RaiseAndSetIfChanged(ref _flipDirection, value);
- }
+ public FlipType FlipDirection
+ {
+ get => _flipDirection;
+ set => RaiseAndSetIfChanged(ref _flipDirection, value);
+ }
- public bool MakeCopy
- {
- get => _makeCopy;
- set => RaiseAndSetIfChanged(ref _makeCopy, value);
- }
+ public bool MakeCopy
+ {
+ get => _makeCopy;
+ set => RaiseAndSetIfChanged(ref _makeCopy, value);
+ }
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationFlip() { }
+ public OperationFlip() { }
- public OperationFlip(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationFlip(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationFlip other)
- {
- return _makeCopy == other._makeCopy && _flipDirection == other._flipDirection;
- }
+ protected bool Equals(OperationFlip other)
+ {
+ return _makeCopy == other._makeCopy && _flipDirection == other._flipDirection;
+ }
- public override bool Equals(object obj)
- {
- if (obj is null) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((OperationFlip) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationFlip) obj);
+ }
- public override int GetHashCode()
+ public override int GetHashCode()
+ {
+ unchecked
{
- unchecked
- {
- return (_makeCopy.GetHashCode() * 397) ^ (int) _flipDirection;
- }
+ return (_makeCopy.GetHashCode() * 397) ^ (int) _flipDirection;
}
+ }
- #endregion
+ #endregion
- #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
+ #region Methods
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat);
- SlicerFile[layerIndex].LayerMat = mat;
-
- progress.LockAndIncrement();
- });
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ SlicerFile[layerIndex].LayerMat = mat;
+
+ progress.LockAndIncrement();
+ });
- return !progress.Token.IsCancellationRequested;
- }
+ return !progress.Token.IsCancellationRequested;
+ }
+
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
- public override bool Execute(Mat mat, params object[] arguments)
+ if (MakeCopy)
{
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
-
- if (MakeCopy)
- {
- using Mat dst = new();
- CvInvoke.Flip(target, dst, _flipDirection);
- CvInvoke.Add(target, dst, target);
- }
- else
- {
- CvInvoke.Flip(target, target, _flipDirection);
- }
-
- ApplyMask(original, target);
-
- return true;
+ using Mat dst = new();
+ CvInvoke.Flip(target, dst, _flipDirection);
+ CvInvoke.Add(target, dst, target);
}
- #endregion
+ else
+ {
+ CvInvoke.Flip(target, target, _flipDirection);
+ }
+
+ ApplyMask(original, target);
+
+ return true;
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationIPrintedThisFile.cs b/UVtools.Core/Operations/OperationIPrintedThisFile.cs
index c36faeb..742db65 100644
--- a/UVtools.Core/Operations/OperationIPrintedThisFile.cs
+++ b/UVtools.Core/Operations/OperationIPrintedThisFile.cs
@@ -12,172 +12,171 @@ using UVtools.Core.FileFormats;
using UVtools.Core.Managers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationIPrintedThisFile : Operation
{
- [Serializable]
- public class OperationIPrintedThisFile : Operation
- {
- #region Members
- private Material _materialItem;
- private decimal _volume;
- private float _printTime;
- private decimal _multiplier = 1;
+ #region Members
+ private Material? _materialItem;
+ private decimal _volume;
+ private float _printTime;
+ private decimal _multiplier = 1;
- #endregion
+ #endregion
- #region Overrides
+ #region Overrides
- public override bool CanRunInPartialMode => true;
+ public override bool CanRunInPartialMode => true;
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI => false;
- public override bool CanHaveProfiles => false;
- public override string ButtonOkText => "Consume";
+ public override bool CanROI => false;
+ public override bool CanHaveProfiles => false;
+ public override string ButtonOkText => "Consume";
+ public override string IconClass => "fas fa-flask";
+ public override string Title => "I printed this file";
+ public override string Description => "Select a material and consume resin from stock and print time.";
- public override string Title => "I printed this file";
- public override string Description => "Select a material and consume resin from stock and print time.";
+ public override string ConfirmationText =>
+ $"consume {FinalVolume}ml and {FinalPrintTimeHours:F4}h on:\n{_materialItem} ?";
- public override string ConfirmationText =>
- $"consume {FinalVolume}ml and {FinalPrintTimeHours:F4}h on:\n{_materialItem} ?";
+ public override string ProgressTitle =>
+ $"Consuming";
- public override string ProgressTitle =>
- $"Consuming";
+ public override string ProgressAction => "Consumed";
- public override string ProgressAction => "Consumed";
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ValidateInternally()
+ if (_materialItem is null)
{
- var sb = new StringBuilder();
-
- if (_materialItem is null)
- {
- sb.AppendLine("You must select an material.");
- }
- if (_volume <= 0)
- {
- sb.AppendLine("Volume must be higher than 0ml.");
- }
- if (_printTime <= 0)
- {
- sb.AppendLine("Print time must be higher than 0s.");
- }
-
- return sb.ToString();
+ sb.AppendLine("You must select an material.");
}
-
- public override string ToString()
+ if (_volume <= 0)
{
- var result = $"{FinalVolume}ml {FinalPrintTimeHours:F4}h on {_materialItem.Name}";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("Volume must be higher than 0ml.");
}
- #endregion
-
- #region Properties
-
- public Material MaterialItem
+ if (_printTime <= 0)
{
- get => _materialItem;
- set => RaiseAndSetIfChanged(ref _materialItem, value);
+ sb.AppendLine("Print time must be higher than 0s.");
}
- public decimal Volume
+ return sb.ToString();
+ }
+
+ public override string ToString()
+ {
+ var result = $"{FinalVolume}ml {FinalPrintTimeHours:F4}h on {_materialItem?.Name}";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Properties
+
+ public Material? MaterialItem
+ {
+ get => _materialItem;
+ set => RaiseAndSetIfChanged(ref _materialItem, value);
+ }
+
+ public decimal Volume
+ {
+ get => _volume;
+ set
{
- get => _volume;
- set
- {
- if(!RaiseAndSetIfChanged(ref _volume, value)) return;
- RaisePropertyChanged(nameof(FinalVolume));
- }
+ if(!RaiseAndSetIfChanged(ref _volume, value)) return;
+ RaisePropertyChanged(nameof(FinalVolume));
}
+ }
- public decimal FinalVolume => _volume * _multiplier;
+ public decimal FinalVolume => _volume * _multiplier;
- public float PrintTime
+ public float PrintTime
+ {
+ get => _printTime;
+ set
{
- get => _printTime;
- set
- {
- if(!RaiseAndSetIfChanged(ref _printTime, value)) return;
- RaisePropertyChanged(nameof(PrintTimeHours));
- RaisePropertyChanged(nameof(FinalPrintTime));
- RaisePropertyChanged(nameof(FinalPrintTimeHours));
- }
+ if(!RaiseAndSetIfChanged(ref _printTime, value)) return;
+ RaisePropertyChanged(nameof(PrintTimeHours));
+ RaisePropertyChanged(nameof(FinalPrintTime));
+ RaisePropertyChanged(nameof(FinalPrintTimeHours));
}
+ }
- public float PrintTimeHours => _printTime / 60 / 60;
- public float FinalPrintTime => _printTime * (float)_multiplier;
- public float FinalPrintTimeHours => FinalPrintTime / 60 / 60;
+ public float PrintTimeHours => _printTime / 60 / 60;
+ public float FinalPrintTime => _printTime * (float)_multiplier;
+ public float FinalPrintTimeHours => FinalPrintTime / 60 / 60;
- /// <summary>
- /// Number of times this file has been printed
- /// </summary>
- public decimal Multiplier
+ /// <summary>
+ /// Number of times this file has been printed
+ /// </summary>
+ public decimal Multiplier
+ {
+ get => _multiplier;
+ set
{
- get => _multiplier;
- set
- {
- if (!RaiseAndSetIfChanged(ref _multiplier, value)) return;
- RaisePropertyChanged(nameof(FinalVolume));
- RaisePropertyChanged(nameof(FinalPrintTime));
- }
+ if (!RaiseAndSetIfChanged(ref _multiplier, value)) return;
+ RaisePropertyChanged(nameof(FinalVolume));
+ RaisePropertyChanged(nameof(FinalPrintTime));
}
+ }
- public MaterialManager Manager => MaterialManager.Instance;
-
- #endregion
+ public MaterialManager Manager => MaterialManager.Instance;
- #region Constructor
+ #endregion
- public OperationIPrintedThisFile() { }
+ #region Constructor
- public OperationIPrintedThisFile(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationIPrintedThisFile() { }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- _volume = (decimal) SlicerFile.MaterialMilliliters;
- _printTime = SlicerFile.PrintTime;
- MaterialManager.Load();
- }
+ public OperationIPrintedThisFile(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ _volume = (decimal) SlicerFile.MaterialMilliliters;
+ _printTime = SlicerFile.PrintTime;
+ MaterialManager.Load();
+ }
- #region Methods
+ #endregion
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- if (MaterialItem is null) return !progress.Token.IsCancellationRequested;
+ #region Methods
- MaterialItem.Consume(FinalVolume, FinalPrintTime);
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (MaterialItem is null) return !progress.Token.IsCancellationRequested;
- MaterialManager.Save();
- return !progress.Token.IsCancellationRequested;
- }
+ MaterialItem.Consume(FinalVolume, FinalPrintTime);
- #endregion
+ MaterialManager.Save();
+ return !progress.Token.IsCancellationRequested;
+ }
- #region Equality
+ #endregion
- protected bool Equals(OperationIPrintedThisFile other)
- {
- return _volume == other._volume && _printTime.Equals(other._printTime) && Equals(_materialItem, other._materialItem) && _multiplier == other._multiplier;
- }
+ #region Equality
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationIPrintedThisFile) obj);
- }
+ protected bool Equals(OperationIPrintedThisFile other)
+ {
+ return _volume == other._volume && _printTime.Equals(other._printTime) && Equals(_materialItem, other._materialItem) && _multiplier == other._multiplier;
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_volume, _printTime, _materialItem, _multiplier);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationIPrintedThisFile) obj);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_volume, _printTime, _materialItem, _multiplier);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs
index 74f1d9a..e8dadac 100644
--- a/UVtools.Core/Operations/OperationInfill.cs
+++ b/UVtools.Core/Operations/OperationInfill.cs
@@ -6,367 +6,366 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Drawing;
-using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
+using System;
+using System.Drawing;
+using System.Threading.Tasks;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationInfill : Operation
{
- [Serializable]
- public sealed class OperationInfill : Operation
- {
- #region Members
- private InfillAlgorithm _infillType = InfillAlgorithm.CubicDynamicLink;
- private ushort _wallThickness = 64;
- private ushort _infillThickness = 45;
- private ushort _infillSpacing = 160;
- private ushort _infillBrightness = 255;
- #endregion
+ #region Members
+ private InfillAlgorithm _infillType = InfillAlgorithm.CubicDynamicLink;
+ private ushort _wallThickness = 64;
+ private ushort _infillThickness = 45;
+ private ushort _infillSpacing = 160;
+ private ushort _infillBrightness = 255;
+ #endregion
- #region Overrides
+ #region Overrides
+ public override string IconClass => "mdi-checkerboard";
+ public override string Title => "Infill";
- public override string Title => "Infill";
+ public override string Description =>
+ $"Generate infill patterns in the model.\n\nNOTES:\n1) You must exclude floor and ceil layers from the range.\n2) You must take care of drain holes after the operation.";
- public override string Description =>
- $"Generate infill patterns in the model.\n\nNOTES:\n1) You must exclude floor and ceil layers from the range.\n2) You must take care of drain holes after the operation.";
+ public override string ConfirmationText =>
+ $"infill model with {InfillType} from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ConfirmationText =>
- $"infill model with {InfillType} from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ProgressTitle =>
+ $"Infill model with {InfillType} from layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressTitle =>
- $"Infill model with {InfillType} from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressAction => "Infilled layers";
- public override string ProgressAction => "Infilled layers";
+ #endregion
- #endregion
+ #region Enums
+ public enum InfillAlgorithm
+ {
+ //Rhombus,
+ Cubic,
+ CubicCenterLink,
+ CubicDynamicLink,
+ CubicInterlinked,
+ Honeycomb
+ }
+ #endregion
- #region Enums
- public enum InfillAlgorithm
- {
- //Rhombus,
- Cubic,
- CubicCenterLink,
- CubicDynamicLink,
- CubicInterlinked,
- Honeycomb
- }
- #endregion
+ #region Properties
+ public static Array InfillAlgorithmTypes => Enum.GetValues(typeof(InfillAlgorithm));
+ public InfillAlgorithm InfillType
+ {
+ get => _infillType;
+ set => RaiseAndSetIfChanged(ref _infillType, value);
+ }
- #region Properties
- public static Array InfillAlgorithmTypes => Enum.GetValues(typeof(InfillAlgorithm));
- public InfillAlgorithm InfillType
- {
- get => _infillType;
- set => RaiseAndSetIfChanged(ref _infillType, value);
- }
+ public ushort WallThickness
+ {
+ get => _wallThickness;
+ set => RaiseAndSetIfChanged(ref _wallThickness, value);
+ }
- public ushort WallThickness
- {
- get => _wallThickness;
- set => RaiseAndSetIfChanged(ref _wallThickness, value);
- }
+ public ushort InfillBrightness
+ {
+ get => _infillBrightness;
+ set => RaiseAndSetIfChanged(ref _infillBrightness, value);
+ }
- public ushort InfillBrightness
- {
- get => _infillBrightness;
- set => RaiseAndSetIfChanged(ref _infillBrightness, value);
- }
+ public ushort InfillThickness
+ {
+ get => _infillThickness;
+ set => RaiseAndSetIfChanged(ref _infillThickness, value);
+ }
- public ushort InfillThickness
- {
- get => _infillThickness;
- set => RaiseAndSetIfChanged(ref _infillThickness, value);
- }
+ public ushort InfillSpacing
+ {
+ get => _infillSpacing;
+ set => RaiseAndSetIfChanged(ref _infillSpacing, value);
+ }
- public ushort InfillSpacing
- {
- get => _infillSpacing;
- set => RaiseAndSetIfChanged(ref _infillSpacing, value);
- }
+ public override string ToString()
+ {
+ var result = $"[{_infillType}] [Wall: {_wallThickness}px] [B: {_infillBrightness}px] [T: {_infillThickness}px] [S: {_infillSpacing}px]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- public override string ToString()
- {
- var result = $"[{_infillType}] [Wall: {_wallThickness}px] [B: {_infillBrightness}px] [T: {_infillThickness}px] [S: {_infillSpacing}px]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ #endregion
- #endregion
+ #region Constructor
- #region Constructor
+ public OperationInfill() { }
- public OperationInfill() { }
+ public OperationInfill(FileFormat slicerFile) : base(slicerFile) { }
- public OperationInfill(FileFormat slicerFile) : base(slicerFile) { }
+ #endregion
- #endregion
+ #region Equality
- #region Equality
+ private bool Equals(OperationInfill other)
+ {
+ return _infillType == other._infillType && _wallThickness == other._wallThickness && _infillThickness == other._infillThickness && _infillSpacing == other._infillSpacing && _infillBrightness == other._infillBrightness;
+ }
- private bool Equals(OperationInfill other)
- {
- return _infillType == other._infillType && _wallThickness == other._wallThickness && _infillThickness == other._infillThickness && _infillSpacing == other._infillSpacing && _infillBrightness == other._infillBrightness;
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationInfill other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine((int) _infillType, _wallThickness, _infillThickness, _infillSpacing, _infillBrightness);
+ }
+
+ #endregion
- public override bool Equals(object obj)
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Mat? mask = null;
+ if (_infillType == InfillAlgorithm.Honeycomb)
{
- return ReferenceEquals(this, obj) || obj is OperationInfill other && Equals(other);
+ mask = GetHoneycombMask(GetRoiSizeOrDefault());
}
- public override int GetHashCode()
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- return HashCode.Combine((int) _infillType, _wallThickness, _infillThickness, _infillSpacing, _infillBrightness);
- }
+ if (progress.Token.IsCancellationRequested) return;
- #endregion
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat, layerIndex, mask!);
+ SlicerFile[layerIndex].LayerMat = mat;
- #region Methods
+ progress.LockAndIncrement();
+ });
+ mask?.Dispose();
+ return !progress.Token.IsCancellationRequested;
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ if (arguments is null || arguments.Length < 1) return false;
+ var anchor = new Point(-1, -1);
+ var kernel = EmguExtensions.Kernel3x3Rectangle;
+ uint index = Convert.ToUInt32(arguments[0]);
+ uint layerIndex = index - LayerIndexStart;
+ var infillColor = new MCvScalar(_infillBrightness);
+
+ Mat? patternMask = null;
+ using Mat erode = new ();
+ using Mat diff = new ();
+ var target = GetRoiOrDefault(mat);
+ using var mask = GetMask(mat);
+ bool disposeTargetMask = true;
+
+ if (_infillType is InfillAlgorithm.Cubic
+ or InfillAlgorithm.CubicCenterLink
+ or InfillAlgorithm.CubicDynamicLink
+ or InfillAlgorithm.CubicInterlinked)
{
- Mat mask = null;
- if (_infillType == InfillAlgorithm.Honeycomb)
+ using var infillPattern = EmguExtensions.InitMat(new Size(_infillSpacing, _infillSpacing));
+ using var matPattern = mat.NewBlank();
+ bool firstPattern = true;
+ uint accumulator = 0;
+ bool dynamicCenter = false;
+ while (accumulator < layerIndex)
{
- mask = GetHoneycombMask(GetRoiSizeOrDefault());
+ dynamicCenter = !dynamicCenter;
+ firstPattern = true;
+ accumulator += _infillSpacing;
+
+ if (accumulator >= layerIndex) break;
+ firstPattern = false;
+ accumulator += _infillThickness;
}
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ if (firstPattern)
{
- if (progress.Token.IsCancellationRequested) return;
+ int thickness = _infillThickness / 2;
+ // Top Left
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(0, 0, thickness, thickness),
+ infillColor, -1);
+
+ // Top Right
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(infillPattern.Width - thickness, 0, thickness, thickness),
+ infillColor, -1);
+
+ // Bottom Left
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(0, infillPattern.Height - thickness, thickness, thickness),
+ infillColor, -1);
+
+ // Bottom Right
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(infillPattern.Width - thickness, infillPattern.Height - thickness,
+ thickness, thickness),
+ infillColor, -1);
+
+ // Center cross
+ int margin = (int) (InfillSpacing - accumulator + layerIndex) - thickness;
+ int marginInv = (int) (accumulator - layerIndex) - thickness;
+
+ if (_infillType == InfillAlgorithm.CubicCenterLink ||
+ (_infillType == InfillAlgorithm.CubicDynamicLink &&
+ dynamicCenter) ||
+ _infillType == InfillAlgorithm.CubicInterlinked)
+ {
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat, layerIndex, mask);
- SlicerFile[layerIndex].LayerMat = mat;
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(margin, margin, _infillThickness, _infillThickness),
+ infillColor, -1);
- progress.LockAndIncrement();
- });
- mask?.Dispose();
- return !progress.Token.IsCancellationRequested;
- }
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(marginInv, marginInv, _infillThickness,
+ _infillThickness),
+ infillColor, -1);
- public override bool Execute(Mat mat, params object[] arguments)
- {
- if (arguments is null || arguments.Length < 1) return false;
- var anchor = new Point(-1, -1);
- var kernel = EmguExtensions.Kernel3x3Rectangle;
- uint index = Convert.ToUInt32(arguments[0]);
- uint layerIndex = index - LayerIndexStart;
- var infillColor = new MCvScalar(_infillBrightness);
-
- Mat patternMask = null;
- using Mat erode = new ();
- using Mat diff = new ();
- var target = GetRoiOrDefault(mat);
- using var mask = GetMask(mat);
- bool disposeTargetMask = true;
-
- if (_infillType is InfillAlgorithm.Cubic
- or InfillAlgorithm.CubicCenterLink
- or InfillAlgorithm.CubicDynamicLink
- or InfillAlgorithm.CubicInterlinked)
- {
- using var infillPattern = EmguExtensions.InitMat(new Size(_infillSpacing, _infillSpacing));
- using var matPattern = mat.NewBlank();
- bool firstPattern = true;
- uint accumulator = 0;
- bool dynamicCenter = false;
- while (accumulator < layerIndex)
- {
- dynamicCenter = !dynamicCenter;
- firstPattern = true;
- accumulator += _infillSpacing;
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(margin, marginInv, _infillThickness,
+ _infillThickness),
+ infillColor, -1);
- if (accumulator >= layerIndex) break;
- firstPattern = false;
- accumulator += _infillThickness;
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(marginInv, margin, _infillThickness,
+ _infillThickness),
+ infillColor, -1);
}
- if (firstPattern)
+
+ if (_infillType == InfillAlgorithm.CubicInterlinked ||
+ (_infillType == InfillAlgorithm.CubicDynamicLink &&
+ !dynamicCenter))
{
- int thickness = _infillThickness / 2;
- // Top Left
CvInvoke.Rectangle(infillPattern,
- new Rectangle(0, 0, thickness, thickness),
+ new Rectangle(margin, -thickness, _infillThickness,
+ _infillThickness),
infillColor, -1);
- // Top Right
CvInvoke.Rectangle(infillPattern,
- new Rectangle(infillPattern.Width - thickness, 0, thickness, thickness),
+ new Rectangle(marginInv, -thickness, _infillThickness,
+ _infillThickness),
infillColor, -1);
- // Bottom Left
CvInvoke.Rectangle(infillPattern,
- new Rectangle(0, infillPattern.Height - thickness, thickness, thickness),
+ new Rectangle(-thickness, margin, _infillThickness,
+ _infillThickness),
infillColor, -1);
- // Bottom Right
CvInvoke.Rectangle(infillPattern,
- new Rectangle(infillPattern.Width - thickness, infillPattern.Height - thickness,
- thickness, thickness),
+ new Rectangle(-thickness, marginInv, _infillThickness,
+ _infillThickness),
infillColor, -1);
- // Center cross
- int margin = (int) (InfillSpacing - accumulator + layerIndex) - thickness;
- int marginInv = (int) (accumulator - layerIndex) - thickness;
-
- if (_infillType == InfillAlgorithm.CubicCenterLink ||
- (_infillType == InfillAlgorithm.CubicDynamicLink &&
- dynamicCenter) ||
- _infillType == InfillAlgorithm.CubicInterlinked)
- {
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, margin, _infillThickness, _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, marginInv, _infillThickness,
- _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, marginInv, _infillThickness,
- _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, margin, _infillThickness,
- _infillThickness),
- infillColor, -1);
- }
-
-
- if (_infillType == InfillAlgorithm.CubicInterlinked ||
- (_infillType == InfillAlgorithm.CubicDynamicLink &&
- !dynamicCenter))
- {
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, -thickness, _infillThickness,
- _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, -thickness, _infillThickness,
- _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(-thickness, margin, _infillThickness,
- _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(-thickness, marginInv, _infillThickness,
- _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(InfillSpacing - thickness, margin,
- _infillThickness, _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(InfillSpacing - thickness, marginInv,
- _infillThickness, _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(margin, InfillSpacing - thickness,
- _infillThickness, _infillThickness),
- infillColor, -1);
-
- CvInvoke.Rectangle(infillPattern,
- new Rectangle(marginInv, InfillSpacing - thickness,
- _infillThickness, _infillThickness),
- infillColor, -1);
- }
- }
- else
- {
CvInvoke.Rectangle(infillPattern,
- new Rectangle(0, 0, _infillSpacing, _infillSpacing),
- infillColor, _infillThickness);
- }
+ new Rectangle(InfillSpacing - thickness, margin,
+ _infillThickness, _infillThickness),
+ infillColor, -1);
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(InfillSpacing - thickness, marginInv,
+ _infillThickness, _infillThickness),
+ infillColor, -1);
- CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1,
- target.Cols / infillPattern.Cols + 1, matPattern);
- patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
- disposeTargetMask = true;
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(margin, InfillSpacing - thickness,
+ _infillThickness, _infillThickness),
+ infillColor, -1);
+
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(marginInv, InfillSpacing - thickness,
+ _infillThickness, _infillThickness),
+ infillColor, -1);
+ }
}
- else if (_infillType == InfillAlgorithm.Honeycomb)
+ else
{
- if (arguments.Length >= 2)
- {
- patternMask = (Mat)arguments[1];
- disposeTargetMask = false;
- }
- else
- {
- patternMask = GetHoneycombMask(target.Size);
- disposeTargetMask = true;
- }
+ CvInvoke.Rectangle(infillPattern,
+ new Rectangle(0, 0, _infillSpacing, _infillSpacing),
+ infillColor, _infillThickness);
}
- //patternMask.Save("D:\\pattern.png");
- CvInvoke.Erode(target, erode, kernel, anchor, WallThickness, BorderType.Reflect101,
- default);
- CvInvoke.Subtract(target, erode, diff);
- CvInvoke.BitwiseAnd(erode, patternMask, target, mask);
- CvInvoke.Add(target, diff, target, mask);
- if (disposeTargetMask)
+ CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1,
+ target.Cols / infillPattern.Cols + 1, matPattern);
+ patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
+ disposeTargetMask = true;
+ }
+ else if (_infillType == InfillAlgorithm.Honeycomb)
+ {
+ if (arguments.Length >= 2)
{
- patternMask.Dispose();
+ patternMask = (Mat)arguments[1];
+ disposeTargetMask = false;
+ }
+ else
+ {
+ patternMask = GetHoneycombMask(target.Size);
+ disposeTargetMask = true;
}
-
- return true;
}
+ //patternMask.Save("D:\\pattern.png");
+ CvInvoke.Erode(target, erode, kernel, anchor, WallThickness, BorderType.Reflect101,
+ default);
+ CvInvoke.Subtract(target, erode, diff);
- public Mat GetHoneycombMask(Size targetSize)
+ CvInvoke.BitwiseAnd(erode, patternMask, target, mask);
+ CvInvoke.Add(target, diff, target, mask);
+
+ if (disposeTargetMask)
{
- var patternMask = EmguExtensions.InitMat(targetSize);
+ patternMask!.Dispose();
+ }
- var halfInfillSpacing = _infillSpacing / 2;
- var halfThickenss = _infillThickness / 2;
- int width = (int)Math.Round(4 * (_infillSpacing / 2.0 / Math.Sqrt(3)));
- var infillColor = new MCvScalar(_infillBrightness);
+ return true;
+ }
- for (int col = 0; col <= targetSize.Width / _infillSpacing; col++)
+ public Mat GetHoneycombMask(Size targetSize)
+ {
+ var patternMask = EmguExtensions.InitMat(targetSize);
+
+ var halfInfillSpacing = _infillSpacing / 2;
+ var halfThickenss = _infillThickness / 2;
+ int width = (int)Math.Round(4 * (_infillSpacing / 2.0 / Math.Sqrt(3)));
+ var infillColor = new MCvScalar(_infillBrightness);
+
+ for (int col = 0; col <= targetSize.Width / _infillSpacing; col++)
+ {
+ for (int row = 0; row <= targetSize.Height / _infillSpacing; row++)
{
- for (int row = 0; row <= targetSize.Height / _infillSpacing; row++)
+ // Move over for the column number.
+ int x = (int)Math.Round(col * (width * 0.75f));
+
+ // Move down the required number of rows.
+ int y = row * _infillSpacing;
+
+ // If the column is odd, move down half a hex more.
+ if (col % 2 == 1) y += halfInfillSpacing;
+
+ var points = new Point[]
{
- // Move over for the column number.
- int x = (int)Math.Round(col * (width * 0.75f));
-
- // Move down the required number of rows.
- int y = row * _infillSpacing;
-
- // If the column is odd, move down half a hex more.
- if (col % 2 == 1) y += halfInfillSpacing;
-
- var points = new Point[]
- {
- new(x, y),
- new((int) Math.Round(x + width * 0.25f), y - _infillSpacing / 2),
- new((int) Math.Round(x + width * 0.75f), y - _infillSpacing / 2),
- new(x + width, y),
- new((int) Math.Round(x + width * 0.75f), y + _infillSpacing / 2),
- new((int) Math.Round(x + width * 0.25f), y + _infillSpacing / 2),
- };
-
- CvInvoke.Polylines(patternMask, points, true, infillColor, _infillThickness);
- }
+ new(x, y),
+ new((int) Math.Round(x + width * 0.25f), y - _infillSpacing / 2),
+ new((int) Math.Round(x + width * 0.75f), y - _infillSpacing / 2),
+ new(x + width, y),
+ new((int) Math.Round(x + width * 0.75f), y + _infillSpacing / 2),
+ new((int) Math.Round(x + width * 0.25f), y + _infillSpacing / 2),
+ };
+
+ CvInvoke.Polylines(patternMask, points, true, infillColor, _infillThickness);
}
-
- return patternMask;
}
- #endregion
+ return patternMask;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs
index e3a8411..3bdafdc 100644
--- a/UVtools.Core/Operations/OperationLayerArithmetic.cs
+++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs
@@ -6,329 +6,329 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationLayerArithmetic : Operation
{
- [Serializable]
- public class OperationLayerArithmetic : Operation
+ #region Members
+ private string _sentence = null!;
+ #endregion
+
+ #region Enums
+ public enum LayerArithmeticOperators : byte
{
- #region Members
- private string _sentence;
- #endregion
+ None,
+ Add,
+ Subtract,
+ Multiply,
+ Divide,
+ BitwiseAnd,
+ BitwiseOr,
+ BitwiseXor,
+ AbsDiff
+ }
+ #endregion
- #region Enums
- public enum LayerArithmeticOperators : byte
- {
- None,
- Add,
- Subtract,
- Multiply,
- Divide,
- BitwiseAnd,
- BitwiseOr,
- BitwiseXor,
- AbsDiff
- }
- #endregion
+ #region SubClasses
+ public sealed class ArithmeticOperation
+ {
+ public uint LayerIndex { get; }
+ public LayerArithmeticOperators Operator { get; }
- #region SubClasses
- public sealed class ArithmeticOperation
+ public ArithmeticOperation(uint layerIndex, LayerArithmeticOperators layerArithmeticOperator)
{
- public uint LayerIndex { get; }
- public LayerArithmeticOperators Operator { get; }
-
- public ArithmeticOperation(uint layerIndex, LayerArithmeticOperators layerArithmeticOperator)
- {
- LayerIndex = layerIndex;
- Operator = layerArithmeticOperator;
- }
- }
- #endregion
-
- #region Overrides
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override string Title => "Layer arithmetic";
- public override string Description =>
- "Perform arithmetic operations over the layers\n" +
- "Available operators:\n" +
- " + - * / = Add, Subtract, Multiply, Divide\n" +
- " & | ^ = Bitwise AND, OR, XOR\n" +
- " $ = Absolute difference\n\n" +
- "Syntax: <set_to_layer_indexes> = <layer_index> <operator> <layer_index>\n" +
- "When: \"<set_to_layer_indexes> =\" is omitted, the result will assign to the first layer on the sentence.\n\n" +
- "Example 1: 10+11\n" +
- "Example 2: 10,11,12 = 11+12-10*5 Same as: 10:12 = 11+12-10*5\n" +
- "On example 1 the layer 10 will be set with the result of layer 10 plus layer 11.\n" +
- "On example 2 the layers 10,11,12 will be set with the result of layer 11 plus 12 minus 10 all multiplied by layer 5.\n\n" +
- "Note: Calculation are made sequential, math order rules wont apply here.";
-
- public override string ConfirmationText =>
- $"perform this arithmetic operation";
-
- public override string ProgressTitle =>
- $"performing the arithmetic operations";
-
- public override string ProgressAction => "Calculated layers";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
- if (string.IsNullOrWhiteSpace(_sentence))
- sb.AppendLine("The sentence is empty.");
- else if(!Parse())
- sb.AppendLine("Unable to parse the sentence, malformed or incomplete.");
- else if (SetLayers.Count == 0)
- sb.AppendLine("No layers to assign.");
- else if (Operations.Count == 0)
- sb.AppendLine("No operations to perform.");
- else if (!IsValid)
- sb.AppendLine("The operation will have no impact and will not be performed.");
-
- return sb.ToString();
+ LayerIndex = layerIndex;
+ Operator = layerArithmeticOperator;
}
+ }
+ #endregion
+
+ #region Overrides
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "fas fa-square-root-alt";
+ public override string Title => "Layer arithmetic";
+ public override string Description =>
+ "Perform arithmetic operations over the layers\n" +
+ "Available operators:\n" +
+ " + - * / = Add, Subtract, Multiply, Divide\n" +
+ " & | ^ = Bitwise AND, OR, XOR\n" +
+ " $ = Absolute difference\n\n" +
+ "Syntax: <set_to_layer_indexes> = <layer_index> <operator> <layer_index>\n" +
+ "When: \"<set_to_layer_indexes> =\" is omitted, the result will assign to the first layer on the sentence.\n\n" +
+ "Example 1: 10+11\n" +
+ "Example 2: 10,11,12 = 11+12-10*5 Same as: 10:12 = 11+12-10*5\n" +
+ "On example 1 the layer 10 will be set with the result of layer 10 plus layer 11.\n" +
+ "On example 2 the layers 10,11,12 will be set with the result of layer 11 plus 12 minus 10 all multiplied by layer 5.\n\n" +
+ "Note: Calculation are made sequential, math order rules wont apply here.";
+
+ public override string ConfirmationText =>
+ $"perform this arithmetic operation";
+
+ public override string ProgressTitle =>
+ $"performing the arithmetic operations";
+
+ public override string ProgressAction => "Calculated layers";
+
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ if (string.IsNullOrWhiteSpace(_sentence))
+ sb.AppendLine("The sentence is empty.");
+ else if(!Parse())
+ sb.AppendLine("Unable to parse the sentence, malformed or incomplete.");
+ else if (SetLayers.Count == 0)
+ sb.AppendLine("No layers to assign.");
+ else if (Operations.Count == 0)
+ sb.AppendLine("No operations to perform.");
+ else if (!IsValid)
+ sb.AppendLine("The operation will have no impact and will not be performed.");
+
+ return sb.ToString();
+ }
- public override string ToString()
- {
- var result = $"{_sentence}";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
+ public override string ToString()
+ {
+ var result = $"{_sentence}";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- #region Properties
- public string Sentence
- {
- get => _sentence;
- set => RaiseAndSetIfChanged(ref _sentence, value);
- }
- [XmlIgnore]
- public List<ArithmeticOperation> Operations { get; private set; } = new();
+ #region Properties
+ public string Sentence
+ {
+ get => _sentence;
+ set => RaiseAndSetIfChanged(ref _sentence, value);
+ }
+ [XmlIgnore]
+ public List<ArithmeticOperation> Operations { get; private set; } = new();
- [XmlIgnore]
- public List<uint> SetLayers { get; private set; } = new();
+ [XmlIgnore]
+ public List<uint> SetLayers { get; private set; } = new();
- public bool IsValid => SetLayers.Count > 0 && Operations.Count > 0 &&
- !(SetLayers.Count == 1 && Operations.Count == 1 && SetLayers[0] == Operations[0].LayerIndex);
- #endregion
+ public bool IsValid => SetLayers.Count > 0 && Operations.Count > 0 &&
+ !(SetLayers.Count == 1 && Operations.Count == 1 && SetLayers[0] == Operations[0].LayerIndex);
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationLayerArithmetic() { }
+ public OperationLayerArithmetic() { }
- public OperationLayerArithmetic(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationLayerArithmetic(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- public bool Parse()
- {
- if (string.IsNullOrEmpty(_sentence)) return false;
- SetLayers.Clear();
- Operations.Clear();
+ public bool Parse()
+ {
+ if (string.IsNullOrEmpty(_sentence)) return false;
+ SetLayers.Clear();
+ Operations.Clear();
- var splitSentence = _sentence.Split('=');
- var operations = splitSentence[0];
- if (splitSentence.Length >= 2)
+ var splitSentence = _sentence.Split('=');
+ var operations = splitSentence[0];
+ if (splitSentence.Length >= 2)
+ {
+ operations = splitSentence[1];
+ var setLayers = splitSentence[0].Replace(" ", string.Empty).Split(',', StringSplitOptions.TrimEntries);
+ foreach (var layer in setLayers)
{
- operations = splitSentence[1];
- var setLayers = splitSentence[0].Replace(" ", string.Empty).Split(',', StringSplitOptions.TrimEntries);
- foreach (var layer in setLayers)
+ var rangeSplit = layer.Split(':', StringSplitOptions.TrimEntries);
+ if (rangeSplit.Length > 1)
{
- var rangeSplit = layer.Split(':', StringSplitOptions.TrimEntries);
- if (rangeSplit.Length > 1)
+ uint.TryParse(rangeSplit[0].Trim(), out var startLayer);
+ if (!uint.TryParse(rangeSplit[1].Trim(), out var endLayer)) endLayer = SlicerFile.LastLayerIndex;
+ for (var index = startLayer; index <= endLayer; index++)
{
- uint.TryParse(rangeSplit[0].Trim(), out var startLayer);
- if (!uint.TryParse(rangeSplit[1].Trim(), out var endLayer)) endLayer = SlicerFile.LastLayerIndex;
- for (var index = startLayer; index <= endLayer; index++)
- {
- if (SetLayers.Contains(index)) continue;
- SetLayers.Add(index);
- }
- continue;
+ if (SetLayers.Contains(index)) continue;
+ SetLayers.Add(index);
}
-
- if (!uint.TryParse(layer.Trim(), out var layerIndex)) continue;
- if (SetLayers.Contains(layerIndex)) continue;
- SetLayers.Add(layerIndex);
+ continue;
}
+
+ if (!uint.TryParse(layer.Trim(), out var layerIndex)) continue;
+ if (SetLayers.Contains(layerIndex)) continue;
+ SetLayers.Add(layerIndex);
}
+ }
- SetLayers = SetLayers.Where(layerIndex => layerIndex <= SlicerFile.LastLayerIndex).ToList();
- SetLayers.Sort();
+ SetLayers = SetLayers.Where(layerIndex => layerIndex <= SlicerFile.LastLayerIndex).ToList();
+ SetLayers.Sort();
- operations = operations.Replace(" ", string.Empty);
- if (string.IsNullOrWhiteSpace(operations)) return false;
+ operations = operations.Replace(" ", string.Empty);
+ if (string.IsNullOrWhiteSpace(operations)) return false;
- string layerIndexStr = string.Empty;
- foreach (char c in operations)
+ string layerIndexStr = string.Empty;
+ foreach (char c in operations)
+ {
+ if (c >= '0' && c <= '9')
{
- if (c >= '0' && c <= '9')
- {
- layerIndexStr += c;
- continue;
- }
-
- LayerArithmeticOperators op = LayerArithmeticOperators.None;
- switch (c)
- {
- case '+':
- op = LayerArithmeticOperators.Add;
- break;
- case '-':
- op = LayerArithmeticOperators.Subtract;
- break;
- case '*':
- op = LayerArithmeticOperators.Multiply;
- break;
- case '/':
- op = LayerArithmeticOperators.Divide;
- break;
- case '&':
- op = LayerArithmeticOperators.BitwiseAnd;
- break;
- case '|':
- op = LayerArithmeticOperators.BitwiseOr;
- break;
- case '^':
- op = LayerArithmeticOperators.BitwiseXor;
- break;
- case '$':
- op = LayerArithmeticOperators.AbsDiff;
- break;
- }
-
- if (op == LayerArithmeticOperators.None // No valid operator
- || string.IsNullOrWhiteSpace(layerIndexStr) // Started with a operator instead of layer
- ) continue;
+ layerIndexStr += c;
+ continue;
+ }
+ LayerArithmeticOperators op = LayerArithmeticOperators.None;
+ switch (c)
+ {
+ case '+':
+ op = LayerArithmeticOperators.Add;
+ break;
+ case '-':
+ op = LayerArithmeticOperators.Subtract;
+ break;
+ case '*':
+ op = LayerArithmeticOperators.Multiply;
+ break;
+ case '/':
+ op = LayerArithmeticOperators.Divide;
+ break;
+ case '&':
+ op = LayerArithmeticOperators.BitwiseAnd;
+ break;
+ case '|':
+ op = LayerArithmeticOperators.BitwiseOr;
+ break;
+ case '^':
+ op = LayerArithmeticOperators.BitwiseXor;
+ break;
+ case '$':
+ op = LayerArithmeticOperators.AbsDiff;
+ break;
+ }
- if (uint.TryParse(layerIndexStr, out var layerIndex))
- {
- Operations.Add(new ArithmeticOperation(layerIndex, op));
- }
+ if (op == LayerArithmeticOperators.None // No valid operator
+ || string.IsNullOrWhiteSpace(layerIndexStr) // Started with a operator instead of layer
+ ) continue;
- // Reset layer string
- layerIndexStr = string.Empty;
- }
- // Append the left over
- if (!string.IsNullOrWhiteSpace(layerIndexStr))
+ if (uint.TryParse(layerIndexStr, out var layerIndex))
{
- if (uint.TryParse(layerIndexStr, out var layerIndex))
- {
- Operations.Add(new ArithmeticOperation(layerIndex, LayerArithmeticOperators.None));
- }
+ Operations.Add(new ArithmeticOperation(layerIndex, op));
}
- Operations = Operations.Where(op => op.LayerIndex <= SlicerFile.LastLayerIndex).ToList();
+ // Reset layer string
+ layerIndexStr = string.Empty;
+ }
- //if (Operations.Count == 0) return false;
- if (SetLayers.Count == 0 && Operations.Count > 0)
+ // Append the left over
+ if (!string.IsNullOrWhiteSpace(layerIndexStr))
+ {
+ if (uint.TryParse(layerIndexStr, out var layerIndex))
{
- SetLayers.Add(Operations[0].LayerIndex);
+ Operations.Add(new ArithmeticOperation(layerIndex, LayerArithmeticOperators.None));
}
-
- return true;
}
- protected override bool ExecuteInternally(OperationProgress progress)
+ Operations = Operations.Where(op => op.LayerIndex <= SlicerFile.LastLayerIndex).ToList();
+
+ //if (Operations.Count == 0) return false;
+ if (SetLayers.Count == 0 && Operations.Count > 0)
{
- if (!IsValid) return false;
+ SetLayers.Add(Operations[0].LayerIndex);
+ }
- using var result = SlicerFile[Operations[0].LayerIndex].LayerMat;
- using var resultRoi = GetRoiOrDefault(result);
- using var imageMask = GetMask(resultRoi);
+ return true;
+ }
- progress.ItemCount = (uint) Operations.Count;
- for (int i = 1; i < Operations.Count; i++)
- {
- progress.Token.ThrowIfCancellationRequested();
- using var image = SlicerFile[Operations[i].LayerIndex].LayerMat;
- var imageRoi = GetRoiOrDefault(image);
-
- switch (Operations[i - 1].Operator)
- {
- case LayerArithmeticOperators.Add:
- CvInvoke.Add(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.Subtract:
- CvInvoke.Subtract(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.Multiply:
- CvInvoke.Multiply(resultRoi, imageRoi, resultRoi);
- break;
- case LayerArithmeticOperators.Divide:
- CvInvoke.Divide(resultRoi, imageRoi, resultRoi);
- break;
- case LayerArithmeticOperators.BitwiseAnd:
- CvInvoke.BitwiseAnd(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.BitwiseOr:
- CvInvoke.BitwiseOr(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.BitwiseXor:
- CvInvoke.BitwiseXor(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.AbsDiff:
- CvInvoke.AbsDiff(resultRoi, imageRoi, resultRoi);
- break;
- }
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (!IsValid) return false;
+
+ using var result = SlicerFile[Operations[0].LayerIndex].LayerMat;
+ using var resultRoi = GetRoiOrDefault(result);
+ using var imageMask = GetMask(resultRoi);
- progress++;
+ progress.ItemCount = (uint) Operations.Count;
+ for (int i = 1; i < Operations.Count; i++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ using var image = SlicerFile[Operations[i].LayerIndex].LayerMat;
+ var imageRoi = GetRoiOrDefault(image);
+
+ switch (Operations[i - 1].Operator)
+ {
+ case LayerArithmeticOperators.Add:
+ CvInvoke.Add(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.Subtract:
+ CvInvoke.Subtract(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.Multiply:
+ CvInvoke.Multiply(resultRoi, imageRoi, resultRoi);
+ break;
+ case LayerArithmeticOperators.Divide:
+ CvInvoke.Divide(resultRoi, imageRoi, resultRoi);
+ break;
+ case LayerArithmeticOperators.BitwiseAnd:
+ CvInvoke.BitwiseAnd(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.BitwiseOr:
+ CvInvoke.BitwiseOr(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.BitwiseXor:
+ CvInvoke.BitwiseXor(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.AbsDiff:
+ CvInvoke.AbsDiff(resultRoi, imageRoi, resultRoi);
+ break;
}
- progress.Reset("Applied layers", (uint) SetLayers.Count);
- Parallel.ForEach(SetLayers, CoreSettings.ParallelOptions, layerIndex =>
+ progress++;
+ }
+
+ progress.Reset("Applied layers", (uint) SetLayers.Count);
+ Parallel.ForEach(SetLayers, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ progress.LockAndIncrement();
+ if (Operations.Count == 1 || HaveROIorMask)
{
- if (progress.Token.IsCancellationRequested) return;
- progress.LockAndIncrement();
- if (Operations.Count == 1 || HaveROIorMask)
- {
- using var mat = SlicerFile[layerIndex].LayerMat;
- var matRoi = GetRoiOrDefault(mat);
- resultRoi.CopyTo(matRoi, imageMask);
- SlicerFile[layerIndex].LayerMat = mat;
- return;
- }
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ var matRoi = GetRoiOrDefault(mat);
+ resultRoi.CopyTo(matRoi, imageMask);
+ SlicerFile[layerIndex].LayerMat = mat;
+ return;
+ }
- //ApplyMask(mat, resultRoi, imageMask);
+ //ApplyMask(mat, resultRoi, imageMask);
- SlicerFile[layerIndex].LayerMat = result;
- });
+ SlicerFile[layerIndex].LayerMat = result;
+ });
- return !progress.Token.IsCancellationRequested;
- }
+ return !progress.Token.IsCancellationRequested;
+ }
- #endregion
+ #endregion
- #region Equality
- protected bool Equals(OperationLayerArithmetic other)
- {
- return _sentence == other._sentence;
- }
+ #region Equality
+ protected bool Equals(OperationLayerArithmetic other)
+ {
+ return _sentence == other._sentence;
+ }
- public override bool Equals(object obj)
- {
- if (obj is null) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((OperationLayerArithmetic) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationLayerArithmetic) obj);
+ }
- public override int GetHashCode()
- {
- return (_sentence != null ? _sentence.GetHashCode() : 0);
- }
- #endregion
+ public override int GetHashCode()
+ {
+ return (_sentence != null ? _sentence.GetHashCode() : 0);
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs
index 917ad13..3dd6229 100644
--- a/UVtools.Core/Operations/OperationLayerClone.cs
+++ b/UVtools.Core/Operations/OperationLayerClone.cs
@@ -7,156 +7,153 @@
*/
using System;
+using System.Linq;
using System.Text;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public sealed class OperationLayerClone : Operation
- {
- #region Members
- private uint _clones = 1;
- private bool _keepSamePositionZ;
+namespace UVtools.Core.Operations;
- #endregion
+[Serializable]
+public sealed class OperationLayerClone : Operation
+{
+ #region Members
+ private uint _clones = 1;
+ private bool _keepSamePositionZ;
- #region Overrides
+ #endregion
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
- public override bool CanROI => false;
- public override bool PassActualLayerIndex => true;
+ #region Overrides
- public override string Title => "Clone layers";
- public override string Description =>
- "Clone layers.\n\n" +
- "Useful to increase the height of the model or add additional structure by duplicating layers. For example, can be used to increase the raft height for added stability.";
- public override string ConfirmationText =>
- $"clone layers {LayerIndexStart} through {LayerIndexEnd}, {Clones} time{(Clones != 1 ? "s" : "")}?";
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
+ public override bool CanROI => false;
+ public override bool PassActualLayerIndex => true;
+ public override string IconClass => "fas fa-clone";
+ public override string Title => "Clone layers";
+ public override string Description =>
+ "Clone layers.\n\n" +
+ "Useful to increase the height of the model or add additional structure by duplicating layers. For example, can be used to increase the raft height for added stability.";
+ public override string ConfirmationText =>
+ $"clone layers {LayerIndexStart} through {LayerIndexEnd}, {Clones} time{(Clones != 1 ? "s" : "")}?";
- public override string ProgressTitle =>
- $"Cloning layers {LayerIndexStart} through {LayerIndexEnd}, {Clones} time{(Clones != 1 ? "s" : "")}";
+ public override string ProgressTitle =>
+ $"Cloning layers {LayerIndexStart} through {LayerIndexEnd}, {Clones} time{(Clones != 1 ? "s" : "")}";
- public override string ProgressAction => "Cloned layers";
+ public override string ProgressAction => "Cloned layers";
- public override bool CanCancel => false;
+ public override bool CanCancel => false;
- public override bool CanHaveProfiles => false;
+ public override bool CanHaveProfiles => false;
- public override string ValidateInternally()
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ if (Clones <= 0)
{
- var sb = new StringBuilder();
- if (Clones <= 0)
- {
- sb.AppendLine("Clones must be a positive number");
- }
-
- return sb.ToString();
+ sb.AppendLine("Clones must be a positive number");
}
- public override string ToString()
- {
- var result = $"[Clones: {Clones}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ return sb.ToString();
+ }
- #endregion
+ public override string ToString()
+ {
+ var result = $"[Clones: {Clones}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #region Properties
+ #endregion
- /// <summary>
- /// Gets or sets if cloned layers will keep same position z or get the height rebuilt
- /// </summary>
- public bool KeepSamePositionZ
- {
- get => _keepSamePositionZ;
- set => RaiseAndSetIfChanged(ref _keepSamePositionZ, value);
- }
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets if cloned layers will keep same position z or get the height rebuilt
+ /// </summary>
+ public bool KeepSamePositionZ
+ {
+ get => _keepSamePositionZ;
+ set => RaiseAndSetIfChanged(ref _keepSamePositionZ, value);
+ }
- /// <summary>
- /// Gets or sets the number of clones
- /// </summary>
- public uint Clones
+ /// <summary>
+ /// Gets or sets the number of clones
+ /// </summary>
+ public uint Clones
+ {
+ get => _clones;
+ set
{
- get => _clones;
- set
- {
- if(!RaiseAndSetIfChanged(ref _clones, value)) return;
- RaisePropertyChanged(nameof(ExtraLayers));
- }
+ if(!RaiseAndSetIfChanged(ref _clones, value)) return;
+ RaisePropertyChanged(nameof(ExtraLayers));
}
+ }
- public uint ExtraLayers => (uint)Math.Max(0, ((int)LayerIndexEnd - LayerIndexStart + 1) * _clones);
+ public uint ExtraLayers => (uint)Math.Max(0, ((int)LayerIndexEnd - LayerIndexStart + 1) * _clones);
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationLayerClone() { }
+ public OperationLayerClone() { }
- public OperationLayerClone(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationLayerClone(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- uint totalClones = (LayerIndexEnd - LayerIndexStart + 1) * Clones;
- progress.Reset(ProgressAction, totalClones);
+ void Increment()
+ {
- var newLayers = new Layer[SlicerFile.LayerCount + totalClones];
- uint newLayerIndex = 0;
- for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++)
- {
- newLayers[newLayerIndex++] = SlicerFile[layerIndex];
- if (layerIndex < LayerIndexStart || layerIndex > LayerIndexEnd) continue;
- for (uint i = 0; i < Clones; i++)
- {
- newLayers[newLayerIndex++] = SlicerFile[layerIndex].Clone();
- progress++;
- }
- }
+ }
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerManager.Layers = newLayers;
- }, !_keepSamePositionZ);
-
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ uint totalClones = (LayerIndexEnd - LayerIndexStart + 1) * Clones;
+ progress.Reset(ProgressAction, totalClones);
- /*var oldLayers = SlicerFile.LayerManager.Layers;
+ var oldLayers = SlicerFile.ToArray();
- uint totalClones = (LayerIndexEnd - LayerIndexStart + 1) * Clones;
- uint newLayerCount = (uint) (oldLayers.Length + totalClones);
- var layers = new Layer[newLayerCount];
+ SlicerFile.Init(SlicerFile.LayerCount + totalClones);
+ //var newLayers = new Layer[SlicerFile.LayerCount + totalClones];
+ uint newLayerIndex = 0;
+ float incrementedPositionZ = 0;
+ for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
+ {
+ SlicerFile[newLayerIndex++] = oldLayers[layerIndex];
- uint newLayerIndex = 0;
- for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
+ if (!_keepSamePositionZ && incrementedPositionZ > 0)
+ {
+ oldLayers[layerIndex].PositionZ += incrementedPositionZ;
+ }
+
+ if (layerIndex < LayerIndexStart || layerIndex > LayerIndexEnd) continue;
+ float increment = SlicerFile[layerIndex].RelativePositionZ;
+ for (uint i = 0; i < _clones; i++)
{
- layers[newLayerIndex] = oldLayers[layerIndex];
- if (layerIndex >= LayerIndexStart && layerIndex <= LayerIndexEnd)
+ SlicerFile[newLayerIndex] = oldLayers[layerIndex].Clone();
+
+ if (!_keepSamePositionZ)
{
- for (uint i = 0; i < Clones; i++)
- {
- newLayerIndex++;
- layers[newLayerIndex] = oldLayers[layerIndex].Clone();
- layers[newLayerIndex].IsModified = true;
-
- progress++;
- }
+ incrementedPositionZ += increment;
+ SlicerFile[newLayerIndex].PositionZ += increment * (i + 1);
}
newLayerIndex++;
+ progress++;
}
+ }
- SlicerFile.LayerManager.Layers = layers;*/
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.Layers = SlicerFile.Layers; // Reassign for update
+ });
- return !progress.Token.IsCancellationRequested;
- }
- #endregion
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerExportGif.cs b/UVtools.Core/Operations/OperationLayerExportGif.cs
index 039f6d0..f3258f6 100644
--- a/UVtools.Core/Operations/OperationLayerExportGif.cs
+++ b/UVtools.Core/Operations/OperationLayerExportGif.cs
@@ -6,311 +6,311 @@
* of this license document, but changing it is not allowed.
*/
+using AnimatedGif;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading.Tasks;
-using AnimatedGif;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public sealed class OperationLayerExportGif : Operation
- {
- #region Members
- private string _filePath;
- private bool _clipByVolumeBounds;
- private bool _renderLayerCount = true;
- private byte _fps = 30;
- private ushort _repeats;
- private ushort _skip;
- private decimal _scale = 50;
- private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
- private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
-
- #endregion
+namespace UVtools.Core.Operations;
- #region Overrides
+[Serializable]
+public sealed class OperationLayerExportGif : Operation
+{
+ #region Members
+ private string _filePath = null!;
+ private bool _clipByVolumeBounds;
+ private bool _renderLayerCount = true;
+ private byte _fps = 30;
+ private ushort _repeats;
+ private ushort _skip;
+ private decimal _scale = 50;
+ private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
+ private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
- public override string Title => "Export layers to GIF";
+ #endregion
- public override string Description =>
- "Export a layer range to an animated GIF file.\n" +
- "Note: This process is slow, optimize the parameters to output few layers as possible and/or scale them down.";
+ #region Overrides
- public override string ConfirmationText =>
- $"export layers {LayerIndexStart} through {LayerIndexEnd} and pack {TotalLayers} layers?";
+ public override string IconClass => "mdi-file-gif-box";
+ public override string Title => "Export layers to GIF";
- public override string ProgressTitle =>
- $"Exporting layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string Description =>
+ "Export a layer range to an animated GIF file.\n" +
+ "Note: This process is slow, optimize the parameters to output few layers as possible and/or scale them down.";
- public override string ProgressAction => "Exported layers";
+ public override string ConfirmationText =>
+ $"export layers {LayerIndexStart} through {LayerIndexEnd} and pack {TotalLayers} layers?";
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ public override string ProgressTitle =>
+ $"Exporting layers {LayerIndexStart} through {LayerIndexEnd}";
- if (TotalLayers == 0)
- {
- sb.AppendLine("There are no layers to pack, please adjust the configurations.");
- }
- /*else if (TotalLayers > 500)
- {
- sb.AppendLine("Packing more than 500 layers will cause most of the systems and browsers to not play the GIF animation.\n" +
- "For that reason the pack is limited to 500 maximum layers.\n" +
- "Use the 'Skip layers' option or adjust the layer range to limit the number of layers in the pack.");
- }*/
+ public override string ProgressAction => "Exported layers";
- return sb.ToString();
- }
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ToString()
+ if (TotalLayers == 0)
{
- var result = $"[Clip bounds: {_clipByVolumeBounds}]" +
- $" [Render count: {_renderLayerCount}]" +
- $" [FPS: {_fps}]" +
- $" [Repeats: {_repeats}]" +
- $" [Skip: {_skip}]" +
- $" [Scale: {_scale}%]" +
- $" [Rotate: {_rotateDirection}]" +
- $" [Flip: {_flipDirection}]" +
- LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("There are no layers to pack, please adjust the configurations.");
}
-
- protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ /*else if (TotalLayers > 500)
{
- if (e.PropertyName is nameof(LayerRangeCount))
- {
- RaisePropertyChanged(nameof(TotalLayers));
- RaisePropertyChanged(nameof(GifDurationMilliseconds));
- RaisePropertyChanged(nameof(GifDurationSeconds));
- }
- base.OnPropertyChanged(e);
- }
+ sb.AppendLine("Packing more than 500 layers will cause most of the systems and browsers to not play the GIF animation.\n" +
+ "For that reason the pack is limited to 500 maximum layers.\n" +
+ "Use the 'Skip layers' option or adjust the layer range to limit the number of layers in the pack.");
+ }*/
- #endregion
+ return sb.ToString();
+ }
- #region Properties
+ public override string ToString()
+ {
+ var result = $"[Clip bounds: {_clipByVolumeBounds}]" +
+ $" [Render count: {_renderLayerCount}]" +
+ $" [FPS: {_fps}]" +
+ $" [Repeats: {_repeats}]" +
+ $" [Skip: {_skip}]" +
+ $" [Scale: {_scale}%]" +
+ $" [Rotate: {_rotateDirection}]" +
+ $" [Flip: {_flipDirection}]" +
+ LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- public string FilePath
+ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName is nameof(LayerRangeCount))
{
- get => _filePath;
- set => RaiseAndSetIfChanged(ref _filePath, value);
+ RaisePropertyChanged(nameof(TotalLayers));
+ RaisePropertyChanged(nameof(GifDurationMilliseconds));
+ RaisePropertyChanged(nameof(GifDurationSeconds));
}
+ base.OnPropertyChanged(e);
+ }
- public bool ClipByVolumeBounds
- {
- get => _clipByVolumeBounds;
- set => RaiseAndSetIfChanged(ref _clipByVolumeBounds, value);
- }
+ #endregion
- public bool RenderLayerCount
- {
- get => _renderLayerCount;
- set => RaiseAndSetIfChanged(ref _renderLayerCount, value);
- }
+ #region Properties
- public byte FPS
- {
- get => _fps;
- set
- {
- if(!RaiseAndSetIfChanged(ref _fps, value)) return;
- RaisePropertyChanged(nameof(FPSToMilliseconds));
- RaisePropertyChanged(nameof(GifDurationMilliseconds));
- RaisePropertyChanged(nameof(GifDurationSeconds));
- }
- }
+ public string FilePath
+ {
+ get => _filePath;
+ set => RaiseAndSetIfChanged(ref _filePath, value);
+ }
- public int FPSToMilliseconds => 1000 / _fps;
+ public bool ClipByVolumeBounds
+ {
+ get => _clipByVolumeBounds;
+ set => RaiseAndSetIfChanged(ref _clipByVolumeBounds, value);
+ }
- public ushort Repeats
- {
- get => _repeats;
- set => RaiseAndSetIfChanged(ref _repeats, value);
- }
+ public bool RenderLayerCount
+ {
+ get => _renderLayerCount;
+ set => RaiseAndSetIfChanged(ref _renderLayerCount, value);
+ }
- public ushort Skip
+ public byte FPS
+ {
+ get => _fps;
+ set
{
- get => _skip;
- set
- {
- if(!RaiseAndSetIfChanged(ref _skip, value)) return;
- RaisePropertyChanged(nameof(TotalLayers));
- RaisePropertyChanged(nameof(GifDurationMilliseconds));
- RaisePropertyChanged(nameof(GifDurationSeconds));
- }
+ if(!RaiseAndSetIfChanged(ref _fps, value)) return;
+ RaisePropertyChanged(nameof(FPSToMilliseconds));
+ RaisePropertyChanged(nameof(GifDurationMilliseconds));
+ RaisePropertyChanged(nameof(GifDurationSeconds));
}
+ }
- public uint TotalLayers => (uint)(LayerRangeCount / (float) (_skip + 1));
+ public int FPSToMilliseconds => 1000 / _fps;
- public uint GifDurationMilliseconds => (uint)(TotalLayers * FPSToMilliseconds);
- public float GifDurationSeconds => (float)Math.Round(GifDurationMilliseconds / 1000.0, 2);
+ public ushort Repeats
+ {
+ get => _repeats;
+ set => RaiseAndSetIfChanged(ref _repeats, value);
+ }
- public decimal Scale
+ public ushort Skip
+ {
+ get => _skip;
+ set
{
- get => _scale;
- set => RaiseAndSetIfChanged(ref _scale, Math.Round(value, 2));
+ if(!RaiseAndSetIfChanged(ref _skip, value)) return;
+ RaisePropertyChanged(nameof(TotalLayers));
+ RaisePropertyChanged(nameof(GifDurationMilliseconds));
+ RaisePropertyChanged(nameof(GifDurationSeconds));
}
+ }
- public float ScaleFactor => (float)_scale / 100f;
+ public uint TotalLayers => (uint)(LayerRangeCount / (float) (_skip + 1));
- public Enumerations.RotateDirection RotateDirection
- {
- get => _rotateDirection;
- set => RaiseAndSetIfChanged(ref _rotateDirection, value);
- }
+ public uint GifDurationMilliseconds => (uint)(TotalLayers * FPSToMilliseconds);
+ public float GifDurationSeconds => (float)Math.Round(GifDurationMilliseconds / 1000.0, 2);
- public Enumerations.FlipDirection FlipDirection
- {
- get => _flipDirection;
- set => RaiseAndSetIfChanged(ref _flipDirection, value);
- }
+ public decimal Scale
+ {
+ get => _scale;
+ set => RaiseAndSetIfChanged(ref _scale, Math.Round(value, 2));
+ }
- #endregion
+ public float ScaleFactor => (float)_scale / 100f;
- #region Constructor
+ public Enumerations.RotateDirection RotateDirection
+ {
+ get => _rotateDirection;
+ set => RaiseAndSetIfChanged(ref _rotateDirection, value);
+ }
- public OperationLayerExportGif()
- { }
+ public Enumerations.FlipDirection FlipDirection
+ {
+ get => _flipDirection;
+ set => RaiseAndSetIfChanged(ref _flipDirection, value);
+ }
- public OperationLayerExportGif(FileFormat slicerFile) : base(slicerFile)
- {
- _flipDirection = SlicerFile.DisplayMirror;
- _skip = TotalLayers switch
- {
- > 5000 => 2,
- > 1000 => 1,
- _ => _skip
- };
- /*while (TotalLayers > 500)
- {
- _skip++;
- }*/
- }
+ #endregion
+
+ #region Constructor
- public override void InitWithSlicerFile()
+ public OperationLayerExportGif()
+ { }
+
+ public OperationLayerExportGif(FileFormat slicerFile) : base(slicerFile)
+ {
+ _flipDirection = SlicerFile.DisplayMirror;
+ _skip = TotalLayers switch
{
- _filePath = SlicerFile.FileFullPath + ".gif";
- }
+ > 5000 => 2,
+ > 1000 => 1,
+ _ => _skip
+ };
+ /*while (TotalLayers > 500)
+ {
+ _skip++;
+ }*/
+ }
- #endregion
+ public override void InitWithSlicerFile()
+ {
+ _filePath = SlicerFile.FileFullPath + ".gif";
+ }
- #region Methods
+ #endregion
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- using var gif = AnimatedGif.AnimatedGif.Create(_filePath, FPSToMilliseconds, _repeats);
- var layerBuffer = new byte[TotalLayers][];
- progress.Reset("Optimized layers", TotalLayers);
+ #region Methods
- var fontFace = FontFace.HersheyDuplex;
- float fontScale = 1.5f;
- int fontThickness = 2;
- MCvScalar textColor = new(200);
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ using var gif = AnimatedGif.AnimatedGif.Create(_filePath, FPSToMilliseconds, _repeats);
+ var layerBuffer = new byte[TotalLayers][];
+ progress.Reset("Optimized layers", TotalLayers);
+
+ var fontFace = FontFace.HersheyDuplex;
+ float fontScale = 1.5f;
+ int fontThickness = 2;
+ MCvScalar textColor = new(200);
- if (_clipByVolumeBounds)
+ if (_clipByVolumeBounds)
+ {
+ ROI = SlicerFile.BoundingRectangle;
+ }
+
+ Parallel.For(0, TotalLayers, CoreSettings.ParallelOptions, i =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ uint layerIndex = (uint) (LayerIndexStart + i * (_skip + 1));
+ var layer = SlicerFile[layerIndex];
+ using var mat = layer.LayerMat;
+ //using var matOriginal = mat.Clone();
+ var matRoi = GetRoiOrDefault(mat);
+
+ if (_scale != 100)
{
- ROI = SlicerFile.BoundingRectangle;
+ CvInvoke.Resize(matRoi, matRoi, new Size((int) (matRoi.Width * ScaleFactor), (int)(matRoi.Height * ScaleFactor)));
}
- Parallel.For(0, TotalLayers, CoreSettings.ParallelOptions, i =>
- {
- if (progress.Token.IsCancellationRequested) return;
- uint layerIndex = (uint) (LayerIndexStart + i * (_skip + 1));
- var layer = SlicerFile[layerIndex];
- using var mat = layer.LayerMat;
- //using var matOriginal = mat.Clone();
- var matRoi = GetRoiOrDefault(mat);
-
- if (_scale != 100)
- {
- CvInvoke.Resize(matRoi, matRoi, new Size((int) (matRoi.Width * ScaleFactor), (int)(matRoi.Height * ScaleFactor)));
- }
-
- if (_flipDirection != Enumerations.FlipDirection.None)
- {
- CvInvoke.Flip(matRoi, matRoi, Enumerations.ToOpenCVFlipType(_flipDirection));
- }
-
- if (_rotateDirection != Enumerations.RotateDirection.None)
- {
- CvInvoke.Rotate(matRoi, matRoi, Enumerations.ToOpenCVRotateFlags(_rotateDirection));
- }
-
- if (_renderLayerCount)
- {
- int baseLine = 0;
- var text = $"{layerIndex.ToString().PadLeft(SlicerFile.LayerCount.ToString().Length, '0')}/{SlicerFile.LayerCount-1}";
- var fontSize = CvInvoke.GetTextSize(text, fontFace, fontScale, fontThickness, ref baseLine);
-
- Point point = new(
- matRoi.Width / 2 - fontSize.Width / 2,
- 70);
- CvInvoke.PutText(matRoi, text, point, fontFace, fontScale, textColor, fontThickness, LineType.AntiAlias);
- }
-
- //ApplyMask(matOriginal, matRoi);
-
- layerBuffer[i] = matRoi.GetPngByes();
-
- progress.LockAndIncrement();
- });
-
- progress.ResetNameAndProcessed("Packed layers");
- foreach (var buffer in layerBuffer)
+ if (_flipDirection != Enumerations.FlipDirection.None)
{
- if (progress.Token.IsCancellationRequested) break;
- using Stream stream = new MemoryStream(buffer);
- using var img = Image.FromStream(stream);
- gif.AddFrame(img, -1, GifQuality.Bit8);
- progress++;
+ CvInvoke.Flip(matRoi, matRoi, Enumerations.ToOpenCVFlipType(_flipDirection));
}
- if (progress.Token.IsCancellationRequested)
+ if (_rotateDirection != Enumerations.RotateDirection.None)
{
- try
- {
- File.Delete(_filePath);
- }
- catch
- {
- // ignored
- }
+ CvInvoke.Rotate(matRoi, matRoi, Enumerations.ToOpenCVRotateFlags(_rotateDirection));
}
- return !progress.Token.IsCancellationRequested;
- }
+ if (_renderLayerCount)
+ {
+ int baseLine = 0;
+ var text = $"{layerIndex.ToString().PadLeft(SlicerFile.LayerCount.ToString().Length, '0')}/{SlicerFile.LayerCount-1}";
+ var fontSize = CvInvoke.GetTextSize(text, fontFace, fontScale, fontThickness, ref baseLine);
+
+ Point point = new(
+ matRoi.Width / 2 - fontSize.Width / 2,
+ 70);
+ CvInvoke.PutText(matRoi, text, point, fontFace, fontScale, textColor, fontThickness, LineType.AntiAlias);
+ }
+ //ApplyMask(matOriginal, matRoi);
- #endregion
+ layerBuffer[i] = matRoi.GetPngByes();
- #region Equality
+ progress.LockAndIncrement();
+ });
- private bool Equals(OperationLayerExportGif other)
+ progress.ResetNameAndProcessed("Packed layers");
+ foreach (var buffer in layerBuffer)
{
- return _clipByVolumeBounds == other._clipByVolumeBounds && _renderLayerCount == other._renderLayerCount && _fps == other._fps && _skip == other._skip && _scale == other._scale && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _repeats == other._repeats;
+ if (progress.Token.IsCancellationRequested) break;
+ using Stream stream = new MemoryStream(buffer);
+ using var img = Image.FromStream(stream);
+ gif.AddFrame(img, -1, GifQuality.Bit8);
+ progress++;
}
- public override bool Equals(object obj)
+ if (progress.Token.IsCancellationRequested)
{
- return ReferenceEquals(this, obj) || obj is OperationLayerExportGif other && Equals(other);
+ try
+ {
+ File.Delete(_filePath);
+ }
+ catch
+ {
+ // ignored
+ }
}
- public override int GetHashCode()
- {
- return HashCode.Combine(_clipByVolumeBounds, _renderLayerCount, _fps, _skip, _scale, (int) _rotateDirection, (int) _flipDirection, _repeats);
- }
+ return !progress.Token.IsCancellationRequested;
+ }
+
+
+ #endregion
+
+ #region Equality
+
+ private bool Equals(OperationLayerExportGif other)
+ {
+ return _clipByVolumeBounds == other._clipByVolumeBounds && _renderLayerCount == other._renderLayerCount && _fps == other._fps && _skip == other._skip && _scale == other._scale && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _repeats == other._repeats;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationLayerExportGif other && Equals(other);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_clipByVolumeBounds, _renderLayerCount, _fps, _skip, _scale, (int) _rotateDirection, (int) _flipDirection, _repeats);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs
index fea1420..de9084c 100644
--- a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs
+++ b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs
@@ -6,217 +6,218 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public sealed class OperationLayerExportHeatMap : Operation
- {
- #region Members
- private string _filePath;
- private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
- private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
- private bool _mergeSamePositionedLayers = true;
- private bool _cropByRoi = true;
+namespace UVtools.Core.Operations;
- #endregion
+[Serializable]
+public sealed class OperationLayerExportHeatMap : Operation
+{
+ #region Members
+ private string _filePath = null!;
+ private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
+ private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
+ private bool _mergeSamePositionedLayers = true;
+ private bool _cropByRoi = true;
- #region Overrides
+ #endregion
- public override bool CanHaveProfiles => false;
- public override string Title => "Export layers to heat map";
+ #region Overrides
- public override string Description =>
- "Export a layer range to a grayscale heat map image that represents the median of the mass in the Z depth/perception.\n" +
- "The pixel brightness/intensity shows where the most mass are concentrated.";
+ public override bool CanHaveProfiles => false;
- public override string ConfirmationText =>
- $"generate a heatmap from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string IconClass => "fas fa-file-image";
+ public override string Title => "Export layers to heat map";
- public override string ProgressTitle =>
- $"Generating a heatmap from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string Description =>
+ "Export a layer range to a grayscale heat map image that represents the median of the mass in the Z depth/perception.\n" +
+ "The pixel brightness/intensity shows where the most mass are concentrated.";
- public override string ProgressAction => "Packed layers";
+ public override string ConfirmationText =>
+ $"generate a heatmap from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ public override string ProgressTitle =>
+ $"Generating a heatmap from layers {LayerIndexStart} through {LayerIndexEnd}";
- if (LayerRangeCount < 2)
- {
- sb.AppendLine("To generate a heat map at least two layers are required.");
- }
+ public override string ProgressAction => "Packed layers";
- return sb.ToString();
- }
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ToString()
+ if (LayerRangeCount < 2)
{
- var result = $"[Crop by ROI: {_cropByRoi}]" +
- LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("To generate a heat map at least two layers are required.");
}
- #endregion
+ return sb.ToString();
+ }
- #region Properties
+ public override string ToString()
+ {
+ var result = $"[Crop by ROI: {_cropByRoi}]" +
+ LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- public string FilePath
- {
- get => _filePath;
- set => RaiseAndSetIfChanged(ref _filePath, value);
- }
+ #endregion
- public Enumerations.RotateDirection RotateDirection
- {
- get => _rotateDirection;
- set => RaiseAndSetIfChanged(ref _rotateDirection, value);
- }
+ #region Properties
- public Enumerations.FlipDirection FlipDirection
- {
- get => _flipDirection;
- set => RaiseAndSetIfChanged(ref _flipDirection, value);
- }
+ public string FilePath
+ {
+ get => _filePath;
+ set => RaiseAndSetIfChanged(ref _filePath, value);
+ }
- public bool MergeSamePositionedLayers
- {
- get => _mergeSamePositionedLayers;
- set => RaiseAndSetIfChanged(ref _mergeSamePositionedLayers, value);
- }
+ public Enumerations.RotateDirection RotateDirection
+ {
+ get => _rotateDirection;
+ set => RaiseAndSetIfChanged(ref _rotateDirection, value);
+ }
- public bool CropByROI
- {
- get => _cropByRoi;
- set => RaiseAndSetIfChanged(ref _cropByRoi, value);
- }
+ public Enumerations.FlipDirection FlipDirection
+ {
+ get => _flipDirection;
+ set => RaiseAndSetIfChanged(ref _flipDirection, value);
+ }
- #endregion
+ public bool MergeSamePositionedLayers
+ {
+ get => _mergeSamePositionedLayers;
+ set => RaiseAndSetIfChanged(ref _mergeSamePositionedLayers, value);
+ }
- #region Constructor
+ public bool CropByROI
+ {
+ get => _cropByRoi;
+ set => RaiseAndSetIfChanged(ref _cropByRoi, value);
+ }
- public OperationLayerExportHeatMap()
- { }
+ #endregion
- public OperationLayerExportHeatMap(FileFormat slicerFile) : base(slicerFile)
- {
- _flipDirection = SlicerFile.DisplayMirror;
- }
+ #region Constructor
- public override void InitWithSlicerFile()
- {
- _filePath = SlicerFile.FileFullPath + ".heatmap.png";
- }
+ public OperationLayerExportHeatMap()
+ { }
- #endregion
+ public OperationLayerExportHeatMap(FileFormat slicerFile) : base(slicerFile)
+ {
+ _flipDirection = SlicerFile.DisplayMirror;
+ }
+
+ public override void InitWithSlicerFile()
+ {
+ _filePath = SlicerFile.FileFullPath + ".heatmap.png";
+ }
- #region Methods
+ #endregion
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- using var sumMat32 = EmguExtensions.InitMat(SlicerFile.Resolution, 1, DepthType.Cv32S);
- var sumMat32Roi = GetRoiOrDefault(sumMat32);
- using var mask = GetMask(sumMat32);
+ #region Methods
- var layerRange = _mergeSamePositionedLayers
- ? SlicerFile.LayerManager.GetDistinctLayersByPositionZ(LayerIndexStart, LayerIndexEnd) .ToArray()
- : GetSelectedLayerRange().ToArray();
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ using var sumMat32 = EmguExtensions.InitMat(SlicerFile.Resolution, 1, DepthType.Cv32S);
+ var sumMat32Roi = GetRoiOrDefault(sumMat32);
+ using var mask = GetMask(sumMat32);
- progress.ItemCount = (uint)layerRange.Length;
+ var layerRange = _mergeSamePositionedLayers
+ ? SlicerFile.GetDistinctLayersByPositionZ(LayerIndexStart, LayerIndexEnd) .ToArray()
+ : GetSelectedLayerRange().ToArray();
+ progress.ItemCount = (uint)layerRange.Length;
- /*Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- using var mat32 = new Mat();
- mat.ConvertTo(mat32, DepthType.Cv32S);
- var mat32Roi = GetRoiOrDefault(mat32);
+ /*Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
- lock (progress.Mutex)
- {
- CvInvoke.Add(sumMat32Roi, mat32Roi, sumMat32Roi, mask);
- progress++;
- }
- });*/
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ using var mat32 = new Mat();
+ mat.ConvertTo(mat32, DepthType.Cv32S);
+ var mat32Roi = GetRoiOrDefault(mat32);
- Parallel.ForEach(layerRange, CoreSettings.ParallelOptions, layer =>
+ lock (progress.Mutex)
{
- if (progress.Token.IsCancellationRequested) return;
+ CvInvoke.Add(sumMat32Roi, mat32Roi, sumMat32Roi, mask);
+ progress++;
+ }
+ });*/
- using var mat = _mergeSamePositionedLayers
- ? SlicerFile.LayerManager.GetMergedMatForSequentialPositionedLayers(layer.Index)
- : layer.LayerMat;
+ Parallel.ForEach(layerRange, CoreSettings.ParallelOptions, layer =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
- using var mat32 = new Mat();
- mat.ConvertTo(mat32, DepthType.Cv32S);
- var mat32Roi = GetRoiOrDefault(mat32);
+ using var mat = _mergeSamePositionedLayers
+ ? SlicerFile.GetMergedMatForSequentialPositionedLayers(layer.Index)
+ : layer.LayerMat;
- lock (progress.Mutex)
- {
- CvInvoke.Add(sumMat32Roi, mat32Roi, sumMat32Roi, mask);
- progress++;
- }
- });
+ using var mat32 = new Mat();
+ mat.ConvertTo(mat32, DepthType.Cv32S);
+ var mat32Roi = GetRoiOrDefault(mat32);
- if (!progress.Token.IsCancellationRequested)
+ lock (progress.Mutex)
{
- using var sumMat = EmguExtensions.InitMat(sumMat32.Size);
- sumMat32.ConvertTo(sumMat, DepthType.Cv8U, 1.0 / layerRange.Length);
-
- if (_flipDirection != Enumerations.FlipDirection.None)
- {
- CvInvoke.Flip(sumMat, sumMat, Enumerations.ToOpenCVFlipType(_flipDirection));
- }
-
- if (_rotateDirection != Enumerations.RotateDirection.None)
- {
- CvInvoke.Rotate(sumMat, sumMat, Enumerations.ToOpenCVRotateFlags(_rotateDirection));
- }
-
- if (_cropByRoi && HaveROI)
- {
- var sumMatRoi = GetRoiOrDefault(sumMat);
- sumMatRoi.Save(_filePath);
- }
- else
- {
- sumMat.Save(_filePath);
- }
+ CvInvoke.Add(sumMat32Roi, mat32Roi, sumMat32Roi, mask);
+ progress++;
}
+ });
- return !progress.Token.IsCancellationRequested;
- }
+ if (!progress.Token.IsCancellationRequested)
+ {
+ using var sumMat = EmguExtensions.InitMat(sumMat32.Size);
+ sumMat32.ConvertTo(sumMat, DepthType.Cv8U, 1.0 / layerRange.Length);
- #endregion
+ if (_flipDirection != Enumerations.FlipDirection.None)
+ {
+ CvInvoke.Flip(sumMat, sumMat, Enumerations.ToOpenCVFlipType(_flipDirection));
+ }
- #region Equality
+ if (_rotateDirection != Enumerations.RotateDirection.None)
+ {
+ CvInvoke.Rotate(sumMat, sumMat, Enumerations.ToOpenCVRotateFlags(_rotateDirection));
+ }
- private bool Equals(OperationLayerExportHeatMap other)
- {
- return _filePath == other._filePath && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _mergeSamePositionedLayers == other._mergeSamePositionedLayers && _cropByRoi == other._cropByRoi;
+ if (_cropByRoi && HaveROI)
+ {
+ var sumMatRoi = GetRoiOrDefault(sumMat);
+ sumMatRoi.Save(_filePath);
+ }
+ else
+ {
+ sumMat.Save(_filePath);
+ }
}
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationLayerExportHeatMap other && Equals(other);
- }
+ return !progress.Token.IsCancellationRequested;
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_filePath, (int)_rotateDirection, (int)_flipDirection, _mergeSamePositionedLayers, _cropByRoi);
- }
+ #endregion
- #endregion
+ #region Equality
+
+ private bool Equals(OperationLayerExportHeatMap other)
+ {
+ return _filePath == other._filePath && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _mergeSamePositionedLayers == other._mergeSamePositionedLayers && _cropByRoi == other._cropByRoi;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationLayerExportHeatMap other && Equals(other);
}
-}
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_filePath, (int)_rotateDirection, (int)_flipDirection, _mergeSamePositionedLayers, _cropByRoi);
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs
index fa06e97..c2c353f 100644
--- a/UVtools.Core/Operations/OperationLayerExportImage.cs
+++ b/UVtools.Core/Operations/OperationLayerExportImage.cs
@@ -6,339 +6,339 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Util;
using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationLayerExportImage : Operation
{
- [Serializable]
- public sealed class OperationLayerExportImage : Operation
+ #region Enums
+
+ public enum LayerExportImageTypes : byte
{
- #region Enums
+ [Description("PNG: Portable Network Graphics")]
+ PNG,
+ [Description("JPG: Joint Photographic Experts Group")]
+ JPG,
+ [Description("JPEG: Joint Photographic Experts Group")]
+ JPEG,
+ [Description("JP2: Joint Photographic Experts Group (JPEG 2000)")]
+ JP2,
+ [Description("TIF: Tag Image File Format")]
+ TIF,
+ [Description("TIFF: Tag Image File Format")]
+ TIFF,
+ [Description("BMP: Bitmap")]
+ BMP,
+ [Description("PBM: Portable Bitmap")]
+ PBM,
+ [Description("PGM: Portable Greymap")]
+ PGM,
+ //[Description("PPM: Portable Pixmap")]
+ //PPM,
+ [Description("SR: Sun raster")]
+ SR,
+ [Description("RAS: Sun raster")]
+ RAS,
+ [Description("SVG: Scalable Vector Graphics")]
+ SVG
+ }
+ #endregion
- public enum LayerExportImageTypes : byte
- {
- [Description("PNG: Portable Network Graphics")]
- PNG,
- [Description("JPG: Joint Photographic Experts Group")]
- JPG,
- [Description("JPEG: Joint Photographic Experts Group")]
- JPEG,
- [Description("JP2: Joint Photographic Experts Group (JPEG 2000)")]
- JP2,
- [Description("TIF: Tag Image File Format")]
- TIF,
- [Description("TIFF: Tag Image File Format")]
- TIFF,
- [Description("BMP: Bitmap")]
- BMP,
- [Description("PBM: Portable Bitmap")]
- PBM,
- [Description("PGM: Portable Greymap")]
- PGM,
- //[Description("PPM: Portable Pixmap")]
- //PPM,
- [Description("SR: Sun raster")]
- SR,
- [Description("RAS: Sun raster")]
- RAS,
- [Description("SVG: Scalable Vector Graphics")]
- SVG
- }
- #endregion
+ #region Members
- #region Members
+ private string _outputFolder = null!;
+ private string _filename = "layer";
+ private LayerExportImageTypes _imageType = LayerExportImageTypes.PNG;
+ private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
+ private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
+ private bool _padLayerIndex = true;
+ private bool _cropByRoi = true;
- private string _outputFolder;
- private string _filename = "layer";
- private LayerExportImageTypes _imageType = LayerExportImageTypes.PNG;
- private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
- private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
- private bool _padLayerIndex = true;
- private bool _cropByRoi = true;
+ #endregion
- #endregion
+ #region Overrides
- #region Overrides
+ public override bool CanHaveProfiles => false;
- public override bool CanHaveProfiles => false;
- public override string Title => "Export layers to images";
+ public override string IconClass => "fas fa-file-image";
+ public override string Title => "Export layers to images";
- public override string Description =>
- "Export a layer range to images.";
+ public override string Description =>
+ "Export a layer range to images.";
- public override string ConfirmationText =>
- $"export {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ConfirmationText =>
+ $"export {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressTitle =>
- $"Exporting {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Exporting {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => $"Exported {_imageType} images";
+ public override string ProgressAction => $"Exported {_imageType} images";
+
+ /*public override string ValidateInternally()
+ {
+ var sb = new StringBuilder();
- /*public override string ValidateInternally()
+ if (LayerRangeCount < 2)
{
- var sb = new StringBuilder();
+ sb.AppendLine("To generate a heat map at least two layers are required.");
+ }
- if (LayerRangeCount < 2)
- {
- sb.AppendLine("To generate a heat map at least two layers are required.");
- }
+ return sb.ToString();
+ }*/
- return sb.ToString();
- }*/
+ public override string ToString()
+ {
+ var result = $"[Crop by ROI: {_cropByRoi}] [Pad index: {_padLayerIndex}]" +
+ LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- public override string ToString()
- {
- var result = $"[Crop by ROI: {_cropByRoi}] [Pad index: {_padLayerIndex}]" +
- LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ #endregion
- #endregion
+ #region Properties
- #region Properties
+ public string OutputFolder
+ {
+ get => _outputFolder;
+ set => RaiseAndSetIfChanged(ref _outputFolder, value);
+ }
- public string OutputFolder
- {
- get => _outputFolder;
- set => RaiseAndSetIfChanged(ref _outputFolder, value);
- }
+ public string Filename
+ {
+ get => _filename;
+ set => RaiseAndSetIfChanged(ref _filename, value);
+ }
- public string Filename
- {
- get => _filename;
- set => RaiseAndSetIfChanged(ref _filename, value);
- }
+ public LayerExportImageTypes ImageType
+ {
+ get => _imageType;
+ set => RaiseAndSetIfChanged(ref _imageType, value);
+ }
- public LayerExportImageTypes ImageType
- {
- get => _imageType;
- set => RaiseAndSetIfChanged(ref _imageType, value);
- }
+ public Enumerations.RotateDirection RotateDirection
+ {
+ get => _rotateDirection;
+ set => RaiseAndSetIfChanged(ref _rotateDirection, value);
+ }
- public Enumerations.RotateDirection RotateDirection
- {
- get => _rotateDirection;
- set => RaiseAndSetIfChanged(ref _rotateDirection, value);
- }
+ public Enumerations.FlipDirection FlipDirection
+ {
+ get => _flipDirection;
+ set => RaiseAndSetIfChanged(ref _flipDirection, value);
+ }
- public Enumerations.FlipDirection FlipDirection
- {
- get => _flipDirection;
- set => RaiseAndSetIfChanged(ref _flipDirection, value);
- }
+ public bool PadLayerIndex
+ {
+ get => _padLayerIndex;
+ set => RaiseAndSetIfChanged(ref _padLayerIndex, value);
+ }
- public bool PadLayerIndex
- {
- get => _padLayerIndex;
- set => RaiseAndSetIfChanged(ref _padLayerIndex, value);
- }
+ public bool CropByROI
+ {
+ get => _cropByRoi;
+ set => RaiseAndSetIfChanged(ref _cropByRoi, value);
+ }
- public bool CropByROI
- {
- get => _cropByRoi;
- set => RaiseAndSetIfChanged(ref _cropByRoi, value);
- }
+ #endregion
- #endregion
+ #region Constructor
- #region Constructor
+ public OperationLayerExportImage()
+ { }
- public OperationLayerExportImage()
- { }
+ public OperationLayerExportImage(FileFormat slicerFile) : base(slicerFile)
+ {
+ _flipDirection = SlicerFile.DisplayMirror;
+ }
- public OperationLayerExportImage(FileFormat slicerFile) : base(slicerFile)
- {
- _flipDirection = SlicerFile.DisplayMirror;
- }
+ public override void InitWithSlicerFile()
+ {
+ _outputFolder = Path.Combine(Path.GetDirectoryName(SlicerFile.FileFullPath) ?? string.Empty, FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath) ?? string.Empty);
+ }
- public override void InitWithSlicerFile()
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (!Directory.Exists(_outputFolder))
{
- _outputFolder = Path.Combine(Path.GetDirectoryName(SlicerFile.FileFullPath) ?? string.Empty, FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath));
+ Directory.CreateDirectory(_outputFolder);
}
- #endregion
+ var slicedFileNameNoExt = SlicerFile.FilenameNoExt;
- #region Methods
-
- protected override bool ExecuteInternally(OperationProgress progress)
+ Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
{
- if (!Directory.Exists(_outputFolder))
+ if (progress.Token.IsCancellationRequested) return;
+
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ var matRoi = mat;
+ if (_cropByRoi && HaveROI)
{
- Directory.CreateDirectory(_outputFolder);
+ matRoi = GetRoiOrDefault(mat);
}
- var slicedFileNameNoExt = SlicerFile.FilenameNoExt;
-
- Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
+ if (_flipDirection != Enumerations.FlipDirection.None)
{
- if (progress.Token.IsCancellationRequested) return;
-
- using var mat = SlicerFile[layerIndex].LayerMat;
- var matRoi = mat;
- if (_cropByRoi && HaveROI)
- {
- matRoi = GetRoiOrDefault(mat);
- }
-
- if (_flipDirection != Enumerations.FlipDirection.None)
- {
- CvInvoke.Flip(matRoi, matRoi, Enumerations.ToOpenCVFlipType(_flipDirection));
- }
+ CvInvoke.Flip(matRoi, matRoi, Enumerations.ToOpenCVFlipType(_flipDirection));
+ }
- if (_rotateDirection != Enumerations.RotateDirection.None)
- {
- CvInvoke.Rotate(matRoi, matRoi, Enumerations.ToOpenCVRotateFlags(_rotateDirection));
- }
+ if (_rotateDirection != Enumerations.RotateDirection.None)
+ {
+ CvInvoke.Rotate(matRoi, matRoi, Enumerations.ToOpenCVRotateFlags(_rotateDirection));
+ }
- var filename = SlicerFile[layerIndex].FormatFileName(_filename, _padLayerIndex ? SlicerFile.LayerManager.LayerDigits : byte.MinValue, true, string.Empty);
- var fileFullPath = Path.Combine(_outputFolder, $"{filename}.{_imageType.ToString().ToLower()}");
+ var filename = SlicerFile[layerIndex].FormatFileName(_filename, _padLayerIndex ? SlicerFile.LayerDigits : byte.MinValue, true, string.Empty);
+ var fileFullPath = Path.Combine(_outputFolder, $"{filename}.{_imageType.ToString().ToLower()}");
- if (_imageType != LayerExportImageTypes.SVG)
- {
- matRoi.Save(fileFullPath);
- }
- else
- {
- // SVG
+ if (_imageType != LayerExportImageTypes.SVG)
+ {
+ matRoi.Save(fileFullPath);
+ }
+ else
+ {
+ // SVG
- CvInvoke.Threshold(matRoi, matRoi, 127, byte.MaxValue, ThresholdType.Binary); // Remove AA
+ CvInvoke.Threshold(matRoi, matRoi, 127, byte.MaxValue, ThresholdType.Binary); // Remove AA
- using var contours = matRoi.FindContours(out var hierarchy, RetrType.Tree);
+ using var contours = matRoi.FindContours(out var hierarchy, RetrType.Tree);
- using TextWriter tw = new StreamWriter(fileFullPath);
- tw.WriteLine("<!--");
- tw.WriteLine($"# Generated by {About.Software} v{About.VersionStr} {About.Arch} @ {DateTime.UtcNow} #");
- tw.WriteLine($"File: {SlicerFile.Filename}");
- tw.WriteLine($"{SlicerFile[layerIndex].ToString().Replace(", ", "\n")}");
- tw.WriteLine("-->");
- tw.WriteLine("<svg " +
- "xmlns=\"http://www.w3.org/2000/svg\" " +
- //"xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +
- //"version=\"1.1\" " +
- $"id=\"{slicedFileNameNoExt}_{filename}\" " +
- $"data-name=\"{slicedFileNameNoExt}_{filename}\" " +
- //"x=\"0\" " +
- //"y=\"0\" " +
- $"width=\"{matRoi.Width}\" " +
- $"height=\"{matRoi.Height}\" " +
- $"viewBox=\"0 0 {matRoi.Width} {matRoi.Height}\">");
- tw.WriteLine("\t<defs>");
- tw.WriteLine("\t\t<style>");
- //tw.WriteLine("\t\tsvg { background-color: #000000; }");
- tw.WriteLine("\t\t.background { fill: #000000; }");
- //tw.WriteLine("\t\t.black { fill: #000000; fill-rule: evenodd; }");
- //tw.WriteLine("\t\t.white { fill: #FFFFFF; fill-rule: evenodd; }");
- tw.WriteLine("\t\tpath { fill: #FFFFFF; fill-rule: evenodd; }");
- tw.WriteLine("\t\t</style>");
- tw.WriteLine("\t</defs>");
- tw.WriteLine($"\t<title>{slicedFileNameNoExt} #{layerIndex}</title>");
-
- tw.WriteLine($"\t<g id=\"layer{layerIndex}\">");
- tw.WriteLine($"\t<rect class=\"background\" width=\"{mat.Width}\" height=\"{mat.Height}\"/>");
+ using TextWriter tw = new StreamWriter(fileFullPath);
+ tw.WriteLine("<!--");
+ tw.WriteLine($"# Generated by {About.Software} v{About.VersionStr} {About.Arch} @ {DateTime.UtcNow} #");
+ tw.WriteLine($"File: {SlicerFile.Filename}");
+ tw.WriteLine($"{SlicerFile[layerIndex].ToString().Replace(", ", "\n")}");
+ tw.WriteLine("-->");
+ tw.WriteLine("<svg " +
+ "xmlns=\"http://www.w3.org/2000/svg\" " +
+ //"xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +
+ //"version=\"1.1\" " +
+ $"id=\"{slicedFileNameNoExt}_{filename}\" " +
+ $"data-name=\"{slicedFileNameNoExt}_{filename}\" " +
+ //"x=\"0\" " +
+ //"y=\"0\" " +
+ $"width=\"{matRoi.Width}\" " +
+ $"height=\"{matRoi.Height}\" " +
+ $"viewBox=\"0 0 {matRoi.Width} {matRoi.Height}\">");
+ tw.WriteLine("\t<defs>");
+ tw.WriteLine("\t\t<style>");
+ //tw.WriteLine("\t\tsvg { background-color: #000000; }");
+ tw.WriteLine("\t\t.background { fill: #000000; }");
+ //tw.WriteLine("\t\t.black { fill: #000000; fill-rule: evenodd; }");
+ //tw.WriteLine("\t\t.white { fill: #FFFFFF; fill-rule: evenodd; }");
+ tw.WriteLine("\t\tpath { fill: #FFFFFF; fill-rule: evenodd; }");
+ tw.WriteLine("\t\t</style>");
+ tw.WriteLine("\t</defs>");
+ tw.WriteLine($"\t<title>{slicedFileNameNoExt} #{layerIndex}</title>");
+
+ tw.WriteLine($"\t<g id=\"layer{layerIndex}\">");
+ tw.WriteLine($"\t<rect class=\"background\" width=\"{mat.Width}\" height=\"{mat.Height}\"/>");
- //
- //hierarchy[i][0]: the index of the next contour of the same level
- //hierarchy[i][1]: the index of the previous contour of the same level
- //hierarchy[i][2]: the index of the first child
- //hierarchy[i][3]: the index of the parent
- //
+ //
+ //hierarchy[i][0]: the index of the next contour of the same level
+ //hierarchy[i][1]: the index of the previous contour of the same level
+ //hierarchy[i][2]: the index of the first child
+ //hierarchy[i][3]: the index of the parent
+ //
- bool firstTime = true;
- for (int i = 0; i < contours.Size; i++)
+ bool firstTime = true;
+ for (int i = 0; i < contours.Size; i++)
+ {
+ if (hierarchy[i, EmguContour.HierarchyParent] == -1) // Top hierarchy
{
- if (hierarchy[i, EmguContour.HierarchyParent] == -1) // Top hierarchy
+ if (firstTime)
{
- if (firstTime)
- {
- firstTime = false;
- }
- else
- {
- tw.WriteLine("\"/>");
- }
-
- tw.Write("\t<path d=\"");
+ firstTime = false;
}
else
{
- tw.Write(" ");
+ tw.WriteLine("\"/>");
}
- tw.Write($"M {contours[i][0].X} {contours[i][0].Y} L");
- for (int x = 1; x < contours[i].Size; x++)
- {
- tw.Write($" {contours[i][x].X} {contours[i][x].Y}");
- }
- tw.Write(" Z");
+ tw.Write("\t<path d=\"");
+ }
+ else
+ {
+ tw.Write(" ");
}
- if(!firstTime) tw.WriteLine("\"/>");
-
- // Old method!
- /*for (int i = 0; i < contours.Size; i++)
+ tw.Write($"M {contours[i][0].X} {contours[i][0].Y} L");
+ for (int x = 1; x < contours[i].Size; x++)
{
- if (contours[i].Size == 0) continue;
+ tw.Write($" {contours[i][x].X} {contours[i][x].Y}");
+ }
+ tw.Write(" Z");
+ }
- var style = "white";
+ if(!firstTime) tw.WriteLine("\"/>");
- int parentIndex = i;
- int count = 0;
- while ((parentIndex = (int)hierarchyJagged.GetValue(0, parentIndex, 3)) != -1)
- {
- count++;
- }
+ // Old method!
+ /*for (int i = 0; i < contours.Size; i++)
+ {
+ if (contours[i].Size == 0) continue;
- if (count % 2 != 0)
- style = "black";
+ var style = "white";
- tw.Write($"\t<path class=\"{style}\" d=\"M{contours[i][0].X} {contours[i][0].Y}");
- for (int x = 1; x < contours[i].Size; x++)
- {
- tw.Write($",L{contours[i][x].X} {contours[i][x].Y}");
- }
- tw.WriteLine("Z\"/>");
- }*/
+ int parentIndex = i;
+ int count = 0;
+ while ((parentIndex = (int)hierarchyJagged.GetValue(0, parentIndex, 3)) != -1)
+ {
+ count++;
+ }
- tw.WriteLine("\t</g>");
- tw.WriteLine("</svg>");
- }
-
- progress.LockAndIncrement();
- });
+ if (count % 2 != 0)
+ style = "black";
- return !progress.Token.IsCancellationRequested;
- }
+ tw.Write($"\t<path class=\"{style}\" d=\"M{contours[i][0].X} {contours[i][0].Y}");
+ for (int x = 1; x < contours[i].Size; x++)
+ {
+ tw.Write($",L{contours[i][x].X} {contours[i][x].Y}");
+ }
+ tw.WriteLine("Z\"/>");
+ }*/
- #endregion
+ tw.WriteLine("\t</g>");
+ tw.WriteLine("</svg>");
+ }
+
+ progress.LockAndIncrement();
+ });
- #region Equality
+ return !progress.Token.IsCancellationRequested;
+ }
- private bool Equals(OperationLayerExportImage other)
- {
- return _outputFolder == other._outputFolder && _filename == other._filename && _imageType == other._imageType && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _padLayerIndex == other._padLayerIndex && _cropByRoi == other._cropByRoi;
- }
+ #endregion
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationLayerExportImage other && Equals(other);
- }
+ #region Equality
- public override int GetHashCode()
- {
- return HashCode.Combine(_outputFolder, _filename, (int) _imageType, (int) _rotateDirection, (int) _flipDirection, _padLayerIndex, _cropByRoi);
- }
+ private bool Equals(OperationLayerExportImage other)
+ {
+ return _outputFolder == other._outputFolder && _filename == other._filename && _imageType == other._imageType && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _padLayerIndex == other._padLayerIndex && _cropByRoi == other._cropByRoi;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationLayerExportImage other && Equals(other);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_outputFolder, _filename, (int) _imageType, (int) _rotateDirection, (int) _flipDirection, _padLayerIndex, _cropByRoi);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerExportMesh.cs b/UVtools.Core/Operations/OperationLayerExportMesh.cs
index 1f8c730..5accdaa 100644
--- a/UVtools.Core/Operations/OperationLayerExportMesh.cs
+++ b/UVtools.Core/Operations/OperationLayerExportMesh.cs
@@ -6,6 +6,10 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using KdTree;
+using KdTree.Math;
using System;
using System.Collections.Generic;
using System.Drawing;
@@ -13,195 +17,239 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using KdTree;
-using KdTree.Math;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Managers;
using UVtools.Core.MeshFormats;
using UVtools.Core.Voxel;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationLayerExportMesh : Operation
{
- [Serializable]
- public sealed class OperationLayerExportMesh : Operation
+ #region Enums
+ public enum ExportMeshQuality : byte
{
- #region Enums
- public enum ExportMeshQuality : byte
- {
- Accurate = 1,
- Average = 2,
- Quick = 3,
+ Accurate = 1,
+ Average = 2,
+ Quick = 3,
- Dirty = 6,
- Minecraft = 8
- }
+ Dirty = 6,
+ Minecraft = 8
+ }
- #endregion
+ #endregion
- #region Members
- private string _filePath;
- private MeshFile.MeshFileFormat _meshFileFormat = MeshFile.MeshFileFormat.BINARY;
- private ExportMeshQuality _quality = ExportMeshQuality.Accurate;
- private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
- private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
- private bool _stripAntiAliasing = true;
+ #region Members
+ private string _filePath = null!;
+ private MeshFile.MeshFileFormat _meshFileFormat = MeshFile.MeshFileFormat.BINARY;
+ private ExportMeshQuality _quality = ExportMeshQuality.Accurate;
+ private Enumerations.RotateDirection _rotateDirection = Enumerations.RotateDirection.None;
+ private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.None;
+ private bool _stripAntiAliasing = true;
- #endregion
+ #endregion
- #region Overrides
+ #region Overrides
- public override bool CanHaveProfiles => false;
- public override string Title => "Export layers to mesh";
+ public override bool CanHaveProfiles => false;
- public override string Description =>
- "Reconstructs and export a layer range to a 3D mesh via voxelization.\n" +
- "Note: Depending on quality and triangle count, this will often render heavy files.\n" +
- "This process will not recover your original 3D model as data was already lost when sliced.";
+ public override string IconClass => "fas fa-cubes";
+ public override string Title => "Export layers to mesh";
- public override string ConfirmationText =>
- $"generate a mesh from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string Description =>
+ "Reconstructs and export a layer range to a 3D mesh via voxelization.\n" +
+ "Note: Depending on quality and triangle count, this will often render heavy files.\n" +
+ "This process will not recover your original 3D model as data was already lost when sliced.";
- public override string ProgressTitle =>
- $"Generating a mesh from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ConfirmationText =>
+ $"generate a mesh from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressAction => "Packed layers";
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ public override string ProgressTitle =>
+ $"Generating a mesh from layers {LayerIndexStart} through {LayerIndexEnd}";
- if (MeshFile.FindFileExtension(_filePath) is null)
- {
- sb.AppendLine("The used file extension is invalid.");
- }
-
- return sb.ToString();
- }
+ public override string ProgressAction => "Packed layers";
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- /*public override string ToString()
+ if (MeshFile.FindFileExtension(_filePath) is null)
{
- var result = $"[Crop by ROI: {_cropByRoi}]" +
- LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }*/
+ sb.AppendLine("The used file extension is invalid.");
+ }
+
+ return sb.ToString();
+ }
- #endregion
+ /*public override string ToString()
+ {
+ var result = $"[Crop by ROI: {_cropByRoi}]" +
+ LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }*/
- #region Properties
+ #endregion
- public string FilePath
- {
- get => _filePath;
- set => RaiseAndSetIfChanged(ref _filePath, value);
- }
+ #region Properties
- public MeshFile.MeshFileFormat MeshFileFormat
- {
- get => _meshFileFormat;
- set => RaiseAndSetIfChanged(ref _meshFileFormat, value);
- }
+ public string FilePath
+ {
+ get => _filePath;
+ set => RaiseAndSetIfChanged(ref _filePath, value);
+ }
- public ExportMeshQuality Quality
- {
- get => _quality;
- set => RaiseAndSetIfChanged(ref _quality, value);
- }
+ public MeshFile.MeshFileFormat MeshFileFormat
+ {
+ get => _meshFileFormat;
+ set => RaiseAndSetIfChanged(ref _meshFileFormat, value);
+ }
- public Enumerations.RotateDirection RotateDirection
- {
- get => _rotateDirection;
- set => RaiseAndSetIfChanged(ref _rotateDirection, value);
- }
+ public ExportMeshQuality Quality
+ {
+ get => _quality;
+ set => RaiseAndSetIfChanged(ref _quality, value);
+ }
- public Enumerations.FlipDirection FlipDirection
- {
- get => _flipDirection;
- set => RaiseAndSetIfChanged(ref _flipDirection, value);
- }
+ public Enumerations.RotateDirection RotateDirection
+ {
+ get => _rotateDirection;
+ set => RaiseAndSetIfChanged(ref _rotateDirection, value);
+ }
- public bool StripAntiAliasing
- {
- get => _stripAntiAliasing;
- set => RaiseAndSetIfChanged(ref _stripAntiAliasing, value);
- }
+ public Enumerations.FlipDirection FlipDirection
+ {
+ get => _flipDirection;
+ set => RaiseAndSetIfChanged(ref _flipDirection, value);
+ }
- #endregion
+ public bool StripAntiAliasing
+ {
+ get => _stripAntiAliasing;
+ set => RaiseAndSetIfChanged(ref _stripAntiAliasing, value);
+ }
- #region Constructor
+ #endregion
- public OperationLayerExportMesh() { }
+ #region Constructor
- public OperationLayerExportMesh(FileFormat slicerFile) : base(slicerFile)
- {
- _flipDirection = SlicerFile.DisplayMirror;
- }
+ public OperationLayerExportMesh() { }
- public override void InitWithSlicerFile()
- {
- _filePath = Path.Combine(Path.GetDirectoryName(SlicerFile.FileFullPath), $"{SlicerFile.FilenameNoExt}.{STLMeshFile.FileExtension.Extension}");
- }
+ public OperationLayerExportMesh(FileFormat slicerFile) : base(slicerFile)
+ {
+ _flipDirection = SlicerFile.DisplayMirror;
+ }
- #endregion
+ public override void InitWithSlicerFile()
+ {
+ _filePath = Path.Combine(Path.GetDirectoryName(SlicerFile.FileFullPath) ?? string.Empty, $"{SlicerFile.FilenameNoExt}.{STLMeshFile.FileExtension.Extension}");
+ }
- #region Methods
+ #endregion
- protected override unsafe bool ExecuteInternally(OperationProgress progress)
- {
- var fileExtension = MeshFile.FindFileExtension(_filePath);
- if (fileExtension is null) return false;
+ #region Methods
- //using var meshFile = fileExtension.FileFormatType.CreateInstance<MeshFile>(_filePath, FileMode.Create);
- //new Voxelizer().CreateVoxelMesh(fileExtension.FileFormatType, SlicerFile, _filePath, progress);
+ protected override unsafe bool ExecuteInternally(OperationProgress progress)
+ {
+ var fileExtension = MeshFile.FindFileExtension(_filePath);
+ if (fileExtension is null) return false;
+
+ //using var meshFile = fileExtension.FileFormatType.CreateInstance<MeshFile>(_filePath, FileMode.Create);
+ //new Voxelizer().CreateVoxelMesh(fileExtension.FileFormatType, SlicerFile, _filePath, progress);
- /* Voxelization has 4 overall stages
- * 1.) Generate all visible faces, this is for each pixel we determine which of its faces are visible from outside the model
- * 2.) Collapse faces horizontally, this combines faces that are coplanar horizontally into a longer face, this reduces triangles
- * 3.) Collapse faces that are coplanar and the same size vertically leveraging KD Trees for fast lookups, O(logn) vs O(n) for a normal list
- * 4.) Generate triangles for faces and write out to file
- */
+ /* Voxelization has 4 overall stages
+ * 1.) Generate all visible faces, this is for each pixel we determine which of its faces are visible from outside the model
+ * 2.) Collapse faces horizontally, this combines faces that are coplanar horizontally into a longer face, this reduces triangles
+ * 3.) Collapse faces that are coplanar and the same size vertically leveraging KD Trees for fast lookups, O(logn) vs O(n) for a normal list
+ * 4.) Generate triangles for faces and write out to file
+ */
- /* Basic information for the file, how many layers, how big should each voxel be) */
- var pixelSize = SlicerFile.PixelSize;
- float xWidth = (pixelSize.Width > 0 ? pixelSize.Width : 0.035f) * (byte)_quality;
- float yWidth = (pixelSize.Height > 0 ? pixelSize.Height : 0.035f) * (byte)_quality;
+ /* Basic information for the file, how many layers, how big should each voxel be) */
+ var pixelSize = SlicerFile.PixelSize;
+ float xWidth = (pixelSize.Width > 0 ? pixelSize.Width : 0.035f) * (byte)_quality;
+ float yWidth = (pixelSize.Height > 0 ? pixelSize.Height : 0.035f) * (byte)_quality;
- //var totalLayerCount = SlicerFile.LayerCount;
- var distinctLayers = SlicerFile.LayerManager.GetDistinctLayersByPositionZ(LayerIndexStart, LayerIndexEnd).ToArray();
+ //var totalLayerCount = SlicerFile.LayerCount;
+ var distinctLayers = SlicerFile.GetDistinctLayersByPositionZ(LayerIndexStart, LayerIndexEnd).ToArray();
- /* work around the mirror effect, this is caused by the voxel algorithm assuming 0,0 is bottom left, when 0,0 is top left for a Mat
- * ideally we would fix the algorithm itself but that's more invovled. for the time being we'll just flip it verticaly. */
- var workAroundFlip = _flipDirection switch
+ /* work around the mirror effect, this is caused by the voxel algorithm assuming 0,0 is bottom left, when 0,0 is top left for a Mat
+ * ideally we would fix the algorithm itself but that's more invovled. for the time being we'll just flip it verticaly. */
+ var workAroundFlip = _flipDirection switch
+ {
+ Enumerations.FlipDirection.None => Enumerations.FlipDirection.Vertically,
+ Enumerations.FlipDirection.Horizontally => Enumerations.FlipDirection.Both,
+ Enumerations.FlipDirection.Vertically => Enumerations.FlipDirection.None,
+ Enumerations.FlipDirection.Both => Enumerations.FlipDirection.Horizontally,
+ _ => throw new NotImplementedException($"Flip type: {_flipDirection} not handled!")
+ };
+
+ using var cacheManager = new MatCacheManager(this)
+ {
+ AutoDispose = true,
+ AutoDisposeKeepLast = 1,
+ Rotate = _rotateDirection,
+ Flip = workAroundFlip,
+ StripAntiAliasing = _stripAntiAliasing
+ };
+
+
+ /* For the 1st stage, we maintain up to 3 mats, the current layer, the one below us, and the one above us
+ * (below will be null when current layer is 0, above will be null when currentlayer is layercount-1) */
+ /* We init the aboveLayer to the first layer, in the loop coming up we shift above->current->below, so this effectively inits current layer */
+ Mat? aboveLayer;
+ using (var mat = SlicerFile.GetMergedMatForSequentialPositionedLayers(distinctLayers[0].Index, cacheManager))
+ {
+ var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
+
+ if ((byte)_quality > 1)
{
- Enumerations.FlipDirection.None => Enumerations.FlipDirection.Vertically,
- Enumerations.FlipDirection.Horizontally => Enumerations.FlipDirection.Both,
- Enumerations.FlipDirection.Vertically => Enumerations.FlipDirection.None,
- Enumerations.FlipDirection.Both => Enumerations.FlipDirection.Horizontally,
- _ => throw new NotImplementedException($"Flip type: {_flipDirection} not handled!")
- };
-
- using var cacheManager = new MatCacheManager(this)
+ aboveLayer = new Mat();
+ CvInvoke.Resize(matRoi, aboveLayer, Size.Empty, 1.0 / (int)_quality, 1.0 / (int)_quality, Inter.Area);
+ }
+ else
{
- AutoDispose = true,
- AutoDisposeKeepLast = 1,
- Rotate = _rotateDirection,
- Flip = workAroundFlip,
- StripAntiAliasing = _stripAntiAliasing
- };
-
-
- /* For the 1st stage, we maintain up to 3 mats, the current layer, the one below us, and the one above us
- * (below will be null when current layer is 0, above will be null when currentlayer is layercount-1) */
- /* We init the aboveLayer to the first layer, in the loop coming up we shift above->current->below, so this effectively inits current layer */
- Mat aboveLayer = null;
- using (var mat = SlicerFile.LayerManager.GetMergedMatForSequentialPositionedLayers(distinctLayers[0].Index, cacheManager))
+ aboveLayer = matRoi.Clone(); /* clone and then dispose of the ROI mat, not efficient but keeps the GetPixelPos working and clean */
+ }
+ }
+
+ Mat? curLayer = null;
+ Mat? belowLayer;
+
+ /* List of faces to process, great for debugging if you are haveing issues with a face of particular orientation. */
+ var facesToCheck = new[] { Voxelizer.FaceOrientation.Front, Voxelizer.FaceOrientation.Back, Voxelizer.FaceOrientation.Left, Voxelizer.FaceOrientation.Right, Voxelizer.FaceOrientation.Top, Voxelizer.FaceOrientation.Bottom };
+
+ /* Init of other objects that will be used in subsequent stages */
+ var rootFaces = new Voxelizer.UVFace?[distinctLayers.Length];
+ var layerFaceCounts = new uint[distinctLayers.Length];
+ var layerTrees = new KdTree<float, Voxelizer.UVFace>[distinctLayers.Length];
+
+ progress.Reset("layers", (uint)distinctLayers.Length);
+ progress.Title = "Stage 1: Generating faces from layers";
+ //progress.ItemCount = LayerRangeCount;
+
+ /* Begin Stage 1, identifying all faces that are visible from outside the model */
+ for (uint layerIndex = 0; layerIndex < distinctLayers.Length; layerIndex++)
+ {
+ Voxelizer.UVFace? currentFaceItem = null;
+
+ /* Should contain a list of all found faces on this layer, keyed by the face orientation */
+ var foundFaces = new Dictionary<Voxelizer.FaceOrientation, List<Point>>();
+
+ /* move current layer to below */
+ belowLayer = curLayer;
+
+ /* move above layer to us */
+ curLayer = aboveLayer;
+
+ /* bring in a new aboveLayer if we need to */
+ if (layerIndex < distinctLayers.Length - 1)
{
+ using var mat = SlicerFile.GetMergedMatForSequentialPositionedLayers(distinctLayers[(int)layerIndex+1].Index, cacheManager);
var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
if ((byte)_quality > 1)
@@ -211,155 +259,87 @@ namespace UVtools.Core.Operations
}
else
{
- aboveLayer = matRoi.Clone(); /* clone and then dispose of the ROI mat, not efficient but keeps the GetPixelPos working and clean */
+ aboveLayer = matRoi.Clone();
}
- }
-
- Mat curLayer = null;
- Mat belowLayer = null;
-
- /* List of faces to process, great for debugging if you are haveing issues with a face of particular orientation. */
- var facesToCheck = new[] { Voxelizer.FaceOrientation.Front, Voxelizer.FaceOrientation.Back, Voxelizer.FaceOrientation.Left, Voxelizer.FaceOrientation.Right, Voxelizer.FaceOrientation.Top, Voxelizer.FaceOrientation.Bottom };
- /* Init of other objects that will be used in subsequent stages */
- var rootFaces = new Voxelizer.UVFace[distinctLayers.Length];
- var layerFaceCounts = new uint[distinctLayers.Length];
- var layerTrees = new KdTree<float, Voxelizer.UVFace>[distinctLayers.Length];
-
- progress.Reset("layers", (uint)distinctLayers.Length);
- progress.Title = "Stage 1: Generating faces from layers";
- //progress.ItemCount = LayerRangeCount;
-
- /* Begin Stage 1, identifying all faces that are visible from outside the model */
- for (uint layerIndex = 0; layerIndex < distinctLayers.Length; layerIndex++)
+ //CvInvoke.Threshold(aboveLayer, aboveLayer, 1, 255, ThresholdType.Binary);
+ }
+ else
{
- Voxelizer.UVFace currentFaceItem = null;
-
- /* Should contain a list of all found faces on this layer, keyed by the face orientation */
- var foundFaces = new Dictionary<Voxelizer.FaceOrientation, List<Point>>();
-
- /* move current layer to below */
- belowLayer = curLayer;
+ aboveLayer = null;
+ }
- /* move above layer to us */
- curLayer = aboveLayer;
+ /* get image of pixels to do neighbor checks on */
+ var voxelLayer = Voxelizer.BuildVoxelLayerImage(curLayer!, aboveLayer, belowLayer);
+ var voxelSpan = voxelLayer.GetBytePointer();
- /* bring in a new aboveLayer if we need to */
- if (layerIndex < distinctLayers.Length - 1)
+ /* Seems to be faster to parallel on the Y and not the X */
+ Parallel.For(0, curLayer!.Height, CoreSettings.ParallelOptions, y =>
+ {
+ /* Collects all the faces found for this thread, will be combined into the main dictionary later */
+ var threadDict = new Dictionary<Voxelizer.FaceOrientation, List<Point>>();
+ for (var x = 0; x < curLayer.Width; x++)
{
- using var mat = SlicerFile.LayerManager.GetMergedMatForSequentialPositionedLayers(distinctLayers[(int)layerIndex+1].Index, cacheManager);
- var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
+ if (voxelSpan[voxelLayer.GetPixelPos(x, y)] == 0) continue;
- if ((byte)_quality > 1)
- {
- aboveLayer = new Mat();
- CvInvoke.Resize(matRoi, aboveLayer, Size.Empty, 1.0 / (int)_quality, 1.0 / (int)_quality, Inter.Area);
- }
- else
+ var faces = Voxelizer.GetOpenFaces(curLayer, x, y, belowLayer, aboveLayer);
+ if (faces == Voxelizer.FaceOrientation.None) continue;
+ foreach (var face in facesToCheck)
{
- aboveLayer = matRoi.Clone();
+ if (!faces.HasFlag(face)) continue;
+ if (!threadDict.ContainsKey(face)) threadDict.Add(face, new());
+ threadDict[face].Add(new Point(x, y));
}
-
- //CvInvoke.Threshold(aboveLayer, aboveLayer, 1, 255, ThresholdType.Binary);
- }
- else
- {
- aboveLayer = null;
}
- /* get image of pixels to do neighbor checks on */
- var voxelLayer = Voxelizer.BuildVoxelLayerImage(curLayer, aboveLayer, belowLayer);
- var voxelSpan = voxelLayer.GetBytePointer();
-
- /* Seems to be faster to parallel on the Y and not the X */
- Parallel.For(0, curLayer.Height, CoreSettings.ParallelOptions, y =>
+ /* merge all found faces to main foundFaces dictionary */
+ lock (foundFaces)
{
- /* Collects all the faces found for this thread, will be combined into the main dictionary later */
- var threadDict = new Dictionary<Voxelizer.FaceOrientation, List<Point>>();
- for (var x = 0; x < curLayer.Width; x++)
+ foreach (var kvp in threadDict)
{
- if (voxelSpan[voxelLayer.GetPixelPos(x, y)] == 0) continue;
-
- var faces = Voxelizer.GetOpenFaces(curLayer, x, y, belowLayer, aboveLayer);
- if (faces == Voxelizer.FaceOrientation.None) continue;
- foreach (var face in facesToCheck)
- {
- if (!faces.HasFlag(face)) continue;
- if (!threadDict.ContainsKey(face)) threadDict.Add(face, new());
- threadDict[face].Add(new Point(x, y));
- }
+ if (!foundFaces.ContainsKey(kvp.Key)) foundFaces.Add(kvp.Key, new());
+ lock (foundFaces[kvp.Key]) foundFaces[kvp.Key].AddRange(kvp.Value);
}
+ }
+ });
- /* merge all found faces to main foundFaces dictionary */
- lock (foundFaces)
- {
- foreach (var kvp in threadDict)
- {
- if (!foundFaces.ContainsKey(kvp.Key)) foundFaces.Add(kvp.Key, new());
- lock (foundFaces[kvp.Key]) foundFaces[kvp.Key].AddRange(kvp.Value);
- }
- }
- });
+ /* Begin stage 2, horizontal combining of coplanar faces */
+ foreach (var faceType in facesToCheck)
+ {
+ if (foundFaces.ContainsKey(faceType) == false || foundFaces[faceType].Count == 0) continue;
- /* Begin stage 2, horizontal combining of coplanar faces */
- foreach (var faceType in facesToCheck)
+ if (faceType
+ is Voxelizer.FaceOrientation.Front
+ or Voxelizer.FaceOrientation.Back
+ or Voxelizer.FaceOrientation.Top
+ or Voxelizer.FaceOrientation.Bottom)
{
- if (foundFaces.ContainsKey(faceType) == false || foundFaces[faceType].Count == 0) continue;
-
- if (faceType
- is Voxelizer.FaceOrientation.Front
- or Voxelizer.FaceOrientation.Back
- or Voxelizer.FaceOrientation.Top
- or Voxelizer.FaceOrientation.Bottom)
- {
- /* sort the faces by coordinate */
- foundFaces[faceType] = foundFaces[faceType].OrderBy(f => f.Y).ThenBy(f => f.X).ToList();
+ /* sort the faces by coordinate */
+ foundFaces[faceType] = foundFaces[faceType].OrderBy(f => f.Y).ThenBy(f => f.X).ToList();
- var startX = foundFaces[faceType][0].X;
- var curX = foundFaces[faceType][0].X;
- var startY = foundFaces[faceType][0].Y;
- var curY = foundFaces[faceType][0].Y;
+ var startX = foundFaces[faceType][0].X;
+ var curX = foundFaces[faceType][0].X;
+ var startY = foundFaces[faceType][0].Y;
+ var curY = foundFaces[faceType][0].Y;
- foreach (var f in foundFaces[faceType].Skip(1))
+ foreach (var f in foundFaces[faceType].Skip(1))
+ {
+ if (f.Y == curY)
{
- if (f.Y == curY)
+ /* same row...*/
+ if (f.X == curX + 1)
{
- /* same row...*/
- if (f.X == curX + 1)
- {
- /* this face is adjecent to the previous, just increase the "width" */
- curX++;
- }
- else
- {
- /* This face is disconnected by at least 1 pixel from the chain we've been building */
- /* Create a UVFace for the current chain and reset to this one */
- layerFaceCounts[layerIndex]++;
- if (currentFaceItem is null)
- {
- rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight};
- currentFaceItem = rootFaces[layerIndex];
- }
- else
- {
- currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
- currentFaceItem = currentFaceItem.FlatListNext;
- }
- //faceTree.Add(new float[] { (float)faceType, startX, startY, layerIndex }, new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) });
- /* disconnected */
- startX = f.X;
- curX = f.X;
-
- }
+ /* this face is adjecent to the previous, just increase the "width" */
+ curX++;
}
else
{
- /* this face isn't on the same Y row as previous, therefore it is disconnected. */
+ /* This face is disconnected by at least 1 pixel from the chain we've been building */
/* Create a UVFace for the current chain and reset to this one */
layerFaceCounts[layerIndex]++;
if (currentFaceItem is null)
{
- rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight};
currentFaceItem = rootFaces[layerIndex];
}
else
@@ -367,68 +347,71 @@ namespace UVtools.Core.Operations
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
currentFaceItem = currentFaceItem.FlatListNext;
}
- startY = f.Y;
- curY = f.Y;
+ //faceTree.Add(new float[] { (float)faceType, startX, startY, layerIndex }, new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) });
+ /* disconnected */
startX = f.X;
curX = f.X;
+
}
}
- /* we've gone through all the faces, add the final chain we've been building */
- /* Create a UVFace for the final chain */
- layerFaceCounts[layerIndex]++;
- if (currentFaceItem is null)
- {
- rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
- currentFaceItem = rootFaces[layerIndex];
- }
else
{
- currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
- currentFaceItem = currentFaceItem.FlatListNext;
+ /* this face isn't on the same Y row as previous, therefore it is disconnected. */
+ /* Create a UVFace for the current chain and reset to this one */
+ layerFaceCounts[layerIndex]++;
+ if (currentFaceItem is null)
+ {
+ rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = rootFaces[layerIndex];
+ }
+ else
+ {
+ currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = currentFaceItem.FlatListNext;
+ }
+ startY = f.Y;
+ curY = f.Y;
+ startX = f.X;
+ curX = f.X;
}
}
+ /* we've gone through all the faces, add the final chain we've been building */
+ /* Create a UVFace for the final chain */
+ layerFaceCounts[layerIndex]++;
+ if (currentFaceItem is null)
+ {
+ rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = rootFaces[layerIndex];
+ }
+ else
+ {
+ currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = currentFaceItem.FlatListNext;
+ }
+ }
- if (faceType is Voxelizer.FaceOrientation.Left or Voxelizer.FaceOrientation.Right)
+ if (faceType is Voxelizer.FaceOrientation.Left or Voxelizer.FaceOrientation.Right)
+ {
+ /* sort the faces by coordinate */
+ foundFaces[faceType] = foundFaces[faceType].OrderBy(f => f.X).ThenBy(f => f.Y).ToList();
+
+ var startX = foundFaces[faceType][0].X;
+ var curX = foundFaces[faceType][0].X;
+ var startY = foundFaces[faceType][0].Y;
+ var curY = foundFaces[faceType][0].Y;
+ foreach (var f in foundFaces[faceType].Skip(1))
{
- /* sort the faces by coordinate */
- foundFaces[faceType] = foundFaces[faceType].OrderBy(f => f.X).ThenBy(f => f.Y).ToList();
-
- var startX = foundFaces[faceType][0].X;
- var curX = foundFaces[faceType][0].X;
- var startY = foundFaces[faceType][0].Y;
- var curY = foundFaces[faceType][0].Y;
- foreach (var f in foundFaces[faceType].Skip(1))
+ if (f.X == curX)
{
- if (f.X == curX)
+ /* same column...*/
+ if (f.Y == curY + 1)
{
- /* same column...*/
- if (f.Y == curY + 1)
- {
- /* this face is adjecent to the previous, just increase the "width" */
- curY++;
- }
- else
- {
- /* This face is disconnected by at least 1 pixel from the chain we've been building */
- /* Create a UVFace for the current chain and reset to this one */
- layerFaceCounts[layerIndex]++;
- if (currentFaceItem is null)
- {
- rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
- currentFaceItem = rootFaces[layerIndex];
- }
- else
- {
- currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
- currentFaceItem = currentFaceItem.FlatListNext;
- }
- startY = f.Y;
- curY = f.Y;
- }
+ /* this face is adjecent to the previous, just increase the "width" */
+ curY++;
}
else
{
- /* this face is on a different column, cannot be part of the current chain we're building */
+ /* This face is disconnected by at least 1 pixel from the chain we've been building */
/* Create a UVFace for the current chain and reset to this one */
layerFaceCounts[layerIndex]++;
if (currentFaceItem is null)
@@ -443,213 +426,231 @@ namespace UVtools.Core.Operations
}
startY = f.Y;
curY = f.Y;
- startX = f.X;
- curX = f.X;
}
}
- layerFaceCounts[layerIndex]++;
- if (currentFaceItem is null)
- {
- rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
- currentFaceItem = rootFaces[layerIndex];
- }
else
{
- currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
- currentFaceItem = currentFaceItem.FlatListNext;
+ /* this face is on a different column, cannot be part of the current chain we're building */
+ /* Create a UVFace for the current chain and reset to this one */
+ layerFaceCounts[layerIndex]++;
+ if (currentFaceItem is null)
+ {
+ rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = rootFaces[layerIndex];
+ }
+ else
+ {
+ currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = currentFaceItem.FlatListNext;
+ }
+ startY = f.Y;
+ curY = f.Y;
+ startX = f.X;
+ curX = f.X;
}
}
+ layerFaceCounts[layerIndex]++;
+ if (currentFaceItem is null)
+ {
+ rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = rootFaces[layerIndex];
+ }
+ else
+ {
+ currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
+ currentFaceItem = currentFaceItem.FlatListNext;
+ }
}
+ }
- progress++;
-
- if (progress.Token.IsCancellationRequested)
- {
- Cleanup();
- return false;
- }
+ progress++;
+ if (progress.Token.IsCancellationRequested)
+ {
+ Cleanup();
+ return false;
}
- progress.Title = "Stage 2: Building KD Trees";
- progress.ProcessedItems = 0;
+ }
- /* We build out a 3 dimensional KD tree for each layer, having 1 big KD tree is prohibitive when you get to millions and millions of faces. */
- Parallel.For(0, distinctLayers.Length, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
+ progress.Title = "Stage 2: Building KD Trees";
+ progress.ProcessedItems = 0;
- /* Create the KD tree for the layer, in practice there should never be dups, but just in case, set to skip */
- layerTrees[layerIndex] = new KdTree<float, Voxelizer.UVFace>(3, new FloatMath(), AddDuplicateBehavior.Skip);
+ /* We build out a 3 dimensional KD tree for each layer, having 1 big KD tree is prohibitive when you get to millions and millions of faces. */
+ Parallel.For(0, distinctLayers.Length, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
- /* Walk the linked list of UVFaces, adding them to the tree */
- var currentFaceItem = rootFaces[layerIndex];
- if (currentFaceItem is null) return;
- while (currentFaceItem.FlatListNext is not null)
- {
- layerTrees[layerIndex].Add(new[] { (float)currentFaceItem.Type, currentFaceItem.FaceRect.X, currentFaceItem.FaceRect.Y }, currentFaceItem);
- currentFaceItem = currentFaceItem.FlatListNext;
- }
+ /* Create the KD tree for the layer, in practice there should never be dups, but just in case, set to skip */
+ layerTrees[layerIndex] = new KdTree<float, Voxelizer.UVFace>(3, new FloatMath(), AddDuplicateBehavior.Skip);
+
+ /* Walk the linked list of UVFaces, adding them to the tree */
+ var currentFaceItem = rootFaces[layerIndex];
+ if (currentFaceItem is null) return;
+ while (currentFaceItem.FlatListNext is not null)
+ {
layerTrees[layerIndex].Add(new[] { (float)currentFaceItem.Type, currentFaceItem.FaceRect.X, currentFaceItem.FaceRect.Y }, currentFaceItem);
+ currentFaceItem = currentFaceItem.FlatListNext;
+ }
+ layerTrees[layerIndex].Add(new[] { (float)currentFaceItem.Type, currentFaceItem.FaceRect.X, currentFaceItem.FaceRect.Y }, currentFaceItem);
- progress.LockAndIncrement();
- });
+ progress.LockAndIncrement();
+ });
+
+ if (progress.Token.IsCancellationRequested)
+ {
+ Cleanup();
+ return false;
+ }
+
+ progress.Title = "Stage 3: Collapsing faces";
+ progress.ProcessedItems = 0;
+ long collapseCount = 0;
+ /* Begin Stage 3: Vertical collapse
+ * Since we don't modify the lists/objects and only connect them via doubly linked list
+ * we can process each layer independant of the others.
+ */
+ Parallel.For(0, distinctLayers.Length, i =>
+ {
if (progress.Token.IsCancellationRequested)
{
- Cleanup();
- return false;
+ return;
}
- progress.Title = "Stage 3: Collapsing faces";
- progress.ProcessedItems = 0;
- long collapseCount = 0;
+ /* if no faces on this layer... skip.... needed for empty layers */
+ if (layerTrees[i] is null) return;
- /* Begin Stage 3: Vertical collapse
- * Since we don't modify the lists/objects and only connect them via doubly linked list
- * we can process each layer independant of the others.
- */
- Parallel.For(0, distinctLayers.Length, i =>
+ /* check each point in the current layers tree */
+ foreach (var point in layerTrees[i])
{
- if (progress.Token.IsCancellationRequested)
- {
- return;
- }
+ /* if this point already has a parent, skip */
+ if (point.Value.Parent is not null) continue;
- /* if no faces on this layer... skip.... needed for empty layers */
- if (layerTrees[i] is null) return;
-
- /* check each point in the current layers tree */
- foreach (var point in layerTrees[i])
- {
- /* if this point already has a parent, skip */
- if (point.Value.Parent is not null) continue;
+ /* deterimine the point below to check.
+ * For front/back/left/right its the same X/Y point and Z is different, and Z is done basically by looking at the layer tree below us
+ * For Top/Bottom its a bit different, the Z stays the same (we query our own layer tree) but the Y coordinate is 1 less */
- /* deterimine the point below to check.
- * For front/back/left/right its the same X/Y point and Z is different, and Z is done basically by looking at the layer tree below us
- * For Top/Bottom its a bit different, the Z stays the same (we query our own layer tree) but the Y coordinate is 1 less */
-
- float[] pointBelow = null;
- KdTree<float, Voxelizer.UVFace> treeBelow = null;
- if (point.Value.Type is Voxelizer.FaceOrientation.Top or Voxelizer.FaceOrientation.Bottom)
+ float[]? pointBelow = null;
+ KdTree<float, Voxelizer.UVFace>? treeBelow = null;
+ if (point.Value.Type is Voxelizer.FaceOrientation.Top or Voxelizer.FaceOrientation.Bottom)
+ {
+ if (point.Value.Type == Voxelizer.FaceOrientation.Top)
{
- if (point.Value.Type == Voxelizer.FaceOrientation.Top)
- {
- pointBelow = new[] { point.Point[0], point.Point[1], point.Point[2] - 1 };
- }
- else
- {
- pointBelow = new[] { point.Point[0], point.Point[1], point.Point[2] - 1 };
- }
- treeBelow = layerTrees[i];
+ pointBelow = new[] { point.Point[0], point.Point[1], point.Point[2] - 1 };
}
else
{
- pointBelow = new[] { point.Point[0], point.Point[1], point.Point[2] };
- if (i > 0)
- {
- treeBelow = layerTrees[i - 1];
- }
+ pointBelow = new[] { point.Point[0], point.Point[1], point.Point[2] - 1 };
}
-
- var faceBelow = treeBelow?.FindValueAt(pointBelow);
- if (faceBelow is null) continue;
- /* if we find a face below us it has to be the same width too */
- if (point.Value.FaceRect.Width == faceBelow.FaceRect.Width)
+ treeBelow = layerTrees[i];
+ }
+ else
+ {
+ pointBelow = new[] { point.Point[0], point.Point[1], point.Point[2] };
+ if (i > 0)
{
- /* same coordinate, same width, safe to merge together. Do so by doubly linking the items */
- point.Value.Parent = faceBelow;
- faceBelow.Child = point.Value;
- collapseCount++;
+ treeBelow = layerTrees[i - 1];
}
}
- progress.LockAndIncrement();
- });
- if (progress.Token.IsCancellationRequested)
- {
- Cleanup();
- return false;
+ var faceBelow = treeBelow?.FindValueAt(pointBelow);
+ if (faceBelow is null) continue;
+ /* if we find a face below us it has to be the same width too */
+ if (point.Value.FaceRect.Width == faceBelow.FaceRect.Width)
+ {
+ /* same coordinate, same width, safe to merge together. Do so by doubly linking the items */
+ point.Value.Parent = faceBelow;
+ faceBelow.Child = point.Value;
+ collapseCount++;
+ }
}
+ progress.LockAndIncrement();
+ });
- progress.Title = "Stage 4: Writing the file";
- progress.ProcessedItems = 0;
+ if (progress.Token.IsCancellationRequested)
+ {
+ Cleanup();
+ return false;
+ }
+ progress.Title = "Stage 4: Writing the file";
+ progress.ProcessedItems = 0;
- using var mesh = fileExtension.FileFormatType.CreateInstance<MeshFile>(_filePath, FileMode.Create, _meshFileFormat, SlicerFile);
- mesh.BeginWrite();
- /* Begin Stage 4, generating triangles and saving to file */
- for (var treeIndex = 0; treeIndex < layerTrees.Length; treeIndex++) {
- var tree = layerTrees[treeIndex];
- if (tree is null) continue;
+ using var mesh = fileExtension.FileFormatType.CreateInstance<MeshFile>(_filePath, FileMode.Create, _meshFileFormat, SlicerFile);
+ mesh!.BeginWrite();
- /* only process UVFaces that do not have a parent, these are the "root" faces that couldn't be combined with something above them */
- foreach (var p in tree.Where(p => p.Value.Parent is null))
- {
- /* generate the triangles */
- foreach (var f in Voxelizer.MakeFacetsForUVFace(p.Value, xWidth, yWidth,distinctLayers[treeIndex].PositionZ))
- {
- /* write to file */
- mesh.WriteTriangle(f.p1, f.p2, f.p3, f.normal);
- }
- }
+ /* Begin Stage 4, generating triangles and saving to file */
+ for (var treeIndex = 0; treeIndex < layerTrees.Length; treeIndex++) {
+ var tree = layerTrees[treeIndex];
+ if (tree is null) continue;
- /* check for cancellation at every layer, and if so, close the file properly */
- if (progress.Token.IsCancellationRequested)
+ /* only process UVFaces that do not have a parent, these are the "root" faces that couldn't be combined with something above them */
+ foreach (var p in tree.Where(p => p.Value.Parent is null))
+ {
+ /* generate the triangles */
+ foreach (var f in Voxelizer.MakeFacetsForUVFace(p.Value, xWidth, yWidth,distinctLayers[treeIndex].PositionZ))
{
- Cleanup();
- return false;
+ /* write to file */
+ mesh.WriteTriangle(f.p1, f.p2, f.p3, f.normal);
}
-
- progress++;
}
- void Cleanup()
+ /* check for cancellation at every layer, and if so, close the file properly */
+ if (progress.Token.IsCancellationRequested)
{
- /* dispose of everything */
- for (var x = 0; x < layerTrees.Length; x++)
- {
- layerTrees[x] = null;
- }
+ Cleanup();
+ return false;
+ }
- layerTrees = null;
+ progress++;
+ }
- for (var x = 0; x < rootFaces.Length; x++)
- {
- if (rootFaces[x] is not null) rootFaces[x].FlatListNext = null;
- rootFaces[x] = null;
- }
- rootFaces = null;
- GC.Collect();
+ void Cleanup()
+ {
+ /* dispose of everything */
+ for (var x = 0; x < layerTrees.Length; x++)
+ {
+ layerTrees[x] = null!;
}
- mesh.EndWrite();
+ layerTrees = null;
- return !progress.Token.IsCancellationRequested;
+ for (var x = 0; x < rootFaces.Length; x++)
+ {
+ if (rootFaces[x] is not null) rootFaces[x]!.FlatListNext = null;
+ rootFaces[x] = null!;
+ }
+ rootFaces = null;
+ GC.Collect();
}
+ mesh.EndWrite();
- #endregion
+ return !progress.Token.IsCancellationRequested;
+ }
- #region Equality
- private bool Equals(OperationLayerExportMesh other)
- {
- return _filePath == other._filePath && _meshFileFormat == other._meshFileFormat && _quality == other._quality && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _stripAntiAliasing == other._stripAntiAliasing;
- }
+ #endregion
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationLayerExportMesh other && Equals(other);
- }
+ #region Equality
- public override int GetHashCode()
- {
- return HashCode.Combine(_filePath, (int)_meshFileFormat, (int)_quality, (int)_rotateDirection, (int)_flipDirection, _stripAntiAliasing);
- }
+ private bool Equals(OperationLayerExportMesh other)
+ {
+ return _filePath == other._filePath && _meshFileFormat == other._meshFileFormat && _quality == other._quality && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _stripAntiAliasing == other._stripAntiAliasing;
+ }
- #endregion
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationLayerExportMesh other && Equals(other);
}
-}
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_filePath, (int)_meshFileFormat, (int)_quality, (int)_rotateDirection, (int)_flipDirection, _stripAntiAliasing);
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs
index 2fe70e4..bfadfcf 100644
--- a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs
+++ b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs
@@ -6,137 +6,138 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Threading.Tasks;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationLayerExportSkeleton : Operation
{
- [Serializable]
- public sealed class OperationLayerExportSkeleton : Operation
- {
- #region Members
- private string _filePath;
- private bool _cropByRoi = true;
+ #region Members
+ private string _filePath = null!;
+ private bool _cropByRoi = true;
- #endregion
+ #endregion
- #region Overrides
+ #region Overrides
- public override bool CanHaveProfiles => false;
- public override string Title => "Export layers to skeleton";
+ public override bool CanHaveProfiles => false;
- public override string Description =>
- "Export a layer range to a skeletonized image that is the sum of each layer skeleton.";
+ public override string IconClass => "fas fa-file-image";
+ public override string Title => "Export layers to skeleton";
- public override string ConfirmationText =>
- $"skeletonize from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string Description =>
+ "Export a layer range to a skeletonized image that is the sum of each layer skeleton.";
- public override string ProgressTitle =>
- $"Skeletonizing from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ConfirmationText =>
+ $"skeletonize from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressAction => "Skeletonized layers";
+ public override string ProgressTitle =>
+ $"Skeletonizing from layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ToString()
- {
- var result = $"[Crop by ROI: {_cropByRoi}]" +
- LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ public override string ProgressAction => "Skeletonized layers";
- #endregion
+ public override string ToString()
+ {
+ var result = $"[Crop by ROI: {_cropByRoi}]" +
+ LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #region Properties
+ #endregion
- public string FilePath
- {
- get => _filePath;
- set => RaiseAndSetIfChanged(ref _filePath, value);
- }
+ #region Properties
- public bool CropByROI
- {
- get => _cropByRoi;
- set => RaiseAndSetIfChanged(ref _cropByRoi, value);
- }
+ public string FilePath
+ {
+ get => _filePath;
+ set => RaiseAndSetIfChanged(ref _filePath, value);
+ }
+
+ public bool CropByROI
+ {
+ get => _cropByRoi;
+ set => RaiseAndSetIfChanged(ref _cropByRoi, value);
+ }
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationLayerExportSkeleton()
- { }
+ public OperationLayerExportSkeleton()
+ { }
- public OperationLayerExportSkeleton(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationLayerExportSkeleton(FileFormat slicerFile) : base(slicerFile)
+ { }
- public override void InitWithSlicerFile()
- {
- _filePath = SlicerFile.FileFullPath + ".skeleton.png";
- }
+ public override void InitWithSlicerFile()
+ {
+ _filePath = SlicerFile.FileFullPath + ".skeleton.png";
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- using var skeletonSum = EmguExtensions.InitMat(SlicerFile.Resolution);
- var skeletonSumRoi = GetRoiOrDefault(skeletonSum);
- using var mask = GetMask(skeletonSum);
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ using var skeletonSum = EmguExtensions.InitMat(SlicerFile.Resolution);
+ var skeletonSumRoi = GetRoiOrDefault(skeletonSum);
+ using var mask = GetMask(skeletonSum);
- Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- using var mat = SlicerFile[layerIndex].LayerMat;
- var matRoi = GetRoiOrDefault(mat);
- using var skeletonRoi = matRoi.Skeletonize();
- lock (progress.Mutex)
- {
- CvInvoke.Add(skeletonSumRoi, skeletonRoi, skeletonSumRoi, mask);
- progress++;
- }
- });
-
- if (!progress.Token.IsCancellationRequested)
+ Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ var matRoi = GetRoiOrDefault(mat);
+ using var skeletonRoi = matRoi.Skeletonize();
+ lock (progress.Mutex)
{
- if (_cropByRoi && HaveROI)
- {
- skeletonSumRoi.Save(_filePath);
- }
- else
- {
- skeletonSum.Save(_filePath);
- }
+ CvInvoke.Add(skeletonSumRoi, skeletonRoi, skeletonSumRoi, mask);
+ progress++;
}
+ });
- return !progress.Token.IsCancellationRequested;
+ if (!progress.Token.IsCancellationRequested)
+ {
+ if (_cropByRoi && HaveROI)
+ {
+ skeletonSumRoi.Save(_filePath);
+ }
+ else
+ {
+ skeletonSum.Save(_filePath);
+ }
}
- #endregion
+ return !progress.Token.IsCancellationRequested;
+ }
- #region Equality
+ #endregion
- private bool Equals(OperationLayerExportSkeleton other)
- {
- return _filePath == other._filePath && _cropByRoi == other._cropByRoi;
- }
+ #region Equality
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationLayerExportSkeleton other && Equals(other);
- }
+ private bool Equals(OperationLayerExportSkeleton other)
+ {
+ return _filePath == other._filePath && _cropByRoi == other._cropByRoi;
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_filePath, _cropByRoi);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationLayerExportSkeleton other && Equals(other);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_filePath, _cropByRoi);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index 4b1bf6d..67d568b 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -5,6 +5,8 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -14,513 +16,513 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationLayerImport : Operation
{
- [Serializable]
- public sealed class OperationLayerImport : Operation
+ #region Enums
+ public enum ImportTypes : byte
{
- #region Enums
- public enum ImportTypes : byte
- {
- [Description("Insert: Inserts the new imported layers")]
- Insert,
- [Description("Replace: Replaces layers with the imported layers")]
- Replace,
- [Description("Stack: Stacks and combine imported layers in the current layers")]
- Stack,
- [Description("MergeSum: Merges current layers with the imported content by summing value of layer pixels")]
- MergeSum,
- [Description("MergeMax: Merges current layers with the imported content by using the maximum value of layer pixels")]
- MergeMax,
- [Description("Subtract: Subtracts current layers with the imported content")]
- Subtract,
- [Description("AbsDiff: Absolute difference between current layers and the imported content")]
- AbsDiff,
- [Description("BitwiseAnd: Perform a 'bitwise AND' operation over layers and the imported pixels")]
- BitwiseAnd,
- [Description("BitwiseOr: Perform a 'bitwise OR' operation over layers and the imported pixels")]
- BitwiseOr,
- [Description("BitwiseXOr: Perform a 'bitwise XOR' operation over layers and the imported pixels")]
- BitwiseXOr,
- }
- #endregion
+ [Description("Insert: Inserts the new imported layers")]
+ Insert,
+ [Description("Replace: Replaces layers with the imported layers")]
+ Replace,
+ [Description("Stack: Stacks and combine imported layers in the current layers")]
+ Stack,
+ [Description("MergeSum: Merges current layers with the imported content by summing value of layer pixels")]
+ MergeSum,
+ [Description("MergeMax: Merges current layers with the imported content by using the maximum value of layer pixels")]
+ MergeMax,
+ [Description("Subtract: Subtracts current layers with the imported content")]
+ Subtract,
+ [Description("AbsDiff: Absolute difference between current layers and the imported content")]
+ AbsDiff,
+ [Description("BitwiseAnd: Perform a 'bitwise AND' operation over layers and the imported pixels")]
+ BitwiseAnd,
+ [Description("BitwiseOr: Perform a 'bitwise OR' operation over layers and the imported pixels")]
+ BitwiseOr,
+ [Description("BitwiseXOr: Perform a 'bitwise XOR' operation over layers and the imported pixels")]
+ BitwiseXOr,
+ }
+ #endregion
+
+ #region Members
+ private ImportTypes _importType = ImportTypes.Stack;
+ private uint _startLayerIndex;
+ private bool _extendBeyondLayerCount = true;
+ private bool _discardUnmodifiedLayers;
+ private ushort _stackMargin = 50;
+ private RangeObservableCollection<ValueDescription> _files = new();
+ #endregion
+
+ #region Overrides
- #region Members
- private ImportTypes _importType = ImportTypes.Stack;
- private uint _startLayerIndex;
- private bool _extendBeyondLayerCount = true;
- private bool _discardUnmodifiedLayers;
- private ushort _stackMargin = 50;
- private RangeObservableCollection<ValueDescription> _files = new();
- #endregion
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
- #region Overrides
+ public override string IconClass => "mdi-database-import";
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI => false;
- public override string Title => "Import layers";
+ public override string Title => "Import layers";
- public override string Description =>
- "Import layers from local files into the model at a selected layer height.\n" +
- "NOTE: Imported images must be greyscale and have the same resolution as the model.";
+ public override string Description =>
+ "Import layers from local files into the model at a selected layer height.\n" +
+ "NOTE: Imported images must be greyscale and have the same resolution as the model.";
- public override string ConfirmationText => $"{_importType} import {Count} file{(Count>=1?"s":"")}?";
+ public override string ConfirmationText => $"{_importType} import {Count} file{(Count>=1?"s":"")}?";
- public override string ProgressTitle =>
- $"{_importType} importing {Count} file{(Count>=1 ? "s" : "")}";
+ public override string ProgressTitle =>
+ $"{_importType} importing {Count} file{(Count>=1 ? "s" : "")}";
- public override string ProgressAction => "Imported layers";
+ public override string ProgressAction => "Imported layers";
- public override bool CanCancel => true;
+ public override bool CanCancel => true;
- public override uint LayerIndexEnd => _startLayerIndex + Count - 1;
+ public override uint LayerIndexEnd => _startLayerIndex + Count - 1;
- public override bool CanHaveProfiles => false;
+ public override bool CanHaveProfiles => false;
- public override string ValidateInternally()
+ public override string? ValidateInternally()
+ {
+ /*var result = new ConcurrentBag<StringTag>();
+ Parallel.ForEach(Files, CoreSettings.ParallelOptions, file =>
{
- /*var result = new ConcurrentBag<StringTag>();
- Parallel.ForEach(Files, CoreSettings.ParallelOptions, file =>
+ using (Mat mat = CvInvoke.Imread(file.TagString, ImreadModes.AnyColor))
{
- using (Mat mat = CvInvoke.Imread(file.TagString, ImreadModes.AnyColor))
+ if (mat.Size != FileResolution)
{
- if (mat.Size != FileResolution)
- {
- result.Add(file);
- }
+ result.Add(file);
}
- });
-
- if (result.IsEmpty) return null;
- var message = new StringBuilder();
- message.AppendLine($"The following {result.Count} files mismatched the target resolution of {FileResolution.Width}x{FileResolution.Height}:");
- message.AppendLine();
- uint count = 0;
- foreach (var file in result)
+ }
+ });
+
+ if (result.IsEmpty) return null;
+ var message = new StringBuilder();
+ message.AppendLine($"The following {result.Count} files mismatched the target resolution of {FileResolution.Width}x{FileResolution.Height}:");
+ message.AppendLine();
+ uint count = 0;
+ foreach (var file in result)
+ {
+ count++;
+ if (count == 20)
{
- count++;
- if (count == 20)
- {
- message.AppendLine("... Too many to show ...");
- break;
- }
- message.AppendLine(file.Content);
+ message.AppendLine("... Too many to show ...");
+ break;
}
-
-
- return new StringTag(message.ToString(), result);*/
+ message.AppendLine(file.Content);
+ }
+
- StringBuilder sb = new();
+ return new StringTag(message.ToString(), result);*/
- if (_files.Count == 0)
- {
- sb.AppendLine("No files to import.");
- }
+ StringBuilder sb = new();
- return sb.ToString();
+ if (_files.Count == 0)
+ {
+ sb.AppendLine("No files to import.");
}
- #endregion
- #region Properties
+ return sb.ToString();
+ }
+ #endregion
+
+ #region Properties
- public ImportTypes ImportType
+ public ImportTypes ImportType
+ {
+ get => _importType;
+ set
{
- get => _importType;
- set
- {
- if(!RaiseAndSetIfChanged(ref _importType, value)) return;
- RaisePropertyChanged(nameof(IsImportStackType));
- RaisePropertyChanged(nameof(IsExtendBeyondLayerCountVisible));
- }
+ if(!RaiseAndSetIfChanged(ref _importType, value)) return;
+ RaisePropertyChanged(nameof(IsImportStackType));
+ RaisePropertyChanged(nameof(IsExtendBeyondLayerCountVisible));
}
+ }
- public static Array ImportTypesItems => Enum.GetValues(typeof(ImportTypes));
+ public static Array ImportTypesItems => Enum.GetValues(typeof(ImportTypes));
- public bool IsImportStackType => _importType == ImportTypes.Stack;
+ public bool IsImportStackType => _importType == ImportTypes.Stack;
- public uint StartLayerIndex
- {
- get => _startLayerIndex;
- set => RaiseAndSetIfChanged(ref _startLayerIndex, value);
- }
+ public uint StartLayerIndex
+ {
+ get => _startLayerIndex;
+ set => RaiseAndSetIfChanged(ref _startLayerIndex, value);
+ }
- public bool ExtendBeyondLayerCount
- {
- get => _extendBeyondLayerCount;
- set => RaiseAndSetIfChanged(ref _extendBeyondLayerCount, value);
- }
+ public bool ExtendBeyondLayerCount
+ {
+ get => _extendBeyondLayerCount;
+ set => RaiseAndSetIfChanged(ref _extendBeyondLayerCount, value);
+ }
- public bool IsExtendBeyondLayerCountVisible => _importType is ImportTypes.Replace or ImportTypes.Stack or ImportTypes.MergeSum or ImportTypes.MergeMax;
+ public bool IsExtendBeyondLayerCountVisible => _importType is ImportTypes.Replace or ImportTypes.Stack or ImportTypes.MergeSum or ImportTypes.MergeMax;
- public bool DiscardUnmodifiedLayers
- {
- get => _discardUnmodifiedLayers;
- set => RaiseAndSetIfChanged(ref _discardUnmodifiedLayers, value);
- }
+ public bool DiscardUnmodifiedLayers
+ {
+ get => _discardUnmodifiedLayers;
+ set => RaiseAndSetIfChanged(ref _discardUnmodifiedLayers, value);
+ }
- public ushort StackMargin
- {
- get => _stackMargin;
- set => RaiseAndSetIfChanged(ref _stackMargin, value);
- }
+ public ushort StackMargin
+ {
+ get => _stackMargin;
+ set => RaiseAndSetIfChanged(ref _stackMargin, value);
+ }
- [XmlIgnore]
- public RangeObservableCollection<ValueDescription> Files
- {
- get => _files;
- set => RaiseAndSetIfChanged(ref _files, value);
- }
+ [XmlIgnore]
+ public RangeObservableCollection<ValueDescription> Files
+ {
+ get => _files;
+ set => RaiseAndSetIfChanged(ref _files, value);
+ }
- public uint Count => (uint)_files.Count;
- #endregion
+ public uint Count => (uint)_files.Count;
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationLayerImport() { }
+ public OperationLayerImport() { }
- public OperationLayerImport(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationLayerImport(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- public void AddFile(string file)
+ public void AddFile(string file)
+ {
+ _files.Add(new ValueDescription(file, Path.GetFileNameWithoutExtension(file)));
+ }
+
+ public void Sort()
+ {
+ _files.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.ValueAsString), Path.GetFileNameWithoutExtension(file2.ValueAsString), StringComparison.Ordinal));
+ }
+
+
+ /*public uint CalculateTotalLayers(uint totalLayers)
+ {
+ if (DiscardRemainingLayers)
{
- _files.Add(new ValueDescription(file, Path.GetFileNameWithoutExtension(file)));
+ return (uint) (1 + InsertAfterLayerIndex + Files.Count - (ReplaceStartLayer ? 1 : 0));
}
-
- public void Sort()
+ if (ReplaceSubsequentLayers)
{
- _files.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.ValueAsString), Path.GetFileNameWithoutExtension(file2.ValueAsString), StringComparison.Ordinal));
+ uint result = (uint) (1 + InsertAfterLayerIndex + Files.Count - (ReplaceStartLayer ? 1 : 0));
+ return result <= totalLayers ? totalLayers : result;
}
-
- /*public uint CalculateTotalLayers(uint totalLayers)
- {
- if (DiscardRemainingLayers)
+ return (uint)(totalLayers + Files.Count - (ReplaceStartLayer ? 1 : 0));
+ }*/
+
+ public override string ToString()
+ {
+ var result = $"[Files: {Count}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = 0;
+ var result = SlicerFile.SuppressRebuildPropertiesWork(() => {
+ List<FileFormat> fileFormats = new();
+ List<KeyValuePair<uint, string>> keyImage = new();
+ int lastProcessedLayerIndex = -1;
+
+ // Order raw images
+ for (int i = 0; i < Count; i++)
{
- return (uint) (1 + InsertAfterLayerIndex + Files.Count - (ReplaceStartLayer ? 1 : 0));
+ if (!_files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
+ keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, _files[i].ValueAsString));
}
- if (ReplaceSubsequentLayers)
+
+ // Create virtual file format with images
+ if (keyImage.Count > 0)
{
- uint result = (uint) (1 + InsertAfterLayerIndex + Files.Count - (ReplaceStartLayer ? 1 : 0));
- return result <= totalLayers ? totalLayers : result;
- }
+ progress.Reset("Packing images", (uint)keyImage.Count);
+ SL1File format = new();
+ format.Init((uint)keyImage.Count);
- return (uint)(totalLayers + Files.Count - (ReplaceStartLayer ? 1 : 0));
- }*/
+ Parallel.ForEach(keyImage, CoreSettings.ParallelOptions, pair =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = CvInvoke.Imread(pair.Value, ImreadModes.Grayscale);
+ if (pair.Key == 0) format.Resolution = mat.Size;
+ format[pair.Key] = new Layer(pair.Key, mat, format);
- public override string ToString()
- {
- var result = $"[Files: {Count}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ progress.LockAndIncrement();
+ });
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- progress.ItemCount = 0;
- var result = SlicerFile.SuppressRebuildPropertiesWork(() => {
- List<FileFormat> fileFormats = new();
- List<KeyValuePair<uint, string>> keyImage = new();
- int lastProcessedLayerIndex = -1;
-
- // Order raw images
- for (int i = 0; i < Count; i++)
- {
- if (!_files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
- !_files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
- !_files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) &&
- !_files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) &&
- !_files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
- keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, _files[i].ValueAsString));
- }
+ progress.Token.ThrowIfCancellationRequested();
+ fileFormats.Add(format);
+ }
- // Create virtual file format with images
- if (keyImage.Count > 0)
- {
- progress.Reset("Packing images", (uint)keyImage.Count);
- SL1File format = new();
- format.LayerManager.Init((uint)keyImage.Count);
+ // Order remaining possible file formats
+ for (int i = 0; i < Count; i++)
+ {
+ if (_files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
+
+ var fileFormat = FileFormat.FindByExtensionOrFilePath(_files[i].ValueAsString, true);
+ if (fileFormat is null) continue;
+ fileFormat.FileFullPath = _files[i].ValueAsString;
+ fileFormats.Add(fileFormat);
+ }
- Parallel.ForEach(keyImage, CoreSettings.ParallelOptions, pair =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = CvInvoke.Imread(pair.Value, ImreadModes.Grayscale);
- if (pair.Key == 0) format.Resolution = mat.Size;
- format[pair.Key] = new Layer(pair.Key, mat, format);
+ progress.Token.ThrowIfCancellationRequested();
- progress.LockAndIncrement();
- });
+ if (fileFormats.Count == 0) return false;
- progress.Token.ThrowIfCancellationRequested();
- fileFormats.Add(format);
- }
+ if (_importType == ImportTypes.Stack)
+ {
+ new OperationMove(SlicerFile, Enumerations.Anchor.TopLeft).Execute(progress);
+ }
- // Order remaining possible file formats
- for (int i = 0; i < Count; i++)
+ foreach (var fileFormat in fileFormats)
+ {
+ if (!string.IsNullOrEmpty(fileFormat.FileFullPath))
{
- if (_files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ||
- _files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) ||
- _files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) ||
- _files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ||
- _files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
-
- var fileFormat = FileFormat.FindByExtensionOrFilePath(_files[i].ValueAsString, true);
- if (fileFormat is null) continue;
- fileFormat.FileFullPath = _files[i].ValueAsString;
- fileFormats.Add(fileFormat);
+ fileFormat.Decode(fileFormat.FileFullPath, progress);
}
- progress.Token.ThrowIfCancellationRequested();
-
- if (fileFormats.Count == 0) return false;
+ var boundingRectangle = SlicerFile.GetBoundingRectangle(progress);
+ var fileFormatBoundingRectangle = fileFormat.GetBoundingRectangle(progress);
+ var roiRectangle = Rectangle.Empty;
- if (_importType == ImportTypes.Stack)
+ // Check if is possible to process this file
+ switch (_importType)
{
- new OperationMove(SlicerFile, Enumerations.Anchor.TopLeft).Execute(progress);
+ case ImportTypes.Insert:
+ if (SlicerFile.Resolution != fileFormat.Resolution &&
+ (SlicerFile.Resolution.Width < fileFormatBoundingRectangle.Width ||
+ SlicerFile.Resolution.Height < fileFormatBoundingRectangle.Height)) continue;
+ SlicerFile.ReallocateInsert(_startLayerIndex, fileFormat.LayerCount);
+ break;
+ case ImportTypes.Replace:
+ case ImportTypes.Stack:
+ if (SlicerFile.Resolution != fileFormat.Resolution &&
+ (SlicerFile.Resolution.Width < fileFormatBoundingRectangle.Width ||
+ SlicerFile.Resolution.Height < fileFormatBoundingRectangle.Height)) continue;
+
+
+ //if(fileFormatBoundingRectangle.Width >= SlicerFile.ResolutionX || fileFormatBoundingRectangle.Height >= SlicerFile.ResolutionY)
+ // continue;
+
+ if (_importType == ImportTypes.Stack)
+ {
+ int x = 0;
+ int y = 0;
+
+ if (boundingRectangle.Right + _stackMargin + fileFormatBoundingRectangle.Width <
+ SlicerFile.ResolutionX)
+ {
+ x = boundingRectangle.Right + _stackMargin;
+ }
+ else
+ {
+ y = boundingRectangle.Bottom + _stackMargin;
+ }
+
+ if (x + fileFormatBoundingRectangle.Width >= SlicerFile.ResolutionX)
+ continue;
+ if (y + fileFormatBoundingRectangle.Height >= SlicerFile.ResolutionY)
+ continue;
+
+ roiRectangle = new Rectangle(x, y, fileFormatBoundingRectangle.Width,
+ fileFormatBoundingRectangle.Height);
+ }
+
+ if (_extendBeyondLayerCount)
+ {
+ int layerCountDifference = (int)(_startLayerIndex + fileFormat.LayerCount - SlicerFile.LayerCount);
+ if (layerCountDifference > 0)
+ {
+ SlicerFile.ReallocateEnd((uint)layerCountDifference, _importType == ImportTypes.Stack);
+ }
+ }
+
+ break;
+ case ImportTypes.MergeSum:
+ case ImportTypes.MergeMax:
+ if (SlicerFile.Resolution != fileFormat.Resolution) continue;
+ if (_extendBeyondLayerCount)
+ {
+ int layerCountDifference = (int)(_startLayerIndex + fileFormat.LayerCount - SlicerFile.LayerCount);
+ if (layerCountDifference > 0)
+ {
+ SlicerFile.ReallocateEnd((uint)layerCountDifference, true);
+ }
+ }
+ break;
+ case ImportTypes.Subtract:
+ case ImportTypes.AbsDiff:
+ case ImportTypes.BitwiseAnd:
+ case ImportTypes.BitwiseOr:
+ case ImportTypes.BitwiseXOr:
+ if (SlicerFile.Resolution != fileFormat.Resolution) continue;
+ break;
}
- foreach (var fileFormat in fileFormats)
+ progress.Reset(ProgressAction, fileFormat.LayerCount);
+ Parallel.For(0, fileFormat.LayerCount, CoreSettings.ParallelOptions, i =>
{
- if (!string.IsNullOrEmpty(fileFormat.FileFullPath))
- {
- fileFormat.Decode(fileFormat.FileFullPath, progress);
- }
+ if (progress.Token.IsCancellationRequested) return;
+ uint layerIndex = (uint)(_startLayerIndex + i);
- var boundingRectangle = SlicerFile.LayerManager.GetBoundingRectangle(progress);
- var fileFormatBoundingRectangle = fileFormat.LayerManager.GetBoundingRectangle(progress);
- var roiRectangle = Rectangle.Empty;
-
- // Check if is possible to process this file
switch (_importType)
{
case ImportTypes.Insert:
- if (SlicerFile.Resolution != fileFormat.Resolution &&
- (SlicerFile.Resolution.Width < fileFormatBoundingRectangle.Width ||
- SlicerFile.Resolution.Height < fileFormatBoundingRectangle.Height)) continue;
- SlicerFile.LayerManager.ReallocateInsert(_startLayerIndex, fileFormat.LayerCount);
- break;
- case ImportTypes.Replace:
- case ImportTypes.Stack:
- if (SlicerFile.Resolution != fileFormat.Resolution &&
- (SlicerFile.Resolution.Width < fileFormatBoundingRectangle.Width ||
- SlicerFile.Resolution.Height < fileFormatBoundingRectangle.Height)) continue;
-
-
- //if(fileFormatBoundingRectangle.Width >= SlicerFile.ResolutionX || fileFormatBoundingRectangle.Height >= SlicerFile.ResolutionY)
- // continue;
-
- if (_importType == ImportTypes.Stack)
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ if (SlicerFile.Resolution == fileFormat.Resolution)
{
- int x = 0;
- int y = 0;
-
- if (boundingRectangle.Right + _stackMargin + fileFormatBoundingRectangle.Width <
- SlicerFile.ResolutionX)
- {
- x = boundingRectangle.Right + _stackMargin;
- }
- else
- {
- y = boundingRectangle.Bottom + _stackMargin;
- }
-
- if (x + fileFormatBoundingRectangle.Width >= SlicerFile.ResolutionX)
- continue;
- if (y + fileFormatBoundingRectangle.Height >= SlicerFile.ResolutionY)
- continue;
-
- roiRectangle = new Rectangle(x, y, fileFormatBoundingRectangle.Width,
- fileFormatBoundingRectangle.Height);
+ SlicerFile[layerIndex] = fileFormat[i];
+ break;
}
- if (_extendBeyondLayerCount)
+ using var layer = fileFormat[i].LayerMat;
+ using var layerRoi = layer.NewMatFromCenterRoi(SlicerFile.Resolution, fileFormatBoundingRectangle);
+ SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
+
+ break;
+ }
+ case ImportTypes.Replace:
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ if (SlicerFile.Resolution == fileFormat.Resolution)
{
- int layerCountDifference = (int)(_startLayerIndex + fileFormat.LayerCount - SlicerFile.LayerCount);
- if (layerCountDifference > 0)
- {
- SlicerFile.LayerManager.ReallocateEnd((uint)layerCountDifference, _importType == ImportTypes.Stack);
- }
+ SlicerFile[layerIndex] = fileFormat[i];
+ break;
}
+ using var layer = fileFormat[i].LayerMat;
+ using var layerRoi = layer.NewMatFromCenterRoi(SlicerFile.Resolution, fileFormatBoundingRectangle);
+ SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
+ break;
+ }
+ case ImportTypes.Stack:
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ using var importMat = fileFormat[i].LayerMat;
+ var matRoi = new Mat(mat, roiRectangle);
+ var importMatRoi = new Mat(importMat, fileFormatBoundingRectangle);
+ importMatRoi.CopyTo(matRoi);
+ SlicerFile[layerIndex].LayerMat = mat;
+
break;
+ }
case ImportTypes.MergeSum:
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var originalMat = SlicerFile[layerIndex].LayerMat;
+ using var newMat = fileFormat[i].LayerMat;
+ CvInvoke.Add(originalMat, newMat, newMat);
+ SlicerFile[layerIndex].LayerMat = newMat;
+ break;
+ }
case ImportTypes.MergeMax:
- if (SlicerFile.Resolution != fileFormat.Resolution) continue;
- if (_extendBeyondLayerCount)
- {
- int layerCountDifference = (int)(_startLayerIndex + fileFormat.LayerCount - SlicerFile.LayerCount);
- if (layerCountDifference > 0)
- {
- SlicerFile.LayerManager.ReallocateEnd((uint)layerCountDifference, true);
- }
- }
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var originalMat = SlicerFile[layerIndex].LayerMat;
+ using var newMat = fileFormat[i].LayerMat;
+ CvInvoke.Max(originalMat, newMat, newMat);
+ SlicerFile[layerIndex].LayerMat = newMat;
break;
+ }
case ImportTypes.Subtract:
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var originalMat = SlicerFile[layerIndex].LayerMat;
+ using var newMat = fileFormat[i].LayerMat;
+ CvInvoke.Subtract(originalMat, newMat, newMat);
+ SlicerFile[layerIndex].LayerMat = newMat;
+ break;
+ }
case ImportTypes.AbsDiff:
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var originalMat = SlicerFile[layerIndex].LayerMat;
+ using var newMat = fileFormat[i].LayerMat;
+ CvInvoke.AbsDiff(originalMat, newMat, newMat);
+ SlicerFile[layerIndex].LayerMat = newMat;
+ break;
+ }
case ImportTypes.BitwiseAnd:
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var originalMat = SlicerFile[layerIndex].LayerMat;
+ using var newMat = fileFormat[i].LayerMat;
+ CvInvoke.BitwiseAnd(originalMat, newMat, newMat);
+ SlicerFile[layerIndex].LayerMat = newMat;
+ break;
+ }
case ImportTypes.BitwiseOr:
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var originalMat = SlicerFile[layerIndex].LayerMat;
+ using var newMat = fileFormat[i].LayerMat;
+ CvInvoke.BitwiseOr(originalMat, newMat, newMat);
+ SlicerFile[layerIndex].LayerMat = newMat;
+ break;
+ }
case ImportTypes.BitwiseXOr:
- if (SlicerFile.Resolution != fileFormat.Resolution) continue;
+ {
+ if (layerIndex >= SlicerFile.LayerCount) return;
+ using var originalMat = SlicerFile[layerIndex].LayerMat;
+ using var newMat = fileFormat[i].LayerMat;
+ CvInvoke.BitwiseXor(originalMat, newMat, newMat);
+ SlicerFile[layerIndex].LayerMat = newMat;
break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException();
}
- progress.Reset(ProgressAction, fileFormat.LayerCount);
- Parallel.For(0, fileFormat.LayerCount, CoreSettings.ParallelOptions, i =>
- {
- if (progress.Token.IsCancellationRequested) return;
- uint layerIndex = (uint)(_startLayerIndex + i);
-
- switch (_importType)
- {
- case ImportTypes.Insert:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- if (SlicerFile.Resolution == fileFormat.Resolution)
- {
- SlicerFile[layerIndex] = fileFormat[i];
- break;
- }
-
- using var layer = fileFormat[i].LayerMat;
- using var layerRoi = layer.NewMatFromCenterRoi(SlicerFile.Resolution, fileFormatBoundingRectangle);
- SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
-
- break;
- }
- case ImportTypes.Replace:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- if (SlicerFile.Resolution == fileFormat.Resolution)
- {
- SlicerFile[layerIndex] = fileFormat[i];
- break;
- }
-
- using var layer = fileFormat[i].LayerMat;
- using var layerRoi = layer.NewMatFromCenterRoi(SlicerFile.Resolution, fileFormatBoundingRectangle);
- SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
- break;
- }
- case ImportTypes.Stack:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- using var importMat = fileFormat[i].LayerMat;
- var matRoi = new Mat(mat, roiRectangle);
- var importMatRoi = new Mat(importMat, fileFormatBoundingRectangle);
- importMatRoi.CopyTo(matRoi);
- SlicerFile[layerIndex].LayerMat = mat;
-
- break;
- }
- case ImportTypes.MergeSum:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var originalMat = SlicerFile[layerIndex].LayerMat;
- using var newMat = fileFormat[i].LayerMat;
- CvInvoke.Add(originalMat, newMat, newMat);
- SlicerFile[layerIndex].LayerMat = newMat;
- break;
- }
- case ImportTypes.MergeMax:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var originalMat = SlicerFile[layerIndex].LayerMat;
- using var newMat = fileFormat[i].LayerMat;
- CvInvoke.Max(originalMat, newMat, newMat);
- SlicerFile[layerIndex].LayerMat = newMat;
- break;
- }
- case ImportTypes.Subtract:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var originalMat = SlicerFile[layerIndex].LayerMat;
- using var newMat = fileFormat[i].LayerMat;
- CvInvoke.Subtract(originalMat, newMat, newMat);
- SlicerFile[layerIndex].LayerMat = newMat;
- break;
- }
- case ImportTypes.AbsDiff:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var originalMat = SlicerFile[layerIndex].LayerMat;
- using var newMat = fileFormat[i].LayerMat;
- CvInvoke.AbsDiff(originalMat, newMat, newMat);
- SlicerFile[layerIndex].LayerMat = newMat;
- break;
- }
- case ImportTypes.BitwiseAnd:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var originalMat = SlicerFile[layerIndex].LayerMat;
- using var newMat = fileFormat[i].LayerMat;
- CvInvoke.BitwiseAnd(originalMat, newMat, newMat);
- SlicerFile[layerIndex].LayerMat = newMat;
- break;
- }
- case ImportTypes.BitwiseOr:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var originalMat = SlicerFile[layerIndex].LayerMat;
- using var newMat = fileFormat[i].LayerMat;
- CvInvoke.BitwiseOr(originalMat, newMat, newMat);
- SlicerFile[layerIndex].LayerMat = newMat;
- break;
- }
- case ImportTypes.BitwiseXOr:
- {
- if (layerIndex >= SlicerFile.LayerCount) return;
- using var originalMat = SlicerFile[layerIndex].LayerMat;
- using var newMat = fileFormat[i].LayerMat;
- CvInvoke.BitwiseXor(originalMat, newMat, newMat);
- SlicerFile[layerIndex].LayerMat = newMat;
- break;
- }
- default:
- throw new ArgumentOutOfRangeException();
- }
+ lock (progress.Mutex)
+ {
+ lastProcessedLayerIndex = Math.Max(lastProcessedLayerIndex, (int)layerIndex);
+ progress++;
+ }
+ });
- lock (progress.Mutex)
- {
- lastProcessedLayerIndex = Math.Max(lastProcessedLayerIndex, (int)layerIndex);
- progress++;
- }
- });
-
- fileFormat.Dispose();
- progress.Token.ThrowIfCancellationRequested();
- }
+ fileFormat.Dispose();
+ progress.Token.ThrowIfCancellationRequested();
+ }
- if (_importType == ImportTypes.Stack)
- {
- new OperationMove(SlicerFile).Execute(progress);
- }
+ if (_importType == ImportTypes.Stack)
+ {
+ new OperationMove(SlicerFile).Execute(progress);
+ }
- if (lastProcessedLayerIndex <= -1) return false;
+ if (lastProcessedLayerIndex <= -1) return false;
- if (lastProcessedLayerIndex + 1 < SlicerFile.LayerCount && _discardUnmodifiedLayers)
- {
- SlicerFile.LayerManager.Reallocate((uint)lastProcessedLayerIndex + 1);
- }
+ if (lastProcessedLayerIndex + 1 < SlicerFile.LayerCount && _discardUnmodifiedLayers)
+ {
+ SlicerFile.Reallocate((uint)lastProcessedLayerIndex + 1);
+ }
- return true;
- }, true);
+ return true;
+ }, true);
- return !progress.Token.IsCancellationRequested && result;
- }
-
- #endregion
+ return !progress.Token.IsCancellationRequested && result;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs
index 27f40f9..f53f5e4 100644
--- a/UVtools.Core/Operations/OperationLayerReHeight.cs
+++ b/UVtools.Core/Operations/OperationLayerReHeight.cs
@@ -6,211 +6,288 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationLayerReHeight : Operation
{
- [Serializable]
- public sealed class OperationLayerReHeight : Operation
+ #region Enums
+
+ public enum OperationLayerReHeightMethod : byte
{
- #region Enums
+ [Description("Re-height: Change layer height / thickness")]
+ ReHeight,
+ [Description("Offset - Change layers position by an offset")]
+ OffsetPositionZ
+ }
- public enum OperationLayerReHeightAntiAliasingType : byte
+ public enum OperationLayerReHeightAntiAliasingType : byte
+ {
+ None,
+ [Description("Difference - Compute anti-aliasing by the layers difference and perform a down/up sample over pixels")]
+ Difference,
+ [Description("Average - Compute anti-aliasing by averaging the layers pixels")]
+ Average
+ }
+ #endregion
+
+ #region Members
+
+ private OperationLayerReHeightMethod _method;
+ private decimal _positionZOffset = 0.01m;
+ private OperationLayerReHeightItem? _selectedItem;
+ private OperationLayerReHeightAntiAliasingType _antiAliasingType;
+ private decimal _bottomExposure;
+ private decimal _normalExposure;
+
+ #endregion
+
+ #region Overrides
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override bool CanROI => false;
+ public override string IconClass => "mdi-arrow-split-horizontal";
+ public override string Title => "Adjust layer height";
+ public override string Description =>
+ "Adjust the layer height of the model or offset layers position by a set value.\n\n" +
+ "Adjusting to values lower than current height will reduce layer lines, adjusting to values higher" +
+ " than current height will reduce model detail.\n" +
+ "Different layer thickness will require different exposure times, adjust accordingly.\n\n" +
+ "Note: Using dedicated slicer software to re-slice will usually yield better results.";
+ public override string ConfirmationText =>
+ _method == OperationLayerReHeightMethod.ReHeight
+ ? $"adjust layer height to {_selectedItem!.LayerHeight}mm?"
+ : $"adjust layers position by a offset of {_positionZOffset}mm?";
+
+ public override string ProgressTitle =>
+ _method == OperationLayerReHeightMethod.ReHeight
+ ? $"Adjusting layer height to {_selectedItem!.LayerHeight}mm"
+ : $"Adjusting layers position by a offset of {_positionZOffset}mm";
+
+ public override string ProgressAction => "Height adjusted layers";
+
+ public override bool CanHaveProfiles => false;
+
+ public override string? ValidateSpawn()
+ {
+ if ((Presets is null || Presets.Length == 0) && !SlicerFile.CanUseLayerPositionZ)
{
- //[Description("Hello")]
- None,
- [Description("Difference - Compute anti-aliasing by the layers difference and perform a down/up sample over pixels")]
- Difference,
- [Description("Average - Compute anti-aliasing by averaging the layers pixels")]
- Average
+ return "No valid configuration to be able to re-height.\n" +
+ "As workaround clone first or last layer and try re run this tool.";
}
- #endregion
- #region Members
- private OperationLayerReHeightItem _selectedItem;
- private OperationLayerReHeightAntiAliasingType _antiAliasingType;
- private decimal _bottomExposure;
- private decimal _normalExposure;
-
- #endregion
-
- #region Overrides
+ return null;
+ }
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
- public override bool CanROI => false;
- public override string Title => "Adjust layer height";
- public override string Description =>
- "Adjust the layer height of the model.\n\n" +
- "Adjusting to values lower than current height will reduce layer lines, adjusting to values higher" +
- " than current height will reduce model detail.\n" +
- "Different layer thickness will require different exposure times, adjust accordingly.\n\n" +
- "Note: Using dedicated slicer software to re-slice will usually yeild better results.";
- public override string ConfirmationText =>
- $"adjust layer height to {SelectedItem.LayerHeight}mm?";
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ProgressTitle =>
- $"Adjusting layer height to {SelectedItem.LayerHeight}mm";
+ switch (_method)
+ {
+ case OperationLayerReHeightMethod.ReHeight:
+ if (Presets.Length == 0)
+ {
+ sb.AppendLine("No valid configurations, unable to proceed.");
+ }
- public override string ProgressAction => "Height adjusted layers";
+ if (_selectedItem is null)
+ {
+ sb.AppendLine("No new height was selected.");
+ }
- public override bool CanHaveProfiles => false;
+ break;
+ case OperationLayerReHeightMethod.OffsetPositionZ:
+ if (!SlicerFile.CanUseLayerPositionZ)
+ {
+ sb.AppendLine($"This file format / printer is unable to use the {_method} method.");
+ break;
+ }
- public override string ValidateSpawn()
- {
- if (Presets is null || Presets.Length == 0)
- {
- return "No valid configuration to be able to re-height.\n" +
- "As workaround clone first or last layer and try re run this tool.";
- }
+ if (_positionZOffset == 0)
+ {
+ sb.AppendLine("Position offset can't be 0, it have no effect.");
+ break;
+ }
+
+ for (var layerIndex = LayerIndexStart; layerIndex < LayerIndexEnd; layerIndex++)
+ {
+ if ((decimal) SlicerFile[layerIndex].PositionZ + _positionZOffset < 0)
+ {
+ sb.AppendLine($"Offset position by {_positionZOffset}mm will put layer {layerIndex} under 0mm.");
+ break;
+ }
+ }
- return null;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
}
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
- if (SelectedItem is null)
- {
- sb.AppendLine("No valid configurations, unable to proceed.");
- }
+ return sb.ToString();
+ }
+ public override string ToString()
+ {
+ var result = $"[{_method}]" +
+ (_method == OperationLayerReHeightMethod.ReHeight
+ ? $"[Layer Count: {_selectedItem!.LayerCount}] " +
+ $"[Layer Height: {_selectedItem!.LayerHeight}] " +
+ $"[Exposure: {_bottomExposure}/{_normalExposure}s]" :
+ $"[Offset: {_positionZOffset}mm]")
+ + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- return sb.ToString();
- }
+ #region Properties
- public override string ToString()
+ public OperationLayerReHeightMethod Method
+ {
+ get => _method;
+ set
{
- var result = $"[Layer Count: {SelectedItem.LayerCount}] " +
- $"[Layer Height: {SelectedItem.LayerHeight}] " +
- $"[Exposure: {_bottomExposure}/{_normalExposure}s]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ if(!RaiseAndSetIfChanged(ref _method, value)) return;
+ RaisePropertyChanged(nameof(IsReHeightMethod));
+ RaisePropertyChanged(nameof(IsOffsetPositionZMethod));
}
- #endregion
+ }
- #region Properties
+ public bool IsReHeightMethod => _method is OperationLayerReHeightMethod.ReHeight;
+ public bool IsOffsetPositionZMethod => _method is OperationLayerReHeightMethod.OffsetPositionZ;
- public OperationLayerReHeightItem[] Presets { get; set; }
+ public decimal PositionZOffset
+ {
+ get => _positionZOffset;
+ set => RaiseAndSetIfChanged(ref _positionZOffset, value);
+ }
+
+ public OperationLayerReHeightItem[] Presets { get; set; } = null!;
- public OperationLayerReHeightItem SelectedItem
+ public OperationLayerReHeightItem? SelectedItem
+ {
+ get => _selectedItem;
+ set
{
- get => _selectedItem;
- set
- {
- if(!RaiseAndSetIfChanged(ref _selectedItem, value)) return;
- RaisePropertyChanged(nameof(CanAntiAliasing));
- }
+ if(!RaiseAndSetIfChanged(ref _selectedItem, value)) return;
+ RaisePropertyChanged(nameof(CanAntiAliasing));
}
+ }
- public bool CanAntiAliasing => _selectedItem?.IsMultiply ?? false;
+ public bool CanAntiAliasing => _selectedItem?.IsMultiply ?? false;
- public OperationLayerReHeightAntiAliasingType AntiAliasingType
- {
- get => _antiAliasingType;
- set => RaiseAndSetIfChanged(ref _antiAliasingType, value);
- }
+ public OperationLayerReHeightAntiAliasingType AntiAliasingType
+ {
+ get => _antiAliasingType;
+ set => RaiseAndSetIfChanged(ref _antiAliasingType, value);
+ }
+
+ public decimal BottomExposure
+ {
+ get => _bottomExposure;
+ set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ }
+
+ public decimal NormalExposure
+ {
+ get => _normalExposure;
+ set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
+ }
- public decimal BottomExposure
+
+ public static OperationLayerReHeightItem[] GetItems(uint layerCount, decimal layerHeight)
+ {
+ var list = new List<OperationLayerReHeightItem>();
+ for (byte i = 2; i < 255; i++) // Go lower heights
{
- get => _bottomExposure;
- set => RaiseAndSetIfChanged(ref _bottomExposure, Math.Round(value, 2));
+ if (layerHeight / i < Layer.MinimumHeight) break;
+ if ((layerCount * (decimal)i).DecimalDigits() > 0) continue; // Cant multiply layers, no half layers!
+ if ((layerHeight / i).DecimalDigits() > Layer.HeightPrecision) continue; // Cant divide height, more than 3 digits
+
+ var item = new OperationLayerReHeightItem(false, i, Layer.RoundHeight(layerHeight / i), layerCount * i);
+ list.Add(item);
}
- public decimal NormalExposure
+ for (byte i = 2; i < 255; i++) // Go higher heights
{
- get => _normalExposure;
- set => RaiseAndSetIfChanged(ref _normalExposure, Math.Round(value, 2));
- }
+ if (layerHeight * i > Layer.MaximumHeight) break;
+ if ((layerCount / (decimal)i).DecimalDigits() > 0) continue; // Cant divide layers, no half layers!
+ if ((layerHeight * i).DecimalDigits() > Layer.HeightPrecision) continue; // Cant multiply height, more than 3 digits
+ var item = new OperationLayerReHeightItem(true, i, Layer.RoundHeight(layerHeight * i), layerCount / i);
+ list.Add(item);
+ }
- public static OperationLayerReHeightItem[] GetItems(uint layerCount, decimal layerHeight)
- {
- var list = new List<OperationLayerReHeightItem>();
- for (byte i = 2; i < 255; i++) // Go lower heights
- {
- if (layerHeight / i < Layer.MinimumHeight) break;
- if ((layerCount * (decimal)i).DecimalDigits() > 0) continue; // Cant multiply layers, no half layers!
- if ((layerHeight / i).DecimalDigits() > Layer.HeightPrecision) continue; // Cant divide height, more than 3 digits
+ return list.ToArray();
+ }
+ #endregion
- var item = new OperationLayerReHeightItem(false, i, Layer.RoundHeight(layerHeight / i), layerCount * i);
- list.Add(item);
- }
+ #region Constructor
- for (byte i = 2; i < 255; i++) // Go higher heights
- {
- if (layerHeight * i > Layer.MaximumHeight) break;
- if ((layerCount / (decimal)i).DecimalDigits() > 0) continue; // Cant divide layers, no half layers!
- if ((layerHeight * i).DecimalDigits() > Layer.HeightPrecision) continue; // Cant multiply height, more than 3 digits
+ public OperationLayerReHeight() { }
- var item = new OperationLayerReHeightItem(true, i, Layer.RoundHeight(layerHeight * i), layerCount / i);
- list.Add(item);
- }
+ public OperationLayerReHeight(FileFormat slicerFile) : base(slicerFile)
+ { }
- return list.ToArray();
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ Presets = GetItems(SlicerFile.LayerCount, (decimal)SlicerFile.LayerHeight);
+ if (Presets.Length > 0)
+ {
+ _selectedItem = Presets[0];
}
- #endregion
- #region Constructor
+ if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
+ if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ }
- public OperationLayerReHeight() { }
+ #endregion
- public OperationLayerReHeight(FileFormat slicerFile) : base(slicerFile)
- { }
+ #region Subclasses
+ public class OperationLayerReHeightItem
+ {
+ public bool IsMultiply { get; }
+ public bool IsDivision => !IsMultiply;
+ public byte Modifier { get; }
+ public decimal LayerHeight { get; }
+ public uint LayerCount { get; }
- public override void InitWithSlicerFile()
+ public OperationLayerReHeightItem(bool isMultiply, byte modifier, decimal layerHeight, uint layerCount)
{
- base.InitWithSlicerFile();
- Presets = GetItems(SlicerFile.LayerCount, (decimal)SlicerFile.LayerHeight);
- if (Presets is not null && Presets.Length > 0)
- {
- _selectedItem = Presets[0];
- }
-
- if (_bottomExposure <= 0) _bottomExposure = (decimal)SlicerFile.BottomExposureTime;
- if (_normalExposure <= 0) _normalExposure = (decimal)SlicerFile.ExposureTime;
+ IsMultiply = isMultiply;
+ Modifier = modifier;
+ LayerHeight = layerHeight;
+ LayerCount = layerCount;
}
- #endregion
-
- #region Subclasses
- public class OperationLayerReHeightItem
+ public override string ToString()
{
- public bool IsMultiply { get; }
- public bool IsDivision => !IsMultiply;
- public byte Modifier { get; }
- public decimal LayerHeight { get; }
- public uint LayerCount { get; }
-
- public OperationLayerReHeightItem(bool isMultiply, byte modifier, decimal layerHeight, uint layerCount)
- {
- IsMultiply = isMultiply;
- Modifier = modifier;
- LayerHeight = layerHeight;
- LayerCount = layerCount;
- }
-
- public override string ToString()
- {
- return (IsMultiply ? 'x' : '÷') + $" {Modifier} → {LayerCount} layers at {LayerHeight}mm";
- }
+ return (IsMultiply ? 'x' : '÷') + $" {Modifier} → {LayerCount} layers at {LayerHeight}mm";
}
- #endregion
+ }
+ #endregion
- #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
+ #region Methods
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (_method == OperationLayerReHeightMethod.ReHeight)
{
- progress.ItemCount = _selectedItem.LayerCount;
+ progress.ItemCount = _selectedItem!.LayerCount;
var layers = new Layer[_selectedItem.LayerCount];
@@ -246,7 +323,7 @@ namespace UVtools.Core.Operations
if (progress.Token.IsCancellationRequested) return;
var oldLayer = SlicerFile[layerIndex];
using var matSum = oldLayer.LayerMat;
- Mat matXorSum = null;
+ Mat? matXorSum = null;
using Mat aaAverageSum = new();
if (_antiAliasingType == OperationLayerReHeightAntiAliasingType.Average)
@@ -254,9 +331,9 @@ namespace UVtools.Core.Operations
matSum.ConvertTo(aaAverageSum, DepthType.Cv16U);
}
- for (byte i = 1; i < SelectedItem.Modifier; i++)
+ for (byte i = 1; i < _selectedItem.Modifier; i++)
{
- using var nextMat = SlicerFile[layerIndex+i].LayerMat;
+ using var nextMat = SlicerFile[layerIndex + i].LayerMat;
switch (_antiAliasingType)
{
@@ -270,7 +347,8 @@ namespace UVtools.Core.Operations
//CvInvoke.Threshold(previousMat, previousMat, 127, 255, ThresholdType.Binary);
//CvInvoke.Threshold(nextMat, nextMat, 127, 255, ThresholdType.Binary);
CvInvoke.BitwiseXor(previousMat, nextMat, matXor);
- matXor.SetTo(new MCvScalar((byte)(byte.MaxValue / _selectedItem.Modifier)), matXor);
+ matXor.SetTo(new MCvScalar((byte) (byte.MaxValue / _selectedItem.Modifier)),
+ matXor);
if (matXorSum is null)
{
matXorSum = matXor.Clone();
@@ -280,6 +358,7 @@ namespace UVtools.Core.Operations
CvInvoke.Add(matXorSum, matXorSum, matXorSum);
CvInvoke.Add(matXorSum, matXor, matXorSum);
}
+
break;
}
case OperationLayerReHeightAntiAliasingType.Average:
@@ -297,7 +376,7 @@ namespace UVtools.Core.Operations
CvInvoke.Add(matSum, matXorSum, matSum);
CvInvoke.PyrDown(matSum, matSum);
CvInvoke.PyrUp(matSum, matSum);
- matXorSum.Dispose();
+ matXorSum!.Dispose();
break;
case OperationLayerReHeightAntiAliasingType.Average:
aaAverageSum.ConvertTo(matSum, DepthType.Cv8U, 1.0 / _selectedItem.Modifier);
@@ -320,15 +399,22 @@ namespace UVtools.Core.Operations
SlicerFile.SuppressRebuildPropertiesWork(() =>
{
- SlicerFile.LayerHeight = (float)SelectedItem.LayerHeight;
- SlicerFile.BottomExposureTime = (float)_bottomExposure;
- SlicerFile.ExposureTime = (float)_normalExposure;
- SlicerFile.LayerManager.Layers = layers;
+ SlicerFile.LayerHeight = (float)_selectedItem!.LayerHeight;
+ SlicerFile.BottomExposureTime = (float) _bottomExposure;
+ SlicerFile.ExposureTime = (float) _normalExposure;
+ SlicerFile.Layers = layers;
}, true);
-
-
- return !progress.Token.IsCancellationRequested;
}
- #endregion
+ else if (_method == OperationLayerReHeightMethod.OffsetPositionZ)
+ {
+ for (var layerIndex = LayerIndexStart; layerIndex < LayerIndexEnd; layerIndex++)
+ {
+ SlicerFile[layerIndex].PositionZ += (float)_positionZOffset;
+ progress++;
+ }
+ }
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerRemove.cs b/UVtools.Core/Operations/OperationLayerRemove.cs
index 77824c0..c2563e2 100644
--- a/UVtools.Core/Operations/OperationLayerRemove.cs
+++ b/UVtools.Core/Operations/OperationLayerRemove.cs
@@ -10,135 +10,205 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using UVtools.Core.FileFormats;
-using UVtools.Core.Objects;
+using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationLayerRemove : Operation
{
- [Serializable]
- public sealed class OperationLayerRemove : Operation
- {
- #region Overrides
+ #region Members
+ private bool _useThreshold;
+ private uint _pixelThreshold;
+ #endregion
+
+ #region Overrides
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
+ public override bool CanROI => false;
+ public override bool PassActualLayerIndex => true;
+ public override string IconClass => "mdi-layers-remove";
+ public override string Title => "Remove layers";
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
- public override bool CanROI => false;
- public override bool PassActualLayerIndex => true;
+ public override string Description =>
+ "Remove layers in a given range.";
- public override string Title => "Remove layers";
+ public override string ConfirmationText =>
+ $"remove layers {LayerIndexStart} through {LayerIndexEnd}"+
+ (_useThreshold ? $" with an pixel threshold of {_pixelThreshold}px" : string.Empty)
+ +"?";
- public override string Description =>
- "Remove Layers in a given range.";
+ public override string ProgressTitle =>
+ $"Removing layers {LayerIndexStart} through {LayerIndexEnd}" +
+ (_useThreshold ? $" with an pixel threshold of {_pixelThreshold}px" : string.Empty);
- public override string ConfirmationText =>
- $"remove layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ProgressAction => "Removed layers";
+
+ public override bool CanCancel => false;
+
+ public override bool CanHaveProfiles => false;
+
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ProgressTitle =>
- $"Removing layers {LayerIndexStart} through {LayerIndexEnd}";
+ var layersToRemove = LayerRemoveCount;
+ if (LayerRemoveCount == 0)
+ {
+ sb.AppendLine("The used values will not remove any layer, please adjust.");
+ }
+
+ if (layersToRemove == SlicerFile.LayerCount)
+ {
+ sb.AppendLine("You can't remove all layers from the file. Keep at least one.");
+ }
+
+ return sb.ToString();
+ }
- public override string ProgressAction => "Removed layers";
+ #endregion
- public override bool CanCancel => false;
+ #region Properties
- public override bool CanHaveProfiles => false;
+ public bool UseThreshold
+ {
+ get => _useThreshold;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _useThreshold, value)) return;
+ RaisePropertyChanged(nameof(LayerRemoveCount));
+ }
+ }
- public override string ValidateInternally()
+ public uint PixelThreshold
+ {
+ get => _pixelThreshold;
+ set
{
- var sb = new StringBuilder();
+ if(!RaiseAndSetIfChanged(ref _pixelThreshold, value)) return;
+ RaisePropertyChanged(nameof(LayerRemoveCount));
+ }
+ }
- if (LayerRangeCount == SlicerFile.LayerCount)
+ public uint LayerRemoveCount
+ {
+ get
+ {
+ if (!_useThreshold) return LayerRangeCount;
+ uint layers = 0;
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
{
- sb.AppendLine("You can't remove all layers from the file. Keep at least one.");
+ if (SlicerFile[layerIndex].NonZeroPixelCount > _pixelThreshold) continue;
+ layers++;
}
- return sb.ToString();
+ return layers;
}
+ }
- #endregion
+ #endregion
- #region Properties
+ #region Constructor
+ public OperationLayerRemove() { }
- #endregion
+ public OperationLayerRemove(FileFormat slicerFile) : base(slicerFile) { }
- #region Constructor
+ #endregion
- public OperationLayerRemove() { }
+ #region Equality
+ private bool Equals(OperationLayerRemove other)
+ {
+ return _useThreshold == other._useThreshold && _pixelThreshold == other._pixelThreshold;
+ }
- public OperationLayerRemove(FileFormat slicerFile) : base(slicerFile) { }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationLayerRemove other && Equals(other);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_useThreshold, _pixelThreshold);
+ }
+ #endregion
- #region Methods
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.CanCancel = false;
+ var layersRemove = new List<uint>();
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
{
- progress.CanCancel = false;
- var layersRemove = new List<uint>();
- for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
- {
- layersRemove.Add(layerIndex);
- }
-
- return RemoveLayers(SlicerFile, layersRemove, progress);
+ if(_useThreshold && SlicerFile[layerIndex].NonZeroPixelCount > _pixelThreshold) continue;
+ layersRemove.Add(layerIndex);
}
- public static bool RemoveLayers(FileFormat slicerFile, IEnumerable<uint> layersRemove, OperationProgress progress = null)
- {
- if (!layersRemove.Any()) return false;
+ return RemoveLayers(SlicerFile, layersRemove, progress);
+ }
- progress ??= new OperationProgress(false);
+ public static bool RemoveLayers(FileFormat slicerFile, IEnumerable<uint> layersRemove, OperationProgress? progress = null)
+ {
+ if (!layersRemove.Any()) return false;
- progress.Reset("Removed layers", (uint)layersRemove.Count());
+ progress ??= new OperationProgress(false);
- foreach (var layerIndex in layersRemove)
- {
- slicerFile[layerIndex] = null;
- progress++;
- }
+ progress.Reset("Removed layers", (uint)layersRemove.Count());
- slicerFile.LayerManager.RemoveNulls();
+ var layers = slicerFile.ToList();
+ int removedBottomLayers = 0;
+ //uint lastRemovedBottomLayerIndex = 0;
- /*var oldLayers = slicerFile.LayerManager.Layers;
- var layerHeight = slicerFile.LayerHeight;
+ var lastBottomLayer = slicerFile.LastBottomLayer;
- var layers = new Layer[oldLayers.Length - layersRemove.Count];
+ // Register bottom layers
+ if (slicerFile.BottomLayerCount > 0)
+ {
+ var layersRemoveAsc = layersRemove.OrderBy(index => index);
+ foreach (var layerIndex in layersRemoveAsc)
+ {
+ if (!slicerFile[layerIndex].IsBottomLayer) continue;
+ removedBottomLayers++;
+ //lastRemovedBottomLayerIndex = layerIndex;
+ }
+ }
+
+ // Remove layers
+ var layersRemoveDesc = layersRemove.OrderByDescending(index => index);
+ foreach (var layerIndex in layersRemoveDesc)
+ {
+ layers.RemoveAt((int)layerIndex);
- // Re-set
- uint newLayerIndex = 0;
- for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++)
+ // Shift layer positions
+ var relativeZ = slicerFile[layerIndex].RelativePositionZ;
+ if (relativeZ <= 0) continue;
+ for (uint i = layerIndex + 1; i < slicerFile.LayerCount; i++)
{
- if (layersRemove.Contains(layerIndex)) continue;
- layers[newLayerIndex] = oldLayers[layerIndex];
- layers[newLayerIndex].Index = newLayerIndex;
+ slicerFile[i].PositionZ -= relativeZ;
+ }
+ progress++;
+ }
- // Re-Z
- float posZ = layerHeight;
- if (newLayerIndex > 0)
+ // Should never happen, still use this safe-check
+ if (slicerFile.LayerCount != layers.Count)
+ {
+ // Try to copy bottom parameters to shifted new bottom layers
+ if (removedBottomLayers > 0 && lastBottomLayer is not null)
+ {
+ var startIndex = (uint) Math.Max(lastBottomLayer.Index + 1, layersRemove.Count());
+ var endIndex = startIndex + removedBottomLayers;
+ var copyFromFromLayerIndex = (uint)Math.Max(0, (int)lastBottomLayer.Index);
+ for (var layerIndex = startIndex; layerIndex < endIndex && layerIndex < slicerFile.LayerCount; layerIndex++)
{
- if (oldLayers[layerIndex - 1].PositionZ == oldLayers[layerIndex].PositionZ)
- {
- posZ = layers[newLayerIndex - 1].PositionZ;
- }
- else
- {
- posZ = Layer.RoundHeight(layers[newLayerIndex - 1].PositionZ + layerHeight);
- }
+ slicerFile[copyFromFromLayerIndex].CopyParametersTo(slicerFile[layerIndex]);
}
-
- layers[newLayerIndex].PositionZ = posZ;
- layers[newLayerIndex].IsModified = true;
-
- newLayerIndex++;
- progress++;
}
-
- slicerFile.LayerManager.Layers = layers;*/
-
-
-
- return true;
+ slicerFile.SuppressRebuildPropertiesWork(() => slicerFile.Layers = layers.ToArray());
}
- #endregion
+
+ return true;
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLightBleedCompensation.cs b/UVtools.Core/Operations/OperationLightBleedCompensation.cs
index dd86654..ac0d69d 100644
--- a/UVtools.Core/Operations/OperationLightBleedCompensation.cs
+++ b/UVtools.Core/Operations/OperationLightBleedCompensation.cs
@@ -6,272 +6,271 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationLightBleedCompensation : Operation
{
- [Serializable]
- public class OperationLightBleedCompensation : Operation
- {
- #region Enums
+ #region Enums
- public enum LightBleedCompensationLookupMode : byte
- {
- [Description("Previous: Look for sequential pixels relative to the previous layers")]
- Previous,
+ public enum LightBleedCompensationLookupMode : byte
+ {
+ [Description("Previous: Look for sequential pixels relative to the previous layers")]
+ Previous,
- [Description("Next: Look for sequential pixels relative to the next layers")]
- Next,
+ [Description("Next: Look for sequential pixels relative to the next layers")]
+ Next,
- [Description("Both: Look for sequential pixels relative to the previous and next layers")]
- Both
- }
+ [Description("Both: Look for sequential pixels relative to the previous and next layers")]
+ Both
+ }
- #endregion
+ #endregion
- #region Members
+ #region Members
- private LightBleedCompensationLookupMode _lookupMode = LightBleedCompensationLookupMode.Next;
- private string _dimBy = "25,15,10,5";
+ private LightBleedCompensationLookupMode _lookupMode = LightBleedCompensationLookupMode.Next;
+ private string _dimBy = "25,15,10,5";
- #endregion
+ #endregion
- #region Overrides
+ #region Overrides
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
+ public override string IconClass => "mdi-lightbulb-on";
+ public override string Title => "Light bleed compensation";
+ public override string Description =>
+ "Compensate the over-curing and light bleed from clear resins by dimming the sequential pixels.\n" +
+ "Note: You need to find the optimal minimum pixel brightness that such resin can print in order to optimize this process.\n" +
+ "With more translucent resins you can go with lower brightness but stick to a limit that can form the layer without loss." +
+ " Tiny details can be lost when using low brightness level.\n" +
+ "After apply a light bleed compensation, do not apply or re-run this tool over.";
- public override string Title => "Light bleed compensation";
- public override string Description =>
- "Compensate the over-curing and light bleed from clear resins by dimming the sequential pixels.\n" +
- "Note: You need to find the optimal minimum pixel brightness that such resin can print in order to optimize this process.\n" +
- "With more translucent resins you can go with lower brightness but stick to a limit that can form the layer without loss." +
- " Tiny details can be lost when using low brightness level.\n" +
- "After apply a light bleed compensation, do not apply or re-run this tool over.";
+ public override string ConfirmationText =>
+ $"compensate layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ConfirmationText =>
- $"compensate layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ProgressTitle =>
+ $"Compensate layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressTitle =>
- $"Compensate layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressAction => "Compensated layers";
- public override string ProgressAction => "Compensated layers";
+ public override string? ValidateInternally()
+ {
+ StringBuilder sb = new();
- public override string ValidateInternally()
+ if (DimByArray.Length == 0)
{
- StringBuilder sb = new();
-
- if (DimByArray.Length == 0)
- {
- sb.AppendLine($"The dim levels are invalid or not set.");
- }
-
- if (MaximumSubtraction >= byte.MaxValue)
- {
- sb.AppendLine($"The sum of dim levels are producing black pixels.");
- }
-
- return sb.ToString();
+ sb.AppendLine($"The dim levels are invalid or not set.");
}
- public override string ToString()
+ if (MaximumSubtraction >= byte.MaxValue)
{
- var result = $"[Lookup: {_lookupMode}]" +
- $" [Dim by: {_dimBy}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine($"The sum of dim levels are producing black pixels.");
}
- #endregion
- #region Constructor
+ return sb.ToString();
+ }
+
+ public override string ToString()
+ {
+ var result = $"[Lookup: {_lookupMode}]" +
+ $" [Dim by: {_dimBy}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- public OperationLightBleedCompensation() { }
+ #region Constructor
- public OperationLightBleedCompensation(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationLightBleedCompensation() { }
- #endregion
+ public OperationLightBleedCompensation(FileFormat slicerFile) : base(slicerFile) { }
- #region Properties
+ #endregion
- public LightBleedCompensationLookupMode LookupMode
- {
- get => _lookupMode;
- set => RaiseAndSetIfChanged(ref _lookupMode, value);
- }
+ #region Properties
+
+ public LightBleedCompensationLookupMode LookupMode
+ {
+ get => _lookupMode;
+ set => RaiseAndSetIfChanged(ref _lookupMode, value);
+ }
- public string DimBy
+ public string DimBy
+ {
+ get => _dimBy;
+ set
{
- get => _dimBy;
- set
- {
- if(!RaiseAndSetIfChanged(ref _dimBy, value)) return;
- RaisePropertyChanged(nameof(MinimumBrightness));
- RaisePropertyChanged(nameof(MaximumSubtraction));
- }
+ if(!RaiseAndSetIfChanged(ref _dimBy, value)) return;
+ RaisePropertyChanged(nameof(MinimumBrightness));
+ RaisePropertyChanged(nameof(MaximumSubtraction));
}
+ }
- public int MinimumBrightness => 255 - MaximumSubtraction;
- public int MaximumSubtraction => DimByArray.Aggregate(0, (current, dim) => current + dim);
+ public int MinimumBrightness => 255 - MaximumSubtraction;
+ public int MaximumSubtraction => DimByArray.Aggregate(0, (current, dim) => current + dim);
- public byte[] DimByArray
+ public byte[] DimByArray
+ {
+ get
{
- get
+ List<byte> levels = new();
+ var split = _dimBy.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var str in split)
{
- List<byte> levels = new();
- var split = _dimBy.Split(',', StringSplitOptions.TrimEntries);
- foreach (var str in split)
- {
- if (!byte.TryParse(str, out var brightness)) continue;
- if (brightness is byte.MinValue or byte.MaxValue) continue;
- levels.Add(brightness);
- }
-
- return levels.ToArray();
+ if (!byte.TryParse(str, out var brightness)) continue;
+ if (brightness is byte.MinValue or byte.MaxValue) continue;
+ levels.Add(brightness);
}
+
+ return levels.ToArray();
}
+ }
- public MCvScalar[] DimByMCvScalar
+ public MCvScalar[] DimByMCvScalar
+ {
+ get
{
- get
+ List<MCvScalar> levels = new();
+ var split = _dimBy.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var str in split)
{
- List<MCvScalar> levels = new();
- var split = _dimBy.Split(',', StringSplitOptions.TrimEntries);
- foreach (var str in split)
- {
- if (!byte.TryParse(str, out var brightness)) continue;
- if (brightness is byte.MinValue or byte.MaxValue) continue;
- levels.Add(new MCvScalar(brightness));
- }
-
- return levels.ToArray();
+ if (!byte.TryParse(str, out var brightness)) continue;
+ if (brightness is byte.MinValue or byte.MaxValue) continue;
+ levels.Add(new MCvScalar(brightness));
}
+
+ return levels.ToArray();
}
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationLightBleedCompensation other)
- {
- return _lookupMode == other._lookupMode && _dimBy == other._dimBy;
- }
+ protected bool Equals(OperationLightBleedCompensation other)
+ {
+ return _lookupMode == other._lookupMode && _dimBy == other._dimBy;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationLightBleedCompensation) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationLightBleedCompensation) obj);
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine((int) _lookupMode, _dimBy);
- }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine((int) _lookupMode, _dimBy);
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- /// <summary>
- /// Get the cached dim mat's
- /// </summary>
- /// <returns></returns>
- public Mat[] GetDimMats()
+ /// <summary>
+ /// Get the cached dim mat's
+ /// </summary>
+ /// <returns></returns>
+ public Mat[] GetDimMats()
+ {
+ var dimLevels = DimByMCvScalar;
+ if (dimLevels.Length == 0) return Array.Empty<Mat>();
+ var mats = new Mat[dimLevels.Length];
+ var matSize = GetRoiSizeOrDefault();
+ for (var i = 0; i < mats.Length; i++)
{
- var dimLevels = DimByMCvScalar;
- if (dimLevels.Length == 0) return Array.Empty<Mat>();
- var mats = new Mat[dimLevels.Length];
- var matSize = GetRoiSizeOrDefault();
- for (var i = 0; i < mats.Length; i++)
- {
- mats[i] = EmguExtensions.InitMat(matSize, dimLevels[i]);
- }
-
- return mats;
+ mats[i] = EmguExtensions.InitMat(matSize, dimLevels[i]);
}
- protected override bool ExecuteInternally(OperationProgress progress)
+ return mats;
+ }
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var dimMats = GetDimMats();
+ if (dimMats.Length == 0) return false;
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- var dimMats = GetDimMats();
- if (dimMats.Length == 0) return false;
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
+ if (progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
- var layer = SlicerFile[layerIndex];
- using var mat = layer.LayerMat;
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ var layer = SlicerFile[layerIndex];
+ using var mat = layer.LayerMat;
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
- for (byte i = 0; i < dimMats.Length; i++)
- {
- Mat mask = null;
- Mat previousMat = null;
- Mat previousMatRoi = null;
- Mat nextMat = null;
- Mat nextMatRoi = null;
+ for (byte i = 0; i < dimMats.Length; i++)
+ {
+ Mat? mask = null;
+ Mat? previousMat = null;
+ Mat? previousMatRoi = null;
+ Mat? nextMat = null;
+ Mat? nextMatRoi = null;
- if (_lookupMode is LightBleedCompensationLookupMode.Previous or LightBleedCompensationLookupMode.Both)
+ if (_lookupMode is LightBleedCompensationLookupMode.Previous or LightBleedCompensationLookupMode.Both)
+ {
+ int layerPreviousIndex = (int)layerIndex - i - 1;
+ if (layerPreviousIndex >= LayerIndexStart)
{
- int layerPreviousIndex = (int)layerIndex - i - 1;
- if (layerPreviousIndex >= LayerIndexStart)
- {
- previousMat = SlicerFile[layerPreviousIndex].LayerMat;
- mask = previousMatRoi = GetRoiOrDefault(previousMat);
- }
+ previousMat = SlicerFile[layerPreviousIndex].LayerMat;
+ mask = previousMatRoi = GetRoiOrDefault(previousMat);
}
- if (_lookupMode is LightBleedCompensationLookupMode.Next or LightBleedCompensationLookupMode.Both)
+ }
+ if (_lookupMode is LightBleedCompensationLookupMode.Next or LightBleedCompensationLookupMode.Both)
+ {
+ uint layerIndexNext = (uint) (layerIndex + i + 1);
+ if (layerIndexNext <= LayerIndexEnd)
{
- uint layerIndexNext = (uint) (layerIndex + i + 1);
- if (layerIndexNext <= LayerIndexEnd)
- {
- nextMat = SlicerFile[layerIndexNext].LayerMat;
- mask = nextMatRoi = GetRoiOrDefault(nextMat);
- }
+ nextMat = SlicerFile[layerIndexNext].LayerMat;
+ mask = nextMatRoi = GetRoiOrDefault(nextMat);
}
+ }
- if (previousMat is null && nextMat is null) break; // Nothing more to do
- if (previousMat is not null && nextMat is not null) // both, need to merge previous with next layer
- {
- CvInvoke.Add(previousMatRoi, nextMatRoi, previousMatRoi);
- mask = previousMatRoi;
- }
-
- CvInvoke.Subtract(target, dimMats[i], target, mask);
-
- previousMat?.Dispose();
- nextMat?.Dispose();
+ if (previousMat is null && nextMat is null) break; // Nothing more to do
+ if (previousMat is not null && nextMat is not null) // both, need to merge previous with next layer
+ {
+ CvInvoke.Add(previousMatRoi, nextMatRoi, previousMatRoi);
+ mask = previousMatRoi;
}
- // Apply the results only to the selected masked area, if user selected one
- ApplyMask(original, target);
+ CvInvoke.Subtract(target, dimMats[i], target, mask);
+
+ previousMat?.Dispose();
+ nextMat?.Dispose();
+ }
- // Set current layer image with the modified mat we just manipulated
- layer.LayerMat = mat;
+ // Apply the results only to the selected masked area, if user selected one
+ ApplyMask(original, target);
- // Increment progress bar by 1
- progress.LockAndIncrement();
- });
+ // Set current layer image with the modified mat we just manipulated
+ layer.LayerMat = mat;
- foreach (var dimMat in dimMats)
- {
- dimMat.Dispose();
- }
+ // Increment progress bar by 1
+ progress.LockAndIncrement();
+ });
- // return true if not cancelled by user
- return !progress.Token.IsCancellationRequested;
+ foreach (var dimMat in dimMats)
+ {
+ dimMat.Dispose();
}
- #endregion
+ // return true if not cancelled by user
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs
index 8b35a39..04ea172 100644
--- a/UVtools.Core/Operations/OperationMask.cs
+++ b/UVtools.Core/Operations/OperationMask.cs
@@ -6,96 +6,94 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.FileFormats;
-using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationMask : Operation
{
- [Serializable]
- public class OperationMask : Operation
- {
- #region Overrides
- public override string Title => "Mask";
- public override string Description =>
- "Mask the intensity of the LCD output using a greyscale input image.\n\n" +
- "Useful to correct LCD light uniformity for a specific printer.\n\n" +
- "NOTE: This operation should be run only after repairs and other transformations. The provided" +
- "input mask image must match the output resolution of the target printer.";
+ #region Overrides
+ public override string IconClass => "fas fa-mask";
+ public override string Title => "Mask";
+ public override string Description =>
+ "Mask the intensity of the LCD output using a greyscale input image.\n\n" +
+ "Useful to correct LCD light uniformity for a specific printer.\n\n" +
+ "NOTE: This operation should be run only after repairs and other transformations. The provided" +
+ "input mask image must match the output resolution of the target printer.";
- public override string ConfirmationText =>
- $"mask layers from {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ConfirmationText =>
+ $"mask layers from {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressTitle =>
- $"Masking layers from {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Masking layers from {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => "Masked layers";
+ public override string ProgressAction => "Masked layers";
- public override bool CanHaveProfiles => false;
+ public override bool CanHaveProfiles => false;
- public override string ValidateInternally()
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ if (!HaveInputMask)
{
- var sb = new StringBuilder();
- if (!HaveInputMask)
- {
- sb.AppendLine("The mask can not be empty.");
- }
-
- return sb.ToString();
+ sb.AppendLine("The mask can not be empty.");
}
- #endregion
- #region Properties
- [XmlIgnore]
- public Mat Mask { get; set; }
+ return sb.ToString();
+ }
+ #endregion
- public bool HaveInputMask => Mask is not null;
- #endregion
+ #region Properties
+ [XmlIgnore]
+ public Mat? Mask { get; set; }
- #region Constructor
+ public bool HaveInputMask => Mask is not null;
+ #endregion
- public OperationMask() { }
+ #region Constructor
- public OperationMask(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationMask() { }
- #endregion
+ public OperationMask(FileFormat slicerFile) : base(slicerFile) { }
- #region Methods
+ #endregion
- public void InvertMask()
- {
- if (!HaveInputMask) return;
- CvInvoke.BitwiseNot(Mask, Mask);
- }
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat);
- SlicerFile[layerIndex].LayerMat = mat;
- progress.LockAndIncrement();
- });
-
- return !progress.Token.IsCancellationRequested;
- }
+ public void InvertMask()
+ {
+ if (!HaveInputMask) return;
+ CvInvoke.BitwiseNot(Mask, Mask);
+ }
- public override bool Execute(Mat mat, params object[] arguments)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- var target = GetRoiOrDefault(mat);
- using var mask = GetMask(mat);
- if (Mask.Size != target.Size) return false;
- CvInvoke.BitwiseAnd(target, Mask, target, mask);
- return true;
- }
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ SlicerFile[layerIndex].LayerMat = mat;
+ progress.LockAndIncrement();
+ });
+
+ return !progress.Token.IsCancellationRequested;
+ }
- #endregion
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ var target = GetRoiOrDefault(mat);
+ using var mask = GetMask(mat);
+ if (Mask!.Size != target.Size) return false;
+ CvInvoke.BitwiseAnd(target, Mask, target, mask);
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs
index cdf70c0..b666545 100644
--- a/UVtools.Core/Operations/OperationMorph.cs
+++ b/UVtools.Core/Operations/OperationMorph.cs
@@ -6,235 +6,231 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
-using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationMorph : Operation
{
- [Serializable]
- public sealed class OperationMorph : Operation
+ #region Enums
+ public enum MorphOperations
{
- #region Enums
- public enum MorphOperations
- {
- [Description("Erode: Contracts the boundaries within the object")]
- Erode = MorphOp.Erode,
+ [Description("Erode: Contracts the boundaries within the object")]
+ Erode = MorphOp.Erode,
- [Description("Dilate: Expands the boundaries within the object")]
- Dilate = MorphOp.Dilate,
+ [Description("Dilate: Expands the boundaries within the object")]
+ Dilate = MorphOp.Dilate,
- [Description("Noise removal: Removes small isolated pixels (Erode -> Dilate)")]
- Open = MorphOp.Open,
+ [Description("Noise removal: Removes small isolated pixels (Erode -> Dilate)")]
+ Open = MorphOp.Open,
- [Description("Gap closing: Closes small holes inside the objects (Dilate -> Erode)")]
- Close = MorphOp.Close,
+ [Description("Gap closing: Closes small holes inside the objects (Dilate -> Erode)")]
+ Close = MorphOp.Close,
- [Description("Gradient: Removes the interior areas of objects and expand the boundaries by half (Dilate - Erode)")]
- Gradient = MorphOp.Gradient,
-
- // White top-hat transform: This is the difference between the image and its opening.
- [Description("White tophat: Removes small isolated pixels and only return its affected pixels (Image - Noise removal)")]
- WhiteTopHat = MorphOp.Tophat,
+ [Description("Gradient: Removes the interior areas of objects and expand the boundaries by half (Dilate - Erode)")]
+ Gradient = MorphOp.Gradient,
- // Black top-hat transform: Difference between the closing and the input image.
- [Description("Black tophat: Closes small holes inside the objects and only return its affected pixels (Gap closing - Image)")]
- BlackTopHat = MorphOp.Blackhat,
+ // White top-hat transform: This is the difference between the image and its opening.
+ [Description("White tophat: Removes small isolated pixels and only return its affected pixels (Image - Noise removal)")]
+ WhiteTopHat = MorphOp.Tophat,
- [Description("Hit or miss: Finds pixels in a given kernel pattern")]
- HitMiss = MorphOp.HitMiss,
+ // Black top-hat transform: Difference between the closing and the input image.
+ [Description("Black tophat: Closes small holes inside the objects and only return its affected pixels (Gap closing - Image)")]
+ BlackTopHat = MorphOp.Blackhat,
- [Description("Offset crop: Like erode but discards the outer pixels")]
- OffsetCrop,
+ [Description("Hit or miss: Finds pixels in a given kernel pattern")]
+ HitMiss = MorphOp.HitMiss,
- //[Description("Isolate features: Isolates thin features and discards other pixels (Opening - )")]
- //IsolateFeatures,
- }
- #endregion
+ [Description("Offset crop: Like erode but discards the outer pixels")]
+ OffsetCrop,
- #region Members
- private MorphOperations _morphOperation = MorphOperations.Erode;
- private uint _iterationsStart = 1;
- private uint _iterationsEnd = 1;
- private bool _chamfer;
- #endregion
+ //[Description("Isolate features: Isolates thin features and discards other pixels (Opening - )")]
+ //IsolateFeatures,
+ }
+ #endregion
- #region Overrides
+ #region Members
+ private MorphOperations _morphOperation = MorphOperations.Erode;
+ private uint _iterationsStart = 1;
+ private uint _iterationsEnd = 1;
+ private bool _chamfer;
+ #endregion
- public override string Title => "Morph";
- public override string Description =>
- $"Morph Model - " +
- $"Various operations that can be used to change the physical structure of the model or individual layers.";
- public override string ConfirmationText =>
- $"morph model layers {LayerIndexStart} through {LayerIndexEnd}?";
+ #region Overrides
+ public override string IconClass => "fas fa-dharmachakra";
+ public override string Title => "Morph";
+ public override string Description =>
+ $"Morph Model - " +
+ $"Various operations that can be used to change the physical structure of the model or individual layers.";
+ public override string ConfirmationText =>
+ $"morph model layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressTitle =>
- $"Morphing layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Morphing layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => "Morphed layers";
+ public override string ProgressAction => "Morphed layers";
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public MorphOp MorphOperationOpenCV
+ public MorphOp MorphOperationOpenCV
+ {
+ get
{
- get
+ return _morphOperation switch
{
- switch (_morphOperation)
- {
- case MorphOperations.OffsetCrop:
- return MorphOp.Erode;
- default:
- return (MorphOp)_morphOperation;
- }
- }
+ MorphOperations.OffsetCrop => MorphOp.Erode,
+ _ => (MorphOp) _morphOperation
+ };
}
+ }
- public MorphOperations MorphOperation
- {
- get => _morphOperation;
- set => RaiseAndSetIfChanged(ref _morphOperation, value);
- }
+ public MorphOperations MorphOperation
+ {
+ get => _morphOperation;
+ set => RaiseAndSetIfChanged(ref _morphOperation, value);
+ }
- public uint Iterations
- {
- get => IterationsStart;
- set => IterationsStart = IterationsEnd = value;
- }
+ public uint Iterations
+ {
+ get => IterationsStart;
+ set => IterationsStart = IterationsEnd = value;
+ }
- public uint IterationsStart
- {
- get => _iterationsStart;
- set => RaiseAndSetIfChanged(ref _iterationsStart, value);
- }
+ public uint IterationsStart
+ {
+ get => _iterationsStart;
+ set => RaiseAndSetIfChanged(ref _iterationsStart, value);
+ }
- public uint IterationsEnd
- {
- get => _iterationsEnd;
- set => RaiseAndSetIfChanged(ref _iterationsEnd, value);
- }
+ public uint IterationsEnd
+ {
+ get => _iterationsEnd;
+ set => RaiseAndSetIfChanged(ref _iterationsEnd, value);
+ }
- public bool Chamfer
- {
- get => _chamfer;
- set => RaiseAndSetIfChanged(ref _chamfer, value);
- }
+ public bool Chamfer
+ {
+ get => _chamfer;
+ set => RaiseAndSetIfChanged(ref _chamfer, value);
+ }
- public KernelConfiguration Kernel { get; set; } = new();
+ public KernelConfiguration Kernel { get; set; } = new();
- public override string ToString()
- {
- var result = $"[{_morphOperation}] [Iterations: {_iterationsStart}/{_iterationsEnd}] [Chamfer: {_chamfer}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ public override string ToString()
+ {
+ var result = $"[{_morphOperation}] [Iterations: {_iterationsStart}/{_iterationsEnd}] [Chamfer: {_chamfer}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationMorph() { }
+ public OperationMorph() { }
- public OperationMorph(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationMorph(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- private bool Equals(OperationMorph other)
- {
- return _morphOperation == other._morphOperation && _iterationsStart == other._iterationsStart && _iterationsEnd == other._iterationsEnd && _chamfer == other._chamfer;
- }
+ private bool Equals(OperationMorph other)
+ {
+ return _morphOperation == other._morphOperation && _iterationsStart == other._iterationsStart && _iterationsEnd == other._iterationsEnd && _chamfer == other._chamfer;
+ }
- public override bool Equals(object obj)
- {
- return ReferenceEquals(this, obj) || obj is OperationMorph other && Equals(other);
- }
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is OperationMorph other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine((int)_morphOperation, _iterationsStart, _iterationsEnd, _chamfer);
+ }
+
+ #endregion
- public override int GetHashCode()
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var isFade = Chamfer;
+ FileFormat.MutateGetVarsIterationChamfer(
+ LayerIndexStart,
+ LayerIndexEnd,
+ (int)IterationsStart,
+ (int)IterationsEnd,
+ ref isFade,
+ out var iterationSteps,
+ out var maxIteration
+ );
+
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- return HashCode.Combine((int)_morphOperation, _iterationsStart, _iterationsEnd, _chamfer);
- }
+ if (progress.Token.IsCancellationRequested) return;
+ int iterations = FileFormat.MutateGetIterationVar(isFade, (int)IterationsStart, (int)IterationsEnd, iterationSteps, maxIteration, LayerIndexStart, (uint)layerIndex);
+
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat, iterations);
+ SlicerFile[layerIndex].LayerMat = mat;
- #endregion
+ progress.LockAndIncrement();
+ });
- #region Methods
+ return !progress.Token.IsCancellationRequested;
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ int iterations = (int) _iterationsStart;
+ if (arguments is not null && arguments.Length >= 1)
{
- var isFade = Chamfer;
- LayerManager.MutateGetVarsIterationChamfer(
- LayerIndexStart,
- LayerIndexEnd,
- (int)IterationsStart,
- (int)IterationsEnd,
- ref isFade,
- out var iterationSteps,
- out var maxIteration
- );
-
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- int iterations = LayerManager.MutateGetIterationVar(isFade, (int)IterationsStart, (int)IterationsEnd, iterationSteps, maxIteration, LayerIndexStart, (uint)layerIndex);
-
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat, iterations);
- SlicerFile[layerIndex].LayerMat = mat;
-
- progress.LockAndIncrement();
- });
-
- return !progress.Token.IsCancellationRequested;
+ iterations = (int) arguments[0];
}
+
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
- public override bool Execute(Mat mat, params object[] arguments)
+ /*if (CoreSettings.CanUseCuda)
{
- int iterations = (int) _iterationsStart;
- if (arguments is not null && arguments.Length >= 1)
- {
- iterations = (int) arguments[0];
- }
-
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ var gpuMat = target.ToGpuMat();
+ using var morph = new CudaMorphologyFilter(MorphOperationOpenCV, target.Depth, target.NumberOfChannels, Kernel.Matrix, Kernel.Anchor, iterations);
+ morph.Apply(gpuMat, gpuMat);
+ gpuMat.Download(target);
+ }
+ else
+ {*/
- /*if (CoreSettings.CanUseCuda)
- {
- var gpuMat = target.ToGpuMat();
- using var morph = new CudaMorphologyFilter(MorphOperationOpenCV, target.Depth, target.NumberOfChannels, Kernel.Matrix, Kernel.Anchor, iterations);
- morph.Apply(gpuMat, gpuMat);
- gpuMat.Download(target);
- }
- else
- {*/
-
- var kernel = Kernel.GetKernel(ref iterations);
- CvInvoke.MorphologyEx(target, target, MorphOperationOpenCV, kernel, Kernel.Anchor, iterations, BorderType.Reflect101, default);
-
- if (_morphOperation == MorphOperations.OffsetCrop)
- {
- var originalRoi = GetRoiOrDefault(original);
- originalRoi.CopyTo(target, target);
- }
- /*else if (_morphOperation == MorphOperations.IsolateFeatures)
- {
- var originalRoi = GetRoiOrDefault(original);
- CvInvoke.Subtract(originalRoi, target, target);
- }*/
- //}
+ var kernel = Kernel.GetKernel(ref iterations);
+ CvInvoke.MorphologyEx(target, target, MorphOperationOpenCV, kernel, Kernel.Anchor, iterations, BorderType.Reflect101, default);
-
- ApplyMask(original, target);
- return true;
+ if (_morphOperation == MorphOperations.OffsetCrop)
+ {
+ var originalRoi = GetRoiOrDefault(original);
+ originalRoi.CopyTo(target, target);
}
+ /*else if (_morphOperation == MorphOperations.IsolateFeatures)
+ {
+ var originalRoi = GetRoiOrDefault(original);
+ CvInvoke.Subtract(originalRoi, target, target);
+ }*/
+ //}
- #endregion
+
+ ApplyMask(original, target);
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs
index 8b82d29..b1de342 100644
--- a/UVtools.Core/Operations/OperationMove.cs
+++ b/UVtools.Core/Operations/OperationMove.cs
@@ -5,324 +5,303 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.Structure;
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.Structure;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationMove : Operation
{
- [Serializable]
- public class OperationMove : Operation
- {
- #region Overrides
+ #region Overrides
- public override bool CanMask => false;
+ public override bool CanMask => false;
+ public override string IconClass => "fas fa-arrows-alt";
+ public override string Title => "Move";
+ public override string Description =>
+ "Change or copy the position of the model on the build plate.\n" +
+ "Note: Before perform this operation, un-rotate the layer preview to see the real orientation.";
- public override string Title => "Move";
- public override string Description =>
- "Change or copy the position of the model on the build plate.\n" +
- "Note: Before perform this operation, un-rotate the layer preview to see the real orientation.";
+ public override string ConfirmationText =>
+ (IsCutMove ? "move" : "copy") + $" model layers {LayerIndexStart} through {LayerIndexEnd} from " +
+ $"location {{X={ROI.X},Y={ROI.Y}}} to " +
+ $"location {{X={DstRoi.X},Y={DstRoi.Y}}}?";
- public override string ConfirmationText =>
- (IsCutMove ? "move" : "copy") + $" model layers {LayerIndexStart} through {LayerIndexEnd} from " +
- $"location {{X={ROI.X},Y={ROI.Y}}} to " +
- $"location {{X={DstRoi.X},Y={DstRoi.Y}}}?";
+ public override string ProgressTitle =>
+ (IsCutMove ? "Moving" : "Copying") +$" model to {{X={DstRoi.X},Y={DstRoi.Y}}}";
- public override string ProgressTitle =>
- (IsCutMove ? "Moving" : "Copying") +$" model to {{X={DstRoi.X},Y={DstRoi.Y}}}";
+ public override string ProgressAction => (IsCutMove ? "Moved" : "Copied")+" layers";
- public override string ProgressAction => (IsCutMove ? "Moved" : "Copied")+" layers";
+ public override bool CanHaveProfiles => false;
- public override bool CanHaveProfiles => false;
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ValidateInternally()
+ if (!ValidateBounds())
{
- var sb = new StringBuilder();
-
- if (!ValidateBounds())
- {
- sb.AppendLine("Your parameters will put the model outside of build plate. Please adjust the location and margins.");
- }
-
- return sb.ToString();
+ sb.AppendLine("Your parameters will put the model outside of build plate. Please adjust the location and margins.");
}
- public override string ToString()
- {
- var result = $"[{ROI} -> {DstRoi}] [Cut: {IsCutMove}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
-
- #region Members
- private Rectangle _dstRoi = Rectangle.Empty;
- private uint _imageWidth;
- private uint _imageHeight;
- private Enumerations.Anchor _anchor = Enumerations.Anchor.MiddleCenter;
- private int _marginLeft;
- private int _marginTop;
- private int _marginRight;
- private int _marginBottom;
- private bool _isCutMove = true;
- private bool _isWithinBoundary;
- #endregion
-
- #region Properties
-
- public Rectangle DstRoi
- {
- get
- {
- if(!_dstRoi.IsEmpty) return _dstRoi;
- CalculateDstRoi();
- return _dstRoi;
- }
- }
+ return sb.ToString();
+ }
- public uint ImageWidth
+ public override string ToString()
+ {
+ var result = $"[{ROI} -> {DstRoi}] [Cut: {IsCutMove}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Members
+ private Rectangle _dstRoi = Rectangle.Empty;
+ private uint _imageWidth;
+ private uint _imageHeight;
+ private Enumerations.Anchor _anchor = Enumerations.Anchor.MiddleCenter;
+ private int _marginLeft;
+ private int _marginTop;
+ private int _marginRight;
+ private int _marginBottom;
+ private bool _isCutMove = true;
+ private bool _isWithinBoundary;
+ #endregion
+
+ #region Properties
+
+ public Rectangle DstRoi
+ {
+ get
{
- get => _imageWidth;
- set => RaiseAndSetIfChanged(ref _imageWidth, value);
+ if(!_dstRoi.IsEmpty) return _dstRoi;
+ CalculateDstRoi();
+ return _dstRoi;
}
+ }
- public uint ImageHeight
- {
- get => _imageHeight;
- set => RaiseAndSetIfChanged(ref _imageHeight, value);
- }
+ public uint ImageWidth
+ {
+ get => _imageWidth;
+ set => RaiseAndSetIfChanged(ref _imageWidth, value);
+ }
- public Enumerations.Anchor Anchor
- {
- get => _anchor;
- set
- {
- RaiseAndSetIfChanged(ref _anchor, value);
- CalculateDstRoi();
- }
- }
+ public uint ImageHeight
+ {
+ get => _imageHeight;
+ set => RaiseAndSetIfChanged(ref _imageHeight, value);
+ }
- public int MarginLeft
+ public Enumerations.Anchor Anchor
+ {
+ get => _anchor;
+ set
{
- get => _marginLeft;
- set
- {
- RaiseAndSetIfChanged(ref _marginLeft, value);
- CalculateDstRoi();
- }
+ RaiseAndSetIfChanged(ref _anchor, value);
+ CalculateDstRoi();
}
+ }
- public int MarginTop
+ public int MarginLeft
+ {
+ get => _marginLeft;
+ set
{
- get => _marginTop;
- set
- {
- RaiseAndSetIfChanged(ref _marginTop, value);
- CalculateDstRoi();
- }
+ RaiseAndSetIfChanged(ref _marginLeft, value);
+ CalculateDstRoi();
}
+ }
- public int MarginRight
+ public int MarginTop
+ {
+ get => _marginTop;
+ set
{
- get => _marginRight;
- set
- {
- RaiseAndSetIfChanged(ref _marginRight, value);
- CalculateDstRoi();
- }
+ RaiseAndSetIfChanged(ref _marginTop, value);
+ CalculateDstRoi();
}
+ }
- public int MarginBottom
+ public int MarginRight
+ {
+ get => _marginRight;
+ set
{
- get => _marginBottom;
- set
- {
- RaiseAndSetIfChanged(ref _marginBottom, value);
- CalculateDstRoi();
- }
+ RaiseAndSetIfChanged(ref _marginRight, value);
+ CalculateDstRoi();
}
+ }
- public bool IsCutMove
+ public int MarginBottom
+ {
+ get => _marginBottom;
+ set
{
- get => _isCutMove;
- set => RaiseAndSetIfChanged(ref _isCutMove, value);
+ RaiseAndSetIfChanged(ref _marginBottom, value);
+ CalculateDstRoi();
}
+ }
+
+ public bool IsCutMove
+ {
+ get => _isCutMove;
+ set => RaiseAndSetIfChanged(ref _isCutMove, value);
+ }
- public string LocationXStr => $"X: {DstRoi.X} / {ImageWidth - ROI.Width}";
- public string LocationYStr => $"Y: {DstRoi.Y} / {ImageHeight - ROI.Height}";
+ public string LocationXStr => $"X: {DstRoi.X} / {ImageWidth - ROI.Width}";
+ public string LocationYStr => $"Y: {DstRoi.Y} / {ImageHeight - ROI.Height}";
- public string LocationWidthStr => $"Width: {ROI.Width} / {ImageWidth}";
- public string LocationHeightStr => $"Height: {ROI.Height} / {ImageHeight}";
+ public string LocationWidthStr => $"Width: {ROI.Width} / {ImageWidth}";
+ public string LocationHeightStr => $"Height: {ROI.Height} / {ImageHeight}";
- public bool IsWithinBoundary
+ public bool IsWithinBoundary
+ {
+ get => _isWithinBoundary;
+ set
{
- get => _isWithinBoundary;
- set
- {
- if (!RaiseAndSetIfChanged(ref _isWithinBoundary, value)) return;
- RaisePropertyChanged(nameof(IsWithinBoundaryStr));
- }
+ if (!RaiseAndSetIfChanged(ref _isWithinBoundary, value)) return;
+ RaisePropertyChanged(nameof(IsWithinBoundaryStr));
}
+ }
- public string IsWithinBoundaryStr => "Model within boundary: " + (_isWithinBoundary ? "Yes" : "No");
+ public string IsWithinBoundaryStr => "Model within boundary: " + (_isWithinBoundary ? "Yes" : "No");
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationMove() { }
+ public OperationMove() { }
- public OperationMove(FileFormat slicerFile) : this(slicerFile, Enumerations.Anchor.MiddleCenter)
- { }
+ public OperationMove(FileFormat slicerFile) : this(slicerFile, Enumerations.Anchor.MiddleCenter)
+ { }
- public OperationMove(FileFormat slicerFile, Enumerations.Anchor anchor) : base(slicerFile)
- {
- _anchor = anchor;
- }
-
- public OperationMove(FileFormat slicerFile, Rectangle srcRoi, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) : this(slicerFile, anchor)
- {
- if(!srcRoi.IsEmpty) ROI = srcRoi;
- }
-
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
- ROI = SlicerFile.BoundingRectangle;
- _imageWidth = SlicerFile.ResolutionX;
- _imageHeight = SlicerFile.ResolutionY;
- }
+ public OperationMove(FileFormat slicerFile, Enumerations.Anchor anchor) : base(slicerFile)
+ {
+ _anchor = anchor;
+ }
- /*public OperationMove(FileFormat slicerFile, Rectangle srcRoi, Mat mat, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) : this(slicerFile, srcRoi, anchor)
- {
- ImageWidth = (uint) mat.Width;
- ImageHeight = (uint) mat.Height;
- }*/
+ public OperationMove(FileFormat slicerFile, Rectangle srcRoi, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) : this(slicerFile, anchor)
+ {
+ if(!srcRoi.IsEmpty) ROI = srcRoi;
+ }
- #endregion
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
+ ROI = SlicerFile.BoundingRectangle;
+ _imageWidth = SlicerFile.ResolutionX;
+ _imageHeight = SlicerFile.ResolutionY;
+ }
- #region Methods
- public void CalculateDstRoi()
- {
- _dstRoi.Size = ROI.Size;
+ /*public OperationMove(FileFormat slicerFile, Rectangle srcRoi, Mat mat, Enumerations.Anchor anchor = Enumerations.Anchor.MiddleCenter) : this(slicerFile, srcRoi, anchor)
+ {
+ ImageWidth = (uint) mat.Width;
+ ImageHeight = (uint) mat.Height;
+ }*/
- switch (Anchor)
- {
- case Enumerations.Anchor.TopLeft:
- _dstRoi.Location = new Point(0, 0);
- break;
- case Enumerations.Anchor.TopCenter:
- _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), 0);
- break;
- case Enumerations.Anchor.TopRight:
- _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), 0);
- break;
- case Enumerations.Anchor.MiddleLeft:
- _dstRoi.Location = new Point(0, (int)(ImageHeight / 2 - ROI.Height / 2));
- break;
- case Enumerations.Anchor.MiddleCenter:
- //case Anchor.None:
- _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), (int)(ImageHeight / 2 - ROI.Height / 2));
- break;
- case Enumerations.Anchor.MiddleRight:
- _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), (int)(ImageHeight / 2 - ROI.Height / 2));
- break;
- case Enumerations.Anchor.BottomLeft:
- _dstRoi.Location = new Point(0, (int)(ImageHeight - ROI.Height));
- break;
- case Enumerations.Anchor.BottomCenter:
- _dstRoi.Location = new Point((int)(ImageWidth / 2 - ROI.Width / 2), (int)(ImageHeight - ROI.Height));
- break;
- case Enumerations.Anchor.BottomRight:
- _dstRoi.Location = new Point((int)(ImageWidth - ROI.Width), (int)(ImageHeight - ROI.Height));
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
+ #endregion
- _dstRoi.X += MarginLeft;
- _dstRoi.X -= MarginRight;
- _dstRoi.Y += MarginTop;
- _dstRoi.Y -= MarginBottom;
+ #region Methods
+ public void CalculateDstRoi()
+ {
+ _dstRoi.Size = ROI.Size;
- IsWithinBoundary = !(_dstRoi.IsEmpty || _dstRoi.X < 0 || _dstRoi.Y < 0 ||
- _dstRoi.Width == 0 || _dstRoi.Right > ImageWidth ||
- _dstRoi.Height == 0 || _dstRoi.Bottom > ImageHeight);
+ _dstRoi.Location = Anchor switch
+ {
+ Enumerations.Anchor.TopLeft => new Point(0, 0),
+ Enumerations.Anchor.TopCenter => new Point((int) (ImageWidth / 2 - ROI.Width / 2), 0),
+ Enumerations.Anchor.TopRight => new Point((int) (ImageWidth - ROI.Width), 0),
+ Enumerations.Anchor.MiddleLeft => new Point(0, (int) (ImageHeight / 2 - ROI.Height / 2)),
+ Enumerations.Anchor.MiddleCenter => new Point((int) (ImageWidth / 2 - ROI.Width / 2), (int) (ImageHeight / 2 - ROI.Height / 2)),
+ Enumerations.Anchor.MiddleRight => new Point((int) (ImageWidth - ROI.Width), (int) (ImageHeight / 2 - ROI.Height / 2)),
+ Enumerations.Anchor.BottomLeft => new Point(0, (int) (ImageHeight - ROI.Height)),
+ Enumerations.Anchor.BottomCenter => new Point((int) (ImageWidth / 2 - ROI.Width / 2), (int) (ImageHeight - ROI.Height)),
+ Enumerations.Anchor.BottomRight => new Point((int) (ImageWidth - ROI.Width), (int) (ImageHeight - ROI.Height)),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ _dstRoi.X += MarginLeft;
+ _dstRoi.X -= MarginRight;
+ _dstRoi.Y += MarginTop;
+ _dstRoi.Y -= MarginBottom;
+
+ IsWithinBoundary = !(_dstRoi.IsEmpty || _dstRoi.X < 0 || _dstRoi.Y < 0 ||
+ _dstRoi.Width == 0 || _dstRoi.Right > ImageWidth ||
+ _dstRoi.Height == 0 || _dstRoi.Bottom > ImageHeight);
+
+ RaisePropertyChanged(nameof(DstRoi));
+ RaisePropertyChanged(nameof(LocationXStr));
+ RaisePropertyChanged(nameof(LocationYStr));
+ }
- RaisePropertyChanged(nameof(DstRoi));
- RaisePropertyChanged(nameof(LocationXStr));
- RaisePropertyChanged(nameof(LocationYStr));
- }
+ public void Reset()
+ {
+ MarginLeft = MarginTop = MarginRight = MarginBottom = 0;
+ Anchor = Enumerations.Anchor.MiddleCenter;
+ IsCutMove = true;
+ }
- public void Reset()
- {
- MarginLeft = MarginTop = MarginRight = MarginBottom = 0;
- Anchor = Enumerations.Anchor.MiddleCenter;
- IsCutMove = true;
- }
+ public void SetAnchor(byte value)
+ {
+ Anchor = (Enumerations.Anchor)value;
+ }
- public void SetAnchor(byte value)
- {
- Anchor = (Enumerations.Anchor)value;
- }
+ public bool ValidateBounds()
+ {
+ CalculateDstRoi();
+ return IsWithinBoundary;
+ }
- public bool ValidateBounds()
- {
- CalculateDstRoi();
- return IsWithinBoundary;
- }
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (ROI.IsEmpty) ROI = SlicerFile.GetBoundingRectangle(progress);
+ CalculateDstRoi();
- protected override bool ExecuteInternally(OperationProgress progress)
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- if (ROI.IsEmpty) ROI = SlicerFile.LayerManager.GetBoundingRectangle(progress);
- CalculateDstRoi();
+ if (progress.Token.IsCancellationRequested) return;
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ using (var mat = SlicerFile[layerIndex].LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
+ Execute(mat);
+ SlicerFile[layerIndex].LayerMat = mat;
+ }
- using (var mat = SlicerFile[layerIndex].LayerMat)
- {
- Execute(mat);
- SlicerFile[layerIndex].LayerMat = mat;
- }
+ progress.LockAndIncrement();
+ });
- progress.LockAndIncrement();
- });
+ SlicerFile.BoundingRectangle = Rectangle.Empty;
- SlicerFile.LayerManager.BoundingRectangle = Rectangle.Empty;
+ return !progress.Token.IsCancellationRequested;
+ }
- return !progress.Token.IsCancellationRequested;
- }
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ if (_imageWidth == 0) _imageWidth = (uint) mat.Width;
+ if (_imageHeight == 0) _imageHeight = (uint) mat.Height;
- public override bool Execute(Mat mat, params object[] arguments)
+ using var srcRoi = new Mat(mat, ROI);
+ using var dstRoi = new Mat(mat, DstRoi);
+ if (IsCutMove)
{
- if (_imageWidth == 0) _imageWidth = (uint) mat.Width;
- if (_imageHeight == 0) _imageHeight = (uint) mat.Height;
-
- using var srcRoi = new Mat(mat, ROI);
- using var dstRoi = new Mat(mat, DstRoi);
- if (IsCutMove)
- {
- using var targetRoi = srcRoi.Clone();
- srcRoi.SetTo(new MCvScalar(0));
- targetRoi.CopyTo(dstRoi);
- }
- else
- {
- srcRoi.CopyTo(dstRoi);
- }
-
- return true;
+ using var targetRoi = srcRoi.Clone();
+ srcRoi.SetTo(new MCvScalar(0));
+ targetRoi.CopyTo(dstRoi);
+ }
+ else
+ {
+ srcRoi.CopyTo(dstRoi);
}
- #endregion
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs
index 9e668b2..aab78c9 100644
--- a/UVtools.Core/Operations/OperationPattern.cs
+++ b/UVtools.Core/Operations/OperationPattern.cs
@@ -6,347 +6,347 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationPattern : Operation
{
- [Serializable]
- public class OperationPattern : Operation
+ #region Members
+ private Enumerations.Anchor _anchor = Enumerations.Anchor.None;
+ private ushort _colSpacing;
+ private ushort _rowSpacing;
+ private ushort _maxColSpacing;
+ private ushort _maxRowSpacing;
+ private ushort _cols = 1;
+ private ushort _rows = 1;
+ private ushort _maxCols;
+ private ushort _maxRows;
+ private bool _isWithinBoundary = true;
+ #endregion
+
+ #region Overrides
+ public override bool CanMask => false;
+ public override string IconClass => "fas fa-th-large";
+ public override string Title => "Pattern";
+ public override string Description =>
+ "Duplicates the model in a rectangular pattern around the build plate.\n" +
+ "Note: Before perform this operation, un-rotate the layer preview to see the real orientation.";
+ public override string ConfirmationText =>
+ $"pattern the object across {Cols} columns and {Rows} rows?";
+
+ public override string ProgressTitle =>
+ $"Patterning the object across {Cols} columns and {Rows} rows";
+
+ public override string ProgressAction => "Patterned layers";
+
+ public override bool CanHaveProfiles => false;
+
+ public override string? ValidateSpawn()
{
- #region Members
- private Enumerations.Anchor _anchor = Enumerations.Anchor.None;
- private ushort _colSpacing;
- private ushort _rowSpacing;
- private ushort _maxColSpacing;
- private ushort _maxRowSpacing;
- private ushort _cols = 1;
- private ushort _rows = 1;
- private ushort _maxCols;
- private ushort _maxRows;
- private bool _isWithinBoundary = true;
- #endregion
-
- #region Overrides
- public override bool CanMask => false;
- public override string Title => "Pattern";
- public override string Description =>
- "Duplicates the model in a rectangular pattern around the build plate.\n" +
- "Note: Before perform this operation, un-rotate the layer preview to see the real orientation.";
- public override string ConfirmationText =>
- $"pattern the object across {Cols} columns and {Rows} rows?";
-
- public override string ProgressTitle =>
- $"Patterning the object across {Cols} columns and {Rows} rows";
-
- public override string ProgressAction => "Patterned layers";
-
- public override bool CanHaveProfiles => false;
-
- public override string ValidateSpawn()
+ if (MaxRows < 2 && MaxCols < 2)
{
- if (MaxRows < 2 && MaxCols < 2)
- {
- return "The available free volume is not enough to pattern this object.\n" +
- "To run this tool the free space must allow at least 1 copy.";
- }
-
- return null;
+ return "The available free volume is not enough to pattern this object.\n" +
+ "To run this tool the free space must allow at least 1 copy.";
}
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ return null;
+ }
- if (Cols <= 1 && Rows <= 1)
- {
- sb.AppendLine("Either columns or rows must be greater than 1.");
- }
-
- if (!ValidateBounds())
- {
- sb.AppendLine("Your parameters will put the object outside of the build plate, please adjust the margins.");
- }
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- return sb.ToString();
+ if (Cols <= 1 && Rows <= 1)
+ {
+ sb.AppendLine("Either columns or rows must be greater than 1.");
}
-
- #endregion
-
- #region Properties
- public Enumerations.Anchor Anchor
+
+ if (!ValidateBounds())
{
- get => _anchor;
- set => RaiseAndSetIfChanged(ref _anchor, value);
+ sb.AppendLine("Your parameters will put the object outside of the build plate, please adjust the margins.");
}
- public uint ImageWidth { get; private set; }
- public uint ImageHeight { get; private set; }
+ return sb.ToString();
+ }
- public ushort Cols
- {
- get => _cols;
- set
- {
- if (!RaiseAndSetIfChanged(ref _cols, value)) return;
- RaisePropertyChanged(nameof(InfoCols));
- RaisePropertyChanged(nameof(InfoWidthStr));
- RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
- ValidateBounds();
- }
- }
+ #endregion
- public ushort Rows
- {
- get => _rows;
- set
- {
- if (!RaiseAndSetIfChanged(ref _rows, value)) return;
- RaisePropertyChanged(nameof(InfoRows));
- RaisePropertyChanged(nameof(InfoHeightStr));
- RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
- ValidateBounds();
- }
- }
+ #region Properties
+ public Enumerations.Anchor Anchor
+ {
+ get => _anchor;
+ set => RaiseAndSetIfChanged(ref _anchor, value);
+ }
+
+ public uint ImageWidth { get; private set; }
+ public uint ImageHeight { get; private set; }
- public ushort MaxCols
+ public ushort Cols
+ {
+ get => _cols;
+ set
{
- get => _maxCols;
- set
- {
- if(!RaiseAndSetIfChanged(ref _maxCols, value)) return;
- RaisePropertyChanged(nameof(InfoCols));
- ValidateBounds();
- }
+ if (!RaiseAndSetIfChanged(ref _cols, value)) return;
+ RaisePropertyChanged(nameof(InfoCols));
+ RaisePropertyChanged(nameof(InfoWidthStr));
+ RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
+ ValidateBounds();
}
+ }
- public ushort MaxRows
+ public ushort Rows
+ {
+ get => _rows;
+ set
{
- get => _maxRows;
- set
- {
- if (!RaiseAndSetIfChanged(ref _maxRows, value)) return;
- RaisePropertyChanged(nameof(InfoRows));
- ValidateBounds();
- }
+ if (!RaiseAndSetIfChanged(ref _rows, value)) return;
+ RaisePropertyChanged(nameof(InfoRows));
+ RaisePropertyChanged(nameof(InfoHeightStr));
+ RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
+ ValidateBounds();
}
+ }
- public ushort ColSpacing
+ public ushort MaxCols
+ {
+ get => _maxCols;
+ set
{
- get => _colSpacing;
- set
- {
- if(!RaiseAndSetIfChanged(ref _colSpacing, value)) return;
- RaisePropertyChanged(nameof(InfoWidthStr));
- RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
- ValidateBounds();
- }
+ if(!RaiseAndSetIfChanged(ref _maxCols, value)) return;
+ RaisePropertyChanged(nameof(InfoCols));
+ ValidateBounds();
}
+ }
- public ushort RowSpacing
+ public ushort MaxRows
+ {
+ get => _maxRows;
+ set
{
- get => _rowSpacing;
- set
- {
- if (!RaiseAndSetIfChanged(ref _rowSpacing, value)) return;
- RaisePropertyChanged(nameof(InfoHeightStr));
- RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
- ValidateBounds();
- }
+ if (!RaiseAndSetIfChanged(ref _maxRows, value)) return;
+ RaisePropertyChanged(nameof(InfoRows));
+ ValidateBounds();
}
+ }
- public ushort MaxColSpacing
+ public ushort ColSpacing
+ {
+ get => _colSpacing;
+ set
{
- get => _maxColSpacing;
- set => RaiseAndSetIfChanged(ref _maxColSpacing, value);
+ if(!RaiseAndSetIfChanged(ref _colSpacing, value)) return;
+ RaisePropertyChanged(nameof(InfoWidthStr));
+ RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
+ ValidateBounds();
}
+ }
- public ushort MaxRowSpacing
+ public ushort RowSpacing
+ {
+ get => _rowSpacing;
+ set
{
- get => _maxRowSpacing;
- set => RaiseAndSetIfChanged(ref _maxRowSpacing, value);
+ if (!RaiseAndSetIfChanged(ref _rowSpacing, value)) return;
+ RaisePropertyChanged(nameof(InfoHeightStr));
+ RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
+ ValidateBounds();
}
+ }
+
+ public ushort MaxColSpacing
+ {
+ get => _maxColSpacing;
+ set => RaiseAndSetIfChanged(ref _maxColSpacing, value);
+ }
+
+ public ushort MaxRowSpacing
+ {
+ get => _maxRowSpacing;
+ set => RaiseAndSetIfChanged(ref _maxRowSpacing, value);
+ }
- public string InfoCols => $"Columns: {Cols} / {MaxCols}";
- public string InfoRows => $"Rows: {Rows} / {MaxRows}";
+ public string InfoCols => $"Columns: {Cols} / {MaxCols}";
+ public string InfoRows => $"Rows: {Rows} / {MaxRows}";
- public string InfoWidthStr =>
- $"Width: {GetPatternVolume.Width} (Min: {ROI.Width}, Max: {ImageWidth})";
+ public string InfoWidthStr =>
+ $"Width: {GetPatternVolume.Width} (Min: {ROI.Width}, Max: {ImageWidth})";
- public string InfoHeightStr =>
- $"Width: {GetPatternVolume.Height} (Min: {ROI.Height}, Max: {ImageHeight})";
+ public string InfoHeightStr =>
+ $"Width: {GetPatternVolume.Height} (Min: {ROI.Height}, Max: {ImageHeight})";
- public bool IsWithinBoundary
+ public bool IsWithinBoundary
+ {
+ get => _isWithinBoundary;
+ set
{
- get => _isWithinBoundary;
- set
- {
- if (!RaiseAndSetIfChanged(ref _isWithinBoundary, value)) return;
- RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
- }
+ if (!RaiseAndSetIfChanged(ref _isWithinBoundary, value)) return;
+ RaisePropertyChanged(nameof(InfoModelWithinBoundaryStr));
}
+ }
- public string InfoModelWithinBoundaryStr => "Model within boundary: " + (_isWithinBoundary ? "Yes" : "No");
+ public string InfoModelWithinBoundaryStr => "Model within boundary: " + (_isWithinBoundary ? "Yes" : "No");
- public Size GetPatternVolume => new(Cols * ROI.Width + (Cols - 1) * ColSpacing, Rows * ROI.Height + (Rows - 1) * RowSpacing);
- #endregion
+ public Size GetPatternVolume => new(Cols * ROI.Width + (Cols - 1) * ColSpacing, Rows * ROI.Height + (Rows - 1) * RowSpacing);
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationPattern() { }
+ public OperationPattern() { }
- public OperationPattern(FileFormat slicerFile) : base(slicerFile)
- {
- SetRoi(SlicerFile.BoundingRectangle);
- }
+ public OperationPattern(FileFormat slicerFile) : base(slicerFile)
+ {
+ SetRoi(SlicerFile.BoundingRectangle);
+ }
- public OperationPattern(FileFormat slicerFile, Rectangle srcRoi) : base(slicerFile)
- {
- SetRoi(srcRoi.IsEmpty ? SlicerFile.BoundingRectangle : srcRoi);
- }
+ public OperationPattern(FileFormat slicerFile, Rectangle srcRoi) : base(slicerFile)
+ {
+ SetRoi(srcRoi.IsEmpty ? SlicerFile.BoundingRectangle : srcRoi);
+ }
- public override void InitWithSlicerFile()
- {
- base.InitWithSlicerFile();
+ public override void InitWithSlicerFile()
+ {
+ base.InitWithSlicerFile();
- ImageWidth = SlicerFile.ResolutionX;
- ImageHeight = SlicerFile.ResolutionY;
+ ImageWidth = SlicerFile.ResolutionX;
+ ImageHeight = SlicerFile.ResolutionY;
- SetRoi(SlicerFile.BoundingRectangle);
- Fill();
- }
+ SetRoi(SlicerFile.BoundingRectangle);
+ Fill();
+ }
- #endregion
+ #endregion
- #region Methods
- public void SetAnchor(byte value)
- {
- Anchor = (Enumerations.Anchor)value;
- }
+ #region Methods
+ public void SetAnchor(byte value)
+ {
+ Anchor = (Enumerations.Anchor)value;
+ }
- public void SetRoi(Rectangle srcRoi)
- {
- ROI = srcRoi;
+ public void SetRoi(Rectangle srcRoi)
+ {
+ ROI = srcRoi;
- MaxCols = (ushort)(ImageWidth / srcRoi.Width);
- MaxRows = (ushort)(ImageHeight / srcRoi.Height);
+ MaxCols = (ushort)(ImageWidth / srcRoi.Width);
+ MaxRows = (ushort)(ImageHeight / srcRoi.Height);
- MaxColSpacing = CalculateAutoColSpacing(MaxCols);
- MaxRowSpacing = CalculateAutoRowSpacing(MaxRows);
- }
+ MaxColSpacing = CalculateAutoColSpacing(MaxCols);
+ MaxRowSpacing = CalculateAutoRowSpacing(MaxRows);
+ }
- /// <summary>
- /// Fills the plate with maximum cols and rows
- /// </summary>
- public void Fill()
- {
- Cols = MaxCols;
- ColSpacing = MaxColSpacing;
+ /// <summary>
+ /// Fills the plate with maximum cols and rows
+ /// </summary>
+ public void Fill()
+ {
+ Cols = MaxCols;
+ ColSpacing = MaxColSpacing;
- Rows = MaxRows;
- RowSpacing = MaxRowSpacing;
- }
+ Rows = MaxRows;
+ RowSpacing = MaxRowSpacing;
+ }
- public ushort CalculateAutoColSpacing(ushort cols)
- {
- if (cols <= 1) return 0;
- return (ushort)((ImageWidth - ROI.Width * cols) / cols);
- }
+ public ushort CalculateAutoColSpacing(ushort cols)
+ {
+ if (cols <= 1) return 0;
+ return (ushort)((ImageWidth - ROI.Width * cols) / cols);
+ }
- public ushort CalculateAutoRowSpacing(ushort rows)
- {
- if (rows <= 1) return 0;
- return (ushort)((ImageHeight - ROI.Height * rows) / rows);
- }
+ public ushort CalculateAutoRowSpacing(ushort rows)
+ {
+ if (rows <= 1) return 0;
+ return (ushort)((ImageHeight - ROI.Height * rows) / rows);
+ }
- public Rectangle GetRoi(ushort col, ushort row)
- {
- var patternVolume = GetPatternVolume;
+ public Rectangle GetRoi(ushort col, ushort row)
+ {
+ var patternVolume = GetPatternVolume;
- return new Rectangle(new Point(
- (int) (col * ROI.Width + col * ColSpacing + (ImageWidth - patternVolume.Width) / 2),
- (int) (row * ROI.Height + row * RowSpacing + (ImageHeight - patternVolume.Height) / 2)), ROI.Size);
- }
+ return new Rectangle(new Point(
+ (int) (col * ROI.Width + col * ColSpacing + (ImageWidth - patternVolume.Width) / 2),
+ (int) (row * ROI.Height + row * RowSpacing + (ImageHeight - patternVolume.Height) / 2)), ROI.Size);
+ }
- public void FillColumnSpacing()
- {
- ColSpacing = CalculateAutoColSpacing(_cols);
- }
+ public void FillColumnSpacing()
+ {
+ ColSpacing = CalculateAutoColSpacing(_cols);
+ }
- public void FillRowSpacing()
- {
- RowSpacing = CalculateAutoRowSpacing(_rows);
- }
+ public void FillRowSpacing()
+ {
+ RowSpacing = CalculateAutoRowSpacing(_rows);
+ }
- public bool ValidateBounds()
- {
- var volume = GetPatternVolume;
- return IsWithinBoundary = volume.Width <= ImageWidth && volume.Height <= ImageHeight;
- }
+ public bool ValidateBounds()
+ {
+ var volume = GetPatternVolume;
+ return IsWithinBoundary = volume.Width <= ImageWidth && volume.Height <= ImageHeight;
+ }
- public override string ToString()
- {
- var result = $"[Rows: {Rows}] [Cols: {Cols}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ public override string ToString()
+ {
+ var result = $"[Rows: {Rows}] [Cols: {Cols}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
-
- using var mat = SlicerFile[layerIndex].LayerMat;
- using var layerRoi = new Mat(mat, ROI);
- using var dstLayer = mat.NewBlank();
- for (ushort col = 0; col < Cols; col++)
- for (ushort row = 0; row < Rows; row++)
- {
- var roi = GetRoi(col, row);
- using var dstRoi = new Mat(dstLayer, roi);
- layerRoi.CopyTo(dstRoi);
- }
- //Execute(mat);
- SlicerFile[layerIndex].LayerMat = dstLayer;
-
- progress.LockAndIncrement();
- });
-
- SlicerFile.LayerManager.BoundingRectangle = Rectangle.Empty;
-
- progress.Token.ThrowIfCancellationRequested();
-
- if (Anchor == Enumerations.Anchor.None) return true;
- var operationMove = new OperationMove(SlicerFile, Anchor)
- {
- LayerIndexStart = LayerIndexStart,
- LayerIndexEnd = LayerIndexEnd
- };
- operationMove.Execute(progress);
-
- return !progress.Token.IsCancellationRequested;
- }
+ if (progress.Token.IsCancellationRequested) return;
- /*public override bool Execute(Mat mat, params object[] arguments)
- {
+ using var mat = SlicerFile[layerIndex].LayerMat;
using var layerRoi = new Mat(mat, ROI);
- using var dstLayer = mat.CloneBlank();
+ using var dstLayer = mat.NewBlank();
for (ushort col = 0; col < Cols; col++)
for (ushort row = 0; row < Rows; row++)
{
- var dstRoi = new Mat(dstLayer, GetRoi(col, row));
+ var roi = GetRoi(col, row);
+ using var dstRoi = new Mat(dstLayer, roi);
layerRoi.CopyTo(dstRoi);
}
+ //Execute(mat);
+ SlicerFile[layerIndex].LayerMat = dstLayer;
+
+ progress.LockAndIncrement();
+ });
+
+ SlicerFile.BoundingRectangle = Rectangle.Empty;
- return true;
- }*/
+ progress.Token.ThrowIfCancellationRequested();
- #endregion
+ if (Anchor == Enumerations.Anchor.None) return true;
+ var operationMove = new OperationMove(SlicerFile, Anchor)
+ {
+ LayerIndexStart = LayerIndexStart,
+ LayerIndexEnd = LayerIndexEnd
+ };
+ operationMove.Execute(progress);
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ /*public override bool Execute(Mat mat, params object[] arguments)
+ {
+ using var layerRoi = new Mat(mat, ROI);
+ using var dstLayer = mat.CloneBlank();
+ for (ushort col = 0; col < Cols; col++)
+ for (ushort row = 0; row < Rows; row++)
+ {
+ var dstRoi = new Mat(dstLayer, GetRoi(col, row));
+ layerRoi.CopyTo(dstRoi);
+ }
+
+ return true;
+ }*/
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs
index d89befb..7d94fb4 100644
--- a/UVtools.Core/Operations/OperationPixelArithmetic.cs
+++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs
@@ -6,6 +6,9 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.ComponentModel;
using System.Diagnostics;
@@ -13,1304 +16,1301 @@ using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationPixelArithmetic : Operation
{
- [Serializable]
- public class OperationPixelArithmetic : Operation
+ #region Enums
+
+ public enum PixelArithmeticIgnoreAreaOperator
{
- #region Enums
+ [Description("Smaller than")]
+ SmallerThan,
+ [Description("Larger than")]
+ LargerThan
+ }
- public enum PixelArithmeticIgnoreAreaOperator
- {
- [Description("Smaller than")]
- SmallerThan,
- [Description("Larger than")]
- LargerThan
- }
+ #endregion
- #endregion
+ #region Subclasses
+ class StringMatrix
+ {
+ public string Text { get; }
+ public Matrix<byte> Pattern { get; set; } = null!;
- #region Subclasses
- class StringMatrix
+ public StringMatrix(string text)
{
- public string Text { get; }
- public Matrix<byte> Pattern { get; set; }
-
- public StringMatrix(string text)
- {
- Text = text;
- }
- }
- #endregion
-
- #region Members
- private PixelArithmeticOperators _operator = PixelArithmeticOperators.Set;
- private PixelArithmeticApplyMethod _applyMethod = PixelArithmeticApplyMethod.Model;
- private uint _wallThicknessStart = 20;
- private uint _wallThicknessEnd = 20;
- private bool _wallChamfer;
- private PixelArithmeticIgnoreAreaOperator _ignoreAreaOperator = PixelArithmeticIgnoreAreaOperator.SmallerThan;
- private uint _ignoreAreaThreshold;
- private byte _value = byte.MaxValue;
- private bool _usePattern;
- private ThresholdType _thresholdType = ThresholdType.Binary;
- private byte _thresholdMaxValue = 255;
- private ushort _patternAlternatePerLayersNumber = 1;
- private bool _patternInvert;
- private string _patternText;
- private string _patternTextAlternate;
- private Matrix<byte> _pattern;
- private Matrix<byte> _patternAlternate;
- private byte _patternGenMinBrightness;
- private byte _patternGenBrightness = 128;
- private byte _patternGenInfillThickness = 10;
- private byte _patternGenInfillSpacing = 20;
- private short _noiseMinOffset = -128;
- private short _noiseMaxOffset = 128;
- private byte _noiseThreshold;
- private ushort _noisePixelArea = 1;
- private byte _noisePasses = 1;
-
- #endregion
-
- #region Enums
- public enum PixelArithmeticOperators : byte
- {
- [Description("Set: to a brightness")]
- Set,
- [Description("Add: with a brightness")]
- Add,
- [Description("Subtract: with a brightness")]
- Subtract,
- [Description("Multiply: with a brightness")]
- Multiply,
- [Description("Divide: with a brightness")]
- Divide,
- //[Description("Exponential: pixels by a brightness")]
- //Exponential,
- [Description("Minimum: set to a brightness if is lower than the current pixel")]
- Minimum,
- [Description("Maximum: set to a brightness if is higher than the current pixel")]
- Maximum,
- [Description("Bitwise Not: invert pixels")]
- BitwiseNot,
- [Description("Bitwise And: with a brightness")]
- BitwiseAnd,
- [Description("Bitwise Or: with a brightness")]
- BitwiseOr,
- [Description("Bitwise Xor: with a brightness")]
- BitwiseXor,
- [Description("AbsDiff: perform a absolute difference between pixel and brightness")]
- AbsDiff,
- [Description("Corrode: Diffuse pixels using uniform random noise")]
- Corrode,
- [Description("Threshold: between a minimum/maximum brightness")]
- Threshold,
- [Description("Keep Region: in the selected ROI or masks")]
- KeepRegion,
- [Description("Discard Region: in the selected ROI or masks")]
- DiscardRegion,
+ Text = text;
}
+ }
+ #endregion
+
+ #region Members
+ private PixelArithmeticOperators _operator = PixelArithmeticOperators.Set;
+ private PixelArithmeticApplyMethod _applyMethod = PixelArithmeticApplyMethod.Model;
+ private uint _wallThicknessStart = 20;
+ private uint _wallThicknessEnd = 20;
+ private bool _wallChamfer;
+ private PixelArithmeticIgnoreAreaOperator _ignoreAreaOperator = PixelArithmeticIgnoreAreaOperator.SmallerThan;
+ private uint _ignoreAreaThreshold;
+ private byte _value = byte.MaxValue;
+ private bool _usePattern;
+ private ThresholdType _thresholdType = ThresholdType.Binary;
+ private byte _thresholdMaxValue = 255;
+ private ushort _patternAlternatePerLayersNumber = 1;
+ private bool _patternInvert;
+ private string _patternText = null!;
+ private string _patternTextAlternate = null!;
+ private Matrix<byte> _pattern = null!;
+ private Matrix<byte>? _patternAlternate;
+ private byte _patternGenMinBrightness;
+ private byte _patternGenBrightness = 128;
+ private byte _patternGenInfillThickness = 10;
+ private byte _patternGenInfillSpacing = 20;
+ private short _noiseMinOffset = -128;
+ private short _noiseMaxOffset = 128;
+ private byte _noiseThreshold;
+ private ushort _noisePixelArea = 1;
+ private byte _noisePasses = 1;
+
+ #endregion
+
+ #region Enums
+ public enum PixelArithmeticOperators : byte
+ {
+ [Description("Set: to a brightness")]
+ Set,
+ [Description("Add: with a brightness")]
+ Add,
+ [Description("Subtract: with a brightness")]
+ Subtract,
+ [Description("Multiply: with a brightness")]
+ Multiply,
+ [Description("Divide: with a brightness")]
+ Divide,
+ //[Description("Exponential: pixels by a brightness")]
+ //Exponential,
+ [Description("Minimum: set to a brightness if is lower than the current pixel")]
+ Minimum,
+ [Description("Maximum: set to a brightness if is higher than the current pixel")]
+ Maximum,
+ [Description("Bitwise Not: invert pixels")]
+ BitwiseNot,
+ [Description("Bitwise And: with a brightness")]
+ BitwiseAnd,
+ [Description("Bitwise Or: with a brightness")]
+ BitwiseOr,
+ [Description("Bitwise Xor: with a brightness")]
+ BitwiseXor,
+ [Description("AbsDiff: perform a absolute difference between pixel and brightness")]
+ AbsDiff,
+ [Description("Corrode: Diffuse pixels using uniform random noise")]
+ Corrode,
+ [Description("Threshold: between a minimum/maximum brightness")]
+ Threshold,
+ [Description("Keep Region: in the selected ROI or masks")]
+ KeepRegion,
+ [Description("Discard Region: in the selected ROI or masks")]
+ DiscardRegion,
+ }
- public enum PixelArithmeticApplyMethod : byte
- {
- [Description("All: Apply to all pixels within the layer")]
- All,
- [Description("Model: Apply only to model pixels")]
- Model,
- [Description("Model surface: Apply only to model surface/visible pixels")]
- ModelSurface,
- [Description("Model surface & inset: Apply only to model surface/visible pixels and within a inset from walls")]
- ModelSurfaceAndInset,
- [Description("Model inner: Apply only to model pixels within a margin from walls")]
- ModelInner,
- [Description("Model walls: Apply only to model walls with a set thickness")]
- ModelWalls,
- //[Description("Model walls minimum: Apply only to model walls where walls must have at least a minimum set thickness")]
- //ModelWallsMinimum
- }
- #endregion
+ public enum PixelArithmeticApplyMethod : byte
+ {
+ [Description("All: Apply to all pixels within the layer")]
+ All,
+ [Description("Model: Apply only to model pixels")]
+ Model,
+ [Description("Model surface: Apply only to model surface/visible pixels")]
+ ModelSurface,
+ [Description("Model surface & inset: Apply only to model surface/visible pixels and within a inset from walls")]
+ ModelSurfaceAndInset,
+ [Description("Model inner: Apply only to model pixels within a margin from walls")]
+ ModelInner,
+ [Description("Model walls: Apply only to model walls with a set thickness")]
+ ModelWalls,
+ //[Description("Model walls minimum: Apply only to model walls where walls must have at least a minimum set thickness")]
+ //ModelWallsMinimum
+ }
+ #endregion
- #region Overrides
- public override string Title => "Pixel arithmetic";
+ #region Overrides
+ public override string IconClass => "mdi-circle-opacity";
+ public override string Title => "Pixel arithmetic";
- public override string Description =>
- "Perform arithmetic operations over the pixels.";
+ public override string Description =>
+ "Perform arithmetic operations over the pixels.";
- public override string ConfirmationText =>
- $"arithmetic {_operator}" +
- (ValueEnabled && !_usePattern ? $"={_value}" : string.Empty) +
- (_usePattern && IsUsePatternVisible ? " with pattern" : string.Empty) +
- (_operator is PixelArithmeticOperators.Threshold ? $"/{_thresholdMaxValue}" : string.Empty)
- + $" layers from {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ConfirmationText =>
+ $"arithmetic {_operator}" +
+ (ValueEnabled && !_usePattern ? $"={_value}" : string.Empty) +
+ (_usePattern && IsUsePatternVisible ? " with pattern" : string.Empty) +
+ (_operator is PixelArithmeticOperators.Threshold ? $"/{_thresholdMaxValue}" : string.Empty)
+ + $" layers from {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressTitle =>
- $"Arithmetic {_operator}"+
- (ValueEnabled && !_usePattern ? $"={_value}" : string.Empty) +
- (_usePattern && IsUsePatternVisible ? " with pattern" : string.Empty) +
- $" layers from {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Arithmetic {_operator}"+
+ (ValueEnabled && !_usePattern ? $"={_value}" : string.Empty) +
+ (_usePattern && IsUsePatternVisible ? " with pattern" : string.Empty) +
+ $" layers from {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => "Calculated layers";
+ public override string ProgressAction => "Calculated layers";
- public override string ValidateInternally()
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ if (_operator == PixelArithmeticOperators.KeepRegion && !HaveROI && !HaveMask)
{
- var sb = new StringBuilder();
- if (_operator == PixelArithmeticOperators.KeepRegion && !HaveROI && !HaveMask)
- {
- sb.AppendLine("The 'Keep' operator requires selected ROI/masks.");
- }
- else if (_operator == PixelArithmeticOperators.DiscardRegion && !HaveROI && !HaveMask)
- {
- sb.AppendLine("The 'Discard' operator requires selected ROI/masks.");
- }
- else if (_operator
- is PixelArithmeticOperators.Add
- or PixelArithmeticOperators.Subtract
- or PixelArithmeticOperators.Maximum
- or PixelArithmeticOperators.BitwiseOr
- or PixelArithmeticOperators.BitwiseXor
- or PixelArithmeticOperators.AbsDiff
- && _value == 0)
- /*||
- (_operator is PixelArithmeticOperators.Exponential && _value == 1)
- )*/
- {
- sb.AppendLine($"{_operator} by {_value} will have no effect.");
- }
- else if (_operator == PixelArithmeticOperators.Divide && _value == 0)
- {
- sb.AppendLine("Can't divide by 0.");
- }
- else if (_operator == PixelArithmeticOperators.Corrode && _noiseMinOffset >= _noiseMaxOffset)
- {
- sb.AppendLine("Minimum noise offset must be less than the maximum offset.");
- }
+ sb.AppendLine("The 'Keep' operator requires selected ROI/masks.");
+ }
+ else if (_operator == PixelArithmeticOperators.DiscardRegion && !HaveROI && !HaveMask)
+ {
+ sb.AppendLine("The 'Discard' operator requires selected ROI/masks.");
+ }
+ else if (_operator
+ is PixelArithmeticOperators.Add
+ or PixelArithmeticOperators.Subtract
+ or PixelArithmeticOperators.Maximum
+ or PixelArithmeticOperators.BitwiseOr
+ or PixelArithmeticOperators.BitwiseXor
+ or PixelArithmeticOperators.AbsDiff
+ && _value == 0)
+ /*||
+ (_operator is PixelArithmeticOperators.Exponential && _value == 1)
+ )*/
+ {
+ sb.AppendLine($"{_operator} by {_value} will have no effect.");
+ }
+ else if (_operator == PixelArithmeticOperators.Divide && _value == 0)
+ {
+ sb.AppendLine("Can't divide by 0.");
+ }
+ else if (_operator == PixelArithmeticOperators.Corrode && _noiseMinOffset >= _noiseMaxOffset)
+ {
+ sb.AppendLine("Minimum noise offset must be less than the maximum offset.");
+ }
- if (_applyMethod is PixelArithmeticApplyMethod.ModelWalls //or PixelArithmeticApplyMethod.ModelWallsMinimum
- && (
- (_wallChamfer && _wallThicknessStart == 0 && _wallThicknessEnd == 0) ||
- (!_wallChamfer && _wallThicknessStart == 0)
- )
+ if (_applyMethod is PixelArithmeticApplyMethod.ModelWalls //or PixelArithmeticApplyMethod.ModelWallsMinimum
+ && (
+ (_wallChamfer && _wallThicknessStart == 0 && _wallThicknessEnd == 0) ||
+ (!_wallChamfer && _wallThicknessStart == 0)
)
- {
- sb.AppendLine("The current wall settings will have no effect.");
- }
+ )
+ {
+ sb.AppendLine("The current wall settings will have no effect.");
+ }
- if (_usePattern && IsUsePatternVisible)
+ if (_usePattern && IsUsePatternVisible)
+ {
+ var stringMatrix = new[]
{
- var stringMatrix = new[]
- {
- new StringMatrix(PatternText),
- new StringMatrix(PatternTextAlternate),
- };
+ new StringMatrix(PatternText),
+ new StringMatrix(PatternTextAlternate),
+ };
- foreach (var item in stringMatrix)
+ foreach (var item in stringMatrix)
+ {
+ if (string.IsNullOrWhiteSpace(item.Text)) continue;
+ var lines = item.Text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+ for (var row = 0; row < lines.Length; row++)
{
- if (string.IsNullOrWhiteSpace(item.Text)) continue;
- var lines = item.Text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
- for (var row = 0; row < lines.Length; row++)
+ var bytes = lines[row].Split(' ');
+ if (row == 0)
+ {
+ item.Pattern = new Matrix<byte>(lines.Length, bytes.Length);
+ }
+ else
{
- var bytes = lines[row].Split(' ');
- if (row == 0)
+ if (item.Pattern.Cols != bytes.Length)
{
- item.Pattern = new Matrix<byte>(lines.Length, bytes.Length);
+ sb.AppendLine($"Row {row + 1} have invalid number of pixels, the pattern must have equal pixel count per line, per defined on line 1");
+ return sb.ToString();
}
- else
+ }
+
+ for (int col = 0; col < bytes.Length; col++)
+ {
+ if (byte.TryParse(bytes[col], out var value))
{
- if (item.Pattern.Cols != bytes.Length)
- {
- sb.AppendLine($"Row {row + 1} have invalid number of pixels, the pattern must have equal pixel count per line, per defined on line 1");
- return sb.ToString();
- }
+ item.Pattern[row, col] = (byte)(_patternInvert ? byte.MaxValue - value : value);
}
-
- for (int col = 0; col < bytes.Length; col++)
+ else
{
- if (byte.TryParse(bytes[col], out var value))
- {
- item.Pattern[row, col] = (byte)(_patternInvert ? byte.MaxValue - value : value);
- }
- else
- {
- sb.AppendLine($"{bytes[col]} is a invalid number, use values from 0 to 255");
- return sb.ToString();
- }
+ sb.AppendLine($"{bytes[col]} is a invalid number, use values from 0 to 255");
+ return sb.ToString();
}
}
}
+ }
- _pattern = stringMatrix[0].Pattern;
- _patternAlternate = stringMatrix[1].Pattern;
+ _pattern = stringMatrix[0].Pattern;
+ _patternAlternate = stringMatrix[1].Pattern;
- if (_pattern is null && _patternAlternate is null)
- {
- sb.AppendLine("Either even or odd pattern must contain a valid matrix.");
- return sb.ToString();
- }
+ if (_pattern is null && _patternAlternate is null)
+ {
+ sb.AppendLine("Either even or odd pattern must contain a valid matrix.");
+ return sb.ToString();
}
-
- return sb.ToString();
}
- public override string ToString()
- {
- var result = $"[{_operator}: {_value}] [Apply: {_applyMethod}] " +
- $"[Pattern: {_usePattern}]"
- + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
+ return sb.ToString();
+ }
- #region Properties
+ public override string ToString()
+ {
+ var result = $"[{_operator}: {_value}] [Apply: {_applyMethod}] " +
+ $"[Pattern: {_usePattern}]"
+ + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- public PixelArithmeticOperators Operator
+ #region Properties
+
+ public PixelArithmeticOperators Operator
+ {
+ get => _operator;
+ set
{
- get => _operator;
- set
- {
- if(!RaiseAndSetIfChanged(ref _operator, value)) return;
- RaisePropertyChanged(nameof(ValueEnabled));
- RaisePropertyChanged(nameof(IsUsePatternVisible));
- RaisePropertyChanged(nameof(IsThresholdVisible));
- RaisePropertyChanged(nameof(IsApplyMethodEnabled));
- RaisePropertyChanged(nameof(IsCorrodeVisible));
- }
+ if(!RaiseAndSetIfChanged(ref _operator, value)) return;
+ RaisePropertyChanged(nameof(ValueEnabled));
+ RaisePropertyChanged(nameof(IsUsePatternVisible));
+ RaisePropertyChanged(nameof(IsThresholdVisible));
+ RaisePropertyChanged(nameof(IsApplyMethodEnabled));
+ RaisePropertyChanged(nameof(IsCorrodeVisible));
}
+ }
- public bool IsApplyMethodEnabled =>
- _operator is not (PixelArithmeticOperators.KeepRegion or PixelArithmeticOperators.DiscardRegion);
+ public bool IsApplyMethodEnabled =>
+ _operator is not (PixelArithmeticOperators.KeepRegion or PixelArithmeticOperators.DiscardRegion);
- public PixelArithmeticApplyMethod ApplyMethod
+ public PixelArithmeticApplyMethod ApplyMethod
+ {
+ get => _applyMethod;
+ set
{
- get => _applyMethod;
- set
- {
- if(!RaiseAndSetIfChanged(ref _applyMethod, value)) return;
- RaisePropertyChanged(nameof(IsWallSettingVisible));
- }
+ if(!RaiseAndSetIfChanged(ref _applyMethod, value)) return;
+ RaisePropertyChanged(nameof(IsWallSettingVisible));
}
+ }
- public bool IsWallSettingVisible => _applyMethod
- is PixelArithmeticApplyMethod.ModelSurfaceAndInset
- or PixelArithmeticApplyMethod.ModelInner
- or PixelArithmeticApplyMethod.ModelWalls; //or PixelArithmeticApplyMethod.ModelWallsMinimum;
+ public bool IsWallSettingVisible => _applyMethod
+ is PixelArithmeticApplyMethod.ModelSurfaceAndInset
+ or PixelArithmeticApplyMethod.ModelInner
+ or PixelArithmeticApplyMethod.ModelWalls; //or PixelArithmeticApplyMethod.ModelWallsMinimum;
- public uint WallThickness
+ public uint WallThickness
+ {
+ get => _wallThicknessStart;
+ set
{
- get => _wallThicknessStart;
- set
- {
- WallThicknessStart = value;
- WallThicknessEnd = value;
- }
+ WallThicknessStart = value;
+ WallThicknessEnd = value;
}
+ }
- public uint WallThicknessStart
- {
- get => _wallThicknessStart;
- set => RaiseAndSetIfChanged(ref _wallThicknessStart, value);
- }
+ public uint WallThicknessStart
+ {
+ get => _wallThicknessStart;
+ set => RaiseAndSetIfChanged(ref _wallThicknessStart, value);
+ }
- public uint WallThicknessEnd
- {
- get => _wallThicknessEnd;
- set => RaiseAndSetIfChanged(ref _wallThicknessEnd, value);
- }
+ public uint WallThicknessEnd
+ {
+ get => _wallThicknessEnd;
+ set => RaiseAndSetIfChanged(ref _wallThicknessEnd, value);
+ }
- public bool WallChamfer
- {
- get => _wallChamfer;
- set => RaiseAndSetIfChanged(ref _wallChamfer, value);
- }
+ public bool WallChamfer
+ {
+ get => _wallChamfer;
+ set => RaiseAndSetIfChanged(ref _wallChamfer, value);
+ }
- public PixelArithmeticIgnoreAreaOperator IgnoreAreaOperator
- {
- get => _ignoreAreaOperator;
- set => RaiseAndSetIfChanged(ref _ignoreAreaOperator, value);
- }
+ public PixelArithmeticIgnoreAreaOperator IgnoreAreaOperator
+ {
+ get => _ignoreAreaOperator;
+ set => RaiseAndSetIfChanged(ref _ignoreAreaOperator, value);
+ }
- public uint IgnoreAreaThreshold
- {
- get => _ignoreAreaThreshold;
- set => RaiseAndSetIfChanged(ref _ignoreAreaThreshold, value);
- }
+ public uint IgnoreAreaThreshold
+ {
+ get => _ignoreAreaThreshold;
+ set => RaiseAndSetIfChanged(ref _ignoreAreaThreshold, value);
+ }
- public bool IsCorrodeVisible => _operator is PixelArithmeticOperators.Corrode;
+ public bool IsCorrodeVisible => _operator is PixelArithmeticOperators.Corrode;
- public short NoiseMinOffset
- {
- get => _noiseMinOffset;
- set => RaiseAndSetIfChanged(ref _noiseMinOffset, value);
- }
+ public short NoiseMinOffset
+ {
+ get => _noiseMinOffset;
+ set => RaiseAndSetIfChanged(ref _noiseMinOffset, value);
+ }
- public short NoiseMaxOffset
- {
- get => _noiseMaxOffset;
- set => RaiseAndSetIfChanged(ref _noiseMaxOffset, value);
- }
+ public short NoiseMaxOffset
+ {
+ get => _noiseMaxOffset;
+ set => RaiseAndSetIfChanged(ref _noiseMaxOffset, value);
+ }
- public byte NoiseThreshold
- {
- get => _noiseThreshold;
- set => RaiseAndSetIfChanged(ref _noiseThreshold, value);
- }
+ public byte NoiseThreshold
+ {
+ get => _noiseThreshold;
+ set => RaiseAndSetIfChanged(ref _noiseThreshold, value);
+ }
- public ushort NoisePixelArea
- {
- get => _noisePixelArea;
- set => RaiseAndSetIfChanged(ref _noisePixelArea, Math.Max((byte)1, value));
- }
+ public ushort NoisePixelArea
+ {
+ get => _noisePixelArea;
+ set => RaiseAndSetIfChanged(ref _noisePixelArea, Math.Max((byte)1, value));
+ }
- public byte NoisePasses
- {
- get => _noisePasses;
- set => RaiseAndSetIfChanged(ref _noisePasses, Math.Max((byte)1, value));
- }
+ public byte NoisePasses
+ {
+ get => _noisePasses;
+ set => RaiseAndSetIfChanged(ref _noisePasses, Math.Max((byte)1, value));
+ }
- public byte Value
+ public byte Value
+ {
+ get => _value;
+ set
{
- get => _value;
- set
- {
- if(!RaiseAndSetIfChanged(ref _value, value)) return;
- RaisePropertyChanged(nameof(ValuePercent));
- }
+ if(!RaiseAndSetIfChanged(ref _value, value)) return;
+ RaisePropertyChanged(nameof(ValuePercent));
}
+ }
- // 255 - 100
- //value - x
- public float ValuePercent => (float) Math.Round(_value * 100f / byte.MaxValue, 2);
-
- public bool ValueEnabled => _operator
- is not PixelArithmeticOperators.BitwiseNot
- and not PixelArithmeticOperators.KeepRegion
- and not PixelArithmeticOperators.DiscardRegion
- and not PixelArithmeticOperators.Corrode
- ;
-
- public bool IsUsePatternVisible => _operator
- is not PixelArithmeticOperators.Threshold
- and not PixelArithmeticOperators.BitwiseNot
- and not PixelArithmeticOperators.KeepRegion
- and not PixelArithmeticOperators.DiscardRegion
- and not PixelArithmeticOperators.Corrode
- ;
-
- public bool UsePattern
- {
- get => _usePattern;
- set => RaiseAndSetIfChanged(ref _usePattern, value);
- }
+ // 255 - 100
+ //value - x
+ public float ValuePercent => (float) Math.Round(_value * 100f / byte.MaxValue, 2);
+
+ public bool ValueEnabled => _operator
+ is not PixelArithmeticOperators.BitwiseNot
+ and not PixelArithmeticOperators.KeepRegion
+ and not PixelArithmeticOperators.DiscardRegion
+ and not PixelArithmeticOperators.Corrode
+ ;
+
+ public bool IsUsePatternVisible => _operator
+ is not PixelArithmeticOperators.Threshold
+ and not PixelArithmeticOperators.BitwiseNot
+ and not PixelArithmeticOperators.KeepRegion
+ and not PixelArithmeticOperators.DiscardRegion
+ and not PixelArithmeticOperators.Corrode
+ ;
+
+ public bool UsePattern
+ {
+ get => _usePattern;
+ set => RaiseAndSetIfChanged(ref _usePattern, value);
+ }
- public ThresholdType ThresholdType
- {
- get => _thresholdType;
- set => RaiseAndSetIfChanged(ref _thresholdType, value);
- }
+ public ThresholdType ThresholdType
+ {
+ get => _thresholdType;
+ set => RaiseAndSetIfChanged(ref _thresholdType, value);
+ }
- public byte ThresholdMaxValue
- {
- get => _thresholdMaxValue;
- set => RaiseAndSetIfChanged(ref _thresholdMaxValue, value);
- }
+ public byte ThresholdMaxValue
+ {
+ get => _thresholdMaxValue;
+ set => RaiseAndSetIfChanged(ref _thresholdMaxValue, value);
+ }
- public bool IsThresholdVisible => _operator is PixelArithmeticOperators.Threshold;
+ public bool IsThresholdVisible => _operator is PixelArithmeticOperators.Threshold;
- /*public bool AffectBackPixelsEnabled => _operator
- is not PixelArithmeticOperators.Subtract
- and not PixelArithmeticOperators.Multiply
- and not PixelArithmeticOperators.Divide
- and not PixelArithmeticOperators.BitwiseNot
- and not PixelArithmeticOperators.BitwiseAnd
- and not PixelArithmeticOperators.KeepRegion
- and not PixelArithmeticOperators.DiscardRegion
- and not PixelArithmeticOperators.Threshold
- ;*/
+ /*public bool AffectBackPixelsEnabled => _operator
+ is not PixelArithmeticOperators.Subtract
+ and not PixelArithmeticOperators.Multiply
+ and not PixelArithmeticOperators.Divide
+ and not PixelArithmeticOperators.BitwiseNot
+ and not PixelArithmeticOperators.BitwiseAnd
+ and not PixelArithmeticOperators.KeepRegion
+ and not PixelArithmeticOperators.DiscardRegion
+ and not PixelArithmeticOperators.Threshold
+ ;*/
- public ushort PatternAlternatePerLayersNumber
- {
- get => _patternAlternatePerLayersNumber;
- set => RaiseAndSetIfChanged(ref _patternAlternatePerLayersNumber, value);
- }
+ public ushort PatternAlternatePerLayersNumber
+ {
+ get => _patternAlternatePerLayersNumber;
+ set => RaiseAndSetIfChanged(ref _patternAlternatePerLayersNumber, value);
+ }
- public bool PatternInvert
- {
- get => _patternInvert;
- set => RaiseAndSetIfChanged(ref _patternInvert, value);
- }
+ public bool PatternInvert
+ {
+ get => _patternInvert;
+ set => RaiseAndSetIfChanged(ref _patternInvert, value);
+ }
- public string PatternText
- {
- get => _patternText;
- set => RaiseAndSetIfChanged(ref _patternText, value);
- }
+ public string PatternText
+ {
+ get => _patternText;
+ set => RaiseAndSetIfChanged(ref _patternText, value);
+ }
- public string PatternTextAlternate
- {
- get => _patternTextAlternate;
- set => RaiseAndSetIfChanged(ref _patternTextAlternate, value);
- }
+ public string PatternTextAlternate
+ {
+ get => _patternTextAlternate;
+ set => RaiseAndSetIfChanged(ref _patternTextAlternate, value);
+ }
- [XmlIgnore]
- public Matrix<byte> Pattern
- {
- get => _pattern;
- set => RaiseAndSetIfChanged(ref _pattern, value);
- }
+ [XmlIgnore]
+ public Matrix<byte> Pattern
+ {
+ get => _pattern;
+ set => RaiseAndSetIfChanged(ref _pattern, value);
+ }
- [XmlIgnore]
- public Matrix<byte> PatternAlternate
- {
- get => _patternAlternate;
- set => RaiseAndSetIfChanged(ref _patternAlternate, value);
- }
+ [XmlIgnore]
+ public Matrix<byte>? PatternAlternate
+ {
+ get => _patternAlternate;
+ set => RaiseAndSetIfChanged(ref _patternAlternate, value);
+ }
- public byte PatternGenMinBrightness
- {
- get => _patternGenMinBrightness;
- set => RaiseAndSetIfChanged(ref _patternGenMinBrightness, value);
- }
+ public byte PatternGenMinBrightness
+ {
+ get => _patternGenMinBrightness;
+ set => RaiseAndSetIfChanged(ref _patternGenMinBrightness, value);
+ }
- public byte PatternGenBrightness
+ public byte PatternGenBrightness
+ {
+ get => _patternGenBrightness;
+ set
{
- get => _patternGenBrightness;
- set
- {
- RaiseAndSetIfChanged(ref _patternGenBrightness, value);
- RaisePropertyChanged(nameof(PatternGenBrightnessPercent));
- }
+ RaiseAndSetIfChanged(ref _patternGenBrightness, value);
+ RaisePropertyChanged(nameof(PatternGenBrightnessPercent));
}
+ }
- public float PatternGenBrightnessPercent => Helpers.BrightnessToPercent(_patternGenBrightness);
+ public float PatternGenBrightnessPercent => Helpers.BrightnessToPercent(_patternGenBrightness);
- public byte PatternGenInfillThickness
- {
- get => _patternGenInfillThickness;
- set => RaiseAndSetIfChanged(ref _patternGenInfillThickness, value);
- }
+ public byte PatternGenInfillThickness
+ {
+ get => _patternGenInfillThickness;
+ set => RaiseAndSetIfChanged(ref _patternGenInfillThickness, value);
+ }
- public byte PatternGenInfillSpacing
- {
- get => _patternGenInfillSpacing;
- set => RaiseAndSetIfChanged(ref _patternGenInfillSpacing, value);
- }
+ public byte PatternGenInfillSpacing
+ {
+ get => _patternGenInfillSpacing;
+ set => RaiseAndSetIfChanged(ref _patternGenInfillSpacing, value);
+ }
- public KernelConfiguration Kernel { get; set; } = new();
+ public KernelConfiguration Kernel { get; set; } = new();
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationPixelArithmetic() { }
+ public OperationPixelArithmetic() { }
- public OperationPixelArithmetic(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationPixelArithmetic(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- private Size GetMatSizeCropped(Mat mat = null)
- {
- return _applyMethod == PixelArithmeticApplyMethod.All ? GetRoiSizeOrDefault(mat) : GetRoiSizeOrDefault(OriginalBoundingRectangle);
- }
+ private Size GetMatSizeCropped(Mat? mat = null)
+ {
+ return _applyMethod == PixelArithmeticApplyMethod.All ? GetRoiSizeOrDefault(mat) : GetRoiSizeOrDefault(OriginalBoundingRectangle);
+ }
- private Mat GetMatRoiCropped(Mat mat)
- {
- return _applyMethod == PixelArithmeticApplyMethod.All ? GetRoiOrDefault(mat) : GetRoiOrVolumeBounds(mat);
- }
+ private Mat GetMatRoiCropped(Mat mat)
+ {
+ return _applyMethod == PixelArithmeticApplyMethod.All ? GetRoiOrDefault(mat) : GetRoiOrVolumeBounds(mat);
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- Mat patternMat = null;
- Mat patternAlternateMat = null;
- Mat patternMatMask = null;
- Mat patternAlternateMatMask = null;
- var anchor = new Point(-1, -1);
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Mat? patternMat = null;
+ Mat? patternAlternateMat = null;
+ Mat patternMatMask = null!;
+ Mat patternAlternateMatMask = null!;
+ var anchor = new Point(-1, -1);
- if (_usePattern && IsUsePatternVisible)
+ if (_usePattern && IsUsePatternVisible)
+ {
+ if (_pattern is null)
{
- if (_pattern is null)
+ _pattern = new Matrix<byte>(2, 2)
{
- _pattern = new Matrix<byte>(2, 2)
- {
- [0, 0] = 0,
- [0, 1] = 127,
- [1, 0] = 127,
- [1, 1] = 0,
- };
+ [0, 0] = 0,
+ [0, 1] = 127,
+ [1, 0] = 127,
+ [1, 1] = 0,
+ };
- _patternAlternate ??= new Matrix<byte>(2, 2)
- {
- [0, 0] = 127,
- [0, 1] = 0,
- [1, 0] = 0,
- [1, 1] = 127,
- };
- }
+ _patternAlternate ??= new Matrix<byte>(2, 2)
+ {
+ [0, 0] = 127,
+ [0, 1] = 0,
+ [1, 0] = 0,
+ [1, 1] = 127,
+ };
+ }
- _patternAlternate ??= _pattern;
+ _patternAlternate ??= _pattern;
- var target = new Mat(GetMatSizeCropped(), DepthType.Cv8U, 1);
- patternMat = target.NewBlank();
- patternAlternateMat = target.NewBlank();
+ var target = new Mat(GetMatSizeCropped(), DepthType.Cv8U, 1);
+ patternMat = target.NewBlank();
+ patternAlternateMat = target.NewBlank();
- CvInvoke.Repeat(_pattern, (int)Math.Ceiling((double)target.Rows / _pattern.Rows), (int)Math.Ceiling((double)target.Cols / _pattern.Cols), patternMat);
- CvInvoke.Repeat(_patternAlternate, (int)Math.Ceiling((double)target.Rows / _patternAlternate.Rows), (int)Math.Ceiling((double)target.Cols / _patternAlternate.Cols), patternAlternateMat);
+ CvInvoke.Repeat(_pattern, (int)Math.Ceiling((double)target.Rows / _pattern.Rows), (int)Math.Ceiling((double)target.Cols / _pattern.Cols), patternMat);
+ CvInvoke.Repeat(_patternAlternate, (int)Math.Ceiling((double)target.Rows / _patternAlternate.Rows), (int)Math.Ceiling((double)target.Cols / _patternAlternate.Cols), patternAlternateMat);
- patternMatMask = patternMat.Roi(target);
- patternAlternateMatMask = patternAlternateMat.Roi(target);
+ patternMatMask = patternMat.Roi(target);
+ patternAlternateMatMask = patternAlternateMat.Roi(target);
- /*if (_patternInvert)
- {
- CvInvoke.BitwiseNot(patternMatMask, patternMatMask);
- CvInvoke.BitwiseNot(patternAlternateMatMask, patternAlternateMatMask);
- }*/
- }
- else if (IsUsePatternVisible)
+ /*if (_patternInvert)
{
- patternMatMask = EmguExtensions.InitMat(GetMatSizeCropped(), new MCvScalar(_value));
- }
+ CvInvoke.BitwiseNot(patternMatMask, patternMatMask);
+ CvInvoke.BitwiseNot(patternAlternateMatMask, patternAlternateMatMask);
+ }*/
+ }
+ else if (IsUsePatternVisible)
+ {
+ patternMatMask = EmguExtensions.InitMat(GetMatSizeCropped(), new MCvScalar(_value));
+ }
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = SlicerFile[layerIndex];
+ using (var mat = layer.LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- var layer = SlicerFile[layerIndex];
- using (var mat = layer.LayerMat)
- {
- using var original = mat.Clone();
- var originalRoi = GetMatRoiCropped(original);
- var target = GetMatRoiCropped(mat);
- Mat tempMat;
+ using var original = mat.Clone();
+ var originalRoi = GetMatRoiCropped(original);
+ var target = GetMatRoiCropped(mat);
+ Mat tempMat;
- if (_usePattern && IsUsePatternVisible)
- {
- tempMat = IsNormalPattern((uint)layerIndex) ? patternMatMask : patternAlternateMatMask;
- }
- else
- {
- tempMat = patternMatMask;
- }
+ if (_usePattern && IsUsePatternVisible)
+ {
+ tempMat = IsNormalPattern((uint)layerIndex) ? patternMatMask : patternAlternateMatMask;
+ }
+ else
+ {
+ tempMat = patternMatMask;
+ }
- Mat applyMask;
+ Mat? applyMask;
- int wallThickness = LayerManager.MutateGetIterationChamfer(
- (uint)layerIndex,
- LayerIndexStart,
- LayerIndexEnd,
- (int)_wallThicknessStart,
- (int)_wallThicknessEnd,
- _wallChamfer
- );
-
- switch (_applyMethod)
- {
- case PixelArithmeticApplyMethod.All:
- applyMask = null;
- break;
- case PixelArithmeticApplyMethod.Model:
- applyMask = target.Clone();
- break;
- case PixelArithmeticApplyMethod.ModelSurface:
- case PixelArithmeticApplyMethod.ModelSurfaceAndInset:
- if (layerIndex == SlicerFile.LastLayerIndex)
- {
- applyMask = target.Clone();
- }
- else
- {
- applyMask = new Mat();
-
- // Difference
- using var nextMat = SlicerFile[layerIndex + 1].LayerMat;
- var nextMatRoi = GetMatRoiCropped(nextMat);
- CvInvoke.Subtract(target, nextMatRoi, applyMask);
-
- // 1px walls
- using var erode = new Mat();
- int iterations = 1;
- var kernel = Kernel.GetKernel(ref iterations);
- CvInvoke.Erode(target, erode, kernel, anchor, iterations, BorderType.Reflect101, default);
- CvInvoke.Subtract(target, erode, erode);
- CvInvoke.Add(applyMask, erode, applyMask);
-
-
- // Inset from walls
- if (_applyMethod == PixelArithmeticApplyMethod.ModelSurfaceAndInset && (wallThickness-1) > 0)
- {
- iterations = wallThickness - 1;
- kernel = Kernel.GetKernel(ref iterations);
- CvInvoke.Dilate(applyMask, erode, kernel, anchor, iterations, BorderType.Reflect101, default);
- erode.CopyTo(applyMask, target);
- }
- }
-
- break;
- case PixelArithmeticApplyMethod.ModelInner:
+ int wallThickness = FileFormat.MutateGetIterationChamfer(
+ (uint)layerIndex,
+ LayerIndexStart,
+ LayerIndexEnd,
+ (int)_wallThicknessStart,
+ (int)_wallThicknessEnd,
+ _wallChamfer
+ );
+
+ switch (_applyMethod)
+ {
+ case PixelArithmeticApplyMethod.All:
+ applyMask = null;
+ break;
+ case PixelArithmeticApplyMethod.Model:
+ applyMask = target.Clone();
+ break;
+ case PixelArithmeticApplyMethod.ModelSurface:
+ case PixelArithmeticApplyMethod.ModelSurfaceAndInset:
+ if (layerIndex == SlicerFile.LastLayerIndex)
{
- if (wallThickness <= 0)
- {
- applyMask = target.Clone();
- break;
- }
-
- applyMask = new Mat();
- int iterations = wallThickness;
- var kernel = Kernel.GetKernel(ref iterations);
- CvInvoke.Erode(target, applyMask, kernel, anchor, iterations, BorderType.Reflect101, default);
- break;
+ applyMask = target.Clone();
}
- case PixelArithmeticApplyMethod.ModelWalls:
+ else
{
- if (wallThickness <= 0) // No effect, skip
- {
- progress.LockAndIncrement();
- return;
- }
+ applyMask = new Mat();
+
+ // Difference
+ using var nextMat = SlicerFile[layerIndex + 1].LayerMat;
+ var nextMatRoi = GetMatRoiCropped(nextMat);
+ CvInvoke.Subtract(target, nextMatRoi, applyMask);
+ // 1px walls
using var erode = new Mat();
- applyMask = target.Clone();
- int iterations = wallThickness;
+ int iterations = 1;
var kernel = Kernel.GetKernel(ref iterations);
CvInvoke.Erode(target, erode, kernel, anchor, iterations, BorderType.Reflect101, default);
- applyMask.SetTo(EmguExtensions.BlackColor, erode);
- break;
- }
- /*case PixelArithmeticApplyMethod.ModelWallsMinimum:
- {
- if (wallThickness <= 0) // No effect, skip
+ CvInvoke.Subtract(target, erode, erode);
+ CvInvoke.Add(applyMask, erode, applyMask);
+
+
+ // Inset from walls
+ if (_applyMethod == PixelArithmeticApplyMethod.ModelSurfaceAndInset && (wallThickness-1) > 0)
{
- progress.LockAndIncrement();
- return;
+ iterations = wallThickness - 1;
+ kernel = Kernel.GetKernel(ref iterations);
+ CvInvoke.Dilate(applyMask, erode, kernel, anchor, iterations, BorderType.Reflect101, default);
+ erode.CopyTo(applyMask, target);
}
+ }
- using var erode = new Mat();
- using var erodeInv = new Mat();
+ break;
+ case PixelArithmeticApplyMethod.ModelInner:
+ {
+ if (wallThickness <= 0)
+ {
applyMask = target.Clone();
- target.Save($"D:\\wallmin\\original{layerIndex}.png");
- CvInvoke.Erode(target, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
- erode.Save($"D:\\wallmin\\erode{layerIndex}.png");
- CvInvoke.Dilate(erode, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
- erode.Save($"D:\\wallmin\\dilate{layerIndex}.png");
- //CvInvoke.BitwiseXor(target, erode, applyMask);
- //applyMask.Save($"D:\\wallmin\\bitwiseXor{layerIndex}.png");
- CvInvoke.BitwiseNot(erode, erodeInv);
- erodeInv.Save($"D:\\wallmin\\erodeInv{layerIndex}.png");
- CvInvoke.BitwiseXor(target, erode, erode, erodeInv);
- erode.Save($"D:\\wallmin\\BitwiseXor{layerIndex}.png");
- applyMask.SetTo(EmguExtensions.BlackColor, erode);
- applyMask.Save($"D:\\wallmin\\applymask{layerIndex}.png");
- break;
- }*/
- default:
- throw new ArgumentOutOfRangeException();
+ break;
+ }
+
+ applyMask = new Mat();
+ int iterations = wallThickness;
+ var kernel = Kernel.GetKernel(ref iterations);
+ CvInvoke.Erode(target, applyMask, kernel, anchor, iterations, BorderType.Reflect101, default);
+ break;
}
+ case PixelArithmeticApplyMethod.ModelWalls:
+ {
+ if (wallThickness <= 0) // No effect, skip
+ {
+ progress.LockAndIncrement();
+ return;
+ }
- switch (_operator)
+ using var erode = new Mat();
+ applyMask = target.Clone();
+ int iterations = wallThickness;
+ var kernel = Kernel.GetKernel(ref iterations);
+ CvInvoke.Erode(target, erode, kernel, anchor, iterations, BorderType.Reflect101, default);
+ applyMask.SetTo(EmguExtensions.BlackColor, erode);
+ break;
+ }
+ /*case PixelArithmeticApplyMethod.ModelWallsMinimum:
{
- case PixelArithmeticOperators.Set:
- tempMat.CopyTo(target, applyMask);
- break;
- case PixelArithmeticOperators.Add:
- CvInvoke.Add(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.Subtract:
- CvInvoke.Subtract(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.Multiply:
- CvInvoke.Multiply(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
- break;
- case PixelArithmeticOperators.Divide:
- CvInvoke.Divide(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
- break;
- /*case PixelArithmeticOperators.Exponential:
- CvInvoke.Pow(target, _value, tempMat);
- if(!_affectBackPixels) ApplyMask(original, mat, original);
- break;*/
- case PixelArithmeticOperators.Minimum:
- CvInvoke.Min(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
- break;
- case PixelArithmeticOperators.Maximum:
- CvInvoke.Max(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
- break;
- case PixelArithmeticOperators.BitwiseNot:
- CvInvoke.BitwiseNot(target, target, applyMask);
- break;
- case PixelArithmeticOperators.BitwiseAnd:
- CvInvoke.BitwiseAnd(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.BitwiseOr:
- CvInvoke.BitwiseOr(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.BitwiseXor:
- CvInvoke.BitwiseXor(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.AbsDiff:
- CvInvoke.AbsDiff(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
- break;
- case PixelArithmeticOperators.Threshold:
- var tempThreshold = _thresholdType;
- if (_thresholdType is ThresholdType.Otsu or ThresholdType.Triangle) tempThreshold = ThresholdType.Binary | tempThreshold;
- CvInvoke.Threshold(target, target, _value, _thresholdMaxValue, tempThreshold);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
- break;
- case PixelArithmeticOperators.Corrode:
- var span = mat.GetDataByteSpan();
- var random = new Random();
+ if (wallThickness <= 0) // No effect, skip
+ {
+ progress.LockAndIncrement();
+ return;
+ }
- var bounds = HaveROI ? ROI : layer.BoundingRectangle;
+ using var erode = new Mat();
+ using var erodeInv = new Mat();
+ applyMask = target.Clone();
+ target.Save($"D:\\wallmin\\original{layerIndex}.png");
+ CvInvoke.Erode(target, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
+ erode.Save($"D:\\wallmin\\erode{layerIndex}.png");
+ CvInvoke.Dilate(erode, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
+ erode.Save($"D:\\wallmin\\dilate{layerIndex}.png");
+ //CvInvoke.BitwiseXor(target, erode, applyMask);
+ //applyMask.Save($"D:\\wallmin\\bitwiseXor{layerIndex}.png");
+ CvInvoke.BitwiseNot(erode, erodeInv);
+ erodeInv.Save($"D:\\wallmin\\erodeInv{layerIndex}.png");
+ CvInvoke.BitwiseXor(target, erode, erode, erodeInv);
+ erode.Save($"D:\\wallmin\\BitwiseXor{layerIndex}.png");
+ applyMask.SetTo(EmguExtensions.BlackColor, erode);
+ applyMask.Save($"D:\\wallmin\\applymask{layerIndex}.png");
+ break;
+ }*/
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
- for (var y = bounds.Y; y < bounds.Bottom; y += _noisePixelArea)
- for (var x = bounds.X; x < bounds.Right; x += _noisePixelArea)
+ switch (_operator)
+ {
+ case PixelArithmeticOperators.Set:
+ tempMat.CopyTo(target, applyMask);
+ break;
+ case PixelArithmeticOperators.Add:
+ CvInvoke.Add(target, tempMat, target, applyMask);
+ break;
+ case PixelArithmeticOperators.Subtract:
+ CvInvoke.Subtract(target, tempMat, target, applyMask);
+ break;
+ case PixelArithmeticOperators.Multiply:
+ CvInvoke.Multiply(target, tempMat, target);
+ if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
+ break;
+ case PixelArithmeticOperators.Divide:
+ CvInvoke.Divide(target, tempMat, target);
+ if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
+ break;
+ /*case PixelArithmeticOperators.Exponential:
+ CvInvoke.Pow(target, _value, tempMat);
+ if(!_affectBackPixels) ApplyMask(original, mat, original);
+ break;*/
+ case PixelArithmeticOperators.Minimum:
+ CvInvoke.Min(target, tempMat, target);
+ if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
+ break;
+ case PixelArithmeticOperators.Maximum:
+ CvInvoke.Max(target, tempMat, target);
+ if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
+ break;
+ case PixelArithmeticOperators.BitwiseNot:
+ CvInvoke.BitwiseNot(target, target, applyMask);
+ break;
+ case PixelArithmeticOperators.BitwiseAnd:
+ CvInvoke.BitwiseAnd(target, tempMat, target, applyMask);
+ break;
+ case PixelArithmeticOperators.BitwiseOr:
+ CvInvoke.BitwiseOr(target, tempMat, target, applyMask);
+ break;
+ case PixelArithmeticOperators.BitwiseXor:
+ CvInvoke.BitwiseXor(target, tempMat, target, applyMask);
+ break;
+ case PixelArithmeticOperators.AbsDiff:
+ CvInvoke.AbsDiff(target, tempMat, target);
+ if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
+ break;
+ case PixelArithmeticOperators.Threshold:
+ var tempThreshold = _thresholdType;
+ if (_thresholdType is ThresholdType.Otsu or ThresholdType.Triangle) tempThreshold = ThresholdType.Binary | tempThreshold;
+ CvInvoke.Threshold(target, target, _value, _thresholdMaxValue, tempThreshold);
+ if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
+ break;
+ case PixelArithmeticOperators.Corrode:
+ var span = mat.GetDataByteSpan();
+ var random = new Random();
+
+ var bounds = HaveROI ? ROI : layer.BoundingRectangle;
+
+ for (var y = bounds.Y; y < bounds.Bottom; y += _noisePixelArea)
+ for (var x = bounds.X; x < bounds.Right; x += _noisePixelArea)
+ {
+ byte zoneBrightness = 0;
+ for (var y1 = y; y1 < y + _noisePixelArea && y1 < bounds.Bottom && zoneBrightness < byte.MaxValue; y1++)
{
- byte zoneBrightness = 0;
- for (var y1 = y; y1 < y + _noisePixelArea && y1 < bounds.Bottom && zoneBrightness < byte.MaxValue; y1++)
+ var pixelPos = mat.GetPixelPos(x, y1);
+ for (var x1 = x; x1 < x + _noisePixelArea && x1 < bounds.Right && zoneBrightness < byte.MaxValue; x1++)
{
- var pixelPos = mat.GetPixelPos(x, y1);
- for (var x1 = x; x1 < x + _noisePixelArea && x1 < bounds.Right && zoneBrightness < byte.MaxValue; x1++)
- {
- zoneBrightness = Math.Max(zoneBrightness, span[pixelPos++]);
- }
+ zoneBrightness = Math.Max(zoneBrightness, span[pixelPos++]);
}
+ }
- if (zoneBrightness <= _noiseThreshold) continue;
- byte brightness = zoneBrightness;
+ if (zoneBrightness <= _noiseThreshold) continue;
+ byte brightness = zoneBrightness;
- for (ushort i = 0; i < _noisePasses; i++)
- {
- brightness = (byte)Math.Clamp(random.Next(_noiseMinOffset, _noiseMaxOffset + 1) + brightness, byte.MinValue, byte.MaxValue);
- }
+ for (ushort i = 0; i < _noisePasses; i++)
+ {
+ brightness = (byte)Math.Clamp(random.Next(_noiseMinOffset, _noiseMaxOffset + 1) + brightness, byte.MinValue, byte.MaxValue);
+ }
- //byte brightness = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + zoneBrightness, byte.MinValue, byte.MaxValue);
- for (var y1 = y; y1 < y + _noisePixelArea && y1 < bounds.Bottom; y1++)
+ //byte brightness = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + zoneBrightness, byte.MinValue, byte.MaxValue);
+ for (var y1 = y; y1 < y + _noisePixelArea && y1 < bounds.Bottom; y1++)
+ {
+ var pixelPos = mat.GetPixelPos(x, y1);
+ for (var x1 = x; x1 < x + _noisePixelArea && x1 < bounds.Right; x1++)
{
- var pixelPos = mat.GetPixelPos(x, y1);
- for (var x1 = x; x1 < x + _noisePixelArea && x1 < bounds.Right; x1++)
- {
- if (span[pixelPos] <= _noiseThreshold) continue;
- span[pixelPos++] = brightness;
- }
+ if (span[pixelPos] <= _noiseThreshold) continue;
+ span[pixelPos++] = brightness;
}
}
+ }
- if (_applyMethod is not PixelArithmeticApplyMethod.All and not PixelArithmeticApplyMethod.Model) ApplyMask(originalRoi, target, applyMask);
+ if (_applyMethod is not PixelArithmeticApplyMethod.All and not PixelArithmeticApplyMethod.Model) ApplyMask(originalRoi, target, applyMask);
- // old method
- /*if (HaveROI)
+ // old method
+ /*if (HaveROI)
+ {
+ for (var y = ROI.Y; y < ROI.Bottom; y++)
+ for (var x = ROI.X; x < ROI.Right; x++)
{
- for (var y = ROI.Y; y < ROI.Bottom; y++)
- for (var x = ROI.X; x < ROI.Right; x++)
- {
- var pos = mat.GetPixelPos(x, y);
- if (span[pos] <= _noiseThreshold) continue;
- span[pos] = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + span[pos], byte.MinValue, byte.MaxValue);
- }
-
- if (_applyMethod
- is not PixelArithmeticApplyMethod.All
- and not PixelArithmeticApplyMethod.Model)
- ApplyMask(originalRoi, target, applyMask);
+ var pos = mat.GetPixelPos(x, y);
+ if (span[pos] <= _noiseThreshold) continue;
+ span[pos] = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + span[pos], byte.MinValue, byte.MaxValue);
}
- else // Whole image
- {
- var spanMask = applyMask is null ? span : applyMask.GetDataByteSpan();
- for (var i = 0; i < span.Length; i++)
- {
- //if (span[i] <= _noiseThreshold || spanMask[i] == 0) continue;
- //span[i] = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + span[i], byte.MinValue, byte.MaxValue);
- span[i] = (byte)Math.Clamp(random.Next(_noiseMinOffset, _noiseMaxOffset + 1) + span[i], byte.MinValue, byte.MaxValue);
- }
- }*/
+ if (_applyMethod
+ is not PixelArithmeticApplyMethod.All
+ and not PixelArithmeticApplyMethod.Model)
+ ApplyMask(originalRoi, target, applyMask);
+ }
+ else // Whole image
+ {
+ var spanMask = applyMask is null ? span : applyMask.GetDataByteSpan();
- break;
- case PixelArithmeticOperators.KeepRegion:
+ for (var i = 0; i < span.Length; i++)
{
- using var targetClone = target.Clone();
- original.SetTo(EmguExtensions.BlackColor);
- mat.SetTo(EmguExtensions.BlackColor);
- targetClone.CopyTo(target);
- break;
+ //if (span[i] <= _noiseThreshold || spanMask[i] == 0) continue;
+ //span[i] = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + span[i], byte.MinValue, byte.MaxValue);
+ span[i] = (byte)Math.Clamp(random.Next(_noiseMinOffset, _noiseMaxOffset + 1) + span[i], byte.MinValue, byte.MaxValue);
}
- case PixelArithmeticOperators.DiscardRegion:
- target.SetTo(EmguExtensions.BlackColor);
- break;
- default:
- throw new NotImplementedException();
- }
+ }*/
- switch (_ignoreAreaOperator)
+ break;
+ case PixelArithmeticOperators.KeepRegion:
{
- case PixelArithmeticIgnoreAreaOperator.SmallerThan:
- originalRoi.CopyAreasSmallerThan(_ignoreAreaThreshold, target);
- break;
- case PixelArithmeticIgnoreAreaOperator.LargerThan:
- originalRoi.CopyAreasLargerThan(_ignoreAreaThreshold, target);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(_ignoreAreaOperator));
+ using var targetClone = target.Clone();
+ original.SetTo(EmguExtensions.BlackColor);
+ mat.SetTo(EmguExtensions.BlackColor);
+ targetClone.CopyTo(target);
+ break;
}
- ApplyMask(originalRoi, target);
-
- SlicerFile[layerIndex].LayerMat = mat;
+ case PixelArithmeticOperators.DiscardRegion:
+ target.SetTo(EmguExtensions.BlackColor);
+ break;
+ default:
+ throw new NotImplementedException();
+ }
- if (applyMask is not null && !ReferenceEquals(applyMask, target)) applyMask.Dispose();
+ switch (_ignoreAreaOperator)
+ {
+ case PixelArithmeticIgnoreAreaOperator.SmallerThan:
+ originalRoi.CopyAreasSmallerThan(_ignoreAreaThreshold, target);
+ break;
+ case PixelArithmeticIgnoreAreaOperator.LargerThan:
+ originalRoi.CopyAreasLargerThan(_ignoreAreaThreshold, target);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(_ignoreAreaOperator));
}
+ ApplyMask(originalRoi, target);
- progress.LockAndIncrement();
- });
+ SlicerFile[layerIndex].LayerMat = mat;
- patternMat?.Dispose();
- patternAlternateMat?.Dispose();
+ if (applyMask is not null && !ReferenceEquals(applyMask, target)) applyMask.Dispose();
+ }
- return !progress.Token.IsCancellationRequested;
- }
+ progress.LockAndIncrement();
+ });
- public bool IsNormalPattern(uint layerIndex) => layerIndex / _patternAlternatePerLayersNumber % 2 == 0;
+ patternMat?.Dispose();
+ patternAlternateMat?.Dispose();
- public bool IsAlternatePattern(uint layerIndex) => !IsNormalPattern(layerIndex);
+ return !progress.Token.IsCancellationRequested;
+ }
- public void PresetElephantFootCompensation()
+ public bool IsNormalPattern(uint layerIndex) => layerIndex / _patternAlternatePerLayersNumber % 2 == 0;
+
+ public bool IsAlternatePattern(uint layerIndex) => !IsNormalPattern(layerIndex);
+
+ 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;
+ WallChamfer = false;
+ UsePattern = true;
+ }
+
+ public void PresetPixelLightening()
+ {
+ PresetPixelDimming();
+ Operator = PixelArithmeticOperators.Add;
+ }
+
+ public void PresetFuzzySkin()
+ {
+ Operator = PixelArithmeticOperators.Corrode;
+ ApplyMethod = PixelArithmeticApplyMethod.ModelSurfaceAndInset;
+ NoiseMinOffset = -200;
+ NoiseMaxOffset = 64;
+ WallThickness = 4;
+ IgnoreAreaOperator = PixelArithmeticIgnoreAreaOperator.SmallerThan;
+ IgnoreAreaThreshold = 5000;
+ }
+
+ public void PresetStripAntiAliasing()
+ {
+ Operator = PixelArithmeticOperators.Threshold;
+ ApplyMethod = PixelArithmeticApplyMethod.All;
+ UsePattern = false;
+ Value = 127;
+ ThresholdMaxValue = 255;
+ ThresholdType = ThresholdType.Binary;
+ }
+
+ public void PresetHealAntiAliasing()
+ {
+ Operator = PixelArithmeticOperators.Threshold;
+ ApplyMethod = PixelArithmeticApplyMethod.All;
+ UsePattern = false;
+ Value = 119;
+ //ThresholdMaxValue = 255;
+ ThresholdType = ThresholdType.ToZero;
+ }
+
+ public void PresetHalfBrightness()
+ {
+ Value = 128;
+ }
+
+ public unsafe void LoadPatternFromImage(Mat mat, bool isAlternatePattern = false)
+ {
+ var result = new string[mat.Height];
+ var span = mat.GetBytePointer();
+ Parallel.For(0, mat.Height, CoreSettings.ParallelOptions, y =>
{
- SelectBottomLayers();
- Operator = PixelArithmeticOperators.Set;
- ApplyMethod = PixelArithmeticApplyMethod.ModelWalls;
- //Value = 190;
- //WallThickness = 20;
- WallChamfer = false;
- UsePattern = false;
+ result[y] = string.Empty;
+ var pixelPos = mat.GetPixelPos(0, y);
+ for (int x = 0; x < mat.Width; x++)
+ {
+ result[y] += $"{span[pixelPos++]} ";
+ }
+
+ result[y] = result[y].Trim();
+ });
+
+ StringBuilder sb = new();
+ foreach (var s in result)
+ {
+ sb.AppendLine(s);
}
- public void PresetPixelDimming()
+ if (isAlternatePattern)
+ {
+ PatternTextAlternate = sb.ToString();
+ }
+ else
{
- Operator = PixelArithmeticOperators.Subtract;
- ApplyMethod = PixelArithmeticApplyMethod.ModelInner;
- //WallThickness = 20;
- WallChamfer = false;
- UsePattern = true;
+ PatternText = sb.ToString();
}
+ }
- public void PresetPixelLightening()
+ public void LoadPatternFromImage(string filepath, bool isAlternatePattern = false)
+ {
+ try
+ {
+ using var mat = CvInvoke.Imread(filepath, ImreadModes.Grayscale);
+ LoadPatternFromImage(mat, isAlternatePattern);
+ }
+ catch (Exception e)
{
- PresetPixelDimming();
- Operator = PixelArithmeticOperators.Add;
+ Debug.WriteLine(e);
}
- public void PresetFuzzySkin()
+ }
+
+
+ public void GeneratePattern(string pattern)
+ {
+ if (pattern == "Chessboard")
{
- Operator = PixelArithmeticOperators.Corrode;
- ApplyMethod = PixelArithmeticApplyMethod.ModelSurfaceAndInset;
- NoiseMinOffset = -200;
- NoiseMaxOffset = 64;
- WallThickness = 4;
- IgnoreAreaOperator = PixelArithmeticIgnoreAreaOperator.SmallerThan;
- IgnoreAreaThreshold = 5000;
+ PatternText = string.Format(
+ "{0} {1}{2}" +
+ "{1} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{1} {0}{2}" +
+ "{0} {1}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ return;
}
- public void PresetStripAntiAliasing()
+ if (pattern == "Sparse")
{
- Operator = PixelArithmeticOperators.Threshold;
- ApplyMethod = PixelArithmeticApplyMethod.All;
- UsePattern = false;
- Value = 127;
- ThresholdMaxValue = 255;
- ThresholdType = ThresholdType.Binary;
+ PatternText = string.Format(
+ "{1} {0} {0} {0}{2}" +
+ "{0} {0} {1} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0} {0} {1} {0}{2}" +
+ "{1} {0} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
}
- public void PresetHealAntiAliasing()
+ if (pattern == "Crosses")
{
- Operator = PixelArithmeticOperators.Threshold;
- ApplyMethod = PixelArithmeticApplyMethod.All;
- UsePattern = false;
- Value = 119;
- //ThresholdMaxValue = 255;
- ThresholdType = ThresholdType.ToZero;
+ PatternText = string.Format(
+ "{1} {0} {1} {0}{2}" +
+ "{0} {1} {0} {0}{2}" +
+ "{1} {0} {1} {0}{2}" +
+ "{0} {0} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0} {0} {0} {0}{2}" +
+ "{1} {0} {1} {0}{2}" +
+ "{0} {1} {0} {0}{2}" +
+ "{1} {0} {1} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
}
- public void PresetHalfBrightness()
+ if (pattern == "Strips")
{
- Value = 128;
+ PatternText = string.Format(
+ "{1}{2}" +
+ "{0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0}{2}" +
+ "{1}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
}
- public unsafe void LoadPatternFromImage(Mat mat, bool isAlternatePattern = false)
+ if (pattern == "Pyramid")
{
- var result = new string[mat.Height];
- var span = mat.GetBytePointer();
- Parallel.For(0, mat.Height, CoreSettings.ParallelOptions, y =>
- {
- result[y] = string.Empty;
- var pixelPos = mat.GetPixelPos(0, y);
- for (int x = 0; x < mat.Width; x++)
- {
- result[y] += $"{span[pixelPos++]} ";
- }
+ PatternText = string.Format(
+ "{0} {0} {1} {0} {0} {0}{2}" +
+ "{0} {1} {0} {1} {0} {0}{2}" +
+ "{1} {0} {1} {0} {1} {0}{2}" +
+ "{0} {0} {0} {0} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0} {1} {0} {1} {0} {1}{2}" +
+ "{0} {0} {1} {0} {1} {0}{2}" +
+ "{0} {0} {0} {1} {0} {0}{2}" +
+ "{0} {0} {0} {0} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
+ }
- result[y] = result[y].Trim();
- });
+ if (pattern == "Rhombus")
+ {
+ PatternText = string.Format(
+ "{0} {1} {0} {0}{2}" +
+ "{1} {0} {1} {0}{2}" +
+ "{0} {1} {0} {0}{2}" +
+ "{0} {0} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0} {0} {0} {0}{2}" +
+ "{0} {1} {0} {0}{2}" +
+ "{1} {0} {1} {0}{2}" +
+ "{0} {1} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
+ }
- StringBuilder sb = new();
- foreach (var s in result)
- {
- sb.AppendLine(s);
- }
+ if (pattern == "Hearts")
+ {
+ PatternText = string.Format(
+ "{0} {1} {0} {1} {0} {0}{2}" +
+ "{1} {0} {1} {0} {1} {0}{2}" +
+ "{1} {0} {0} {0} {1} {0}{2}" +
+ "{0} {1} {0} {1} {0} {0}{2}" +
+ "{0} {0} {1} {0} {0} {0}{2}" +
+ "{0} {0} {0} {0} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0} {0} {0} {0} {0} {0}{2}" +
+ "{0} {0} {1} {0} {1} {0}{2}" +
+ "{0} {1} {0} {1} {0} {1}{2}" +
+ "{0} {1} {0} {0} {0} {1}{2}" +
+ "{0} {0} {1} {0} {1} {0}{2}" +
+ "{0} {0} {0} {1} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
+ }
- if (isAlternatePattern)
- {
- PatternTextAlternate = sb.ToString();
- }
- else
- {
- PatternText = sb.ToString();
- }
+ if (pattern == "Slashes")
+ {
+ PatternText = string.Format(
+ "{1} {0} {0}{2}" +
+ "{0} {1} {0}{2}" +
+ "{0} {0} {1}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0} {0} {1}{2}" +
+ "{0} {1} {0}{2}" +
+ "{1} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
}
- public void LoadPatternFromImage(string filepath, bool isAlternatePattern = false)
+ if (pattern == "Waves")
{
- try
- {
- using var mat = CvInvoke.Imread(filepath, ImreadModes.Grayscale);
- LoadPatternFromImage(mat, isAlternatePattern);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
+ PatternText = string.Format(
+ "{1} {0} {0}{2}" +
+ "{0} {0} {1}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+
+ PatternTextAlternate = string.Format(
+ "{0} {0} {1}{2}" +
+ "{1} {0} {0}"
+ , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ return;
+ }
+ if (pattern == "Solid")
+ {
+ PatternText = _patternGenBrightness.ToString();
+ PatternTextAlternate = null!;
+ return;
}
+ }
+ public void GenerateInfill(string pattern)
+ {
+ if (pattern == "Rectilinear")
+ {
+ PatternText = ($"255\n".Repeat(_patternGenInfillSpacing) + $"0\n".Repeat(_patternGenInfillThickness)).Trim('\n', '\r');
+ PatternTextAlternate = null!;
+ return;
+ }
- public void GeneratePattern(string pattern)
+ if (pattern == "Square grid")
{
- if (pattern == "Chessboard")
- {
- PatternText = string.Format(
- "{0} {1}{2}" +
- "{1} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
+ var p1 = "255 ".Repeat(_patternGenInfillSpacing) + "0 ".Repeat(_patternGenInfillThickness);
+ p1 = p1.Trim() + "\n";
+ p1 += p1.Repeat(_patternGenInfillThickness);
- PatternTextAlternate = string.Format(
- "{1} {0}{2}" +
- "{0} {1}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
- }
+ var p2 = "0 ".Repeat(_patternGenInfillSpacing) + "0 ".Repeat(_patternGenInfillThickness);
+ p2 = p2.Trim() + '\n';
+ p2 += p2.Repeat(_patternGenInfillThickness);
- if (pattern == "Sparse")
- {
- PatternText = string.Format(
- "{1} {0} {0} {0}{2}" +
- "{0} {0} {1} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0} {0} {1} {0}{2}" +
- "{1} {0} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
- }
+ p2 = p2.Trim('\n', '\r');
- if (pattern == "Crosses")
- {
- PatternText = string.Format(
- "{1} {0} {1} {0}{2}" +
- "{0} {1} {0} {0}{2}" +
- "{1} {0} {1} {0}{2}" +
- "{0} {0} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0} {0} {0} {0}{2}" +
- "{1} {0} {1} {0}{2}" +
- "{0} {1} {0} {0}{2}" +
- "{1} {0} {1} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
- }
+ PatternText = p1 + p2;
+ PatternTextAlternate = null!;
+ return;
+ }
- if (pattern == "Strips")
+ if (pattern == "Waves")
+ {
+ var p1 = string.Empty;
+ var pos = 0;
+ for (sbyte dir = 1; dir >= -1; dir -= 2)
{
- PatternText = string.Format(
- "{1}{2}" +
- "{0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0}{2}" +
- "{1}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
- }
+ while (pos >= 0 && pos <= _patternGenInfillSpacing)
+ {
+ p1 += "255 ".Repeat(pos);
+ p1 += "0 ".Repeat(_patternGenInfillThickness);
+ p1 += "255 ".Repeat(_patternGenInfillSpacing - pos);
+ p1 = p1.Trim() + '\n';
- if (pattern == "Pyramid")
- {
- PatternText = string.Format(
- "{0} {0} {1} {0} {0} {0}{2}" +
- "{0} {1} {0} {1} {0} {0}{2}" +
- "{1} {0} {1} {0} {1} {0}{2}" +
- "{0} {0} {0} {0} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0} {1} {0} {1} {0} {1}{2}" +
- "{0} {0} {1} {0} {1} {0}{2}" +
- "{0} {0} {0} {1} {0} {0}{2}" +
- "{0} {0} {0} {0} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
- }
+ pos += dir;
+ }
- if (pattern == "Rhombus")
- {
- PatternText = string.Format(
- "{0} {1} {0} {0}{2}" +
- "{1} {0} {1} {0}{2}" +
- "{0} {1} {0} {0}{2}" +
- "{0} {0} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0} {0} {0} {0}{2}" +
- "{0} {1} {0} {0}{2}" +
- "{1} {0} {1} {0}{2}" +
- "{0} {1} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
+ pos--;
}
- if (pattern == "Hearts")
- {
- PatternText = string.Format(
- "{0} {1} {0} {1} {0} {0}{2}" +
- "{1} {0} {1} {0} {1} {0}{2}" +
- "{1} {0} {0} {0} {1} {0}{2}" +
- "{0} {1} {0} {1} {0} {0}{2}" +
- "{0} {0} {1} {0} {0} {0}{2}" +
- "{0} {0} {0} {0} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0} {0} {0} {0} {0} {0}{2}" +
- "{0} {0} {1} {0} {1} {0}{2}" +
- "{0} {1} {0} {1} {0} {1}{2}" +
- "{0} {1} {0} {0} {0} {1}{2}" +
- "{0} {0} {1} {0} {1} {0}{2}" +
- "{0} {0} {0} {1} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
- }
+ PatternText = p1.Trim('\n', '\r');
+ PatternTextAlternate = null!;
+ return;
+ }
- if (pattern == "Slashes")
- {
- PatternText = string.Format(
- "{1} {0} {0}{2}" +
- "{0} {1} {0}{2}" +
- "{0} {0} {1}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0} {0} {1}{2}" +
- "{0} {1} {0}{2}" +
- "{1} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
- }
+ if (pattern == "Lattice")
+ {
+ var p1 = string.Empty;
+ var p2 = string.Empty;
+
+ var zeros = Math.Max(0, _patternGenInfillSpacing - _patternGenInfillThickness * 2);
- if (pattern == "Waves")
+ // Pillar
+ for (int i = 0; i < _patternGenInfillThickness; i++)
{
- PatternText = string.Format(
- "{1} {0} {0}{2}" +
- "{0} {0} {1}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
-
- PatternTextAlternate = string.Format(
- "{0} {0} {1}{2}" +
- "{1} {0} {0}"
- , _patternGenMinBrightness, _patternGenBrightness, "\n");
- return;
+ p1 += "0 ".Repeat(_patternGenInfillThickness);
+ p1 += "255 ".Repeat(zeros);
+ p1 += "0 ".Repeat(_patternGenInfillThickness);
+ p1 = p1.Trim() + '\n';
}
- if (pattern == "Solid")
+ for (int i = 0; i < zeros; i++)
{
- PatternText = _patternGenBrightness.ToString();
- PatternTextAlternate = null;
- return;
+ p1 += "255 ".Repeat(_patternGenInfillSpacing);
+ p1 = p1.Trim() + '\n';
}
- }
- public void GenerateInfill(string pattern)
- {
- if (pattern == "Rectilinear")
+ for (int i = 0; i < _patternGenInfillThickness; i++)
{
- PatternText = ($"255\n".Repeat(_patternGenInfillSpacing) + $"0\n".Repeat(_patternGenInfillThickness)).Trim('\n', '\r');
- PatternTextAlternate = null;
- return;
+ p1 += "0 ".Repeat(_patternGenInfillThickness);
+ p1 += "255 ".Repeat(zeros);
+ p1 += "0 ".Repeat(_patternGenInfillThickness);
+ p1 = p1.Trim() + '\n';
}
- if (pattern == "Square grid")
+ // Square
+ for (int i = 0; i < _patternGenInfillThickness; i++)
{
- var p1 = "255 ".Repeat(_patternGenInfillSpacing) + "0 ".Repeat(_patternGenInfillThickness);
- p1 = p1.Trim() + "\n";
- p1 += p1.Repeat(_patternGenInfillThickness);
-
-
- var p2 = "0 ".Repeat(_patternGenInfillSpacing) + "0 ".Repeat(_patternGenInfillThickness);
+ p2 += "0 ".Repeat(_patternGenInfillSpacing);
p2 = p2.Trim() + '\n';
- p2 += p2.Repeat(_patternGenInfillThickness);
-
- p2 = p2.Trim('\n', '\r');
-
- PatternText = p1 + p2;
- PatternTextAlternate = null;
- return;
}
- if (pattern == "Waves")
+ for (int i = 0; i < zeros; i++)
{
- var p1 = string.Empty;
- var pos = 0;
- for (sbyte dir = 1; dir >= -1; dir -= 2)
- {
- while (pos >= 0 && pos <= _patternGenInfillSpacing)
- {
- p1 += "255 ".Repeat(pos);
- p1 += "0 ".Repeat(_patternGenInfillThickness);
- p1 += "255 ".Repeat(_patternGenInfillSpacing - pos);
- p1 = p1.Trim() + '\n';
-
- pos += dir;
- }
-
- pos--;
- }
-
- PatternText = p1.Trim('\n', '\r');
- PatternTextAlternate = null;
- return;
+ p2 += "0 ".Repeat(_patternGenInfillThickness);
+ p2 += "255 ".Repeat(zeros);
+ p2 += "0 ".Repeat(_patternGenInfillThickness);
+ p2 = p2.Trim() + '\n';
}
- if (pattern == "Lattice")
+ for (int i = 0; i < _patternGenInfillThickness; i++)
{
- var p1 = string.Empty;
- var p2 = string.Empty;
-
- var zeros = Math.Max(0, _patternGenInfillSpacing - _patternGenInfillThickness * 2);
-
- // Pillar
- for (int i = 0; i < _patternGenInfillThickness; i++)
- {
- p1 += "0 ".Repeat(_patternGenInfillThickness);
- p1 += "255 ".Repeat(zeros);
- p1 += "0 ".Repeat(_patternGenInfillThickness);
- p1 = p1.Trim() + '\n';
- }
-
- for (int i = 0; i < zeros; i++)
- {
- p1 += "255 ".Repeat(_patternGenInfillSpacing);
- p1 = p1.Trim() + '\n';
- }
-
- for (int i = 0; i < _patternGenInfillThickness; i++)
- {
- p1 += "0 ".Repeat(_patternGenInfillThickness);
- p1 += "255 ".Repeat(zeros);
- p1 += "0 ".Repeat(_patternGenInfillThickness);
- p1 = p1.Trim() + '\n';
- }
-
- // Square
- for (int i = 0; i < _patternGenInfillThickness; i++)
- {
- p2 += "0 ".Repeat(_patternGenInfillSpacing);
- p2 = p2.Trim() + '\n';
- }
-
- for (int i = 0; i < zeros; i++)
- {
- p2 += "0 ".Repeat(_patternGenInfillThickness);
- p2 += "255 ".Repeat(zeros);
- p2 += "0 ".Repeat(_patternGenInfillThickness);
- p2 = p2.Trim() + '\n';
- }
-
- for (int i = 0; i < _patternGenInfillThickness; i++)
- {
- p2 += "0 ".Repeat(_patternGenInfillSpacing);
- p2 = p2.Trim() + '\n';
- }
+ p2 += "0 ".Repeat(_patternGenInfillSpacing);
+ p2 = p2.Trim() + '\n';
+ }
- PatternText = p1.Trim('\n', '\r');
- PatternTextAlternate = p2.Trim('\n', '\r'); ;
- return;
- }
+ PatternText = p1.Trim('\n', '\r');
+ PatternTextAlternate = p2.Trim('\n', '\r'); ;
+ return;
}
+ }
- #endregion
-
- #region Equality
+ #endregion
- protected bool Equals(OperationPixelArithmetic other)
- {
- return _operator == other._operator && _applyMethod == other._applyMethod && _wallThicknessStart == other._wallThicknessStart && _wallThicknessEnd == other._wallThicknessEnd && _wallChamfer == other._wallChamfer && _ignoreAreaOperator == other._ignoreAreaOperator && _ignoreAreaThreshold == other._ignoreAreaThreshold && _value == other._value && _usePattern == other._usePattern && _thresholdType == other._thresholdType && _thresholdMaxValue == other._thresholdMaxValue && _patternAlternatePerLayersNumber == other._patternAlternatePerLayersNumber && _patternInvert == other._patternInvert && _patternText == other._patternText && _patternTextAlternate == other._patternTextAlternate && _patternGenMinBrightness == other._patternGenMinBrightness && _patternGenBrightness == other._patternGenBrightness && _patternGenInfillThickness == other._patternGenInfillThickness && _patternGenInfillSpacing == other._patternGenInfillSpacing && _noiseMinOffset == other._noiseMinOffset && _noiseMaxOffset == other._noiseMaxOffset && _noiseThreshold == other._noiseThreshold && _noisePixelArea == other._noisePixelArea && _noisePasses == other._noisePasses;
- }
+ #region Equality
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationPixelArithmetic) obj);
- }
+ protected bool Equals(OperationPixelArithmetic other)
+ {
+ return _operator == other._operator && _applyMethod == other._applyMethod && _wallThicknessStart == other._wallThicknessStart && _wallThicknessEnd == other._wallThicknessEnd && _wallChamfer == other._wallChamfer && _ignoreAreaOperator == other._ignoreAreaOperator && _ignoreAreaThreshold == other._ignoreAreaThreshold && _value == other._value && _usePattern == other._usePattern && _thresholdType == other._thresholdType && _thresholdMaxValue == other._thresholdMaxValue && _patternAlternatePerLayersNumber == other._patternAlternatePerLayersNumber && _patternInvert == other._patternInvert && _patternText == other._patternText && _patternTextAlternate == other._patternTextAlternate && _patternGenMinBrightness == other._patternGenMinBrightness && _patternGenBrightness == other._patternGenBrightness && _patternGenInfillThickness == other._patternGenInfillThickness && _patternGenInfillSpacing == other._patternGenInfillSpacing && _noiseMinOffset == other._noiseMinOffset && _noiseMaxOffset == other._noiseMaxOffset && _noiseThreshold == other._noiseThreshold && _noisePixelArea == other._noisePixelArea && _noisePasses == other._noisePasses;
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add((int) _operator);
- hashCode.Add((int) _applyMethod);
- hashCode.Add(_wallThicknessStart);
- hashCode.Add(_wallThicknessEnd);
- hashCode.Add(_wallChamfer);
- hashCode.Add((int) _ignoreAreaOperator);
- hashCode.Add(_ignoreAreaThreshold);
- hashCode.Add(_value);
- hashCode.Add(_usePattern);
- hashCode.Add((int) _thresholdType);
- hashCode.Add(_thresholdMaxValue);
- hashCode.Add(_patternAlternatePerLayersNumber);
- hashCode.Add(_patternInvert);
- hashCode.Add(_patternText);
- hashCode.Add(_patternTextAlternate);
- hashCode.Add(_patternGenMinBrightness);
- hashCode.Add(_patternGenBrightness);
- hashCode.Add(_patternGenInfillThickness);
- hashCode.Add(_patternGenInfillSpacing);
- hashCode.Add(_noiseMinOffset);
- hashCode.Add(_noiseMaxOffset);
- hashCode.Add(_noiseThreshold);
- hashCode.Add(_noisePixelArea);
- hashCode.Add(_noisePasses);
- return hashCode.ToHashCode();
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationPixelArithmetic) obj);
+ }
- #endregion
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add((int) _operator);
+ hashCode.Add((int) _applyMethod);
+ hashCode.Add(_wallThicknessStart);
+ hashCode.Add(_wallThicknessEnd);
+ hashCode.Add(_wallChamfer);
+ hashCode.Add((int) _ignoreAreaOperator);
+ hashCode.Add(_ignoreAreaThreshold);
+ hashCode.Add(_value);
+ hashCode.Add(_usePattern);
+ hashCode.Add((int) _thresholdType);
+ hashCode.Add(_thresholdMaxValue);
+ hashCode.Add(_patternAlternatePerLayersNumber);
+ hashCode.Add(_patternInvert);
+ hashCode.Add(_patternText);
+ hashCode.Add(_patternTextAlternate);
+ hashCode.Add(_patternGenMinBrightness);
+ hashCode.Add(_patternGenBrightness);
+ hashCode.Add(_patternGenInfillThickness);
+ hashCode.Add(_patternGenInfillSpacing);
+ hashCode.Add(_noiseMinOffset);
+ hashCode.Add(_noiseMaxOffset);
+ hashCode.Add(_noiseThreshold);
+ hashCode.Add(_noisePixelArea);
+ hashCode.Add(_noisePasses);
+ return hashCode.ToHashCode();
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs
index 827219d..26435ef 100644
--- a/UVtools.Core/Operations/OperationPixelDimming.cs
+++ b/UVtools.Core/Operations/OperationPixelDimming.cs
@@ -6,711 +6,711 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationPixelDimming : Operation
{
- [Serializable]
- public class OperationPixelDimming : Operation
+ #region Subclasses
+ class StringMatrix
{
- #region Subclasses
- class StringMatrix
- {
- public string Text { get; }
- public Matrix<byte> Pattern { get; set; }
+ public string? Text { get; }
+ public Matrix<byte> Pattern { get; set; } = null!;
- public StringMatrix(string text)
- {
- Text = text;
- }
+ public StringMatrix(string? text)
+ {
+ Text = text;
}
- #endregion
-
- #region Members
- private bool _lighteningPixels;
- private uint _wallThicknessStart = 10;
- private uint _wallThicknessEnd = 10;
- private bool _wallsOnly;
- private bool _chamfer;
- private Matrix<byte> _pattern;
- private Matrix<byte> _alternatePattern;
- private ushort _alternatePatternPerLayers = 1;
- private string _patternText;
- private string _alternatePatternText;
- private byte _brightness = 127;
- private ushort _infillGenThickness = 10;
- private ushort _infillGenSpacing = 20;
-
- #endregion
-
- #region Overrides
- public override string Title => "Pixel dimming";
- public override string Description =>
- "Dim white pixels in a chosen pattern applied over the print area.\n\n" +
- "The selected pattern will tiled over the image. Benefits are:\n" +
- "1) Reduced layer expansion for large layer objects\n" +
- "2) Reduced cross layer exposure\n" +
- "3) Extended pixel life of the LCD\n\n" +
- "NOTE: Run this tool only after repairs and all other transformations.\n" +
- "To create your own patterns: www.piskelapp.com";
-
- public override string ConfirmationText =>
- $"dim pixels from layers {LayerIndexStart} through {LayerIndexEnd}?";
-
- public override string ProgressTitle =>
- $"Dimming from layers {LayerIndexStart} through {LayerIndexEnd}";
-
- public override string ProgressAction => "Dimmed layers";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
- /*if (WallThicknessStart == 0 && WallsOnly)
- {
- sb.AppendLine("Border size must be positive in order to use \"Dim only borders\" function.");
- }*/
+ }
+ #endregion
+
+ #region Members
+ private bool _lighteningPixels;
+ private uint _wallThicknessStart = 10;
+ private uint _wallThicknessEnd = 10;
+ private bool _wallsOnly;
+ private bool _chamfer;
+ private Matrix<byte> _pattern = null!;
+ private Matrix<byte> _alternatePattern = null!;
+ private ushort _alternatePatternPerLayers = 1;
+ private string _patternText = null!;
+ private string? _alternatePatternText;
+ private byte _brightness = 127;
+ private ushort _infillGenThickness = 10;
+ private ushort _infillGenSpacing = 20;
+
+ #endregion
+
+ #region Overrides
+ public override string IconClass => "mdi-circle-opacity";
+ public override string Title => "Pixel dimming";
+ public override string Description =>
+ "Dim white pixels in a chosen pattern applied over the print area.\n\n" +
+ "The selected pattern will tiled over the image. Benefits are:\n" +
+ "1) Reduced layer expansion for large layer objects\n" +
+ "2) Reduced cross layer exposure\n" +
+ "3) Extended pixel life of the LCD\n\n" +
+ "NOTE: Run this tool only after repairs and all other transformations.\n" +
+ "To create your own patterns: www.piskelapp.com";
+
+ public override string ConfirmationText =>
+ $"dim pixels from layers {LayerIndexStart} through {LayerIndexEnd}?";
+
+ public override string ProgressTitle =>
+ $"Dimming from layers {LayerIndexStart} through {LayerIndexEnd}";
+
+ public override string ProgressAction => "Dimmed layers";
+
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ /*if (WallThicknessStart == 0 && WallsOnly)
+ {
+ sb.AppendLine("Border size must be positive in order to use \"Dim only borders\" function.");
+ }*/
- var stringMatrix = new[]
- {
- new StringMatrix(PatternText),
- new StringMatrix(AlternatePatternText),
- };
+ var stringMatrix = new[]
+ {
+ new StringMatrix(PatternText),
+ new StringMatrix(AlternatePatternText),
+ };
- foreach (var item in stringMatrix)
+ foreach (var item in stringMatrix)
+ {
+ if (string.IsNullOrWhiteSpace(item.Text)) continue;
+ var lines = item.Text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+ for (var row = 0; row < lines.Length; row++)
{
- if (string.IsNullOrWhiteSpace(item.Text)) continue;
- var lines = item.Text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
- for (var row = 0; row < lines.Length; row++)
+ var bytes = lines[row].Split(' ');
+ if (row == 0)
+ {
+ item.Pattern = new Matrix<byte>(lines.Length, bytes.Length);
+ }
+ else
{
- var bytes = lines[row].Split(' ');
- if (row == 0)
+ if (item.Pattern.Cols != bytes.Length)
{
- item.Pattern = new Matrix<byte>(lines.Length, bytes.Length);
+ sb.AppendLine($"Row {row + 1} have invalid number of pixels, the pattern must have equal pixel count per line, per defined on line 1");
+ return sb.ToString();
}
- else
+ }
+
+ for (int col = 0; col < bytes.Length; col++)
+ {
+ if (byte.TryParse(bytes[col], out var value))
{
- if (item.Pattern.Cols != bytes.Length)
- {
- sb.AppendLine($"Row {row + 1} have invalid number of pixels, the pattern must have equal pixel count per line, per defined on line 1");
- return sb.ToString();
- }
+ item.Pattern[row, col] = value;
}
-
- for (int col = 0; col < bytes.Length; col++)
+ else
{
- if (byte.TryParse(bytes[col], out var value))
- {
- item.Pattern[row, col] = value;
- }
- else
- {
- sb.AppendLine($"{bytes[col]} is a invalid number, use values from 0 to 255");
- return sb.ToString();
- }
+ sb.AppendLine($"{bytes[col]} is a invalid number, use values from 0 to 255");
+ return sb.ToString();
}
}
}
+ }
- Pattern = stringMatrix[0].Pattern;
- AlternatePattern = stringMatrix[1].Pattern;
-
- if (Pattern is null && AlternatePattern is null)
- {
- sb.AppendLine("Either even or odd pattern must contain a valid matrix.");
- return sb.ToString();
- }
+ Pattern = stringMatrix[0].Pattern;
+ AlternatePattern = stringMatrix[1].Pattern;
+ if (Pattern is null && AlternatePattern is null)
+ {
+ sb.AppendLine("Either even or odd pattern must contain a valid matrix.");
return sb.ToString();
}
+
+ return sb.ToString();
+ }
- public override string ToString()
+ public override string ToString()
+ {
+ var result = $"[Border: {_wallThicknessStart}px to {_wallThicknessEnd}px] [Chamfer: {_chamfer}] [Only borders: {_wallsOnly}] [Alternate every: {_alternatePatternPerLayers}] [B: {_brightness}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Constructor
+
+ public OperationPixelDimming()
+ { }
+
+ public OperationPixelDimming(FileFormat slicerFile) : base(slicerFile) { }
+
+ #endregion
+
+ #region Properties
+
+ public bool LighteningPixels
+ {
+ get => _lighteningPixels;
+ set => RaiseAndSetIfChanged(ref _lighteningPixels, value);
+ }
+
+ public uint WallThickness
+ {
+ get => _wallThicknessStart;
+ set
{
- var result = $"[Border: {_wallThicknessStart}px to {_wallThicknessEnd}px] [Chamfer: {_chamfer}] [Only borders: {_wallsOnly}] [Alternate every: {_alternatePatternPerLayers}] [B: {_brightness}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ WallThicknessStart = value;
+ WallThicknessEnd = value;
}
- #endregion
+ }
+
+ public uint WallThicknessStart
+ {
+ get => _wallThicknessStart;
+ set => RaiseAndSetIfChanged(ref _wallThicknessStart, value);
+ }
+
+ public uint WallThicknessEnd
+ {
+ get => _wallThicknessEnd;
+ set => RaiseAndSetIfChanged(ref _wallThicknessEnd, value);
+ }
- #region Constructor
+ public bool WallsOnly
+ {
+ get => _wallsOnly;
+ set => RaiseAndSetIfChanged(ref _wallsOnly, value);
+ }
+
+ public bool Chamfer
+ {
+ get => _chamfer;
+ set => RaiseAndSetIfChanged(ref _chamfer, value);
+ }
+
+ /// <summary>
+ /// Use the alternate pattern every <see cref="AlternatePatternPerLayers"/> layers
+ /// </summary>
+ public ushort AlternatePatternPerLayers
+ {
+ get => _alternatePatternPerLayers;
+ set => RaiseAndSetIfChanged(ref _alternatePatternPerLayers, Math.Max((ushort)1, value));
+ }
- public OperationPixelDimming()
- { }
+ public string PatternText
+ {
+ get => _patternText;
+ set => RaiseAndSetIfChanged(ref _patternText, value);
+ }
- public OperationPixelDimming(FileFormat slicerFile) : base(slicerFile) { }
+ public string? AlternatePatternText
+ {
+ get => _alternatePatternText;
+ set => RaiseAndSetIfChanged(ref _alternatePatternText, value);
+ }
- #endregion
+ [XmlIgnore]
+ public Matrix<byte> Pattern
+ {
+ get => _pattern;
+ set => RaiseAndSetIfChanged(ref _pattern, value);
+ }
- #region Properties
+ [XmlIgnore]
+ public Matrix<byte> AlternatePattern
+ {
+ get => _alternatePattern;
+ set => RaiseAndSetIfChanged(ref _alternatePattern, value);
+ }
- public bool LighteningPixels
+ public byte Brightness
+ {
+ get => _brightness;
+ set
{
- get => _lighteningPixels;
- set => RaiseAndSetIfChanged(ref _lighteningPixels, value);
+ if(!RaiseAndSetIfChanged(ref _brightness, value)) return;
+ RaisePropertyChanged(nameof(BrightnessPercent));
}
+ }
+
+ public float BrightnessPercent => (float)Math.Round(_brightness * 100 / 255f, 2);
+
+
+ public ushort InfillGenThickness
+ {
+ get => _infillGenThickness;
+ set => RaiseAndSetIfChanged(ref _infillGenThickness, value);
+ }
+
+ public ushort InfillGenSpacing
+ {
+ get => _infillGenSpacing;
+ set => RaiseAndSetIfChanged(ref _infillGenSpacing, value);
+ }
+
+ #endregion
+
+ #region Equality
+
+ protected bool Equals(OperationPixelDimming other)
+ {
+ return _lighteningPixels == other._lighteningPixels && _wallThicknessStart == other._wallThicknessStart && _wallThicknessEnd == other._wallThicknessEnd && _wallsOnly == other._wallsOnly && _chamfer == other._chamfer && _alternatePatternPerLayers == other._alternatePatternPerLayers && _patternText == other._patternText && _alternatePatternText == other._alternatePatternText && _brightness == other._brightness && _infillGenThickness == other._infillGenThickness && _infillGenSpacing == other._infillGenSpacing;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationPixelDimming)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_lighteningPixels);
+ hashCode.Add(_wallThicknessStart);
+ hashCode.Add(_wallThicknessEnd);
+ hashCode.Add(_wallsOnly);
+ hashCode.Add(_chamfer);
+ hashCode.Add(_alternatePatternPerLayers);
+ hashCode.Add(_patternText);
+ hashCode.Add(_alternatePatternText);
+ hashCode.Add(_brightness);
+ hashCode.Add(_infillGenThickness);
+ hashCode.Add(_infillGenSpacing);
+ return hashCode.ToHashCode();
+ }
+
+ #endregion
+
+ #region Methods
+ public bool IsNormalPattern(uint layerIndex) => layerIndex / AlternatePatternPerLayers % 2 == 0;
+
+ public bool IsAlternatePattern(uint layerIndex) => !IsNormalPattern(layerIndex);
- public uint WallThickness
+ public unsafe void LoadPatternFromImage(Mat mat, bool isAlternatePattern = false)
+ {
+ var result = new string[mat.Height];
+ var span = mat.GetBytePointer();
+ Parallel.For(0, mat.Height, CoreSettings.ParallelOptions, y =>
{
- get => _wallThicknessStart;
- set
+ result[y] = string.Empty;
+ for (int x = 0; x < mat.Width; x++)
{
- WallThicknessStart = value;
- WallThicknessEnd = value;
+ result[y] += $"{span[mat.GetPixelPos(x, y)]} ";
}
- }
- public uint WallThicknessStart
+ result[y] = result[y].Trim();
+ });
+
+ StringBuilder sb = new();
+ foreach (var s in result)
{
- get => _wallThicknessStart;
- set => RaiseAndSetIfChanged(ref _wallThicknessStart, value);
+ sb.AppendLine(s);
}
- public uint WallThicknessEnd
+ if (isAlternatePattern)
{
- get => _wallThicknessEnd;
- set => RaiseAndSetIfChanged(ref _wallThicknessEnd, value);
+ AlternatePatternText = sb.ToString();
}
-
- public bool WallsOnly
+ else
{
- get => _wallsOnly;
- set => RaiseAndSetIfChanged(ref _wallsOnly, value);
+ PatternText = sb.ToString();
}
+ }
- public bool Chamfer
+ public void LoadPatternFromImage(string filepath, bool isAlternatePattern = false)
+ {
+ try
{
- get => _chamfer;
- set => RaiseAndSetIfChanged(ref _chamfer, value);
+ using var mat = CvInvoke.Imread(filepath, ImreadModes.Grayscale);
+ LoadPatternFromImage(mat, isAlternatePattern);
}
-
- /// <summary>
- /// Use the alternate pattern every <see cref="AlternatePatternPerLayers"/> layers
- /// </summary>
- public ushort AlternatePatternPerLayers
+ catch (Exception e)
{
- get => _alternatePatternPerLayers;
- set => RaiseAndSetIfChanged(ref _alternatePatternPerLayers, Math.Max((ushort)1, value));
+ Debug.WriteLine(e);
}
+
+ }
+
- public string PatternText
+ public void GeneratePixelDimming(string pattern)
+ {
+ if (pattern == "Chessboard")
{
- get => _patternText;
- set => RaiseAndSetIfChanged(ref _patternText, value);
+ PatternText = string.Format(
+ "255 {0}{1}" +
+ "{0} 255"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "{0} 255{1}" +
+ "255 {0}"
+ , _brightness, "\n");
+
+ return;
}
- public string AlternatePatternText
+ if (pattern == "Sparse")
{
- get => _alternatePatternText;
- set => RaiseAndSetIfChanged(ref _alternatePatternText, value);
+ PatternText = string.Format(
+ "{0} 255 255 255{1}" +
+ "255 255 {0} 255"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255 255 {0} 255{1}" +
+ "{0} 255 255 255"
+ , _brightness, "\n");
+ return;
}
- [XmlIgnore]
- public Matrix<byte> Pattern
+ if (pattern == "Crosses")
{
- get => _pattern;
- set => RaiseAndSetIfChanged(ref _pattern, value);
+ PatternText = string.Format(
+ "{0} 255 {0} 255{1}" +
+ "255 {0} 255 255{1}" +
+ "{0} 255 {0} 255{1}" +
+ "255 255 255 255"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255 255 255 255{1}" +
+ "{0} 255 {0} 255{1}" +
+ "255 {0} 255 255{1}" +
+ "{0} 255 {0} 255"
+ , _brightness, "\n");
+ return;
}
- [XmlIgnore]
- public Matrix<byte> AlternatePattern
+ if (pattern == "Strips")
{
- get => _alternatePattern;
- set => RaiseAndSetIfChanged(ref _alternatePattern, value);
+ PatternText = string.Format(
+ "{0}{1}" +
+ "255"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255{1}" +
+ "{0}"
+ , _brightness, "\n");
+ return;
}
- public byte Brightness
+ if (pattern == "Pyramid")
{
- get => _brightness;
- set
- {
- if(!RaiseAndSetIfChanged(ref _brightness, value)) return;
- RaisePropertyChanged(nameof(BrightnessPercent));
- }
+ PatternText = string.Format(
+ "255 255 {0} 255 255 255{1}" +
+ "255 {0} 255 {0} 255 255{1}" +
+ "{0} 255 {0} 255 {0} 255{1}" +
+ "255 255 255 255 255 255"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255 {0} 255 {0} 255 {0}{1}" +
+ "255 255 {0} 255 {0} 255{1}" +
+ "255 255 255 {0} 255 255{1}" +
+ "255 255 255 255 255 255"
+ , _brightness, "\n");
+ return;
}
-
- public float BrightnessPercent => (float)Math.Round(_brightness * 100 / 255f, 2);
-
- public ushort InfillGenThickness
+ if (pattern == "Rhombus")
{
- get => _infillGenThickness;
- set => RaiseAndSetIfChanged(ref _infillGenThickness, value);
+ PatternText = string.Format(
+ "255 {0} 255 255{1}" +
+ "{0} 255 {0} 255{1}" +
+ "255 {0} 255 255{1}" +
+ "255 255 255 255"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255 255 255 255{1}" +
+ "255 {0} 255 255{1}" +
+ "{0} 255 {0} 255{1}" +
+ "255 {0} 255 255"
+ , _brightness, "\n");
+ return;
}
- public ushort InfillGenSpacing
+ if (pattern == "Hearts")
{
- get => _infillGenSpacing;
- set => RaiseAndSetIfChanged(ref _infillGenSpacing, value);
+ PatternText = string.Format(
+ "255 {0} 255 {0} 255 255{1}" +
+ "{0} 255 {0} 255 {0} 255{1}" +
+ "{0} 255 255 255 {0} 255{1}" +
+ "255 {0} 255 {0} 255 255{1}" +
+ "255 255 {0} 255 255 255{1}" +
+ "255 255 255 255 255 255"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255 255 255 255 255 255{1}" +
+ "255 255 {0} 255 {0} 255{1}" +
+ "255 {0} 255 {0} 255 {0}{1}" +
+ "255 {0} 255 255 255 {0}{1}" +
+ "255 255 {0} 255 {0} 255{1}" +
+ "255 255 255 {0} 255 255"
+ , _brightness, "\n");
+ return;
}
-
- #endregion
- #region Equality
+ if (pattern == "Slashes")
+ {
+ PatternText = string.Format(
+ "{0} 255 255{1}" +
+ "255 {0} 255{1}" +
+ "255 255 {0}"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255 255 {0}{1}" +
+ "255 {0} 255{1}" +
+ "{0} 255 255"
+ , _brightness, "\n");
+ return;
+ }
- protected bool Equals(OperationPixelDimming other)
+ if (pattern == "Waves")
{
- return _lighteningPixels == other._lighteningPixels && _wallThicknessStart == other._wallThicknessStart && _wallThicknessEnd == other._wallThicknessEnd && _wallsOnly == other._wallsOnly && _chamfer == other._chamfer && _alternatePatternPerLayers == other._alternatePatternPerLayers && _patternText == other._patternText && _alternatePatternText == other._alternatePatternText && _brightness == other._brightness && _infillGenThickness == other._infillGenThickness && _infillGenSpacing == other._infillGenSpacing;
+ PatternText = string.Format(
+ "{0} 255 255{1}" +
+ "255 255 {0}"
+ , _brightness, "\n");
+
+ AlternatePatternText = string.Format(
+ "255 255 {0}{1}" +
+ "{0} 255 255"
+ , _brightness, "\n");
+ return;
}
- public override bool Equals(object obj)
+ if (pattern == "Solid")
{
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationPixelDimming)obj);
+ PatternText = _brightness.ToString();
+ AlternatePatternText = null;
+ return;
}
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_lighteningPixels);
- hashCode.Add(_wallThicknessStart);
- hashCode.Add(_wallThicknessEnd);
- hashCode.Add(_wallsOnly);
- hashCode.Add(_chamfer);
- hashCode.Add(_alternatePatternPerLayers);
- hashCode.Add(_patternText);
- hashCode.Add(_alternatePatternText);
- hashCode.Add(_brightness);
- hashCode.Add(_infillGenThickness);
- hashCode.Add(_infillGenSpacing);
- return hashCode.ToHashCode();
+ public void GenerateInfill(string pattern)
+ {
+ if (pattern == "Rectilinear")
+ {
+ PatternText = ($"0\n".Repeat(_infillGenSpacing) + $"255\n".Repeat(_infillGenSpacing)).Trim('\n', '\r');
+ AlternatePatternText = null;
+ return;
}
- #endregion
+ if (pattern == "Square grid")
+ {
+ var p1 = "0 ".Repeat(_infillGenSpacing) + "255 ".Repeat(_infillGenThickness);
+ p1 = p1.Trim() + "\n";
+ p1 += p1.Repeat(_infillGenThickness);
+
- #region Methods
- public bool IsNormalPattern(uint layerIndex) => layerIndex / AlternatePatternPerLayers % 2 == 0;
+ var p2 = "255 ".Repeat(_infillGenSpacing) + "255 ".Repeat(_infillGenThickness);
+ p2 = p2.Trim() + '\n';
+ p2 += p2.Repeat(_infillGenThickness);
- public bool IsAlternatePattern(uint layerIndex) => !IsNormalPattern(layerIndex);
+ p2 = p2.Trim('\n', '\r');
- public unsafe void LoadPatternFromImage(Mat mat, bool isAlternatePattern = false)
+ PatternText = p1 + p2;
+ AlternatePatternText = null;
+ return;
+ }
+
+ if (pattern == "Waves")
{
- var result = new string[mat.Height];
- var span = mat.GetBytePointer();
- Parallel.For(0, mat.Height, CoreSettings.ParallelOptions, y =>
+ var p1 = string.Empty;
+ var pos = 0;
+ for (sbyte dir = 1; dir >= -1; dir -= 2)
{
- result[y] = string.Empty;
- for (int x = 0; x < mat.Width; x++)
+ while (pos >= 0 && pos <= _infillGenSpacing)
{
- result[y] += $"{span[mat.GetPixelPos(x, y)]} ";
- }
+ p1 += "0 ".Repeat(pos);
+ p1 += "255 ".Repeat(_infillGenThickness);
+ p1 += "0 ".Repeat(_infillGenSpacing - pos);
+ p1 = p1.Trim() + '\n';
- result[y] = result[y].Trim();
- });
+ pos += dir;
+ }
- StringBuilder sb = new();
- foreach (var s in result)
- {
- sb.AppendLine(s);
+ pos--;
}
- if (isAlternatePattern)
- {
- AlternatePatternText = sb.ToString();
- }
- else
- {
- PatternText = sb.ToString();
- }
+ PatternText = p1.Trim('\n', '\r');
+ AlternatePatternText = null;
+ return;
}
- public void LoadPatternFromImage(string filepath, bool isAlternatePattern = false)
+ if (pattern == "Lattice")
{
- try
- {
- using var mat = CvInvoke.Imread(filepath, ImreadModes.Grayscale);
- LoadPatternFromImage(mat, isAlternatePattern);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
-
- }
-
-
- public void GeneratePixelDimming(string pattern)
- {
- if (pattern == "Chessboard")
- {
- PatternText = string.Format(
- "255 {0}{1}" +
- "{0} 255"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "{0} 255{1}" +
- "255 {0}"
- , _brightness, "\n");
+ var p1 = string.Empty;
+ var p2 = string.Empty;
- return;
- }
+ var zeros = Math.Max(0, _infillGenSpacing - _infillGenThickness * 2);
- if (pattern == "Sparse")
+ // Pillar
+ for (int i = 0; i < _infillGenThickness; i++)
{
- PatternText = string.Format(
- "{0} 255 255 255{1}" +
- "255 255 {0} 255"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255 255 {0} 255{1}" +
- "{0} 255 255 255"
- , _brightness, "\n");
- return;
+ p1 += "255 ".Repeat(_infillGenThickness);
+ p1 += "0 ".Repeat(zeros);
+ p1 += "255 ".Repeat(_infillGenThickness);
+ p1 = p1.Trim() + '\n';
}
- if (pattern == "Crosses")
+ for (int i = 0; i < zeros; i++)
{
- PatternText = string.Format(
- "{0} 255 {0} 255{1}" +
- "255 {0} 255 255{1}" +
- "{0} 255 {0} 255{1}" +
- "255 255 255 255"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255 255 255 255{1}" +
- "{0} 255 {0} 255{1}" +
- "255 {0} 255 255{1}" +
- "{0} 255 {0} 255"
- , _brightness, "\n");
- return;
+ p1 += "0 ".Repeat(_infillGenSpacing);
+ p1 = p1.Trim() + '\n';
}
- if (pattern == "Strips")
+ for (int i = 0; i < _infillGenThickness; i++)
{
- PatternText = string.Format(
- "{0}{1}" +
- "255"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255{1}" +
- "{0}"
- , _brightness, "\n");
- return;
+ p1 += "255 ".Repeat(_infillGenThickness);
+ p1 += "0 ".Repeat(zeros);
+ p1 += "255 ".Repeat(_infillGenThickness);
+ p1 = p1.Trim() + '\n';
}
- if (pattern == "Pyramid")
+ // Square
+ for (int i = 0; i < _infillGenThickness; i++)
{
- PatternText = string.Format(
- "255 255 {0} 255 255 255{1}" +
- "255 {0} 255 {0} 255 255{1}" +
- "{0} 255 {0} 255 {0} 255{1}" +
- "255 255 255 255 255 255"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255 {0} 255 {0} 255 {0}{1}" +
- "255 255 {0} 255 {0} 255{1}" +
- "255 255 255 {0} 255 255{1}" +
- "255 255 255 255 255 255"
- , _brightness, "\n");
- return;
+ p2 += "255 ".Repeat(_infillGenSpacing);
+ p2 = p2.Trim() + '\n';
}
- if (pattern == "Rhombus")
+ for (int i = 0; i < zeros; i++)
{
- PatternText = string.Format(
- "255 {0} 255 255{1}" +
- "{0} 255 {0} 255{1}" +
- "255 {0} 255 255{1}" +
- "255 255 255 255"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255 255 255 255{1}" +
- "255 {0} 255 255{1}" +
- "{0} 255 {0} 255{1}" +
- "255 {0} 255 255"
- , _brightness, "\n");
- return;
+ p2 += "255 ".Repeat(_infillGenThickness);
+ p2 += "0 ".Repeat(zeros);
+ p2 += "255 ".Repeat(_infillGenThickness);
+ p2 = p2.Trim() + '\n';
}
- if (pattern == "Hearts")
+ for (int i = 0; i < _infillGenThickness; i++)
{
- PatternText = string.Format(
- "255 {0} 255 {0} 255 255{1}" +
- "{0} 255 {0} 255 {0} 255{1}" +
- "{0} 255 255 255 {0} 255{1}" +
- "255 {0} 255 {0} 255 255{1}" +
- "255 255 {0} 255 255 255{1}" +
- "255 255 255 255 255 255"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255 255 255 255 255 255{1}" +
- "255 255 {0} 255 {0} 255{1}" +
- "255 {0} 255 {0} 255 {0}{1}" +
- "255 {0} 255 255 255 {0}{1}" +
- "255 255 {0} 255 {0} 255{1}" +
- "255 255 255 {0} 255 255"
- , _brightness, "\n");
- return;
+ p2 += "255 ".Repeat(_infillGenSpacing);
+ p2 = p2.Trim() + '\n';
}
- if (pattern == "Slashes")
- {
- PatternText = string.Format(
- "{0} 255 255{1}" +
- "255 {0} 255{1}" +
- "255 255 {0}"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255 255 {0}{1}" +
- "255 {0} 255{1}" +
- "{0} 255 255"
- , _brightness, "\n");
- return;
- }
- if (pattern == "Waves")
- {
- PatternText = string.Format(
- "{0} 255 255{1}" +
- "255 255 {0}"
- , _brightness, "\n");
-
- AlternatePatternText = string.Format(
- "255 255 {0}{1}" +
- "{0} 255 255"
- , _brightness, "\n");
- return;
- }
- if (pattern == "Solid")
- {
- PatternText = _brightness.ToString();
- AlternatePatternText = null;
- return;
- }
+ PatternText = p1.Trim('\n', '\r');
+ AlternatePatternText = p2.Trim('\n', '\r'); ;
+ return;
}
+ }
- public void GenerateInfill(string pattern)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (Pattern is null)
{
- if (pattern == "Rectilinear")
+ Pattern = new Matrix<byte>(2, 2)
{
- PatternText = ($"0\n".Repeat(_infillGenSpacing) + $"255\n".Repeat(_infillGenSpacing)).Trim('\n', '\r');
- AlternatePatternText = null;
- return;
- }
+ [0, 0] = 127,
+ [0, 1] = 255,
+ [1, 0] = 255,
+ [1, 1] = 127,
+ };
- if (pattern == "Square grid")
+ AlternatePattern ??= new Matrix<byte>(2, 2)
{
- var p1 = "0 ".Repeat(_infillGenSpacing) + "255 ".Repeat(_infillGenThickness);
- p1 = p1.Trim() + "\n";
- p1 += p1.Repeat(_infillGenThickness);
-
+ [0, 0] = 255,
+ [0, 1] = 127,
+ [1, 0] = 127,
+ [1, 1] = 255,
+ };
+ }
- var p2 = "255 ".Repeat(_infillGenSpacing) + "255 ".Repeat(_infillGenThickness);
- p2 = p2.Trim() + '\n';
- p2 += p2.Repeat(_infillGenThickness);
+ AlternatePattern ??= Pattern;
- p2 = p2.Trim('\n', '\r');
+ using var blankMat = EmguExtensions.InitMat(SlicerFile.Resolution);
+ using var matPattern = blankMat.NewBlank();
+ using var matAlternatePattern = blankMat.NewBlank();
+ var target = GetRoiOrDefault(blankMat);
- PatternText = p1 + p2;
- AlternatePatternText = null;
- return;
- }
+ CvInvoke.Repeat(Pattern, target.Rows / Pattern.Rows + 1, target.Cols / Pattern.Cols + 1, matPattern);
+ CvInvoke.Repeat(AlternatePattern, target.Rows / AlternatePattern.Rows + 1, target.Cols / AlternatePattern.Cols + 1, matAlternatePattern);
- if (pattern == "Waves")
- {
- var p1 = string.Empty;
- var pos = 0;
- for (sbyte dir = 1; dir >= -1; dir -= 2)
- {
- while (pos >= 0 && pos <= _infillGenSpacing)
- {
- p1 += "0 ".Repeat(pos);
- p1 += "255 ".Repeat(_infillGenThickness);
- p1 += "0 ".Repeat(_infillGenSpacing - pos);
- p1 = p1.Trim() + '\n';
-
- pos += dir;
- }
-
- pos--;
- }
+ using var patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
+ using var alternatePatternMask = new Mat(matAlternatePattern, new Rectangle(0, 0, target.Width, target.Height));
+ /*if (_wallsOnly)
+ {
+ CvInvoke.BitwiseNot(patternMask, patternMask);
+ CvInvoke.BitwiseNot(alternatePatternMask, alternatePatternMask);
+ }*/
- PatternText = p1.Trim('\n', '\r');
- AlternatePatternText = null;
- return;
- }
+ CvInvoke.BitwiseNot(patternMask, patternMask);
+ CvInvoke.BitwiseNot(alternatePatternMask, alternatePatternMask);
- if (pattern == "Lattice")
- {
- var p1 = string.Empty;
- var p2 = string.Empty;
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat, layerIndex, patternMask, alternatePatternMask);
+ SlicerFile[layerIndex].LayerMat = mat;
- var zeros = Math.Max(0, _infillGenSpacing - _infillGenThickness * 2);
+ progress.LockAndIncrement();
+ });
- // Pillar
- for (int i = 0; i < _infillGenThickness; i++)
- {
- p1 += "255 ".Repeat(_infillGenThickness);
- p1 += "0 ".Repeat(zeros);
- p1 += "255 ".Repeat(_infillGenThickness);
- p1 = p1.Trim() + '\n';
- }
+ return !progress.Token.IsCancellationRequested;
+ }
- for (int i = 0; i < zeros; i++)
- {
- p1 += "0 ".Repeat(_infillGenSpacing);
- p1 = p1.Trim() + '\n';
- }
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ if (arguments is null || arguments.Length < 2) return false;
+ var anchor = new Point(-1, -1);
+ var kernel = EmguExtensions.Kernel3x3Rectangle;
- for (int i = 0; i < _infillGenThickness; i++)
- {
- p1 += "255 ".Repeat(_infillGenThickness);
- p1 += "0 ".Repeat(zeros);
- p1 += "255 ".Repeat(_infillGenThickness);
- p1 = p1.Trim() + '\n';
- }
+ uint layerIndex = Convert.ToUInt32(arguments[0]);
+ Mat patternMask = (Mat)arguments[1];
+ Mat alternatePatternMask = arguments.Length >= 3 && arguments[2] is not null ? (Mat)arguments[2] : patternMask;
- // Square
- for (int i = 0; i < _infillGenThickness; i++)
- {
- p2 += "255 ".Repeat(_infillGenSpacing);
- p2 = p2.Trim() + '\n';
- }
+ int wallThickness = FileFormat.MutateGetIterationChamfer(
+ layerIndex,
+ LayerIndexStart,
+ LayerIndexEnd,
+ (int)WallThicknessStart,
+ (int)WallThicknessEnd,
+ Chamfer
+ );
- for (int i = 0; i < zeros; i++)
- {
- p2 += "255 ".Repeat(_infillGenThickness);
- p2 += "0 ".Repeat(zeros);
- p2 += "255 ".Repeat(_infillGenThickness);
- p2 = p2.Trim() + '\n';
- }
- for (int i = 0; i < _infillGenThickness; i++)
- {
- p2 += "255 ".Repeat(_infillGenSpacing);
- p2 = p2.Trim() + '\n';
- }
+ using Mat erode = new();
+ //using Mat diff = new();
+ var original = mat.Clone();
+ var originalRoi = GetRoiOrDefault(original);
+ var target = GetRoiOrDefault(mat);
+ using var mask = GetMask(mat);
-
- PatternText = p1.Trim('\n', '\r');
- AlternatePatternText = p2.Trim('\n', '\r'); ;
- return;
- }
+ CvInvoke.Erode(target, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
+
+ if (_lighteningPixels)
+ {
+ CvInvoke.Add(target, IsNormalPattern(layerIndex) ? patternMask : alternatePatternMask, target, _wallsOnly ? target : erode);
}
-
- protected override bool ExecuteInternally(OperationProgress progress)
+ else
{
- if (Pattern is null)
- {
- Pattern = new Matrix<byte>(2, 2)
- {
- [0, 0] = 127,
- [0, 1] = 255,
- [1, 0] = 255,
- [1, 1] = 127,
- };
-
- AlternatePattern ??= new Matrix<byte>(2, 2)
- {
- [0, 0] = 255,
- [0, 1] = 127,
- [1, 0] = 127,
- [1, 1] = 255,
- };
- }
-
- AlternatePattern ??= Pattern;
-
- using var blankMat = EmguExtensions.InitMat(SlicerFile.Resolution);
- using var matPattern = blankMat.NewBlank();
- using var matAlternatePattern = blankMat.NewBlank();
- var target = GetRoiOrDefault(blankMat);
-
- CvInvoke.Repeat(Pattern, target.Rows / Pattern.Rows + 1, target.Cols / Pattern.Cols + 1, matPattern);
- CvInvoke.Repeat(AlternatePattern, target.Rows / AlternatePattern.Rows + 1, target.Cols / AlternatePattern.Cols + 1, matAlternatePattern);
-
- using var patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height));
- using var alternatePatternMask = new Mat(matAlternatePattern, new Rectangle(0, 0, target.Width, target.Height));
- /*if (_wallsOnly)
- {
- CvInvoke.BitwiseNot(patternMask, patternMask);
- CvInvoke.BitwiseNot(alternatePatternMask, alternatePatternMask);
- }*/
-
- CvInvoke.BitwiseNot(patternMask, patternMask);
- CvInvoke.BitwiseNot(alternatePatternMask, alternatePatternMask);
-
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat, layerIndex, patternMask, alternatePatternMask);
- SlicerFile[layerIndex].LayerMat = mat;
-
- progress.LockAndIncrement();
- });
-
- return !progress.Token.IsCancellationRequested;
+ CvInvoke.Subtract(target, IsNormalPattern(layerIndex) ? patternMask : alternatePatternMask, target, _wallsOnly ? target : erode);
}
- public override bool Execute(Mat mat, params object[] arguments)
+ if (_wallsOnly)
{
- if (arguments is null || arguments.Length < 2) return false;
- var anchor = new Point(-1, -1);
- var kernel = EmguExtensions.Kernel3x3Rectangle;
-
- uint layerIndex = Convert.ToUInt32(arguments[0]);
- Mat patternMask = (Mat)arguments[1];
- Mat alternatePatternMask = arguments.Length >= 3 && arguments[2] is not null ? (Mat)arguments[2] : patternMask;
-
- int wallThickness = LayerManager.MutateGetIterationChamfer(
- layerIndex,
- LayerIndexStart,
- LayerIndexEnd,
- (int)WallThicknessStart,
- (int)WallThicknessEnd,
- Chamfer
- );
-
-
- using Mat erode = new();
- //using Mat diff = new();
- var original = mat.Clone();
- var originalRoi = GetRoiOrDefault(original);
- var target = GetRoiOrDefault(mat);
- using var mask = GetMask(mat);
-
-
- CvInvoke.Erode(target, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default);
-
- if (_lighteningPixels)
- {
- CvInvoke.Add(target, IsNormalPattern(layerIndex) ? patternMask : alternatePatternMask, target, _wallsOnly ? target : erode);
- }
- else
- {
- CvInvoke.Subtract(target, IsNormalPattern(layerIndex) ? patternMask : alternatePatternMask, target, _wallsOnly ? target : erode);
- }
-
- if (_wallsOnly)
- {
- originalRoi.CopyTo(target, erode);
- }
-
- ApplyMask(originalRoi, target, mask);
-
- return true;
+ originalRoi.CopyTo(target, erode);
}
- #endregion
+ ApplyMask(originalRoi, target, mask);
+
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs
index 480b75a..aff671d 100644
--- a/UVtools.Core/Operations/OperationProgress.cs
+++ b/UVtools.Core/Operations/OperationProgress.cs
@@ -11,234 +11,232 @@ using System.Threading;
using UVtools.Core.Extensions;
using UVtools.Core.Objects;
-namespace UVtools.Core.Operations
-{
- public sealed class OperationProgress : BindableBase
- {
- public const string StatusDecodePreviews = "Decoded Previews";
- public const string StatusGatherLayers = "Gathered Layers";
- public const string StatusDecodeLayers = "Decoded Layers";
- public const string StatusEncodePreviews = "Encoded Previews";
- public const string StatusEncodeLayers = "Encoded Layers";
- public const string StatusWritingFile = "Writing File";
- public const string StatusDecodeGcode = "Decoding GCode";
- public const string StatusEncodeGcode = "Encoding GCode";
-
- public const string StatusOptimizingBounds = "Gathering Bounds";
- public const string StatusCalculatingBounds = "Calculating Bounds";
+namespace UVtools.Core.Operations;
- public const string StatusExtracting = "Extracting";
-
- public const string StatusIslands = "Layers processed (Islands/Overhangs/Resin traps)";
- public const string StatusResinTrapsOptimized = "Layers optimized (Resin traps)";
- public const string StatusResinTraps = "Layers processed (Resin traps)";
- public const string StatusRepairLayers = "Repaired Layers";
+public sealed class OperationProgress : BindableBase
+{
+ public const string StatusDecodePreviews = "Decoded Previews";
+ public const string StatusGatherLayers = "Gathered Layers";
+ public const string StatusDecodeLayers = "Decoded Layers";
+ public const string StatusEncodePreviews = "Encoded Previews";
+ public const string StatusEncodeLayers = "Encoded Layers";
+ public const string StatusWritingFile = "Writing File";
+ public const string StatusDecodeGcode = "Decoding GCode";
+ public const string StatusEncodeGcode = "Encoding GCode";
+
+ public const string StatusOptimizingBounds = "Gathering Bounds";
+ public const string StatusCalculatingBounds = "Calculating Bounds";
+
+ public const string StatusExtracting = "Extracting";
+
+ public const string StatusIslands = "Layers processed (Islands/Overhangs/Resin traps)";
+ public const string StatusResinTrapsOptimized = "Layers optimized (Resin traps)";
+ public const string StatusResinTraps = "Layers processed (Resin traps)";
+ public const string StatusRepairLayers = "Repaired Layers";
+
+ public readonly object Mutex = new();
+
+ public CancellationTokenSource TokenSource { get; private set; } = null!;
+ public CancellationToken Token => TokenSource.Token;
+
+ private bool _canCancel = true;
+ private string _title = "Operation";
+ private string _itemName = "Initializing";
+ private uint _processedItems;
+ private uint _itemCount;
+
- public readonly object Mutex = new();
+ public OperationProgress()
+ {
+ Init();
+ }
- public CancellationTokenSource TokenSource { get; private set; }
- public CancellationToken Token => TokenSource.Token;
+ public OperationProgress(string name, uint value = 0) : this()
+ {
+ Reset(name, value);
+ }
- private bool _canCancel = true;
- private string _title = "Operation";
- private string _itemName = "Initializing";
- private uint _processedItems;
- private uint _itemCount;
-
+ public OperationProgress(bool canCancel) : this()
+ {
+ _canCancel = canCancel;
+ }
- public
- OperationProgress()
- {
- Init();
- }
+ public Stopwatch StopWatch { get; } = new ();
- public OperationProgress(string name, uint value = 0) : this()
+ /// <summary>
+ /// Gets or sets if operation can be cancelled
+ /// </summary>
+ public bool CanCancel
+ {
+ get
{
- Reset(name, value);
+ if (!_canCancel) return _canCancel;
+ return !Token.IsCancellationRequested && Token.CanBeCanceled && _canCancel;
}
+ set => RaiseAndSetIfChanged(ref _canCancel, value);
+ }
- public OperationProgress(bool canCancel) : this()
- {
- _canCancel = canCancel;
- }
+ /// <summary>
+ /// Gets or sets the item name for the operation
+ /// </summary>
+ public string Title
+ {
+ get => _title;
+ set => RaiseAndSetIfChanged(ref _title, value);
+ }
- public Stopwatch StopWatch { get; } = new ();
+ public string ElapsedTimeStr => $"{StopWatch.Elapsed.Minutes}m {StopWatch.Elapsed.Seconds}s";
+ //{StopWatch.Elapsed.Milliseconds} ms
- /// <summary>
- /// Gets or sets if operation can be cancelled
- /// </summary>
- public bool CanCancel
+ /// <summary>
+ /// Gets or sets the item name for the operation
+ /// </summary>
+ public string ItemName
+ {
+ get => _itemName;
+ set
{
- get
- {
- if (!_canCancel) return _canCancel;
- return !Token.IsCancellationRequested && Token.CanBeCanceled && _canCancel;
- }
- set => RaiseAndSetIfChanged(ref _canCancel, value);
+ if(!RaiseAndSetIfChanged(ref _itemName, value)) return;
+ RaisePropertyChanged(nameof(Description));
}
+ }
- /// <summary>
- /// Gets or sets the item name for the operation
- /// </summary>
- public string Title
+ /// <summary>
+ /// Gets or sets the number of processed items
+ /// </summary>
+ public uint ProcessedItems
+ {
+ get => _processedItems;
+ set
{
- get => _title;
- set => RaiseAndSetIfChanged(ref _title, value);
+ //_processedItems = value;
+ if(!RaiseAndSetIfChanged(ref _processedItems, value)) return;
+ RaisePropertyChanged(nameof(ProgressPercent));
+ RaisePropertyChanged(nameof(Description));
}
+ }
- public string ElapsedTimeStr => $"{StopWatch.Elapsed.Minutes}m {StopWatch.Elapsed.Seconds}s";
- //{StopWatch.Elapsed.Milliseconds} ms
-
- /// <summary>
- /// Gets or sets the item name for the operation
- /// </summary>
- public string ItemName
+ /// <summary>
+ /// Gets or sets the total of item count on this operation
+ /// </summary>
+ public uint ItemCount
+ {
+ get => _itemCount;
+ set
{
- get => _itemName;
- set
- {
- if(!RaiseAndSetIfChanged(ref _itemName, value)) return;
- RaisePropertyChanged(nameof(Description));
- }
+ RaiseAndSetIfChanged(ref _itemCount, value);
+ RaisePropertyChanged(nameof(IsIndeterminate));
+ RaisePropertyChanged(nameof(ProgressPercent));
+ RaisePropertyChanged(nameof(Description));
}
+ }
- /// <summary>
- /// Gets or sets the number of processed items
- /// </summary>
- public uint ProcessedItems
- {
- get => _processedItems;
- set
- {
- //_processedItems = value;
- if(!RaiseAndSetIfChanged(ref _processedItems, value)) return;
- RaisePropertyChanged(nameof(ProgressPercent));
- RaisePropertyChanged(nameof(Description));
- }
- }
+ /// <summary>
+ /// Gets or sets an tag
+ /// </summary>
+ public object? Tag { get; set; }
- /// <summary>
- /// Gets or sets the total of item count on this operation
- /// </summary>
- public uint ItemCount
- {
- get => _itemCount;
- set
- {
- RaiseAndSetIfChanged(ref _itemCount, value);
- RaisePropertyChanged(nameof(IsIndeterminate));
- RaisePropertyChanged(nameof(ProgressPercent));
- RaisePropertyChanged(nameof(Description));
- }
- }
+ /// <summary>
+ /// Gets the remaining items to be processed
+ /// </summary>
+ public uint RemainingItems => _itemCount - _processedItems;
+
+ public int ProgressStep => (int)ProgressPercent;
- /// <summary>
- /// Gets or sets an tag
- /// </summary>
- public object Tag { get; set; }
+ public string Description => ToString();
- /// <summary>
- /// Gets the remaining items to be processed
- /// </summary>
- public uint RemainingItems => _itemCount - _processedItems;
+ public bool IsIndeterminate => _itemCount == 0;
- public int ProgressStep => (int)ProgressPercent;
+ /// <summary>
+ /// Gets the progress from 0 to 100%
+ /// </summary>
+ public double ProgressPercent => _itemCount == 0 ? 0 : Math.Round(_processedItems * 100.0 / _itemCount, 2).Clamp(0, 100);
- public string Description => ToString();
+ public static OperationProgress operator +(OperationProgress progress, uint value)
+ {
+ progress.ProcessedItems += value;
+ return progress;
+ }
- public bool IsIndeterminate => _itemCount == 0;
+ public static OperationProgress operator ++(OperationProgress progress)
+ {
+ progress.ProcessedItems++;
+ return progress;
+ }
- /// <summary>
- /// Gets the progress from 0 to 100%
- /// </summary>
- public double ProgressPercent => _itemCount == 0 ? 0 : Math.Round(_processedItems * 100.0 / _itemCount, 2).Clamp(0, 100);
+ public static OperationProgress operator --(OperationProgress progress)
+ {
+ progress.ProcessedItems--;
+ return progress;
+ }
- public static OperationProgress operator +(OperationProgress progress, uint value)
- {
- progress.ProcessedItems += value;
- return progress;
- }
+ public void Init(bool canCancel = true)
+ {
+ CanCancel = canCancel;
+ Title = "Operation";
+ ItemName = "Initializing";
+ ItemCount = 0;
+ ProcessedItems = 0;
+
+ TokenSource = new();
+ RaisePropertyChanged(nameof(CanCancel));
+ }
- public static OperationProgress operator ++(OperationProgress progress)
- {
- progress.ProcessedItems++;
- return progress;
- }
+ public void ResetAll(string title, string name = "", uint itemCount = 0, uint items = 0)
+ {
+ Title = title;
+ Reset(name, itemCount, items);
+ }
- public static OperationProgress operator --(OperationProgress progress)
- {
- progress.ProcessedItems--;
- return progress;
- }
+ public void Reset(string name = "", uint itemCount = 0, uint items = 0)
+ {
+ ItemName = name;
+ ItemCount = itemCount;
+ ProcessedItems = items;
+ }
- public void Init(bool canCancel = true)
- {
- CanCancel = canCancel;
- Title = "Operation";
- ItemName = "Initializing";
- ItemCount = 0;
- ProcessedItems = 0;
-
- TokenSource = new();
- RaisePropertyChanged(nameof(CanCancel));
- }
+ public void ResetNameAndProcessed(string name = "", uint items = 0)
+ {
+ ItemName = name;
+ ProcessedItems = items;
+ }
- public void ResetAll(string title, string name = "", uint itemCount = 0, uint items = 0)
- {
- Title = title;
- Reset(name, itemCount, items);
- }
- public void Reset(string name = "", uint itemCount = 0, uint items = 0)
+ public override string ToString()
+ {
+ if (_itemCount == 0 && _processedItems == 0)
{
- ItemName = name;
- ItemCount = itemCount;
- ProcessedItems = items;
+ return $"{_itemName}";
}
- public void ResetNameAndProcessed(string name = "", uint items = 0)
+ if (_itemCount == 0 && _processedItems > 0)
{
- ItemName = name;
- ProcessedItems = items;
+ return $"{_processedItems} {_itemName}";
}
-
- public override string ToString()
- {
- if (_itemCount == 0 && _processedItems == 0)
- {
- return $"{_itemName}";
- }
-
- if (_itemCount == 0 && _processedItems > 0)
- {
- return $"{_processedItems} {_itemName}";
- }
-
- return $"{_processedItems.ToString().PadLeft(_itemCount.ToString().Length, '0')}/{_itemCount} {_itemName} | {ProgressPercent:F2}%";
- /*return _itemCount == 0 ?
+ return $"{_processedItems.ToString().PadLeft(_itemCount.ToString().Length, '0')}/{_itemCount} {_itemName} | {ProgressPercent:F2}%";
+ /*return _itemCount == 0 ?
$"{_processedItems}/? {_itemName}" :
$"{_processedItems.ToString().PadLeft(_itemCount.ToString().Length, '0')}/{_itemCount} {_itemName} | {ProgressPercent:F2}%";*/
- }
+ }
- public void TriggerRefresh()
- {
- RaisePropertyChanged(nameof(ElapsedTimeStr));
- RaisePropertyChanged(nameof(CanCancel));
- //OnPropertyChanged(nameof(ProgressPercent));
- //OnPropertyChanged(nameof(Description));
- }
+ public void TriggerRefresh()
+ {
+ RaisePropertyChanged(nameof(ElapsedTimeStr));
+ RaisePropertyChanged(nameof(CanCancel));
+ //OnPropertyChanged(nameof(ProgressPercent));
+ //OnPropertyChanged(nameof(Description));
+ }
- public void LockAndIncrement()
- {
- /*lock (Mutex)
- {
- ProcessedItems++;
- }*/
- Interlocked.Increment(ref _processedItems);
- RaisePropertyChanged(nameof(ProcessedItems));
- RaisePropertyChanged(nameof(ProgressPercent));
- RaisePropertyChanged(nameof(Description));
- }
+ public void LockAndIncrement()
+ {
+ /*lock (Mutex)
+ {
+ ProcessedItems++;
+ }*/
+ Interlocked.Increment(ref _processedItems);
+ RaisePropertyChanged(nameof(ProcessedItems));
+ RaisePropertyChanged(nameof(ProgressPercent));
+ RaisePropertyChanged(nameof(Description));
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs
index ba7d394..6feb72d 100644
--- a/UVtools.Core/Operations/OperationRaftRelief.cs
+++ b/UVtools.Core/Operations/OperationRaftRelief.cs
@@ -6,280 +6,280 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Drawing;
-using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
+using System;
+using System.Drawing;
+using System.Threading.Tasks;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationRaftRelief : Operation
{
- [Serializable]
- public class OperationRaftRelief : Operation
+ #region Enums
+ public enum RaftReliefTypes : byte
{
- #region Enums
- public enum RaftReliefTypes : byte
- {
- Relief,
- Dimming,
- Decimate
- }
- #endregion
+ Relief,
+ Dimming,
+ Decimate
+ }
+ #endregion
+
+ #region Members
+ private RaftReliefTypes _reliefType = RaftReliefTypes.Relief;
+ private uint _maskLayerIndex;
+ private byte _ignoreFirstLayers;
+ private byte _brightness;
+ private byte _dilateIterations = 15;// +/- 1.5mm radius
+ private byte _wallMargin = 40; // +/- 2mm
+ private byte _holeDiameter = 80; // +/- 4mm
+ private byte _holeSpacing = 40; // +/- 2mm
+
+ #endregion
+
+ #region Overrides
+ public override string IconClass => "fas fa-bowling-ball";
+ public override string Title => "Raft relief";
+ public override string Description =>
+ "Relief raft by adding holes in between to reduce FEP suction, save resin and easier to remove the prints.";
+
+ public override string ConfirmationText =>
+ $"relief the raft";
+
+ public override string ProgressTitle =>
+ $"Relieving raft";
+
+ public override string ProgressAction => "Relieved layers";
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection =>
+ Enumerations.LayerRangeSelection.None;
- #region Members
- private RaftReliefTypes _reliefType = RaftReliefTypes.Relief;
- private uint _maskLayerIndex;
- private byte _ignoreFirstLayers;
- private byte _brightness;
- private byte _dilateIterations = 15;// +/- 1.5mm radius
- private byte _wallMargin = 40; // +/- 2mm
- private byte _holeDiameter = 80; // +/- 4mm
- private byte _holeSpacing = 40; // +/- 2mm
+ public override string ToString()
+ {
+ var result = $"[{_reliefType}] [Mask layer: {_maskLayerIndex}] [Ignore: {_ignoreFirstLayers}] [B: {_brightness}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- #endregion
+ #region Constructor
- #region Overrides
- public override string Title => "Raft relief";
- public override string Description =>
- "Relief raft by adding holes in between to reduce FEP suction, save resin and easier to remove the prints.";
+ public OperationRaftRelief() { }
- public override string ConfirmationText =>
- $"relief the raft";
+ public OperationRaftRelief(FileFormat slicerFile) : base(slicerFile) { }
- public override string ProgressTitle =>
- $"Relieving raft";
+ #endregion
- public override string ProgressAction => "Relieved layers";
+ #region Properties
+ public static Array RaftReliefItems => Enum.GetValues(typeof(RaftReliefTypes));
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection =>
- Enumerations.LayerRangeSelection.None;
-
- public override string ToString()
+ public RaftReliefTypes ReliefType
+ {
+ get => _reliefType;
+ set
{
- var result = $"[{_reliefType}] [Mask layer: {_maskLayerIndex}] [Ignore: {_ignoreFirstLayers}] [B: {_brightness}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ if(!RaiseAndSetIfChanged(ref _reliefType, value)) return;
+ RaisePropertyChanged(nameof(IsRelief));
+ RaisePropertyChanged(nameof(IsDimming));
+ RaisePropertyChanged(nameof(IsDecimate));
}
- #endregion
-
- #region Constructor
-
- public OperationRaftRelief() { }
+ }
- public OperationRaftRelief(FileFormat slicerFile) : base(slicerFile) { }
+ public bool IsRelief => _reliefType == RaftReliefTypes.Relief;
+ public bool IsDimming => _reliefType == RaftReliefTypes.Dimming;
+ public bool IsDecimate => _reliefType == RaftReliefTypes.Decimate;
- #endregion
+ public uint MaskLayerIndex
+ {
+ get => _maskLayerIndex;
+ set => RaiseAndSetIfChanged(ref _maskLayerIndex, value);
+ }
- #region Properties
- public static Array RaftReliefItems => Enum.GetValues(typeof(RaftReliefTypes));
+ public byte IgnoreFirstLayers
+ {
+ get => _ignoreFirstLayers;
+ set => RaiseAndSetIfChanged(ref _ignoreFirstLayers, value);
+ }
- public RaftReliefTypes ReliefType
+ public byte Brightness
+ {
+ get => _brightness;
+ set
{
- get => _reliefType;
- set
- {
- if(!RaiseAndSetIfChanged(ref _reliefType, value)) return;
- RaisePropertyChanged(nameof(IsRelief));
- RaisePropertyChanged(nameof(IsDimming));
- RaisePropertyChanged(nameof(IsDecimate));
- }
+ if (!RaiseAndSetIfChanged(ref _brightness, value)) return;
+ RaisePropertyChanged(nameof(BrightnessPercent));
}
+ }
- public bool IsRelief => _reliefType == RaftReliefTypes.Relief;
- public bool IsDimming => _reliefType == RaftReliefTypes.Dimming;
- public bool IsDecimate => _reliefType == RaftReliefTypes.Decimate;
+ public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
- public uint MaskLayerIndex
- {
- get => _maskLayerIndex;
- set => RaiseAndSetIfChanged(ref _maskLayerIndex, value);
- }
+ public byte DilateIterations
+ {
+ get => _dilateIterations;
+ set => RaiseAndSetIfChanged(ref _dilateIterations, value);
+ }
- public byte IgnoreFirstLayers
- {
- get => _ignoreFirstLayers;
- set => RaiseAndSetIfChanged(ref _ignoreFirstLayers, value);
- }
+ public byte WallMargin
+ {
+ get => _wallMargin;
+ set => RaiseAndSetIfChanged(ref _wallMargin, value);
+ }
- public byte Brightness
- {
- get => _brightness;
- set
- {
- if (!RaiseAndSetIfChanged(ref _brightness, value)) return;
- RaisePropertyChanged(nameof(BrightnessPercent));
- }
- }
+ public byte HoleDiameter
+ {
+ get => _holeDiameter;
+ set => RaiseAndSetIfChanged(ref _holeDiameter, value);
+ }
- public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
+ public byte HoleSpacing
+ {
+ get => _holeSpacing;
+ set => RaiseAndSetIfChanged(ref _holeSpacing, value);
+ }
+ #endregion
- public byte DilateIterations
- {
- get => _dilateIterations;
- set => RaiseAndSetIfChanged(ref _dilateIterations, value);
- }
+ #region Equality
- public byte WallMargin
- {
- get => _wallMargin;
- set => RaiseAndSetIfChanged(ref _wallMargin, value);
- }
+ protected bool Equals(OperationRaftRelief other)
+ {
+ return _reliefType == other._reliefType && _maskLayerIndex == other._maskLayerIndex && _ignoreFirstLayers == other._ignoreFirstLayers && _brightness == other._brightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing;
+ }
- public byte HoleDiameter
- {
- get => _holeDiameter;
- set => RaiseAndSetIfChanged(ref _holeDiameter, value);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationRaftRelief) obj);
+ }
- public byte HoleSpacing
- {
- get => _holeSpacing;
- set => RaiseAndSetIfChanged(ref _holeSpacing, value);
- }
- #endregion
+ public override int GetHashCode()
+ {
+ return HashCode.Combine((int) _reliefType, _maskLayerIndex, _ignoreFirstLayers, _brightness, _dilateIterations, _wallMargin, _holeDiameter, _holeSpacing);
+ }
- #region Equality
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ progress.ItemCount = 0;
+ const uint minSupportsRequired = 5;
+ const uint maxLayerCount = 1000;
- protected bool Equals(OperationRaftRelief other)
+ Mat? supportsMat = null;
+ var anchor = new Point(-1, -1);
+ var kernel = EmguExtensions.Kernel3x3Rectangle;
+
+
+ uint firstSupportLayerIndex = _maskLayerIndex;
+ if (firstSupportLayerIndex <= 0)
{
- return _reliefType == other._reliefType && _maskLayerIndex == other._maskLayerIndex && _ignoreFirstLayers == other._ignoreFirstLayers && _brightness == other._brightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing;
+ uint layerCount = Math.Min(SlicerFile.LayerCount, maxLayerCount);
+ progress.Reset("Tracing raft", layerCount, firstSupportLayerIndex);
+ for (; firstSupportLayerIndex < layerCount; firstSupportLayerIndex++)
+ {
+ if (progress.Token.IsCancellationRequested) return false;
+ progress++;
+ supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat);
+ var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 5, 80, 35, 5, 200);
+ if (circles.Length >= minSupportsRequired) break;
+
+ supportsMat.Dispose();
+ supportsMat = null;
+ }
}
-
- public override bool Equals(object obj)
+ else
{
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationRaftRelief) obj);
+ supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat);
}
- public override int GetHashCode()
+ if (supportsMat is null || /*firstSupportLayerIndex == 0 ||*/ _ignoreFirstLayers >= firstSupportLayerIndex) return false;
+ Mat? patternMat = null;
+
+ if (DilateIterations > 0)
{
- return HashCode.Combine((int) _reliefType, _maskLayerIndex, _ignoreFirstLayers, _brightness, _dilateIterations, _wallMargin, _holeDiameter, _holeSpacing);
+ CvInvoke.Dilate(supportsMat, supportsMat,
+ CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1)),
+ new Point(-1, -1), DilateIterations, BorderType.Reflect101, new MCvScalar());
}
- #endregion
-
- #region Methods
+ var color = new MCvScalar(255 - Brightness);
- protected override bool ExecuteInternally(OperationProgress progress)
+ switch (ReliefType)
{
- progress.ItemCount = 0;
- const uint minSupportsRequired = 5;
- const uint maxLayerCount = 1000;
+ case RaftReliefTypes.Relief:
+ patternMat = EmguExtensions.InitMat(supportsMat.Size);
+ int shapeSize = HoleDiameter + HoleSpacing;
+ using (var shape = EmguExtensions.InitMat(new Size(shapeSize, shapeSize)))
+ {
- Mat supportsMat = null;
- var anchor = new Point(-1, -1);
- var kernel = EmguExtensions.Kernel3x3Rectangle;
+ int center = HoleDiameter / 2;
+ //int centerTwo = operation.HoleDiameter + operation.HoleSpacing + operation.HoleDiameter / 2;
+ int radius = center;
+ CvInvoke.Circle(shape, new Point(shapeSize / 2, shapeSize / 2), radius, color, -1);
+ CvInvoke.Circle(shape, new Point(0, 0), radius / 2, color, -1);
+ CvInvoke.Circle(shape, new Point(0, shapeSize), radius / 2, color, -1);
+ CvInvoke.Circle(shape, new Point(shapeSize, 0), radius / 2, color, -1);
+ CvInvoke.Circle(shape, new Point(shapeSize, shapeSize), radius / 2, color, -1);
+ CvInvoke.Repeat(shape, supportsMat.Height / shape.Height + 1, supportsMat.Width / shape.Width + 1, patternMat);
- uint firstSupportLayerIndex = _maskLayerIndex;
- if (firstSupportLayerIndex <= 0)
- {
- uint layerCount = Math.Min(SlicerFile.LayerCount, maxLayerCount);
- progress.Reset("Tracing raft", layerCount, firstSupportLayerIndex);
- for (; firstSupportLayerIndex < layerCount; firstSupportLayerIndex++)
- {
- if (progress.Token.IsCancellationRequested) return false;
- progress++;
- supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat);
- var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 5, 80, 35, 5, 200);
- if (circles.Length >= minSupportsRequired) break;
-
- supportsMat.Dispose();
- supportsMat = null;
+ patternMat = new Mat(patternMat, new Rectangle(0, 0, supportsMat.Width, supportsMat.Height));
}
- }
- else
- {
- supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat);
- }
- if (supportsMat is null || /*firstSupportLayerIndex == 0 ||*/ _ignoreFirstLayers >= firstSupportLayerIndex) return false;
- Mat patternMat = null;
-
- if (DilateIterations > 0)
- {
- CvInvoke.Dilate(supportsMat, supportsMat,
- CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1)),
- new Point(-1, -1), DilateIterations, BorderType.Reflect101, new MCvScalar());
- }
+ break;
+ case RaftReliefTypes.Dimming:
+ patternMat = EmguExtensions.InitMat(supportsMat.Size, color);
+ break;
+ }
- var color = new MCvScalar(255 - Brightness);
+ progress.Reset(ProgressAction, firstSupportLayerIndex - _ignoreFirstLayers);
+ Parallel.For(_ignoreFirstLayers, firstSupportLayerIndex, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
switch (ReliefType)
{
case RaftReliefTypes.Relief:
- patternMat = EmguExtensions.InitMat(supportsMat.Size);
- int shapeSize = HoleDiameter + HoleSpacing;
- using (var shape = EmguExtensions.InitMat(new Size(shapeSize, shapeSize)))
+ case RaftReliefTypes.Dimming:
+ using (Mat mask = new())
{
+ /*CvInvoke.Subtract(target, supportsMat, mask);
+ CvInvoke.Erode(mask, mask, kernel, anchor, operation.WallMargin, BorderType.Reflect101, new MCvScalar());
+ CvInvoke.Subtract(target, patternMat, target, mask);*/
- int center = HoleDiameter / 2;
- //int centerTwo = operation.HoleDiameter + operation.HoleSpacing + operation.HoleDiameter / 2;
- int radius = center;
- CvInvoke.Circle(shape, new Point(shapeSize / 2, shapeSize / 2), radius, color, -1);
- CvInvoke.Circle(shape, new Point(0, 0), radius / 2, color, -1);
- CvInvoke.Circle(shape, new Point(0, shapeSize), radius / 2, color, -1);
- CvInvoke.Circle(shape, new Point(shapeSize, 0), radius / 2, color, -1);
- CvInvoke.Circle(shape, new Point(shapeSize, shapeSize), radius / 2, color, -1);
-
- CvInvoke.Repeat(shape, supportsMat.Height / shape.Height + 1, supportsMat.Width / shape.Width + 1, patternMat);
-
- patternMat = new Mat(patternMat, new Rectangle(0, 0, supportsMat.Width, supportsMat.Height));
+ CvInvoke.Erode(target, mask, kernel, anchor, WallMargin, BorderType.Reflect101, default);
+ CvInvoke.Subtract(mask, supportsMat, mask);
+ CvInvoke.Subtract(target, patternMat, target, mask);
}
break;
- case RaftReliefTypes.Dimming:
- patternMat = EmguExtensions.InitMat(supportsMat.Size, color);
+ case RaftReliefTypes.Decimate:
+ supportsMat.CopyTo(target);
break;
}
- progress.Reset(ProgressAction, firstSupportLayerIndex - _ignoreFirstLayers);
- Parallel.For(_ignoreFirstLayers, firstSupportLayerIndex, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ ApplyMask(original, target);
+ SlicerFile[layerIndex].LayerMat = mat;
- switch (ReliefType)
- {
- case RaftReliefTypes.Relief:
- case RaftReliefTypes.Dimming:
- using (Mat mask = new())
- {
- /*CvInvoke.Subtract(target, supportsMat, mask);
- CvInvoke.Erode(mask, mask, kernel, anchor, operation.WallMargin, BorderType.Reflect101, new MCvScalar());
- CvInvoke.Subtract(target, patternMat, target, mask);*/
-
- CvInvoke.Erode(target, mask, kernel, anchor, WallMargin, BorderType.Reflect101, default);
- CvInvoke.Subtract(mask, supportsMat, mask);
- CvInvoke.Subtract(target, patternMat, target, mask);
- }
-
- break;
- case RaftReliefTypes.Decimate:
- supportsMat.CopyTo(target);
- break;
- }
+ progress.LockAndIncrement();
+ });
- ApplyMask(original, target);
- SlicerFile[layerIndex].LayerMat = mat;
- progress.LockAndIncrement();
- });
+ supportsMat.Dispose();
+ patternMat?.Dispose();
-
- supportsMat.Dispose();
- patternMat?.Dispose();
-
- return !progress.Token.IsCancellationRequested;
- }
-
- #endregion
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs b/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs
index d545b5c..d8473d6 100644
--- a/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs
+++ b/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs
@@ -8,175 +8,173 @@
using System;
using System.Text;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public class OperationRaiseOnPrintFinish : Operation
- {
- #region Constants
- #endregion
+namespace UVtools.Core.Operations;
- #region Members
- private decimal _positionZ;
+[Serializable]
+public class OperationRaiseOnPrintFinish : Operation
+{
+ #region Constants
+ #endregion
- private bool _outputDummyPixel = true;
- #endregion
+ #region Members
+ private decimal _positionZ;
- #region Overrides
+ private bool _outputDummyPixel = true;
+ #endregion
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ #region Overrides
- public override string Title => "Raise platform on print finish";
- public override string Description =>
- "Raise the build platform to a set position after finish the print.\n\n" +
- "NOTE: Only use this tool once and if your printer firmware don't already raise the build platform after finish the print.\n" +
- "This will create a \"empty\" layer on end to simulate a print at a defined height.\n" +
- "Not compatible with all printers, still it won't cause any harm if printer don't support this strategy.";
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "fas fa-level-up-alt";
+ public override string Title => "Raise platform on print finish";
+ public override string Description =>
+ "Raise the build platform to a set position after finish the print.\n\n" +
+ "NOTE: Only use this tool once and if your printer firmware don't already raise the build platform after finish the print.\n" +
+ "This will create a \"empty\" layer on end to simulate a print at a defined height.\n" +
+ "Not compatible with all printers, still it won't cause any harm if printer don't support this strategy.";
- public override string ConfirmationText =>
- $"raise the platform on print finish to Z={_positionZ}mm";
+ public override string ConfirmationText =>
+ $"raise the platform on print finish to Z={_positionZ}mm";
- public override string ProgressTitle =>
- $"Inserting dummy layer on end";
+ public override string ProgressTitle =>
+ $"Inserting dummy layer on end";
- public override string ProgressAction => "Inserted layer";
+ public override string ProgressAction => "Inserted layer";
- public override string ValidateSpawn()
+ public override string? ValidateSpawn()
+ {
+ if(!SlicerFile.CanUseLayerPositionZ)
{
- if(!SlicerFile.CanUseLayerPositionZ)
- {
- return NotSupportedMessage;
- }
-
- if (SlicerFile.LayerCount >= 2)
- {
- var layerHeight = SlicerFile.LastLayer.LayerHeight;
- var criteria = Math.Max((float) Layer.MaximumHeight, SlicerFile.LayerHeight);
-
- if (layerHeight > criteria)
- {
- return $"With a difference of {layerHeight}mm between the last two layers, it looks like this tool had already been applied.\n" +
- $"The difference must be less or equal to {criteria}mm in order to run this tool.";
- }
- }
-
- return null;
+ return NotSupportedMessage;
}
- public override string ValidateInternally()
+ if (SlicerFile.LayerCount >= 2)
{
- var sb = new StringBuilder();
+ var layerHeight = SlicerFile.LastLayer!.LayerHeight;
+ var criteria = Math.Max((float) Layer.MaximumHeight, SlicerFile.LayerHeight);
- if (!ValidateSpawn(out var message))
- {
- sb.AppendLine(message);
- }
- if((float)_positionZ < SlicerFile.PrintHeight)
+ if (layerHeight > criteria)
{
- sb.AppendLine($"Can't raise to {_positionZ}mm, because it's below the maximum print height of {SlicerFile.PrintHeight}mm.");
+ return $"With a difference of {layerHeight}mm between the last two layers, it looks like this tool had already been applied.\n" +
+ $"The difference must be less or equal to {criteria}mm in order to run this tool.";
}
- else if ((float)_positionZ == SlicerFile.PrintHeight)
- {
- sb.AppendLine($"Raise to {_positionZ}mm will have no effect because it's the same height as last layer of {SlicerFile.PrintHeight}mm.");
- }
-
- return sb.ToString();
}
- public override string ToString()
- {
- var result = $"[Z={_positionZ}mm] [Dummy pixel: {_outputDummyPixel}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
-
- #region Properties
+ return null;
+ }
- public float MinimumPositionZ => Layer.RoundHeight(SlicerFile.PrintHeight + SlicerFile.LayerHeight);
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- /// <summary>
- /// Sets or gets the Z position to raise to
- /// </summary>
- public decimal PositionZ
+ if (!ValidateSpawn(out var message))
{
- get => _positionZ;
- set => RaiseAndSetIfChanged(ref _positionZ, Layer.RoundHeight(value));
+ sb.AppendLine(message);
}
-
- /// <summary>
- /// True to output a dummy pixel on bounding rectangle position to avoid empty layer and blank image, otherwise set to false
- /// </summary>
- public bool OutputDummyPixel
+ if((float)_positionZ < SlicerFile.PrintHeight)
{
- get => _outputDummyPixel;
- set => RaiseAndSetIfChanged(ref _outputDummyPixel, value);
+ sb.AppendLine($"Can't raise to {_positionZ}mm, because it's below the maximum print height of {SlicerFile.PrintHeight}mm.");
}
- #endregion
-
- #region Constructor
-
- public OperationRaiseOnPrintFinish()
+ else if ((float)_positionZ == SlicerFile.PrintHeight)
{
- //_outputDummyPixel = !SlicerFile.SupportsGCode;
+ sb.AppendLine($"Raise to {_positionZ}mm will have no effect because it's the same height as last layer of {SlicerFile.PrintHeight}mm.");
}
+
+ return sb.ToString();
+ }
- public OperationRaiseOnPrintFinish(FileFormat slicerFile) : base(slicerFile)
- {
- if (_positionZ <= 0) _positionZ = (decimal)SlicerFile.MachineZ;
- }
+ public override string ToString()
+ {
+ var result = $"[Z={_positionZ}mm] [Dummy pixel: {_outputDummyPixel}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- #endregion
+ #region Properties
- #region Equality
+ public float MinimumPositionZ => Layer.RoundHeight(SlicerFile.PrintHeight + SlicerFile.LayerHeight);
- protected bool Equals(OperationRaiseOnPrintFinish other)
- {
- return _positionZ == other._positionZ && _outputDummyPixel == other._outputDummyPixel;
- }
+ /// <summary>
+ /// Sets or gets the Z position to raise to
+ /// </summary>
+ public decimal PositionZ
+ {
+ get => _positionZ;
+ set => RaiseAndSetIfChanged(ref _positionZ, Layer.RoundHeight(value));
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationRaiseOnPrintFinish) obj);
- }
+ /// <summary>
+ /// True to output a dummy pixel on bounding rectangle position to avoid empty layer and blank image, otherwise set to false
+ /// </summary>
+ public bool OutputDummyPixel
+ {
+ get => _outputDummyPixel;
+ set => RaiseAndSetIfChanged(ref _outputDummyPixel, value);
+ }
+ #endregion
- public override int GetHashCode()
- {
- return HashCode.Combine(_positionZ, _outputDummyPixel);
- }
+ #region Constructor
- #endregion
+ public OperationRaiseOnPrintFinish()
+ {
+ //_outputDummyPixel = !SlicerFile.SupportsGCode;
+ }
- #region Methods
+ public OperationRaiseOnPrintFinish(FileFormat slicerFile) : base(slicerFile)
+ {
+ if (_positionZ <= 0) _positionZ = (decimal)SlicerFile.MachineZ;
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- var layer = SlicerFile.LastLayer.Clone();
- layer.PositionZ = (float)_positionZ;
- layer.ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.05f; // Very low exposure time
- layer.LightPWM = 0; // Try to disable light if possible
- layer.SetNoDelays();
- using var newMat = _outputDummyPixel
- ? SlicerFile.CreateMatWithDummyPixel(layer.BoundingRectangle.Center())
- : SlicerFile.CreateMat();
+ #endregion
+
+ #region Equality
+
+ protected bool Equals(OperationRaiseOnPrintFinish other)
+ {
+ return _positionZ == other._positionZ && _outputDummyPixel == other._outputDummyPixel;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationRaiseOnPrintFinish) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_positionZ, _outputDummyPixel);
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var layer = SlicerFile.LastLayer!.Clone();
+ layer.PositionZ = (float)_positionZ;
+ layer.ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.05f; // Very low exposure time
+ layer.LightPWM = 0; // Try to disable light if possible
+ layer.SetNoDelays();
+ using var newMat = _outputDummyPixel
+ ? SlicerFile.CreateMatWithDummyPixel(layer.BoundingRectangle.Center())
+ : SlicerFile.CreateMat();
- layer.LayerMat = newMat;
+ layer.LayerMat = newMat;
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.LayerManager.Append(layer);
- });
- return true;
- //return !progress.Token.IsCancellationRequested;
- }
- #endregion
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.Append(layer);
+ });
+ return true;
+ //return !progress.Token.IsCancellationRequested;
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs
index b4e3637..21515e2 100644
--- a/UVtools.Core/Operations/OperationRedrawModel.cs
+++ b/UVtools.Core/Operations/OperationRedrawModel.cs
@@ -6,247 +6,249 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using System;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Structure;
-using Emgu.CV.Util;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public class OperationRedrawModel : Operation
- {
- #region Members
+namespace UVtools.Core.Operations;
- private string _filePath;
- private byte _brightness = 220;
- private bool _contactPointsOnly = true;
- private RedrawTypes _redrawType = RedrawTypes.Supports;
- private bool _ignoreContactLessPixels = true;
+[Serializable]
+public class OperationRedrawModel : Operation
+{
+ #region Members
- #endregion
-
- #region Overrides
+ private string _filePath = null!;
+ private byte _brightness = 220;
+ private bool _contactPointsOnly = true;
+ private RedrawTypes _redrawType = RedrawTypes.Supports;
+ private bool _ignoreContactLessPixels = true;
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection { get; } = Enumerations.LayerRangeSelection.None;
+ #endregion
- public override string Title => "Redraw model/supports";
+ #region Overrides
- public override string Description =>
- "Redraw the model or supports with a set brightness. This requires an extra sliced file from same object but without any supports and raft, straight to the build plate.\n" +
- "Note: Run this tool prior to any made modification. You must find the optimal exposure/brightness combo, or supports can fail.";
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection { get; } = Enumerations.LayerRangeSelection.None;
+ public override string IconClass => "mdi-puzzle-edit";
+ public override string Title => "Redraw model/supports";
- public override string ConfirmationText => "redraw the "+ (_redrawType == RedrawTypes.Supports ? "supports" : "model") +
- $" with an brightness of {_brightness}?";
+ public override string Description =>
+ "Redraw the model or supports with a set brightness. This requires an extra sliced file from same object but without any supports and raft, straight to the build plate.\n" +
+ "Note: Run this tool prior to any made modification. You must find the optimal exposure/brightness combo, or supports can fail.";
- public override string ProgressTitle => "Redrawing " + (_redrawType == RedrawTypes.Supports ? "supports" : "model");
+ public override string ConfirmationText => "redraw the "+ (_redrawType == RedrawTypes.Supports ? "supports" : "model") +
+ $" with an brightness of {_brightness}?";
- public override string ProgressAction => "Redraw layers";
+ public override string ProgressTitle => "Redrawing " + (_redrawType == RedrawTypes.Supports ? "supports" : "model");
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
+ public override string ProgressAction => "Redraw layers";
- if (IsFileValid() is null)
- {
- sb.AppendLine("The selected file is not valid.");
- }
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- return sb.ToString();
+ if (IsFileValid() is null)
+ {
+ sb.AppendLine("The selected file is not valid.");
}
+ return sb.ToString();
+ }
- public override string ToString()
- {
- var result = $"[{_redrawType}] [B: {_brightness}] [CS: {_contactPointsOnly}] [ICLP: {_ignoreContactLessPixels}]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+
+ public override string ToString()
+ {
+ var result = $"[{_redrawType}] [B: {_brightness}] [CS: {_contactPointsOnly}] [ICLP: {_ignoreContactLessPixels}]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #endregion
+ #endregion
- #region Enums
- public enum RedrawTypes : byte
- {
- Supports,
- Model,
- }
- #endregion
+ #region Enums
+ public enum RedrawTypes : byte
+ {
+ Supports,
+ Model,
+ }
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationRedrawModel() { }
+ public OperationRedrawModel() { }
- public OperationRedrawModel(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationRedrawModel(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- [XmlIgnore]
- public string FilePath
- {
- get => _filePath;
- set => RaiseAndSetIfChanged(ref _filePath, value);
- }
+ [XmlIgnore]
+ public string FilePath
+ {
+ get => _filePath;
+ set => RaiseAndSetIfChanged(ref _filePath, value);
+ }
- public RedrawTypes RedrawType
- {
- get => _redrawType;
- set => RaiseAndSetIfChanged(ref _redrawType, value);
- }
+ public RedrawTypes RedrawType
+ {
+ get => _redrawType;
+ set => RaiseAndSetIfChanged(ref _redrawType, value);
+ }
- public static Array RedrawTypesItems => Enum.GetValues(typeof(RedrawTypes));
+ public static Array RedrawTypesItems => Enum.GetValues(typeof(RedrawTypes));
- public byte Brightness
+ public byte Brightness
+ {
+ get => _brightness;
+ set
{
- get => _brightness;
- set
- {
- if (!RaiseAndSetIfChanged(ref _brightness, value)) return;
- RaisePropertyChanged(nameof(BrightnessPercent));
- }
+ if (!RaiseAndSetIfChanged(ref _brightness, value)) return;
+ RaisePropertyChanged(nameof(BrightnessPercent));
}
+ }
- public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
+ public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
- public bool ContactPointsOnly
- {
- get => _contactPointsOnly;
- set => RaiseAndSetIfChanged(ref _contactPointsOnly, value);
- }
+ public bool ContactPointsOnly
+ {
+ get => _contactPointsOnly;
+ set => RaiseAndSetIfChanged(ref _contactPointsOnly, value);
+ }
- public bool IgnoreContactLessPixels
- {
- get => _ignoreContactLessPixels;
- set => RaiseAndSetIfChanged(ref _ignoreContactLessPixels, value);
- }
+ public bool IgnoreContactLessPixels
+ {
+ get => _ignoreContactLessPixels;
+ set => RaiseAndSetIfChanged(ref _ignoreContactLessPixels, value);
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationRedrawModel other)
- {
- return _brightness == other._brightness && _contactPointsOnly == other._contactPointsOnly && _redrawType == other._redrawType && _ignoreContactLessPixels == other._ignoreContactLessPixels;
- }
+ protected bool Equals(OperationRedrawModel other)
+ {
+ return _brightness == other._brightness && _contactPointsOnly == other._contactPointsOnly && _redrawType == other._redrawType && _ignoreContactLessPixels == other._ignoreContactLessPixels;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationRedrawModel) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationRedrawModel) obj);
+ }
- public override int GetHashCode()
- {
- return HashCode.Combine(_brightness, _contactPointsOnly, (int) _redrawType, _ignoreContactLessPixels);
- }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(_brightness, _contactPointsOnly, (int) _redrawType, _ignoreContactLessPixels);
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- public FileFormat IsFileValid(bool returnNewInstance = false) =>
- FileFormat.FindByExtensionOrFilePath(_filePath, returnNewInstance);
+ public FileFormat? IsFileValid(bool returnNewInstance = false) =>
+ FileFormat.FindByExtensionOrFilePath(_filePath, returnNewInstance);
- protected override bool ExecuteInternally(OperationProgress progress)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var otherFile = IsFileValid(true);
+ if (otherFile is null)
{
- var otherFile = IsFileValid(true);
- otherFile.Decode(_filePath, progress);
+ return false;
+ }
+ otherFile.Decode(_filePath, progress);
- progress.Reset(ProgressAction, otherFile.LayerCount);
+ progress.Reset(ProgressAction, otherFile.LayerCount);
- int startLayerIndex = (int)(SlicerFile.LayerCount - otherFile.LayerCount);
- if (startLayerIndex < 0) return false;
- Parallel.For(0, otherFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ int startLayerIndex = (int)(SlicerFile.LayerCount - otherFile.LayerCount);
+ if (startLayerIndex < 0) return false;
+ Parallel.For(0, otherFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var fullMatLayerIndex = startLayerIndex + layerIndex;
+ using var fullMat = SlicerFile[fullMatLayerIndex].LayerMat;
+ using var original = fullMat.Clone();
+ using var bodyMat = otherFile[layerIndex].LayerMat;
+ using var fullMatRoi = GetRoiOrDefault(fullMat);
+ using var bodyMatRoi = GetRoiOrDefault(bodyMat);
+ using var patternMat = EmguExtensions.InitMat(fullMatRoi.Size, new MCvScalar(255 - _brightness));
+ using var supportsMat = new Mat();
+
+ bool modified = false;
+ if (_redrawType == RedrawTypes.Supports && _contactPointsOnly)
{
- if (progress.Token.IsCancellationRequested) return;
- var fullMatLayerIndex = startLayerIndex + layerIndex;
- using var fullMat = SlicerFile[fullMatLayerIndex].LayerMat;
- using var original = fullMat.Clone();
- using var bodyMat = otherFile[layerIndex].LayerMat;
- using var fullMatRoi = GetRoiOrDefault(fullMat);
- using var bodyMatRoi = GetRoiOrDefault(bodyMat);
- using var patternMat = EmguExtensions.InitMat(fullMatRoi.Size, new MCvScalar(255 - _brightness));
- using var supportsMat = new Mat();
-
- bool modified = false;
- if (_redrawType == RedrawTypes.Supports && _contactPointsOnly)
+ if (layerIndex + 1 >= otherFile.LayerCount) return;
+ CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
+ using var contours = supportsMat.FindContours(RetrType.List);
+ if (contours.Size <= 0) return;
+ using var nextLayerMat = otherFile[layerIndex + 1].LayerMat;
+ using var nextLayerMatRoi = GetRoiOrDefault(nextLayerMat);
+ var fullSpan = fullMatRoi.GetDataByteSpan();
+ var supportsSpan = supportsMat.GetDataByteSpan();
+ var nextSpan = nextLayerMatRoi.GetDataByteSpan();
+ for (int i = 0; i < contours.Size; i++)
{
- if (layerIndex + 1 >= otherFile.LayerCount) return;
- CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
- using var contours = supportsMat.FindContours(RetrType.List);
- if (contours.Size <= 0) return;
- using var nextLayerMat = otherFile[layerIndex + 1].LayerMat;
- using var nextLayerMatRoi = GetRoiOrDefault(nextLayerMat);
- var fullSpan = fullMatRoi.GetDataByteSpan();
- var supportsSpan = supportsMat.GetDataByteSpan();
- var nextSpan = nextLayerMatRoi.GetDataByteSpan();
- for (int i = 0; i < contours.Size; i++)
+ var foundContour = false;
+ var rectangle = CvInvoke.BoundingRectangle(contours[i]);
+ for (int y = rectangle.Y; y < rectangle.Bottom && !foundContour; y++)
+ for (int x = rectangle.X; x < rectangle.Right; x++)
{
- var foundContour = false;
- var rectangle = CvInvoke.BoundingRectangle(contours[i]);
- for (int y = rectangle.Y; y < rectangle.Bottom && !foundContour; y++)
- for (int x = rectangle.X; x < rectangle.Right; x++)
+ var pos = supportsMat.GetPixelPos(x, y);
+ if (_ignoreContactLessPixels)
{
- var pos = supportsMat.GetPixelPos(x, y);
- if (_ignoreContactLessPixels)
- {
- if (supportsSpan[pos] <= 10) continue;
- if (nextSpan[pos] <= 0) continue;
- modified = true;
- fullSpan[pos] = _brightness;
- }
- else
- {
- if (supportsSpan[pos] <= 100) continue;
- if (nextSpan[pos] <= 150) continue;
- CvInvoke.DrawContours(fullMatRoi, contours, i, new MCvScalar(_brightness), -1, LineType.AntiAlias);
- modified = true;
- foundContour = true;
- break;
- }
-
+ if (supportsSpan[pos] <= 10) continue;
+ if (nextSpan[pos] <= 0) continue;
+ modified = true;
+ fullSpan[pos] = _brightness;
}
- }
- }
- else
- {
- switch (_redrawType)
- {
- case RedrawTypes.Supports:
- CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
- break;
- case RedrawTypes.Model:
- CvInvoke.BitwiseAnd(fullMatRoi, bodyMatRoi, supportsMat); // Model
+ else
+ {
+ if (supportsSpan[pos] <= 100) continue;
+ if (nextSpan[pos] <= 150) continue;
+ CvInvoke.DrawContours(fullMatRoi, contours, i, new MCvScalar(_brightness), -1, LineType.AntiAlias);
+ modified = true;
+ foundContour = true;
break;
+ }
+
}
-
- CvInvoke.Subtract(fullMatRoi, patternMat, fullMatRoi, supportsMat);
- modified = true;
}
-
- if (modified)
+ }
+ else
+ {
+ switch (_redrawType)
{
- ApplyMask(original, fullMatRoi);
- SlicerFile[fullMatLayerIndex].LayerMat = fullMat;
+ case RedrawTypes.Supports:
+ CvInvoke.Subtract(fullMatRoi, bodyMatRoi, supportsMat); // Supports
+ break;
+ case RedrawTypes.Model:
+ CvInvoke.BitwiseAnd(fullMatRoi, bodyMatRoi, supportsMat); // Model
+ break;
}
- progress.LockAndIncrement();
- });
+ CvInvoke.Subtract(fullMatRoi, patternMat, fullMatRoi, supportsMat);
+ modified = true;
+ }
- return !progress.Token.IsCancellationRequested;
- }
+ if (modified)
+ {
+ ApplyMask(original, fullMatRoi);
+ SlicerFile[fullMatLayerIndex].LayerMat = fullMat;
+ }
- #endregion
+ progress.LockAndIncrement();
+ });
+
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs
index 9a57421..7f472ac 100644
--- a/UVtools.Core/Operations/OperationRepairLayers.cs
+++ b/UVtools.Core/Operations/OperationRepairLayers.cs
@@ -6,6 +6,9 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -16,478 +19,475 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Serialization;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using Emgu.CV.Util;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
using UVtools.Core.Managers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationRepairLayers : Operation
{
- [Serializable]
- public class OperationRepairLayers : Operation
+ #region Members
+ private bool _repairIslands = true;
+ private bool _repairResinTraps = true;
+ private bool _repairSuctionCups;
+ private bool _removeEmptyLayers = true;
+ private ushort _removeIslandsBelowEqualPixelCount = 5;
+ private ushort _removeIslandsRecursiveIterations = 4;
+ private ushort _attachIslandsBelowLayers = 2;
+ private byte _resinTrapsOverlapBy = 5;
+ private byte _suctionCupsVentHole = 16;
+ private uint _gapClosingIterations = 1;
+ private uint _noiseRemovalIterations;
+
+ #endregion
+
+ #region Overrides
+ public override bool CanROI => false;
+ public override string IconClass => "fas fa-toolbox";
+ public override string Title => "Repair layers and issues";
+ public override string Description => string.Empty;
+
+ public override string ConfirmationText => "attempt this repair?";
+
+ public override string ProgressTitle =>
+ $"Reparing layers {LayerIndexStart} through {LayerIndexEnd}";
+
+ public override string ProgressAction => "Repaired layers";
+
+ public override string? ValidateInternally()
{
- #region Members
- private bool _repairIslands = true;
- private bool _repairResinTraps = true;
- private bool _repairSuctionCups;
- private bool _removeEmptyLayers = true;
- private ushort _removeIslandsBelowEqualPixelCount = 5;
- private ushort _removeIslandsRecursiveIterations = 4;
- private ushort _attachIslandsBelowLayers = 2;
- private byte _resinTrapsOverlapBy = 5;
- private byte _suctionCupsVentHole = 16;
- private uint _gapClosingIterations = 1;
- private uint _noiseRemovalIterations;
-
- #endregion
-
- #region Overrides
- public override bool CanROI => false;
- public override string Title => "Repair layers and issues";
- public override string Description => null;
-
- public override string ConfirmationText => "attempt this repair?";
-
- public override string ProgressTitle =>
- $"Reparing layers {LayerIndexStart} through {LayerIndexEnd}";
-
- public override string ProgressAction => "Repaired layers";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
-
- if (!_repairIslands && !_repairResinTraps && !_repairSuctionCups && !_removeEmptyLayers)
- {
- sb.AppendLine("You must select at least one repair operation.");
- }
-
- return sb.ToString();
- }
+ var sb = new StringBuilder();
- public override string ToString()
+ if (!_repairIslands && !_repairResinTraps && !_repairSuctionCups && !_removeEmptyLayers)
{
- var repair = new List<string>();
- if(_repairIslands) repair.Add("Islands");
- if(_repairResinTraps) repair.Add("Resin traps");
- if(_repairSuctionCups) repair.Add("Suction cups");
- if(_removeEmptyLayers) repair.Add("Empty layers");
- var result = $"[Repair: {string.Join('/', repair)}] " +
- $"[Gap closing: {_gapClosingIterations}px] " +
- $"[Noise removal: {_noiseRemovalIterations}px]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("You must select at least one repair operation.");
}
- #endregion
- #region Constructor
+ return sb.ToString();
+ }
- public OperationRepairLayers() { }
+ public override string ToString()
+ {
+ var repair = new List<string>();
+ if(_repairIslands) repair.Add("Islands");
+ if(_repairResinTraps) repair.Add("Resin traps");
+ if(_repairSuctionCups) repair.Add("Suction cups");
+ if(_removeEmptyLayers) repair.Add("Empty layers");
+ var result = $"[Repair: {string.Join('/', repair)}] " +
+ $"[Gap closing: {_gapClosingIterations}px] " +
+ $"[Noise removal: {_noiseRemovalIterations}px]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- public OperationRepairLayers(FileFormat slicerFile) : base(slicerFile) { }
+ #region Constructor
- #endregion
+ public OperationRepairLayers() { }
- #region Properties
- public bool RepairIslands
- {
- get => _repairIslands;
- set => RaiseAndSetIfChanged(ref _repairIslands, value);
- }
+ public OperationRepairLayers(FileFormat slicerFile) : base(slicerFile) { }
- public bool RepairResinTraps
- {
- get => _repairResinTraps;
- set => RaiseAndSetIfChanged(ref _repairResinTraps, value);
- }
+ #endregion
- public bool RepairSuctionCups
- {
- get => _repairSuctionCups;
- set => RaiseAndSetIfChanged(ref _repairSuctionCups, value);
- }
+ #region Properties
+ public bool RepairIslands
+ {
+ get => _repairIslands;
+ set => RaiseAndSetIfChanged(ref _repairIslands, value);
+ }
- public bool RemoveEmptyLayers
- {
- get => _removeEmptyLayers;
- set => RaiseAndSetIfChanged(ref _removeEmptyLayers, value);
- }
+ public bool RepairResinTraps
+ {
+ get => _repairResinTraps;
+ set => RaiseAndSetIfChanged(ref _repairResinTraps, value);
+ }
- public ushort RemoveIslandsBelowEqualPixelCount
- {
- get => _removeIslandsBelowEqualPixelCount;
- set => RaiseAndSetIfChanged(ref _removeIslandsBelowEqualPixelCount, value);
- }
+ public bool RepairSuctionCups
+ {
+ get => _repairSuctionCups;
+ set => RaiseAndSetIfChanged(ref _repairSuctionCups, value);
+ }
- public ushort RemoveIslandsRecursiveIterations
- {
- get => _removeIslandsRecursiveIterations;
- set => RaiseAndSetIfChanged(ref _removeIslandsRecursiveIterations, value);
- }
+ public bool RemoveEmptyLayers
+ {
+ get => _removeEmptyLayers;
+ set => RaiseAndSetIfChanged(ref _removeEmptyLayers, value);
+ }
- public ushort AttachIslandsBelowLayers
- {
- get => _attachIslandsBelowLayers;
- set => RaiseAndSetIfChanged(ref _attachIslandsBelowLayers, value);
- }
+ public ushort RemoveIslandsBelowEqualPixelCount
+ {
+ get => _removeIslandsBelowEqualPixelCount;
+ set => RaiseAndSetIfChanged(ref _removeIslandsBelowEqualPixelCount, value);
+ }
- public byte ResinTrapsOverlapBy
- {
- get => _resinTrapsOverlapBy;
- set => RaiseAndSetIfChanged(ref _resinTrapsOverlapBy, value);
- }
+ public ushort RemoveIslandsRecursiveIterations
+ {
+ get => _removeIslandsRecursiveIterations;
+ set => RaiseAndSetIfChanged(ref _removeIslandsRecursiveIterations, value);
+ }
- public byte SuctionCupsVentHole
- {
- get => _suctionCupsVentHole;
- set => RaiseAndSetIfChanged(ref _suctionCupsVentHole, value);
- }
+ public ushort AttachIslandsBelowLayers
+ {
+ get => _attachIslandsBelowLayers;
+ set => RaiseAndSetIfChanged(ref _attachIslandsBelowLayers, value);
+ }
- public uint GapClosingIterations
- {
- get => _gapClosingIterations;
- set => RaiseAndSetIfChanged(ref _gapClosingIterations, value);
- }
+ public byte ResinTrapsOverlapBy
+ {
+ get => _resinTrapsOverlapBy;
+ set => RaiseAndSetIfChanged(ref _resinTrapsOverlapBy, value);
+ }
- public uint NoiseRemovalIterations
- {
- get => _noiseRemovalIterations;
- set => RaiseAndSetIfChanged(ref _noiseRemovalIterations, value);
- }
+ public byte SuctionCupsVentHole
+ {
+ get => _suctionCupsVentHole;
+ set => RaiseAndSetIfChanged(ref _suctionCupsVentHole, value);
+ }
- [XmlIgnore]
- public IslandDetectionConfiguration IslandDetectionConfig { get; set; }
+ public uint GapClosingIterations
+ {
+ get => _gapClosingIterations;
+ set => RaiseAndSetIfChanged(ref _gapClosingIterations, value);
+ }
- #endregion
+ public uint NoiseRemovalIterations
+ {
+ get => _noiseRemovalIterations;
+ set => RaiseAndSetIfChanged(ref _noiseRemovalIterations, value);
+ }
- #region Equality
+ [XmlIgnore]
+ public IslandDetectionConfiguration? IslandDetectionConfig { get; set; }
- protected bool Equals(OperationRepairLayers other)
- {
- return _repairIslands == other._repairIslands && _repairResinTraps == other._repairResinTraps && _removeEmptyLayers == other._removeEmptyLayers && _repairSuctionCups == other._repairSuctionCups && _removeIslandsBelowEqualPixelCount == other._removeIslandsBelowEqualPixelCount && _removeIslandsRecursiveIterations == other._removeIslandsRecursiveIterations && _attachIslandsBelowLayers == other._attachIslandsBelowLayers && _resinTrapsOverlapBy == other._resinTrapsOverlapBy && _suctionCupsVentHole == other._suctionCupsVentHole && _gapClosingIterations == other._gapClosingIterations && _noiseRemovalIterations == other._noiseRemovalIterations;
- }
+ #endregion
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationRepairLayers)obj);
- }
+ #region Equality
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_repairIslands);
- hashCode.Add(_repairResinTraps);
- hashCode.Add(_removeEmptyLayers);
- hashCode.Add(_repairSuctionCups);
- hashCode.Add(_removeIslandsBelowEqualPixelCount);
- hashCode.Add(_removeIslandsRecursiveIterations);
- hashCode.Add(_attachIslandsBelowLayers);
- hashCode.Add(_resinTrapsOverlapBy);
- hashCode.Add(_suctionCupsVentHole);
- hashCode.Add(_gapClosingIterations);
- hashCode.Add(_noiseRemovalIterations);
- return hashCode.ToHashCode();
- }
+ protected bool Equals(OperationRepairLayers other)
+ {
+ return _repairIslands == other._repairIslands && _repairResinTraps == other._repairResinTraps && _removeEmptyLayers == other._removeEmptyLayers && _repairSuctionCups == other._repairSuctionCups && _removeIslandsBelowEqualPixelCount == other._removeIslandsBelowEqualPixelCount && _removeIslandsRecursiveIterations == other._removeIslandsRecursiveIterations && _attachIslandsBelowLayers == other._attachIslandsBelowLayers && _resinTrapsOverlapBy == other._resinTrapsOverlapBy && _suctionCupsVentHole == other._suctionCupsVentHole && _gapClosingIterations == other._gapClosingIterations && _noiseRemovalIterations == other._noiseRemovalIterations;
+ }
- #endregion
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationRepairLayers)obj);
+ }
- #region Methods
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_repairIslands);
+ hashCode.Add(_repairResinTraps);
+ hashCode.Add(_removeEmptyLayers);
+ hashCode.Add(_repairSuctionCups);
+ hashCode.Add(_removeIslandsBelowEqualPixelCount);
+ hashCode.Add(_removeIslandsRecursiveIterations);
+ hashCode.Add(_attachIslandsBelowLayers);
+ hashCode.Add(_resinTrapsOverlapBy);
+ hashCode.Add(_suctionCupsVentHole);
+ hashCode.Add(_gapClosingIterations);
+ hashCode.Add(_noiseRemovalIterations);
+ return hashCode.ToHashCode();
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- var issues = SlicerFile.IssueManager.GetVisible().ToList();
- // Remove islands
- if (//Issues is not null
- IslandDetectionConfig is not null
- && _repairIslands
- && _removeIslandsBelowEqualPixelCount > 0
- && _removeIslandsRecursiveIterations != 1)
- {
- progress.Reset("Removed recursive islands");
- ushort limit = _removeIslandsRecursiveIterations == 0
- ? ushort.MaxValue
- : _removeIslandsRecursiveIterations;
-
- var recursiveIssues = issues;
- var islandsToRecompute = new ConcurrentBag<uint>();
- var islandConfig = IslandDetectionConfig.Clone();
- var overhangConfig = new OverhangDetectionConfiguration(false);
- var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
- var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
- var emptyLayersConfig = false;
+ #endregion
- islandConfig.Enabled = true;
- //islandConfig.RequiredAreaToProcessCheck = (ushort)(_removeIslandsBelowEqualPixelCount / 2);
+ #region Methods
- for (uint i = 0; i < limit; i++)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var issues = SlicerFile.IssueManager.GetVisible().ToList();
+ // Remove islands
+ if (//Issues is not null
+ IslandDetectionConfig is not null
+ && _repairIslands
+ && _removeIslandsBelowEqualPixelCount > 0
+ && _removeIslandsRecursiveIterations != 1)
+ {
+ progress.Reset("Removed recursive islands");
+ ushort limit = _removeIslandsRecursiveIterations == 0
+ ? ushort.MaxValue
+ : _removeIslandsRecursiveIterations;
+
+ var recursiveIssues = issues;
+ var islandsToRecompute = new ConcurrentBag<uint>();
+ var islandConfig = IslandDetectionConfig.Clone();
+ var overhangConfig = new OverhangDetectionConfiguration(false);
+ var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
+ var printHeightConfig = new PrintHeightDetectionConfiguration(false);
+ var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
+ var emptyLayersConfig = false;
+
+ islandConfig.Enabled = true;
+ //islandConfig.RequiredAreaToProcessCheck = (ushort)(_removeIslandsBelowEqualPixelCount / 2);
+
+ for (uint i = 0; i < limit; i++)
+ {
+ if (i > 0)
{
- if (i > 0)
- {
- /*var whiteList = islandsToRecompute.GroupBy(u => u)
- .Select(grp => grp.First())
- .ToList();*/
- islandConfig.WhiteListLayers = islandsToRecompute.ToList();
- recursiveIssues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, printHeightConfig, emptyLayersConfig);
- //Debug.WriteLine(i);
- }
+ /*var whiteList = islandsToRecompute.GroupBy(u => u)
+ .Select(grp => grp.First())
+ .ToList();*/
+ islandConfig.WhiteListLayers = islandsToRecompute.ToList();
+ recursiveIssues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, printHeightConfig, emptyLayersConfig);
+ //Debug.WriteLine(i);
+ }
- var issuesGroup = IssueManager.GetIssuesBy(recursiveIssues, MainIssue.IssueType.Island)
- .Where(issue => issue.PixelsCount <= RemoveIslandsBelowEqualPixelCount)
- .GroupBy(issue => issue.LayerIndex);
+ var issuesGroup = IssueManager.GetIssuesBy(recursiveIssues, MainIssue.IssueType.Island)
+ .Where(issue => issue.PixelsCount <= RemoveIslandsBelowEqualPixelCount)
+ .GroupBy(issue => issue.LayerIndex);
- if (!issuesGroup.Any()) break; // Nothing to process
+ if (!issuesGroup.Any()) break; // Nothing to process
- islandsToRecompute.Clear();
- Parallel.ForEach(issuesGroup, CoreSettings.ParallelOptions, group =>
+ islandsToRecompute.Clear();
+ Parallel.ForEach(issuesGroup, CoreSettings.ParallelOptions, group =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = SlicerFile[group.Key];
+ var image = layer.LayerMat;
+ var bytes = image.GetDataByteSpan();
+ foreach (IssueOfPoints issue in group)
{
- if (progress.Token.IsCancellationRequested) return;
- var layer = SlicerFile[group.Key];
- var image = layer.LayerMat;
- var bytes = image.GetDataByteSpan();
- foreach (IssueOfPoints issue in group)
+ foreach (var issuePixel in issue.Points)
{
- foreach (var issuePixel in issue.Points)
- {
- bytes[image.GetPixelPos(issuePixel)] = 0;
- }
-
- progress.LockAndIncrement();
+ bytes[image.GetPixelPos(issuePixel)] = 0;
}
- var nextLayerIndex = group.Key + 1;
- if (nextLayerIndex < SlicerFile.LayerCount)
- islandsToRecompute.Add(nextLayerIndex);
+ progress.LockAndIncrement();
+ }
+
+ var nextLayerIndex = group.Key + 1;
+ if (nextLayerIndex < SlicerFile.LayerCount)
+ islandsToRecompute.Add(nextLayerIndex);
- layer.LayerMat = image;
- });
+ layer.LayerMat = image;
+ });
- // Remove from main list due the replicate below repair
- issues.RemoveAll(mainIssue => mainIssue.Type == MainIssue.IssueType.Island && mainIssue.Area <= RemoveIslandsBelowEqualPixelCount);
+ // Remove from main list due the replicate below repair
+ issues.RemoveAll(mainIssue => mainIssue.Type == MainIssue.IssueType.Island && mainIssue.Area <= RemoveIslandsBelowEqualPixelCount);
- if (islandsToRecompute.IsEmpty) break; // No more leftovers
- }
+ if (islandsToRecompute.IsEmpty) break; // No more leftovers
}
+ }
- if (_repairIslands && _attachIslandsBelowLayers > 0)
+ if (_repairIslands && _attachIslandsBelowLayers > 0)
+ {
+ var islandsToProcess = issues;
+
+ if (islandsToProcess.Count == 0)
{
- var islandsToProcess = issues;
+ var islandConfig = IslandDetectionConfig?.Clone() ?? new IslandDetectionConfiguration();
+ var overhangConfig = new OverhangDetectionConfiguration(false);
+ var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
+ var printHeightConfig = new PrintHeightDetectionConfiguration(false);
+ var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
+ var emptyLayersConfig = false;
- if (islandsToProcess.Count == 0)
- {
- var islandConfig = IslandDetectionConfig.Clone();
- var overhangConfig = new OverhangDetectionConfiguration(false);
- var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
- var resinTrapsConfig = new ResinTrapDetectionConfiguration(false);
- var emptyLayersConfig = false;
+ islandConfig.Enabled = true;
- islandConfig.Enabled = true;
+ islandsToProcess = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, printHeightConfig, emptyLayersConfig, progress);
+ islandsToProcess?.RemoveAll(mainIssue => SlicerFile.IssueManager.IgnoredIssues.Contains(mainIssue));
+ }
- islandsToProcess = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, printHeightConfig, emptyLayersConfig, progress);
- islandsToProcess.RemoveAll(mainIssue => SlicerFile.IssueManager.IgnoredIssues.Contains(mainIssue));
- }
+ var issuesGroup = IssueManager.GetIssuesBy(islandsToProcess!, MainIssue.IssueType.Island).GroupBy(issue => issue.LayerIndex);
- var issuesGroup = IssueManager.GetIssuesBy(islandsToProcess, MainIssue.IssueType.Island).GroupBy(issue => issue.LayerIndex);
+ progress.Reset("Attempt to attach islands below", (uint) islandsToProcess!.Count);
+ var sync = new object();
+ Parallel.ForEach(issuesGroup, CoreSettings.ParallelOptions, group =>
+ {
+ using var mat = SlicerFile[group.Key].LayerMat;
+ var matSpan = mat.GetDataByteSpan();
+ var matCache = new Dictionary<uint, Mat>();
+ var matCacheModified = new Dictionary<uint, bool>();
+ var startLayer = Math.Max(0, (int)group.Key - 2);
+ var lowestPossibleLayer = (uint)Math.Max(0, (int)group.Key - 1 - _attachIslandsBelowLayers);
+
+ for (var layerIndex = startLayer+1; layerIndex >= lowestPossibleLayer; layerIndex--)
+ {
+ Debug.WriteLine(layerIndex);
+ Monitor.Enter(SlicerFile[layerIndex].Mutex);
+ matCache.Add((uint) layerIndex, SlicerFile[layerIndex].LayerMat);
+ matCacheModified.Add((uint) layerIndex, false);
+ }
- progress.Reset("Attempt to attach islands below", (uint) islandsToProcess.Count);
- var sync = new object();
- Parallel.ForEach(issuesGroup, CoreSettings.ParallelOptions, group =>
+ foreach (IssueOfPoints issue in group)
{
- using var mat = SlicerFile[group.Key].LayerMat;
- var matSpan = mat.GetDataByteSpan();
- var matCache = new Dictionary<uint, Mat>();
- var matCacheModified = new Dictionary<uint, bool>();
- var startLayer = Math.Max(0, (int)group.Key - 2);
- var lowestPossibleLayer = (uint)Math.Max(0, (int)group.Key - 1 - _attachIslandsBelowLayers);
-
- for (var layerIndex = startLayer+1; layerIndex >= lowestPossibleLayer; layerIndex--)
- {
- Debug.WriteLine(layerIndex);
- Monitor.Enter(SlicerFile[layerIndex].Mutex);
- matCache.Add((uint) layerIndex, SlicerFile[layerIndex].LayerMat);
- matCacheModified.Add((uint) layerIndex, false);
- }
+ int foundAt = startLayer == 0 ? 0 : - 1;
+ var requiredSupportingPixels = Math.Max(1, issue.PixelsCount * IslandDetectionConfig!.RequiredPixelsToSupportMultiplier);
- foreach (IssueOfPoints issue in group)
+ for (var layerIndex = startLayer; layerIndex >= lowestPossibleLayer && foundAt < 0; layerIndex--)
{
- int foundAt = startLayer == 0 ? 0 : - 1;
- var requiredSupportingPixels = Math.Max(1, issue.PixelsCount * IslandDetectionConfig.RequiredPixelsToSupportMultiplier);
-
- for (var layerIndex = startLayer; layerIndex >= lowestPossibleLayer && foundAt < 0; layerIndex--)
- {
- uint pixelsSupportingIsland = 0;
+ uint pixelsSupportingIsland = 0;
- unsafe
- {
- var span = matCache[(uint) layerIndex].GetBytePointer();
+ unsafe
+ {
+ var span = matCache[(uint) layerIndex].GetBytePointer();
- foreach (var point in issue.Points)
- {
- if (span[mat.GetPixelPos(point)] < IslandDetectionConfig.RequiredPixelBrightnessToSupport)
- continue;
+ foreach (var point in issue.Points)
+ {
+ if (span[mat.GetPixelPos(point)] < IslandDetectionConfig.RequiredPixelBrightnessToSupport)
+ continue;
- pixelsSupportingIsland++;
+ pixelsSupportingIsland++;
- if (pixelsSupportingIsland >= requiredSupportingPixels)
- {
- foundAt = layerIndex + 1;
- break;
- }
+ if (pixelsSupportingIsland >= requiredSupportingPixels)
+ {
+ foundAt = layerIndex + 1;
+ break;
}
-
}
+
}
+ }
- // Copy pixels
- if (foundAt >= 0)
+ // Copy pixels
+ if (foundAt >= 0)
+ {
+ for (var layerIndex = startLayer + 1; layerIndex >= foundAt; layerIndex--)
{
- for (var layerIndex = startLayer + 1; layerIndex >= foundAt; layerIndex--)
+ matCacheModified[(uint) layerIndex] = true;
+ unsafe
{
- matCacheModified[(uint) layerIndex] = true;
- unsafe
- {
- var span = matCache[(uint) layerIndex].GetBytePointer();
+ var span = matCache[(uint) layerIndex].GetBytePointer();
- foreach (var point in issue.Points)
- {
- var pos = mat.GetPixelPos(point);
- span[pos] = (byte)Math.Min(span[pos] + matSpan[pos], byte.MaxValue);
- }
+ foreach (var point in issue.Points)
+ {
+ var pos = mat.GetPixelPos(point);
+ span[pos] = (byte)Math.Min(span[pos] + matSpan[pos], byte.MaxValue);
}
}
-
- lock (sync)
- {
- // Remove from processed issues
- issues.Remove(issue.Parent);
- }
}
- progress.LockAndIncrement();
- }
-
- foreach (var dict in matCache)
- {
- if (matCacheModified[dict.Key])
+ lock (sync)
{
- SlicerFile[dict.Key].LayerMat = dict.Value;
+ // Remove from processed issues
+ issues.Remove(issue.Parent!);
}
- dict.Value.Dispose();
- Monitor.Exit(SlicerFile[dict.Key].Mutex);
}
- });
- }
- progress.Reset(ProgressAction, LayerRangeCount);
- if (_repairIslands || _repairResinTraps)
- {
- Parallel.For(LayerIndexStart, LayerIndexEnd, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- var layer = SlicerFile[layerIndex];
- Mat image = null;
+ progress.LockAndIncrement();
+ }
- void InitImage()
+ foreach (var dict in matCache)
+ {
+ if (matCacheModified[dict.Key])
{
- image ??= layer.LayerMat;
+ SlicerFile[dict.Key].LayerMat = dict.Value;
}
+ dict.Value.Dispose();
+ Monitor.Exit(SlicerFile[dict.Key].Mutex);
+ }
+ });
+ }
- if (issues.Count > 0)
+ progress.Reset(ProgressAction, LayerRangeCount);
+ if (_repairIslands || _repairResinTraps)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var layer = SlicerFile[layerIndex];
+ Mat? image = null;
+
+ void InitImage()
+ {
+ image ??= layer.LayerMat;
+ }
+
+ if (issues.Count > 0)
+ {
+ if (_repairIslands && _removeIslandsBelowEqualPixelCount > 0 && _removeIslandsRecursiveIterations == 1)
{
- if (_repairIslands && _removeIslandsBelowEqualPixelCount > 0 && _removeIslandsRecursiveIterations == 1)
+ Span<byte> bytes = null;
+ foreach (IssueOfPoints issue in IssueManager.GetIssuesBy(issues, MainIssue.IssueType.Island, (uint)layerIndex))
{
- Span<byte> bytes = null;
- foreach (IssueOfPoints issue in IssueManager.GetIssuesBy(issues, MainIssue.IssueType.Island, (uint)layerIndex))
- {
- if (issue.PixelsCount > _removeIslandsBelowEqualPixelCount) continue;
+ if (issue.PixelsCount > _removeIslandsBelowEqualPixelCount) continue;
- InitImage();
- if (bytes == null) bytes = image.GetDataByteSpan();
+ InitImage();
+ if (bytes == null) bytes = image!.GetDataByteSpan();
- foreach (var issuePixel in issue.Points)
- {
- bytes[image.GetPixelPos(issuePixel)] = 0;
- }
+ foreach (var issuePixel in issue.Points)
+ {
+ bytes[image!.GetPixelPos(issuePixel)] = 0;
}
}
+ }
- if (_repairResinTraps)
+ if (_repairResinTraps)
+ {
+ foreach (IssueOfContours issue in IssueManager.GetIssuesBy(issues, MainIssue.IssueType.ResinTrap, (uint)layerIndex))
{
- foreach (IssueOfContours issue in IssueManager.GetIssuesBy(issues, MainIssue.IssueType.ResinTrap, (uint)layerIndex))
+ InitImage();
+ using var vec = new VectorOfVectorOfPoint(issue.Contours);
+ CvInvoke.DrawContours(image, vec, -1, EmguExtensions.WhiteColor, -1);
+ if (_resinTrapsOverlapBy > 0)
{
- InitImage();
- using var vec = new VectorOfVectorOfPoint(issue.Contours);
- CvInvoke.DrawContours(image, vec, -1, EmguExtensions.WhiteColor, -1);
- if (_resinTrapsOverlapBy > 0)
- {
- CvInvoke.DrawContours(image, vec, -1, EmguExtensions.WhiteColor, _resinTrapsOverlapBy * 2 + 1);
- }
+ CvInvoke.DrawContours(image, vec, -1, EmguExtensions.WhiteColor, _resinTrapsOverlapBy * 2 + 1);
}
}
}
+ }
- if (_repairIslands && (_gapClosingIterations > 0 || _noiseRemovalIterations > 0))
+ if (_repairIslands && (_gapClosingIterations > 0 || _noiseRemovalIterations > 0))
+ {
+ InitImage();
+ using var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3),
+ new Point(-1, -1));
+ if (_gapClosingIterations > 0)
{
- InitImage();
- using var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3),
- new Point(-1, -1));
- if (_gapClosingIterations > 0)
- {
- CvInvoke.MorphologyEx(image, image, MorphOp.Close, kernel, new Point(-1, -1),
- (int)_gapClosingIterations, BorderType.Default, default);
- }
-
- if (_noiseRemovalIterations > 0)
- {
- CvInvoke.MorphologyEx(image, image, MorphOp.Open, kernel, new Point(-1, -1),
- (int)_noiseRemovalIterations, BorderType.Default, default);
- }
+ CvInvoke.MorphologyEx(image, image, MorphOp.Close, kernel, new Point(-1, -1),
+ (int)_gapClosingIterations, BorderType.Default, default);
}
- if (image is not null)
+ if (_noiseRemovalIterations > 0)
{
- layer.LayerMat = image;
- image.Dispose();
+ CvInvoke.MorphologyEx(image, image, MorphOp.Open, kernel, new Point(-1, -1),
+ (int)_noiseRemovalIterations, BorderType.Default, default);
}
+ }
- progress.LockAndIncrement();
- });
- }
+ if (image is not null)
+ {
+ layer.LayerMat = image;
+ image.Dispose();
+ }
+ progress.LockAndIncrement();
+ });
+ }
- if (_repairSuctionCups && issues.Count > 0)
- {
- SlicerFile.IssueManager.DrillSuctionCupsForIssues(issues.Where(mainIssue => mainIssue.Type == MainIssue.IssueType.SuctionCup), _suctionCupsVentHole, progress);
- }
- if (_removeEmptyLayers)
- {
- var removeLayers = new List<uint>();
- for (var layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
- {
- if (SlicerFile[layerIndex].NonZeroPixelCount == 0)
- {
- removeLayers.Add(layerIndex);
- }
- }
+ if (_repairSuctionCups && issues.Count > 0)
+ {
+ SlicerFile.IssueManager.DrillSuctionCupsForIssues(issues.Where(mainIssue => mainIssue.Type == MainIssue.IssueType.SuctionCup), _suctionCupsVentHole, progress);
+ }
- if (removeLayers.Count > 0)
+ if (_removeEmptyLayers)
+ {
+ var removeLayers = new List<uint>();
+ for (var layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
+ {
+ if (SlicerFile[layerIndex].NonZeroPixelCount == 0)
{
- OperationLayerRemove.RemoveLayers(SlicerFile, removeLayers, progress);
+ removeLayers.Add(layerIndex);
}
}
- return !progress.Token.IsCancellationRequested;
+ if (removeLayers.Count > 0)
+ {
+ OperationLayerRemove.RemoveLayers(SlicerFile, removeLayers, progress);
+ }
}
- #endregion
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs
index 394e4ac..7667895 100644
--- a/UVtools.Core/Operations/OperationResize.cs
+++ b/UVtools.Core/Operations/OperationResize.cs
@@ -6,207 +6,207 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Text;
using System.Threading.Tasks;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationResize : Operation
{
- [Serializable]
- public class OperationResize : Operation
+ #region Members
+ private decimal _x = 100;
+ private decimal _y = 100;
+ private bool _constrainXy;
+ private bool _isFade;
+ #endregion
+
+ #region Overrides
+ public override string IconClass => "fas fa-expand-alt";
+ public override string Title => "Resize";
+ public override string Description =>
+ "Resize the model by a percentage in the X/Y plane.\n\n" +
+ "NOTE: This operation is applied based on the original orientation of the layers in the model file. " +
+ "If the image is rotated 90° in the Layer Preview, you will need to compensate by inverting " +
+ "the X and Y values. The print bounds will also not be validated as part of this operation, so please " +
+ "ensure that the scaling factor does not result in the model being clipped.";
+
+ public override string ConfirmationText =>
+ $"resize model layers {LayerIndexStart} through {LayerIndexEnd} by " +
+ $"X={X}% and Y={Y}%";
+
+ public override string ProgressTitle =>
+ $"Resizing from layers {LayerIndexStart} to {LayerIndexEnd} at X:{X}% Y:{Y}% ";
+
+ public override string ProgressAction => "Resized layers";
+
+ public override string? ValidateInternally()
{
- #region Members
- private decimal _x = 100;
- private decimal _y = 100;
- private bool _constrainXy;
- private bool _isFade;
- #endregion
-
- #region Overrides
- public override string Title => "Resize";
- public override string Description =>
- "Resize the model by a percentage in the X/Y plane.\n\n" +
- "NOTE: This operation is applied based on the original orientation of the layers in the model file. " +
- "If the image is rotated 90° in the Layer Preview, you will need to compensate by inverting " +
- "the X and Y values. The print bounds will also not be validated as part of this operation, so please " +
- "ensure that the scaling factor does not result in the model being clipped.";
-
- public override string ConfirmationText =>
- $"resize model layers {LayerIndexStart} through {LayerIndexEnd} by " +
- $"X={X}% and Y={Y}%";
-
- public override string ProgressTitle =>
- $"Resizing from layers {LayerIndexStart} to {LayerIndexEnd} at X:{X}% Y:{Y}% ";
-
- public override string ProgressAction => "Resized layers";
-
- public override string ValidateInternally()
- {
- var sb = new StringBuilder();
-
- if (X == 100m && Y == 100m)
- {
- sb.AppendLine("X and Y can't both be 100%.");
- }
+ var sb = new StringBuilder();
- return sb.ToString();
- }
-
- public override string ToString()
+ if (X == 100m && Y == 100m)
{
- var result = $"[X: {_x}%, Y: {_y}%] [Fade: {_isFade}] [Constrain: {_constrainXy}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ sb.AppendLine("X and Y can't both be 100%.");
}
- #endregion
- #region Properties
- public decimal X
+ return sb.ToString();
+ }
+
+ public override string ToString()
+ {
+ var result = $"[X: {_x}%, Y: {_y}%] [Fade: {_isFade}] [Constrain: {_constrainXy}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
+
+ #region Properties
+ public decimal X
+ {
+ get => _x;
+ set
{
- get => _x;
- set
- {
- RaiseAndSetIfChanged(ref _x, value);
- if (_constrainXy)
- Y = value;
- RaisePropertyChanged(nameof(XScale));
- }
+ RaiseAndSetIfChanged(ref _x, value);
+ if (_constrainXy)
+ Y = value;
+ RaisePropertyChanged(nameof(XScale));
}
+ }
- public decimal XScale => _x / 100m;
- public decimal YScale => _y / 100m;
+ public decimal XScale => _x / 100m;
+ public decimal YScale => _y / 100m;
- public decimal Y
+ public decimal Y
+ {
+ get => _y;
+ set
{
- get => _y;
- set
- {
- if(!RaiseAndSetIfChanged(ref _y, value)) return;
- RaisePropertyChanged(nameof(YScale));
- }
+ if(!RaiseAndSetIfChanged(ref _y, value)) return;
+ RaisePropertyChanged(nameof(YScale));
}
+ }
- public bool ConstrainXY
+ public bool ConstrainXY
+ {
+ get => _constrainXy;
+ set
{
- get => _constrainXy;
- set
+ if (!RaiseAndSetIfChanged(ref _constrainXy, value)) return;
+ if (_constrainXy)
{
- if (!RaiseAndSetIfChanged(ref _constrainXy, value)) return;
- if (_constrainXy)
- {
- Y = _x;
- }
+ Y = _x;
}
}
+ }
- public bool IsFade
- {
- get => _isFade;
- set => RaiseAndSetIfChanged(ref _isFade, value);
- }
- #endregion
+ public bool IsFade
+ {
+ get => _isFade;
+ set => RaiseAndSetIfChanged(ref _isFade, value);
+ }
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationResize() { }
+ public OperationResize() { }
- public OperationResize(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationResize(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationResize other)
- {
- return _x == other._x && _y == other._y && _constrainXy == other._constrainXy && _isFade == other._isFade;
- }
+ protected bool Equals(OperationResize other)
+ {
+ return _x == other._x && _y == other._y && _constrainXy == other._constrainXy && _isFade == other._isFade;
+ }
- public override bool Equals(object obj)
- {
- if (obj is null) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((OperationResize) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationResize) obj);
+ }
- public override int GetHashCode()
+ public override int GetHashCode()
+ {
+ unchecked
{
- unchecked
- {
- var hashCode = _x.GetHashCode();
- hashCode = (hashCode * 397) ^ _y.GetHashCode();
- hashCode = (hashCode * 397) ^ _constrainXy.GetHashCode();
- hashCode = (hashCode * 397) ^ _isFade.GetHashCode();
- return hashCode;
- }
+ var hashCode = _x.GetHashCode();
+ hashCode = (hashCode * 397) ^ _y.GetHashCode();
+ hashCode = (hashCode * 397) ^ _constrainXy.GetHashCode();
+ hashCode = (hashCode * 397) ^ _isFade.GetHashCode();
+ return hashCode;
}
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- if (_x <= 0 || _y <= 0) return false;
- if (_x == 100 && _y == 100) return false;
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (_x <= 0 || _y <= 0) return false;
+ if (_x == 100 && _y == 100) return false;
- decimal xSteps = Math.Abs(100 - _x) / (LayerIndexEnd - LayerIndexStart + 1);
- decimal ySteps = Math.Abs(100 - _y) / (LayerIndexEnd - LayerIndexStart + 1);
+ decimal xSteps = Math.Abs(100 - _x) / (LayerIndexEnd - LayerIndexStart + 1);
+ decimal ySteps = Math.Abs(100 - _y) / (LayerIndexEnd - LayerIndexStart + 1);
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var newX = _x;
+ var newY = _y;
+ if (IsFade)
{
- if (progress.Token.IsCancellationRequested) return;
- var newX = _x;
- var newY = _y;
- if (IsFade)
+ if (newX != 100)
{
- if (newX != 100)
- {
- newX = newX < 100
- ? newX + (layerIndex - LayerIndexStart) * xSteps
- : newX - (layerIndex - LayerIndexStart) * xSteps;
- }
-
- if (newY != 100)
- {
- newY = newY < 100
- ? newY + (layerIndex - LayerIndexStart) * ySteps
- : newY - (layerIndex - LayerIndexStart) * ySteps;
- }
+ newX = newX < 100
+ ? newX + (layerIndex - LayerIndexStart) * xSteps
+ : newX - (layerIndex - LayerIndexStart) * xSteps;
}
- progress.LockAndIncrement();
+ if (newY != 100)
+ {
+ newY = newY < 100
+ ? newY + (layerIndex - LayerIndexStart) * ySteps
+ : newY - (layerIndex - LayerIndexStart) * ySteps;
+ }
+ }
- if (newX == 100 && newY == 100) return;
+ progress.LockAndIncrement();
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat, newX / 100, newY / 100);
- SlicerFile[layerIndex].LayerMat = mat;
- });
+ if (newX == 100 && newY == 100) return;
- return !progress.Token.IsCancellationRequested;
- }
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat, newX / 100, newY / 100);
+ SlicerFile[layerIndex].LayerMat = mat;
+ });
- public override bool Execute(Mat mat, params object[] arguments)
- {
- var xScale = XScale;
- var yScale = YScale;
- if (arguments is not null && arguments.Length >= 2)
- {
- xScale = (decimal) arguments[0];
- yScale = (decimal) arguments[1];
- }
+ return !progress.Token.IsCancellationRequested;
+ }
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
- target.TransformFromCenter((double) xScale, (double) yScale);
- ApplyMask(original, target);
- return true;
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ var xScale = XScale;
+ var yScale = YScale;
+ if (arguments is not null && arguments.Length >= 2)
+ {
+ xScale = (decimal) arguments[0];
+ yScale = (decimal) arguments[1];
}
- #endregion
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
+ target.TransformFromCenter((double) xScale, (double) yScale);
+ ApplyMask(original, target);
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs
index 717762c..c45f258 100644
--- a/UVtools.Core/Operations/OperationRotate.cs
+++ b/UVtools.Core/Operations/OperationRotate.cs
@@ -6,105 +6,115 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV;
using System;
using System.Threading.Tasks;
-using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationRotate : Operation
{
- [Serializable]
- public class OperationRotate : Operation
- {
- #region Members
- private decimal _angleDegrees = 90;
- #endregion
+ #region Members
+ private decimal _angleDegrees = 90;
+ #endregion
+
+ #region Overrides
+ public override string IconClass => "fas fa-sync-alt";
+ public override string Title => "Rotate";
+ public override string Description =>
+ "Rotate the layers of the model.\n";
- #region Overrides
- public override string Title => "Rotate";
- public override string Description =>
- "Rotate the layers of the model.\n";
+ public override string ConfirmationText =>
+ $"rotate layers {LayerIndexStart} through {LayerIndexEnd} {(AngleDegrees < 0?"counter-clockwise":"clockwise")} by {Math.Abs(AngleDegrees)}°?";
- public override string ConfirmationText =>
- $"rotate layers {LayerIndexStart} through {LayerIndexEnd} {(AngleDegrees < 0?"counter-clockwise":"clockwise")} by {Math.Abs(AngleDegrees)}°?";
+ public override string ProgressTitle =>
+ $"Rotating layers {LayerIndexStart} through {LayerIndexEnd} {(AngleDegrees < 0 ? "counter-clockwise" : "clockwise")} by {Math.Abs(AngleDegrees)}°";
- public override string ProgressTitle =>
- $"Rotating layers {LayerIndexStart} through {LayerIndexEnd} {(AngleDegrees < 0 ? "counter-clockwise" : "clockwise")} by {Math.Abs(AngleDegrees)}°";
+ public override string ProgressAction => "Rotated layers";
- public override string ProgressAction => "Rotated layers";
-
- public override string ToString()
+ public override string? ValidateInternally()
+ {
+ if (_angleDegrees == 0)
{
- var result = $"[Angle: {Math.Abs(_angleDegrees)}º {(AngleDegrees < 0 ? "CCW" : "CW")}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
+ return "An angle of 0 will have no effect";
}
- #endregion
- #region Constructor
+ return null;
+ }
- public OperationRotate() { }
+ public override string ToString()
+ {
+ var result = $"[Angle: {Math.Abs(_angleDegrees)}º {(AngleDegrees < 0 ? "CCW" : "CW")}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- public OperationRotate(FileFormat slicerFile) : base(slicerFile) { }
+ #region Constructor
- #endregion
+ public OperationRotate() { }
- #region Properties
- public decimal AngleDegrees
- {
- get => _angleDegrees;
- set => RaiseAndSetIfChanged(ref _angleDegrees, value);
- }
- #endregion
+ public OperationRotate(FileFormat slicerFile) : base(slicerFile) { }
- #region Equality
+ #endregion
- protected bool Equals(OperationRotate other)
- {
- return _angleDegrees == other._angleDegrees;
- }
+ #region Properties
+ public decimal AngleDegrees
+ {
+ get => _angleDegrees;
+ set => RaiseAndSetIfChanged(ref _angleDegrees, value);
+ }
+ #endregion
- public override bool Equals(object obj)
- {
- if (obj is null) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != GetType()) return false;
- return Equals((OperationRotate) obj);
- }
+ #region Equality
- public override int GetHashCode()
- {
- return _angleDegrees.GetHashCode();
- }
+ protected bool Equals(OperationRotate other)
+ {
+ return _angleDegrees == other._angleDegrees;
+ }
- #endregion
+ public override bool Equals(object? obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((OperationRotate) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return _angleDegrees.GetHashCode();
+ }
- #region Methods
+ #endregion
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat);
- SlicerFile[layerIndex].LayerMat = mat;
- progress.LockAndIncrement();
- });
-
- return !progress.Token.IsCancellationRequested;
- }
+ #region Methods
- public override bool Execute(Mat mat, params object[] arguments)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
- target.Rotate((double)AngleDegrees);
- ApplyMask(original, target);
- return true;
- }
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ SlicerFile[layerIndex].LayerMat = mat;
+ progress.LockAndIncrement();
+ });
+
+ return !progress.Token.IsCancellationRequested;
+ }
- #endregion
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
+ target.Rotate((double)AngleDegrees);
+ ApplyMask(original, target);
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationScripting.cs b/UVtools.Core/Operations/OperationScripting.cs
index 4ca04a1..f6c2782 100644
--- a/UVtools.Core/Operations/OperationScripting.cs
+++ b/UVtools.Core/Operations/OperationScripting.cs
@@ -6,164 +6,171 @@
* of this license document, but changing it is not allowed.
*/
+using Microsoft.CodeAnalysis.CSharp.Scripting;
+using Microsoft.CodeAnalysis.Scripting;
using System;
using System.IO;
using System.Xml.Serialization;
-using Microsoft.CodeAnalysis.CSharp.Scripting;
-using Microsoft.CodeAnalysis.Scripting;
using UVtools.Core.FileFormats;
using UVtools.Core.Scripting;
-namespace UVtools.Core.Operations
-{
- [Serializable]
- public sealed class OperationScripting : Operation
- {
- #region Members
+namespace UVtools.Core.Operations;
- public event EventHandler OnScriptReload;
- private string _filePath;
- private string _scriptText;
- private ScriptState _scriptState;
+[Serializable]
+public sealed class OperationScripting : Operation
+{
+ #region Members
- #endregion
+ public event EventHandler? OnScriptReload;
+ private string? _filePath;
+ private string? _scriptText;
+ private ScriptState? _scriptState;
- #region Overrides
+ #endregion
- public override bool CanRunInPartialMode => true;
+ #region Overrides
- public override bool CanHaveProfiles => false;
+ public override bool CanRunInPartialMode => true;
- public override string Title => "Scripting";
+ public override bool CanHaveProfiles => false;
+ public override string IconClass => "fas fa-code";
+ public override string Title => "Scripting";
- public override string Description =>
- $"Run external scripts to manipulate the loaded file.\n" +
- $"The scripts have wide access to your system and able to do modifications, read/write files, etc. " +
- $"Make sure to run only the scripts you trust! Or run UVtools in a sandbox while executing this.";
+ public override string Description =>
+ $"Run external scripts to manipulate the loaded file.\n" +
+ $"The scripts have wide access to your system and able to do modifications, read/write files, etc. " +
+ $"Make sure to run only the scripts you trust! Or run UVtools in a sandbox while executing this.";
- public override string ConfirmationText =>
- $"run the {ScriptGlobals.Script.Name} script from layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ConfirmationText =>
+ $"run the {ScriptGlobals?.Script.Name} script from layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressTitle =>
- $"Scripting from layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Scripting from layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => "Scripted layers";
+ public override string ProgressAction => "Scripted layers";
- public override string ValidateInternally()
+ public override string? ValidateInternally()
+ {
+ if (!CanExecute)
{
- if (!CanExecute)
+ if (ScriptGlobals is not null && About.Version.CompareTo(ScriptGlobals.Script.MinimumVersionToRun) >= 0)
{
- return "Script is not loaded.";
+ return
+ $"Unable to run due {About.Software} version {About.VersionStr} is lower than required {ScriptGlobals.Script.MinimumVersionToRun}\n" +
+ $"Please update {About.Software} in order to run this script.";
}
- var scriptValidation = _scriptState.ContinueWithAsync<string>("return ScriptValidate();").Result;
- return scriptValidation.ReturnValue;
+ return "Script is not loaded.";
}
- #endregion
+ var scriptValidation = _scriptState!.ContinueWithAsync<string?>("return ScriptValidate();").Result;
+ return scriptValidation.ReturnValue;
+ }
- #region Enums
+ #endregion
- #endregion
+ #region Enums
- #region Properties
+ #endregion
- public ScriptGlobals ScriptGlobals { get; private set; }
+ #region Properties
- public string FilePath
+ public ScriptGlobals? ScriptGlobals { get; private set; }
+
+ public string? FilePath
+ {
+ get => _filePath;
+ set
{
- get => _filePath;
- set
+ if (value is null)
{
- if (value is null)
- {
- RaiseAndSetIfChanged(ref _filePath, null);
- }
- else
- {
- if (!value.EndsWith(".csx") && !value.EndsWith(".cs")) return;
- if (!File.Exists(value)) return;
- if (!RaiseAndSetIfChanged(ref _filePath, value)) return;
- }
-
- RaisePropertyChanged(nameof(HaveFile));
+ RaiseAndSetIfChanged(ref _filePath, null);
+ }
+ else
+ {
+ if (!value.EndsWith(".csx") && !value.EndsWith(".cs")) return;
+ if (!File.Exists(value)) return;
+ if (!RaiseAndSetIfChanged(ref _filePath, value)) return;
}
- }
- [XmlIgnore]
- public string ScriptText
- {
- get => _scriptText;
- set => RaiseAndSetIfChanged(ref _scriptText, value);
+ RaisePropertyChanged(nameof(HaveFile));
}
+ }
+
+ [XmlIgnore]
+ public string? ScriptText
+ {
+ get => _scriptText;
+ set => RaiseAndSetIfChanged(ref _scriptText, value);
+ }
- [XmlIgnore]
- public bool CanExecute => !string.IsNullOrWhiteSpace(_filePath) && _scriptState is not null;
+ [XmlIgnore]
+ public bool CanExecute => !string.IsNullOrWhiteSpace(_filePath) && _scriptState is not null && ScriptGlobals is not null && About.Version.CompareTo(ScriptGlobals.Script.MinimumVersionToRun) >= 0;
- [XmlIgnore]
- public bool HaveFile => !string.IsNullOrWhiteSpace(_filePath);
+ [XmlIgnore]
+ public bool HaveFile => !string.IsNullOrWhiteSpace(_filePath);
- /*public override string ToString()
- {
- var result = $"[{_infillType}] [Wall: {_wallThickness}px] [B: {_infillBrightness}px] [T: {_infillThickness}px] [S: {_infillSpacing}px]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }*/
+ /*public override string ToString()
+ {
+ var result = $"[{_infillType}] [Wall: {_wallThickness}px] [B: {_infillBrightness}px] [T: {_infillThickness}px] [S: {_infillSpacing}px]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }*/
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationScripting() { }
+ public OperationScripting() { }
- public OperationScripting(FileFormat slicerFile) : base(slicerFile)
- { }
+ public OperationScripting(FileFormat slicerFile) : base(slicerFile)
+ { }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- public void ReloadScriptFromFile(string filePath = null)
- {
- if (!string.IsNullOrWhiteSpace(filePath)) FilePath = filePath;
- if (string.IsNullOrWhiteSpace(_filePath) || !File.Exists(_filePath)) return;
- ReloadScriptFromText(File.ReadAllText(_filePath));
- }
-
- public void ReloadScriptFromText(string text = null)
- {
- if (!string.IsNullOrWhiteSpace(text)) ScriptText = text;
- if (string.IsNullOrWhiteSpace(_scriptText)) return;
+ public void ReloadScriptFromFile(string? filePath = null)
+ {
+ if (!string.IsNullOrWhiteSpace(filePath)) FilePath = filePath;
+ if (string.IsNullOrWhiteSpace(_filePath) || !File.Exists(_filePath)) return;
+ ReloadScriptFromText(File.ReadAllText(_filePath));
+ }
+ public void ReloadScriptFromText(string? text = null)
+ {
+ if (!string.IsNullOrWhiteSpace(text)) ScriptText = text;
+ if (string.IsNullOrWhiteSpace(_scriptText)) return;
- ScriptText = ScriptParser.ParseScriptFromText(_scriptText);
- ScriptGlobals = new ScriptGlobals { SlicerFile = SlicerFile, Operation = this };
- _scriptState = CSharpScript.RunAsync(_scriptText,
- ScriptOptions.Default.AddReferences(typeof(About).Assembly).WithAllowUnsafe(true),
- ScriptGlobals).Result;
+ ScriptText = ScriptParser.ParseScriptFromText(_scriptText);
- var result = _scriptState.ContinueWithAsync("ScriptInit();").Result;
+ ScriptGlobals = new ScriptGlobals { SlicerFile = SlicerFile, Operation = this };
+ _scriptState = CSharpScript.RunAsync(_scriptText,
+ ScriptOptions.Default.AddReferences(typeof(About).Assembly).WithAllowUnsafe(true),
+ ScriptGlobals).Result;
- RaisePropertyChanged(nameof(CanExecute));
- OnScriptReload?.Invoke(this, EventArgs.Empty);
- }
+ var result = _scriptState.ContinueWithAsync("ScriptInit();").Result;
+ RaisePropertyChanged(nameof(CanExecute));
+ OnScriptReload?.Invoke(this, EventArgs.Empty);
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
- {
- ScriptGlobals.Progress = progress;
- var scriptExecute = _scriptState.ContinueWithAsync<bool>("return ScriptExecute();").Result;
- return !progress.Token.IsCancellationRequested && scriptExecute.ReturnValue;
- }
- #endregion
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ if (ScriptGlobals is null || _scriptState is null) return false;
+ ScriptGlobals.Progress = progress;
+ var scriptExecute = _scriptState.ContinueWithAsync<bool>("return ScriptExecute();").Result;
+ return !progress.Token.IsCancellationRequested && scriptExecute.ReturnValue;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs
index 897ec83..2ec2988 100644
--- a/UVtools.Core/Operations/OperationSolidify.cs
+++ b/UVtools.Core/Operations/OperationSolidify.cs
@@ -6,132 +6,131 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
-using Emgu.CV.Util;
+using System;
+using System.Threading.Tasks;
using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public sealed class OperationSolidify : Operation
{
- [Serializable]
- public sealed class OperationSolidify : Operation
+ #region Enums
+ public enum AreaCheckTypes
{
- #region Enums
- public enum AreaCheckTypes
- {
- More,
- Less
- }
- #endregion
+ More,
+ Less
+ }
+ #endregion
- #region Members
- private uint _minimumArea = 1;
- private AreaCheckTypes _areaCheckType = AreaCheckTypes.More;
- #endregion
+ #region Members
+ private uint _minimumArea = 1;
+ private AreaCheckTypes _areaCheckType = AreaCheckTypes.More;
+ #endregion
- #region Overrides
+ #region Overrides
- public override string Title => "Solidify";
+ public override string IconClass => "fas fa-circle";
+ public override string Title => "Solidify";
- public override string Description =>
- "Solidifies the selected layers, closing all interior holes.\n\n" +
- "NOTE: All open areas of the layer that are completely surrounded by pixels will be filled. Please ensure that none of the holes in the layer are required before proceeding.";
+ public override string Description =>
+ "Solidifies the selected layers, closing all interior holes.\n\n" +
+ "NOTE: All open areas of the layer that are completely surrounded by pixels will be filled. Please ensure that none of the holes in the layer are required before proceeding.";
- public override string ConfirmationText =>
- $"solidify layers {LayerIndexStart} through {LayerIndexEnd}?";
+ public override string ConfirmationText =>
+ $"solidify layers {LayerIndexStart} through {LayerIndexEnd}?";
- public override string ProgressTitle =>
- $"Solidifying layers {LayerIndexStart} through {LayerIndexEnd}";
+ public override string ProgressTitle =>
+ $"Solidifying layers {LayerIndexStart} through {LayerIndexEnd}";
- public override string ProgressAction => "Solidified layers";
+ public override string ProgressAction => "Solidified layers";
- /// <summary>
- /// Gets the minimum required area to solidify it
- /// </summary>
- public uint MinimumArea
- {
- get => _minimumArea;
- set => RaiseAndSetIfChanged(ref _minimumArea, Math.Max(1, value));
- }
+ /// <summary>
+ /// Gets the minimum required area to solidify it
+ /// </summary>
+ public uint MinimumArea
+ {
+ get => _minimumArea;
+ set => RaiseAndSetIfChanged(ref _minimumArea, Math.Max(1, value));
+ }
- public AreaCheckTypes AreaCheckType
- {
- get => _areaCheckType;
- set => RaiseAndSetIfChanged(ref _areaCheckType, value);
- }
+ public AreaCheckTypes AreaCheckType
+ {
+ get => _areaCheckType;
+ set => RaiseAndSetIfChanged(ref _areaCheckType, value);
+ }
- public static Array AreaCheckTypeItems => Enum.GetValues(typeof(AreaCheckTypes));
+ public static Array AreaCheckTypeItems => Enum.GetValues(typeof(AreaCheckTypes));
- public override string ToString()
- {
- var result = $"[Area: {_areaCheckType} than {_minimumArea}px²]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
+ public override string ToString()
+ {
+ var result = $"[Area: {_areaCheckType} than {_minimumArea}px²]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationSolidify() { }
+ public OperationSolidify() { }
- public OperationSolidify(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationSolidify(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (progress.Token.IsCancellationRequested) return;
- using var mat = SlicerFile[layerIndex].LayerMat;
- Execute(mat);
- SlicerFile[layerIndex].LayerMat = mat;
- progress.LockAndIncrement();
- });
-
- return !progress.Token.IsCancellationRequested;
- }
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ Execute(mat);
+ SlicerFile[layerIndex].LayerMat = mat;
+ progress.LockAndIncrement();
+ });
+
+ return !progress.Token.IsCancellationRequested;
+ }
- public override bool Execute(Mat mat, params object[] arguments)
- {
- using Mat filteredMat = new();
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ using Mat filteredMat = new();
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
- CvInvoke.Threshold(target, filteredMat, 127, 255, ThresholdType.Binary); // Clean AA
- using var contours = filteredMat.FindContours(out var hierarchy, RetrType.Ccomp);
- for (int i = 0; i < contours.Size; i++)
+ CvInvoke.Threshold(target, filteredMat, 127, 255, ThresholdType.Binary); // Clean AA
+ using var contours = filteredMat.FindContours(out var hierarchy, RetrType.Ccomp);
+ for (int i = 0; i < contours.Size; i++)
+ {
+ if (hierarchy[i, EmguContour.HierarchyFirstChild] != -1 || hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
+ if (MinimumArea >= 1)
{
- if (hierarchy[i, EmguContour.HierarchyFirstChild] != -1 || hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
- if (MinimumArea >= 1)
+ var area = CvInvoke.ContourArea(contours[i]);
+ if (AreaCheckType == AreaCheckTypes.More)
{
- var area = CvInvoke.ContourArea(contours[i]);
- if (AreaCheckType == AreaCheckTypes.More)
- {
- if (area < MinimumArea) continue;
- }
- else
- {
- if (area > MinimumArea) continue;
- }
-
+ if (area < MinimumArea) continue;
+ }
+ else
+ {
+ if (area > MinimumArea) continue;
}
- CvInvoke.DrawContours(target, contours, i, EmguExtensions.WhiteColor, -1);
}
- ApplyMask(original, target);
-
- return true;
+ CvInvoke.DrawContours(target, contours, i, EmguExtensions.WhiteColor, -1);
}
- #endregion
+ ApplyMask(original, target);
+
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs
index 47faf36..4dbbe2f 100644
--- a/UVtools.Core/Operations/OperationThreshold.cs
+++ b/UVtools.Core/Operations/OperationThreshold.cs
@@ -6,133 +6,133 @@
* of this license document, but changing it is not allowed.
*/
-using System;
-using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
+using System;
+using System.Threading.Tasks;
using UVtools.Core.FileFormats;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationThreshold : Operation
{
- [Serializable]
- public class OperationThreshold : Operation
+ #region Members
+ private byte _threshold = 127;
+ private byte _maximum = 255;
+ private ThresholdType _type = ThresholdType.Binary;
+ #endregion
+
+ #region Overrides
+ public override string IconClass => "mdi-opacity";
+ public override string Title => "Threshold pixels";
+ public override string Description =>
+ "Manipulate pixel values based on a threshold.\n\n" +
+ "Pixles brighter than the theshold will be set to the Max value, " +
+ "all other pixels will be set to 0.\n\n" +
+ "See https://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html";
+
+ public override string ConfirmationText =>
+ $"apply threshold {Threshold} with max {Maximum} from layers {LayerIndexStart} through {LayerIndexEnd}";
+
+ public override string ProgressTitle =>
+ $"Applying threshold {Threshold} with max {Maximum} from layers {LayerIndexStart} through {LayerIndexEnd}";
+
+ public override string ProgressAction => "Thresholded layers";
+
+ public override string ToString()
{
- #region Members
- private byte _threshold = 127;
- private byte _maximum = 255;
- private ThresholdType _type = ThresholdType.Binary;
- #endregion
-
- #region Overrides
- public override string Title => "Threshold pixels";
- public override string Description =>
- "Manipulate pixel values based on a threshold.\n\n" +
- "Pixles brighter than the theshold will be set to the Max value, " +
- "all other pixels will be set to 0.\n\n" +
- "See https://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html";
-
- public override string ConfirmationText =>
- $"apply threshold {Threshold} with max {Maximum} from layers {LayerIndexStart} through {LayerIndexEnd}";
-
- public override string ProgressTitle =>
- $"Applying threshold {Threshold} with max {Maximum} from layers {LayerIndexStart} through {LayerIndexEnd}";
-
- public override string ProgressAction => "Thresholded layers";
-
- public override string ToString()
- {
- var result = $"[{_type} = {_threshold} / {_maximum}]" + LayerRangeString;
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
+ var result = $"[{_type} = {_threshold} / {_maximum}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationThreshold() { }
+ public OperationThreshold() { }
- public OperationThreshold(FileFormat slicerFile) : base(slicerFile) { }
+ public OperationThreshold(FileFormat slicerFile) : base(slicerFile) { }
- #endregion
+ #endregion
- #region Properties
- public byte Threshold
- {
- get => _threshold;
- set => RaiseAndSetIfChanged(ref _threshold, value);
- }
+ #region Properties
+ public byte Threshold
+ {
+ get => _threshold;
+ set => RaiseAndSetIfChanged(ref _threshold, value);
+ }
- public byte Maximum
- {
- get => _maximum;
- set => RaiseAndSetIfChanged(ref _maximum, value);
- }
+ public byte Maximum
+ {
+ get => _maximum;
+ set => RaiseAndSetIfChanged(ref _maximum, value);
+ }
- public ThresholdType Type
- {
- get => _type;
- set => RaiseAndSetIfChanged(ref _type, value);
- }
+ public ThresholdType Type
+ {
+ get => _type;
+ set => RaiseAndSetIfChanged(ref _type, value);
+ }
- public static Array ThresholdTypes => Enum.GetValues(typeof(ThresholdType));
- #endregion
+ public static Array ThresholdTypes => Enum.GetValues(typeof(ThresholdType));
+ #endregion
- #region Methods
+ #region Methods
- protected override bool ExecuteInternally(OperationProgress progress)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
+ if (progress.Token.IsCancellationRequested) return;
+ using (var mat = SlicerFile[layerIndex].LayerMat)
{
- if (progress.Token.IsCancellationRequested) return;
- using (var mat = SlicerFile[layerIndex].LayerMat)
- {
- Execute(mat);
- SlicerFile[layerIndex].LayerMat = mat;
- }
+ Execute(mat);
+ SlicerFile[layerIndex].LayerMat = mat;
+ }
- progress.LockAndIncrement();
- });
+ progress.LockAndIncrement();
+ });
- return !progress.Token.IsCancellationRequested;
- }
+ return !progress.Token.IsCancellationRequested;
+ }
- public override bool Execute(Mat mat, params object[] arguments)
- {
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
- CvInvoke.Threshold(target, target, Threshold, Maximum, Type);
- ApplyMask(original, target);
- return true;
- }
+ public override bool Execute(Mat mat, params object[]? arguments)
+ {
+ using var original = mat.Clone();
+ var target = GetRoiOrDefault(mat);
+ CvInvoke.Threshold(target, target, Threshold, Maximum, Type);
+ ApplyMask(original, target);
+ return true;
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationThreshold other)
- {
- return _threshold == other._threshold && _maximum == other._maximum && _type == other._type;
- }
+ protected bool Equals(OperationThreshold other)
+ {
+ return _threshold == other._threshold && _maximum == other._maximum && _type == other._type;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationThreshold) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationThreshold) obj);
+ }
- public override int GetHashCode()
+ public override int GetHashCode()
+ {
+ unchecked
{
- unchecked
- {
- var hashCode = _threshold.GetHashCode();
- hashCode = (hashCode * 397) ^ _maximum.GetHashCode();
- hashCode = (hashCode * 397) ^ (int) _type;
- return hashCode;
- }
+ var hashCode = _threshold.GetHashCode();
+ hashCode = (hashCode * 397) ^ _maximum.GetHashCode();
+ hashCode = (hashCode * 397) ^ (int) _type;
+ return hashCode;
}
-
- #endregion
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationTimelapse.cs b/UVtools.Core/Operations/OperationTimelapse.cs
index a2483f5..256d797 100644
--- a/UVtools.Core/Operations/OperationTimelapse.cs
+++ b/UVtools.Core/Operations/OperationTimelapse.cs
@@ -14,472 +14,468 @@ using System.Text;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
-namespace UVtools.Core.Operations
+namespace UVtools.Core.Operations;
+
+[Serializable]
+public class OperationTimelapse : Operation
{
- [Serializable]
- public class OperationTimelapse : Operation
+ #region Enums
+ public enum TimelapseRaiseMode
{
- #region Constants
- #endregion
+ [Description("Lift height: Use the lift sequence to raise to the set postion (Faster)")]
+ LiftHeight,
- #region Enums
- public enum TimelapseRaiseMode
+ [Description("Virtual layer: Print a blank layer to simulate and raise to the set position (Slower)")]
+ VirtualLayer,
+ }
+ #endregion
+
+ #region Members
+ private decimal _raisePositionZ;
+ private bool _outputDummyPixel = true;
+ private decimal _raiseEachNthHeight = 1;
+ private TimelapseRaiseMode _raiseMode = TimelapseRaiseMode.LiftHeight;
+ private decimal _waitTimeAfterLift = 1;
+ private decimal _exposureTime = 1;
+ private bool _ensureLastLayer = true;
+ private bool _useCustomLift;
+ private decimal _slowLiftHeight = 3;
+ private decimal _liftSpeed;
+ private decimal _liftSpeed2;
+ private decimal _slowRetractHeight = 3;
+ private decimal _retractSpeed;
+ private decimal _retractSpeed2;
+
+ #endregion
+
+ #region Overrides
+
+ public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
+ public override string IconClass => "fas fa-camera";
+ public override string Title => "Timelapse";
+ public override string Description =>
+ "Raise the build platform to a set position every odd-even height to be able to take a photo and create a time-lapse video of the print.\n" +
+ "You will require external hardware to take the photos, and create the time-lapse video by your own.\n" +
+ "NOTE: Only use this tool once. It will delay the total print time significantly.";
+
+ public override string ConfirmationText =>
+ $"raise the platform at every odd-even {_raiseEachNthHeight}mm to Z={_raisePositionZ}mm and generate {NumberOfLifts} additional lifts?";
+
+ public override string ProgressTitle => "Raising layers";
+
+ public override string ProgressAction => "Raised layer";
+
+ public override string? ValidateSpawn()
+ {
+ if(!SlicerFile.CanUseLayerPositionZ && !SlicerFile.CanUseLayerLiftHeight)
{
- [Description("Lift height: Use the lift sequence to raise to the set postion (Faster)")]
- LiftHeight,
-
- [Description("Virtual layer: Print a blank layer to simulate and raise to the set position (Slower)")]
- VirtualLayer,
+ return NotSupportedMessage;
}
- #endregion
-
- #region Members
- private decimal _raisePositionZ;
- private bool _outputDummyPixel = true;
- private decimal _raiseEachNthHeight = 1;
- private TimelapseRaiseMode _raiseMode = TimelapseRaiseMode.LiftHeight;
- private decimal _waitTimeAfterLift = 1;
- private decimal _exposureTime = 1;
- private bool _ensureLastLayer = true;
- private bool _useCustomLift;
- private decimal _slowLiftHeight = 3;
- private decimal _liftSpeed;
- private decimal _liftSpeed2;
- private decimal _slowRetractHeight = 3;
- private decimal _retractSpeed;
- private decimal _retractSpeed2;
-
- #endregion
-
- #region Overrides
-
- public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Normal;
-
- public override string Title => "Timelapse";
- public override string Description =>
- "Raise the build platform to a set position every odd-even height to be able to take a photo and create a time-lapse video of the print.\n" +
- "You will require external hardware to take the photos, and create the time-lapse video by your own.\n" +
- "NOTE: Only use this tool once. It will delay the total print time significantly.";
-
- public override string ConfirmationText =>
- $"raise the platform at every odd-even {_raiseEachNthHeight}mm to Z={_raisePositionZ}mm and generate {NumberOfLifts} additional lifts?";
- public override string ProgressTitle => "Raising layers";
+ return null;
+ }
- public override string ProgressAction => "Raised layer";
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
- public override string ValidateSpawn()
+ if (!ValidateSpawn(out var message))
{
- if(!SlicerFile.CanUseLayerPositionZ && !SlicerFile.CanUseLayerLiftHeight)
- {
- return NotSupportedMessage;
- }
-
- return null;
+ sb.AppendLine(message);
}
- public override string ValidateInternally()
+ if ((_raiseMode == TimelapseRaiseMode.LiftHeight && !SlicerFile.CanUseLayerLiftHeight) ||
+ (_raiseMode == TimelapseRaiseMode.VirtualLayer && !SlicerFile.CanUseLayerPositionZ))
{
- var sb = new StringBuilder();
-
- if (!ValidateSpawn(out var message))
- {
- sb.AppendLine(message);
- }
-
- if ((_raiseMode == TimelapseRaiseMode.LiftHeight && !SlicerFile.CanUseLayerLiftHeight) ||
- (_raiseMode == TimelapseRaiseMode.VirtualLayer && !SlicerFile.CanUseLayerPositionZ))
- {
- return $"The raise method {_raiseMode} is not compatible with this printer / file format, please choose other method.";
- }
-
- return sb.ToString();
+ return $"The raise method {_raiseMode} is not compatible with this printer / file format, please choose other method.";
}
+
+ return sb.ToString();
+ }
- public override string ToString()
- {
- var result = $"[Mode: {_raiseMode}] [Z={_raisePositionZ}mm] [Raise each: {_raiseEachNthHeight}mm]";
- if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
- return result;
- }
- #endregion
+ public override string ToString()
+ {
+ var result = $"[Mode: {_raiseMode}] [Z={_raisePositionZ}mm] [Raise each: {_raiseEachNthHeight}mm]";
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
+ #endregion
- #region Properties
+ #region Properties
- public TimelapseRaiseMode RaiseMode
+ public TimelapseRaiseMode RaiseMode
+ {
+ get => _raiseMode;
+ set
{
- get => _raiseMode;
- set
- {
- if(!RaiseAndSetIfChanged(ref _raiseMode, value)) return;
- RaisePropertyChanged(nameof(IsLiftHeightMode));
- RaisePropertyChanged(nameof(IsVirtualLayerMode));
- RaisePropertyChanged(nameof(NumberOfLifts));
- }
+ if(!RaiseAndSetIfChanged(ref _raiseMode, value)) return;
+ RaisePropertyChanged(nameof(IsLiftHeightMode));
+ RaisePropertyChanged(nameof(IsVirtualLayerMode));
+ RaisePropertyChanged(nameof(NumberOfLifts));
}
+ }
- public bool IsLiftHeightMode => _raiseMode is TimelapseRaiseMode.LiftHeight;
- public bool IsVirtualLayerMode => _raiseMode is TimelapseRaiseMode.VirtualLayer;
+ public bool IsLiftHeightMode => _raiseMode is TimelapseRaiseMode.LiftHeight;
+ public bool IsVirtualLayerMode => _raiseMode is TimelapseRaiseMode.VirtualLayer;
- /// <summary>
- /// Sets or gets the Z position to raise to
- /// </summary>
- public decimal RaisePositionZ
- {
- get => _raisePositionZ;
- set => RaiseAndSetIfChanged(ref _raisePositionZ, Layer.RoundHeight(Math.Clamp(value, 10, 10000)));
- }
+ /// <summary>
+ /// Sets or gets the Z position to raise to
+ /// </summary>
+ public decimal RaisePositionZ
+ {
+ get => _raisePositionZ;
+ set => RaiseAndSetIfChanged(ref _raisePositionZ, Layer.RoundHeight(Math.Clamp(value, 10, 10000)));
+ }
- /// <summary>
- /// True to output a dummy pixel on bounding rectangle position to avoid empty layer and blank image, otherwise set to false
- /// </summary>
- public bool OutputDummyPixel
- {
- get => _outputDummyPixel;
- set => RaiseAndSetIfChanged(ref _outputDummyPixel, value);
- }
+ /// <summary>
+ /// True to output a dummy pixel on bounding rectangle position to avoid empty layer and blank image, otherwise set to false
+ /// </summary>
+ public bool OutputDummyPixel
+ {
+ get => _outputDummyPixel;
+ set => RaiseAndSetIfChanged(ref _outputDummyPixel, value);
+ }
- /// <summary>
- /// Gets or sets the alternating height in millimeters to raise when, it will raise only at each defined millimeters and skip the same next millimeters
- /// </summary>
- public decimal RaiseEachNthHeight
+ /// <summary>
+ /// Gets or sets the alternating height in millimeters to raise when, it will raise only at each defined millimeters and skip the same next millimeters
+ /// </summary>
+ public decimal RaiseEachNthHeight
+ {
+ get => _raiseEachNthHeight;
+ set
{
- get => _raiseEachNthHeight;
- set
- {
- if(!RaiseAndSetIfChanged(ref _raiseEachNthHeight, Math.Max(0, Layer.RoundHeight(value)))) return;
- RaisePropertyChanged(nameof(RaiseEachNthLayers));
- RaisePropertyChanged(nameof(NumberOfLifts));
- }
+ if(!RaiseAndSetIfChanged(ref _raiseEachNthHeight, Math.Max(0, Layer.RoundHeight(value)))) return;
+ RaisePropertyChanged(nameof(RaiseEachNthLayers));
+ RaisePropertyChanged(nameof(NumberOfLifts));
}
+ }
- /// <summary>
- /// Gets or sets the alternating layer count to raise when, it will raise only at each defined layers and skip the same next layers
- /// </summary>
- public ushort RaiseEachNthLayers
+ /// <summary>
+ /// Gets or sets the alternating layer count to raise when, it will raise only at each defined layers and skip the same next layers
+ /// </summary>
+ public ushort RaiseEachNthLayers
+ {
+ get
{
- get
- {
- if (_raiseEachNthHeight == 0) return 1;
- return (ushort)Math.Max(1, _raiseEachNthHeight / (decimal)SlicerFile.LayerHeight);
- }
- set => RaiseEachNthHeight = (decimal)SlicerFile.LayerHeight * value;
+ if (_raiseEachNthHeight == 0) return 1;
+ return (ushort)Math.Max(1, _raiseEachNthHeight / (decimal)SlicerFile.LayerHeight);
}
+ set => RaiseEachNthHeight = (decimal)SlicerFile.LayerHeight * value;
+ }
- /// <summary>
- /// Gets the total number of additional lifts
- /// </summary>
- public uint NumberOfLifts => (uint)(Math.Min(SlicerFile.PrintHeight, (float)_raisePositionZ) / Math.Max(SlicerFile.LayerHeight, (float)_raiseEachNthHeight))
- + (_ensureLastLayer && _raiseMode == TimelapseRaiseMode.VirtualLayer ? 1u : 0);
+ /// <summary>
+ /// Gets the total number of additional lifts
+ /// </summary>
+ public uint NumberOfLifts => (uint)(Math.Min(SlicerFile.PrintHeight, (float)_raisePositionZ) / Math.Max(SlicerFile.LayerHeight, (float)_raiseEachNthHeight))
+ + (_ensureLastLayer && _raiseMode == TimelapseRaiseMode.VirtualLayer ? 1u : 0);
- public decimal WaitTimeAfterLift
- {
- get => _waitTimeAfterLift;
- set => RaiseAndSetIfChanged(ref _waitTimeAfterLift, Math.Max(0, value));
- }
+ public decimal WaitTimeAfterLift
+ {
+ get => _waitTimeAfterLift;
+ set => RaiseAndSetIfChanged(ref _waitTimeAfterLift, Math.Max(0, value));
+ }
- public decimal ExposureTime
- {
- get => _exposureTime;
- set => RaiseAndSetIfChanged(ref _exposureTime, Math.Max(0, value));
- }
+ public decimal ExposureTime
+ {
+ get => _exposureTime;
+ set => RaiseAndSetIfChanged(ref _exposureTime, Math.Max(0, value));
+ }
- /// <summary>
- /// Gets or sets if last layer must be ensured in the sequence,
- /// If true, it will generate an obligatory additional layer to cover the last layer
- /// </summary>
- public bool EnsureLastLayer
+ /// <summary>
+ /// Gets or sets if last layer must be ensured in the sequence,
+ /// If true, it will generate an obligatory additional layer to cover the last layer
+ /// </summary>
+ public bool EnsureLastLayer
+ {
+ get => _ensureLastLayer;
+ set
{
- get => _ensureLastLayer;
- set
- {
- if(!RaiseAndSetIfChanged(ref _ensureLastLayer, value)) return;
- RaisePropertyChanged(nameof(NumberOfLifts));
- }
+ if(!RaiseAndSetIfChanged(ref _ensureLastLayer, value)) return;
+ RaisePropertyChanged(nameof(NumberOfLifts));
}
+ }
- public bool UseCustomLift
- {
- get => _useCustomLift;
- set => RaiseAndSetIfChanged(ref _useCustomLift, value);
- }
+ public bool UseCustomLift
+ {
+ get => _useCustomLift;
+ set => RaiseAndSetIfChanged(ref _useCustomLift, value);
+ }
- public bool CanUseCustomLift => _useCustomLift && SlicerFile.CanUseLayerLiftHeight;
+ public bool CanUseCustomLift => _useCustomLift && SlicerFile.CanUseLayerLiftHeight;
- public decimal SlowLiftHeight
- {
- get => _slowLiftHeight;
- set => RaiseAndSetIfChanged(ref _slowLiftHeight, Math.Max(0, value));
- }
+ public decimal SlowLiftHeight
+ {
+ get => _slowLiftHeight;
+ set => RaiseAndSetIfChanged(ref _slowLiftHeight, Math.Max(0, value));
+ }
- public decimal LiftSpeed
- {
- get => _liftSpeed;
- set => RaiseAndSetIfChanged(ref _liftSpeed, Math.Max(0, value));
- }
+ public decimal LiftSpeed
+ {
+ get => _liftSpeed;
+ set => RaiseAndSetIfChanged(ref _liftSpeed, Math.Max(0, value));
+ }
- public decimal LiftSpeed2
- {
- get => _liftSpeed2;
- set => RaiseAndSetIfChanged(ref _liftSpeed2, Math.Max(0, value));
- }
+ public decimal LiftSpeed2
+ {
+ get => _liftSpeed2;
+ set => RaiseAndSetIfChanged(ref _liftSpeed2, Math.Max(0, value));
+ }
- public decimal SlowRetractHeight
- {
- get => _slowRetractHeight;
- set => RaiseAndSetIfChanged(ref _slowRetractHeight, Math.Max(0, value));
- }
+ public decimal SlowRetractHeight
+ {
+ get => _slowRetractHeight;
+ set => RaiseAndSetIfChanged(ref _slowRetractHeight, Math.Max(0, value));
+ }
- public decimal RetractSpeed
- {
- get => _retractSpeed;
- set => RaiseAndSetIfChanged(ref _retractSpeed, Math.Max(0, value));
- }
+ public decimal RetractSpeed
+ {
+ get => _retractSpeed;
+ set => RaiseAndSetIfChanged(ref _retractSpeed, Math.Max(0, value));
+ }
- public decimal RetractSpeed2
- {
- get => _retractSpeed2;
- set => RaiseAndSetIfChanged(ref _retractSpeed2, Math.Max(0, value));
- }
+ public decimal RetractSpeed2
+ {
+ get => _retractSpeed2;
+ set => RaiseAndSetIfChanged(ref _retractSpeed2, Math.Max(0, value));
+ }
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- public OperationTimelapse() { }
+ public OperationTimelapse() { }
- public OperationTimelapse(FileFormat slicerFile) : base(slicerFile)
- {
- if (_raisePositionZ <= 0) _raisePositionZ = (decimal)SlicerFile.MachineZ;
- if(_exposureTime <= 0) _exposureTime = SlicerFile.SupportsGCode ? 0 : 0.05M;
+ public OperationTimelapse(FileFormat slicerFile) : base(slicerFile)
+ {
+ if (_raisePositionZ <= 0) _raisePositionZ = (decimal)SlicerFile.MachineZ;
+ if(_exposureTime <= 0) _exposureTime = SlicerFile.SupportsGCode ? 0 : 0.05M;
- if (_liftSpeed <= 0) _liftSpeed = (decimal) SlicerFile.LiftSpeed;
- if (_liftSpeed2 <= 0) _liftSpeed2 = (decimal) SlicerFile.MaximumSpeed;
+ if (_liftSpeed <= 0) _liftSpeed = (decimal) SlicerFile.LiftSpeed;
+ if (_liftSpeed2 <= 0) _liftSpeed2 = (decimal) SlicerFile.MaximumSpeed;
- if (SlicerFile.CanUseLayerRetractSpeed2) // TSMC
- {
- if (_retractSpeed <= 0) _retractSpeed = (decimal)SlicerFile.MaximumSpeed;
- if (_retractSpeed2 <= 0) _retractSpeed2 = (decimal)SlicerFile.RetractSpeed2;
- }
- else
- {
- if (_retractSpeed <= 0) _retractSpeed = (decimal)SlicerFile.RetractSpeed;
- if (_retractSpeed2 <= 0) _retractSpeed2 = (decimal)SlicerFile.MaximumSpeed;
- }
-
+ if (SlicerFile.CanUseLayerRetractSpeed2) // TSMC
+ {
+ if (_retractSpeed <= 0) _retractSpeed = (decimal)SlicerFile.MaximumSpeed;
+ if (_retractSpeed2 <= 0) _retractSpeed2 = (decimal)SlicerFile.RetractSpeed2;
+ }
+ else
+ {
+ if (_retractSpeed <= 0) _retractSpeed = (decimal)SlicerFile.RetractSpeed;
+ if (_retractSpeed2 <= 0) _retractSpeed2 = (decimal)SlicerFile.MaximumSpeed;
}
+
+ }
- #endregion
+ #endregion
- #region Equality
+ #region Equality
- protected bool Equals(OperationTimelapse other)
- {
- return _raisePositionZ == other._raisePositionZ && _outputDummyPixel == other._outputDummyPixel && _raiseEachNthHeight == other._raiseEachNthHeight && _raiseMode == other._raiseMode && _waitTimeAfterLift == other._waitTimeAfterLift && _exposureTime == other._exposureTime && _ensureLastLayer == other._ensureLastLayer && _useCustomLift == other._useCustomLift && _slowLiftHeight == other._slowLiftHeight && _liftSpeed == other._liftSpeed && _liftSpeed2 == other._liftSpeed2 && _slowRetractHeight == other._slowRetractHeight && _retractSpeed == other._retractSpeed && _retractSpeed2 == other._retractSpeed2;
- }
+ protected bool Equals(OperationTimelapse other)
+ {
+ return _raisePositionZ == other._raisePositionZ && _outputDummyPixel == other._outputDummyPixel && _raiseEachNthHeight == other._raiseEachNthHeight && _raiseMode == other._raiseMode && _waitTimeAfterLift == other._waitTimeAfterLift && _exposureTime == other._exposureTime && _ensureLastLayer == other._ensureLastLayer && _useCustomLift == other._useCustomLift && _slowLiftHeight == other._slowLiftHeight && _liftSpeed == other._liftSpeed && _liftSpeed2 == other._liftSpeed2 && _slowRetractHeight == other._slowRetractHeight && _retractSpeed == other._retractSpeed && _retractSpeed2 == other._retractSpeed2;
+ }
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationTimelapse) obj);
- }
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((OperationTimelapse) obj);
+ }
- public override int GetHashCode()
- {
- var hashCode = new HashCode();
- hashCode.Add(_raisePositionZ);
- hashCode.Add(_outputDummyPixel);
- hashCode.Add(_raiseEachNthHeight);
- hashCode.Add((int) _raiseMode);
- hashCode.Add(_waitTimeAfterLift);
- hashCode.Add(_exposureTime);
- hashCode.Add(_ensureLastLayer);
- hashCode.Add(_useCustomLift);
- hashCode.Add(_slowLiftHeight);
- hashCode.Add(_liftSpeed);
- hashCode.Add(_liftSpeed2);
- hashCode.Add(_slowRetractHeight);
- hashCode.Add(_retractSpeed);
- hashCode.Add(_retractSpeed2);
- return hashCode.ToHashCode();
- }
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_raisePositionZ);
+ hashCode.Add(_outputDummyPixel);
+ hashCode.Add(_raiseEachNthHeight);
+ hashCode.Add((int) _raiseMode);
+ hashCode.Add(_waitTimeAfterLift);
+ hashCode.Add(_exposureTime);
+ hashCode.Add(_ensureLastLayer);
+ hashCode.Add(_useCustomLift);
+ hashCode.Add(_slowLiftHeight);
+ hashCode.Add(_liftSpeed);
+ hashCode.Add(_liftSpeed2);
+ hashCode.Add(_slowRetractHeight);
+ hashCode.Add(_retractSpeed);
+ hashCode.Add(_retractSpeed2);
+ return hashCode.ToHashCode();
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- public void OptimizeRaisePositionZ()
- {
- RaisePositionZ = (decimal) Math.Min(SlicerFile.MachineZ, SlicerFile.PrintHeight + 5);
- }
+ public void OptimizeRaisePositionZ()
+ {
+ RaisePositionZ = (decimal) Math.Min(SlicerFile.MachineZ, SlicerFile.PrintHeight + 5);
+ }
- public void MaxRaisePositionZ()
- {
- RaisePositionZ = (decimal) SlicerFile.MachineZ;
- }
+ public void MaxRaisePositionZ()
+ {
+ RaisePositionZ = (decimal) SlicerFile.MachineZ;
+ }
- protected override bool ExecuteInternally(OperationProgress progress)
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var virtualLayers = new List<uint>();
+ float checkpointHeight = SlicerFile[0].PositionZ;
+
+ for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
{
- var virtualLayers = new List<uint>();
- float checkpointHeight = SlicerFile[0].PositionZ;
+ progress++;
+ var layer = SlicerFile[layerIndex];
+ if ((decimal)layer.PositionZ >= _raisePositionZ) break; // pass the target height, do not continue
+ if (_raiseMode == TimelapseRaiseMode.VirtualLayer && _ensureLastLayer && layerIndex == LayerIndexEnd)
+ {
+ virtualLayers.Add(layerIndex+1);
+ break;
+ }
+ if (_raiseEachNthHeight > 0 && (decimal)Layer.RoundHeight(layer.PositionZ - checkpointHeight) < _raiseEachNthHeight) continue;
+ checkpointHeight = layer.PositionZ;
- for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
+ switch (_raiseMode)
{
- progress++;
- var layer = SlicerFile[layerIndex];
- if ((decimal)layer.PositionZ >= _raisePositionZ) break; // pass the target height, do not continue
- if (_raiseMode == TimelapseRaiseMode.VirtualLayer && _ensureLastLayer && layerIndex == LayerIndexEnd)
- {
- virtualLayers.Add(layerIndex+1);
- break;
- }
- if (_raiseEachNthHeight > 0 && (decimal)Layer.RoundHeight(layer.PositionZ - checkpointHeight) < _raiseEachNthHeight) continue;
- checkpointHeight = layer.PositionZ;
+ case TimelapseRaiseMode.LiftHeight:
+ if (CanUseCustomLift)
+ {
+ layer.LiftSpeed = (float)_liftSpeed;
+ layer.RetractSpeed = (float)_retractSpeed;
- switch (_raiseMode)
- {
- case TimelapseRaiseMode.LiftHeight:
- if (CanUseCustomLift)
+ if (SlicerFile.CanUseLayerLiftHeight2)
{
- layer.LiftSpeed = (float)_liftSpeed;
- layer.RetractSpeed = (float)_retractSpeed;
-
- if (SlicerFile.CanUseLayerLiftHeight2)
- {
- layer.LiftHeight = (float)_slowLiftHeight;
- }
-
- if (SlicerFile.CanUseLayerLiftSpeed2)
- {
- layer.LiftSpeed2 = (float)_liftSpeed2;
- }
-
- if (SlicerFile.CanUseLayerRetractHeight2)
- {
- layer.RetractHeight2 = (float)_slowRetractHeight;
- }
-
- if (SlicerFile.CanUseLayerRetractSpeed2)
- {
- layer.RetractSpeed2 = (float)_retractSpeed2;
- }
+ layer.LiftHeight = (float)_slowLiftHeight;
}
- if (SlicerFile.CanUseLayerLiftHeight2 && (layer.LiftHeight2 > 0 || CanUseCustomLift && _slowLiftHeight > 0)) // TSMC
+ if (SlicerFile.CanUseLayerLiftSpeed2)
{
- layer.LiftHeight2 = Math.Max(0, (float)_raisePositionZ - layer.PositionZ - layer.LiftHeight);
+ layer.LiftSpeed2 = (float)_liftSpeed2;
}
- else
+
+ if (SlicerFile.CanUseLayerRetractHeight2)
{
- layer.LiftHeightTotal = Math.Max(layer.LiftHeightTotal, (float)_raisePositionZ - layer.PositionZ);
+ layer.RetractHeight2 = (float)_slowRetractHeight;
}
- if (SlicerFile.CanUseLayerWaitTimeAfterLift && _waitTimeAfterLift > 0)
+ if (SlicerFile.CanUseLayerRetractSpeed2)
{
- layer.WaitTimeAfterLift = (float) _waitTimeAfterLift;
+ layer.RetractSpeed2 = (float)_retractSpeed2;
}
+ }
- break;
- case TimelapseRaiseMode.VirtualLayer:
- virtualLayers.Add(layerIndex);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(RaiseMode));
- }
+ if (SlicerFile.CanUseLayerLiftHeight2 && (layer.LiftHeight2 > 0 || CanUseCustomLift && _slowLiftHeight > 0)) // TSMC
+ {
+ layer.LiftHeight2 = Math.Max(0, (float)_raisePositionZ - layer.PositionZ - layer.LiftHeight);
+ }
+ else
+ {
+ layer.LiftHeightTotal = Math.Max(layer.LiftHeightTotal, (float)_raisePositionZ - layer.PositionZ);
+ }
+
+ if (SlicerFile.CanUseLayerWaitTimeAfterLift && _waitTimeAfterLift > 0)
+ {
+ layer.WaitTimeAfterLift = (float) _waitTimeAfterLift;
+ }
+
+ break;
+ case TimelapseRaiseMode.VirtualLayer:
+ virtualLayers.Add(layerIndex);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(RaiseMode));
}
+ }
- if (virtualLayers.Count > 0 && _raiseMode == TimelapseRaiseMode.VirtualLayer)
- {
- var minLiftSpeed = CanUseCustomLift ? (float)Math.Min(_liftSpeed, _liftSpeed2) : SlicerFile.MinimumNormalSpeed;
- var minRetractSpeed = CanUseCustomLift ? (float)Math.Min(_retractSpeed, _retractSpeed2) : SlicerFile.MinimumNormalSpeed;
- var maxLiftSpeed = CanUseCustomLift ? (float)Math.Max(_liftSpeed, _liftSpeed2) : SlicerFile.MaximumSpeed;
- var maxRetractSpeed = CanUseCustomLift ? (float)Math.Max(_retractSpeed, _retractSpeed2) : SlicerFile.MaximumSpeed;
+ if (virtualLayers.Count > 0 && _raiseMode == TimelapseRaiseMode.VirtualLayer)
+ {
+ var minLiftSpeed = CanUseCustomLift ? (float)Math.Min(_liftSpeed, _liftSpeed2) : SlicerFile.MinimumNormalSpeed;
+ var minRetractSpeed = CanUseCustomLift ? (float)Math.Min(_retractSpeed, _retractSpeed2) : SlicerFile.MinimumNormalSpeed;
+ var maxLiftSpeed = CanUseCustomLift ? (float)Math.Max(_liftSpeed, _liftSpeed2) : SlicerFile.MaximumSpeed;
+ var maxRetractSpeed = CanUseCustomLift ? (float)Math.Max(_retractSpeed, _retractSpeed2) : SlicerFile.MaximumSpeed;
- using var mat = _outputDummyPixel
- ? SlicerFile.CreateMatWithDummyPixel()
- : SlicerFile.CreateMat();
+ using var mat = _outputDummyPixel
+ ? SlicerFile.CreateMatWithDummyPixel()
+ : SlicerFile.CreateMat();
- var virtualSlowLiftLayer = new Layer(SlicerFile.LayerCount, mat, SlicerFile)
- {
- PositionZ = (float)_raisePositionZ,
- ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.01f,
- LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f,
- LiftSpeed = minLiftSpeed,
- LiftSpeed2 = maxLiftSpeed,
- RetractSpeed = maxRetractSpeed,
- RetractSpeed2 = maxRetractSpeed,
- RetractHeight2 = 0
- };
+ var virtualSlowLiftLayer = new Layer(SlicerFile.LayerCount, mat, SlicerFile)
+ {
+ PositionZ = (float)_raisePositionZ,
+ ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.01f,
+ LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f,
+ LiftSpeed = minLiftSpeed,
+ LiftSpeed2 = maxLiftSpeed,
+ RetractSpeed = maxRetractSpeed,
+ RetractSpeed2 = maxRetractSpeed,
+ RetractHeight2 = 0
+ };
- virtualSlowLiftLayer.SetNoDelays();
+ virtualSlowLiftLayer.SetNoDelays();
- var virtualPhotoLayer = virtualSlowLiftLayer.Clone();
- virtualPhotoLayer.ExposureTime = (float)_exposureTime;
- virtualPhotoLayer.LiftSpeed = maxLiftSpeed;
+ var virtualPhotoLayer = virtualSlowLiftLayer.Clone();
+ virtualPhotoLayer.ExposureTime = (float)_exposureTime;
+ virtualPhotoLayer.LiftSpeed = maxLiftSpeed;
- virtualSlowLiftLayer.LightPWM = 0; // Disable light power if possible
+ virtualSlowLiftLayer.LightPWM = 0; // Disable light power if possible
- var slowLiftHeight = CanUseCustomLift ? (float)_slowLiftHeight : SlicerFile.LiftHeight;
+ var slowLiftHeight = CanUseCustomLift ? (float)_slowLiftHeight : SlicerFile.LiftHeight;
- var layers = SlicerFile.ToList();
- uint insertedLayers = 0;
- foreach (var insertIndex in virtualLayers)
+ var layers = SlicerFile.ToList();
+ uint insertedLayers = 0;
+ foreach (var insertIndex in virtualLayers)
+ {
+ if (insertIndex < LayerIndexEnd)
{
- if (insertIndex < LayerIndexEnd)
+ // Replace lift with retract
+ if (SlicerFile.CanUseLayerRetractHeight2 && SlicerFile[insertIndex].RetractHeight2 > 0)
+ {
+ SlicerFile[insertIndex].LiftHeightTotal = SlicerFile[insertIndex].RetractHeight2;
+ }
+
+ SlicerFile[insertIndex].RetractHeight2 = 0;
+ SlicerFile[insertIndex].LiftSpeed = maxRetractSpeed;
+
+ if (CanUseCustomLift)
{
- // Replace lift with retract
- if (SlicerFile.CanUseLayerRetractHeight2 && SlicerFile[insertIndex].RetractHeight2 > 0)
+ SlicerFile[insertIndex].LiftHeight = (float) _slowRetractHeight;
+ SlicerFile[insertIndex].RetractSpeed = minRetractSpeed;
+
+ if (SlicerFile.CanUseLayerLiftSpeed2)
{
- SlicerFile[insertIndex].LiftHeightTotal = SlicerFile[insertIndex].RetractHeight2;
+ SlicerFile[insertIndex].LiftSpeed2 = maxLiftSpeed;
}
- SlicerFile[insertIndex].RetractHeight2 = 0;
- SlicerFile[insertIndex].LiftSpeed = maxRetractSpeed;
-
- if (CanUseCustomLift)
+ if (SlicerFile.CanUseLayerRetractSpeed2)
{
- SlicerFile[insertIndex].LiftHeight = (float) _slowRetractHeight;
- SlicerFile[insertIndex].RetractSpeed = minRetractSpeed;
-
- if (SlicerFile.CanUseLayerLiftSpeed2)
- {
- SlicerFile[insertIndex].LiftSpeed2 = maxLiftSpeed;
- }
-
- if (SlicerFile.CanUseLayerRetractSpeed2)
- {
- SlicerFile[insertIndex].RetractSpeed2 = maxRetractSpeed;
- }
+ SlicerFile[insertIndex].RetractSpeed2 = maxRetractSpeed;
}
}
+ }
- layers.Insert((int)(insertIndex + insertedLayers), virtualPhotoLayer.Clone());
+ layers.Insert((int)(insertIndex + insertedLayers), virtualPhotoLayer.Clone());
- if (slowLiftHeight > 0 && insertIndex > 0)
+ if (slowLiftHeight > 0 && insertIndex > 0)
+ {
+ virtualSlowLiftLayer.PositionZ = SlicerFile[insertIndex - 1].PositionZ + slowLiftHeight;
+ if (virtualSlowLiftLayer.PositionZ >= virtualPhotoLayer.PositionZ)
{
- virtualSlowLiftLayer.PositionZ = SlicerFile[insertIndex - 1].PositionZ + slowLiftHeight;
- if (virtualSlowLiftLayer.PositionZ >= virtualPhotoLayer.PositionZ)
- {
- // Slow lift layer must be lower than photo layer, break this insertion from now on
- slowLiftHeight = 0;
- virtualPhotoLayer.LiftSpeed = minLiftSpeed;
- continue;
- }
- layers.Insert((int)(insertIndex + insertedLayers), virtualSlowLiftLayer.Clone());
- insertedLayers++;
+ // Slow lift layer must be lower than photo layer, break this insertion from now on
+ slowLiftHeight = 0;
+ virtualPhotoLayer.LiftSpeed = minLiftSpeed;
+ continue;
}
-
+ layers.Insert((int)(insertIndex + insertedLayers), virtualSlowLiftLayer.Clone());
insertedLayers++;
}
- SlicerFile.SuppressRebuildPropertiesWork(() => SlicerFile.LayerManager.Layers = layers.ToArray());
+ insertedLayers++;
}
- return !progress.Token.IsCancellationRequested;
+ SlicerFile.SuppressRebuildPropertiesWork(() => SlicerFile.Layers = layers.ToArray());
}
- #endregion
+ return !progress.Token.IsCancellationRequested;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/PixelEditor/PixelDrainHole.cs b/UVtools.Core/PixelEditor/PixelDrainHole.cs
index 934864d..9d01966 100644
--- a/UVtools.Core/PixelEditor/PixelDrainHole.cs
+++ b/UVtools.Core/PixelEditor/PixelDrainHole.cs
@@ -7,25 +7,24 @@
*/
using System.Drawing;
-namespace UVtools.Core.PixelEditor
+namespace UVtools.Core.PixelEditor;
+
+public class PixelDrainHole : PixelOperation
{
- public class PixelDrainHole : PixelOperation
- {
- private ushort _diameter = 50;
- public override PixelOperationType OperationType => PixelOperationType.DrainHole;
+ private ushort _diameter = 50;
+ public override PixelOperationType OperationType => PixelOperationType.DrainHole;
- public ushort Diameter
- {
- get => _diameter;
- set => RaiseAndSetIfChanged(ref _diameter, value);
- }
+ public ushort Diameter
+ {
+ get => _diameter;
+ set => RaiseAndSetIfChanged(ref _diameter, value);
+ }
- public PixelDrainHole(){ _pixelBrightness = 0; }
+ public PixelDrainHole(){ _pixelBrightness = 0; }
- public PixelDrainHole(uint layerIndex, Point location, ushort diameter) : base(layerIndex, location)
- {
- Diameter = diameter;
- Size = new Size(diameter, diameter);
- }
+ public PixelDrainHole(uint layerIndex, Point location, ushort diameter) : base(layerIndex, location)
+ {
+ Diameter = diameter;
+ Size = new Size(diameter, diameter);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/PixelEditor/PixelDrawing.cs b/UVtools.Core/PixelEditor/PixelDrawing.cs
index 9aa8269..6782a09 100644
--- a/UVtools.Core/PixelEditor/PixelDrawing.cs
+++ b/UVtools.Core/PixelEditor/PixelDrawing.cs
@@ -5,110 +5,109 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV.CvEnum;
using System;
using System.Drawing;
-using Emgu.CV.CvEnum;
-namespace UVtools.Core.PixelEditor
+namespace UVtools.Core.PixelEditor;
+
+public class PixelDrawing : PixelOperation
{
- public class PixelDrawing : PixelOperation
+ private BrushShapeType _brushShape = BrushShapeType.Square;
+ private ushort _brushSize = 1;
+ private short _thickness = -1;
+ private byte _removePixelBrightness;
+ private double _rotationAngle;
+ public const byte MinRectangleBrush = 1;
+ public const byte MinCircleBrush = 7;
+ public enum BrushShapeType : byte
{
- private BrushShapeType _brushShape = BrushShapeType.Square;
- private ushort _brushSize = 1;
- private short _thickness = -1;
- private byte _removePixelBrightness;
- private double _rotationAngle;
- public const byte MinRectangleBrush = 1;
- public const byte MinCircleBrush = 7;
- public enum BrushShapeType : byte
- {
- //Mask = 0,
- Line = 1,
- Triangle = 3,
- Square = 4,
- Pentagon = 5,
- Hexagon = 6,
- Heptagon = 7,
- Octagon = 8,
- Nonagon = 9,
- Decagon = 10,
- Hendecagon = 11,
- Dodecagon = 12,
- Circle = 100,
- }
+ //Mask = 0,
+ Line = 1,
+ Triangle = 3,
+ Square = 4,
+ Pentagon = 5,
+ Hexagon = 6,
+ Heptagon = 7,
+ Octagon = 8,
+ Nonagon = 9,
+ Decagon = 10,
+ Hendecagon = 11,
+ Dodecagon = 12,
+ Circle = 100,
+ }
- public override PixelOperationType OperationType => PixelOperationType.Drawing;
+ public override PixelOperationType OperationType => PixelOperationType.Drawing;
- public static BrushShapeType[] BrushShapeTypes => (BrushShapeType[])Enum.GetValues(typeof(BrushShapeType));
+ public static BrushShapeType[] BrushShapeTypes => (BrushShapeType[])Enum.GetValues(typeof(BrushShapeType));
- public BrushShapeType BrushShape
+ public BrushShapeType BrushShape
+ {
+ get => _brushShape;
+ set
{
- get => _brushShape;
- set
+ if (!RaiseAndSetIfChanged(ref _brushShape, value)) return;
+ if (_brushShape == BrushShapeType.Circle)
{
- if (!RaiseAndSetIfChanged(ref _brushShape, value)) return;
- if (_brushShape == BrushShapeType.Circle)
- {
- BrushSize = Math.Max(MinCircleBrush, BrushSize);
- }
+ BrushSize = Math.Max(MinCircleBrush, BrushSize);
}
}
+ }
- public double RotationAngle
- {
- get => _rotationAngle;
- set => RaiseAndSetIfChanged(ref _rotationAngle, Math.Round(value, 2));
- }
+ public double RotationAngle
+ {
+ get => _rotationAngle;
+ set => RaiseAndSetIfChanged(ref _rotationAngle, Math.Round(value, 2));
+ }
- public ushort BrushSize
- {
- get => _brushSize;
- set => RaiseAndSetIfChanged(ref _brushSize, value);
- }
+ public ushort BrushSize
+ {
+ get => _brushSize;
+ set => RaiseAndSetIfChanged(ref _brushSize, value);
+ }
- public short Thickness
- {
- get => _thickness;
- set => RaiseAndSetIfChanged(ref _thickness, value);
- }
+ public short Thickness
+ {
+ get => _thickness;
+ set => RaiseAndSetIfChanged(ref _thickness, value);
+ }
- public byte RemovePixelBrightness
+ public byte RemovePixelBrightness
+ {
+ get => _removePixelBrightness;
+ set
{
- get => _removePixelBrightness;
- set
- {
- if (!RaiseAndSetIfChanged(ref _removePixelBrightness, value)) return;
- RaisePropertyChanged(nameof(RemovePixelBrightnessPercent));
- }
+ if (!RaiseAndSetIfChanged(ref _removePixelBrightness, value)) return;
+ RaisePropertyChanged(nameof(RemovePixelBrightnessPercent));
}
+ }
- public decimal RemovePixelBrightnessPercent => Math.Round(_removePixelBrightness * 100M / 255M, 2);
+ public decimal RemovePixelBrightnessPercent => Math.Round(_removePixelBrightness * 100M / 255M, 2);
- public bool IsAdd { get; }
+ public bool IsAdd { get; }
- public byte Brightness => IsAdd ? _pixelBrightness : _removePixelBrightness;
+ public byte Brightness => IsAdd ? _pixelBrightness : _removePixelBrightness;
- public Rectangle Rectangle { get; }
+ public Rectangle Rectangle { get; }
- public PixelDrawing()
- {
+ public PixelDrawing()
+ {
- }
+ }
- public PixelDrawing(uint layerIndex, Point location, LineType lineType, BrushShapeType brushShape, double rotationAngle, ushort brushSize, short thickness, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness)
- {
- _brushShape = brushShape;
- _rotationAngle = rotationAngle;
- _brushSize = brushSize;
- _thickness = thickness;
- _removePixelBrightness = removePixelBrightness;
- IsAdd = isAdd;
-
- int shiftPos = brushSize / 2;
- Rectangle = new Rectangle(Math.Max(0, location.X - shiftPos), Math.Max(0, location.Y - shiftPos), brushSize-1, brushSize-1);
- Size = new Size(BrushSize, BrushSize);
- }
+ public PixelDrawing(uint layerIndex, Point location, LineType lineType, BrushShapeType brushShape, double rotationAngle, ushort brushSize, short thickness, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness)
+ {
+ _brushShape = brushShape;
+ _rotationAngle = rotationAngle;
+ _brushSize = brushSize;
+ _thickness = thickness;
+ _removePixelBrightness = removePixelBrightness;
+ IsAdd = isAdd;
+
+ int shiftPos = brushSize / 2;
+ Rectangle = new Rectangle(Math.Max(0, location.X - shiftPos), Math.Max(0, location.Y - shiftPos), brushSize-1, brushSize-1);
+ Size = new Size(BrushSize, BrushSize);
+ }
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/PixelEditor/PixelEraser.cs b/UVtools.Core/PixelEditor/PixelEraser.cs
index 45e9a7f..f45cbd1 100644
--- a/UVtools.Core/PixelEditor/PixelEraser.cs
+++ b/UVtools.Core/PixelEditor/PixelEraser.cs
@@ -5,25 +5,24 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
-using System.Drawing;
using Emgu.CV.CvEnum;
+using System.Drawing;
-namespace UVtools.Core.PixelEditor
+namespace UVtools.Core.PixelEditor;
+
+public class PixelEraser : PixelOperation
{
- public class PixelEraser : PixelOperation
- {
- public const byte Diameter = 4;
+ public const byte Diameter = 4;
- public override PixelOperationType OperationType => PixelOperationType.Eraser;
+ public override PixelOperationType OperationType => PixelOperationType.Eraser;
- public PixelEraser()
- {
- _pixelBrightness = 0;
- }
+ public PixelEraser()
+ {
+ _pixelBrightness = 0;
+ }
- public PixelEraser(uint layerIndex, Point location, byte pixelBrightness) : base(layerIndex, location, LineType.AntiAlias, pixelBrightness)
- {
- Size = new Size(Diameter, Diameter);
- }
+ public PixelEraser(uint layerIndex, Point location, byte pixelBrightness) : base(layerIndex, location, LineType.AntiAlias, pixelBrightness)
+ {
+ Size = new Size(Diameter, Diameter);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/PixelEditor/PixelHistory.cs b/UVtools.Core/PixelEditor/PixelHistory.cs
index 2953d77..a8c1663 100644
--- a/UVtools.Core/PixelEditor/PixelHistory.cs
+++ b/UVtools.Core/PixelEditor/PixelHistory.cs
@@ -6,65 +6,62 @@
* of this license document, but changing it is not allowed.
*/
-using System;
using System.Collections;
using System.Collections.Generic;
-using System.Text;
-namespace UVtools.Core.PixelEditor
+namespace UVtools.Core.PixelEditor;
+
+public class PixelHistory : IEnumerable<PixelOperation>
{
- public class PixelHistory : IEnumerable<PixelOperation>
- {
- public List<PixelOperation> Items { get; } = new List<PixelOperation>();
+ public List<PixelOperation> Items { get; } = new List<PixelOperation>();
- public int Count => Items.Count;
+ public int Count => Items.Count;
- #region Indexers
- public PixelOperation this[uint index] => Items[(int) index];
+ #region Indexers
+ public PixelOperation this[uint index] => Items[(int) index];
- public PixelOperation this[int index] => Items[index];
+ public PixelOperation this[int index] => Items[index];
- public PixelOperation this[long index] => Items[(int) index];
+ public PixelOperation this[long index] => Items[(int) index];
- #endregion
+ #endregion
- #region Numerators
- public IEnumerator<PixelOperation> GetEnumerator()
- {
- return ((IEnumerable<PixelOperation>)Items).GetEnumerator();
- }
+ #region Numerators
+ public IEnumerator<PixelOperation> GetEnumerator()
+ {
+ return ((IEnumerable<PixelOperation>)Items).GetEnumerator();
+ }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- #endregion
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ #endregion
- #region Methods
+ #region Methods
- public void Add(PixelOperation item) => Items.Add(item);
- public void Clear() => Items.Clear();
+ public void Add(PixelOperation item) => Items.Add(item);
+ public void Clear() => Items.Clear();
- public bool Contains(PixelOperation operation)
+ public bool Contains(PixelOperation operation)
+ {
+ for (int i = 0; i < Count; i++)
{
- for (int i = 0; i < Count; i++)
- {
- if (Items[i].Location == operation.Location &&
- Items[i].OperationType == operation.OperationType &&
- Items[i].LayerIndex == operation.LayerIndex) return true;
- }
-
- return false;
+ if (Items[i].Location == operation.Location &&
+ Items[i].OperationType == operation.OperationType &&
+ Items[i].LayerIndex == operation.LayerIndex) return true;
}
- #endregion
+ return false;
+ }
+
+ #endregion
- public void Renumber()
+ public void Renumber()
+ {
+ for (int i = 0; i < Count; i++)
{
- for (int i = 0; i < Count; i++)
- {
- Items[i].Index = (uint) (i + 1);
- }
+ Items[i].Index = (uint) (i + 1);
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/PixelEditor/PixelOperation.cs b/UVtools.Core/PixelEditor/PixelOperation.cs
index dc6a989..a683adf 100644
--- a/UVtools.Core/PixelEditor/PixelOperation.cs
+++ b/UVtools.Core/PixelEditor/PixelOperation.cs
@@ -6,113 +6,112 @@
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV.CvEnum;
using System;
using System.Drawing;
-using Emgu.CV.CvEnum;
using UVtools.Core.Objects;
-namespace UVtools.Core.PixelEditor
+namespace UVtools.Core.PixelEditor;
+
+public abstract class PixelOperation : BindableBase
{
- public abstract class PixelOperation : BindableBase
- {
- private protected uint _index;
- private protected LineType _lineType = LineType.AntiAlias;
- private protected byte _pixelBrightness = 255;
- private protected uint _layersBelow;
- private protected uint _layersAbove;
+ private protected uint _index;
+ private protected LineType _lineType = LineType.AntiAlias;
+ private protected byte _pixelBrightness = 255;
+ private protected uint _layersBelow;
+ private protected uint _layersAbove;
- public enum PixelOperationType : byte
- {
- Drawing,
- Text,
- Eraser,
- Supports,
- DrainHole,
- }
+ public enum PixelOperationType : byte
+ {
+ Drawing,
+ Text,
+ Eraser,
+ Supports,
+ DrainHole,
+ }
- /// <summary>
- /// Gets or sets the index number to show on GUI
- /// </summary>
- public uint Index
- {
- get => _index;
- set => RaiseAndSetIfChanged(ref _index, value);
- }
+ /// <summary>
+ /// Gets or sets the index number to show on GUI
+ /// </summary>
+ public uint Index
+ {
+ get => _index;
+ set => RaiseAndSetIfChanged(ref _index, value);
+ }
- /// <summary>
- /// Gets the <see cref="PixelOperationType"/>
- /// </summary>
- public abstract PixelOperationType OperationType { get; }
-
- /// <summary>
- /// Gets the layer index
- /// </summary>
- public uint LayerIndex { get; }
-
- /// <summary>
- /// Gets the location of the operation
- /// </summary>
- public Point Location { get; }
-
- /// <summary>
- /// Gets the <see cref="LineType"/> for the draw operation
- /// </summary>
- public LineType LineType
- {
- get => _lineType;
- set => RaiseAndSetIfChanged(ref _lineType, value);
- }
+ /// <summary>
+ /// Gets the <see cref="PixelOperationType"/>
+ /// </summary>
+ public abstract PixelOperationType OperationType { get; }
+
+ /// <summary>
+ /// Gets the layer index
+ /// </summary>
+ public uint LayerIndex { get; }
+
+ /// <summary>
+ /// Gets the location of the operation
+ /// </summary>
+ public Point Location { get; }
+
+ /// <summary>
+ /// Gets the <see cref="LineType"/> for the draw operation
+ /// </summary>
+ public LineType LineType
+ {
+ get => _lineType;
+ set => RaiseAndSetIfChanged(ref _lineType, value);
+ }
- public LineType[] LineTypes => new[]
- {
- LineType.FourConnected,
- LineType.EightConnected,
- LineType.AntiAlias
- };
+ public LineType[] LineTypes => new[]
+ {
+ LineType.FourConnected,
+ LineType.EightConnected,
+ LineType.AntiAlias
+ };
- public byte PixelBrightness
+ public byte PixelBrightness
+ {
+ get => _pixelBrightness;
+ set
{
- get => _pixelBrightness;
- set
- {
- if(!RaiseAndSetIfChanged(ref _pixelBrightness, value)) return;
- RaisePropertyChanged(nameof(PixelBrightnessPercent));
- }
+ if(!RaiseAndSetIfChanged(ref _pixelBrightness, value)) return;
+ RaisePropertyChanged(nameof(PixelBrightnessPercent));
}
+ }
- public decimal PixelBrightnessPercent => Math.Round(_pixelBrightness * 100M / 255M, 2);
+ public decimal PixelBrightnessPercent => Math.Round(_pixelBrightness * 100M / 255M, 2);
- public uint LayersBelow
- {
- get => _layersBelow;
- set => RaiseAndSetIfChanged(ref _layersBelow, value);
- }
+ public uint LayersBelow
+ {
+ get => _layersBelow;
+ set => RaiseAndSetIfChanged(ref _layersBelow, value);
+ }
- public uint LayersAbove
- {
- get => _layersAbove;
- set => RaiseAndSetIfChanged(ref _layersAbove, value);
- }
+ public uint LayersAbove
+ {
+ get => _layersAbove;
+ set => RaiseAndSetIfChanged(ref _layersAbove, value);
+ }
- /// <summary>
- /// Gets the total size of the operation
- /// </summary>
- public Size Size { get; private protected set; } = Size.Empty;
+ /// <summary>
+ /// Gets the total size of the operation
+ /// </summary>
+ public Size Size { get; private protected set; } = Size.Empty;
- protected PixelOperation() { }
+ protected PixelOperation() { }
- protected PixelOperation(uint layerIndex, Point location, LineType lineType = LineType.AntiAlias, int pixelBrightness = -1)
- {
- Location = location;
- LayerIndex = layerIndex;
- LineType = lineType;
- if (pixelBrightness > -1)
- _pixelBrightness = (byte) pixelBrightness;
- }
+ protected PixelOperation(uint layerIndex, Point location, LineType lineType = LineType.AntiAlias, int pixelBrightness = -1)
+ {
+ Location = location;
+ LayerIndex = layerIndex;
+ LineType = lineType;
+ if (pixelBrightness > -1)
+ _pixelBrightness = (byte) pixelBrightness;
+ }
- public PixelOperation Clone()
- {
- return (PixelOperation) MemberwiseClone();
- }
+ public PixelOperation Clone()
+ {
+ return (PixelOperation) MemberwiseClone();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/PixelEditor/PixelSupport.cs b/UVtools.Core/PixelEditor/PixelSupport.cs
index bbe3392..61c2baa 100644
--- a/UVtools.Core/PixelEditor/PixelSupport.cs
+++ b/UVtools.Core/PixelEditor/PixelSupport.cs
@@ -5,44 +5,43 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
-using System.Drawing;
using Emgu.CV.CvEnum;
+using System.Drawing;
+
+namespace UVtools.Core.PixelEditor;
-namespace UVtools.Core.PixelEditor
+public class PixelSupport : PixelOperation
{
- public class PixelSupport : PixelOperation
- {
- private byte _tipDiameter = 19;
- private byte _pillarDiameter = 32;
- private byte _baseDiameter = 60;
- public override PixelOperationType OperationType => PixelOperationType.Supports;
+ private byte _tipDiameter = 19;
+ private byte _pillarDiameter = 32;
+ private byte _baseDiameter = 60;
+ public override PixelOperationType OperationType => PixelOperationType.Supports;
- public byte TipDiameter
- {
- get => _tipDiameter;
- set => RaiseAndSetIfChanged(ref _tipDiameter, value);
- }
+ public byte TipDiameter
+ {
+ get => _tipDiameter;
+ set => RaiseAndSetIfChanged(ref _tipDiameter, value);
+ }
- public byte PillarDiameter
- {
- get => _pillarDiameter;
- set => RaiseAndSetIfChanged(ref _pillarDiameter, value);
- }
+ public byte PillarDiameter
+ {
+ get => _pillarDiameter;
+ set => RaiseAndSetIfChanged(ref _pillarDiameter, value);
+ }
- public byte BaseDiameter
- {
- get => _baseDiameter;
- set => RaiseAndSetIfChanged(ref _baseDiameter, value);
- }
+ public byte BaseDiameter
+ {
+ get => _baseDiameter;
+ set => RaiseAndSetIfChanged(ref _baseDiameter, value);
+ }
- public PixelSupport(){}
+ public PixelSupport(){}
- public PixelSupport(uint layerIndex, Point location, byte tipDiameter, byte pillarDiameter, byte baseDiameter, byte pixelBrightness) : base(layerIndex, location, LineType.AntiAlias, pixelBrightness)
- {
- TipDiameter = tipDiameter;
- PillarDiameter = pillarDiameter;
- BaseDiameter = baseDiameter;
- Size = new Size(TipDiameter, TipDiameter);
- }
+ public PixelSupport(uint layerIndex, Point location, byte tipDiameter, byte pillarDiameter, byte baseDiameter, byte pixelBrightness) : base(layerIndex, location, LineType.AntiAlias, pixelBrightness)
+ {
+ TipDiameter = tipDiameter;
+ PillarDiameter = pillarDiameter;
+ BaseDiameter = baseDiameter;
+ Size = new Size(TipDiameter, TipDiameter);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/PixelEditor/PixelText.cs b/UVtools.Core/PixelEditor/PixelText.cs
index f3179dd..ff63d8a 100644
--- a/UVtools.Core/PixelEditor/PixelText.cs
+++ b/UVtools.Core/PixelEditor/PixelText.cs
@@ -5,107 +5,105 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+using Emgu.CV.CvEnum;
using System;
using System.Drawing;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
using UVtools.Core.Extensions;
-namespace UVtools.Core.PixelEditor
+namespace UVtools.Core.PixelEditor;
+
+public class PixelText : PixelOperation
{
- public class PixelText : PixelOperation
+ private FontFace _font;
+ private double _fontScale = 1;
+ private ushort _thickness = 1;
+ private string _text = null!;
+ private bool _mirror;
+ private EmguExtensions.PutTextLineAlignment _lineAlignment = EmguExtensions.PutTextLineAlignment.Left;
+ private double _angle;
+ private byte _removePixelBrightness;
+ public override PixelOperationType OperationType => PixelOperationType.Text;
+
+ public static FontFace[] FontFaces => (FontFace[]) Enum.GetValues(typeof(FontFace));
+
+ public FontFace Font
{
- private FontFace _font;
- private double _fontScale = 1;
- private ushort _thickness = 1;
- private string _text;
- private bool _mirror;
- private EmguExtensions.PutTextLineAlignment _lineAlignment = EmguExtensions.PutTextLineAlignment.Left;
- private double _angle;
- private byte _removePixelBrightness;
- public override PixelOperationType OperationType => PixelOperationType.Text;
-
- public static FontFace[] FontFaces => (FontFace[]) Enum.GetValues(typeof(FontFace));
-
- public FontFace Font
- {
- get => _font;
- set => RaiseAndSetIfChanged(ref _font, value);
- }
+ get => _font;
+ set => RaiseAndSetIfChanged(ref _font, value);
+ }
- public double FontScale
- {
- get => _fontScale;
- set => RaiseAndSetIfChanged(ref _fontScale, Math.Round(value, 2));
- }
+ public double FontScale
+ {
+ get => _fontScale;
+ set => RaiseAndSetIfChanged(ref _fontScale, Math.Round(value, 2));
+ }
- public ushort Thickness
- {
- get => _thickness;
- set => RaiseAndSetIfChanged(ref _thickness, value);
- }
+ public ushort Thickness
+ {
+ get => _thickness;
+ set => RaiseAndSetIfChanged(ref _thickness, value);
+ }
- public string Text
- {
- get => _text;
- set => RaiseAndSetIfChanged(ref _text, value);
- }
+ public string Text
+ {
+ get => _text;
+ set => RaiseAndSetIfChanged(ref _text, value);
+ }
- public bool Mirror
- {
- get => _mirror;
- set => RaiseAndSetIfChanged(ref _mirror, value);
- }
+ public bool Mirror
+ {
+ get => _mirror;
+ set => RaiseAndSetIfChanged(ref _mirror, value);
+ }
- public EmguExtensions.PutTextLineAlignment LineAlignment
- {
- get => _lineAlignment;
- set => RaiseAndSetIfChanged(ref _lineAlignment, value);
- }
+ public EmguExtensions.PutTextLineAlignment LineAlignment
+ {
+ get => _lineAlignment;
+ set => RaiseAndSetIfChanged(ref _lineAlignment, value);
+ }
- public double Angle
- {
- get => _angle;
- set => RaiseAndSetIfChanged(ref _angle, value);
- }
+ public double Angle
+ {
+ get => _angle;
+ set => RaiseAndSetIfChanged(ref _angle, value);
+ }
- public byte RemovePixelBrightness
+ public byte RemovePixelBrightness
+ {
+ get => _removePixelBrightness;
+ set
{
- get => _removePixelBrightness;
- set
- {
- if (!RaiseAndSetIfChanged(ref _removePixelBrightness, value)) return;
- RaisePropertyChanged(nameof(RemovePixelBrightnessPercent));
- }
+ if (!RaiseAndSetIfChanged(ref _removePixelBrightness, value)) return;
+ RaisePropertyChanged(nameof(RemovePixelBrightnessPercent));
}
+ }
- public decimal RemovePixelBrightnessPercent => Math.Round(_removePixelBrightness * 100M / 255M, 2);
+ public decimal RemovePixelBrightnessPercent => Math.Round(_removePixelBrightness * 100M / 255M, 2);
- public bool IsAdd { get; }
+ public bool IsAdd { get; }
- public byte Brightness => IsAdd ? _pixelBrightness : _removePixelBrightness;
+ public byte Brightness => IsAdd ? _pixelBrightness : _removePixelBrightness;
- public Rectangle Rectangle { get; }
+ public Rectangle Rectangle { get; }
- public PixelText(){}
+ public PixelText(){}
- public PixelText(uint layerIndex, Point location, LineType lineType, FontFace font, double fontScale, ushort thickness, string text, bool mirror, EmguExtensions.PutTextLineAlignment lineAlignment, double angle, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness)
- {
- _font = font;
- _fontScale = fontScale;
- _thickness = thickness;
- _text = text;
- _mirror = mirror;
- _lineAlignment = lineAlignment;
- _angle = angle;
- IsAdd = isAdd;
- _removePixelBrightness = removePixelBrightness;
-
- int baseLine = 0;
- Size = EmguExtensions.GetTextSizeExtended(text, font, fontScale, thickness, ref baseLine, lineAlignment);
- Rectangle = new Rectangle(location, Size);
- }
+ public PixelText(uint layerIndex, Point location, LineType lineType, FontFace font, double fontScale, ushort thickness, string text, bool mirror, EmguExtensions.PutTextLineAlignment lineAlignment, double angle, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness)
+ {
+ _font = font;
+ _fontScale = fontScale;
+ _thickness = thickness;
+ _text = text;
+ _mirror = mirror;
+ _lineAlignment = lineAlignment;
+ _angle = angle;
+ IsAdd = isAdd;
+ _removePixelBrightness = removePixelBrightness;
+
+ int baseLine = 0;
+ Size = EmguExtensions.GetTextSizeExtended(text, font, fontScale, thickness, ref baseLine, lineAlignment);
+ Rectangle = new Rectangle(location, Size);
+ }
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptBaseInput.cs b/UVtools.Core/Scripting/ScriptBaseInput.cs
index a7d764c..39c9160 100644
--- a/UVtools.Core/Scripting/ScriptBaseInput.cs
+++ b/UVtools.Core/Scripting/ScriptBaseInput.cs
@@ -6,31 +6,30 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Scripting
-{
- public abstract class ScriptBaseInput
- {
- /// <summary>
- /// Gets the input label
- /// </summary>
- public string Label { get; set; }
+namespace UVtools.Core.Scripting;
- /// <summary>
- /// Gets the hover tooltip for this input
- /// </summary>
- public string ToolTip { get; set; }
+public abstract class ScriptBaseInput
+{
+ /// <summary>
+ /// Gets the input label
+ /// </summary>
+ public string? Label { get; set; }
- /// <summary>
- /// Gets the value representative unit name
- /// </summary>
- public string Unit { get; set; }
- }
+ /// <summary>
+ /// Gets the hover tooltip for this input
+ /// </summary>
+ public string? ToolTip { get; set; }
- public abstract class ScriptBaseInput<T> : ScriptBaseInput
- {
- /// <summary>
- /// Gets or sets the value for this input
- /// </summary>
- public T Value { get; set; }
- }
+ /// <summary>
+ /// Gets the value representative unit name
+ /// </summary>
+ public string? Unit { get; set; }
}
+
+public abstract class ScriptBaseInput<T> : ScriptBaseInput
+{
+ /// <summary>
+ /// Gets or sets the value for this input
+ /// </summary>
+ public T? Value { get; set; }
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptCheckBoxInput.cs b/UVtools.Core/Scripting/ScriptCheckBoxInput.cs
index 551e6f0..38057b9 100644
--- a/UVtools.Core/Scripting/ScriptCheckBoxInput.cs
+++ b/UVtools.Core/Scripting/ScriptCheckBoxInput.cs
@@ -6,9 +6,8 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public class ScriptCheckBoxInput : ScriptBaseInput<bool>
{
- public class ScriptCheckBoxInput : ScriptBaseInput<bool>
- {
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptConfiguration.cs b/UVtools.Core/Scripting/ScriptConfiguration.cs
index 321c49c..437e3a3 100644
--- a/UVtools.Core/Scripting/ScriptConfiguration.cs
+++ b/UVtools.Core/Scripting/ScriptConfiguration.cs
@@ -1,33 +1,38 @@
using System;
using System.Collections.Generic;
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public sealed class ScriptConfiguration
{
- public sealed class ScriptConfiguration
- {
- /// <summary>
- /// Gets the script name
- /// </summary>
- public string Name { get; set; } = "Unnamed script";
+ /// <summary>
+ /// Gets the script name
+ /// </summary>
+ public string Name { get; set; } = "Unnamed script";
+
+ /// <summary>
+ /// Gets the script description of what it does
+ /// </summary>
+ public string Description { get; set; } = "I don't know my purpose, do not run me!";
- /// <summary>
- /// Gets the script description of what it does
- /// </summary>
- public string Description { get; set; } = "I don't know my purpose, do not run me!";
+ /// <summary>
+ /// Gets the script author name
+ /// </summary>
+ public string Author { get; set; } = "Undefined";
- /// <summary>
- /// Gets the script author name
- /// </summary>
- public string Author { get; set; } = "Undefined";
+ /// <summary>
+ /// Gets the script version
+ /// </summary>
+ public Version Version { get; set; } = new(0, 1);
- /// <summary>
- /// Gets the script version
- /// </summary>
- public Version Version { get; set; } = new(0, 1);
+ /// <summary>
+ /// Gets the minimum version able to run this script
+ /// Scripts were introduced on v2.8
+ /// </summary>
+ public Version MinimumVersionToRun { get; set; } = new(2, 8, 0);
- /// <summary>
- /// List of user inputs to show on GUI for configuration of the script
- /// </summary>
- public List<ScriptBaseInput> UserInputs { get; } = new();
- }
-}
+ /// <summary>
+ /// List of user inputs to show on GUI for configuration of the script
+ /// </summary>
+ public List<ScriptBaseInput> UserInputs { get; } = new();
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptFileDialogInput.cs b/UVtools.Core/Scripting/ScriptFileDialogInput.cs
index a8b4aef..3ebfb2c 100644
--- a/UVtools.Core/Scripting/ScriptFileDialogInput.cs
+++ b/UVtools.Core/Scripting/ScriptFileDialogInput.cs
@@ -8,34 +8,33 @@
using System.Collections.Generic;
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public abstract class ScriptFileDialogInput : ScriptBaseInput<string>
{
- public abstract class ScriptFileDialogInput : ScriptBaseInput<string>
+ public class ScriptFileDialogFilter
{
- public class ScriptFileDialogFilter
- {
- public string Name { get; set; }
- public List<string> Extensions { get; set; } = new();
- }
+ public string? Name { get; set; }
+ public List<string> Extensions { get; set; } = new();
+ }
- /// <summary>
- /// Gets or sets the title for the dialog
- /// </summary>
- public string Title { get; set; }
+ /// <summary>
+ /// Gets or sets the title for the dialog
+ /// </summary>
+ public string? Title { get; set; }
- /// <summary>
- /// Gets or sets the default directory to open the dialog in
- /// </summary>
- public string Directory { get; set; }
+ /// <summary>
+ /// Gets or sets the default directory to open the dialog in
+ /// </summary>
+ public string? Directory { get; set; }
- /// <summary>
- /// Gets or sets the initial filename to be on the dialog
- /// </summary>
- public string InitialFilename { get; set; }
+ /// <summary>
+ /// Gets or sets the initial filename to be on the dialog
+ /// </summary>
+ public string? InitialFilename { get; set; }
- /// <summary>
- /// Gets or sets the file filters on the dropdown list
- /// </summary>
- public List<ScriptFileDialogFilter> Filters { get; set; }
- }
-}
+ /// <summary>
+ /// Gets or sets the file filters on the dropdown list
+ /// </summary>
+ public List<ScriptFileDialogFilter>? Filters { get; set; }
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptGlobals.cs b/UVtools.Core/Scripting/ScriptGlobals.cs
index 15bd062..4b7f0ff 100644
--- a/UVtools.Core/Scripting/ScriptGlobals.cs
+++ b/UVtools.Core/Scripting/ScriptGlobals.cs
@@ -8,28 +8,27 @@
using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public class ScriptGlobals
{
- public class ScriptGlobals
- {
- /// <summary>
- /// Gets the loaded slicer file
- /// </summary>
- public FileFormat SlicerFile { get; init; }
+ /// <summary>
+ /// Gets the loaded slicer file
+ /// </summary>
+ public FileFormat SlicerFile { get; init; } = null!;
- /// <summary>
- /// Gets the progress operation for loading bar
- /// </summary>
- public OperationProgress Progress { get; set; } = new("Unknown");
+ /// <summary>
+ /// Gets the progress operation for loading bar
+ /// </summary>
+ public OperationProgress Progress { get; set; } = new("Unknown");
- /// <summary>
- /// Gets the current operation holding the layer range, mask, roi, etc
- /// </summary>
- public Operation Operation { get; init; }
+ /// <summary>
+ /// Gets the current operation holding the layer range, mask, roi, etc
+ /// </summary>
+ public Operation Operation { get; init; } = null!;
- /// <summary>
- /// Gets the script configuration
- /// </summary>
- public ScriptConfiguration Script { get; } = new();
- }
-}
+ /// <summary>
+ /// Gets the script configuration
+ /// </summary>
+ public ScriptConfiguration Script { get; } = new();
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptNumericalInput.cs b/UVtools.Core/Scripting/ScriptNumericalInput.cs
index 8ce1a04..1b7373d 100644
--- a/UVtools.Core/Scripting/ScriptNumericalInput.cs
+++ b/UVtools.Core/Scripting/ScriptNumericalInput.cs
@@ -6,30 +6,27 @@
* of this license document, but changing it is not allowed.
*/
-using System;
+namespace UVtools.Core.Scripting;
-namespace UVtools.Core.Scripting
+public class ScriptNumericalInput<T> : ScriptBaseInput<T>
{
- public class ScriptNumericalInput<T> : ScriptBaseInput<T>
- {
- /// <summary>
- /// Gets the minimum for this input
- /// </summary>
- public T Minimum { get; set; }
+ /// <summary>
+ /// Gets the minimum for this input
+ /// </summary>
+ public T? Minimum { get; set; }
- /// <summary>
- /// Gets the minimum for this input
- /// </summary>
- public T Maximum { get; set; }
+ /// <summary>
+ /// Gets the minimum for this input
+ /// </summary>
+ public T? Maximum { get; set; }
- /// <summary>
- /// Gets the increment value for this
- /// </summary>
- public T Increment { get; set; }
+ /// <summary>
+ /// Gets the increment value for this
+ /// </summary>
+ public T? Increment { get; set; }
- /// <summary>
- /// Gets the number of decimal plates to round the value
- /// </summary>
- public byte DecimalPlates { get; set; } = 2;
- }
-}
+ /// <summary>
+ /// Gets the number of decimal plates to round the value
+ /// </summary>
+ public byte DecimalPlates { get; set; } = 2;
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptOpenFileDialogInput.cs b/UVtools.Core/Scripting/ScriptOpenFileDialogInput.cs
index 209b0c5..2374aab 100644
--- a/UVtools.Core/Scripting/ScriptOpenFileDialogInput.cs
+++ b/UVtools.Core/Scripting/ScriptOpenFileDialogInput.cs
@@ -6,18 +6,17 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public class ScriptOpenFileDialogInput : ScriptFileDialogInput
{
- public class ScriptOpenFileDialogInput : ScriptFileDialogInput
- {
- /// <summary>
- /// Gets or sets if allow multiple file selection
- /// </summary>
- public bool AllowMultiple { get; set; }
+ /// <summary>
+ /// Gets or sets if allow multiple file selection
+ /// </summary>
+ public bool AllowMultiple { get; set; }
- /// <summary>
- /// Gets or sets the selected files
- /// </summary>
- public string[] Files {get; set; }
- }
-}
+ /// <summary>
+ /// Gets or sets the selected files
+ /// </summary>
+ public string[]? Files {get; set; }
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptOpenFolderDialogInput.cs b/UVtools.Core/Scripting/ScriptOpenFolderDialogInput.cs
index 5baa2f1..2263229 100644
--- a/UVtools.Core/Scripting/ScriptOpenFolderDialogInput.cs
+++ b/UVtools.Core/Scripting/ScriptOpenFolderDialogInput.cs
@@ -6,13 +6,12 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public class ScriptOpenFolderDialogInput : ScriptBaseInput<string>
{
- public class ScriptOpenFolderDialogInput : ScriptBaseInput<string>
- {
- /// <summary>
- /// Gets the title for the dialog
- /// </summary>
- public string Title { get; set; }
- }
-}
+ /// <summary>
+ /// Gets the title for the dialog
+ /// </summary>
+ public string? Title { get; set; }
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptParser.cs b/UVtools.Core/Scripting/ScriptParser.cs
index b7c4923..2223487 100644
--- a/UVtools.Core/Scripting/ScriptParser.cs
+++ b/UVtools.Core/Scripting/ScriptParser.cs
@@ -10,51 +10,52 @@ using System;
using System.IO;
using System.Text.RegularExpressions;
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public static class ScriptParser
{
- public static class ScriptParser
+ public static string ParseScriptFromFile(string path)
+ {
+ return ParseScriptFromText(File.ReadAllText(path));
+ }
+
+ /// <summary>
+ /// Parse the script and clean forbidden keywords
+ /// </summary>
+ /// <param name="text">Text to parse</param>
+ /// <returns>The parsed text</returns>
+ public static string ParseScriptFromText(string text)
{
- public static string ParseScriptFromFile(string path)
+ if(!Regex.Match(text, @"(void\s+ScriptInit\s*\(\s*\))").Success)
+ {
+ throw new ArgumentException("The method \"void ScriptInit()\" was not found on script, please verify the script.");
+ }
+ if (!Regex.Match(text, @"(string\s*[?]?\s+ScriptValidate\s*\(\s*\))").Success)
{
- return ParseScriptFromText(File.ReadAllText(path));
+ throw new ArgumentException("The method \"string ScriptValidate()\" was not found on script, please verify the script.");
}
+ if (!Regex.Match(text, @"(bool\s+ScriptExecute\s*\(\s*\))").Success)
+ {
+ throw new ArgumentException("The method \"bool ScriptExecute()\" was not found on script, please verify the script.");
+ }
+
+ var textLength = text.Length;
+ sbyte bracketsToRemove = 0;
+ text = Regex.Replace(text, @"(namespace\s+.+\n*.*{)", string.Empty);
+ if (textLength != text.Length) bracketsToRemove++;
+ else text = Regex.Replace(text, @"(namespace\s+.+\n*.*;)", string.Empty); // NET 6.0
- /// <summary>
- /// Parse the script and clean forbidden keywords
- /// </summary>
- /// <param name="text">Text to parse</param>
- /// <returns>The parsed text</returns>
- public static string ParseScriptFromText(string text)
+ textLength = text.Length;
+ text = Regex.Replace(text, @"(.*class\s+.*\n*.*{)", string.Empty);
+ if (textLength != text.Length) bracketsToRemove++;
+
+ if (bracketsToRemove <= 0) return text;
+
+ for (textLength = text.Length - 1; textLength >= 0 && bracketsToRemove > 0; textLength--)
{
- if(!Regex.Match(text, @"(void\s*ScriptInit\s*\(\s*\))").Success)
- {
- throw new ArgumentException("The method \"void ScriptInit()\" was not found on script, please verify the script.");
- }
- if (!Regex.Match(text, @"(string\s*ScriptValidate\s*\(\s*\))").Success)
- {
- throw new ArgumentException("The method \"string ScriptValidate()\" was not found on script, please verify the script.");
- }
- if (!Regex.Match(text, @"(bool\s*ScriptExecute\s*\(\s*\))").Success)
- {
- throw new ArgumentException("The method \"bool ScriptExecute()\" was not found on script, please verify the script.");
- }
-
- var textLength = text.Length;
- sbyte bracketsToRemove = 0;
- text = Regex.Replace(text, @"(namespace .*\n*.*{)", string.Empty);
- if (textLength != text.Length) bracketsToRemove++;
- textLength = text.Length;
- text = Regex.Replace(text, "(.*class .*\n*.*{)", string.Empty);
- if (textLength != text.Length) bracketsToRemove++;
-
- if (bracketsToRemove <= 0) return text;
-
- for (textLength = text.Length - 1; textLength >= 0 && bracketsToRemove > 0; textLength--)
- {
- if (text[textLength] == '}') bracketsToRemove--;
- }
-
- return text.Substring(0, textLength);
+ if (text[textLength] == '}') bracketsToRemove--;
}
+
+ return text[..textLength];
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptSaveFileDialogInput.cs b/UVtools.Core/Scripting/ScriptSaveFileDialogInput.cs
index e2d64c8..7ddd698 100644
--- a/UVtools.Core/Scripting/ScriptSaveFileDialogInput.cs
+++ b/UVtools.Core/Scripting/ScriptSaveFileDialogInput.cs
@@ -6,13 +6,12 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public class ScriptSaveFileDialogInput : ScriptFileDialogInput
{
- public class ScriptSaveFileDialogInput : ScriptFileDialogInput
- {
- /// <summary>
- /// Gets or sets the default extension for the dialog
- /// </summary>
- public string DefaultExtension { get; set; }
- }
-}
+ /// <summary>
+ /// Gets or sets the default extension for the dialog
+ /// </summary>
+ public string? DefaultExtension { get; set; }
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptTextBoxInput.cs b/UVtools.Core/Scripting/ScriptTextBoxInput.cs
index ae84e8d..429c08a 100644
--- a/UVtools.Core/Scripting/ScriptTextBoxInput.cs
+++ b/UVtools.Core/Scripting/ScriptTextBoxInput.cs
@@ -6,13 +6,12 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public class ScriptTextBoxInput : ScriptBaseInput<string>
{
- public class ScriptTextBoxInput : ScriptBaseInput<string>
- {
- /// <summary>
- /// Gets if this input accepts multi lines
- /// </summary>
- public bool MultiLine { get; set; }
- }
-}
+ /// <summary>
+ /// Gets if this input accepts multi lines
+ /// </summary>
+ public bool MultiLine { get; set; }
+} \ No newline at end of file
diff --git a/UVtools.Core/Scripting/ScriptToggleSwitchInput.cs b/UVtools.Core/Scripting/ScriptToggleSwitchInput.cs
index b7968e9..b1fb0b2 100644
--- a/UVtools.Core/Scripting/ScriptToggleSwitchInput.cs
+++ b/UVtools.Core/Scripting/ScriptToggleSwitchInput.cs
@@ -6,18 +6,17 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Scripting
+namespace UVtools.Core.Scripting;
+
+public class ScriptToggleSwitchInput : ScriptBaseInput<bool>
{
- public class ScriptToggleSwitchInput : ScriptBaseInput<bool>
- {
- /// <summary>
- /// Gets or sets the text when the switch is turned off
- /// </summary>
- public string OffText { get; set; }
+ /// <summary>
+ /// Gets or sets the text when the switch is turned off
+ /// </summary>
+ public string OffText { get; set; } = "Off";
- /// <summary>
- /// Gets or sets the text when the switch is turned on
- /// </summary>
- public string OnText { get; set; }
- }
-}
+ /// <summary>
+ /// Gets or sets the text when the switch is turned on
+ /// </summary>
+ public string OnText { get; set; } = "On";
+} \ No newline at end of file
diff --git a/UVtools.Core/Slicer/LinAlgUtils.cs b/UVtools.Core/Slicer/LinAlgUtils.cs
index 58961cc..2e764c7 100644
--- a/UVtools.Core/Slicer/LinAlgUtils.cs
+++ b/UVtools.Core/Slicer/LinAlgUtils.cs
@@ -5,96 +5,91 @@
* 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.Drawing;
-using System.Numerics;
-namespace UVtools.Core.Slicer
+namespace UVtools.Core.Slicer;
+
+public static class LinAlgUtils
{
- public static class LinAlgUtils
+ /*#region Find Facets
+ public static List<Facet> FindFacetsIntersectingZIndex(STLDocument stl, float z)
{
- /*#region Find Facets
- public static List<Facet> FindFacetsIntersectingZIndex(STLDocument stl, float z)
- {
- return stl.Facets.Where(f => f.Vertices.Any(v => v.Z <= z) && f.Vertices.Any(v => v.Z >= z) && Math.Abs(f.Normal.Z) != 1).ToList();
- }
+ return stl.Facets.Where(f => f.Vertices.Any(v => v.Z <= z) && f.Vertices.Any(v => v.Z >= z) && Math.Abs(f.Normal.Z) != 1).ToList();
+ }
- public static List<Facet> FindFlatFacetsAtZIndex(STLDocument stl, float z)
- {
- return stl.Facets.Where(f => f.Vertices.All(v => v.Z == z)).ToList();
- }
+ public static List<Facet> FindFlatFacetsAtZIndex(STLDocument stl, float z)
+ {
+ return stl.Facets.Where(f => f.Vertices.All(v => v.Z == z)).ToList();
+ }
- public static List<Facet> FindTopFlatFacetsAtZIndex(STLDocument stl, float z)
+ public static List<Facet> FindTopFlatFacetsAtZIndex(STLDocument stl, float z)
+ {
+ return stl.Facets.Where(f => f.Vertices.Any(v => v.Z == z) && f.Normal.Z == 1).ToList();
+ }
+ public static List<Facet> FindBottomFlatFacetsAtZIndex(STLDocument stl, float z)
+ {
+ return stl.Facets.Where(f => f.Vertices.Any(v => v.Z == z) && f.Normal.Z == -1).ToList();
+ }
+ #endregion
+
+ public static SliceLine CreateLineFromFacetAtZIndex(Facet facet, float z)
+ {
+ // This won't work for flat horizontal plane vertices, so throw if that's what we've got
+ if (Math.Abs(facet.Normal.Z) == 1)
+ throw new Exception("Cannot create lines for flat horizontal planes");
+ var verts = facet.Vertices;
+ var returnLine = new SliceLine();
+ // If any vertices are ON the z-index, just return that line
+ returnLine.AddRange(verts.Where(v => v.Z == z).Select(v => new PointF(v.X, v.Y)));
+ // For uniformity, I'm representing a point as a "line" between two of the same point
+ // It's janky, I'll refactor later
+ if (returnLine.Count == 1)
{
- return stl.Facets.Where(f => f.Vertices.Any(v => v.Z == z) && f.Normal.Z == 1).ToList();
+ returnLine.Add(returnLine[0]);
}
- public static List<Facet> FindBottomFlatFacetsAtZIndex(STLDocument stl, float z)
+ if (returnLine.Count == 2)
{
- return stl.Facets.Where(f => f.Vertices.Any(v => v.Z == z) && f.Normal.Z == -1).ToList();
+ returnLine.Normal = new PointF(facet.Normal.X, facet.Normal.Y);
+ return returnLine;
}
- #endregion
+ // If no vertices are ON the z-index, find the two points where they CROSS it
+ if ((verts[0].Z - z) / (verts[1].Z - z) < 0)
+ returnLine.Add(CalculateZIntercept(verts[0], verts[1], z));
+ if ((verts[2].Z - z) / (verts[1].Z - z) < 0)
+ returnLine.Add(CalculateZIntercept(verts[2], verts[1], z));
- public static SliceLine CreateLineFromFacetAtZIndex(Facet facet, float z)
- {
- // This won't work for flat horizontal plane vertices, so throw if that's what we've got
- if (Math.Abs(facet.Normal.Z) == 1)
- throw new Exception("Cannot create lines for flat horizontal planes");
- var verts = facet.Vertices;
- var returnLine = new SliceLine();
- // If any vertices are ON the z-index, just return that line
- returnLine.AddRange(verts.Where(v => v.Z == z).Select(v => new PointF(v.X, v.Y)));
- // For uniformity, I'm representing a point as a "line" between two of the same point
- // It's janky, I'll refactor later
- if (returnLine.Count == 1)
- {
- returnLine.Add(returnLine[0]);
- }
- if (returnLine.Count == 2)
- {
- returnLine.Normal = new PointF(facet.Normal.X, facet.Normal.Y);
- return returnLine;
- }
- // If no vertices are ON the z-index, find the two points where they CROSS it
- if ((verts[0].Z - z) / (verts[1].Z - z) < 0)
- returnLine.Add(CalculateZIntercept(verts[0], verts[1], z));
- if ((verts[2].Z - z) / (verts[1].Z - z) < 0)
- returnLine.Add(CalculateZIntercept(verts[2], verts[1], z));
+ if (returnLine.Count < 2)
+ returnLine.Add(CalculateZIntercept(verts[0], verts[2], z));
- if (returnLine.Count < 2)
- returnLine.Add(CalculateZIntercept(verts[0], verts[2], z));
+ returnLine.Normal = new PointF(facet.Normal.X, facet.Normal.Y);
- returnLine.Normal = new PointF(facet.Normal.X, facet.Normal.Y);
-
- if (!returnLine.Validate())
- throw new InvalidOperationException("Invalid Point Data");
-
- return returnLine;
- }
+ if (!returnLine.Validate())
+ throw new InvalidOperationException("Invalid Point Data");
- public static PointF CalculateZIntercept(Vector3 v1, Vector3 v2, float z)
- {
- var returnX = CalculateDimensionalValueAtIndex(new PointF(v1.Z, v1.X), new PointF(v2.Z, v2.X), z);
- var returnY = CalculateDimensionalValueAtIndex(new PointF(v1.Z, v1.Y), new PointF(v2.Z, v2.Y), z);
- return new PointF
- {
- X = returnX,
- Y = returnY
- };
- }
+ return returnLine;
+ }
- public static float CalculateDimensionalValueAtIndex(PointF p1, PointF p2, float z, byte precision = 4)
+ public static PointF CalculateZIntercept(Vector3 v1, Vector3 v2, float z)
+ {
+ var returnX = CalculateDimensionalValueAtIndex(new PointF(v1.Z, v1.X), new PointF(v2.Z, v2.X), z);
+ var returnY = CalculateDimensionalValueAtIndex(new PointF(v1.Z, v1.Y), new PointF(v2.Z, v2.Y), z);
+ return new PointF
{
- var slope = (p1.Y - p2.Y) / (p1.X - p2.X);
- var intercept = p1.Y - (slope * p1.X);
- var rawVal = slope * z + intercept;
- return (float)Math.Round(rawVal, precision);
- // using floats we end up with some infinitesimal rounding errors,
- // so we need to set the precision to something reasonable. Default is 1/100th of a micron
- // I'm sure there's a better way than converting it to a string and then back to a float,
- // but that's what I've got right now, so that's what I'm doing.
- //var strVal = rawVal.ToString($"0.{precision}");
- //return float.Parse(strVal, CultureInfo.InvariantCulture.NumberFormat);
- }*/
+ X = returnX,
+ Y = returnY
+ };
}
-}
+
+ public static float CalculateDimensionalValueAtIndex(PointF p1, PointF p2, float z, byte precision = 4)
+ {
+ var slope = (p1.Y - p2.Y) / (p1.X - p2.X);
+ var intercept = p1.Y - (slope * p1.X);
+ var rawVal = slope * z + intercept;
+ return (float)Math.Round(rawVal, precision);
+ // using floats we end up with some infinitesimal rounding errors,
+ // so we need to set the precision to something reasonable. Default is 1/100th of a micron
+ // I'm sure there's a better way than converting it to a string and then back to a float,
+ // but that's what I've got right now, so that's what I'm doing.
+ //var strVal = rawVal.ToString($"0.{precision}");
+ //return float.Parse(strVal, CultureInfo.InvariantCulture.NumberFormat);
+ }*/
+} \ No newline at end of file
diff --git a/UVtools.Core/Slicer/RangeUtils.cs b/UVtools.Core/Slicer/RangeUtils.cs
index baeb9f4..5371a2a 100644
--- a/UVtools.Core/Slicer/RangeUtils.cs
+++ b/UVtools.Core/Slicer/RangeUtils.cs
@@ -6,71 +6,70 @@
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.Core.Slicer
+namespace UVtools.Core.Slicer;
+
+public static class RangeUtils
{
- public static class RangeUtils
+ /*public static float CalculateHighX(STLDocument stl)
{
- /*public static float CalculateHighX(STLDocument stl)
+ float highestX = stl.Facets[0].Vertices[0].X;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
{
- float highestX = stl.Facets[0].Vertices[0].X;
- stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
- {
- if (v.X > highestX)
- highestX = v.X;
- }));
- return highestX;
- }
- public static float CalculateLowX(STLDocument stl)
+ if (v.X > highestX)
+ highestX = v.X;
+ }));
+ return highestX;
+ }
+ public static float CalculateLowX(STLDocument stl)
+ {
+ float lowestX = stl.Facets[0].Vertices[0].X;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
{
- float lowestX = stl.Facets[0].Vertices[0].X;
- stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
- {
- if (v.X < lowestX)
- lowestX = v.X;
- }));
- return lowestX;
- }
+ if (v.X < lowestX)
+ lowestX = v.X;
+ }));
+ return lowestX;
+ }
- public static float CalculateHighY(STLDocument stl)
+ public static float CalculateHighY(STLDocument stl)
+ {
+ float highestY = stl.Facets[0].Vertices[0].Y;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
{
- float highestY = stl.Facets[0].Vertices[0].Y;
- stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
- {
- if (v.Y > highestY)
- highestY = v.Y;
- }));
- return highestY;
- }
- public static float CalculateLowY(STLDocument stl)
+ if (v.Y > highestY)
+ highestY = v.Y;
+ }));
+ return highestY;
+ }
+ public static float CalculateLowY(STLDocument stl)
+ {
+ float lowestY = stl.Facets[0].Vertices[0].Y;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
{
- float lowestY = stl.Facets[0].Vertices[0].Y;
- stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
- {
- if (v.Y < lowestY)
- lowestY = v.Y;
- }));
- return lowestY;
- }
+ if (v.Y < lowestY)
+ lowestY = v.Y;
+ }));
+ return lowestY;
+ }
- public static float CalculateHighZ(STLDocument stl)
- {
- float highestZ = stl.Facets[0].Vertices[0].Z;
- stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
- {
- if (v.Z > highestZ)
- highestZ = v.Z;
- }));
- return highestZ;
- }
- public static float CalculateLowZ(STLDocument stl)
+ public static float CalculateHighZ(STLDocument stl)
+ {
+ float highestZ = stl.Facets[0].Vertices[0].Z;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
{
- float lowestZ = stl.Facets[0].Vertices[0].Z;
- stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
- {
- if (v.Z < lowestZ)
- lowestZ = v.Z;
- }));
- return lowestZ;
- }*/
+ if (v.Z > highestZ)
+ highestZ = v.Z;
+ }));
+ return highestZ;
}
-}
+ public static float CalculateLowZ(STLDocument stl)
+ {
+ float lowestZ = stl.Facets[0].Vertices[0].Z;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
+ {
+ if (v.Z < lowestZ)
+ lowestZ = v.Z;
+ }));
+ return lowestZ;
+ }*/
+} \ No newline at end of file
diff --git a/UVtools.Core/Slicer/Slice.cs b/UVtools.Core/Slicer/Slice.cs
index 50ee130..3d853c2 100644
--- a/UVtools.Core/Slicer/Slice.cs
+++ b/UVtools.Core/Slicer/Slice.cs
@@ -10,47 +10,46 @@ using System.Drawing;
using System.Linq;
using UVtools.Core.Extensions;
-namespace UVtools.Core.Slicer
+namespace UVtools.Core.Slicer;
+
+public class Slice : List<SliceLine>
{
- public class Slice : List<SliceLine>
+ public bool ValidateShapeIntegrity()
{
- public bool ValidateShapeIntegrity()
+ // A shape has to have at least three lines to close
+ if (Count < 3)
+ return false;
+
+ var allPoints = this.ToList();
+ var pointFreq = new Dictionary<SliceLine, int>();
+
+ // There can't be any hanging lines. Verify every point connects to at least one other line.
+ foreach (var p in allPoints)
{
- // A shape has to have at least three lines to close
- if (Count < 3)
- return false;
+ if (!pointFreq.ContainsKey(p))
+ pointFreq[p] = 1;
+ else
+ pointFreq[p]++;
+ }
- var allPoints = this.ToList();
- var pointFreq = new Dictionary<SliceLine, int>();
-
- // There can't be any hanging lines. Verify every point connects to at least one other line.
- foreach (var p in allPoints)
- {
- if (!pointFreq.ContainsKey(p))
- pointFreq[p] = 1;
- else
- pointFreq[p]++;
- }
-
- foreach (var p in pointFreq.Keys)
- {
- if (pointFreq[p] < 2)
- return false;
- }
-
- return true;
+ foreach (var p in pointFreq.Keys)
+ {
+ if (pointFreq[p] < 2)
+ return false;
}
- public Point[] ToContour()
+ return true;
+ }
+
+ public Point[] ToContour()
+ {
+ var points = new Point[Count*2];
+ for (var i = 0; i < Count; i++)
{
- var points = new Point[Count*2];
- for (var i = 0; i < Count; i++)
- {
- points[i] = this[i][0].ToPoint();
- points[i+1] = this[i][1].ToPoint();
- }
-
- return points;
+ points[i] = this[i][0].ToPoint();
+ points[i+1] = this[i][1].ToPoint();
}
+
+ return points;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Slicer/SliceLine.cs b/UVtools.Core/Slicer/SliceLine.cs
index 1d82a7a..3345f9a 100644
--- a/UVtools.Core/Slicer/SliceLine.cs
+++ b/UVtools.Core/Slicer/SliceLine.cs
@@ -9,53 +9,52 @@ using System;
using System.Collections.Generic;
using System.Drawing;
-namespace UVtools.Core.Slicer
+namespace UVtools.Core.Slicer;
+
+public class SliceLine : List<PointF>
{
- public class SliceLine : List<PointF>
+ private PointF _normal;
+
+ public PointF Normal
+ {
+ get => _normal;
+ set => _normal = CalculateNormal(value);
+ }
+
+ public PointF CalculateNormal(PointF normal = default)
+ {
+ if (!Validate())
+ throw new InvalidOperationException("Can't calculate Normal without points set");
+
+ if (normal == PointF.Empty)
+ normal = _normal;
+ if (normal == PointF.Empty)
+ throw new InvalidOperationException("Can't calculate Normal without a starting point");
+
+ // calculate normal slopes
+ var dX = this[0].Y - this[1].Y;
+ var dY = this[0].X - this[1].X;
+ // determine normal direction
+ var xDir = normal.X >= 0 ? 1 : -1;
+ var yDir = normal.Y >= 0 ? 1 : -1;
+ // check for delta 0 in either direction
+ if (dX == 0 && dY == 0)
+ return new PointF(0, 0);
+ if (dX == 0)
+ return new PointF(0, yDir);
+ if (dY == 0)
+ return new PointF(xDir, 0);
+
+ // if there aren't any zeroes, calculate the hypotenuse
+ var hypotenuse = (float)Math.Sqrt((dX * dX) + (dY * dY));
+ // use cross-multiplication and solve for x & y to find normal values where hypotenuse == 1
+ dX = Math.Abs(dX / hypotenuse) * xDir;
+ dY = Math.Abs(dY / hypotenuse) * yDir;
+ return new PointF(dX, dY);
+ }
+
+ public bool Validate()
{
- private PointF _normal;
-
- public PointF Normal
- {
- get => _normal;
- set => _normal = CalculateNormal(value);
- }
-
- public PointF CalculateNormal(PointF normal = default)
- {
- if (!Validate())
- throw new InvalidOperationException("Can't calculate Normal without points set");
-
- if (normal == PointF.Empty)
- normal = _normal;
- if (normal == PointF.Empty)
- throw new InvalidOperationException("Can't calculate Normal without a starting point");
-
- // calculate normal slopes
- var dX = this[0].Y - this[1].Y;
- var dY = this[0].X - this[1].X;
- // determine normal direction
- var xDir = normal.X >= 0 ? 1 : -1;
- var yDir = normal.Y >= 0 ? 1 : -1;
- // check for delta 0 in either direction
- if (dX == 0 && dY == 0)
- return new PointF(0, 0);
- if (dX == 0)
- return new PointF(0, yDir);
- if (dY == 0)
- return new PointF(xDir, 0);
-
- // if there aren't any zeroes, calculate the hypotenuse
- var hypotenuse = (float)Math.Sqrt((dX * dX) + (dY * dY));
- // use cross-multiplication and solve for x & y to find normal values where hypotenuse == 1
- dX = Math.Abs(dX / hypotenuse) * xDir;
- dY = Math.Abs(dY / hypotenuse) * yDir;
- return new PointF(dX, dY);
- }
-
- public bool Validate()
- {
- return Count == 2;
- }
+ return Count == 2;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/Slicer/Slicer.cs b/UVtools.Core/Slicer/Slicer.cs
index a91e260..aa6727a 100644
--- a/UVtools.Core/Slicer/Slicer.cs
+++ b/UVtools.Core/Slicer/Slicer.cs
@@ -10,202 +10,201 @@ using System;
using System.Collections.Generic;
using System.Drawing;
-namespace UVtools.Core.Slicer
+namespace UVtools.Core.Slicer;
+
+public class Slicer
{
- public class Slicer
+ /*private float _lowX = float.NaN;
+ private float _highX = float.NaN;
+ private float _lowY = float.NaN;
+ private float _highY = float.NaN;
+ private float _lowZ = float.NaN;
+ private float _highZ = float.NaN;*/
+ protected Dictionary<float, Slice> _slices = new ();
+
+
+ /// <summary>
+ /// Gets the size of resolution
+ /// </summary>
+ public Size Resolution { get; private set; }
+
+ /// <summary>
+ /// Gets the size of display
+ /// </summary>
+ public SizeF Display { get; private set; }
+
+ /// <summary>
+ /// Gets the pixels per millimeters
+ /// </summary>
+ public SizeF Ppmm { get; private set; }
+
+ /*public float LowX
{
- private float _lowX = float.NaN;
- private float _highX = float.NaN;
- private float _lowY = float.NaN;
- private float _highY = float.NaN;
- private float _lowZ = float.NaN;
- private float _highZ = float.NaN;
- protected Dictionary<float, Slice> _slices = new ();
-
-
- /// <summary>
- /// Gets the size of resolution
- /// </summary>
- public Size Resolution { get; private set; }
-
- /// <summary>
- /// Gets the size of display
- /// </summary>
- public SizeF Display { get; private set; }
-
- /// <summary>
- /// Gets the pixels per millimeters
- /// </summary>
- public SizeF Ppmm { get; private set; }
-
- /*public float LowX
+ get
{
- get
- {
- if(_lowX == float.NaN)
- _lowX = RangeUtils.CalculateLowX(_stl);
+ if(_lowX == float.NaN)
+ _lowX = RangeUtils.CalculateLowX(_stl);
- return _lowX;
- }
+ return _lowX;
}
+ }
- public float HighX
+ public float HighX
+ {
+ get
{
- get
- {
- if (_highX == float.NaN)
- _highX = RangeUtils.CalculateHighX(_stl);
+ if (_highX == float.NaN)
+ _highX = RangeUtils.CalculateHighX(_stl);
- return _highX;
- }
+ return _highX;
}
+ }
- public float LowY
+ public float LowY
+ {
+ get
{
- get
- {
- if (_lowY == float.NaN)
- _lowY = RangeUtils.CalculateLowY(_stl);
+ if (_lowY == float.NaN)
+ _lowY = RangeUtils.CalculateLowY(_stl);
- return _lowY;
- }
+ return _lowY;
}
+ }
- public float HighY
+ public float HighY
+ {
+ get
{
- get
- {
- if (_highY == float.NaN)
- _highY = RangeUtils.CalculateHighY(_stl);
+ if (_highY == float.NaN)
+ _highY = RangeUtils.CalculateHighY(_stl);
- return _highY;
- }
+ return _highY;
}
+ }
- public float LowZ
+ public float LowZ
+ {
+ get
{
- get
- {
- if (_lowZ == float.NaN)
- _lowZ = RangeUtils.CalculateLowZ(_stl);
+ if (_lowZ == float.NaN)
+ _lowZ = RangeUtils.CalculateLowZ(_stl);
- return _lowZ;
- }
+ return _lowZ;
}
+ }
- public float HighZ
+ public float HighZ
+ {
+ get
{
- get
- {
- if (_highZ == float.NaN)
- _highZ = RangeUtils.CalculateHighZ(_stl);
-
- return _highZ;
- }
- }*/
+ if (_highZ == float.NaN)
+ _highZ = RangeUtils.CalculateHighZ(_stl);
- public Slicer(Size resolution, SizeF display)
- {
- Init(resolution, display);
+ return _highZ;
}
+ }*/
- /*public Slicer(Size resolution, SizeF display, STLDocument stl) : this(resolution, display)
- {
- _stl = stl;
- }
+ public Slicer(Size resolution, SizeF display)
+ {
+ Init(resolution, display);
+ }
- public Slicer(Size resolution, SizeF display, string stlPath) : this(resolution, display)
- {
- _stl = STLDocument.Open(stlPath);
+ /*public Slicer(Size resolution, SizeF display, STLDocument stl) : this(resolution, display)
+ {
+ _stl = stl;
+ }
- if (_stl is null)
- throw new FileNotFoundException(null, stlPath);
- }*/
+ public Slicer(Size resolution, SizeF display, string stlPath) : this(resolution, display)
+ {
+ _stl = STLDocument.Open(stlPath);
- public void Init(Size resolution, SizeF display)
- {
- Resolution = resolution;
- Display = display;
+ if (_stl is null)
+ throw new FileNotFoundException(null, stlPath);
+ }*/
+
+ public void Init(Size resolution, SizeF display)
+ {
+ Resolution = resolution;
+ Display = display;
- Ppmm = new SizeF(resolution.Width / display.Width, resolution.Height / display.Height);
- }
+ Ppmm = new SizeF(resolution.Width / display.Width, resolution.Height / display.Height);
+ }
- #region Slice Methods
- /*public Slice GetSliceAtZIndex(float z)
+ #region Slice Methods
+ /*public Slice GetSliceAtZIndex(float z)
+ {
+ // cache this in the event you have to retrieve a single value more than once,
+ // we don't want to have to do this math again
+ if (!_slices.ContainsKey(z))
{
- // cache this in the event you have to retrieve a single value more than once,
- // we don't want to have to do this math again
- if (!_slices.ContainsKey(z))
- {
- var facets = LinAlgUtils.FindFacetsIntersectingZIndex(_stl, z);
- _slices[z] = new Slice();
- _slices[z].AddRange(facets.Select(f => LinAlgUtils.CreateLineFromFacetAtZIndex(f, z)));
- }
+ var facets = LinAlgUtils.FindFacetsIntersectingZIndex(_stl, z);
+ _slices[z] = new Slice();
+ _slices[z].AddRange(facets.Select(f => LinAlgUtils.CreateLineFromFacetAtZIndex(f, z)));
+ }
- return _slices[z];
- }*/
+ return _slices[z];
+ }*/
- /*public Dictionary<float, Slice> SliceModel(float layerHeight)
+ /*public Dictionary<float, Slice> SliceModel(float layerHeight)
+ {
+ float volume = 0;
+ _stl.Facets.ForEach(facet =>
{
- float volume = 0;
- _stl.Facets.ForEach(facet =>
- {
- var v1 = facet.Vertices[0];
- var v2 = facet.Vertices[1];
- var v3 = facet.Vertices[2];
-
- volume += (
- -(v3.X * v2.Y * v1.Z)
- + (v2.X * v3.Y * v1.Z)
- + (v3.X * v1.Y * v2.Z)
- - (v1.X * v3.Y * v2.Z)
- - (v2.X * v1.Y * v3.Z)
- + (v1.X * v2.Y * v3.Z)
- ) / 6;
- });
- /*var newDict = new Dictionary<float, Slice>();
-
- for (var z = Layer.RoundHeight(LowZ); z <= HighZ; z = Layer.RoundHeight(z+layerHeight))
+ var v1 = facet.Vertices[0];
+ var v2 = facet.Vertices[1];
+ var v3 = facet.Vertices[2];
+
+ volume += (
+ -(v3.X * v2.Y * v1.Z)
+ + (v2.X * v3.Y * v1.Z)
+ + (v3.X * v1.Y * v2.Z)
+ - (v1.X * v3.Y * v2.Z)
+ - (v2.X * v1.Y * v3.Z)
+ + (v1.X * v2.Y * v3.Z)
+ ) / 6;
+ });
+ /*var newDict = new Dictionary<float, Slice>();
+
+ for (var z = Layer.RoundHeight(LowZ); z <= HighZ; z = Layer.RoundHeight(z+layerHeight))
+ {
+ if (_slices.Keys.Contains(z))
+ newDict[z] = _slices[z];
+ else
{
- if (_slices.Keys.Contains(z))
- newDict[z] = _slices[z];
- else
- {
- newDict[z] = GetSliceAtZIndex(z);
- _slices[z] = newDict[z];
- }
+ newDict[z] = GetSliceAtZIndex(z);
+ _slices[z] = newDict[z];
}
- return newDict;
- }*/
+ }
+ return newDict;
+ }*/
- public void SliceModel2()
- {
+ public void SliceModel2()
+ {
- }
- #endregion
+ }
+ #endregion
- public decimal MillimetersFromPixelsX(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Width, 2);
- public decimal MillimetersFromPixelsY(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Height, 2);
- public decimal MillimetersFromPixels (uint pixels) => (decimal) Math.Round(pixels / Math.Max(Ppmm.Width, Ppmm.Height), 2);
+ public decimal MillimetersFromPixelsX(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Width, 2);
+ public decimal MillimetersFromPixelsY(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Height, 2);
+ public decimal MillimetersFromPixels (uint pixels) => (decimal) Math.Round(pixels / Math.Max(Ppmm.Width, Ppmm.Height), 2);
- public static decimal MillimetersFromPixelsX(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Width / display.Width), 2);
- public static decimal MillimetersFromPixelsY(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Height / display.Height), 2);
+ public static decimal MillimetersFromPixelsX(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Width / display.Width), 2);
+ public static decimal MillimetersFromPixelsY(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Height / display.Height), 2);
- public uint PixelsFromMillimetersX(decimal millimeters) => (uint)(millimeters * (decimal) Ppmm.Width);
- public uint PixelsFromMillimetersY(decimal millimeters) => (uint)(millimeters * (decimal) Ppmm.Height);
- public uint PixelsFromMillimeters (decimal millimeters) => (uint)(millimeters * (decimal) Math.Max(Ppmm.Width, Ppmm.Height));
+ public uint PixelsFromMillimetersX(decimal millimeters) => (uint)(millimeters * (decimal) Ppmm.Width);
+ public uint PixelsFromMillimetersY(decimal millimeters) => (uint)(millimeters * (decimal) Ppmm.Height);
+ public uint PixelsFromMillimeters (decimal millimeters) => (uint)(millimeters * (decimal) Math.Max(Ppmm.Width, Ppmm.Height));
- public static uint PixelsFromMillimetersX(Size resolution, SizeF display, decimal millimeters) => (uint)(resolution.Width / display.Width * (double) millimeters);
- public static uint PixelsFromMillimetersY(Size resolution, SizeF display, decimal millimeters) => (uint)(resolution.Height / display.Height * (double) millimeters);
+ public static uint PixelsFromMillimetersX(Size resolution, SizeF display, decimal millimeters) => (uint)(resolution.Width / display.Width * (double) millimeters);
+ public static uint PixelsFromMillimetersY(Size resolution, SizeF display, decimal millimeters) => (uint)(resolution.Height / display.Height * (double) millimeters);
- public static uint MillimetersToLayers(float millimeters, float layerHeight) => (uint)(millimeters / layerHeight);
- public static uint MillimetersToLayers(double millimeters, double layerHeight) => (uint)(millimeters / layerHeight);
- public static uint MillimetersToLayers(decimal millimeters, decimal layerHeight) => (uint)(millimeters / layerHeight);
+ public static uint MillimetersToLayers(float millimeters, float layerHeight) => (uint)(millimeters / layerHeight);
+ public static uint MillimetersToLayers(double millimeters, double layerHeight) => (uint)(millimeters / layerHeight);
+ public static uint MillimetersToLayers(decimal millimeters, decimal layerHeight) => (uint)(millimeters / layerHeight);
- public static uint LayersToMillimeters(uint layers, float layerHeight) => (uint)(layers * layerHeight);
- public static uint LayersToMillimeters(uint layers, double layerHeight) => (uint)(layers * layerHeight);
- public static uint LayersToMillimeters(uint layers, decimal layerHeight) => (uint)(layers * layerHeight);
- }
-}
+ public static uint LayersToMillimeters(uint layers, float layerHeight) => (uint)(layers * layerHeight);
+ public static uint LayersToMillimeters(uint layers, double layerHeight) => (uint)(layers * layerHeight);
+ public static uint LayersToMillimeters(uint layers, decimal layerHeight) => (uint)(layers * layerHeight);
+} \ No newline at end of file
diff --git a/UVtools.Core/Statistics.cs b/UVtools.Core/Statistics.cs
index 6f81006..ce0ab1c 100644
--- a/UVtools.Core/Statistics.cs
+++ b/UVtools.Core/Statistics.cs
@@ -10,37 +10,36 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-namespace UVtools.Core
+namespace UVtools.Core;
+
+public class Statistics
{
- public class Statistics
+ #region Properties
+
+ public List<string> ImplementedKeys { get; } = new List<string>();
+ public List<string> MissingKeys { get; } = new List<string>();
+ public ushort TotalKeys => (ushort)(ImplementedKeys.Count + MissingKeys.Count);
+
+ public Stopwatch ExecutionTime { get; } = new Stopwatch();
+ #endregion
+
+ #region Overrides
+ public override string ToString()
+ {
+ string message = $"{nameof(ImplementedKeys)}: {ImplementedKeys.Count}, {nameof(MissingKeys)}: {MissingKeys.Count}, {nameof(TotalKeys)}: {TotalKeys}, {nameof(ExecutionTime)}: {ExecutionTime.ElapsedMilliseconds}ms";
+ message = MissingKeys.Aggregate(message, (current, missingKey) => current + ("\n" + missingKey));
+ return message;
+ }
+
+ #endregion
+
+ #region Methods
+
+ public void Clear()
{
- #region Properties
-
- public List<string> ImplementedKeys { get; } = new List<string>();
- public List<string> MissingKeys { get; } = new List<string>();
- public ushort TotalKeys => (ushort)(ImplementedKeys.Count + MissingKeys.Count);
-
- public Stopwatch ExecutionTime { get; } = new Stopwatch();
- #endregion
-
- #region Overrides
- public override string ToString()
- {
- string message = $"{nameof(ImplementedKeys)}: {ImplementedKeys.Count}, {nameof(MissingKeys)}: {MissingKeys.Count}, {nameof(TotalKeys)}: {TotalKeys}, {nameof(ExecutionTime)}: {ExecutionTime.ElapsedMilliseconds}ms";
- message = MissingKeys.Aggregate(message, (current, missingKey) => current + ("\n" + missingKey));
- return message;
- }
-
- #endregion
-
- #region Methods
-
- public void Clear()
- {
- ImplementedKeys.Clear();
- MissingKeys.Clear();
- ExecutionTime.Reset();
- }
- #endregion
+ ImplementedKeys.Clear();
+ MissingKeys.Clear();
+ ExecutionTime.Reset();
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Suggestions/Suggestion.cs b/UVtools.Core/Suggestions/Suggestion.cs
index e9623bd..613a3ee 100644
--- a/UVtools.Core/Suggestions/Suggestion.cs
+++ b/UVtools.Core/Suggestions/Suggestion.cs
@@ -7,153 +7,189 @@
*/
using System;
+using System.ComponentModel;
using System.Xml.Serialization;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
-namespace UVtools.Core.Suggestions
+namespace UVtools.Core.Suggestions;
+
+public abstract class Suggestion : BindableBase
{
- public abstract class Suggestion : BindableBase
+ #region Enums
+ public enum SuggestionApplyWhen : byte
{
- #region Members
+ [Description("Outside limits: Applies when the recommended value does not meet the current limit criteria")]
+ OutsideLimits,
- private bool _enabled = true;
- private bool _autoApply;
- private FileFormat _slicerFile;
+ [Description("Different: Applies when the recommended value is different from the current")]
+ Different,
+ }
+ #endregion
- #endregion
+ #region Members
- #region Properties
+ protected FileFormat _slicerFile = null!;
+ protected bool _enabled = true;
+ protected bool _autoApply;
+ protected SuggestionApplyWhen _applyWhen = SuggestionApplyWhen.OutsideLimits;
- /// <summary>
- /// Gets or sets if this suggestion is enabled
- /// </summary>
- public bool Enabled
- {
- get => _enabled;
- set => RaiseAndSetIfChanged(ref _enabled, value);
- }
+ #endregion
- /// <summary>
- /// Gets or sets if this suggestion can be auto applied once file load
- /// </summary>
- public bool AutoApply
- {
- get => _autoApply;
- set => RaiseAndSetIfChanged(ref _autoApply, value);
- }
+ #region Properties
- /// <summary>
- /// Gets or sets the <see cref="FileFormat"/>
- /// </summary>
- [XmlIgnore]
- public FileFormat SlicerFile
+ /// <summary>
+ /// Gets or sets the <see cref="FileFormat"/>
+ /// </summary>
+ [XmlIgnore]
+ public FileFormat SlicerFile
+ {
+ get => _slicerFile;
+ set
{
- get => _slicerFile;
- set
- {
- if (ReferenceEquals(_slicerFile, value)) return;
- _slicerFile = value;
- RaisePropertyChanged();
- RaisePropertyChanged(nameof(IsAvailable));
- RaisePropertyChanged(nameof(IsApplied));
- }
+ if (ReferenceEquals(_slicerFile, value)) return;
+ _slicerFile = value;
+ RaisePropertyChanged();
+ RaisePropertyChanged(nameof(IsAvailable));
+ RaisePropertyChanged(nameof(IsApplied));
}
+ }
+
+ /// <summary>
+ /// Gets or sets if this suggestion is enabled
+ /// </summary>
+ public bool Enabled
+ {
+ get => _enabled;
+ set => RaiseAndSetIfChanged(ref _enabled, value);
+ }
+
+ /// <summary>
+ /// Gets or sets if this suggestion can be auto applied once file load
+ /// </summary>
+ public bool AutoApply
+ {
+ get => _autoApply;
+ set => RaiseAndSetIfChanged(ref _autoApply, value);
+ }
+
+ /// <summary>
+ /// Gets or sets when to apply the suggestion
+ /// </summary>
+ public SuggestionApplyWhen ApplyWhen
+ {
+ get => _applyWhen;
+ set => RaiseAndSetIfChanged(ref _applyWhen, value);
+ }
+
+ /// <summary>
+ /// Gets if this suggestion is informative only and contain no actions to execute
+ /// </summary>
+ public virtual bool IsInformativeOnly => false;
+
+ /// <summary>
+ /// Gets if this suggestion is available given the <see cref="SlicerFile"/>
+ /// </summary>
+ public virtual bool IsAvailable => true;
+
+ /// <summary>
+ /// Gets if this suggestion is already applied given the <see cref="SlicerFile"/>
+ /// </summary>
+ public abstract bool IsApplied { get; }
+
+ /// <summary>
+ /// Gets the title for this suggestion
+ /// </summary>
+ public abstract string Title { get; }
+
+ public abstract string Description { get; }
+
+ /// <summary>
+ /// Gets the message for this suggestion
+ /// </summary>
+ public abstract string Message { get; }
+
+ /// <summary>
+ /// Gets the tooltip message
+ /// </summary>
+ public virtual string ToolTip => Description;
+
+ public virtual string? InformationUrl => null;
- /// <summary>
- /// Gets if this suggestion is informative only and contain no actions to execute
- /// </summary>
- public virtual bool IsInformativeOnly => false;
-
- /// <summary>
- /// Gets if this suggestion is available given the <see cref="SlicerFile"/>
- /// </summary>
- public virtual bool IsAvailable => true;
-
- /// <summary>
- /// Gets if this suggestion is already applied given the <see cref="SlicerFile"/>
- /// </summary>
- public abstract bool IsApplied { get; }
-
- /// <summary>
- /// Gets the title for this suggestion
- /// </summary>
- public abstract string Title { get; }
-
- /// <summary>
- /// Gets the message for this suggestion
- /// </summary>
- public abstract string Message { get; }
-
- /// <summary>
- /// Gets the tooltip message
- /// </summary>
- public virtual string ToolTip => null;
-
- public virtual string InformationUrl => null;
-
- /// <summary>
- /// Gets the confirmation message before apply the suggestion
- /// </summary>
- public virtual string ConfirmationMessage => null;
+ /// <summary>
+ /// Gets the confirmation message before apply the suggestion
+ /// </summary>
+ public virtual string? ConfirmationMessage => null;
- public string GlobalAppliedMessage => $"✓ {Title}";
- public string GlobalNotAppliedMessage => $"⚠ {Title}";
+ public string GlobalAppliedMessage => $"✓ {Title}";
+ public string GlobalNotAppliedMessage => $"⚠ {Title}";
- #endregion
+ #endregion
- #region Methods
+ #region Methods
- public void RefreshNotifyAll()
- {
- RaisePropertyChanged(nameof(IsAvailable));
- RaisePropertyChanged(nameof(IsApplied));
- RefreshNotifyMessage();
- }
+ public void RefreshNotifyAll()
+ {
+ RaisePropertyChanged(nameof(IsAvailable));
+ RaisePropertyChanged(nameof(IsApplied));
+ RefreshNotifyMessage();
+ }
- public void RefreshNotifyMessage()
- {
- RaisePropertyChanged(nameof(Message));
- RaisePropertyChanged(nameof(ToolTip));
- }
+ public void RefreshNotifyMessage()
+ {
+ RaisePropertyChanged(nameof(Message));
+ RaisePropertyChanged(nameof(ToolTip));
+ }
- /// <summary>
- /// Executes and applies the suggestion
- /// </summary>
- /// <returns></returns>
- /// <exception cref="NotImplementedException"></exception>
- protected virtual bool ExecuteInternally()
- {
- throw new NotImplementedException();
- }
+ /// <summary>
+ /// Validates the settings and return a string message if got any error
+ /// </summary>
+ /// <returns></returns>
+ public virtual string? Validate() => null;
+
+ /// <summary>
+ /// Executes and applies the suggestion
+ /// </summary>
+ /// <returns></returns>
+ /// <exception cref="NotImplementedException"></exception>
+ protected virtual bool ExecuteInternally()
+ {
+ throw new NotImplementedException();
+ }
- /// <summary>
- /// Executes and applies the suggestion
- /// </summary>
- /// <returns></returns>
- /// <exception cref="InvalidOperationException"></exception>
- public bool Execute()
- {
- if (_slicerFile is null) throw new InvalidOperationException($"The suggestion '{Title}' can't execute due the lacking of a file parent.");
- if (!Enabled || !IsAvailable || IsApplied || IsInformativeOnly) return false;
+ /// <summary>
+ /// Executes and applies the suggestion
+ /// </summary>
+ /// <returns></returns>
+ /// <exception cref="InvalidOperationException"></exception>
+ public bool Execute()
+ {
+ if (_slicerFile is null) throw new InvalidOperationException($"The suggestion '{Title}' can't execute due the lacking of a file parent.");
+ if (!Enabled || !IsAvailable || IsApplied || IsInformativeOnly || SlicerFile.LayerCount == 0) return false;
- var result = ExecuteInternally();
+ var result = ExecuteInternally();
- RaisePropertyChanged(nameof(IsApplied));
- RefreshNotifyMessage();
+ RaisePropertyChanged(nameof(IsApplied));
+ RefreshNotifyMessage();
- return result;
- }
+ return result;
+ }
- /// <summary>
- /// Executes only if this suggestion is marked with <see cref="AutoApply"/> as true
- /// </summary>
- /// <returns></returns>
- public bool ExecuteIfAutoApply()
- {
- return _autoApply && Execute();
- }
- #endregion
+ /// <summary>
+ /// Executes only if this suggestion is marked with <see cref="AutoApply"/> as true
+ /// </summary>
+ /// <returns></returns>
+ public bool ExecuteIfAutoApply()
+ {
+ return _autoApply && Execute();
+ }
+
+ public Suggestion Clone()
+ {
+ return (MemberwiseClone() as Suggestion)!;
}
-}
+ #endregion
+
+
+} \ No newline at end of file
diff --git a/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs b/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs
index 5c5ddf7..28c3c77 100644
--- a/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs
+++ b/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs
@@ -7,90 +7,131 @@
*/
using System;
+using System.Text;
+using UVtools.Core.Layers;
-namespace UVtools.Core.Suggestions
+namespace UVtools.Core.Suggestions;
+
+public sealed class SuggestionBottomLayerCount : Suggestion
{
- public sealed class SuggestionBottomLayerCount : Suggestion
- {
- #region Members
+ #region Members
- private decimal _targetBottomHeight = 0.25m;
- private decimal _minimumBottomHeight = 0.07m;
- private decimal _maximumBottomHeight = 0.4m;
- private byte _minimumBottomLayerCount = 3;
- private byte _maximumBottomLayerCount = 7;
+ private decimal _targetBottomHeight = 0.25m;
+ private decimal _minimumBottomHeight = 0.07m;
+ private decimal _maximumBottomHeight = 0.4m;
+ private byte _minimumBottomLayerCount = 3;
+ private byte _maximumBottomLayerCount = 7;
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public override bool IsAvailable => SlicerFile?.CanUseBottomLayerCount ?? false;
+ public override bool IsAvailable => SlicerFile?.CanUseBottomLayerCount ?? false;
- public override bool IsApplied
+ public override bool IsApplied
+ {
+ get
{
- get
+ if (SlicerFile is null) return false;
+ var bottomHeight = (decimal)SlicerFile.BottomLayersHeight;
+
+ return _applyWhen switch
{
- if (SlicerFile is null) return false;
- var bottomHeight = (decimal)SlicerFile.BottomLayersHeight;
- return bottomHeight >= _minimumBottomHeight && bottomHeight <= _maximumBottomHeight
- && SlicerFile.BottomLayerCount >= _minimumBottomLayerCount && SlicerFile.BottomLayerCount <= _maximumBottomLayerCount;
- }
+ SuggestionApplyWhen.OutsideLimits => bottomHeight >= _minimumBottomHeight &&
+ bottomHeight <= _maximumBottomHeight &&
+ SlicerFile.BottomLayerCount >= _minimumBottomLayerCount &&
+ SlicerFile.BottomLayerCount <= _maximumBottomLayerCount,
+ SuggestionApplyWhen.Different => bottomHeight == TargetBottomHeight,
+
+ _ => throw new ArgumentOutOfRangeException()
+ };
}
+ }
- public override string Title => "Bottom layer count";
- public override string Message => IsApplied
- ? $"{GlobalAppliedMessage}: {SlicerFile.BottomLayerCount} / {SlicerFile.BottomLayersHeight}mm"
- : $"{GlobalNotAppliedMessage} ({SlicerFile.BottomLayerCount}) is out of the recommended {BottomLayerCountValue} layers";
+ public override string Title => "Bottom layer count";
- public override string ToolTip => $"The recommended total height for the bottom layers must be between {_minimumBottomHeight}mm and {_maximumBottomHeight}mm constrained from {_minimumBottomLayerCount} to {_maximumBottomLayerCount} layers.\n" +
- "Explanation: Bottom layers should be kept to a minimum, usually from 2 or 3, it function is to provide a good adhesion to the build plate, and that happens on the first layer, using a high count have disadvantages.";
+ public override string Description => "Bottom layers should be kept to a minimum, usually from 2 to 3, it function is to provide a good adhesion to the first layer on the build plate, using a high count have disadvantages.";
- public override string InformationUrl => "https://ameralabs.com/blog/default-3d-printing-raft-settings";
+ public override string Message => IsApplied
+ ? $"{GlobalAppliedMessage}: {SlicerFile.BottomLayerCount} / {SlicerFile.BottomLayersHeight}mm"
+ : $"{GlobalNotAppliedMessage} ({SlicerFile.BottomLayerCount}) is out of the recommended {BottomLayerCountValue} layers";
- public override string ConfirmationMessage => $"{Title}: {SlicerFile.BottomLayerCount} » {BottomLayerCountValue}";
+ public override string ToolTip => $"The recommended total height for the bottom layers must be between {_minimumBottomHeight}mm and {_maximumBottomHeight}mm constrained from {_minimumBottomLayerCount} to {_maximumBottomLayerCount} layers.\n" +
+ $"Explanation: {Description}";
- public decimal TargetBottomHeight
- {
- get => _targetBottomHeight;
- set => RaiseAndSetIfChanged(ref _targetBottomHeight, value);
- }
+ public override string? InformationUrl => "https://ameralabs.com/blog/default-3d-printing-raft-settings";
- public decimal MinimumBottomHeight
- {
- get => _minimumBottomHeight;
- set => RaiseAndSetIfChanged(ref _minimumBottomHeight, value);
- }
+ public override string? ConfirmationMessage => $"{Title}: {SlicerFile.BottomLayerCount} » {BottomLayerCountValue}";
- public decimal MaximumBottomHeight
- {
- get => _maximumBottomHeight;
- set => RaiseAndSetIfChanged(ref _maximumBottomHeight, value);
- }
+ public decimal TargetBottomHeight
+ {
+ get => _targetBottomHeight;
+ set => RaiseAndSetIfChanged(ref _targetBottomHeight, Layer.RoundHeight(value));
+ }
+
+ public decimal MinimumBottomHeight
+ {
+ get => _minimumBottomHeight;
+ set => RaiseAndSetIfChanged(ref _minimumBottomHeight, Layer.RoundHeight(value));
+ }
+
+ public decimal MaximumBottomHeight
+ {
+ get => _maximumBottomHeight;
+ set => RaiseAndSetIfChanged(ref _maximumBottomHeight, Layer.RoundHeight(value));
+ }
- public byte MinimumBottomLayerCount
+ public byte MinimumBottomLayerCount
+ {
+ get => _minimumBottomLayerCount;
+ set => RaiseAndSetIfChanged(ref _minimumBottomLayerCount, value);
+ }
+
+ public byte MaximumBottomLayerCount
+ {
+ get => _maximumBottomLayerCount;
+ set => RaiseAndSetIfChanged(ref _maximumBottomLayerCount, value);
+ }
+
+ public ushort BottomLayerCountValue => Math.Clamp((ushort)Math.Ceiling((float)_targetBottomHeight / SlicerFile.LayerHeight), _minimumBottomLayerCount, _maximumBottomLayerCount);
+
+ #endregion
+
+ #region Override
+
+ public override string? Validate()
+ {
+ var sb = new StringBuilder();
+
+ if (_minimumBottomHeight > _maximumBottomHeight)
{
- get => _minimumBottomLayerCount;
- set => RaiseAndSetIfChanged(ref _minimumBottomLayerCount, value);
+ sb.AppendLine("Minimum limit (mm) can't be higher than maximum limit (mm)");
}
- public byte MaximumBottomLayerCount
+ if (_minimumBottomLayerCount > _maximumBottomLayerCount)
{
- get => _maximumBottomLayerCount;
- set => RaiseAndSetIfChanged(ref _maximumBottomLayerCount, value);
+ sb.AppendLine("Minimum limit (layers) can't be higher than maximum limit (layers)");
}
- public ushort BottomLayerCountValue => Math.Clamp((ushort)Math.Ceiling((float)_targetBottomHeight / SlicerFile.LayerHeight), _minimumBottomLayerCount, _maximumBottomLayerCount);
+ return sb.ToString();
+ }
- #endregion
+ #endregion
- #region Methods
+ #region Contructor
+ public SuggestionBottomLayerCount()
+ {
+ _applyWhen = SuggestionApplyWhen.OutsideLimits;
+ }
+ #endregion
- protected override bool ExecuteInternally()
- {
- SlicerFile.BottomLayerCount = BottomLayerCountValue;
- return true;
- }
+ #region Methods
- #endregion
+ protected override bool ExecuteInternally()
+ {
+ SlicerFile.BottomLayerCount = BottomLayerCountValue;
+ return true;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Suggestions/SuggestionLayerHeight.cs b/UVtools.Core/Suggestions/SuggestionLayerHeight.cs
index 2603292..884d32c 100644
--- a/UVtools.Core/Suggestions/SuggestionLayerHeight.cs
+++ b/UVtools.Core/Suggestions/SuggestionLayerHeight.cs
@@ -6,79 +6,108 @@
* of this license document, but changing it is not allowed.
*/
+using System.Text;
using UVtools.Core.Extensions;
using Layer = UVtools.Core.Layers.Layer;
-namespace UVtools.Core.Suggestions
+namespace UVtools.Core.Suggestions;
+
+public sealed class SuggestionLayerHeight : Suggestion
{
- public sealed class SuggestionLayerHeight : Suggestion
- {
- #region Members
+ #region Members
- private decimal _minimumLayerHeight = 0.03m;
- private decimal _maximumLayerHeight = 0.10m;
- private byte _maximumLayerHeightDecimalDigits = 2;
+ private decimal _minimumLayerHeight = 0.03m;
+ private decimal _maximumLayerHeight = 0.10m;
+ private byte _maximumLayerHeightDecimalPlates = 2;
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public override bool IsApplied
+ public override bool IsApplied
+ {
+ get
{
- get
- {
- if (SlicerFile is null) return false;
- if ((decimal)SlicerFile.LayerHeight < _minimumLayerHeight || (decimal)SlicerFile.LayerHeight > _maximumLayerHeight) return false;
- if (SlicerFile.LayerHeight.DecimalDigits() > _maximumLayerHeightDecimalDigits) return false;
+ if (SlicerFile is null) return false;
+ if ((decimal)SlicerFile.LayerHeight < _minimumLayerHeight || (decimal)SlicerFile.LayerHeight > _maximumLayerHeight) return false;
+ if (SlicerFile.LayerHeight.DecimalDigits() > _maximumLayerHeightDecimalPlates) return false;
- foreach (var layer in SlicerFile)
- {
- if ((decimal)layer.LayerHeight < _minimumLayerHeight || (decimal)layer.LayerHeight > _maximumLayerHeight) return false;
- if (layer.LayerHeight.DecimalDigits() > _maximumLayerHeightDecimalDigits) return false;
- }
-
- return true;
+ foreach (var layer in SlicerFile)
+ {
+ if ((decimal)layer.LayerHeight < _minimumLayerHeight || (decimal)layer.LayerHeight > _maximumLayerHeight) return false;
+ if (layer.LayerHeight.DecimalDigits() > _maximumLayerHeightDecimalPlates) return false;
}
+
+ return true;
}
+ }
- public override bool IsInformativeOnly => true;
+ public override bool IsInformativeOnly => true;
- public override string Title => "Layer height";
- public override string Message => IsApplied
- ? $"{GlobalAppliedMessage}: {SlicerFile.LayerHeight}mm"
- : $"{GlobalNotAppliedMessage} is out of the recommended {_minimumLayerHeight}mm » {_maximumLayerHeight}mm, up to {_maximumLayerHeightDecimalDigits} decimal digit(s)";
+ public override string Title => "Layer height";
- public override string ToolTip => $"The recommended layer height is between {_minimumLayerHeight}mm and {_maximumLayerHeight}mm up to {_maximumLayerHeightDecimalDigits} digit(s) precision.\n" +
- "Explanation: Using the right layer height is important to get successful prints:\n" +
- "Thin layers may cause problems on adhesion, delamination, will print much slower and have no real visual benefits.\n" +
- "Thick layers may not fully cure no matter the exposure time you use, causing delamination and other hazards. Read your resin datasheet to know the limits.\n" +
+ public override string Description => "Using the right layer height is important to get successful prints:\n" +
+ "Thin layers may cause problems on adhesion, lamination, will print much slower and have no real visual benefits.\n" +
+ "Thick layers may not fully cure no matter the exposure time you use, causing lamination and other hazards. Read your resin datasheet to know the limits.\n" +
"Using layer height with too many decimal digits may produce a wrong positioning due stepper step loss and/or Z axis quality.\n" +
"Solution: Re-slice the model with proper layer height.";
- public override string ConfirmationMessage => $"{Title}: Re-slice the model with proper layer height";
+ public override string Message => IsApplied
+ ? $"{GlobalAppliedMessage}: {SlicerFile.LayerHeight}mm"
+ : $"{GlobalNotAppliedMessage} is out of the recommended {_minimumLayerHeight}mm » {_maximumLayerHeight}mm, up to {_maximumLayerHeightDecimalPlates} decimal digit(s)";
+
+ public override string ToolTip => $"The recommended layer height is between {_minimumLayerHeight}mm and {_maximumLayerHeight}mm up to {_maximumLayerHeightDecimalPlates} digit(s) precision.\n" +
+ $"Explanation: {Description}";
+
+ public override string? ConfirmationMessage => $"{Title}: Re-slice the model with proper layer height";
+
+ public decimal MinimumLayerHeight
+ {
+ get => _minimumLayerHeight;
+ set => RaiseAndSetIfChanged(ref _minimumLayerHeight, Layer.RoundHeight(value.Clamp(Layer.MinimumHeight, Layer.MaximumHeight)));
+ }
+
+ public decimal MaximumLayerHeight
+ {
+ get => _maximumLayerHeight;
+ set => RaiseAndSetIfChanged(ref _maximumLayerHeight, Layer.RoundHeight(value.Clamp(Layer.MinimumHeight, Layer.MaximumHeight)));
+ }
+
+ public byte MaximumLayerHeightDecimalPlates
+ {
+ get => _maximumLayerHeightDecimalPlates;
+ set => RaiseAndSetIfChanged(ref _maximumLayerHeightDecimalPlates, value.Clamp(2, 4));
+ }
+
+ #endregion
- public decimal MinimumLayerHeight
+ #region Override
+
+ public override string? Validate()
+ {
+ var sb = new StringBuilder();
+
+ if (_maximumLayerHeightDecimalPlates is < 2 or > 10)
{
- get => _minimumLayerHeight;
- set => RaiseAndSetIfChanged(ref _minimumLayerHeight, value.Clamp(Layer.MinimumHeight, Layer.MaximumHeight));
+ sb.AppendLine("Layer height digits must be between 2 and 10");
}
- public decimal MaximumLayerHeight
+ if (_minimumLayerHeight <= 0)
{
- get => _maximumLayerHeight;
- set => RaiseAndSetIfChanged(ref _maximumLayerHeight, value.Clamp(Layer.MinimumHeight, Layer.MaximumHeight));
+ sb.AppendLine("Minimum layer height must be higher than 0mm");
}
- public byte MaximumLayerHeightDecimalPlates
+ if (_minimumLayerHeight > _maximumLayerHeight)
{
- get => _maximumLayerHeightDecimalDigits;
- set => RaiseAndSetIfChanged(ref _maximumLayerHeightDecimalDigits, value.Clamp(2, 4));
+ sb.AppendLine("Minimum layer height can't be higher than maximum layer height");
}
- #endregion
+ return sb.ToString();
+ }
- #region Methods
+ #endregion
- #endregion
- }
-}
+ #region Methods
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs b/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs
index f5b0da5..b34f689 100644
--- a/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs
+++ b/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs
@@ -7,176 +7,261 @@
*/
using System;
+using System.ComponentModel;
+using System.Text;
using UVtools.Core.Extensions;
-namespace UVtools.Core.Suggestions
+namespace UVtools.Core.Suggestions;
+
+public sealed class SuggestionWaitTimeAfterCure : Suggestion
{
- public sealed class SuggestionWaitTimeAfterCure : Suggestion
+ #region Enums
+ public enum SuggestionWaitTimeAfterCureSetType
{
- #region Enums
- public enum SuggestionWaitTimeAfterCureApplyType
- {
- Fixed,
- ExposureProportional
- }
- #endregion
+ [Description("Fixed: Use a fixed time")]
+ Fixed,
+ [Description("Proportional: Proportional to an exposure time")]
+ ProportionalExposure
+ }
+ #endregion
- #region Members
- private SuggestionWaitTimeAfterCureApplyType _applyType = SuggestionWaitTimeAfterCureApplyType.Fixed;
- private decimal _fixedBottomWaitTimeAfterCure = 7;
- private decimal _fixedWaitTimeAfterCure = 1;
- private decimal _proportionalExposureTime = 2;
- private decimal _proportionalWaitTimeAfterCure = 1;
- private decimal _minimumBottomWaitTimeAfterCure = 3;
- private decimal _minimumWaitTimeAfterCure = 1;
- private decimal _maximumBottomWaitTimeAfterCure = 20;
- private decimal _maximumWaitTimeAfterCure = 12;
- #endregion
+ #region Members
+ private SuggestionWaitTimeAfterCureSetType _setType = SuggestionWaitTimeAfterCureSetType.Fixed;
+ private decimal _fixedBottomWaitTimeAfterCure = 7;
+ private decimal _fixedWaitTimeAfterCure = 1;
+ private decimal _proportionalWaitTimeAfterCure = 1;
+ private decimal _proportionalExposureTime = 3;
+ private decimal _minimumBottomWaitTimeAfterCure = 3;
+ private decimal _minimumWaitTimeAfterCure = 1;
+ private decimal _maximumBottomWaitTimeAfterCure = 20;
+ private decimal _maximumWaitTimeAfterCure = 12;
+ #endregion
- #region Properties
+ #region Properties
- public override bool IsAvailable => SlicerFile?.CanUseAnyWaitTimeAfterCure ?? false;
+ public override bool IsAvailable => SlicerFile?.CanUseAnyWaitTimeAfterCure ?? false;
- public override bool IsApplied
+ public override bool IsApplied
+ {
+ get
{
- get
+ if (SlicerFile is null) return false;
+
+ switch (_applyWhen)
{
- if (SlicerFile is null) return false;
- if (SlicerFile.CanUseBottomWaitTimeAfterCure)
- {
- if ((decimal) SlicerFile.BottomWaitTimeAfterCure < _minimumWaitTimeAfterCure ||
- (decimal) SlicerFile.BottomWaitTimeAfterCure > _maximumWaitTimeAfterCure ||
- Math.Abs(SlicerFile.BottomWaitTimeAfterCure - CalculateWaitTime(true, (decimal)SlicerFile.BottomExposureTime)) > 0.1) return false;
- }
- if (SlicerFile.CanUseWaitTimeAfterCure)
- {
- if ((decimal)SlicerFile.WaitTimeAfterCure < _minimumWaitTimeAfterCure ||
- (decimal)SlicerFile.WaitTimeAfterCure > _maximumWaitTimeAfterCure ||
- Math.Abs(SlicerFile.WaitTimeAfterCure - CalculateWaitTime(false, (decimal)SlicerFile.ExposureTime)) > 0.1) return false;
- }
-
- if (SlicerFile.CanUseLayerWaitTimeAfterCure)
- {
- foreach (var layer in SlicerFile)
+ case SuggestionApplyWhen.OutsideLimits:
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure)
+ {
+ if ((decimal)SlicerFile.BottomWaitTimeAfterCure < _minimumWaitTimeAfterCure ||
+ (decimal)SlicerFile.BottomWaitTimeAfterCure > _maximumWaitTimeAfterCure) return false;
+ }
+ if (SlicerFile.CanUseWaitTimeAfterCure)
+ {
+ if ((decimal)SlicerFile.WaitTimeAfterCure < _minimumWaitTimeAfterCure ||
+ (decimal)SlicerFile.WaitTimeAfterCure > _maximumWaitTimeAfterCure) return false;
+ }
+
+ if (SlicerFile.CanUseLayerWaitTimeAfterCure)
{
- if ((decimal)layer.WaitTimeAfterCure < _minimumWaitTimeAfterCure ||
- (decimal)layer.WaitTimeAfterCure > _maximumWaitTimeAfterCure ||
- Math.Abs(layer.WaitTimeAfterCure - CalculateWaitTime(layer.IsBottomLayer, (decimal)layer.ExposureTime)) > 0.1) return false;
+ foreach (var layer in SlicerFile)
+ {
+ if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers
+ if ((decimal)layer.WaitTimeAfterCure < _minimumWaitTimeAfterCure ||
+ (decimal)layer.WaitTimeAfterCure > _maximumWaitTimeAfterCure) return false;
+ }
}
- }
- return true;
+
+ break;
+ case SuggestionApplyWhen.Different:
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure)
+ {
+ if (Math.Abs(SlicerFile.BottomWaitTimeAfterCure - CalculateWaitTime(true, (decimal)SlicerFile.BottomExposureTime)) > 0.1) return false;
+ }
+ if (SlicerFile.CanUseWaitTimeAfterCure)
+ {
+ if (Math.Abs(SlicerFile.WaitTimeAfterCure - CalculateWaitTime(false, (decimal)SlicerFile.ExposureTime)) > 0.1) return false;
+ }
+
+ if (SlicerFile.CanUseLayerWaitTimeAfterCure)
+ {
+ foreach (var layer in SlicerFile)
+ {
+ if(layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers
+ if (Math.Abs(layer.WaitTimeAfterCure - CalculateWaitTime(layer.IsBottomLayer, (decimal)layer.ExposureTime)) > 0.1) return false;
+ }
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
}
+
+ return true;
}
+ }
- public override string Title => "Wait time after cure";
- public override string Message => IsApplied
- ? $"{GlobalAppliedMessage}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s"
- : $"{GlobalNotAppliedMessage} of {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s " +
- $"is out of the recommended {CalculateWaitTime(true, (decimal) SlicerFile.BottomWaitTimeAfterCure)}/{CalculateWaitTime(false, (decimal)SlicerFile.WaitTimeAfterCure)}s";
+ public override string Title => "Wait time after cure";
+ public override string Description => "Rest some time after cure the layer and before the lift sequence can be important to allow the layer to cooldown a bit and detach better from the FEP.";
- //public override string ToolTip => $"The recommended total height for the bottom layers must be between {_minimumBottomHeight}mm and {_maximumBottomHeight}mm constrained from {_minimumBottomLayerCount} to {_maximumBottomLayerCount} layers.\n" +
- // "Explanation: Bottom layers should be kept to a minimum, usually from 2 or 3, it function is to provide a good adhesion to the build plate, and that happens on the first layer, using a high count have disadvantages.";
+ public override string Message => IsApplied
+ ? $"{GlobalAppliedMessage}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s"
+ : $"{GlobalNotAppliedMessage} of {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s " +
+ $"is out of the recommended {CalculateWaitTime(true, (decimal) SlicerFile.BottomExposureTime)}/{CalculateWaitTime(false, (decimal)SlicerFile.ExposureTime)}s";
- public override string InformationUrl => "https://ameralabs.com/blog/default-3d-printing-raft-settings";
+ public override string ToolTip => (_setType == SuggestionWaitTimeAfterCureSetType.Fixed
+ ? $"The recommended wait time must be {_fixedBottomWaitTimeAfterCure}/{_fixedWaitTimeAfterCure}s"
+ : $"The recommended wait time is a ratio of (wait time){_proportionalWaitTimeAfterCure}s to (exposure time){_proportionalExposureTime}s") +
+ $" constrained from [Bottoms={_minimumBottomWaitTimeAfterCure}/{_maximumBottomWaitTimeAfterCure}s] and [Normals={_minimumWaitTimeAfterCure}/{_maximumWaitTimeAfterCure}s].\n" +
+ $"Explanation: {Description}";
- public override string ConfirmationMessage => $"{Title}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s » {CalculateWaitTime(true, (decimal)SlicerFile.BottomWaitTimeAfterCure)}/{CalculateWaitTime(false, (decimal)SlicerFile.WaitTimeAfterCure)}s";
+ public override string? ConfirmationMessage => $"{Title}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s » {CalculateWaitTime(true, (decimal)SlicerFile.BottomWaitTimeAfterCure)}/{CalculateWaitTime(false, (decimal)SlicerFile.WaitTimeAfterCure)}s";
- private SuggestionWaitTimeAfterCureApplyType ApplyType
+ public SuggestionWaitTimeAfterCureSetType SetType
+ {
+ get => _setType;
+ set
{
- get => _applyType;
- set => RaiseAndSetIfChanged(ref _applyType, value);
+ if(!RaiseAndSetIfChanged(ref _setType, value)) return;
+ RaisePropertyChanged(nameof(IsSetTypeFixed));
+ RaisePropertyChanged(nameof(IsSetTypeProportionalExposure));
}
+ }
- public decimal FixedBottomWaitTimeAfterCure
- {
- get => _fixedBottomWaitTimeAfterCure;
- set => RaiseAndSetIfChanged(ref _fixedBottomWaitTimeAfterCure, Math.Round(value, 2));
- }
+ public bool IsSetTypeFixed => _setType == SuggestionWaitTimeAfterCureSetType.Fixed;
+ public bool IsSetTypeProportionalExposure => _setType == SuggestionWaitTimeAfterCureSetType.ProportionalExposure;
- public decimal FixedWaitTimeAfterCure
- {
- get => _fixedWaitTimeAfterCure;
- set => RaiseAndSetIfChanged(ref _fixedWaitTimeAfterCure, Math.Round(value, 2));
- }
+ public decimal FixedBottomWaitTimeAfterCure
+ {
+ get => _fixedBottomWaitTimeAfterCure;
+ set => RaiseAndSetIfChanged(ref _fixedBottomWaitTimeAfterCure, Math.Round(value, 2));
+ }
- public decimal ProportionalExposureTime
- {
- get => _proportionalExposureTime;
- set => RaiseAndSetIfChanged(ref _proportionalExposureTime, Math.Round(value, 2));
- }
+ public decimal FixedWaitTimeAfterCure
+ {
+ get => _fixedWaitTimeAfterCure;
+ set => RaiseAndSetIfChanged(ref _fixedWaitTimeAfterCure, Math.Round(value, 2));
+ }
- public decimal ProportionalWaitTimeAfterCure
- {
- get => _proportionalWaitTimeAfterCure;
- set => RaiseAndSetIfChanged(ref _proportionalWaitTimeAfterCure, Math.Round(value, 2));
- }
+ public decimal ProportionalWaitTimeAfterCure
+ {
+ get => _proportionalWaitTimeAfterCure;
+ set => RaiseAndSetIfChanged(ref _proportionalWaitTimeAfterCure, Math.Round(value, 2));
+ }
- public decimal MinimumBottomWaitTimeAfterCure
- {
- get => _minimumBottomWaitTimeAfterCure;
- set => RaiseAndSetIfChanged(ref _minimumBottomWaitTimeAfterCure, Math.Round(value, 2));
- }
+ public decimal ProportionalExposureTime
+ {
+ get => _proportionalExposureTime;
+ set => RaiseAndSetIfChanged(ref _proportionalExposureTime, Math.Round(value, 2));
+ }
+
+ public decimal MinimumBottomWaitTimeAfterCure
+ {
+ get => _minimumBottomWaitTimeAfterCure;
+ set => RaiseAndSetIfChanged(ref _minimumBottomWaitTimeAfterCure, Math.Round(value, 2));
+ }
- public decimal MinimumWaitTimeAfterCure
- {
- get => _minimumWaitTimeAfterCure;
- set => RaiseAndSetIfChanged(ref _minimumWaitTimeAfterCure, Math.Round(value, 2));
- }
+ public decimal MinimumWaitTimeAfterCure
+ {
+ get => _minimumWaitTimeAfterCure;
+ set => RaiseAndSetIfChanged(ref _minimumWaitTimeAfterCure, Math.Round(value, 2));
+ }
+
+ public decimal MaximumBottomWaitTimeAfterCure
+ {
+ get => _maximumBottomWaitTimeAfterCure;
+ set => RaiseAndSetIfChanged(ref _maximumBottomWaitTimeAfterCure, Math.Round(value, 2));
+ }
+
+ public decimal MaximumWaitTimeAfterCure
+ {
+ get => _maximumWaitTimeAfterCure;
+ set => RaiseAndSetIfChanged(ref _maximumWaitTimeAfterCure, Math.Round(value, 2));
+ }
- public decimal MaximumBottomWaitTimeAfterCure
+ #endregion
+
+ #region Constructor
+
+ public SuggestionWaitTimeAfterCure()
+ {
+ _applyWhen = SuggestionApplyWhen.Different;
+ }
+
+ #endregion
+
+ #region Overrides
+
+ public override string? Validate()
+ {
+ var sb = new StringBuilder();
+
+ if (_minimumBottomWaitTimeAfterCure > _maximumBottomWaitTimeAfterCure)
{
- get => _maximumBottomWaitTimeAfterCure;
- set => RaiseAndSetIfChanged(ref _maximumBottomWaitTimeAfterCure, Math.Round(value, 2));
+ sb.AppendLine("Minimum bottom limit can't be higher than maximum bottom limit");
}
- public decimal MaximumWaitTimeAfterCure
+ if (_minimumWaitTimeAfterCure > _maximumWaitTimeAfterCure)
{
- get => _maximumWaitTimeAfterCure;
- set => RaiseAndSetIfChanged(ref _maximumWaitTimeAfterCure, Math.Round(value, 2));
+ sb.AppendLine("Minimum normal limit can't be higher than maximum normal limit");
}
- #endregion
-
- #region Methods
-
- protected override bool ExecuteInternally()
+ if (_setType == SuggestionWaitTimeAfterCureSetType.ProportionalExposure)
{
- if (SlicerFile.CanUseBottomWaitTimeAfterCure)
- {
- SlicerFile.BottomWaitTimeAfterCure = CalculateWaitTime(true, (decimal)SlicerFile.BottomExposureTime);
- }
- if (SlicerFile.CanUseWaitTimeAfterCure)
+ if (_proportionalWaitTimeAfterCure <= 0)
{
- SlicerFile.WaitTimeAfterCure = CalculateWaitTime(false, (decimal)SlicerFile.ExposureTime);
+ sb.AppendLine("The proportional wait time must be higher than 0s");
}
- if (SlicerFile.CanUseLayerWaitTimeAfterCure)
+ if (_proportionalExposureTime <= 0)
{
- foreach (var layer in SlicerFile)
- {
- layer.WaitTimeAfterCure = CalculateWaitTime(layer.IsBottomLayer, (decimal) layer.ExposureTime);
- }
+ sb.AppendLine("The proportional exposure time must be higher than 0s");
}
+ }
- return true;
+ return sb.ToString();
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally()
+ {
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure)
+ {
+ SlicerFile.BottomWaitTimeAfterCure = CalculateWaitTime(true, (decimal)SlicerFile.BottomExposureTime);
+ }
+ if (SlicerFile.CanUseWaitTimeAfterCure)
+ {
+ SlicerFile.WaitTimeAfterCure = CalculateWaitTime(false, (decimal)SlicerFile.ExposureTime);
}
-
- public float CalculateWaitTime(bool isBottomLayer, decimal exposureTime)
+ if (SlicerFile.CanUseLayerWaitTimeAfterCure)
{
- return _applyType switch
+ foreach (var layer in SlicerFile)
{
- SuggestionWaitTimeAfterCureApplyType.Fixed => (float) (isBottomLayer
- ? _fixedBottomWaitTimeAfterCure
- : _fixedWaitTimeAfterCure),
- SuggestionWaitTimeAfterCureApplyType.ExposureProportional => (float) Math.Round(
- (exposureTime * _proportionalWaitTimeAfterCure / _proportionalExposureTime).Clamp(
- isBottomLayer ? _minimumBottomWaitTimeAfterCure : _minimumWaitTimeAfterCure,
- isBottomLayer ? _maximumBottomWaitTimeAfterCure : _maximumWaitTimeAfterCure), 2),
- _ => throw new ArgumentOutOfRangeException()
- };
+ if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers
+ layer.WaitTimeAfterCure = CalculateWaitTime(layer.IsBottomLayer, (decimal) layer.ExposureTime);
+ }
}
- #endregion
+ return true;
+ }
+
+
+ public float CalculateWaitTime(bool isBottomLayer, decimal exposureTime)
+ {
+ return _setType switch
+ {
+ SuggestionWaitTimeAfterCureSetType.Fixed => (float) (isBottomLayer
+ ? _fixedBottomWaitTimeAfterCure
+ : _fixedWaitTimeAfterCure),
+ SuggestionWaitTimeAfterCureSetType.ProportionalExposure => (float) Math.Round(
+ (exposureTime * _proportionalWaitTimeAfterCure / _proportionalExposureTime).Clamp(
+ isBottomLayer ? _minimumBottomWaitTimeAfterCure : _minimumWaitTimeAfterCure,
+ isBottomLayer ? _maximumBottomWaitTimeAfterCure : _maximumWaitTimeAfterCure), 2),
+ _ => throw new ArgumentOutOfRangeException()
+ };
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
new file mode 100644
index 0000000..b6bc2f9
--- /dev/null
+++ b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
@@ -0,0 +1,393 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Text;
+using UVtools.Core.Extensions;
+using UVtools.Core.Layers;
+
+namespace UVtools.Core.Suggestions;
+
+public sealed class SuggestionWaitTimeBeforeCure : Suggestion
+{
+ #region Enums
+ public enum SuggestionWaitTimeBeforeCureSetType
+ {
+ [Description("Fixed: Use a fixed time")]
+ Fixed,
+ [Description("Proportional to layer pixels")]
+ ProportionalLayerPixels,
+ [Description("Proportional to layer area")]
+ ProportionalLayerArea,
+ }
+ #endregion
+
+ #region Members
+ private SuggestionWaitTimeBeforeCureSetType _setType = SuggestionWaitTimeBeforeCureSetType.Fixed;
+ private decimal _bottomHeight = 1;
+ private decimal _fixedBottomWaitTimeBeforeCure = 20;
+ private decimal _fixedWaitTimeBeforeCure = 2;
+ private decimal _proportionalBottomWaitTimeBeforeCure = 20;
+ private decimal _proportionalWaitTimeBeforeCure = 2;
+ private uint _proportionalBottomLayerPixels = 1000000;
+ private uint _proportionalLayerPixels = 1000000;
+ private uint _proportionalBottomLayerArea = 1000;
+ private uint _proportionalLayerArea = 1000;
+ private decimal _minimumBottomWaitTimeBeforeCure = 5;
+ private decimal _minimumWaitTimeBeforeCure = 1;
+ private decimal _maximumBottomWaitTimeBeforeCure = 120;
+ private decimal _maximumWaitTimeBeforeCure = 12;
+ private bool _createEmptyFirstLayer = true;
+
+ #endregion
+
+ #region Properties
+
+ public override bool IsAvailable
+ {
+ get
+ {
+ if (SlicerFile is null) return false;
+ return SlicerFile.CanUseAnyWaitTimeBeforeCure || SlicerFile.CanUseAnyLightOffDelay;
+ }
+ }
+
+ public override bool IsApplied
+ {
+ get
+ {
+ if (SlicerFile is null) return false;
+
+ switch (_applyWhen)
+ {
+ case SuggestionApplyWhen.OutsideLimits:
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure || SlicerFile.CanUseBottomLightOffDelay)
+ {
+ var waitTime = (decimal)SlicerFile.GetBottomWaitTimeBeforeCure();
+ if (waitTime < _minimumWaitTimeBeforeCure ||
+ waitTime > _maximumWaitTimeBeforeCure) return false;
+ }
+ if (SlicerFile.CanUseWaitTimeAfterCure || SlicerFile.CanUseLightOffDelay)
+ {
+ var waitTime = (decimal)SlicerFile.GetNormalWaitTimeBeforeCure();
+ if (waitTime < _minimumWaitTimeBeforeCure ||
+ waitTime > _maximumWaitTimeBeforeCure) return false;
+ }
+
+ if (SlicerFile.CanUseLayerWaitTimeAfterCure || SlicerFile.CanUseLayerLightOffDelay)
+ {
+ foreach (var layer in SlicerFile)
+ {
+ if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers
+ var waitTime = (decimal)layer.GetBottomWaitTimeBeforeCure();
+ if (waitTime < _minimumWaitTimeBeforeCure || waitTime > _maximumWaitTimeBeforeCure) return false;
+ }
+ }
+
+ break;
+ case SuggestionApplyWhen.Different:
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure || SlicerFile.CanUseBottomLightOffDelay)
+ {
+ if (Math.Abs(SlicerFile.GetBottomWaitTimeBeforeCure() - CalculateWaitTime(true)) > 0.1) return false;
+ }
+ if (SlicerFile.CanUseWaitTimeAfterCure || SlicerFile.CanUseLightOffDelay)
+ {
+ if (Math.Abs(SlicerFile.GetNormalWaitTimeBeforeCure() - CalculateWaitTime(false)) > 0.1) return false;
+ }
+
+ if (SlicerFile.CanUseLayerWaitTimeAfterCure || SlicerFile.CanUseLayerLightOffDelay)
+ {
+ foreach (var layer in SlicerFile)
+ {
+ if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers
+ if (Math.Abs(layer.GetBottomWaitTimeBeforeCure() - CalculateWaitTime(layer)) > 0.1) return false;
+ }
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return true;
+ }
+ }
+
+ public override string Title => "Wait time before cure";
+ public override string Description => "Rest some time before cure the layer is crucial to let the resin settle after the lift sequence and allow some time for the arm settle at the correct Z position as the resin will offer some resistance and push the structure.\n" +
+ "This lead to better quality with more successful prints, less lamination problems, better first layers with more success of stick to the build plate and less elephant foot effect.";
+
+ public override string Message => IsApplied
+ ? $"{GlobalAppliedMessage}: {SlicerFile.GetBottomWaitTimeBeforeCure()}/{SlicerFile.GetNormalWaitTimeBeforeCure()}s"
+ : $"{GlobalNotAppliedMessage} of {SlicerFile.GetBottomWaitTimeBeforeCure()}/{SlicerFile.GetNormalWaitTimeBeforeCure()}s " +
+ $"is out of the recommended {CalculateWaitTime(true)}/{CalculateWaitTime(false)}s";
+
+ public override string ToolTip => (_setType == SuggestionWaitTimeBeforeCureSetType.Fixed
+ ? $"The recommended wait time must be {_fixedBottomWaitTimeBeforeCure}/{_fixedWaitTimeBeforeCure}s"
+ : $"The recommended wait time is a ratio of (wait time){_proportionalWaitTimeBeforeCure}s to (exposure time){_proportionalLayerArea}s") +
+ $" constrained from [Bottoms={_minimumBottomWaitTimeBeforeCure}/{_maximumBottomWaitTimeBeforeCure}s] and [Normals={_minimumWaitTimeBeforeCure}/{_maximumWaitTimeBeforeCure}s].\n" +
+ $"Explanation: {Description}";
+
+ public override string? ConfirmationMessage => $"{Title}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s » {CalculateWaitTime(true)}/{CalculateWaitTime(false)}s";
+
+ public override string? InformationUrl => "https://blog.honzamrazek.cz/2022/01/prints-not-sticking-to-the-build-plate-layer-separation-rough-surface-on-a-resin-printer-resin-viscosity-the-common-denominator";
+
+ public SuggestionWaitTimeBeforeCureSetType SetType
+ {
+ get => _setType;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _setType, value)) return;
+ RaisePropertyChanged(nameof(IsSetTypeFixed));
+ RaisePropertyChanged(nameof(IsSetTypeProportionalLayerPixels));
+ RaisePropertyChanged(nameof(IsSetTypeProportionalLayerArea));
+ }
+ }
+
+ public bool IsSetTypeFixed => _setType == SuggestionWaitTimeBeforeCureSetType.Fixed;
+ public bool IsSetTypeProportionalLayerPixels => _setType == SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels;
+ public bool IsSetTypeProportionalLayerArea => _setType == SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea;
+
+ public decimal BottomHeight
+ {
+ get => _bottomHeight;
+ set => RaiseAndSetIfChanged(ref _bottomHeight, value);
+ }
+
+ public decimal FixedBottomWaitTimeBeforeCure
+ {
+ get => _fixedBottomWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _fixedBottomWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+ public decimal FixedWaitTimeBeforeCure
+ {
+ get => _fixedWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _fixedWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+ public decimal ProportionalBottomWaitTimeBeforeCure
+ {
+ get => _proportionalBottomWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _proportionalBottomWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+ public decimal ProportionalWaitTimeBeforeCure
+ {
+ get => _proportionalWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _proportionalWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+
+ public uint ProportionalBottomLayerPixels
+ {
+ get => _proportionalBottomLayerPixels;
+ set => RaiseAndSetIfChanged(ref _proportionalBottomLayerPixels, Math.Max(1, value));
+ }
+
+ public uint ProportionalLayerPixels
+ {
+ get => _proportionalLayerPixels;
+ set => RaiseAndSetIfChanged(ref _proportionalLayerPixels, Math.Max(1, value));
+ }
+
+ public uint ProportionalBottomLayerArea
+ {
+ get => _proportionalBottomLayerArea;
+ set => RaiseAndSetIfChanged(ref _proportionalBottomLayerArea, Math.Max(1, value));
+ }
+
+ public uint ProportionalLayerArea
+ {
+ get => _proportionalLayerArea;
+ set => RaiseAndSetIfChanged(ref _proportionalLayerArea, Math.Max(1, value));
+ }
+
+ public decimal MinimumBottomWaitTimeBeforeCure
+ {
+ get => _minimumBottomWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _minimumBottomWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+ public decimal MinimumWaitTimeBeforeCure
+ {
+ get => _minimumWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _minimumWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+ public decimal MaximumBottomWaitTimeBeforeCure
+ {
+ get => _maximumBottomWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _maximumBottomWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+ public decimal MaximumWaitTimeBeforeCure
+ {
+ get => _maximumWaitTimeBeforeCure;
+ set => RaiseAndSetIfChanged(ref _maximumWaitTimeBeforeCure, Math.Round(value, 2));
+ }
+
+ public bool CreateEmptyFirstLayer
+ {
+ get => _createEmptyFirstLayer;
+ set => RaiseAndSetIfChanged(ref _createEmptyFirstLayer, value);
+ }
+
+ #endregion
+
+ #region Constructor
+
+ public SuggestionWaitTimeBeforeCure()
+ {
+ _applyWhen = SuggestionApplyWhen.Different;
+ }
+
+ #endregion
+
+ #region Overrides
+
+ public override string? Validate()
+ {
+ var sb = new StringBuilder();
+
+ if (_minimumBottomWaitTimeBeforeCure > _maximumBottomWaitTimeBeforeCure)
+ {
+ sb.AppendLine("Minimum bottom limit can't be higher than maximum bottom limit");
+ }
+
+ if (_minimumWaitTimeBeforeCure > _maximumWaitTimeBeforeCure)
+ {
+ sb.AppendLine("Minimum normal limit can't be higher than maximum normal limit");
+ }
+
+ if (_setType is SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels
+ or SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea)
+ {
+ if (_proportionalWaitTimeBeforeCure <= 0)
+ {
+ sb.AppendLine("The proportional wait time must be higher than 0s");
+ }
+ }
+
+ switch (_setType)
+ {
+ case SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels:
+ {
+ if (_proportionalBottomLayerPixels <= 0)
+ {
+ sb.AppendLine("The bottom proportional pixels must be higher than 0");
+ }
+
+ if (_proportionalLayerPixels <= 0)
+ {
+ sb.AppendLine("The proportional pixels must be higher than 0");
+ }
+
+ break;
+ }
+ case SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea:
+ {
+ if (_proportionalBottomLayerArea <= 0)
+ {
+ sb.AppendLine("The bottom proportional layer area must be higher than 0mm²");
+ }
+
+ if (_proportionalLayerArea <= 0)
+ {
+ sb.AppendLine("The proportional layer area must be higher than 0mm²");
+ }
+
+ break;
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally()
+ {
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure || SlicerFile.CanUseBottomLightOffDelay)
+ {
+ SlicerFile.SetBottomWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(true));
+ }
+ if (SlicerFile.CanUseWaitTimeAfterCure || SlicerFile.CanUseLightOffDelay)
+ {
+ SlicerFile.SetNormalWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(false));
+ }
+
+ if (SlicerFile.CanUseLayerWaitTimeAfterCure || SlicerFile.CanUseLayerLightOffDelay)
+ {
+ foreach (var layer in SlicerFile)
+ {
+ if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers
+ layer.SetWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(layer));
+ }
+ }
+
+ if (_createEmptyFirstLayer && SlicerFile.CanUseLayerPositionZ && !SlicerFile.SupportsGCode)
+ {
+ var firstLayer = SlicerFile.FirstLayer!;
+ if (firstLayer.NonZeroPixelCount > 1) // First layer is not blank as it seems, lets create one
+ {
+ firstLayer = firstLayer.Clone();
+ using var mat = SlicerFile.CreateMatWithDummyPixel();
+ firstLayer.LayerMat = mat;
+ firstLayer.ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.01f;
+ //firstLayer.LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f;
+ SlicerFile.FirstLayer!.LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f; // Already on position, try to not lift
+ firstLayer.SetNoDelays();
+ SlicerFile.SuppressRebuildPropertiesWork(() => { SlicerFile.Prepend(firstLayer); });
+ }
+
+ }
+
+ return true;
+ }
+
+
+ public float CalculateWaitTime(bool isBottomLayer, Layer? layer = null)
+ {
+ if (layer is not null)
+ {
+ // Reassign isBottomLayer given the layer
+ isBottomLayer = layer.IsBottomLayer || (_bottomHeight > 0 && (decimal)layer.PositionZ <= _bottomHeight);
+ }
+
+ if (layer is null || _setType == SuggestionWaitTimeBeforeCureSetType.Fixed)
+ {
+ return (float)(isBottomLayer ? _fixedBottomWaitTimeBeforeCure : _fixedWaitTimeBeforeCure);
+ }
+
+ if (layer.NonZeroPixelCount <= 1) return 0; // Empty layer, don't need wait time
+
+ return _setType switch
+ {
+ SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels => (float)Math.Round(
+ (isBottomLayer
+ ? layer.NonZeroPixelCount * _proportionalBottomWaitTimeBeforeCure / _proportionalBottomLayerPixels
+ : layer.NonZeroPixelCount * _proportionalWaitTimeBeforeCure / _proportionalLayerPixels).Clamp(
+ isBottomLayer ? _minimumBottomWaitTimeBeforeCure : _minimumWaitTimeBeforeCure,
+ isBottomLayer ? _maximumBottomWaitTimeBeforeCure : _maximumWaitTimeBeforeCure), 2),
+ SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea => (float) Math.Round(
+ (isBottomLayer
+ ? (decimal)layer.GetArea() * _proportionalBottomWaitTimeBeforeCure / _proportionalBottomLayerArea
+ : (decimal)layer.GetArea() * _proportionalWaitTimeBeforeCure / _proportionalLayerArea).Clamp(
+ isBottomLayer ? _minimumBottomWaitTimeBeforeCure : _minimumWaitTimeBeforeCure,
+ isBottomLayer ? _maximumBottomWaitTimeBeforeCure : _maximumWaitTimeBeforeCure), 2),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ }
+
+ public float CalculateWaitTime(Layer layer) => CalculateWaitTime(false, layer);
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/SystemOS/SystemAware.cs b/UVtools.Core/SystemOS/SystemAware.cs
index f14b2bc..b4b0e9c 100644
--- a/UVtools.Core/SystemOS/SystemAware.cs
+++ b/UVtools.Core/SystemOS/SystemAware.cs
@@ -6,268 +6,269 @@
* of this license document, but changing it is not allowed.
*/
+using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
-using Microsoft.Win32;
-namespace UVtools.Core.SystemOS
+namespace UVtools.Core.SystemOS;
+
+public static class SystemAware
{
- public static class SystemAware
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public class MEMORYSTATUSEX
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- public class MEMORYSTATUSEX
+ public uint dwLength;
+ public uint dwMemoryLoad;
+ public ulong ullTotalPhys;
+ public ulong ullAvailPhys;
+ public ulong ullTotalPageFile;
+ public ulong ullAvailPageFile;
+ public ulong ullTotalVirtual;
+ public ulong ullAvailVirtual;
+ public ulong ullAvailExtendedVirtual;
+ public MEMORYSTATUSEX()
{
- public uint dwLength;
- public uint dwMemoryLoad;
- public ulong ullTotalPhys;
- public ulong ullAvailPhys;
- public ulong ullTotalPageFile;
- public ulong ullAvailPageFile;
- public ulong ullTotalVirtual;
- public ulong ullAvailVirtual;
- public ulong ullAvailExtendedVirtual;
- public MEMORYSTATUSEX()
- {
- dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
- }
+ dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
}
+ }
- [return: MarshalAs(UnmanagedType.Bool)]
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer); //Used to use ref with comment below
- // but ref doesn't work.(Use of [In, Out] instead of ref
- //causes access violation exception on windows xp
- //comment: most probably caused by MEMORYSTATUSEX being declared as a class
- //(at least at pinvoke.net). On Win7, ref and struct work.
+ [return: MarshalAs(UnmanagedType.Bool)]
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer); //Used to use ref with comment below
+ // but ref doesn't work.(Use of [In, Out] instead of ref
+ //causes access violation exception on windows xp
+ //comment: most probably caused by MEMORYSTATUSEX being declared as a class
+ //(at least at pinvoke.net). On Win7, ref and struct work.
- // Alternate Version Using "ref," And Works With Alternate Code Below.
- // Also See Alternate Version Of [MEMORYSTATUSEX] Structure With
- // Fields Documented.
- [return: MarshalAs(UnmanagedType.Bool)]
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint = "GlobalMemoryStatusEx", SetLastError = true)]
- static extern bool _GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
+ // Alternate Version Using "ref," And Works With Alternate Code Below.
+ // Also See Alternate Version Of [MEMORYSTATUSEX] Structure With
+ // Fields Documented.
+ [return: MarshalAs(UnmanagedType.Bool)]
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint = "GlobalMemoryStatusEx", SetLastError = true)]
+ static extern bool _GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
- public static MEMORYSTATUSEX GetMemoryStatus()
+ public static MEMORYSTATUSEX GetMemoryStatus()
+ {
+ var statEX = new MEMORYSTATUSEX();
+ if (OperatingSystem.IsWindows())
{
- var statEX = new MEMORYSTATUSEX();
- if (OperatingSystem.IsWindows())
- {
- GlobalMemoryStatusEx(statEX);
- }
- else if (OperatingSystem.IsLinux())
+ GlobalMemoryStatusEx(statEX);
+ }
+ else if (OperatingSystem.IsLinux())
+ {
+ if (!File.Exists("/proc/meminfo")) return statEX;
+ try
{
- if (!File.Exists("/proc/meminfo")) return statEX;
- try
+ const ushort factor = 1024;
+ var result = File.ReadAllText("/proc/meminfo");
+ //var result = "MemTotal: 8288440 kB\nMemFree: 5616380 kB\nMemAvailable: 6885408 kB\nBuffers: 63240 kB\nCached: 1390996 kB\nSwapCached: 0 kB\nActive: 272516 kB\nInactive: 1773312 kB\nActive(anon): 1888 kB\nInactive(anon): 596168 kB\nActive(file): 270628 kB\nInactive(file): 1177144 kB\nUnevictable: 0 kB\nMlocked: 0 kB\nSwapTotal: 2097148 kB\nSwapFree: 2097148 kB\nDirty: 172 kB\nWriteback: 0 kB\nAnonPages: 591608 kB\nMapped: 292288 kB\nShmem: 6464 kB\nKReclaimable: 86228 kB\nSlab: 170544 kB\nSReclaimable: 86228 kB\nSUnreclaim: 84316 kB\nKernelStack: 12160 kB\nPageTables: 13992 kB\nNFS_Unstable: 0 kB\nBounce: 0 kB\nWritebackTmp: 0 kB\nCommitLimit: 6241368 kB\nCommitted_AS: 3588500 kB\nVmallocTotal: 34359738367 kB\nVmallocUsed: 61060 kB\nVmallocChunk: 0 kB\nPercpu: 91136 kB\nHardwareCorrupted: 0 kB\nAnonHugePages: 0 kB\nShmemHugePages: 0 kB\nShmemPmdMapped: 0 kB\nFileHugePages: 0 kB\nFilePmdMapped: 0 kB\nHugePages_Total: 0\nHugePages_Free: 0\nHugePages_Rsvd: 0\nHugePages_Surp: 0\nHugepagesize: 2048 kB\nHugetlb: 0 kB\nDirectMap4k: 216464 kB\nDirectMap2M: 4169728 kB\nDirectMap1G: 5242880 kB";
+ var matches = Regex.Matches(result, @"(\S+):\s*(\d+).(\S+)");
+ foreach (Match match in matches)
{
- const ushort factor = 1024;
- var result = File.ReadAllText("/proc/meminfo");
- //var result = "MemTotal: 8288440 kB\nMemFree: 5616380 kB\nMemAvailable: 6885408 kB\nBuffers: 63240 kB\nCached: 1390996 kB\nSwapCached: 0 kB\nActive: 272516 kB\nInactive: 1773312 kB\nActive(anon): 1888 kB\nInactive(anon): 596168 kB\nActive(file): 270628 kB\nInactive(file): 1177144 kB\nUnevictable: 0 kB\nMlocked: 0 kB\nSwapTotal: 2097148 kB\nSwapFree: 2097148 kB\nDirty: 172 kB\nWriteback: 0 kB\nAnonPages: 591608 kB\nMapped: 292288 kB\nShmem: 6464 kB\nKReclaimable: 86228 kB\nSlab: 170544 kB\nSReclaimable: 86228 kB\nSUnreclaim: 84316 kB\nKernelStack: 12160 kB\nPageTables: 13992 kB\nNFS_Unstable: 0 kB\nBounce: 0 kB\nWritebackTmp: 0 kB\nCommitLimit: 6241368 kB\nCommitted_AS: 3588500 kB\nVmallocTotal: 34359738367 kB\nVmallocUsed: 61060 kB\nVmallocChunk: 0 kB\nPercpu: 91136 kB\nHardwareCorrupted: 0 kB\nAnonHugePages: 0 kB\nShmemHugePages: 0 kB\nShmemPmdMapped: 0 kB\nFileHugePages: 0 kB\nFilePmdMapped: 0 kB\nHugePages_Total: 0\nHugePages_Free: 0\nHugePages_Rsvd: 0\nHugePages_Surp: 0\nHugepagesize: 2048 kB\nHugetlb: 0 kB\nDirectMap4k: 216464 kB\nDirectMap2M: 4169728 kB\nDirectMap1G: 5242880 kB";
- var matches = Regex.Matches(result, @"(\S+):\s*(\d+).(\S+)");
- foreach (Match match in matches)
+ if (!match.Success || match.Groups.Count < 2) continue;
+ ulong value = ulong.Parse(match.Groups[2].Value) * factor;
+ switch (match.Groups[1].Value)
{
- if (!match.Success || match.Groups.Count < 2) continue;
- ulong value = ulong.Parse(match.Groups[2].Value) * factor;
- switch (match.Groups[1].Value)
- {
- case "MemTotal":
- statEX.ullTotalPhys = value;
- statEX.ullTotalVirtual = value;
- continue;
- case "MemFree":
- statEX.ullAvailPhys = value;
- continue;
- case "MemAvailable":
- statEX.ullAvailVirtual = value;
- continue;
- case "SwapTotal":
- statEX.ullTotalPageFile = value;
- continue;
- case "SwapFree":
- statEX.ullAvailPageFile = value;
- continue;
- }
+ case "MemTotal":
+ statEX.ullTotalPhys = value;
+ statEX.ullTotalVirtual = value;
+ continue;
+ case "MemFree":
+ statEX.ullAvailPhys = value;
+ continue;
+ case "MemAvailable":
+ statEX.ullAvailVirtual = value;
+ continue;
+ case "SwapTotal":
+ statEX.ullTotalPageFile = value;
+ continue;
+ case "SwapFree":
+ statEX.ullAvailPageFile = value;
+ continue;
}
}
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
}
- else if(OperatingSystem.IsMacOS())
+ catch (Exception e)
{
- try
- {
- var result = GetProcessOutput("vm_stat");
-
- if (string.IsNullOrWhiteSpace(result)) return statEX;
-
- //var result = "Mach Virtual Memory Statistics: (page size of 4096 bytes)\nPages free: 3044485.\nPages active: 400375.\nPages inactive: 235679.\nPages speculative: 189311.\nPages throttled: 0.\nPages wired down: 324269.\nPages purgeable: 27417.\n\"Translation faults\": 5500903.\nPages copy-on-write: 388354.\nPages zero filled: 2724856.\nPages reactivated: 410.\nPages purged: 972.\nFile-backed pages: 400847.\nAnonymous pages: 424518.\nPages stored in compressor: 0.\nPages occupied by compressor: 0.\nDecompressions: 0.\nCompressions: 0.\nPageins: 354428.\nPageouts: 0.\nSwapins: 0.\nSwapouts: 0.";
- var matchPageSize = Regex.Match(result, @"page size of (\d+) bytes");
-
- if (!matchPageSize.Success || matchPageSize.Groups.Count < 2) return statEX;
- ushort pageSize = ushort.Parse(matchPageSize.Groups[1].Value);
-
- var matches = Regex.Matches(result, @"(.*):\s*(\d+)");
- foreach (Match match in matches)
- {
- if (!match.Success || match.Groups.Count < 2) continue;
+ Debug.WriteLine(e);
+ }
+ }
+ else if(OperatingSystem.IsMacOS())
+ {
+ try
+ {
+ var result = GetProcessOutput("vm_stat");
- ulong value = ulong.Parse(match.Groups[2].Value) * pageSize;
+ if (string.IsNullOrWhiteSpace(result)) return statEX;
- if (match.Groups[1].Value.StartsWith("\"Translation faults\"")) break;
- if (match.Groups[1].Value.StartsWith("Pages "))
- {
- statEX.ullTotalPhys += value;
- }
+ //var result = "Mach Virtual Memory Statistics: (page size of 4096 bytes)\nPages free: 3044485.\nPages active: 400375.\nPages inactive: 235679.\nPages speculative: 189311.\nPages throttled: 0.\nPages wired down: 324269.\nPages purgeable: 27417.\n\"Translation faults\": 5500903.\nPages copy-on-write: 388354.\nPages zero filled: 2724856.\nPages reactivated: 410.\nPages purged: 972.\nFile-backed pages: 400847.\nAnonymous pages: 424518.\nPages stored in compressor: 0.\nPages occupied by compressor: 0.\nDecompressions: 0.\nCompressions: 0.\nPageins: 354428.\nPageouts: 0.\nSwapins: 0.\nSwapouts: 0.";
+ var matchPageSize = Regex.Match(result, @"page size of (\d+) bytes");
- switch (match.Groups[1].Value)
- {
- case "Pages free":
- case "Pages inactive":
- case "Pages purgeable":
- case "Pages throttled":
- case "Pages speculative":
- statEX.ullAvailPhys += value;
- continue;
- }
- }
+ if (!matchPageSize.Success || matchPageSize.Groups.Count < 2) return statEX;
+ ushort pageSize = ushort.Parse(matchPageSize.Groups[1].Value);
- statEX.ullTotalVirtual = statEX.ullTotalPhys;
- statEX.ullAvailVirtual = statEX.ullAvailPhys;
- }
- catch (Exception e)
+ var matches = Regex.Matches(result, @"(.*):\s*(\d+)");
+ foreach (Match match in matches)
{
- Debug.WriteLine(e);
- }
- }
+ if (!match.Success || match.Groups.Count < 2) continue;
- return statEX;
- }
+ ulong value = ulong.Parse(match.Groups[2].Value) * pageSize;
- public static string GetProcessorName()
- {
- try
- {
- if (OperatingSystem.IsWindows())
- {
- /*ManagementObjectSearcher mos =
- new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor");
- foreach (ManagementObject mo in mos.Get())
+ if (match.Groups[1].Value.StartsWith("\"Translation faults\"")) break;
+ if (match.Groups[1].Value.StartsWith("Pages "))
{
- Console.WriteLine(mo["Name"]);
- }*/
- using var key = Registry.LocalMachine.OpenSubKey(@"HARDWARE\DESCRIPTION\System\CentralProcessor\0");
- return key?.GetValue("ProcessorNameString")?.ToString();
- }
+ statEX.ullTotalPhys += value;
+ }
- if (OperatingSystem.IsLinux())
- {
- if (!File.Exists("/proc/cpuinfo")) return null;
- //var result = "processor\t: 0\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 0\ncpu cores\t: 6\napicid\t\t: 0\ninitial apicid\t: 0\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 1\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 1\ncpu cores\t: 6\napicid\t\t: 1\ninitial apicid\t: 1\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 2\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 2\ncpu cores\t: 6\napicid\t\t: 2\ninitial apicid\t: 2\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 3\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 3\ncpu cores\t: 6\napicid\t\t: 3\ninitial apicid\t: 3\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 4\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 4\ncpu cores\t: 6\napicid\t\t: 4\ninitial apicid\t: 4\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 5\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 5\ncpu cores\t: 6\napicid\t\t: 5\ninitial apicid\t: 5\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:";
- var result = File.ReadAllText("/proc/cpuinfo");
- var match = Regex.Match(result, @"model name.*:.(.*)");
- if (!match.Success || match.Groups.Count < 2)
+ switch (match.Groups[1].Value)
{
- return null;
+ case "Pages free":
+ case "Pages inactive":
+ case "Pages purgeable":
+ case "Pages throttled":
+ case "Pages speculative":
+ statEX.ullAvailPhys += value;
+ continue;
}
-
- return match.Groups[1].ToString();
}
- if (OperatingSystem.IsMacOS())
- {
- return GetProcessOutput("sysctl", "-n machdep.cpu.brand_string")?.TrimEnd('\r', '\n');
- }
+ statEX.ullTotalVirtual = statEX.ullTotalPhys;
+ statEX.ullAvailVirtual = statEX.ullAvailPhys;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
-
- return null;
}
- public static bool SelectFileOnExplorer(string filePath)
+ return statEX;
+ }
+
+ public static string? GetProcessorName()
+ {
+ try
{
- if (!File.Exists(filePath))
+ if (OperatingSystem.IsWindows())
{
- return false;
+ /*ManagementObjectSearcher mos =
+ new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor");
+ foreach (ManagementObject mo in mos.Get())
+ {
+ Console.WriteLine(mo["Name"]);
+ }*/
+ using var key = Registry.LocalMachine.OpenSubKey(@"HARDWARE\DESCRIPTION\System\CentralProcessor\0");
+ return key?.GetValue("ProcessorNameString")?.ToString();
}
- if (OperatingSystem.IsWindows())
+ if (OperatingSystem.IsLinux())
{
- StartProcess("explorer.exe", $"/select,\"{filePath}\"");
+ if (!File.Exists("/proc/cpuinfo")) return null;
+ //var result = "processor\t: 0\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 0\ncpu cores\t: 6\napicid\t\t: 0\ninitial apicid\t: 0\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 1\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 1\ncpu cores\t: 6\napicid\t\t: 1\ninitial apicid\t: 1\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 2\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 2\ncpu cores\t: 6\napicid\t\t: 2\ninitial apicid\t: 2\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 3\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 3\ncpu cores\t: 6\napicid\t\t: 3\ninitial apicid\t: 3\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 4\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 4\ncpu cores\t: 6\napicid\t\t: 4\ninitial apicid\t: 4\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:\n\nprocessor\t: 5\nvendor_id\t: GenuineIntel\ncpu family\t: 6\nmodel\t\t: 158\nmodel name\t: Intel(R) Core(TM) i9-9900K CPU @ 3.60GHz\nstepping\t: 12\nmicrocode\t: 0xffffffff\ncpu MHz\t\t: 3600.011\ncache size\t: 16384 KB\nphysical id\t: 0\nsiblings\t: 6\ncore id\t\t: 5\ncpu cores\t: 6\napicid\t\t: 5\ninitial apicid\t: 5\nfpu\t\t: yes\nfpu_exception\t: yes\ncpuid level\t: 22\nwp\t\t: yes\nflags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat flush_l1d arch_capabilities\nbugs\t\t: spectre_v1 spectre_v2 spec_store_bypass mds swapgs itlb_multihit srbds\nbogomips\t: 7200.02\nclflush size\t: 64\ncache_alignment\t: 64\naddress sizes\t: 45 bits physical, 48 bits virtual\npower management:";
+ var result = File.ReadAllText("/proc/cpuinfo");
+ var match = Regex.Match(result, @"model name.*:.(.*)");
+ if (!match.Success || match.Groups.Count < 2)
+ {
+ return null;
+ }
+
+ return match.Groups[1].ToString();
}
- else
+
+ if (OperatingSystem.IsMacOS())
{
- StartProcess(Path.GetDirectoryName(filePath));
+ return GetProcessOutput("sysctl", "-n machdep.cpu.brand_string")?.TrimEnd('\r', '\n');
}
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+
+ return null;
+ }
- return true;
+ public static bool SelectFileOnExplorer(string filePath)
+ {
+ if (!File.Exists(filePath))
+ {
+ return false;
}
- public static void OpenBrowser(string url)
+ if (OperatingSystem.IsWindows())
{
- try
+ StartProcess("explorer.exe", $"/select,\"{filePath}\"");
+ }
+ else
+ {
+ var path = Path.GetDirectoryName(filePath);
+ if (path is null) return false;
+ StartProcess(path);
+ }
+
+ return true;
+ }
+
+ public static void OpenBrowser(string url)
+ {
+ try
+ {
+ if (OperatingSystem.IsWindows())
{
- if (OperatingSystem.IsWindows())
- {
- using (Process.Start(new ProcessStartInfo(url) { UseShellExecute = true })) {}
- }
- else if (OperatingSystem.IsLinux())
- {
- using (Process.Start("xdg-open", url)) {}
- }
- else if (OperatingSystem.IsMacOS())
- {
- using (Process.Start("open", url)) {}
- }
- else
- {
- // throw
- }
+ using (Process.Start(new ProcessStartInfo(url) { UseShellExecute = true })) {}
}
- catch (Exception e)
+ else if (OperatingSystem.IsLinux())
{
- Debug.WriteLine(e);
+ using (Process.Start("xdg-open", url)) {}
}
-
- }
-
- public static void StartProcess(string name, string arguments = null)
- {
- try
+ else if (OperatingSystem.IsMacOS())
{
- using (Process.Start(new ProcessStartInfo(name, arguments!) { UseShellExecute = true })) { }
+ using (Process.Start("open", url)) {}
}
- catch (Exception e)
+ else
{
- Debug.WriteLine(e);
+ // throw
}
}
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
- public static string GetProcessOutput(string filename, string arguments = null)
+ }
+
+ public static void StartProcess(string name, string? arguments = null)
+ {
+ try
{
- using var proc = Process.Start(new ProcessStartInfo
- {
- FileName = filename,
- Arguments = arguments,
- RedirectStandardOutput = true,
- UseShellExecute = false,
- });
- if (proc is null) return null;
- var stringBuilder = new StringBuilder();
- while (!proc.HasExited)
- {
- stringBuilder.Append(proc.StandardOutput.ReadToEnd());
- }
+ using (Process.Start(new ProcessStartInfo(name, arguments!) { UseShellExecute = true })) { }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+ }
- return stringBuilder.ToString();
+ public static string? GetProcessOutput(string filename, string? arguments = null)
+ {
+ using var proc = Process.Start(new ProcessStartInfo
+ {
+ FileName = filename,
+ Arguments = arguments,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ });
+ if (proc is null) return null;
+ var stringBuilder = new StringBuilder();
+ while (!proc.HasExited)
+ {
+ stringBuilder.Append(proc.StandardOutput.ReadToEnd());
}
+ return stringBuilder.ToString();
}
-}
+
+} \ No newline at end of file
diff --git a/UVtools.Core/SystemOS/Windows/USB.cs b/UVtools.Core/SystemOS/Windows/USB.cs
index 9467943..cc2d2e0 100644
--- a/UVtools.Core/SystemOS/Windows/USB.cs
+++ b/UVtools.Core/SystemOS/Windows/USB.cs
@@ -10,129 +10,128 @@ using System;
using System.Runtime.InteropServices;
using System.Threading;
-namespace UVtools.Core.SystemOS.Windows
+namespace UVtools.Core.SystemOS.Windows;
+
+public static class USB
{
- public static class USB
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ private static extern IntPtr CreateFile(
+ string lpFileName,
+ uint dwDesiredAccess,
+ uint dwShareMode,
+ IntPtr SecurityAttributes,
+ uint dwCreationDisposition,
+ uint dwFlagsAndAttributes,
+ IntPtr hTemplateFile
+ );
+
+ [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
+ private static extern bool DeviceIoControl(
+ IntPtr hDevice,
+ uint dwIoControlCode,
+ IntPtr lpInBuffer,
+ uint nInBufferSize,
+ IntPtr lpOutBuffer,
+ uint nOutBufferSize,
+ out uint lpBytesReturned,
+ IntPtr lpOverlapped
+ );
+
+ [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
+ private static extern bool DeviceIoControl(
+ IntPtr hDevice,
+ uint dwIoControlCode,
+ byte[] lpInBuffer,
+ uint nInBufferSize,
+ IntPtr lpOutBuffer,
+ uint nOutBufferSize,
+ out uint lpBytesReturned,
+ IntPtr lpOverlapped
+ );
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool CloseHandle(IntPtr hObject);
+
+ const uint GENERIC_READ = 0x80000000;
+ const uint GENERIC_WRITE = 0x40000000;
+ const int FILE_SHARE_READ = 0x1;
+ const int FILE_SHARE_WRITE = 0x2;
+ const int FSCTL_LOCK_VOLUME = 0x00090018;
+ const int FSCTL_DISMOUNT_VOLUME = 0x00090020;
+ const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
+ const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804;
+
+ /// <summary>
+ /// Get the USB handler
+ /// </summary>
+ /// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>
+ public static IntPtr GetUSBHandler(string driveLetter)
{
- [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- private static extern IntPtr CreateFile(
- string lpFileName,
- uint dwDesiredAccess,
- uint dwShareMode,
- IntPtr SecurityAttributes,
- uint dwCreationDisposition,
- uint dwFlagsAndAttributes,
- IntPtr hTemplateFile
- );
-
- [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
- private static extern bool DeviceIoControl(
- IntPtr hDevice,
- uint dwIoControlCode,
- IntPtr lpInBuffer,
- uint nInBufferSize,
- IntPtr lpOutBuffer,
- uint nOutBufferSize,
- out uint lpBytesReturned,
- IntPtr lpOverlapped
- );
-
- [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
- private static extern bool DeviceIoControl(
- IntPtr hDevice,
- uint dwIoControlCode,
- byte[] lpInBuffer,
- uint nInBufferSize,
- IntPtr lpOutBuffer,
- uint nOutBufferSize,
- out uint lpBytesReturned,
- IntPtr lpOverlapped
- );
-
- [DllImport("kernel32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool CloseHandle(IntPtr hObject);
-
- const uint GENERIC_READ = 0x80000000;
- const uint GENERIC_WRITE = 0x40000000;
- const int FILE_SHARE_READ = 0x1;
- const int FILE_SHARE_WRITE = 0x2;
- const int FSCTL_LOCK_VOLUME = 0x00090018;
- const int FSCTL_DISMOUNT_VOLUME = 0x00090020;
- const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
- const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804;
-
- /// <summary>
- /// Get the USB handler
- /// </summary>
- /// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>
- public static IntPtr GetUSBHandler(string driveLetter)
- {
- var filename = @"\\.\" + driveLetter[0] + ":";
- return CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);
- }
+ var filename = @"\\.\" + driveLetter[0] + ":";
+ return CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);
+ }
- /// <summary>
- /// Eject an USB drive
- /// </summary>
- /// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>
- public static bool USBEject(string driveLetter)
- {
- return Eject(GetUSBHandler(driveLetter));
- }
+ /// <summary>
+ /// Eject an USB drive
+ /// </summary>
+ /// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>
+ public static bool USBEject(string driveLetter)
+ {
+ return Eject(GetUSBHandler(driveLetter));
+ }
- public static bool Eject(IntPtr handle)
- {
- var result = false;
+ public static bool Eject(IntPtr handle)
+ {
+ var result = false;
- if (LockVolume(handle) && DismountVolume(handle))
- {
- PreventRemovalOfVolume(handle, false);
- result = AutoEjectVolume(handle);
- }
- CloseHandle(handle);
- return result;
+ if (LockVolume(handle) && DismountVolume(handle))
+ {
+ PreventRemovalOfVolume(handle, false);
+ result = AutoEjectVolume(handle);
}
+ CloseHandle(handle);
+ return result;
+ }
- private static bool LockVolume(IntPtr handle, uint attempts = 10)
- {
- uint byteReturned;
+ private static bool LockVolume(IntPtr handle, uint attempts = 10)
+ {
+ uint byteReturned;
- for (uint i = 0; i < attempts; i++)
+ for (uint i = 0; i < attempts; i++)
+ {
+ if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero))
{
- if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero))
- {
- return true;
- }
- Thread.Sleep(500);
+ return true;
}
- return false;
+ Thread.Sleep(500);
}
+ return false;
+ }
- private static bool PreventRemovalOfVolume(IntPtr handle, bool prevent)
- {
- byte[] buf = new byte[1];
- uint retVal;
+ private static bool PreventRemovalOfVolume(IntPtr handle, bool prevent)
+ {
+ byte[] buf = new byte[1];
+ uint retVal;
- buf[0] = (prevent) ? (byte)1 : (byte)0;
- return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, buf, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
- }
+ buf[0] = (prevent) ? (byte)1 : (byte)0;
+ return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, buf, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
+ }
- private static bool DismountVolume(IntPtr handle)
- {
- uint byteReturned;
- return DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
- }
+ private static bool DismountVolume(IntPtr handle)
+ {
+ uint byteReturned;
+ return DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
+ }
- private static bool AutoEjectVolume(IntPtr handle)
- {
- uint byteReturned;
- return DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
- }
+ private static bool AutoEjectVolume(IntPtr handle)
+ {
+ uint byteReturned;
+ return DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
+ }
- private static bool CloseVolume(IntPtr handle)
- {
- return CloseHandle(handle);
- }
+ private static bool CloseVolume(IntPtr handle)
+ {
+ return CloseHandle(handle);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 22ca56b..c979c0c 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Company>PTRTECH</Company>
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description>
- <Version>2.29.0</Version>
+ <Version>3.0.0</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
@@ -19,6 +19,7 @@
<PackageTags>msla, dlp, resin, printer, slicer, 3d printing, image processing, layers</PackageTags>
<ApplicationIcon>UVtools.ico</ApplicationIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -63,11 +64,9 @@
<PackageReference Include="Emgu.CV" Version="4.5.5.4823" />
<PackageReference Include="KdTree" Version="1.4.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.1.0" />
- <PackageReference Include="Microsoft.Win32.Registry" Version="6.0.0-preview.5.21301.5" />
- <PackageReference Include="morelinq" Version="3.3.2" />
- <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
+ <PackageReference Include="System.Text.Json" Version="6.0.2" />
</ItemGroup>
</Project>
diff --git a/UVtools.Core/UVtools.Core.csproj.DotSettings b/UVtools.Core/UVtools.Core.csproj.DotSettings
deleted file mode 100644
index 6162834..0000000
--- a/UVtools.Core/UVtools.Core.csproj.DotSettings
+++ /dev/null
@@ -1,2 +0,0 @@
-<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
- <s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp90</s:String></wpf:ResourceDictionary> \ No newline at end of file
diff --git a/UVtools.Core/Voxel/Voxelizer.cs b/UVtools.Core/Voxel/Voxelizer.cs
index 812ded2..579b533 100644
--- a/UVtools.Core/Voxel/Voxelizer.cs
+++ b/UVtools.Core/Voxel/Voxelizer.cs
@@ -13,230 +13,229 @@ using System.Drawing;
using System.Numerics;
using UVtools.Core.Extensions;
-namespace UVtools.Core.Voxel
+namespace UVtools.Core.Voxel;
+
+public class Voxelizer
{
- public class Voxelizer
+ public sealed class UVFace
{
- public sealed class UVFace
- {
- public FaceOrientation Type;
- public uint LayerIndex;
- public Rectangle FaceRect;
- public float LayerHeight;
- /* Doubly linked list of UVFaces, used during Stage 3, collapsing the faces vertically.
- * instead of modifying properties and having to remove items from lists, we keep all faces
- * and just link parents and children together.
- * During STL triangle generation, we only draw the 'roots' (faces with no parent) and we count
- * the chain of children for how "high" the face should be. */
- public UVFace Parent = null;
- public UVFace Child = null;
+ public FaceOrientation Type;
+ public uint LayerIndex;
+ public Rectangle FaceRect;
+ public float LayerHeight;
+ /* Doubly linked list of UVFaces, used during Stage 3, collapsing the faces vertically.
+ * instead of modifying properties and having to remove items from lists, we keep all faces
+ * and just link parents and children together.
+ * During STL triangle generation, we only draw the 'roots' (faces with no parent) and we count
+ * the chain of children for how "high" the face should be. */
+ public UVFace? Parent = null;
+ public UVFace? Child = null;
+
+ /* This is used to make a linked list of faces, instead of generating a list which requires resize/reallocation/copies.
+ * Particularly useful when you have a model that consists of 49 million visible faces...*/
+ public UVFace? FlatListNext = null;
+ }
- /* This is used to make a linked list of faces, instead of generating a list which requires resize/reallocation/copies.
- * Particularly useful when you have a model that consists of 49 million visible faces...*/
- public UVFace FlatListNext = null;
- }
+ [Flags]
+ public enum FaceOrientation : short
+ {
+ None = 0,
+ Top = 1,
+ Bottom = 2,
+ Left = 4,
+ Right = 8,
+ Front = 16,
+ Back = 32
+ }
- [Flags]
- public enum FaceOrientation : short
+ public static FaceOrientation GetOpenFaces(Mat layer, int x, int y, Mat? layerBelow = null, Mat? layerAbove = null)
+ {
+ var layerSpan = layer.GetDataByteSpan();
+
+ var foundFaces = FaceOrientation.None;
+ var pixelPos = layer.GetPixelPos(x, y);
+ if (layerSpan[pixelPos] == 0)
{
- None = 0,
- Top = 1,
- Bottom = 2,
- Left = 4,
- Right = 8,
- Front = 16,
- Back = 32
+ return foundFaces;
}
- public static FaceOrientation GetOpenFaces(Mat layer, int x, int y, Mat layerBelow = null, Mat layerAbove = null)
+ if (layerBelow is null)
{
- var layerSpan = layer.GetDataByteSpan();
-
- var foundFaces = FaceOrientation.None;
- var pixelPos = layer.GetPixelPos(x, y);
- if (layerSpan[pixelPos] == 0)
- {
- return foundFaces;
- }
-
- if (layerBelow is null)
+ foundFaces |= FaceOrientation.Bottom;
+ }
+ else
+ {
+ var belowSpan = layerBelow.GetDataByteSpan();
+ if (belowSpan[pixelPos] == 0)
{
foundFaces |= FaceOrientation.Bottom;
}
- else
- {
- var belowSpan = layerBelow.GetDataByteSpan();
- if (belowSpan[pixelPos] == 0)
- {
- foundFaces |= FaceOrientation.Bottom;
- }
- }
+ }
- if (layerAbove is null)
+ if (layerAbove is null)
+ {
+ foundFaces |= FaceOrientation.Top;
+ }
+ else
+ {
+ var aboveSpan = layerAbove.GetDataByteSpan();
+ if (aboveSpan[pixelPos] == 0)
{
foundFaces |= FaceOrientation.Top;
}
- else
- {
- var aboveSpan = layerAbove.GetDataByteSpan();
- if (aboveSpan[pixelPos] == 0)
- {
- foundFaces |= FaceOrientation.Top;
- }
- }
-
- if (x == 0 || layerSpan[pixelPos-1] == 0)
- {
- foundFaces |= FaceOrientation.Left;
- }
-
- if (x == layer.Width - 1 || layerSpan[pixelPos+1] == 0)
- {
- foundFaces |= FaceOrientation.Right;
- }
-
- if (y == 0 || layerSpan[layer.GetPixelPos(x, y - 1)] == 0)
- {
- foundFaces |= FaceOrientation.Front;
- }
+ }
- if (y == layer.Height - 1 || layerSpan[layer.GetPixelPos(x, y + 1)] == 0)
- {
- foundFaces |= FaceOrientation.Back;
- }
+ if (x == 0 || layerSpan[pixelPos-1] == 0)
+ {
+ foundFaces |= FaceOrientation.Left;
+ }
- return foundFaces;
+ if (x == layer.Width - 1 || layerSpan[pixelPos+1] == 0)
+ {
+ foundFaces |= FaceOrientation.Right;
}
- public static Mat BuildVoxelLayerImage(Mat curLayer, Mat layerAbove = null, Mat layerBelow = null)
+ if (y == 0 || layerSpan[layer.GetPixelPos(x, y - 1)] == 0)
{
- /* The goal of the VoxelLayerImage is to reduce as much as possible, the number of pixels we need to do 6 direction neighbor checking on */
+ foundFaces |= FaceOrientation.Front;
+ }
- /* the outer contours of the current layer should always be checked, they by definition should have an exposed face */
- using var contours = curLayer.FindContours(RetrType.Tree);
- var onlyContours = curLayer.NewBlank();
- CvInvoke.DrawContours(onlyContours, contours, -1, EmguExtensions.WhiteColor, 1);
+ if (y == layer.Height - 1 || layerSpan[layer.GetPixelPos(x, y + 1)] == 0)
+ {
+ foundFaces |= FaceOrientation.Back;
+ }
- bool needAboveDispose = layerAbove is null;
- bool needBelowDispose = layerBelow is null;
+ return foundFaces;
+ }
- layerAbove ??= curLayer.NewBlank();
- layerBelow ??= curLayer.NewBlank();
+ public static Mat BuildVoxelLayerImage(Mat curLayer, Mat? layerAbove = null, Mat? layerBelow = null)
+ {
+ /* The goal of the VoxelLayerImage is to reduce as much as possible, the number of pixels we need to do 6 direction neighbor checking on */
- /* anything that is in the current layer but is not in the layer above, by definition has an exposed face */
- var upperSubtract = new Mat();
- CvInvoke.Subtract(curLayer, layerAbove, upperSubtract);
+ /* the outer contours of the current layer should always be checked, they by definition should have an exposed face */
+ using var contours = curLayer.FindContours(RetrType.Tree);
+ var onlyContours = curLayer.NewBlank();
+ CvInvoke.DrawContours(onlyContours, contours, -1, EmguExtensions.WhiteColor, 1);
- /* anything that is in the current layer but is not in the layer below, by definition has an exposed face */
- var lowerSubtract = new Mat();
- CvInvoke.Subtract(curLayer, layerBelow, lowerSubtract);
+ bool needAboveDispose = layerAbove is null;
+ bool needBelowDispose = layerBelow is null;
- /* Or all of these together to get the list of pixels that have exposed face(s) */
- var voxelLayer = curLayer.NewBlank();
- CvInvoke.BitwiseOr(onlyContours, voxelLayer, voxelLayer);
- CvInvoke.BitwiseOr(upperSubtract, voxelLayer, voxelLayer);
- CvInvoke.BitwiseOr(lowerSubtract, voxelLayer, voxelLayer);
+ layerAbove ??= curLayer.NewBlank();
+ layerBelow ??= curLayer.NewBlank();
- /* dispoose of the layerAbove/layerBelow if they were allocated here */
- if (needAboveDispose)
- {
- layerAbove.Dispose();
- }
- if (needBelowDispose)
- {
- layerBelow.Dispose();
- }
- onlyContours.Dispose();
+ /* anything that is in the current layer but is not in the layer above, by definition has an exposed face */
+ var upperSubtract = new Mat();
+ CvInvoke.Subtract(curLayer, layerAbove, upperSubtract);
- return voxelLayer;
- }
+ /* anything that is in the current layer but is not in the layer below, by definition has an exposed face */
+ var lowerSubtract = new Mat();
+ CvInvoke.Subtract(curLayer, layerBelow, lowerSubtract);
- /* CreateVoxelMesh is no longer used, see OperationLayerExportMesh for the logic that used to be here */
+ /* Or all of these together to get the list of pixels that have exposed face(s) */
+ var voxelLayer = curLayer.NewBlank();
+ CvInvoke.BitwiseOr(onlyContours, voxelLayer, voxelLayer);
+ CvInvoke.BitwiseOr(upperSubtract, voxelLayer, voxelLayer);
+ CvInvoke.BitwiseOr(lowerSubtract, voxelLayer, voxelLayer);
- /* NOTE: this took a lot, a lot, a lot, of trial and error, just trust that it generates the correct triangles for a given face ;) */
- public static IEnumerable<(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)> MakeFacetsForUVFace(UVFace face, float xSize, float ySize, float positionZ)
+ /* dispoose of the layerAbove/layerBelow if they were allocated here */
+ if (needAboveDispose)
{
- /* triangles need "normal" vectors to show which is the outside of the triangle */
- /* also, triangle points need to be provided in counter clockwise direction...*/
+ layerAbove.Dispose();
+ }
+ if (needBelowDispose)
+ {
+ layerBelow.Dispose();
+ }
+ onlyContours.Dispose();
- var LeftNormal = new Vector3(-1, 0, 0);
- var RightNormal = new Vector3(1, 0, 0);
- var TopNormal = new Vector3(0, 0, 1);
- var BottomNormal = new Vector3(0, 0, -1);
- var BackNormal = new Vector3(0, 1, 0);
- var FrontNormal = new Vector3(0, -1, 0);
+ return voxelLayer;
+ }
- /* count the "height" of this face, which is == to itself + number of children in its doubly linked list chain */
- float height = 0;
- var totalFaceCount = 1;
- UVFace child = face;
- while (child.Child is not null)
- {
- height += child.LayerHeight;
- totalFaceCount++;
- child = child.Child;
- }
- height += child.LayerHeight;
+ /* CreateVoxelMesh is no longer used, see OperationLayerExportMesh for the logic that used to be here */
- if (face.Type == FaceOrientation.Front)
- {
- var lowerLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ);
- var lowerRight = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize, positionZ);
- var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
- var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
- yield return (lowerLeft, lowerRight, upperRight, FrontNormal);
- yield return (upperRight, upperLeft, lowerLeft, FrontNormal);
- }
- else if (face.Type == FaceOrientation.Back)
- {
- var lowerRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize + ySize, positionZ);
- var lowerLeft = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize + ySize, positionZ);
- var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
- var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
- yield return (lowerLeft, lowerRight, upperRight, BackNormal);
- yield return (upperRight, upperLeft, lowerLeft, BackNormal);
- }
- else if (face.Type == FaceOrientation.Left)
- {
- var lowerLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ);
- var lowerRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y) * ySize, positionZ);
- var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
- var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
- yield return (lowerLeft, lowerRight, upperRight, LeftNormal);
- yield return (upperRight, upperLeft, lowerLeft, LeftNormal);
- }
- else if (face.Type == FaceOrientation.Right)
- {
- var lowerRight = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ);
- var lowerLeft = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y) * ySize, positionZ);
- var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
- var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
- yield return (lowerLeft, lowerRight, upperRight, RightNormal);
- yield return (upperRight, upperLeft, lowerLeft, RightNormal);
- }
- else if (face.Type == FaceOrientation.Top)
- {
- var upperLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ + face.LayerHeight);
- var upperRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + totalFaceCount) * ySize, positionZ + face.LayerHeight);
- var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, positionZ + face.LayerHeight);
- var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, positionZ + face.LayerHeight);
- yield return (lowerLeft, lowerRight, upperRight, TopNormal);
- yield return (upperRight, upperLeft, lowerLeft, TopNormal);
- }
- else if (face.Type == FaceOrientation.Bottom)
- {
- var upperRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ);
- var upperLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + totalFaceCount) * ySize, positionZ);
- var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, positionZ);
- var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, positionZ);
- yield return (lowerLeft, lowerRight, upperRight, BottomNormal);
- yield return (upperRight, upperLeft, lowerLeft, BottomNormal);
- }
- /*else
- {
- yield break;
- }*/
+ /* NOTE: this took a lot, a lot, a lot, of trial and error, just trust that it generates the correct triangles for a given face ;) */
+ public static IEnumerable<(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)> MakeFacetsForUVFace(UVFace face, float xSize, float ySize, float positionZ)
+ {
+ /* triangles need "normal" vectors to show which is the outside of the triangle */
+ /* also, triangle points need to be provided in counter clockwise direction...*/
+
+ var LeftNormal = new Vector3(-1, 0, 0);
+ var RightNormal = new Vector3(1, 0, 0);
+ var TopNormal = new Vector3(0, 0, 1);
+ var BottomNormal = new Vector3(0, 0, -1);
+ var BackNormal = new Vector3(0, 1, 0);
+ var FrontNormal = new Vector3(0, -1, 0);
+
+ /* count the "height" of this face, which is == to itself + number of children in its doubly linked list chain */
+ float height = 0;
+ var totalFaceCount = 1;
+ UVFace child = face;
+ while (child.Child is not null)
+ {
+ height += child.LayerHeight;
+ totalFaceCount++;
+ child = child.Child;
+ }
+ height += child.LayerHeight;
+ if (face.Type == FaceOrientation.Front)
+ {
+ var lowerLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ);
+ var lowerRight = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize, positionZ);
+ var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
+ var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
+ yield return (lowerLeft, lowerRight, upperRight, FrontNormal);
+ yield return (upperRight, upperLeft, lowerLeft, FrontNormal);
+ }
+ else if (face.Type == FaceOrientation.Back)
+ {
+ var lowerRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize + ySize, positionZ);
+ var lowerLeft = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize + ySize, positionZ);
+ var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
+ var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
+ yield return (lowerLeft, lowerRight, upperRight, BackNormal);
+ yield return (upperRight, upperLeft, lowerLeft, BackNormal);
}
+ else if (face.Type == FaceOrientation.Left)
+ {
+ var lowerLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ);
+ var lowerRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y) * ySize, positionZ);
+ var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
+ var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
+ yield return (lowerLeft, lowerRight, upperRight, LeftNormal);
+ yield return (upperRight, upperLeft, lowerLeft, LeftNormal);
+ }
+ else if (face.Type == FaceOrientation.Right)
+ {
+ var lowerRight = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ);
+ var lowerLeft = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y) * ySize, positionZ);
+ var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
+ var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
+ yield return (lowerLeft, lowerRight, upperRight, RightNormal);
+ yield return (upperRight, upperLeft, lowerLeft, RightNormal);
+ }
+ else if (face.Type == FaceOrientation.Top)
+ {
+ var upperLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ + face.LayerHeight);
+ var upperRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + totalFaceCount) * ySize, positionZ + face.LayerHeight);
+ var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, positionZ + face.LayerHeight);
+ var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, positionZ + face.LayerHeight);
+ yield return (lowerLeft, lowerRight, upperRight, TopNormal);
+ yield return (upperRight, upperLeft, lowerLeft, TopNormal);
+ }
+ else if (face.Type == FaceOrientation.Bottom)
+ {
+ var upperRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ);
+ var upperLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + totalFaceCount) * ySize, positionZ);
+ var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, positionZ);
+ var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, positionZ);
+ yield return (lowerLeft, lowerRight, upperRight, BottomNormal);
+ yield return (upperRight, upperLeft, lowerLeft, BottomNormal);
+ }
+ /*else
+ {
+ yield break;
+ }*/
+
}
-}
+} \ No newline at end of file
diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
index 291c1b6..5b13d53 100644
--- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs
+++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
@@ -221,9 +221,6 @@
<Component Id="owc0D5E901B0EDC9AC66EDED7744C9C0EC4" Guid="bf900338-6ac3-e652-0358-b6c5a433ead1" Win64="yes">
<File Id="owf0D5E901B0EDC9AC66EDED7744C9C0EC4" Source="$(var.SourceDir)\BouncyCastle.Crypto.dll" KeyPath="yes" />
</Component>
- <Component Id="owc3D4C6D44ECDB03E5FE1F04B44BCF854F" Guid="7357d916-7bf8-aecc-ec52-30bed411500d" Win64="yes">
- <File Id="owf3D4C6D44ECDB03E5FE1F04B44BCF854F" Source="$(var.SourceDir)\clrcompression.dll" KeyPath="yes" />
- </Component>
<Component Id="owc1EB8A6523152350CEBC63B9D1BC2930F" Guid="a49af45a-fa09-479b-2625-4fdd580aa598" Win64="yes">
<File Id="owf1EB8A6523152350CEBC63B9D1BC2930F" Source="$(var.SourceDir)\clretwrc.dll" KeyPath="yes" />
</Component>
@@ -308,9 +305,6 @@
<Component Id="owc7609DFEE03B95E830FD72B0699C9CF8C" Guid="c7b987b7-1a3c-0d2c-7831-ad6f4c3871e7" Win64="yes">
<File Id="owf7609DFEE03B95E830FD72B0699C9CF8C" Source="$(var.SourceDir)\Microsoft.Win32.SystemEvents.dll" KeyPath="yes" />
</Component>
- <Component Id="owc14CB9EF1BBF3C31A43771E2849BBB039" Guid="785bdc3f-43ca-90d5-d8be-40de895dae62" Win64="yes">
- <File Id="owf14CB9EF1BBF3C31A43771E2849BBB039" Source="$(var.SourceDir)\MoreLinq.dll" KeyPath="yes" />
- </Component>
<Component Id="owc6F31DEF09608AA645959666A4CB7FBBC" Guid="c94570a5-5bb4-a53f-e12f-9581bddf7d08" Win64="yes">
<File Id="owf6F31DEF09608AA645959666A4CB7FBBC" Source="$(var.SourceDir)\mscordaccore.dll" KeyPath="yes" />
</Component>
@@ -1137,6 +1131,27 @@
<Component Id="owc5A0969A08E1AC292A7F06D07A9C219FC" Guid="c767b03e-f767-4482-621d-04b79c6735f9" Win64="yes">
<File Id="owf5A0969A08E1AC292A7F06D07A9C219FC" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\Zortrax Inkspire.ini" KeyPath="yes" />
</Component>
+ <Component Id="owc9FB83579A8F84D6F9FF9A9A8484A58EB" Guid="9FB83579-A8F8-4D6F-9FF9-A9A8484A58EB">
+ <File Id="owf9FB83579A8F84D6F9FF9A9A8484A58EB" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\Elegoo Jupiter.ini" KeyPath="yes" />
+ </Component>
+ <Component Id="owc82970A1CD8C44090848360459C2A215F" Guid="82970A1C-D8C4-4090-8483-60459C2A215F">
+ <File Id="owf82970A1CD8C44090848360459C2A215F" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\EPAX DX1 PRO.ini" KeyPath="yes" />
+ </Component>
+ <Component Id="owc6EDEFD442039448C9136490D663E5E61" Guid="6EDEFD44-2039-448C-9136-490D663E5E61">
+ <File Id="owf6EDEFD442039448C9136490D663E5E61" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\EPAX DX10 Pro 5K.ini" KeyPath="yes" />
+ </Component>
+ <Component Id="owc842C8C8131634222B3AB4A8AE42EA416" Guid="842C8C81-3163-4222-B3AB-4A8AE42EA416">
+ <File Id="owf842C8C8131634222B3AB4A8AE42EA416" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\EPAX DX10 Pro 8K.ini" KeyPath="yes" />
+ </Component>
+ <Component Id="owc318341AC165041A4B1B4EB091AE038D4" Guid="318341AC-1650-41A4-B1B4-EB091AE038D4">
+ <File Id="owf318341AC165041A4B1B4EB091AE038D4" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\EPAX E10 8K.ini" KeyPath="yes" />
+ </Component>
+ <Component Id="owcA62B68A4B6C441CFAE564C091A135E47" Guid="A62B68A4-B6C4-41CF-AE56-4C091A135E47">
+ <File Id="owfA62B68A4B6C441CFAE564C091A135E47" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\Epax X1 4KS.ini" KeyPath="yes" />
+ </Component>
+ <Component Id="owc935F38E5C9A44BB897F2C3B7EF7EED98" Guid="935F38E5-C9A4-4BB8-97F2-C3B7EF7EED98">
+ <File Id="owf935F38E5C9A44BB897F2C3B7EF7EED98" Source="$(var.SourceDir)\Assets\PrusaSlicer\printer\EPAX X133 6K.ini" KeyPath="yes" />
+ </Component>
</Directory>
<Directory Id="owd324203F33CD6B2D491618D3D363F997F" Name="sla_print">
<Component Id="owc988205CB4C2FC6EA3D41704B657C5681" Guid="5dd3f394-0e58-76db-f891-78c219b5339e">
@@ -1410,8 +1425,29 @@
<Component Id="owc2B1C136C44C740F48CDFE91A80F48B4B" Guid="2B1C136C-44C7-40F4-8CDF-E91A80F48B4B">
<File Id="owf2B1C136C44C740F48CDFE91A80F48B4B" Source="$(var.SourceDir)\opencv_videoio_ffmpeg455_64.dll" KeyPath="yes" />
</Component>
- <Component Id="owc32E45984571E4D3293714751B0645034" Guid="32E45984-571E-4D32-9371-4751B0645034">
- <File Id="owf32E45984571E4D3293714751B0645034" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_5.0.1422.5710.dll" KeyPath="yes" />
+ <Component Id="owc8DBF076FA26C4EC39811CAD12FD3937C" Guid="8DBF076F-A26C-4EC3-9811-CAD12FD3937C">
+ <File Id="owf8DBF076FA26C4EC39811CAD12FD3937C" Source="$(var.SourceDir)\api-ms-win-core-fibers-l1-1-0.dll" KeyPath="yes" />
+ </Component>
+ <Component Id="owc04789579CAEC4E9D884765B39BA4BC5D" Guid="04789579-CAEC-4E9D-8847-65B39BA4BC5D">
+ <File Id="owf04789579CAEC4E9D884765B39BA4BC5D" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_6.0.322.12309.dll" KeyPath="yes" />
+ </Component>
+ <Component Id="owcF431B2B5B6264D78AAFA361A07841A68" Guid="F431B2B5-B626-4D78-AAFA-361A07841A68">
+ <File Id="owfF431B2B5B6264D78AAFA361A07841A68" Source="$(var.SourceDir)\msquic.dll" KeyPath="yes" />
+ </Component>
+ <Component Id="owcE030A605EE934F1B985D02784D2973A2" Guid="E030A605-EE93-4F1B-985D-02784D2973A2">
+ <File Id="owfE030A605EE934F1B985D02784D2973A2" Source="$(var.SourceDir)\Projektanker.Icons.Avalonia.dll" KeyPath="yes" />
+ </Component>
+ <Component Id="owc753E17D7CA9541B5944BC3F23884494C" Guid="753E17D7-CA95-41B5-944B-C3F23884494C">
+ <File Id="owf753E17D7CA9541B5944BC3F23884494C" Source="$(var.SourceDir)\Projektanker.Icons.Avalonia.FontAwesome.dll" KeyPath="yes" />
+ </Component>
+ <Component Id="owc76C7A5596AF7490AAA0F02BD144948E5" Guid="76C7A559-6AF7-490A-AA0F-02BD144948E5">
+ <File Id="owf76C7A5596AF7490AAA0F02BD144948E5" Source="$(var.SourceDir)\Projektanker.Icons.Avalonia.MaterialDesign.dll" KeyPath="yes" />
+ </Component>
+ <Component Id="owc168F20362B05422CB839BE56376CE621" Guid="168F2036-2B05-422C-B839-BE56376CE621">
+ <File Id="owf168F20362B05422CB839BE56376CE621" Source="$(var.SourceDir)\System.IO.Compression.Native.dll" KeyPath="yes" />
+ </Component>
+ <Component Id="owc9C1F0C0351694913958CEBFB4D988C2D" Guid="9C1F0C03-5169-4913-958C-EBFB4D988C2D">
+ <File Id="owf9C1F0C0351694913958CEBFB4D988C2D" Source="$(var.SourceDir)\System.Net.Quic.dll" KeyPath="yes" />
</Component>
</Directory>
<Directory Id="ProgramMenuFolder">
diff --git a/UVtools.Platforms/osx-arm64/Info.plist b/UVtools.Platforms/osx-arm64/Info.plist
new file mode 100644
index 0000000..f5e8719
--- /dev/null
+++ b/UVtools.Platforms/osx-arm64/Info.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleIconFile</key>
+ <string>UVtools.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.UVtools</string>
+ <key>CFBundleName</key>
+ <string>UVtools</string>
+ <key>CFBundleVersion</key>
+ <string>#VERSION</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.15</string>
+ <key>CFBundleExecutable</key>
+ <string>UVtools</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>#VERSION</string>
+ <key>CFBundleSupportedPlatforms</key>
+ <array>
+ <string>MacOSX</string>
+ </array>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.utilities</string>
+ <key>NSHighResolutionCapable</key>
+ <true />
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2020 PTRTECH. All rights reserved.</string>
+ </dict>
+</plist>
diff --git a/UVtools.Platforms/osx-arm64/libcvextern.dylib b/UVtools.Platforms/osx-arm64/libcvextern.dylib
new file mode 100644
index 0000000..e6f4c73
--- /dev/null
+++ b/UVtools.Platforms/osx-arm64/libcvextern.dylib
Binary files differ
diff --git a/UVtools.Platforms/osx-x64/Info.plist b/UVtools.Platforms/osx-x64/Info.plist
index bd45bf0..0263aaf 100644
--- a/UVtools.Platforms/osx-x64/Info.plist
+++ b/UVtools.Platforms/osx-x64/Info.plist
@@ -2,7 +2,9 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>CFBundleIconFile</key>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleIconFile</key>
<string>UVtools.icns</string>
<key>CFBundleIdentifier</key>
<string>com.UVtools</string>
@@ -11,7 +13,7 @@
<key>CFBundleVersion</key>
<string>#VERSION</string>
<key>LSMinimumSystemVersion</key>
- <string>10.12</string>
+ <string>10.15</string>
<key>CFBundleExecutable</key>
<string>UVtools</string>
<key>CFBundleInfoDictionaryVersion</key>
@@ -20,7 +22,15 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>#VERSION</string>
- <key>NSHighResolutionCapable</key>
- <true />
+ <key>CFBundleSupportedPlatforms</key>
+ <array>
+ <string>MacOSX</string>
+ </array>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.utilities</string>
+ <key>NSHighResolutionCapable</key>
+ <true />
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2020 PTRTECH. All rights reserved.</string>
</dict>
</plist>
diff --git a/UVtools.ScriptSample/README.md b/UVtools.ScriptSample/README.md
index 4bcdc23..f2d9d53 100644
--- a/UVtools.ScriptSample/README.md
+++ b/UVtools.ScriptSample/README.md
@@ -106,7 +106,7 @@ namespace UVtools.ScriptSample // Require to compile and have the IDE help
/// Validate user inputs here, this function trigger when user click on execute
/// </summary>
/// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
+ public string? ScriptValidate()
{
StringBuilder sb = new();
diff --git a/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs b/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs
index 8ec2695..385b3fd 100644
--- a/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs
+++ b/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs
@@ -12,104 +12,103 @@ using Emgu.CV.CvEnum;
using UVtools.Core.Operations;
using UVtools.Core.Scripting;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// A workflow automation sample
+/// </summary>
+public class ScriptAutomateWorkflowSample : ScriptGlobals
{
+ private readonly ScriptCheckBoxInput InputErode = new()
+ {
+ Label = "Erode base layers",
+ Value = true
+ };
+ private readonly ScriptCheckBoxInput InputReliefRaft = new()
+ {
+ Label = "Relief raft",
+ Value = true
+ };
+ private readonly ScriptCheckBoxInput InputPixelDimming = new()
+ {
+ Label = "Pixel dimming base layers",
+ Value = true
+ };
/// <summary>
- /// A workflow automation sample
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptAutomateWorkflowSample : ScriptGlobals
+ public void ScriptInit()
{
- private ScriptCheckBoxInput InputErode = new()
+ Script.Name = "Automate my workflow";
+ Script.Description = "A workflow automation sample";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ Script.UserInputs.AddRange(new []
{
- Label = "Erode base layers",
- Value = true
- };
- private ScriptCheckBoxInput InputReliefRaft = new()
- {
- Label = "Relief raft",
- Value = true
- };
- private ScriptCheckBoxInput InputPixelDimming = new()
- {
- Label = "Pixel dimming base layers",
- Value = true
- };
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
+ InputErode,
+ InputReliefRaft,
+ InputPixelDimming
+ });
+ }
+
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ List<Operation> operations = new();
+
+ // Morph bottom layers
+ if (InputErode.Value)
{
- Script.Name = "Automate my workflow";
- Script.Description = "A workflow automation sample";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
- Script.UserInputs.AddRange(new []
+ OperationMorph morph = new(SlicerFile)
{
- InputErode,
- InputReliefRaft,
- InputPixelDimming
- });
+ MorphOperation = OperationMorph.MorphOperations.Erode,
+ Iterations = 4,
+ };
+ morph.SelectBottomLayers();
+ operations.Add(morph);
}
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
+ // Raft relief
+ if (InputReliefRaft.Value)
{
- return null;
+ OperationRaftRelief raftRelief = new(SlicerFile)
+ {
+ ReliefType = OperationRaftRelief.RaftReliefTypes.Relief,
+ };
+ operations.Add(raftRelief);
}
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
+ // Dim and apply checkboard pattern to bottom layers
+ if (InputPixelDimming.Value)
{
- List<Operation> operations = new();
-
- // Morph bottom layers
- if (InputErode.Value)
- {
- OperationMorph morph = new(SlicerFile)
- {
- MorphOperation = OperationMorph.MorphOperations.Erode,
- Iterations = 4,
- };
- morph.SelectBottomLayers();
- operations.Add(morph);
- }
+ OperationPixelDimming pixelDimming = new(SlicerFile);
+ pixelDimming.GeneratePixelDimming("Chessboard");
+ pixelDimming.SelectBottomLayers();
+ operations.Add(pixelDimming);
- // Raft relief
- if (InputReliefRaft.Value)
+ foreach (var operation in operations) // Loop all my created operations to execute them
{
- OperationRaftRelief raftRelief = new(SlicerFile)
- {
- ReliefType = OperationRaftRelief.RaftReliefTypes.Relief,
- };
- operations.Add(raftRelief);
+ Progress.Token.ThrowIfCancellationRequested(); // Abort operation, user requested cancellation
+ operation.ROI = Operation.ROI; // Copy user selected ROI to my operation
+ operation.MaskPoints = Operation.MaskPoints; // Copy user selected Masks to my operation
+ if (!operation.CanValidate()) continue; // If cant validate don't execute the operation
+ operation.Execute(Progress);
}
-
- // Dim and apply checkboard pattern to bottom layers
- if (InputPixelDimming.Value)
- {
- OperationPixelDimming pixelDimming = new(SlicerFile);
- pixelDimming.GeneratePixelDimming("Chessboard");
- pixelDimming.SelectBottomLayers();
- operations.Add(pixelDimming);
-
- foreach (var operation in operations) // Loop all my created operations to execute them
- {
- Progress.Token.ThrowIfCancellationRequested(); // Abort operation, user requested cancellation
- operation.ROI = Operation.ROI; // Copy user selected ROI to my operation
- operation.MaskPoints = Operation.MaskPoints; // Copy user selected Masks to my operation
- if (!operation.CanValidate()) continue; // If cant validate don't execute the operation
- operation.Execute(Progress);
- }
- }
-
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
}
+
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs b/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs
index e0286fa..02d1aa2 100644
--- a/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs
+++ b/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs
@@ -9,57 +9,56 @@
using System;
using UVtools.Core.Scripting;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptChangeLayerPropertiesSample : ScriptGlobals
{
/// <summary>
- /// Change layer properties to random values
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptChangeLayerPropertiesSample : ScriptGlobals
+ public void ScriptInit()
{
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
- {
- Script.Name = "Change layer properties";
- Script.Description = "Change layer properties to random values :D";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
- }
-
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
- {
- return null;
- }
+ Script.Name = "Change layer properties";
+ Script.Description = "Change layer properties to random values :D";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ }
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
- {
- Progress.Reset("Changing layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return null;
+ }
- Random random = new();
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ Progress.Reset("Changing layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process
- for (uint layerIndex = Operation.LayerIndexStart; layerIndex <= Operation.LayerIndexEnd; layerIndex++)
- {
- Progress.Token.ThrowIfCancellationRequested(); // Abort operation, user requested cancellation
- var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use
+ Random random = new();
- layer.LiftHeight = random.Next(3, 10); // Random value from 3 to 10
- layer.LiftSpeed = random.Next(50, 200); // Random value from 50 to 200
- layer.RetractSpeed = random.Next(50, 200); // Random value from 50 to 200
+ for (uint layerIndex = Operation.LayerIndexStart; layerIndex <= Operation.LayerIndexEnd; layerIndex++)
+ {
+ Progress.Token.ThrowIfCancellationRequested(); // Abort operation, user requested cancellation
+ var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use
- Progress++; // Increment progress bar by 1
- }
+ layer.LiftHeight = random.Next(3, 10); // Random value from 3 to 10
+ layer.LiftSpeed = random.Next(50, 200); // Random value from 50 to 200
+ layer.RetractSpeed = random.Next(50, 200); // Random value from 50 to 200
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
+ Progress++; // Increment progress bar by 1
}
+
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptCloneSettings.cs b/UVtools.ScriptSample/ScriptCloneSettings.cs
index 9b229b3..f97ea83 100644
--- a/UVtools.ScriptSample/ScriptCloneSettings.cs
+++ b/UVtools.ScriptSample/ScriptCloneSettings.cs
@@ -14,411 +14,413 @@ using UVtools.Core.FileFormats;
using System.Diagnostics;
using System.Linq;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Performs a black inset around objects
+/// </summary>
+public class ScriptCloneSettings : ScriptGlobals
{
+ readonly ScriptCheckBoxInput Recursive = new()
+ {
+ Label = "Recursive",
+ ToolTip = "If unchecked, only files in the same folder are modified; if checked, files in all sub-folders are modified too",
+ Value = true
+ };
+
+ readonly ScriptCheckBoxInput Report = new()
+ {
+ Label = "Generate report file",
+ ToolTip = "Optionally generate a report file in the same directory as the open file",
+ Value = true
+ };
+
+ readonly ScriptCheckBoxInput OpenReport = new()
+ {
+ Label = "Open report file",
+ ToolTip = "Optionally open the result file when completed",
+ Value = true
+ };
+
+ readonly ScriptOpenFolderDialogInput FolderPath = new()
+ {
+ Label = "Folder path",
+ ToolTip = "The folder path to process",
+ };
+
+ private enum ResultStatus
+ {
+ Exception,
+ UnknownFileType,
+ DifferentMachineTypes,
+ Success,
+ Unchanged,
+ }
+
/// <summary>
- /// Performs a black inset around objects
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptCloneSettings : ScriptGlobals
+ public void ScriptInit()
{
- ScriptCheckBoxInput Recursive = new()
- {
- Label = "Recursive",
- ToolTip = "If unchecked, only files in the same folder are modified; if checked, files in all sub-folders are modified too",
- Value = true
- };
+ Script.Name = "Clone Settings";
+ Script.Description = "Copies the print settings from the current file to all files in the same directory and, optionally, recursively below it";
+ Script.Author = "Gina Venolia";
+ Script.Version = new Version(0, 2);
+ Script.UserInputs.Add(Recursive);
+ Script.UserInputs.Add(Report);
+ Script.UserInputs.Add(OpenReport);
+ Script.UserInputs.Add(FolderPath);
+ FolderPath.Value = SlicerFile.DirectoryPath;
+ }
- ScriptCheckBoxInput Report = new()
- {
- Label = "Generate report file",
- ToolTip = "Optionally generate a report file in the same directory as the open file",
- Value = true
- };
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return null;
+ }
- ScriptCheckBoxInput OpenReport = new()
- {
- Label = "Open report file",
- ToolTip = "Optionally open the result file when completed",
- Value = true
- };
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ Progress.CanCancel = true;
- ScriptOpenFolderDialogInput FolderPath = new()
- {
- Label = "Folder path",
- ToolTip = "The folder path to process",
- };
+ // Gather the list of directories to operate on
+ Progress.Reset("Processing files...");
- private enum ResultStatus
- {
- Exception,
- UnknownFileType,
- DifferentMachineTypes,
- Success,
- Unchanged,
- }
+ var directoryPaths = new List<string> {Path.GetDirectoryName(SlicerFile.FileFullPath)!};
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
+ if (Recursive.Value)
{
- Script.Name = "Clone Settings";
- Script.Description = "Copies the print settings from the current file to all files in the same directory and, optionally, recursively below it";
- Script.Author = "Gina Venolia";
- Script.Version = new Version(0, 2);
- Script.UserInputs.Add(Recursive);
- Script.UserInputs.Add(Report);
- Script.UserInputs.Add(OpenReport);
- Script.UserInputs.Add(FolderPath);
- FolderPath.Value = SlicerFile.DirectoryPath;
+ for (var i = 0; i < directoryPaths.Count; i++)
+ {
+ directoryPaths.AddRange(Directory.EnumerateDirectories(directoryPaths[i]));
+ }
}
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
+ // Gather the list of files to operate on
+ var filePaths = new List<string>();
+ foreach (var directoryPath in directoryPaths)
{
- return null;
+ filePaths.AddRange(Directory.EnumerateFiles(directoryPath));
}
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
- {
- Progress.CanCancel = true;
-
- // Gather the list of directories to operate on
- Progress.Reset("Processing files...");
+ // Except for the file we started with
+ var normalizedFilePath = Path.GetFullPath(SlicerFile.FileFullPath!);
+ filePaths.RemoveAll(x => Path.GetFullPath(x) == normalizedFilePath);
- var directoryPaths = new List<string>();
- directoryPaths.Add(Path.GetDirectoryName(SlicerFile.FileFullPath));
+ // Process the files
+ Progress.Reset("Processing files...", (uint)filePaths.Count, 0);
- if (Recursive.Value)
- {
- for (var i = 0; i < directoryPaths.Count; i++)
- {
- directoryPaths.AddRange(Directory.EnumerateDirectories(directoryPaths[i]));
- }
- }
+ var results = new List<Tuple<ResultStatus, string, List<string>?>>();
+ foreach (var filePath in filePaths)
+ {
+ if (Progress.Token.IsCancellationRequested) return false;
- // Gather the list of files to operate on
- var filePaths = new List<string>();
- foreach (var directoryPath in directoryPaths)
+ Tuple<ResultStatus, string, List<string>?> result;
+ try
{
- filePaths.AddRange(Directory.EnumerateFiles(directoryPath));
+ result = ProcessFile(filePath);
}
-
- // Except for the file we started with
- var normalizedFilePath = Path.GetFullPath(SlicerFile.FileFullPath);
- filePaths.RemoveAll(x => Path.GetFullPath(x) == normalizedFilePath);
-
- // Process the files
- Progress.Reset("Processing files...", (uint)filePaths.Count, 0);
-
- var results = new List<Tuple<ResultStatus, string, List<string>>>();
- foreach (var filePath in filePaths)
+ catch (Exception ex)
{
- if (Progress.Token.IsCancellationRequested) return false;
-
- Tuple<ResultStatus, string, List<string>> result;
- try
+ var deets = new List<string>
{
- result = ProcessFile(filePath);
- }
- catch (Exception ex)
- {
- var deets = new List<string>();
- deets.Add(ex.GetType().Name);
- deets.Add(ex.Message);
- result = new Tuple<ResultStatus, string, List<string>>(ResultStatus.Exception, filePath, deets);
- }
+ ex.GetType().Name,
+ ex.Message
+ };
+ result = new Tuple<ResultStatus, string, List<string>?>(ResultStatus.Exception, filePath, deets);
+ }
- results.Add(result);
+ results.Add(result);
- Progress++;
- }
+ Progress++;
+ }
- // Generate the report
- if (Report.Value)
- {
- var reportDirectory = Path.GetDirectoryName(SlicerFile.FileFullPath);
- var reportFilePath = Path.Combine(reportDirectory, "CloneSettingsReport.txt");
- using (var writer = new StreamWriter(reportFilePath))
+ // Generate the report
+ if (Report.Value)
+ {
+ var reportDirectory = Path.GetDirectoryName(SlicerFile.FileFullPath);
+ var reportFilePath = Path.Combine(reportDirectory!, "CloneSettingsReport.txt");
+ using (var writer = new StreamWriter(reportFilePath))
+ {
+ writer.WriteLine(Script.Name + " " + Script.Version.ToString() + " by " + Script.Author);
+ writer.WriteLine("Source file: " + SlicerFile.FileFullPath);
+ writer.WriteLine(DateTime.Now.ToLongDateString() + ", " + DateTime.Now.ToLongTimeString());
+ writer.WriteLine("Recursive: " + Recursive.Value.ToString());
+ writer.WriteLine("Directories: " + directoryPaths.Count);
+ writer.WriteLine("Files: " + filePaths.Count);
+ writer.WriteLine();
+
+ foreach (var statusGroup in results.GroupBy(x => x.Item1))
{
- writer.WriteLine(Script.Name + " " + Script.Version.ToString() + " by " + Script.Author);
- writer.WriteLine("Source file: " + SlicerFile.FileFullPath);
- writer.WriteLine(DateTime.Now.ToLongDateString() + ", " + DateTime.Now.ToLongTimeString());
- writer.WriteLine("Recursive: " + Recursive.Value.ToString());
- writer.WriteLine("Directories: " + directoryPaths.Count);
- writer.WriteLine("Files: " + filePaths.Count);
- writer.WriteLine();
-
- foreach (var statusGroup in results.GroupBy(x => x.Item1))
+ writer.WriteLine(statusGroup.Key.ToString());
+ foreach (var result in statusGroup.OrderBy(x => x.Item2))
{
- writer.WriteLine(statusGroup.Key.ToString());
- foreach (var result in statusGroup.OrderBy(x => x.Item2))
+ writer.WriteLine("\t" + result.Item2);
+ if (result.Item3 is not null)
{
- writer.WriteLine("\t" + result.Item2);
- if (result.Item3 is not null)
+ foreach (var detail in result.Item3)
{
- foreach (var detail in result.Item3)
- {
- writer.WriteLine("\t\t" + detail);
- }
+ writer.WriteLine("\t\t" + detail);
}
}
}
}
+ }
- if (OpenReport.Value)
+ if (OpenReport.Value)
+ {
+ var startInfo = new ProcessStartInfo(reportFilePath)
{
- var startInfo = new ProcessStartInfo(reportFilePath)
- {
- UseShellExecute = true
- };
- Process.Start(startInfo);
- }
+ UseShellExecute = true
+ };
+ Process.Start(startInfo);
}
-
- // return true if not cancelled by user
- return true;
}
- private Tuple<ResultStatus, string, List<string>> ProcessFile(string filePath)
+ // return true if not cancelled by user
+ return true;
+ }
+
+ private Tuple<ResultStatus, string, List<string>?> ProcessFile(string filePath)
+ {
+ // Determine the type of file
+ var file = FileFormat.FindByExtensionOrFilePath(filePath, true);
+ if (file is null)
{
- // Determine the type of file
- var file = FileFormat.FindByExtensionOrFilePath(filePath, true);
- if (file is null)
- {
- return new Tuple<ResultStatus, string, List<string>>(ResultStatus.UnknownFileType, filePath, null);
- }
+ return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.UnknownFileType, filePath, null);
+ }
- // Load the file
- file.Decode(filePath);
- if (string.Compare(file.MachineName, SlicerFile.MachineName, true) != 0)
+ // Load the file
+ file.Decode(filePath);
+ if (string.Compare(file.MachineName, SlicerFile.MachineName, true) != 0)
+ {
+ var deets = new List<string>
{
- var deets = new List<string>();
- deets.Add("Source machine type: " + SlicerFile.MachineName);
- deets.Add("Destination machine type: " + file.MachineName);
- return new Tuple<ResultStatus, string, List<string>>(ResultStatus.DifferentMachineTypes, filePath, deets);
- }
-
- // TODO: Validate that some parameters are the same in both files?
+ "Source machine type: " + SlicerFile.MachineName,
+ "Destination machine type: " + file.MachineName
+ };
+ return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.DifferentMachineTypes, filePath, deets);
+ }
- // Change the parameters
- var changed = false;
- var details = new List<string>();
+ // TODO: Validate that some parameters are the same in both files?
- if (file.CanUseBottomLayerCount && file.BottomLayerCount != SlicerFile.BottomLayerCount)
- {
- details.Add($"Bottom Layer Count: {file.BottomLayerCount} to {SlicerFile.BottomLayerCount}");
- file.BottomLayerCount = SlicerFile.BottomLayerCount;
- changed = true;
- }
-
- if (file.CanUseBottomLightOffDelay && file.BottomLightOffDelay != SlicerFile.BottomLightOffDelay)
- {
- details.Add($"Bottom Light Off Delay: {file.BottomLightOffDelay} to {SlicerFile.BottomLightOffDelay}");
- file.BottomLightOffDelay = SlicerFile.BottomLightOffDelay;
- changed = true;
- }
+ // Change the parameters
+ var changed = false;
+ var details = new List<string>();
- if (file.CanUseLightOffDelay && file.LightOffDelay != SlicerFile.LightOffDelay)
- {
- details.Add($"Light Off Delay: {file.LightOffDelay} to {SlicerFile.LightOffDelay}");
- file.LightOffDelay = SlicerFile.LightOffDelay;
- changed = true;
- }
+ if (file.CanUseBottomLayerCount && file.BottomLayerCount != SlicerFile.BottomLayerCount)
+ {
+ details.Add($"Bottom Layer Count: {file.BottomLayerCount} to {SlicerFile.BottomLayerCount}");
+ file.BottomLayerCount = SlicerFile.BottomLayerCount;
+ changed = true;
+ }
- if (file.CanUseBottomWaitTimeBeforeCure && file.BottomWaitTimeBeforeCure != SlicerFile.BottomWaitTimeBeforeCure)
- {
- details.Add($"Bottom Wait Time Before Cure: {file.BottomWaitTimeBeforeCure} to {SlicerFile.BottomWaitTimeBeforeCure}");
- file.BottomWaitTimeBeforeCure = SlicerFile.BottomWaitTimeBeforeCure;
- changed = true;
- }
+ if (file.CanUseBottomLightOffDelay && file.BottomLightOffDelay != SlicerFile.BottomLightOffDelay)
+ {
+ details.Add($"Bottom Light Off Delay: {file.BottomLightOffDelay} to {SlicerFile.BottomLightOffDelay}");
+ file.BottomLightOffDelay = SlicerFile.BottomLightOffDelay;
+ changed = true;
+ }
- if (file.CanUseWaitTimeBeforeCure && file.WaitTimeBeforeCure != SlicerFile.WaitTimeBeforeCure)
- {
- details.Add($"Wait Time Before Cure: {file.WaitTimeBeforeCure} to {SlicerFile.WaitTimeBeforeCure}");
- file.WaitTimeBeforeCure = SlicerFile.WaitTimeBeforeCure;
- changed = true;
- }
+ if (file.CanUseLightOffDelay && file.LightOffDelay != SlicerFile.LightOffDelay)
+ {
+ details.Add($"Light Off Delay: {file.LightOffDelay} to {SlicerFile.LightOffDelay}");
+ file.LightOffDelay = SlicerFile.LightOffDelay;
+ changed = true;
+ }
- if (file.CanUseBottomExposureTime && file.BottomExposureTime != SlicerFile.BottomExposureTime)
- {
- details.Add($"Bottom Exposure Time: {file.BottomExposureTime} to {SlicerFile.BottomExposureTime}");
- file.BottomExposureTime = SlicerFile.BottomExposureTime;
- changed = true;
- }
+ if (file.CanUseBottomWaitTimeBeforeCure && file.BottomWaitTimeBeforeCure != SlicerFile.BottomWaitTimeBeforeCure)
+ {
+ details.Add($"Bottom Wait Time Before Cure: {file.BottomWaitTimeBeforeCure} to {SlicerFile.BottomWaitTimeBeforeCure}");
+ file.BottomWaitTimeBeforeCure = SlicerFile.BottomWaitTimeBeforeCure;
+ changed = true;
+ }
- if (file.CanUseExposureTime && file.ExposureTime != SlicerFile.ExposureTime)
- {
- details.Add($"Exposure Time: {file.ExposureTime} to {SlicerFile.ExposureTime}");
- file.ExposureTime = SlicerFile.ExposureTime;
- changed = true;
- }
+ if (file.CanUseWaitTimeBeforeCure && file.WaitTimeBeforeCure != SlicerFile.WaitTimeBeforeCure)
+ {
+ details.Add($"Wait Time Before Cure: {file.WaitTimeBeforeCure} to {SlicerFile.WaitTimeBeforeCure}");
+ file.WaitTimeBeforeCure = SlicerFile.WaitTimeBeforeCure;
+ changed = true;
+ }
- if (file.CanUseBottomWaitTimeAfterCure && file.BottomWaitTimeAfterCure != SlicerFile.BottomWaitTimeAfterCure)
- {
- details.Add($"Bottom Wait Time After Cure: {file.BottomWaitTimeAfterCure} to {SlicerFile.BottomWaitTimeAfterCure}");
- file.BottomWaitTimeAfterCure = SlicerFile.BottomWaitTimeAfterCure;
- changed = true;
- }
+ if (file.CanUseBottomExposureTime && file.BottomExposureTime != SlicerFile.BottomExposureTime)
+ {
+ details.Add($"Bottom Exposure Time: {file.BottomExposureTime} to {SlicerFile.BottomExposureTime}");
+ file.BottomExposureTime = SlicerFile.BottomExposureTime;
+ changed = true;
+ }
- if (file.CanUseWaitTimeAfterCure && file.WaitTimeAfterCure != SlicerFile.WaitTimeAfterCure)
- {
- details.Add($"Wait Time After Cure: {file.WaitTimeAfterCure} to {SlicerFile.WaitTimeAfterCure}");
- file.WaitTimeAfterCure = SlicerFile.WaitTimeAfterCure;
- changed = true;
- }
+ if (file.CanUseExposureTime && file.ExposureTime != SlicerFile.ExposureTime)
+ {
+ details.Add($"Exposure Time: {file.ExposureTime} to {SlicerFile.ExposureTime}");
+ file.ExposureTime = SlicerFile.ExposureTime;
+ changed = true;
+ }
- if (file.CanUseBottomLiftHeight && file.BottomLiftHeight != SlicerFile.BottomLiftHeight)
- {
- details.Add($"Bottom Lift Height: {file.BottomLiftHeight} to {SlicerFile.BottomLiftHeight}");
- file.BottomLiftHeight = SlicerFile.BottomLiftHeight;
- changed = true;
- }
+ if (file.CanUseBottomWaitTimeAfterCure && file.BottomWaitTimeAfterCure != SlicerFile.BottomWaitTimeAfterCure)
+ {
+ details.Add($"Bottom Wait Time After Cure: {file.BottomWaitTimeAfterCure} to {SlicerFile.BottomWaitTimeAfterCure}");
+ file.BottomWaitTimeAfterCure = SlicerFile.BottomWaitTimeAfterCure;
+ changed = true;
+ }
- if (file.CanUseBottomLiftSpeed && file.BottomLiftSpeed != SlicerFile.BottomLiftSpeed)
- {
- details.Add($"Bottom Lift Speed: {file.BottomLiftSpeed} to {SlicerFile.BottomLiftSpeed}");
- file.BottomLiftSpeed = SlicerFile.BottomLiftSpeed;
- changed = true;
- }
+ if (file.CanUseWaitTimeAfterCure && file.WaitTimeAfterCure != SlicerFile.WaitTimeAfterCure)
+ {
+ details.Add($"Wait Time After Cure: {file.WaitTimeAfterCure} to {SlicerFile.WaitTimeAfterCure}");
+ file.WaitTimeAfterCure = SlicerFile.WaitTimeAfterCure;
+ changed = true;
+ }
- if (file.CanUseLiftHeight && file.LiftHeight != SlicerFile.LiftHeight)
- {
- details.Add($"Lift Height: {file.LiftHeight} to {SlicerFile.LiftHeight}");
- file.LiftHeight = SlicerFile.LiftHeight;
- changed = true;
- }
+ if (file.CanUseBottomLiftHeight && file.BottomLiftHeight != SlicerFile.BottomLiftHeight)
+ {
+ details.Add($"Bottom Lift Height: {file.BottomLiftHeight} to {SlicerFile.BottomLiftHeight}");
+ file.BottomLiftHeight = SlicerFile.BottomLiftHeight;
+ changed = true;
+ }
- if (file.CanUseLiftSpeed && file.LiftSpeed != SlicerFile.LiftSpeed)
- {
- details.Add($"Lift Speed: {file.LiftSpeed} to {SlicerFile.LiftSpeed}");
- file.LiftSpeed = SlicerFile.LiftSpeed;
- changed = true;
- }
+ if (file.CanUseBottomLiftSpeed && file.BottomLiftSpeed != SlicerFile.BottomLiftSpeed)
+ {
+ details.Add($"Bottom Lift Speed: {file.BottomLiftSpeed} to {SlicerFile.BottomLiftSpeed}");
+ file.BottomLiftSpeed = SlicerFile.BottomLiftSpeed;
+ changed = true;
+ }
- if (file.CanUseBottomLiftHeight2 && file.BottomLiftHeight2 != SlicerFile.BottomLiftHeight2)
- {
- details.Add($"Bottom Lift Height 2: {file.BottomLiftHeight2} to {SlicerFile.BottomLiftHeight2}");
- file.BottomLiftHeight2 = SlicerFile.BottomLiftHeight2;
- changed = true;
- }
+ if (file.CanUseLiftHeight && file.LiftHeight != SlicerFile.LiftHeight)
+ {
+ details.Add($"Lift Height: {file.LiftHeight} to {SlicerFile.LiftHeight}");
+ file.LiftHeight = SlicerFile.LiftHeight;
+ changed = true;
+ }
- if (file.CanUseBottomLiftSpeed2 && file.BottomLiftSpeed2 != SlicerFile.BottomLiftSpeed2)
- {
- details.Add($"Bottom Lift Speed2: {file.BottomLiftSpeed2} to {SlicerFile.BottomLiftSpeed2}");
- file.BottomLiftSpeed2 = SlicerFile.BottomLiftSpeed2;
- changed = true;
- }
+ if (file.CanUseLiftSpeed && file.LiftSpeed != SlicerFile.LiftSpeed)
+ {
+ details.Add($"Lift Speed: {file.LiftSpeed} to {SlicerFile.LiftSpeed}");
+ file.LiftSpeed = SlicerFile.LiftSpeed;
+ changed = true;
+ }
- if (file.CanUseLiftHeight2 && file.LiftHeight2 != SlicerFile.LiftHeight2)
- {
- details.Add($"Lift Height 2: {file.LiftHeight2} to {SlicerFile.LiftHeight2}");
- file.LiftHeight2 = SlicerFile.LiftHeight2;
- changed = true;
- }
+ if (file.CanUseBottomLiftHeight2 && file.BottomLiftHeight2 != SlicerFile.BottomLiftHeight2)
+ {
+ details.Add($"Bottom Lift Height 2: {file.BottomLiftHeight2} to {SlicerFile.BottomLiftHeight2}");
+ file.BottomLiftHeight2 = SlicerFile.BottomLiftHeight2;
+ changed = true;
+ }
- if (file.CanUseLiftSpeed2 && file.LiftSpeed2 != SlicerFile.LiftSpeed2)
- {
- details.Add($"Lift Speed 2: {file.LiftSpeed2} to {SlicerFile.LiftSpeed2}");
- file.LiftSpeed2 = SlicerFile.LiftSpeed2;
- changed = true;
- }
+ if (file.CanUseBottomLiftSpeed2 && file.BottomLiftSpeed2 != SlicerFile.BottomLiftSpeed2)
+ {
+ details.Add($"Bottom Lift Speed2: {file.BottomLiftSpeed2} to {SlicerFile.BottomLiftSpeed2}");
+ file.BottomLiftSpeed2 = SlicerFile.BottomLiftSpeed2;
+ changed = true;
+ }
- if (file.CanUseBottomWaitTimeAfterLift && file.BottomWaitTimeAfterLift != SlicerFile.BottomWaitTimeAfterLift)
- {
- details.Add($"Bottom Wait Time After Lift: {file.BottomWaitTimeAfterLift} to {SlicerFile.BottomWaitTimeAfterLift}");
- file.BottomWaitTimeAfterLift = SlicerFile.BottomWaitTimeAfterLift;
- changed = true;
- }
+ if (file.CanUseLiftHeight2 && file.LiftHeight2 != SlicerFile.LiftHeight2)
+ {
+ details.Add($"Lift Height 2: {file.LiftHeight2} to {SlicerFile.LiftHeight2}");
+ file.LiftHeight2 = SlicerFile.LiftHeight2;
+ changed = true;
+ }
- if (file.CanUseWaitTimeAfterLift && file.WaitTimeAfterLift != SlicerFile.WaitTimeAfterLift)
- {
- details.Add($"Wait Time After Lift: {file.WaitTimeAfterLift} to {SlicerFile.WaitTimeAfterLift}");
- file.WaitTimeAfterLift = SlicerFile.WaitTimeAfterLift;
- changed = true;
- }
+ if (file.CanUseLiftSpeed2 && file.LiftSpeed2 != SlicerFile.LiftSpeed2)
+ {
+ details.Add($"Lift Speed 2: {file.LiftSpeed2} to {SlicerFile.LiftSpeed2}");
+ file.LiftSpeed2 = SlicerFile.LiftSpeed2;
+ changed = true;
+ }
- if (file.CanUseBottomRetractSpeed && file.BottomRetractSpeed != SlicerFile.BottomRetractSpeed)
- {
- details.Add($"Bottom Retract Speed: {file.BottomRetractSpeed} to {SlicerFile.BottomRetractSpeed}");
- file.BottomRetractSpeed = SlicerFile.BottomRetractSpeed;
- changed = true;
- }
+ if (file.CanUseBottomWaitTimeAfterLift && file.BottomWaitTimeAfterLift != SlicerFile.BottomWaitTimeAfterLift)
+ {
+ details.Add($"Bottom Wait Time After Lift: {file.BottomWaitTimeAfterLift} to {SlicerFile.BottomWaitTimeAfterLift}");
+ file.BottomWaitTimeAfterLift = SlicerFile.BottomWaitTimeAfterLift;
+ changed = true;
+ }
- if (file.CanUseRetractSpeed && file.RetractSpeed != SlicerFile.RetractSpeed)
- {
- details.Add($"Retract Speed: {file.RetractSpeed} to {SlicerFile.RetractSpeed}");
- file.RetractSpeed = SlicerFile.RetractSpeed;
- changed = true;
- }
+ if (file.CanUseWaitTimeAfterLift && file.WaitTimeAfterLift != SlicerFile.WaitTimeAfterLift)
+ {
+ details.Add($"Wait Time After Lift: {file.WaitTimeAfterLift} to {SlicerFile.WaitTimeAfterLift}");
+ file.WaitTimeAfterLift = SlicerFile.WaitTimeAfterLift;
+ changed = true;
+ }
- if (file.CanUseBottomRetractHeight2 && file.BottomRetractHeight2 != SlicerFile.BottomRetractHeight2)
- {
- details.Add($"Bottom Retract Height 2: {file.BottomRetractHeight2} to {SlicerFile.BottomRetractHeight2}");
- file.BottomRetractHeight2 = SlicerFile.BottomRetractHeight2;
- changed = true;
- }
+ if (file.CanUseBottomRetractSpeed && file.BottomRetractSpeed != SlicerFile.BottomRetractSpeed)
+ {
+ details.Add($"Bottom Retract Speed: {file.BottomRetractSpeed} to {SlicerFile.BottomRetractSpeed}");
+ file.BottomRetractSpeed = SlicerFile.BottomRetractSpeed;
+ changed = true;
+ }
- if (file.CanUseBottomRetractSpeed2 && file.BottomRetractSpeed2 != SlicerFile.BottomRetractSpeed2)
- {
- details.Add($"Bottom Retract Speed2: {file.BottomRetractSpeed2} to {SlicerFile.BottomRetractSpeed2}");
- file.BottomRetractSpeed2 = SlicerFile.BottomRetractSpeed2;
- changed = true;
- }
+ if (file.CanUseRetractSpeed && file.RetractSpeed != SlicerFile.RetractSpeed)
+ {
+ details.Add($"Retract Speed: {file.RetractSpeed} to {SlicerFile.RetractSpeed}");
+ file.RetractSpeed = SlicerFile.RetractSpeed;
+ changed = true;
+ }
- if (file.CanUseRetractHeight2 && file.RetractHeight2 != SlicerFile.RetractHeight2)
- {
- details.Add($"Retract Height 2: {file.RetractHeight2} to {SlicerFile.RetractHeight2}");
- file.RetractHeight2 = SlicerFile.RetractHeight2;
- changed = true;
- }
+ if (file.CanUseBottomRetractHeight2 && file.BottomRetractHeight2 != SlicerFile.BottomRetractHeight2)
+ {
+ details.Add($"Bottom Retract Height 2: {file.BottomRetractHeight2} to {SlicerFile.BottomRetractHeight2}");
+ file.BottomRetractHeight2 = SlicerFile.BottomRetractHeight2;
+ changed = true;
+ }
- if (file.CanUseRetractSpeed2 && file.RetractSpeed2 != SlicerFile.RetractSpeed2)
- {
- details.Add($"Retract Speed 2: {file.RetractSpeed2} to {SlicerFile.RetractSpeed2}");
- file.RetractSpeed2 = SlicerFile.RetractSpeed2;
- changed = true;
- }
+ if (file.CanUseBottomRetractSpeed2 && file.BottomRetractSpeed2 != SlicerFile.BottomRetractSpeed2)
+ {
+ details.Add($"Bottom Retract Speed2: {file.BottomRetractSpeed2} to {SlicerFile.BottomRetractSpeed2}");
+ file.BottomRetractSpeed2 = SlicerFile.BottomRetractSpeed2;
+ changed = true;
+ }
- if (file.CanUseBottomLightPWM && file.BottomLightPWM != SlicerFile.BottomLightPWM)
- {
- details.Add($"Bottom Light PWM: {file.BottomLightPWM} to {SlicerFile.BottomLightPWM}");
- file.BottomLightPWM = SlicerFile.BottomLightPWM;
- changed = true;
- }
+ if (file.CanUseRetractHeight2 && file.RetractHeight2 != SlicerFile.RetractHeight2)
+ {
+ details.Add($"Retract Height 2: {file.RetractHeight2} to {SlicerFile.RetractHeight2}");
+ file.RetractHeight2 = SlicerFile.RetractHeight2;
+ changed = true;
+ }
- if (file.CanUseLightPWM && file.LightPWM != SlicerFile.LightPWM)
- {
- details.Add($"Light PWM: {file.LightPWM} to {SlicerFile.LightPWM}");
- file.LightPWM = SlicerFile.LightPWM;
- changed = true;
- }
+ if (file.CanUseRetractSpeed2 && file.RetractSpeed2 != SlicerFile.RetractSpeed2)
+ {
+ details.Add($"Retract Speed 2: {file.RetractSpeed2} to {SlicerFile.RetractSpeed2}");
+ file.RetractSpeed2 = SlicerFile.RetractSpeed2;
+ changed = true;
+ }
- // if (file.CanUseXXX && file.XXX != SlicerFile.XXX)
- // {
- // details.Add($"XXX: {file.XXX} to {SlicerFile.XXX}");
- // file.XXX = SlicerFile.XXX;
- // changed = true;
- // }
+ if (file.CanUseBottomLightPWM && file.BottomLightPWM != SlicerFile.BottomLightPWM)
+ {
+ details.Add($"Bottom Light PWM: {file.BottomLightPWM} to {SlicerFile.BottomLightPWM}");
+ file.BottomLightPWM = SlicerFile.BottomLightPWM;
+ changed = true;
+ }
- // Bail out if not changed
- if (!changed)
- {
- return new Tuple<ResultStatus, string, List<string>>(ResultStatus.Unchanged, filePath, null);
- }
+ if (file.CanUseLightPWM && file.LightPWM != SlicerFile.LightPWM)
+ {
+ details.Add($"Light PWM: {file.LightPWM} to {SlicerFile.LightPWM}");
+ file.LightPWM = SlicerFile.LightPWM;
+ changed = true;
+ }
- // Save the changed file
- file.Save();
+ // if (file.CanUseXXX && file.XXX != SlicerFile.XXX)
+ // {
+ // details.Add($"XXX: {file.XXX} to {SlicerFile.XXX}");
+ // file.XXX = SlicerFile.XXX;
+ // changed = true;
+ // }
- return new Tuple<ResultStatus, string, List<string>>(ResultStatus.Success, filePath, details);
+ // Bail out if not changed
+ if (!changed)
+ {
+ return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.Unchanged, filePath, null);
}
+ // Save the changed file
+ file.Save();
+
+ return new Tuple<ResultStatus, string, List<string>?>(ResultStatus.Success, filePath, details);
}
-}
+
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptCustomGCode.cs b/UVtools.ScriptSample/ScriptCustomGCode.cs
index 5d33cb3..b243c0f 100644
--- a/UVtools.ScriptSample/ScriptCustomGCode.cs
+++ b/UVtools.ScriptSample/ScriptCustomGCode.cs
@@ -13,138 +13,137 @@ using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptCustomGCode : ScriptGlobals
{
/// <summary>
- /// Change layer properties to random values
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptCustomGCode : ScriptGlobals
+ public void ScriptInit()
{
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
- {
- Script.Name = "Custo gcode generator";
- Script.Description = "Generates custom gcode and saves the file";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
- }
+ Script.Name = "Custo gcode generator";
+ Script.Description = "Generates custom gcode and saves the file";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ }
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
- {
- return SlicerFile.SupportsGCode ? null : "GCode is not supported on this file";
- }
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return SlicerFile.SupportsGCode ? null : "GCode is not supported on this file";
+ }
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ var gcode = SlicerFile.GCode!;
+ gcode.Clear();
+
+ float pos = 1;
+ //float layerHeight = 0.025f;
+ //float liftHeight = 4.5f;
+ float feedrate = gcode.ConvertFromMillimetersPerMinute(150);
+ float lightoff = gcode.ConvertFromSeconds(1f);
+
+ gcode.AppendStartGCode();
+ //gcode.AppendShowImageM6054(gcode.GetShowImageString(0));
+ //gcode.AppendWaitG4(gcode.ConvertFromSeconds(2));
+ //gcode.AppendTurnLightM106(255);
+ gcode.AppendWaitG4(gcode.ConvertFromSeconds(1));
+ //gcode.AppendTurnLightM106(0);
+ gcode.AppendLiftMoveG0(20, feedrate, pos, feedrate);
+ gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
+
+ // 0.025 test
+ /*gcode.AppendComment("0.025 layer height simulated print test");
+ for (int i = 0; i < 50; i++)
{
- var gcode = SlicerFile.GCode;
- gcode.Clear();
-
- float pos = 1;
- float layerHeight = 0.025f;
- float liftHeight = 4.5f;
- float feedrate = gcode.ConvertFromMillimetersPerMinute(150);
- float lightoff = gcode.ConvertFromSeconds(1f);
-
- gcode.AppendStartGCode();
- //gcode.AppendShowImageM6054(gcode.GetShowImageString(0));
- //gcode.AppendWaitG4(gcode.ConvertFromSeconds(2));
- //gcode.AppendTurnLightM106(255);
- gcode.AppendWaitG4(gcode.ConvertFromSeconds(1));
- //gcode.AppendTurnLightM106(0);
- gcode.AppendLiftMoveG0(20, feedrate, pos, feedrate);
- gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
-
- // 0.025 test
- /*gcode.AppendComment("0.025 layer height simulated print test");
- for (int i = 0; i < 50; i++)
- {
- pos = Layer.RoundHeight(pos + layerHeight);
- var liftPos = Layer.RoundHeight(pos + liftHeight);
- gcode.AppendLiftMoveG0(liftPos, feedrate, pos, feedrate, lightoff);
- }*/
-
- // 0.01 test
- /*gcode.AppendComment("0.01 layer height simulated print test");
- pos = 1;
- layerHeight = 0.01f;
+ pos = Layer.RoundHeight(pos + layerHeight);
+ var liftPos = Layer.RoundHeight(pos + liftHeight);
+ gcode.AppendLiftMoveG0(liftPos, feedrate, pos, feedrate, lightoff);
+ }*/
+ // 0.01 test
+ /*gcode.AppendComment("0.01 layer height simulated print test");
+ pos = 1;
+ layerHeight = 0.01f;
- gcode.AppendMoveG0(pos, feedrate);
- gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
- for (int i = 0; i < 50; i++)
- {
- pos = Layer.RoundHeight(pos + layerHeight);
- var liftPos = Layer.RoundHeight(pos + liftHeight);
- gcode.AppendLiftMoveG0(liftPos, feedrate, pos, feedrate, lightoff);
- }*/
-
- // 0.001 test
- /*gcode.AppendComment("0.001 layer height simulated print test");
- pos = 1;
- layerHeight = 0.001f;
- liftHeight = 1;
- gcode.AppendMoveG0(pos, feedrate);
- gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
- for (int i = 0; i < 50; i++)
- {
- pos = Layer.RoundHeight(pos + layerHeight);
- var liftPos = Layer.RoundHeight(pos + liftHeight);
- gcode.AppendLiftMoveG0(liftPos, feedrate, pos, feedrate, lightoff);
- }*/
+ gcode.AppendMoveG0(pos, feedrate);
+ gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
+ for (int i = 0; i < 50; i++)
+ {
+ pos = Layer.RoundHeight(pos + layerHeight);
+ var liftPos = Layer.RoundHeight(pos + liftHeight);
+ gcode.AppendLiftMoveG0(liftPos, feedrate, pos, feedrate, lightoff);
+ }*/
+
+ // 0.001 test
+ /*gcode.AppendComment("0.001 layer height simulated print test");
+ pos = 1;
+ layerHeight = 0.001f;
+ liftHeight = 1;
+
+ gcode.AppendMoveG0(pos, feedrate);
+ gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
+ for (int i = 0; i < 50; i++)
+ {
+ pos = Layer.RoundHeight(pos + layerHeight);
+ var liftPos = Layer.RoundHeight(pos + liftHeight);
+ gcode.AppendLiftMoveG0(liftPos, feedrate, pos, feedrate, lightoff);
+ }*/
- /*// 0.05 backlash test
- gcode.AppendComment("0.05 backlash test");
- pos = 1;
- layerHeight = 0.02f;
+ /*// 0.05 backlash test
+ gcode.AppendComment("0.05 backlash test");
+ pos = 1;
+ layerHeight = 0.02f;
- gcode.AppendMoveG0(pos, feedrate);
- //gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
- for (int i = 0; i < 50; i++)
- {
- var liftPos = Layer.RoundHeight(pos + layerHeight);
- gcode.AppendMoveG0(liftPos, feedrate);
- gcode.AppendWaitG4(lightoff);
- gcode.AppendMoveG0(pos, feedrate);
- gcode.AppendWaitG4(lightoff);
- }
- */
-
- gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(150), 1, gcode.ConvertFromMillimetersPerMinute(150));
- gcode.AppendWaitG4(lightoff);
- gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(180), 1.5f, gcode.ConvertFromMillimetersPerMinute(180));
- gcode.AppendWaitG4(lightoff);
- gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(195), 2, gcode.ConvertFromMillimetersPerMinute(195));
- gcode.AppendWaitG4(lightoff);
- gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(200), 2.5f, gcode.ConvertFromMillimetersPerMinute(200));
- gcode.AppendWaitG4(lightoff);
- gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(250), 3f, gcode.ConvertFromMillimetersPerMinute(250));
+ gcode.AppendMoveG0(pos, feedrate);
+ //gcode.AppendWaitG4(gcode.ConvertFromSeconds(5));
+ for (int i = 0; i < 50; i++)
+ {
+ var liftPos = Layer.RoundHeight(pos + layerHeight);
+ gcode.AppendMoveG0(liftPos, feedrate);
gcode.AppendWaitG4(lightoff);
- gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(300), 3.5f, gcode.ConvertFromMillimetersPerMinute(300));
+ gcode.AppendMoveG0(pos, feedrate);
gcode.AppendWaitG4(lightoff);
+ }
+ */
- gcode.AppendMoveG0(100, feedrate);
- //gcode.AppendTurnMotors(false);
+ gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(150), 1, gcode.ConvertFromMillimetersPerMinute(150));
+ gcode.AppendWaitG4(lightoff);
+ gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(180), 1.5f, gcode.ConvertFromMillimetersPerMinute(180));
+ gcode.AppendWaitG4(lightoff);
+ gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(195), 2, gcode.ConvertFromMillimetersPerMinute(195));
+ gcode.AppendWaitG4(lightoff);
+ gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(200), 2.5f, gcode.ConvertFromMillimetersPerMinute(200));
+ gcode.AppendWaitG4(lightoff);
+ gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(250), 3f, gcode.ConvertFromMillimetersPerMinute(250));
+ gcode.AppendWaitG4(lightoff);
+ gcode.AppendLiftMoveG0(20, gcode.ConvertFromMillimetersPerMinute(300), 3.5f, gcode.ConvertFromMillimetersPerMinute(300));
+ gcode.AppendWaitG4(lightoff);
+ gcode.AppendMoveG0(100, feedrate);
+ //gcode.AppendTurnMotors(false);
- SlicerFile.SuppressRebuildGCode = true;
- SlicerFile.Save(Progress);
- SlicerFile.SuppressRebuildGCode = false;
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
- }
+ SlicerFile.SuppressRebuildGCode = true;
+ SlicerFile.Save(Progress);
+ SlicerFile.SuppressRebuildGCode = false;
+
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptDebandingZSample.cs b/UVtools.ScriptSample/ScriptDebandingZSample.cs
index 00bec49..43479e5 100644
--- a/UVtools.ScriptSample/ScriptDebandingZSample.cs
+++ b/UVtools.ScriptSample/ScriptDebandingZSample.cs
@@ -11,157 +11,159 @@ using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.Scripting;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptDebandingZSample : ScriptGlobals
{
+ readonly ScriptCheckBoxInput CreateEmptyLayerInput = new()
+ {
+ Label = "Create a first empty layer to overcome printer firmware limitation",
+ ToolTip = "Some printers will not respect wait time for the first layer, introducing the problem once again. Use this option to by pass that",
+ Value = true
+ };
+
+ readonly ScriptNumericalInput<decimal> BottomSafeDebandingHeightInput = new()
+ {
+ Label = "Safe height for the large debanding time",
+ //ToolTip = "Margin in pixels to inset from object edge",
+ Unit = "mm",
+ Minimum = 0.1m,
+ Maximum = 50,
+ Increment = 0.5m,
+ Value = 0.5m,
+ DecimalPlates = 2,
+ };
+
+ readonly ScriptNumericalInput<decimal> BottomWaitTimeBeforeCureInput = new()
+ {
+ Label = "Large debanding wait time before cure",
+ ToolTip = "Time to wait before cure a debanding layer",
+ Unit = "s",
+ Minimum = 5,
+ Maximum = 300,
+ Increment = 1,
+ Value = 40,
+ DecimalPlates = 2,
+ };
+
+ readonly ScriptNumericalInput<decimal> NormalWaitTimeBeforeCureInput = new()
+ {
+ Label = "Normal wait time before cure",
+ ToolTip = "Time to wait before cure a normal layer",
+ Unit = "s",
+ Minimum = 1,
+ Maximum = 300,
+ Increment = 1,
+ Value = 3,
+ DecimalPlates = 2,
+ };
+
+ readonly ScriptNumericalInput<decimal> BottomWaitTimeAfterCureInput = new()
+ {
+ Label = "Bottom wait time after cure",
+ ToolTip = "Time to wait after cure a bottom layer",
+ Unit = "s",
+ Minimum = 0,
+ Maximum = 100,
+ Increment = 1,
+ Value = 5,
+ DecimalPlates = 2,
+ };
+
+ readonly ScriptNumericalInput<decimal> NormalWaitTimeAfterCureInput = new()
+ {
+ Label = "Normal wait time after cure",
+ ToolTip = "Time to wait after cure a normal layer",
+ Unit = "s",
+ Minimum = 0,
+ Maximum = 100,
+ Increment = 1,
+ Value = 2,
+ DecimalPlates = 2,
+ };
+
/// <summary>
- /// Change layer properties to random values
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptDebandingZSample : ScriptGlobals
+ public void ScriptInit()
{
- ScriptCheckBoxInput CreateEmptyLayerInput = new()
- {
- Label = "Create a first empty layer to overcome printer firmware limitation",
- ToolTip = "Some printers will not respect wait time for the first layer, introducing the problem once again. Use this option to by pass that",
- Value = true
- };
+ Script.Name = "Debanding Z with wait time";
+ Script.Description = "Applies wait time at certain layers to help layer adhesion and debanding the Z axis.\n" +
+ "Based on the guide: https://bit.ly/3nkXAOa\n";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 2);
+ if (SlicerFile.SupportsGCode) CreateEmptyLayerInput.Value = false;
+ Script.UserInputs.Add(CreateEmptyLayerInput);
+ Script.UserInputs.Add(BottomSafeDebandingHeightInput);
+ Script.UserInputs.Add(BottomWaitTimeBeforeCureInput);
+ Script.UserInputs.Add(NormalWaitTimeBeforeCureInput);
+ if(SlicerFile.CanUseBottomWaitTimeAfterCure) Script.UserInputs.Add(BottomWaitTimeAfterCureInput);
+ if(SlicerFile.CanUseWaitTimeAfterCure) Script.UserInputs.Add(NormalWaitTimeAfterCureInput);
+ }
- ScriptNumericalInput<decimal> BottomSafeDebandingHeightInput = new()
- {
- Label = "Safe height for the large debanding time",
- //ToolTip = "Margin in pixels to inset from object edge",
- Unit = "mm",
- Minimum = 0.1m,
- Maximum = 50,
- Increment = 0.5m,
- Value = 0.5m,
- DecimalPlates = 2,
- };
-
- ScriptNumericalInput<decimal> BottomWaitTimeBeforeCureInput = new()
- {
- Label = "Large debanding wait time before cure",
- ToolTip = "Time to wait before cure a debanding layer",
- Unit = "s",
- Minimum = 5,
- Maximum = 300,
- Increment = 1,
- Value = 40,
- DecimalPlates = 2,
- };
-
- ScriptNumericalInput<decimal> NormalWaitTimeBeforeCureInput = new()
- {
- Label = "Normal wait time before cure",
- ToolTip = "Time to wait before cure a normal layer",
- Unit = "s",
- Minimum = 1,
- Maximum = 300,
- Increment = 1,
- Value = 3,
- DecimalPlates = 2,
- };
-
- ScriptNumericalInput<decimal> BottomWaitTimeAfterCureInput = new()
- {
- Label = "Bottom wait time after cure",
- ToolTip = "Time to wait after cure a bottom layer",
- Unit = "s",
- Minimum = 0,
- Maximum = 100,
- Increment = 1,
- Value = 5,
- DecimalPlates = 2,
- };
-
- ScriptNumericalInput<decimal> NormalWaitTimeAfterCureInput = new()
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return SlicerFile.CanUseAnyLightOffDelay || SlicerFile.CanUseAnyWaitTimeBeforeCure ? null : "Your printer/file format is not supported.";
+ }
+
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ Progress.Reset("Changing layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process
+
+ if (SlicerFile.CanUseAnyWaitTime)
{
- Label = "Normal wait time after cure",
- ToolTip = "Time to wait after cure a normal layer",
- Unit = "s",
- Minimum = 0,
- Maximum = 100,
- Increment = 1,
- Value = 2,
- DecimalPlates = 2,
- };
-
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
+ SlicerFile.BottomLightOffDelay = 0;
+ SlicerFile.LightOffDelay = 0;
+ SlicerFile.BottomWaitTimeBeforeCure = (float) BottomWaitTimeBeforeCureInput.Value;
+ SlicerFile.WaitTimeBeforeCure = (float)NormalWaitTimeBeforeCureInput.Value;
+ }
+ else
{
- Script.Name = "Debanding Z with wait time";
- Script.Description = "Applies wait time at certain layers to help layer adhesion and debanding the Z axis.\n" +
- "Based on the guide: https://bit.ly/3nkXAOa\n";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 2);
- if (SlicerFile.SupportsGCode) CreateEmptyLayerInput.Value = false;
- Script.UserInputs.Add(CreateEmptyLayerInput);
- Script.UserInputs.Add(BottomSafeDebandingHeightInput);
- Script.UserInputs.Add(BottomWaitTimeBeforeCureInput);
- Script.UserInputs.Add(NormalWaitTimeBeforeCureInput);
- if(SlicerFile.CanUseBottomWaitTimeAfterCure) Script.UserInputs.Add(BottomWaitTimeAfterCureInput);
- if(SlicerFile.CanUseWaitTimeAfterCure) Script.UserInputs.Add(NormalWaitTimeAfterCureInput);
+ SlicerFile.SetBottomLightOffDelay((float)BottomWaitTimeBeforeCureInput.Value);
+ SlicerFile.SetNormalLightOffDelay((float)NormalWaitTimeBeforeCureInput.Value);
}
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
+ if (SlicerFile.CanUseBottomWaitTimeAfterCure) SlicerFile.BottomWaitTimeAfterCure = (float) BottomWaitTimeAfterCureInput.Value;
+ if (SlicerFile.CanUseWaitTimeAfterCure) SlicerFile.WaitTimeAfterCure = (float)NormalWaitTimeAfterCureInput.Value;
+
+ foreach (var layer in SlicerFile)
{
- return SlicerFile.CanUseAnyLightOffDelay || SlicerFile.CanUseAnyWaitTimeBeforeCure ? null : "Your printer/file format is not supported.";
+ if((decimal)layer.PositionZ > BottomSafeDebandingHeightInput.Value) break;
+ layer.SetWaitTimeBeforeCureOrLightOffDelay((float) BottomWaitTimeBeforeCureInput.Value);
}
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
+ if (CreateEmptyLayerInput.Value)
{
- Progress.Reset("Changing layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process
-
- if (SlicerFile.CanUseAnyWaitTime)
+ var firstLayer = SlicerFile.FirstLayer;
+ if (firstLayer is not null)
{
- SlicerFile.BottomLightOffDelay = 0;
- SlicerFile.LightOffDelay = 0;
- SlicerFile.BottomWaitTimeBeforeCure = (float) BottomWaitTimeBeforeCureInput.Value;
- SlicerFile.WaitTimeBeforeCure = (float)NormalWaitTimeBeforeCureInput.Value;
- }
- else
- {
- SlicerFile.SetBottomLightOffDelay((float)BottomWaitTimeBeforeCureInput.Value);
- SlicerFile.SetNormalLightOffDelay((float)NormalWaitTimeBeforeCureInput.Value);
- }
-
- if (SlicerFile.CanUseBottomWaitTimeAfterCure) SlicerFile.BottomWaitTimeAfterCure = (float) BottomWaitTimeAfterCureInput.Value;
- if (SlicerFile.CanUseWaitTimeAfterCure) SlicerFile.WaitTimeAfterCure = (float)NormalWaitTimeAfterCureInput.Value;
-
- foreach (var layer in SlicerFile)
- {
- if((decimal)layer.PositionZ > BottomSafeDebandingHeightInput.Value) break;
- layer.SetWaitTimeBeforeCureOrLightOffDelay((float) BottomWaitTimeBeforeCureInput.Value);
- }
-
- if (CreateEmptyLayerInput.Value)
- {
- var firstLayer = SlicerFile.FirstLayer;
if (firstLayer.NonZeroPixelCount > 1) // First layer is not blank as it seems, lets create one
{
firstLayer = firstLayer.Clone();
using var mat = EmguExtensions.InitMat(SlicerFile.Resolution);
var pixelPos = firstLayer.BoundingRectangle.Center();
- mat.SetByte(pixelPos.X, pixelPos.Y, 1); // Print a very fade pixel to ignore empty layer detection
+ mat.SetByte(pixelPos.X, pixelPos.Y,
+ 1); // Print a very fade pixel to ignore empty layer detection
firstLayer.LayerMat = mat;
firstLayer.ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.05f;
firstLayer.SetNoDelays();
- SlicerFile.SuppressRebuildPropertiesWork(() => { SlicerFile.LayerManager.Prepend(firstLayer); });
+ SlicerFile.SuppressRebuildPropertiesWork(() => { SlicerFile.Prepend(firstLayer); });
}
-
}
-
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
}
+
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptInsetSample.cs b/UVtools.ScriptSample/ScriptInsetSample.cs
index 85b9dd4..9b86059 100644
--- a/UVtools.ScriptSample/ScriptInsetSample.cs
+++ b/UVtools.ScriptSample/ScriptInsetSample.cs
@@ -15,124 +15,123 @@ using Emgu.CV;
using Emgu.CV.CvEnum;
using UVtools.Core;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Performs a black inset around objects
+/// </summary>
+public class ScriptInsetSample : ScriptGlobals
{
+ readonly ScriptNumericalInput<ushort> InsetMarginFromEdge = new()
+ {
+ Label = "Inset from edge",
+ ToolTip = "Margin in pixels to inset from object edge",
+ Unit = "px",
+ Minimum = 1,
+ Maximum = ushort.MaxValue,
+ Increment = 1,
+ Value = 10
+ };
+
+ readonly ScriptNumericalInput<ushort> InsetThickness = new()
+ {
+ Label = "Inset line thickness",
+ ToolTip = "Inset line thickness in pixels",
+ Unit = "px",
+ Minimum = 1,
+ Maximum = ushort.MaxValue,
+ Increment = 1,
+ Value = 5
+ };
+
/// <summary>
- /// Performs a black inset around objects
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptInsetSample : ScriptGlobals
+ public void ScriptInit()
{
- ScriptNumericalInput<ushort> InsetMarginFromEdge = new()
- {
- Label = "Inset from edge",
- ToolTip = "Margin in pixels to inset from object edge",
- Unit = "px",
- Minimum = 1,
- Maximum = ushort.MaxValue,
- Increment = 1,
- Value = 10
- };
-
- ScriptNumericalInput<ushort> InsetThickness = new()
+ Script.Name = "Inset";
+ Script.Description = "Performs a black inset around objects";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ Script.UserInputs.AddRange(new[]
{
- Label = "Inset line thickness",
- ToolTip = "Inset line thickness in pixels",
- Unit = "px",
- Minimum = 1,
- Maximum = ushort.MaxValue,
- Increment = 1,
- Value = 5
- };
-
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
+ InsetMarginFromEdge,
+ InsetThickness
+ });
+ }
+
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ StringBuilder sb = new();
+
+ if (InsetMarginFromEdge.Value < InsetMarginFromEdge.Minimum)
{
- Script.Name = "Inset";
- Script.Description = "Performs a black inset around objects";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
- Script.UserInputs.AddRange(new[]
- {
- InsetMarginFromEdge,
- InsetThickness
- });
+ sb.AppendLine($"Inset edge margin must be at least {InsetMarginFromEdge.Minimum}{InsetMarginFromEdge.Unit}");
}
-
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
+ if (InsetThickness.Value < InsetThickness.Minimum)
{
- StringBuilder sb = new();
-
- if (InsetMarginFromEdge.Value < InsetMarginFromEdge.Minimum)
- {
- sb.AppendLine($"Inset edge margin must be at least {InsetMarginFromEdge.Minimum}{InsetMarginFromEdge.Unit}");
- }
- if (InsetThickness.Value < InsetThickness.Minimum)
- {
- sb.AppendLine($"Inset thickness must be at least {InsetThickness.Minimum}{InsetThickness.Unit}");
- }
-
- return sb.ToString();
+ sb.AppendLine($"Inset thickness must be at least {InsetThickness.Minimum}{InsetThickness.Unit}");
}
+
+ return sb.ToString();
+ }
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
- {
- var anchor = new Point(-1, -1); // Kernel anchor, -1, -1 = center
- var kernel =
- CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); // Rectangle 3x3 kernel
- Progress.Reset("Inset layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ var anchor = new Point(-1, -1); // Kernel anchor, -1, -1 = center
+ var kernel =
+ CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); // Rectangle 3x3 kernel
+ Progress.Reset("Inset layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process
- // Loop user selected layers in parallel, this will put each core of CPU working here on parallel
- Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (Progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
+ // Loop user selected layers in parallel, this will put each core of CPU working here on parallel
+ Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (Progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
- var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use
- using var mat = layer.LayerMat; // Gets this layer mat/image
- var original = mat.Clone(); // Keep a original mat copy
- using var erodeMat = new Mat(); // Creates a temporary mat for the eroded image
- using var wallMat = new Mat(); // Creates a temporary mat for the wall image
+ var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use
+ using var mat = layer.LayerMat; // Gets this layer mat/image
+ var original = mat.Clone(); // Keep a original mat copy
+ using var erodeMat = new Mat(); // Creates a temporary mat for the eroded image
+ using var wallMat = new Mat(); // Creates a temporary mat for the wall image
- var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
+ var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
- // Erode original image by InsetMarginFromEdge pixels, so we get the offset margin from image and put new image on erodeMat
- CvInvoke.Erode(target, erodeMat, kernel, anchor, InsetMarginFromEdge.Value, BorderType.Reflect101, default);
+ // Erode original image by InsetMarginFromEdge pixels, so we get the offset margin from image and put new image on erodeMat
+ CvInvoke.Erode(target, erodeMat, kernel, anchor, InsetMarginFromEdge.Value, BorderType.Reflect101, default);
- // Now erode the eroded image with InsetThickness pixels, so we get the original-margin-thickness image and put the new image on wallMat
- CvInvoke.Erode(erodeMat, wallMat, kernel, anchor, InsetThickness.Value, BorderType.Reflect101, default);
+ // Now erode the eroded image with InsetThickness pixels, so we get the original-margin-thickness image and put the new image on wallMat
+ CvInvoke.Erode(erodeMat, wallMat, kernel, anchor, InsetThickness.Value, BorderType.Reflect101, default);
- // Subtract walls image from eroded image, so we get only the inset line pixels in white and put back into wallMat
- CvInvoke.Subtract(erodeMat, wallMat, wallMat);
+ // Subtract walls image from eroded image, so we get only the inset line pixels in white and put back into wallMat
+ CvInvoke.Subtract(erodeMat, wallMat, wallMat);
- // Invert pixels of wallMat so the whites will become black and blacks whites
- CvInvoke.BitwiseNot(wallMat, wallMat);
+ // Invert pixels of wallMat so the whites will become black and blacks whites
+ CvInvoke.BitwiseNot(wallMat, wallMat);
- // Bitwise And original image with the modified image and put back into mat
- // This will keep only the pixels that are positive in both mat's, so mat[n] & wallMat[n] must both have a positive pixel value (> 0)
- CvInvoke.BitwiseAnd(target, wallMat, target);
+ // Bitwise And original image with the modified image and put back into mat
+ // This will keep only the pixels that are positive in both mat's, so mat[n] & wallMat[n] must both have a positive pixel value (> 0)
+ CvInvoke.BitwiseAnd(target, wallMat, target);
- // Apply the results only to the selected masked area, if user selected one
- Operation.ApplyMask(original, target);
+ // Apply the results only to the selected masked area, if user selected one
+ Operation.ApplyMask(original, target);
- // Set current layer image with the modified mat we just manipulated
- layer.LayerMat = mat;
+ // Set current layer image with the modified mat we just manipulated
+ layer.LayerMat = mat;
- // Increment progress bar by 1
- Progress.LockAndIncrement();
- });
+ // Increment progress bar by 1
+ Progress.LockAndIncrement();
+ });
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
- }
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs b/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs
index d272108..155bee6 100644
--- a/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs
+++ b/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs
@@ -16,111 +16,110 @@ using Emgu.CV.Structure;
using UVtools.Core;
using UVtools.Core.Extensions;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Performs a black inset around objects
+/// </summary>
+public class ScriptLightBleedCompensationSample : ScriptGlobals
{
- /// <summary>
- /// Performs a black inset around objects
- /// </summary>
- public class ScriptLightBleedCompensationSample : ScriptGlobals
+ readonly ScriptTextBoxInput BrightnessesInput = new()
{
- ScriptTextBoxInput BrightnessesInput = new()
- {
- Label = "Brightnesses",
- ToolTip = "Brightness to reduce each subsequent repeated pixels",
- Unit = "1-255",
- Value = "25,20,15,10,5"
- };
+ Label = "Brightnesses",
+ ToolTip = "Brightness to reduce each subsequent repeated pixels",
+ Unit = "1-255",
+ Value = "25,20,15,10,5"
+ };
- public byte[] Levels
+ public byte[] Levels
+ {
+ get
{
- get
+ List<byte> levels = new();
+ var split = BrightnessesInput.Value!.Split(',', StringSplitOptions.TrimEntries);
+ foreach (var str in split)
{
- List<byte> levels = new();
- var split = BrightnessesInput.Value.Split(',', StringSplitOptions.TrimEntries);
- foreach (var str in split)
- {
- if(!byte.TryParse(str, out var brightness)) continue;
- if(brightness is byte.MinValue or byte.MaxValue) continue;
- levels.Add(brightness);
- }
-
- return levels.ToArray();
+ if(!byte.TryParse(str, out var brightness)) continue;
+ if(brightness is byte.MinValue or byte.MaxValue) continue;
+ levels.Add(brightness);
}
- }
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
- {
- Script.Name = "Light bleed compensation";
- Script.Description = "Dim sequential pixels";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
- Script.UserInputs.Add(BrightnessesInput);
+ return levels.ToArray();
}
+ }
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
- {
- StringBuilder sb = new();
-
- if (Levels.Length == 0)
- {
- sb.AppendLine($"No brightness levels are set");
- }
+ /// <summary>
+ /// Set configurations here, this function trigger just after load a script
+ /// </summary>
+ public void ScriptInit()
+ {
+ Script.Name = "Light bleed compensation";
+ Script.Description = "Dim sequential pixels";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ Script.UserInputs.Add(BrightnessesInput);
+ }
+
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ StringBuilder sb = new();
- return sb.ToString();
+ if (Levels.Length == 0)
+ {
+ sb.AppendLine($"No brightness levels are set");
}
+
+ return sb.ToString();
+ }
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
- {
- Progress.Reset("Bleed compensation", Operation.LayerRangeCount); // Sets the progress name and number of items to process
- var brightnesses = Levels;
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ Progress.Reset("Bleed compensation", Operation.LayerRangeCount); // Sets the progress name and number of items to process
+ var brightnesses = Levels;
- // Loop user selected layers in parallel, this will put each core of CPU working here on parallel
- Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
- {
- if (Progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
+ // Loop user selected layers in parallel, this will put each core of CPU working here on parallel
+ Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd+1, CoreSettings.ParallelOptions, layerIndex =>
+ {
+ if (Progress.Token.IsCancellationRequested) return; // Abort operation, user requested cancellation
- var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use
- using var mat = layer.LayerMat; // Gets this layer mat/image
- var original = mat.Clone(); // Keep a original mat copy
+ var layer = SlicerFile[layerIndex]; // Unpack and expose layer variable for easier use
+ using var mat = layer.LayerMat; // Gets this layer mat/image
+ var original = mat.Clone(); // Keep a original mat copy
- var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
+ var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
- for (byte i = 0; i < brightnesses.Length; i++)
- {
- uint layerIndexNext = (uint) (layerIndex + i + 1);
- if (layerIndexNext > Operation.LayerIndexEnd) break;
- using var subtractMat = EmguExtensions.InitMat(target.Size, new MCvScalar(brightnesses[i]));
+ for (byte i = 0; i < brightnesses.Length; i++)
+ {
+ uint layerIndexNext = (uint) (layerIndex + i + 1);
+ if (layerIndexNext > Operation.LayerIndexEnd) break;
+ using var subtractMat = EmguExtensions.InitMat(target.Size, new MCvScalar(brightnesses[i]));
- using var nextMat = SlicerFile[layerIndexNext].LayerMat;
- var nextMatRoi = Operation.GetRoiOrDefault(nextMat);
+ using var nextMat = SlicerFile[layerIndexNext].LayerMat;
+ var nextMatRoi = Operation.GetRoiOrDefault(nextMat);
- CvInvoke.Subtract(target, subtractMat, target, nextMatRoi);
- }
+ CvInvoke.Subtract(target, subtractMat, target, nextMatRoi);
+ }
- // Apply the results only to the selected masked area, if user selected one
- Operation.ApplyMask(original, target);
+ // Apply the results only to the selected masked area, if user selected one
+ Operation.ApplyMask(original, target);
- // Set current layer image with the modified mat we just manipulated
- layer.LayerMat = mat;
+ // Set current layer image with the modified mat we just manipulated
+ layer.LayerMat = mat;
- // Increment progress bar by 1
- Progress.LockAndIncrement();
- });
+ // Increment progress bar by 1
+ Progress.LockAndIncrement();
+ });
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
- }
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptSetLiftHeightSample.cs b/UVtools.ScriptSample/ScriptSetLiftHeightSample.cs
index b5a8c84..9a74123 100644
--- a/UVtools.ScriptSample/ScriptSetLiftHeightSample.cs
+++ b/UVtools.ScriptSample/ScriptSetLiftHeightSample.cs
@@ -9,68 +9,67 @@
using System;
using UVtools.Core.Scripting;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptSetLiftHeightSample : ScriptGlobals
{
- /// <summary>
- /// Change layer properties to random values
- /// </summary>
- public class ScriptSetLiftHeightSample : ScriptGlobals
+ readonly ScriptNumericalInput<float> BottomLiftHeight = new()
{
- ScriptNumericalInput<float> BottomLiftHeight = new()
- {
- Label = "Bottom lift height",
- Unit = "mm",
- Minimum = 0,
- Maximum = 300,
- Increment = 0.5f,
- Value = 0.5f,
- DecimalPlates = 2
- };
+ Label = "Bottom lift height",
+ Unit = "mm",
+ Minimum = 0,
+ Maximum = 300,
+ Increment = 0.5f,
+ Value = 0.5f,
+ DecimalPlates = 2
+ };
- ScriptNumericalInput<float> LiftHeight = new()
- {
- Label = "Lift height",
- Unit = "mm",
- Minimum = 0,
- Maximum = 300,
- Increment = 0.5f,
- Value = 0.5f,
- DecimalPlates = 2
- };
+ readonly ScriptNumericalInput<float> LiftHeight = new()
+ {
+ Label = "Lift height",
+ Unit = "mm",
+ Minimum = 0,
+ Maximum = 300,
+ Increment = 0.5f,
+ Value = 0.5f,
+ DecimalPlates = 2
+ };
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
- {
- Script.Name = "Change lift height properties";
- Script.Description = "Change file lift height";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
+ /// <summary>
+ /// Set configurations here, this function trigger just after load a script
+ /// </summary>
+ public void ScriptInit()
+ {
+ Script.Name = "Change lift height properties";
+ Script.Description = "Change file lift height";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
- Script.UserInputs.AddRange(new []{ BottomLiftHeight , LiftHeight});
- }
+ Script.UserInputs.AddRange(new []{ BottomLiftHeight , LiftHeight});
+ }
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
- {
- return null;
- }
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return null;
+ }
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
- {
- SlicerFile.BottomLiftHeight = BottomLiftHeight.Value;
- SlicerFile.LiftHeight = LiftHeight.Value;
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ SlicerFile.BottomLiftHeight = BottomLiftHeight.Value;
+ SlicerFile.LiftHeight = LiftHeight.Value;
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
- }
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
index 1511af6..2fd3b73 100644
--- a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
+++ b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
@@ -14,134 +14,134 @@ using UVtools.Core.Extensions;
using UVtools.Core.Operations;
using UVtools.Core.Scripting;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptTestPerLayerSettingsSample : ScriptGlobals
{
+ private readonly ScriptCheckBoxInput InputDoNotUseLift = new()
+ {
+ Label = "Do not perform the lift sequence for same height layers",
+ ToolTip = "Not all printers are compatible with this even if they can maintain same Z position, some will require a obligatory lift/retract",
+ Value = true
+ };
+
/// <summary>
- /// Change layer properties to random values
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptTestPerLayerSettingsSample : ScriptGlobals
+ public void ScriptInit()
{
- private ScriptCheckBoxInput InputDoNotUseLift = new()
- {
- Label = "Do not perform the lift sequence for same height layers",
- ToolTip = "Not all printers are compatible with this even if they can maintain same Z position, some will require a obligatory lift/retract",
- Value = true
- };
-
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
- {
- Script.Name = "Test per layer settings capability with a print";
- Script.Description = "Print this file to check if your printer is able to have per layer independent settings.\n" +
- "1) Load a file that you previous printed into UVtools\n" +
- "2) Run this script\n" +
- "3) Go to File -> Save As, and give it a new name\n" +
- "4) Remove printer VAT and head/plate\n" +
- "5) Print the created file and observe the printer LCD and movements:\n" +
- "- First layer must show the whole face and do the normal lift sequence and raise to the next layer.\n" +
- "- Then all the renaming layers should print one object per layer at same height, if not, then your printer is not able.\n" +
- "Note: Look at printer screen to confirm the layer position, exposure time is set to 5s on first two layers, and 10s on remaining layers.\n" +
- "When a layer start to exposure, count out loud the seconds, should be 5s for the first two, and 10s for the 3rd and on layers.\n" +
- "If the time match then your printer is compatible, otherwise if all layers took around 5s then your printer is not compatible.\n" +
- "If you find yours compatible and not on the official list, please report to us.\n" +
- "https://github.com/sn4k3/UVtools/wiki/Printer-compability-with-per-layer-settings-and-advanced-tools";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
- Script.UserInputs.Add(InputDoNotUseLift);
- }
+ Script.Name = "Test per layer settings capability with a print";
+ Script.Description = "Print this file to check if your printer is able to have per layer independent settings.\n" +
+ "1) Load a file that you previous printed into UVtools\n" +
+ "2) Run this script\n" +
+ "3) Go to File -> Save As, and give it a new name\n" +
+ "4) Remove printer VAT and head/plate\n" +
+ "5) Print the created file and observe the printer LCD and movements:\n" +
+ "- First layer must show the whole face and do the normal lift sequence and raise to the next layer.\n" +
+ "- Then all the renaming layers should print one object per layer at same height, if not, then your printer is not able.\n" +
+ "Note: Look at printer screen to confirm the layer position, exposure time is set to 5s on first two layers, and 10s on remaining layers.\n" +
+ "When a layer start to exposure, count out loud the seconds, should be 5s for the first two, and 10s for the 3rd and on layers.\n" +
+ "If the time match then your printer is compatible, otherwise if all layers took around 5s then your printer is not compatible.\n" +
+ "If you find yours compatible and not on the official list, please report to us.\n" +
+ "https://github.com/sn4k3/UVtools/wiki/Printer-compability-with-per-layer-settings-and-advanced-tools";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 2);
+ Script.MinimumVersionToRun = new Version(3, 0, 0);
+ Script.UserInputs.Add(InputDoNotUseLift);
+ }
+
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return null;
+ }
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ const byte layerCount = 5;
+ const ushort eyeDiameter = 300;
+ const ushort noseHeight = eyeDiameter;
+ const ushort noseThickness = 100;
+ const ushort mouthHeight = 300;
+ const ushort faceSpacing = 150;
+ const LineType lineType = LineType.AntiAlias;
+ Progress.Reset("Generating layers", layerCount); // Sets the progress name and number of items to process
+
+ // Layer 0 = Whole face
+ // Layer 1 = Left eye
+ // Layer 2 = Nose
+ // Layer 3 = Right eye
+ // Layer 4 = Mouth
+ // Exercise for you: Do eyebrows
+ var mats = EmguExtensions.InitMats(layerCount, SlicerFile.Resolution); // Allocate x images with file resolution
+
+ int x, y;
+ int xCenter = (int) (SlicerFile.ResolutionX / 2);
+ //int yCenter = (int) (SlicerFile.ResolutionY / 2);
+
+ // Do the left eye
+ x = xCenter - noseThickness/2 - faceSpacing - eyeDiameter/2;
+ y = faceSpacing;
+ CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Circle(mats[1], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType);
+ Progress++;
+
+ // Do the right eye, the mirror of left...
+ x = (int)(SlicerFile.ResolutionX - x);
+ CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Circle(mats[3], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteColor, -1, lineType);
+ Progress++;
+
+ // Do the noose
+ x = xCenter - noseThickness / 2;
+ CvInvoke.Rectangle(mats[0], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Rectangle(mats[2], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteColor, -1, lineType);
+ Progress++;
+
+ // Do the mouth
+ x = xCenter;
+ y += noseHeight + faceSpacing;
+ CvInvoke.Ellipse(mats[0], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteColor, -1, lineType);
+ CvInvoke.Ellipse(mats[4], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteColor, -1, lineType);
+
+ SlicerFile.AllocateAndSetFromMat(mats); // Replace layers and rebuild properties
+
+ SlicerFile.BottomLayerCount = 1; // Set one bottom layer, the whole face
+ SlicerFile.BottomExposureTime = 5; // Set exposure to be fixed at 5s
+ SlicerFile.ExposureTime = 5; // Set exposure to be fixed at 5s
+
+ // Set layers 2-4 all same z height as layer 1
+ for (int layerIndex = 2; layerIndex < mats.Length; layerIndex++)
{
- return null;
+ SlicerFile[layerIndex].PositionZ = SlicerFile[1].PositionZ;
+ SlicerFile[layerIndex].ExposureTime = 10;
}
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
+ if (InputDoNotUseLift.Value)
{
- const byte layerCount = 5;
- const ushort eyeDiameter = 300;
- const ushort noseHeight = eyeDiameter;
- const ushort noseThickness = 100;
- const ushort mouthHeight = 300;
- const ushort faceSpacing = 150;
- const LineType lineType = LineType.AntiAlias;
- Progress.Reset("Generating layers", layerCount); // Sets the progress name and number of items to process
-
- // Layer 0 = Whole face
- // Layer 1 = Left eye
- // Layer 2 = Nose
- // Layer 3 = Right eye
- // Layer 4 = Mouth
- // Exercise for you: Do eyebrows
- var mats = EmguExtensions.InitMats(layerCount, SlicerFile.Resolution); // Allocate x images with file resolution
-
- int x, y;
- int xCenter = (int) (SlicerFile.ResolutionX / 2);
- //int yCenter = (int) (SlicerFile.ResolutionY / 2);
-
- // Do the left eye
- x = xCenter - noseThickness/2 - faceSpacing - eyeDiameter/2;
- y = faceSpacing;
- CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Circle(mats[1], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType);
- Progress++;
-
- // Do the right eye, the mirror of left...
- x = (int)(SlicerFile.ResolutionX - x);
- CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Circle(mats[3], new Point(x, y), eyeDiameter / 2, EmguExtensions.WhiteColor, -1, lineType);
- Progress++;
-
- // Do the noose
- x = xCenter - noseThickness / 2;
- CvInvoke.Rectangle(mats[0], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Rectangle(mats[2], new Rectangle(x, y, noseThickness, noseHeight), EmguExtensions.WhiteColor, -1, lineType);
- Progress++;
-
- // Do the mouth
- x = xCenter;
- y += noseHeight + faceSpacing;
- CvInvoke.Ellipse(mats[0], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteColor, -1, lineType);
- CvInvoke.Ellipse(mats[4], new Point(x, y), new Size(eyeDiameter+faceSpacing+noseThickness/2, mouthHeight), 0, 0, 180, EmguExtensions.WhiteColor, -1, lineType);
-
- SlicerFile.LayerManager.AllocateAndSetFromMat(mats); // Replace layers and rebuild properties
-
- SlicerFile.BottomLayerCount = 1; // Set one bottom layer, the whole face
- SlicerFile.BottomExposureTime = 5; // Set exposure to be fixed at 5s
- SlicerFile.ExposureTime = 5; // Set exposure to be fixed at 5s
-
- // Set layers 2-4 all same z height as layer 1
- for (int layerIndex = 2; layerIndex < mats.Length; layerIndex++)
- {
- SlicerFile[layerIndex].PositionZ = SlicerFile[1].PositionZ;
- SlicerFile[layerIndex].ExposureTime = 10;
- }
-
- if (InputDoNotUseLift.Value)
- {
- SlicerFile.LayerManager.SetLiftForSamePositionedLayers(SlicerFile.SupportsGCode ? 0 : 0.1f);
- }
- Progress++;
-
- // Move me to the middle
- new OperationMove(SlicerFile).Execute(Progress);
-
- // Generate a cool waves pattern, just because i can :)
- var pdOp = new OperationPixelDimming(SlicerFile) {WallThickness = 25};
- pdOp.GenerateInfill("Waves");
- pdOp.Execute(Progress);
-
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
+ SlicerFile.SetLiftForSamePositionedLayers(SlicerFile.SupportsGCode ? 0 : 0.1f);
}
+ Progress++;
+
+ // Move me to the middle
+ new OperationMove(SlicerFile).Execute(Progress);
+
+ // Generate a cool waves pattern, just because i can :)
+ var pdOp = new OperationPixelDimming(SlicerFile) {WallThickness = 25};
+ pdOp.GenerateInfill("Waves");
+ pdOp.Execute(Progress);
+
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptTester.cs b/UVtools.ScriptSample/ScriptTester.cs
index 5ea3789..15453a2 100644
--- a/UVtools.ScriptSample/ScriptTester.cs
+++ b/UVtools.ScriptSample/ScriptTester.cs
@@ -19,91 +19,90 @@ using UVtools.Core;
using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptChangeLayesrPropertiesSample : ScriptGlobals
{
/// <summary>
- /// Change layer properties to random values
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptChangeLayesrPropertiesSample : ScriptGlobals
+ public void ScriptInit()
{
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
- {
- Script.Name = "Change layer properties";
- Script.Description = "Change layer properties to random values :D";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
- }
+ Script.Name = "Change layer properties";
+ Script.Description = "Change layer properties to random values :D";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ }
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
- {
- return null;
- }
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return null;
+ }
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ var dict = new Dictionary<uint, List<(Point[] points, Rectangle rect)>>();
+ Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
- var dict = new Dictionary<uint, List<(Point[] points, Rectangle rect)>>();
- Parallel.For(Operation.LayerIndexStart, Operation.LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
- {
- using var mat = SlicerFile[layerIndex].LayerMat;
- using var contours = mat.FindContours(out var hierarchy, RetrType.Tree);
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ using var contours = mat.FindContours(out var hierarchy, RetrType.Tree);
- var hollowContours = new List<(Point[] points, Rectangle rect)>();
- dict.Add((uint)layerIndex, hollowContours);
+ var hollowContours = new List<(Point[] points, Rectangle rect)>();
+ dict.Add((uint)layerIndex, hollowContours);
- for (int i = 0; i < contours.Size; i++)
- {
- // Only hollow areas inside model
- if (hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
+ for (int i = 0; i < contours.Size; i++)
+ {
+ // Only hollow areas inside model
+ if (hierarchy[i, EmguContour.HierarchyParent] == -1) continue;
- hollowContours.Add((contours[i].ToArray(), CvInvoke.BoundingRectangle(contours[i])));
- }
- });
+ hollowContours.Add((contours[i].ToArray(), CvInvoke.BoundingRectangle(contours[i])));
+ }
+ });
- foreach (var (layerIndex, contours) in dict)
- {
- if (!dict.TryGetValue(layerIndex + 1, out var nextContours)) continue; // No next layer with results
+ foreach (var (layerIndex, contours) in dict)
+ {
+ if (!dict.TryGetValue(layerIndex + 1, out var nextContours)) continue; // No next layer with results
- foreach (var tuple in contours)
+ foreach (var tuple in contours)
+ {
+ Mat? thisContourMat = null;
+ foreach (var nextTuple in nextContours)
{
- Mat thisContourMat = null;
- foreach (var nextTuple in nextContours)
+ if (!tuple.rect.IntersectsWith(nextTuple.rect)) continue;
+ if (thisContourMat is null)
{
- if (!tuple.rect.IntersectsWith(nextTuple.rect)) continue;
- if (thisContourMat is null)
- {
- thisContourMat = EmguExtensions.InitMat(SlicerFile.Resolution);
- using var vec = new VectorOfPoint(tuple.points);
- CvInvoke.DrawContours(thisContourMat, vec, -1, EmguExtensions.WhiteColor, -1);
- }
-
- using var nextContourMat = thisContourMat.NewBlank();
- using var vecNext = new VectorOfPoint(nextTuple.points);
- CvInvoke.DrawContours(nextContourMat, vecNext, -1, EmguExtensions.WhiteColor, -1);
-
- CvInvoke.BitwiseAnd(thisContourMat, nextContourMat, nextContourMat);
- if (CvInvoke.CountNonZero(nextContourMat) == 0) continue; // Does not intersect!
-
- // Intersecting here!
+ thisContourMat = EmguExtensions.InitMat(SlicerFile.Resolution);
+ using var vec = new VectorOfPoint(tuple.points);
+ CvInvoke.DrawContours(thisContourMat, vec, -1, EmguExtensions.WhiteColor, -1);
}
- thisContourMat?.Dispose();
+ using var nextContourMat = thisContourMat.NewBlank();
+ using var vecNext = new VectorOfPoint(nextTuple.points);
+ CvInvoke.DrawContours(nextContourMat, vecNext, -1, EmguExtensions.WhiteColor, -1);
+
+ CvInvoke.BitwiseAnd(thisContourMat, nextContourMat, nextContourMat);
+ if (CvInvoke.CountNonZero(nextContourMat) == 0) continue; // Does not intersect!
+
+ // Intersecting here!
}
+
+ thisContourMat?.Dispose();
}
+ }
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
- }
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptTimelapseSample.cs b/UVtools.ScriptSample/ScriptTimelapseSample.cs
index 75fda23..1f7ceb9 100644
--- a/UVtools.ScriptSample/ScriptTimelapseSample.cs
+++ b/UVtools.ScriptSample/ScriptTimelapseSample.cs
@@ -11,182 +11,182 @@ using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Scripting;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptTimelapseSample : ScriptGlobals
{
+ readonly ScriptNumericalInput<float> InputPositionZ = new()
+ {
+ Label = "Z position to lift to",
+ Unit = "mm",
+ Minimum = 0.01f,
+ Maximum = 1000,
+ Increment = 1f,
+ Value = 0.5f,
+ DecimalPlates = 2
+ };
+
+ readonly ScriptNumericalInput<ushort> InputRaiseEveryLayerN = new()
+ {
+ Label = "Raise every",
+ Unit = "layer(s)",
+ Minimum = 1,
+ Maximum = 1000,
+ Increment = 1,
+ Value = 10,
+ DecimalPlates = 0
+ };
+
+ readonly ScriptNumericalInput<float> InputWaitTime = new()
+ {
+ Label = "Time to wait on still position",
+ Unit = "s",
+ ToolTip = "Note: Not always possible to wait in some cases",
+ Minimum = 0,
+ Maximum = 30,
+ Increment = 1,
+ Value = 2,
+ DecimalPlates = 2
+ };
+
+ readonly ScriptToggleSwitchInput InputUseVirtualLayer = new()
+ {
+ OnText = "Use blank layers to go to the target height",
+ OffText = "Use lift movement to go to the target height",
+ ToolTip = "Use this option if you printer is unable to use large lifts or waits after lift"
+ };
+
+ readonly ScriptNumericalInput<float> InputLiftSpeed = new()
+ {
+ Label = "Virtual layer lift speed",
+ Unit = "mm/min",
+ Minimum = 50,
+ Maximum = 1000,
+ Increment = 10,
+ Value = 200,
+ DecimalPlates = 2
+ };
+
+ readonly ScriptNumericalInput<float> InputRetractSpeed = new()
+ {
+ Label = "Virtual layer retract speed",
+ Unit = "mm/min",
+ Minimum = 50,
+ Maximum = 1000,
+ Increment = 10,
+ Value = 200,
+ DecimalPlates = 2
+ };
+
/// <summary>
- /// Change layer properties to random values
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptTimelapseSample : ScriptGlobals
+ public void ScriptInit()
{
- ScriptNumericalInput<float> InputPositionZ = new()
- {
- Label = "Z position to lift to",
- Unit = "mm",
- Minimum = 0.01f,
- Maximum = 1000,
- Increment = 1f,
- Value = 0.5f,
- DecimalPlates = 2
- };
-
- ScriptNumericalInput<ushort> InputRaiseEveryLayerN = new()
- {
- Label = "Raise every",
- Unit = "layer(s)",
- Minimum = 1,
- Maximum = 1000,
- Increment = 1,
- Value = 10,
- DecimalPlates = 0
- };
-
- ScriptNumericalInput<float> InputWaitTime = new()
- {
- Label = "Time to wait on still position",
- Unit = "s",
- ToolTip = "Note: Not always possible to wait in some cases",
- Minimum = 0,
- Maximum = 30,
- Increment = 1,
- Value = 2,
- DecimalPlates = 2
- };
-
- private ScriptToggleSwitchInput InputUseVirtualLayer = new()
+ Script.Name = "Timelapse position setter";
+ Script.Description = "Raises the build platform to a set position to take a timelapse photo every n layers.\n" +
+ "Do not execute this script twice!";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 1);
+ Script.MinimumVersionToRun = new Version(3, 0, 0);
+
+ InputPositionZ.Value = (float)Math.Round(SlicerFile.PrintHeight + 1, 2);
+ InputPositionZ.Minimum = (float) Math.Round(SlicerFile.PrintHeight + 0.1, 2);
+ Script.UserInputs.Add(InputPositionZ);
+ Script.UserInputs.Add(InputRaiseEveryLayerN);
+ Script.UserInputs.Add(InputWaitTime);
+
+ if (!SlicerFile.SupportsGCode)
{
- OnText = "Use blank layers to go to the target height",
- OffText = "Use lift movement to go to the target height",
- ToolTip = "Use this option if you printer is unable to use large lifts or waits after lift"
- };
+ InputUseVirtualLayer.Value = true;
+ }
- ScriptNumericalInput<float> InputLiftSpeed = new()
- {
- Label = "Virtual layer lift speed",
- Unit = "mm/min",
- Minimum = 50,
- Maximum = 1000,
- Increment = 10,
- Value = 200,
- DecimalPlates = 2
- };
-
- ScriptNumericalInput<float> InputRetractSpeed = new()
+ if (SlicerFile.CanUseLayerLiftHeight)
{
- Label = "Virtual layer retract speed",
- Unit = "mm/min",
- Minimum = 50,
- Maximum = 1000,
- Increment = 10,
- Value = 200,
- DecimalPlates = 2
- };
-
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
+ Script.UserInputs.Add(InputUseVirtualLayer);
+ }
+ else
{
- Script.Name = "Timelapse position setter";
- Script.Description = "Raises the build platform to a set position to take a timelapse photo every n layers.\n" +
- "Do not execute this script twice!";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
-
- InputPositionZ.Value = (float)Math.Round(SlicerFile.PrintHeight + 1, 2);
- InputPositionZ.Minimum = (float) Math.Round(SlicerFile.PrintHeight + 0.1, 2);
- Script.UserInputs.Add(InputPositionZ);
- Script.UserInputs.Add(InputRaiseEveryLayerN);
- Script.UserInputs.Add(InputWaitTime);
-
- if (!SlicerFile.SupportsGCode)
- {
- InputUseVirtualLayer.Value = true;
- }
+ InputUseVirtualLayer.Value = true; // Must use layer height
+ }
- if (SlicerFile.CanUseLayerLiftHeight)
- {
- Script.UserInputs.Add(InputUseVirtualLayer);
- }
- else
- {
- InputUseVirtualLayer.Value = true; // Must use layer height
- }
+ Script.UserInputs.Add(InputLiftSpeed);
+ Script.UserInputs.Add(InputRetractSpeed);
+ }
- Script.UserInputs.Add(InputLiftSpeed);
- Script.UserInputs.Add(InputRetractSpeed);
- }
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ if (!SlicerFile.SupportPerLayerSettings) return "This script is not compatible with your printer / file format";
+ if (InputPositionZ.Value <= SlicerFile.PrintHeight) return $"{InputPositionZ.Label} must be greater than {SlicerFile.PrintHeight}mm";
+ return null;
+ }
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ if (InputUseVirtualLayer.Value)
{
- if (!SlicerFile.SupportPerLayerSettings) return "This script is not compatible with your printer / file format";
- if (InputPositionZ.Value <= SlicerFile.PrintHeight) return $"{InputPositionZ.Label} must be greater than {SlicerFile.PrintHeight}mm";
- return null;
- }
+ using var mat = EmguExtensions.InitMat(SlicerFile.Resolution);
+ var pixelPos = SlicerFile.BoundingRectangle.Center();
+ mat.SetByte(pixelPos.X, pixelPos.Y, 1); // Print a very fade pixel to ignore empty layer detection
+ var layer = new Layer(SlicerFile.LayerCount, mat, SlicerFile)
+ {
+ PositionZ = InputPositionZ.Value,
+ ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.05f,
+ LiftSpeed = InputLiftSpeed.Value,
+ RetractSpeed = InputRetractSpeed.Value
+ };
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
- {
- if (InputUseVirtualLayer.Value)
+ if (InputWaitTime.Value > 0)
{
- using var mat = EmguExtensions.InitMat(SlicerFile.Resolution);
- var pixelPos = SlicerFile.BoundingRectangle.Center();
- mat.SetByte(pixelPos.X, pixelPos.Y, 1); // Print a very fade pixel to ignore empty layer detection
- var layer = new Layer(SlicerFile.LayerCount, mat, SlicerFile)
+ if (SlicerFile.CanUseWaitTimeBeforeCure)
{
- PositionZ = InputPositionZ.Value,
- ExposureTime = SlicerFile.SupportsGCode ? 0 : 0.05f,
- LiftSpeed = InputLiftSpeed.Value,
- RetractSpeed = InputRetractSpeed.Value
- };
-
- if (InputWaitTime.Value > 0)
+ layer.WaitTimeBeforeCure = InputWaitTime.Value;
+ }
+ else
{
- if (SlicerFile.CanUseWaitTimeBeforeCure)
- {
- layer.WaitTimeBeforeCure = InputWaitTime.Value;
- }
- else
- {
- layer.ExposureTime = InputWaitTime.Value;
- }
+ layer.ExposureTime = InputWaitTime.Value;
}
+ }
- SlicerFile.SuppressRebuildPropertiesWork(() =>
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ uint createdLayers = 0;
+ for (uint layerIndex = Math.Max(1, Operation.LayerIndexStart + InputRaiseEveryLayerN.Value); layerIndex <= Operation.LayerIndexEnd; layerIndex += InputRaiseEveryLayerN.Value)
{
- uint createdLayers = 0;
- for (uint layerIndex = Math.Max(1, Operation.LayerIndexStart + InputRaiseEveryLayerN.Value); layerIndex <= Operation.LayerIndexEnd; layerIndex += InputRaiseEveryLayerN.Value)
- {
- SlicerFile.LayerManager.Insert((int)(layerIndex + createdLayers), layer.Clone());
- createdLayers++;
- Progress.ProcessedItems = layerIndex;
- }
- });
+ SlicerFile.Insert((int)(layerIndex + createdLayers), layer.Clone());
+ createdLayers++;
+ Progress.ProcessedItems = layerIndex;
+ }
+ });
- }
- else
+ }
+ else
+ {
+ for (uint layerIndex = Math.Max(1, Operation.LayerIndexStart + InputRaiseEveryLayerN.Value - 1); layerIndex <= Operation.LayerIndexEnd; layerIndex += InputRaiseEveryLayerN.Value)
{
- for (uint layerIndex = Math.Max(1, Operation.LayerIndexStart + InputRaiseEveryLayerN.Value - 1); layerIndex <= Operation.LayerIndexEnd; layerIndex += InputRaiseEveryLayerN.Value)
+ var layer = SlicerFile[layerIndex];
+ layer.LiftHeightTotal = Math.Max(SlicerFile.LiftHeightTotal, InputPositionZ.Value - layer.PositionZ);
+ if (SlicerFile.CanUseLayerWaitTimeAfterLift && InputWaitTime.Value > 0)
{
- var layer = SlicerFile[layerIndex];
- layer.LiftHeightTotal = Math.Max(SlicerFile.LiftHeightTotal, InputPositionZ.Value - layer.PositionZ);
- if (SlicerFile.CanUseLayerWaitTimeAfterLift && InputWaitTime.Value > 0)
- {
- layer.WaitTimeAfterLift = InputWaitTime.Value;
- }
- Progress.ProcessedItems = layerIndex;
+ layer.WaitTimeAfterLift = InputWaitTime.Value;
}
+ Progress.ProcessedItems = layerIndex;
}
-
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
}
+
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/ScriptVATClean.cs b/UVtools.ScriptSample/ScriptVATClean.cs
index bd769b5..065c86e 100644
--- a/UVtools.ScriptSample/ScriptVATClean.cs
+++ b/UVtools.ScriptSample/ScriptVATClean.cs
@@ -14,128 +14,128 @@ using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.Scripting;
-namespace UVtools.ScriptSample
+namespace UVtools.ScriptSample;
+
+/// <summary>
+/// Change layer properties to random values
+/// </summary>
+public class ScriptVATClean : ScriptGlobals
{
+ private readonly ScriptNumericalInput<ushort> InputInset = new()
+ {
+ Label = "Resolution inset",
+ ToolTip = "Inset image resolution by this value to create a black border",
+ Unit = "px",
+ Minimum = 0,
+ Maximum = ushort.MaxValue,
+ Increment = 1
+ };
+
+ private readonly ScriptNumericalInput<float> InputExposureTime = new()
+ {
+ Label = "Exposure time",
+ ToolTip = "Time to exposure the layer",
+ Unit = "s",
+ Minimum = 0,
+ Maximum = 50,
+ DecimalPlates = 2,
+ Increment = 1
+ };
+
/// <summary>
- /// Change layer properties to random values
+ /// Set configurations here, this function trigger just after load a script
/// </summary>
- public class ScriptVATClean : ScriptGlobals
+ public void ScriptInit()
{
- private ScriptNumericalInput<ushort> InputInset = new()
- {
- Label = "Resolution inset",
- ToolTip = "Inset image resolution by this value to create a black border",
- Unit = "px",
- Minimum = 0,
- Maximum = ushort.MaxValue,
- Increment = 1
- };
-
- private ScriptNumericalInput<float> InputExposureTime = new()
- {
- Label = "Exposure time",
- ToolTip = "Time to exposure the layer",
- Unit = "s",
- Minimum = 0,
- Maximum = 50,
- DecimalPlates = 2,
- Increment = 1
- };
-
- /// <summary>
- /// Set configurations here, this function trigger just after load a script
- /// </summary>
- public void ScriptInit()
- {
- Script.Name = "Create a file to clean VAT exposing 1 layer";
- Script.Description = "Print this file to clean your VAT by exposing 1 layer and peel it off.\n" +
- "1) Load a file for your printer that you previous printed into UVtools\n" +
- "2) Configure and run this script\n" +
- "3) Go to File -> Save As, and give it a new name\n" +
- "4) Remove head/plate\n" +
- "5) Place a plastic spatula in the VAT at an angle with the handle laying on the top of the VAT frame\n" +
- "6) Print the created file\n" +
- "7) When print finish slowly peel the layer with the spatula";
- Script.Author = "Tiago Conceição";
- Script.Version = new Version(0, 1);
-
-
- InputInset.Maximum = (ushort) (Math.Max(SlicerFile.ResolutionX, SlicerFile.ResolutionY) / 2 - 2);
- InputExposureTime.Value = SlicerFile.ExposureTime * 2;
-
- Script.UserInputs.Add(InputInset);
- Script.UserInputs.Add(InputExposureTime);
- }
-
- /// <summary>
- /// Validate user inputs here, this function trigger when user click on execute
- /// </summary>
- /// <returns>A error message, empty or null if validation passes.</returns>
- public string ScriptValidate()
- {
- return null;
- }
-
- /// <summary>
- /// Execute the script, this function trigger when when user click on execute and validation passes
- /// </summary>
- /// <returns>True if executes successfully to the end, otherwise false.</returns>
- public bool ScriptExecute()
- {
- Progress.Reset("Generating layers", 1); // Sets the progress name and number of items to process
+ Script.Name = "Create a file to clean VAT exposing 1 layer";
+ Script.Description = "Print this file to clean your VAT by exposing 1 layer and peel it off.\n" +
+ "1) Load a file for your printer that you previous printed into UVtools\n" +
+ "2) Configure and run this script\n" +
+ "3) Go to File -> Save As, and give it a new name\n" +
+ "4) Remove head/plate\n" +
+ "5) Place a plastic spatula in the VAT at an angle with the handle laying on the top of the VAT frame\n" +
+ "6) Print the created file\n" +
+ "7) When print finish slowly peel the layer with the spatula";
+ Script.Author = "Tiago Conceição";
+ Script.Version = new Version(0, 2);
+ Script.MinimumVersionToRun = new Version(3, 0, 0);
+
+
+ InputInset.Maximum = (ushort) (Math.Max(SlicerFile.ResolutionX, SlicerFile.ResolutionY) / 2 - 2);
+ InputExposureTime.Value = SlicerFile.ExposureTime * 2;
+
+ Script.UserInputs.Add(InputInset);
+ Script.UserInputs.Add(InputExposureTime);
+ }
- var layer = SlicerFile[0];
- layer.PositionZ = SlicerFile.MachineZ; // Send head to top if possible
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string? ScriptValidate()
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ public bool ScriptExecute()
+ {
+ Progress.Reset("Generating layers", 1); // Sets the progress name and number of items to process
- using var mat = EmguExtensions.InitMat(SlicerFile.Resolution);
- CvInvoke.Rectangle(mat, new Rectangle(
- new Point(InputInset.Value, InputInset.Value),
- new Size((int) (SlicerFile.ResolutionX - InputInset.Value*2)-1, (int) (SlicerFile.ResolutionY - InputInset.Value*2)-1)
- ), EmguExtensions.WhiteColor, -1, LineType.FourConnected);
- layer.LayerMat = mat;
+ var layer = SlicerFile[0];
+ layer.PositionZ = SlicerFile.MachineZ; // Send head to top if possible
- SlicerFile.SuppressRebuildPropertiesWork(() =>
- {
- SlicerFile.BottomLayerCount = 1;
+ using var mat = EmguExtensions.InitMat(SlicerFile.Resolution);
+ CvInvoke.Rectangle(mat, new Rectangle(
+ new Point(InputInset.Value, InputInset.Value),
+ new Size((int) (SlicerFile.ResolutionX - InputInset.Value*2)-1, (int) (SlicerFile.ResolutionY - InputInset.Value*2)-1)
+ ), EmguExtensions.WhiteColor, -1, LineType.FourConnected);
+ layer.LayerMat = mat;
+
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ SlicerFile.BottomLayerCount = 1;
- SlicerFile.LayerManager.Layers = new[] { layer };
- });
+ SlicerFile.Layers = new[] { layer };
+ });
- SlicerFile.BottomExposureTime =
+ SlicerFile.BottomExposureTime =
SlicerFile.ExposureTime = InputExposureTime.Value;
- SlicerFile.BottomLiftSpeed =
+ SlicerFile.BottomLiftSpeed =
SlicerFile.LiftSpeed =
- SlicerFile.RetractSpeed = 200;
+ SlicerFile.RetractSpeed = 200;
- SlicerFile.BottomLiftHeight =
+ SlicerFile.BottomLiftHeight =
SlicerFile.LiftHeight = 1;
- Progress++;
+ Progress++;
- SlicerFile.SetThumbnails(GetThumbnail());
+ SlicerFile.SetThumbnails(GetThumbnail());
- // return true if not cancelled by user
- return !Progress.Token.IsCancellationRequested;
- }
+ // return true if not cancelled by user
+ return !Progress.Token.IsCancellationRequested;
+ }
- public Mat GetThumbnail()
- {
- Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
- var fontFace = FontFace.HersheyDuplex;
- var fontScale = 1;
- var fontThickness = 2;
- const byte xSpacing = 45;
- const byte ySpacing = 45;
- CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
- CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
- CvInvoke.PutText(thumbnail, "VAT Clean Utility", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
- CvInvoke.PutText(thumbnail, $"Exposure time: {SlicerFile.ExposureTime}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
- CvInvoke.PutText(thumbnail, $"Use the spatula in!", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
-
- return thumbnail;
- }
+ public Mat GetThumbnail()
+ {
+ Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3);
+ var fontFace = FontFace.HersheyDuplex;
+ var fontScale = 1;
+ var fontThickness = 2;
+ const byte xSpacing = 45;
+ const byte ySpacing = 45;
+ CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3);
+ CvInvoke.PutText(thumbnail, "VAT Clean Utility", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness);
+ CvInvoke.PutText(thumbnail, $"Exposure time: {SlicerFile.ExposureTime}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+ CvInvoke.PutText(thumbnail, $"Use the spatula in!", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.WhiteColor, fontThickness);
+
+ return thumbnail;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.ScriptSample/UVtools.ScriptSample.csproj b/UVtools.ScriptSample/UVtools.ScriptSample.csproj
index cf79ecd..51e1abd 100644
--- a/UVtools.ScriptSample/UVtools.ScriptSample.csproj
+++ b/UVtools.ScriptSample/UVtools.ScriptSample.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<Company>PTRTECH</Company>
<Description>Scripting samples</Description>
<Authors>Tiago Conceição</Authors>
@@ -11,6 +11,7 @@
<RepositoryType>Git</RepositoryType>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<Platforms>AnyCPU;x64</Platforms>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
diff --git a/UVtools.WPF/App.axaml b/UVtools.WPF/App.axaml
index 6a486e4..55ef32e 100644
--- a/UVtools.WPF/App.axaml
+++ b/UVtools.WPF/App.axaml
@@ -7,10 +7,15 @@
<converters:FromValueDescriptionToEnumConverter x:Key="FromValueDescriptionToEnumConverter" />
</Application.Resources>
<Application.Styles>
- <FluentTheme Mode="Light"/>
+ <!--
+ <FluentTheme Mode="Light"/>
+ -->
<StyleInclude Source="avares://ThemeEditor.Controls.ColorPicker/ColorPicker.axaml"/>
+ <!--
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
+ !-->
<StyleInclude Source="/Assets/Styles/Styles.xaml" />
- <StyleInclude Source="/Assets/Styles/StylesLight.xaml" />
+ <!--<StyleInclude Source="/Assets/Styles/StylesLight.xaml" />!-->
+
</Application.Styles>
</Application>
diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs
index cad9b28..45e55c6 100644
--- a/UVtools.WPF/App.axaml.cs
+++ b/UVtools.WPF/App.axaml.cs
@@ -7,15 +7,20 @@
*/
using System;
+using System.ComponentModel;
using System.Diagnostics;
+using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using Avalonia;
+using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
-using Avalonia.Media.Imaging;
+using Avalonia.Markup.Xaml.Styling;
using Avalonia.Platform;
+using Avalonia.Styling;
+using Avalonia.Themes.Fluent;
using Emgu.CV;
using UVtools.Core;
using UVtools.Core.FileFormats;
@@ -23,244 +28,389 @@ using UVtools.Core.Managers;
using UVtools.Core.SystemOS;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
+using Bitmap = Avalonia.Media.Imaging.Bitmap;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+#nullable enable
+
+public class App : Application
{
- public class App : Application
+ public enum ApplicationTheme
{
- //public static ThemeSelector ThemeSelector { get; set; }
- public static MainWindow MainWindow;
- public static FileFormat SlicerFile = null;
+ [Description("Fluent light")]
+ FluentLight,
+ [Description("Fluent dark")]
+ FluentDark,
+
+ [Description("Default light")]
+ DefaultLight,
+ [Description("Default dark")]
+ DefaultDark
+ }
+ //public static ThemeSelector ThemeSelector { get; set; }
+ public static MainWindow MainWindow = null!;
+ public static FileFormat? SlicerFile = null;
- public static AppVersionChecker VersionChecker { get; } = new();
+ public static AppVersionChecker VersionChecker { get; } = new();
- public override void Initialize()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ public static StyleInclude DataGridFluent => new(CreateAssemblyUri("/Styles"))
+ {
+ Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml")
+ };
- public override async void OnFrameworkInitializationCompleted()
- {
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- UserSettings.Load();
- UserSettings.SetVersion();
+ public static StyleInclude DataGridDefault => new(CreateAssemblyUri("/Styles"))
+ {
+ Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Default.xaml")
+ };
- OperationProfiles.Load();
+ public static readonly StyleInclude AppStyleLight = new(CreateAssemblyUri("/Assets/Styles"))
+ {
+ Source = CreateAssemblyUri("/Assets/Styles/StylesLight.xaml")
+ };
- MaterialManager.FilePath = Path.Combine(UserSettings.SettingsFolder, "materials.xml");
- MaterialManager.Load();
+ public static readonly StyleInclude AppStyleDark = new(CreateAssemblyUri("/Assets/Styles"))
+ {
+ Source = CreateAssemblyUri("/Assets/Styles/StylesDark.xaml")
+ };
- /*ThemeSelector = ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes"));
- ThemeSelector.LoadSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
- if (ThemeSelector.SelectedTheme.Name == "UVtoolsDark" || ThemeSelector.SelectedTheme.Name == "Light")
- {
- foreach (var theme in ThemeSelector.Themes)
- {
- if (theme.Name != "UVtoolsLight") continue;
- theme.ApplyTheme();
- break;
- }
- }*/
-
- if (!CvInvoke.Init())
- {
- Console.WriteLine("UVtools can not init OpenCV library\n" +
- "Please build or install this dependencies in order to run UVtools\n" +
- "Check manual or page at 'Requirements' section for help");
- }
+ public static FluentTheme Fluent = new(CreateAssemblyUri("/Styles"));
+
+ public static Styles DefaultLight = new()
+ {
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
+ }
+ };
- MainWindow = new MainWindow();
- try
+ public static Styles DefaultDark = new()
+ {
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml")
+ },
+ new StyleInclude(new Uri($"resm:Styles?assembly={AssemblyName}"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
+ }
+ };
+
+ public static void ApplyTheme()
+ {
+ switch (UserSettings.Instance.General.Theme)
+ {
+ case ApplicationTheme.FluentLight:
+ {
+ if (Fluent.Mode != FluentThemeMode.Light)
{
- if(!CvInvoke.Init())
- await MainWindow.MessageBoxError("UVtools can not init OpenCV library\n" +
- "Please build or install this dependencies in order to run UVtools\n" +
- "Check manual or page at 'Requirements' section for help",
- "UVtools can not run");
+ Fluent.Mode = FluentThemeMode.Light;
}
- catch (Exception e)
+
+ Current!.Styles[0] = Fluent;
+ Current.Styles[1] = DataGridFluent;
+ Current.Styles[2] = AppStyleLight;
+ break;
+ }
+ case ApplicationTheme.FluentDark:
+ {
+ if (Fluent.Mode != FluentThemeMode.Dark)
{
- await MainWindow.MessageBoxError("UVtools can not run due lack of dependencies from cvextern/OpenCV\n" +
- "Please build or install this dependencies in order to run UVtools\n" +
- "Check manual or page at 'Requirements' section for help\n\n" +
- "Additional information:\n" +
- $"{e}", "UVtools can not run");
- return;
+ Fluent.Mode = FluentThemeMode.Dark;
}
- desktop.MainWindow = MainWindow;
- //desktop.Exit += (sender, e) => ThemeSelector.SaveSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
+ Current!.Styles[0] = Fluent;
+ Current.Styles[1] = DataGridFluent;
+ Current.Styles[2] = AppStyleDark;
+ break;
}
-
- base.OnFrameworkInitializationCompleted();
+ case ApplicationTheme.DefaultLight:
+ Current!.Styles[0] = DefaultLight;
+ Current.Styles[1] = DataGridDefault;
+ Current.Styles[2] = AppStyleLight;
+ break;
+ case ApplicationTheme.DefaultDark:
+ Current!.Styles[0] = DefaultDark;
+ Current.Styles[1] = DataGridDefault;
+ Current.Styles[2] = AppStyleDark;
+ break;
}
+ }
- #region Utilities
+ public override void Initialize()
+ {
+ Styles.Insert(0, Fluent);
+ Styles.Insert(1, DataGridFluent);
+ Styles.Insert(2, AppStyleLight);
+ AvaloniaXamlLoader.Load(this);
+ }
- public static readonly string AppExecutable = Path.Combine(ApplicationPath, About.Software);
- public static readonly string AppExecutableQuoted = $"\"{AppExecutable}\"";
- public static void NewInstance(string filePath)
+ public override async void OnFrameworkInitializationCompleted()
+ {
+ if (Design.IsDesignMode)
{
- try
+ SlicerFile = new ChituboxFile
{
- if (OperatingSystem.IsWindows())
- {
- SystemAware.StartProcess($"{AppExecutable}.exe", $"\"{filePath}\"");
- }
- else if(File.Exists(AppExecutable)) // Direct execute
- {
- SystemAware.StartProcess(AppExecutable, $"\"{filePath}\"");
- }
- else
+ LayerHeight = 0.05f,
+ Resolution = new (1440, 2560),
+ Display = new (68.04f, 120.96f),
+ DisplayMirror = Enumerations.FlipDirection.Horizontally,
+ MachineZ = 155,
+ BottomLayerCount = 3,
+ MachineName = "Epax X1"
+ };
+ }
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ UserSettings.Load();
+ UserSettings.SetVersion();
+
+ MaterialManager.Load();
+ OperationProfiles.Load();
+ SuggestionManager.Load();
+
+ /*ThemeSelector = ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes"));
+ ThemeSelector.LoadSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
+ if (ThemeSelector.SelectedTheme.Name == "UVtoolsDark" || ThemeSelector.SelectedTheme.Name == "Light")
+ {
+ foreach (var theme in ThemeSelector.Themes)
{
- SystemAware.StartProcess("dotnet", $"UVtools.dll \"{filePath}\"");
+ if (theme.Name != "UVtoolsLight") continue;
+ theme.ApplyTheme();
+ break;
}
+ }*/
+
+ if (!CvInvoke.Init())
+ {
+ Console.WriteLine("UVtools can not init OpenCV library\n" +
+ "Please build or install this dependencies in order to run UVtools\n" +
+ "Check manual or page at 'Requirements' section for help");
+ }
+
+ if (UserSettings.Instance.General.Theme != ApplicationTheme.FluentLight)
+ {
+ ApplyTheme();
+ }
+
+ MainWindow = new MainWindow();
+ try
+ {
+ if(!CvInvoke.Init())
+ await MainWindow.MessageBoxError("UVtools can not init OpenCV library\n" +
+ "Please build or install this dependencies in order to run UVtools\n" +
+ "Check manual or page at 'Requirements' section for help",
+ "UVtools can not run");
}
catch (Exception e)
{
- Debug.WriteLine(e);
+ await MainWindow.MessageBoxError("UVtools can not run due lack of dependencies from cvextern/OpenCV\n" +
+ "Please build or install this dependencies in order to run UVtools\n" +
+ "Check manual or page at 'Requirements' section for help\n\n" +
+ "Additional information:\n" +
+ $"{e}", "UVtools can not run");
+ return;
}
+
+ desktop.MainWindow = MainWindow;
+ //desktop.Exit += (sender, e) => ThemeSelector.SaveSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
}
- public static Stream GetAsset(string url)
- {
- Uri uri;
+ base.OnFrameworkInitializationCompleted();
+ }
- // Allow for assembly overrides
- if (url.StartsWith("avares://"))
+ #region Utilities
+ public static string ApplicationPath => AppContext.BaseDirectory;
+ public static readonly string AppExecutable = Environment.ProcessPath!;
+ public static readonly string AppExecutableQuoted = $"\"{AppExecutable}\"";
+ public static void NewInstance(string filePath)
+ {
+ try
+ {
+ if (File.Exists(AppExecutable)) // Direct execute
{
- uri = new Uri(url);
+ SystemAware.StartProcess(AppExecutable, $"\"{filePath}\"");
}
else
{
- var assemblyName = Assembly.GetEntryAssembly().GetName().Name;
- uri = new Uri($"avares://{assemblyName}{url}");
+ SystemAware.StartProcess("dotnet", $"UVtools.dll \"{filePath}\"");
}
-
- var res = AvaloniaLocator.Current.GetService<IAssetLoader>()?.Open(uri);
- return res;
}
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+ }
- public static Bitmap GetBitmapFromAsset(string url) => new(GetAsset(url));
-
- public static string ApplicationPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ public static Uri CreateAssemblyUri(string url)
+ {
+ Uri uri;
- public static string GetPrusaSlicerDirectory(bool isSuperSlicer = false)
+ // Allow for assembly overrides
+ if (url.StartsWith("avares://"))
{
- var slicerFolder = isSuperSlicer ? "SuperSlicer" : "PrusaSlicer";
- if (OperatingSystem.IsWindows())
- {
- return Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
- slicerFolder);
- }
+ uri = new Uri(url);
+ }
+ else
+ {
+ uri = new Uri($"avares://{AssemblyName}{url}");
+ }
- if (OperatingSystem.IsLinux())
- {
- var folder1 = Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
- ".config",
- slicerFolder);
- if (Directory.Exists(folder1)) return folder1;
- return Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
- $".{slicerFolder}");
- }
+ return uri;
+ }
- if (OperatingSystem.IsMacOS())
- {
- return Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
- "Library",
- "Application Support",
- slicerFolder);
- }
+ public static Stream GetAsset(string url)
+ {
+ return AvaloniaLocator.Current.GetService<IAssetLoader>()?.Open(CreateAssemblyUri(url))!;
+ }
+
+ public static Bitmap GetBitmapFromAsset(string url) => new(GetAsset(url));
+
+
+ public static string? GetPrusaSlicerDirectory(bool isSuperSlicer = false)
+ {
+ var slicerFolder = isSuperSlicer ? "SuperSlicer" : "PrusaSlicer";
+ if (OperatingSystem.IsWindows())
+ {
+ return Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
+ slicerFolder);
+ }
+
+ if (OperatingSystem.IsLinux())
+ {
+ var folder1 = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ ".config",
+ slicerFolder);
+ if (Directory.Exists(folder1)) return folder1;
+ return Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ $".{slicerFolder}");
+ }
- return null;
+ if (OperatingSystem.IsMacOS())
+ {
+ return Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ "Library",
+ "Application Support",
+ slicerFolder);
}
- #endregion
+ return null;
+ }
+
+ #endregion
- #region Assembly Attribute Accessors
+ #region Assembly Attribute Accessors
- public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
- public static string VersionStr => Version.ToString(3);
+ public static Version Version => Assembly.GetExecutingAssembly().GetName().Version!;
+ public static string VersionStr => Version.ToString(3);
- public static string AssemblyTitle
+ public static string AssemblyName => Assembly.GetExecutingAssembly().GetName().Name!;
+
+ public static string AssemblyTitle
+ {
+ get
{
- get
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
+ if (attributes.Length > 0)
{
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
- if (attributes.Length > 0)
+ AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
+ if (titleAttribute.Title != "")
{
- AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
- if (titleAttribute.Title != "")
- {
- return titleAttribute.Title;
- }
+ return titleAttribute.Title;
}
- return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location);
}
+ return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location);
}
+ }
- public static string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ public static string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version?.ToString()!;
- public static string AssemblyDescription
+ public static string AssemblyDescription
+ {
+ get
{
- get
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
+ if (attributes.Length == 0)
{
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
+ return "";
+ }
- string description = ((AssemblyDescriptionAttribute)attributes[0]).Description + $"{Environment.NewLine}{Environment.NewLine}Available File Formats:";
+ string description = ((AssemblyDescriptionAttribute)attributes[0]).Description + $"{Environment.NewLine}{Environment.NewLine}Available File Formats:";
- return FileFormat.AvailableFormats.SelectMany(fileFormat => fileFormat.FileExtensions).Aggregate(description, (current, fileExtension) => current + $"{Environment.NewLine}- {fileExtension.Description} (.{fileExtension.Extension})");
- }
+ return FileFormat.AvailableFormats.SelectMany(fileFormat => fileFormat.FileExtensions).Aggregate(description, (current, fileExtension) => current + $"{Environment.NewLine}- {fileExtension.Description} (.{fileExtension.Extension})");
}
+ }
- public static string AssemblyProduct
+ public static string AssemblyProduct
+ {
+ get
{
- get
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
+ if (attributes.Length == 0)
{
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
- return ((AssemblyProductAttribute)attributes[0]).Product;
+ return "";
}
+ return ((AssemblyProductAttribute)attributes[0]).Product;
}
+ }
- public static string AssemblyCopyright
+ public static string AssemblyCopyright
+ {
+ get
{
- get
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
+ if (attributes.Length == 0)
{
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
- return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
+ return "";
}
+ return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
}
+ }
- public static string AssemblyCompany
+ public static string AssemblyCompany
+ {
+ get
{
- get
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
+ if (attributes.Length == 0)
{
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
- return ((AssemblyCompanyAttribute)attributes[0]).Company;
+ return "";
}
+ return ((AssemblyCompanyAttribute)attributes[0]).Company;
}
-
- #endregion
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/AppSettings.cs b/UVtools.WPF/AppSettings.cs
index 2dc5798..df87f35 100644
--- a/UVtools.WPF/AppSettings.cs
+++ b/UVtools.WPF/AppSettings.cs
@@ -5,37 +5,36 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public static class AppSettings
{
- public static class AppSettings
- {
- // Supported ZoomLevels for Layer Preview.
- // These settings eliminate very small zoom factors from the ImageBox default values,
- // while ensuring that 4K/5K build plates can still easily fit on screen.
- public static readonly int[] ZoomLevels =
- {20, 25, 30, 50, 75, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200};
+ // Supported ZoomLevels for Layer Preview.
+ // These settings eliminate very small zoom factors from the ImageBox default values,
+ // while ensuring that 4K/5K build plates can still easily fit on screen.
+ public static readonly int[] ZoomLevels =
+ {20, 25, 30, 50, 75, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200};
- // Count of the bottom portion of the full zoom range which will be skipped for
- // assignable actions such as auto-zoom level, and crosshair fade level. If values
- // are added/removed from ZoomLevels above, this value may also need to be adjusted.
- public const byte ZoomLevelSkipCount = 7; // Start at 2x which is index 7.
+ // Count of the bottom portion of the full zoom range which will be skipped for
+ // assignable actions such as auto-zoom level, and crosshair fade level. If values
+ // are added/removed from ZoomLevels above, this value may also need to be adjusted.
+ public const byte ZoomLevelSkipCount = 7; // Start at 2x which is index 7.
- /// <summary>
- /// Returns the zoom level at which the crosshairs will fade and no longer be displayed
- /// </summary>
- public static int CrosshairFadeLevel => ZoomLevels[UserSettings.Instance.LayerPreview.CrosshairFadeLevelIndex + ZoomLevelSkipCount];
+ /// <summary>
+ /// Returns the zoom level at which the crosshairs will fade and no longer be displayed
+ /// </summary>
+ public static int CrosshairFadeLevel => ZoomLevels[UserSettings.Instance.LayerPreview.CrosshairFadeLevelIndex + ZoomLevelSkipCount];
- /// <summary>
- /// Returns the zoom level that will be used for autozoom actions
- /// </summary>
- public static int LockedZoomLevel => ZoomLevels[UserSettings.Instance.LayerPreview.ZoomLockLevelIndex + ZoomLevelSkipCount];
+ /// <summary>
+ /// Returns the zoom level that will be used for autozoom actions
+ /// </summary>
+ public static int LockedZoomLevel => ZoomLevels[UserSettings.Instance.LayerPreview.ZoomLockLevelIndex + ZoomLevelSkipCount];
- /// <summary>
- /// Minimum Zoom level to which autozoom can be locked.
- /// </summary>
- public const byte MinLockedZoomLevel = 200;
+ /// <summary>
+ /// Minimum Zoom level to which autozoom can be locked.
+ /// </summary>
+ public const byte MinLockedZoomLevel = 200;
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Assets/Icons/CNCMachine-16x16.png b/UVtools.WPF/Assets/Icons/CNCMachine-16x16.png
deleted file mode 100644
index e2a5e4b..0000000
--- a/UVtools.WPF/Assets/Icons/CNCMachine-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/accept-16x16.png b/UVtools.WPF/Assets/Icons/accept-16x16.png
deleted file mode 100644
index 3b3abf8..0000000
--- a/UVtools.WPF/Assets/Icons/accept-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/angle-double-up-16x16.png b/UVtools.WPF/Assets/Icons/angle-double-up-16x16.png
deleted file mode 100644
index a444e32..0000000
--- a/UVtools.WPF/Assets/Icons/angle-double-up-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/arrow-down-16x16.png b/UVtools.WPF/Assets/Icons/arrow-down-16x16.png
deleted file mode 100644
index 9ce51bb..0000000
--- a/UVtools.WPF/Assets/Icons/arrow-down-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/arrow-down-double-16x16.png b/UVtools.WPF/Assets/Icons/arrow-down-double-16x16.png
deleted file mode 100644
index 7393d68..0000000
--- a/UVtools.WPF/Assets/Icons/arrow-down-double-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/arrow-end-16x16.png b/UVtools.WPF/Assets/Icons/arrow-end-16x16.png
deleted file mode 100644
index ce9d9c7..0000000
--- a/UVtools.WPF/Assets/Icons/arrow-end-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/arrow-top-16x16.png b/UVtools.WPF/Assets/Icons/arrow-top-16x16.png
deleted file mode 100644
index 8afa1a0..0000000
--- a/UVtools.WPF/Assets/Icons/arrow-top-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/arrow-up-16x16.png b/UVtools.WPF/Assets/Icons/arrow-up-16x16.png
deleted file mode 100644
index 585744a..0000000
--- a/UVtools.WPF/Assets/Icons/arrow-up-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/back-16x16.png b/UVtools.WPF/Assets/Icons/back-16x16.png
deleted file mode 100644
index c28c621..0000000
--- a/UVtools.WPF/Assets/Icons/back-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/balance-scale-16x16.png b/UVtools.WPF/Assets/Icons/balance-scale-16x16.png
deleted file mode 100644
index f740ea0..0000000
--- a/UVtools.WPF/Assets/Icons/balance-scale-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/blur-16x16.png b/UVtools.WPF/Assets/Icons/blur-16x16.png
deleted file mode 100644
index 8132066..0000000
--- a/UVtools.WPF/Assets/Icons/blur-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/book-32x32.png b/UVtools.WPF/Assets/Icons/book-32x32.png
deleted file mode 100644
index 351dff2..0000000
--- a/UVtools.WPF/Assets/Icons/book-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/bookmark-16x16.png b/UVtools.WPF/Assets/Icons/bookmark-16x16.png
deleted file mode 100644
index b6e8b3e..0000000
--- a/UVtools.WPF/Assets/Icons/bookmark-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/bowling-ball-16x16.png b/UVtools.WPF/Assets/Icons/bowling-ball-16x16.png
deleted file mode 100644
index f231665..0000000
--- a/UVtools.WPF/Assets/Icons/bowling-ball-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/bug-16x16.png b/UVtools.WPF/Assets/Icons/bug-16x16.png
deleted file mode 100644
index 0f82fe9..0000000
--- a/UVtools.WPF/Assets/Icons/bug-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/burn-16x16.png b/UVtools.WPF/Assets/Icons/burn-16x16.png
deleted file mode 100644
index 217963c..0000000
--- a/UVtools.WPF/Assets/Icons/burn-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/button-info-16x16.png b/UVtools.WPF/Assets/Icons/button-info-16x16.png
deleted file mode 100644
index 560873d..0000000
--- a/UVtools.WPF/Assets/Icons/button-info-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/calculator-16x16.png b/UVtools.WPF/Assets/Icons/calculator-16x16.png
deleted file mode 100644
index b944f66..0000000
--- a/UVtools.WPF/Assets/Icons/calculator-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/camera-16x16.png b/UVtools.WPF/Assets/Icons/camera-16x16.png
deleted file mode 100644
index 423499c..0000000
--- a/UVtools.WPF/Assets/Icons/camera-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/cancel-24x24.png b/UVtools.WPF/Assets/Icons/cancel-24x24.png
deleted file mode 100644
index 74e4559..0000000
--- a/UVtools.WPF/Assets/Icons/cancel-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/cancel-32x32.png b/UVtools.WPF/Assets/Icons/cancel-32x32.png
deleted file mode 100644
index a732caa..0000000
--- a/UVtools.WPF/Assets/Icons/cancel-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/chart-pie-16x16.png b/UVtools.WPF/Assets/Icons/chart-pie-16x16.png
deleted file mode 100644
index 8b23bf4..0000000
--- a/UVtools.WPF/Assets/Icons/chart-pie-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/checkbox-marked-16x16.png b/UVtools.WPF/Assets/Icons/checkbox-marked-16x16.png
deleted file mode 100644
index ab94ab3..0000000
--- a/UVtools.WPF/Assets/Icons/checkbox-marked-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/checkbox-unmarked-16x16.png b/UVtools.WPF/Assets/Icons/checkbox-unmarked-16x16.png
deleted file mode 100644
index 697bb74..0000000
--- a/UVtools.WPF/Assets/Icons/checkbox-unmarked-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/chess-rook-16x16.png b/UVtools.WPF/Assets/Icons/chess-rook-16x16.png
deleted file mode 100644
index b75fa17..0000000
--- a/UVtools.WPF/Assets/Icons/chess-rook-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/chessboard-16x16.png b/UVtools.WPF/Assets/Icons/chessboard-16x16.png
deleted file mode 100644
index e12226e..0000000
--- a/UVtools.WPF/Assets/Icons/chessboard-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/clipboard-16x16.png b/UVtools.WPF/Assets/Icons/clipboard-16x16.png
deleted file mode 100644
index 42f9370..0000000
--- a/UVtools.WPF/Assets/Icons/clipboard-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/clipboard-32x32.png b/UVtools.WPF/Assets/Icons/clipboard-32x32.png
deleted file mode 100644
index e4d7c2b..0000000
--- a/UVtools.WPF/Assets/Icons/clipboard-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/code-16x16.png b/UVtools.WPF/Assets/Icons/code-16x16.png
deleted file mode 100644
index 6aa33a4..0000000
--- a/UVtools.WPF/Assets/Icons/code-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/code-32x32.png b/UVtools.WPF/Assets/Icons/code-32x32.png
deleted file mode 100644
index e8c0f5f..0000000
--- a/UVtools.WPF/Assets/Icons/code-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/code-branch-16x16.png b/UVtools.WPF/Assets/Icons/code-branch-16x16.png
deleted file mode 100644
index e718637..0000000
--- a/UVtools.WPF/Assets/Icons/code-branch-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/code-branch-24x24.png b/UVtools.WPF/Assets/Icons/code-branch-24x24.png
deleted file mode 100644
index 30ca801..0000000
--- a/UVtools.WPF/Assets/Icons/code-branch-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/cog-16x16.png b/UVtools.WPF/Assets/Icons/cog-16x16.png
deleted file mode 100644
index b8a242f..0000000
--- a/UVtools.WPF/Assets/Icons/cog-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/compress-alt-16x16.png b/UVtools.WPF/Assets/Icons/compress-alt-16x16.png
deleted file mode 100644
index a2129b8..0000000
--- a/UVtools.WPF/Assets/Icons/compress-alt-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/convert-16x16.png b/UVtools.WPF/Assets/Icons/convert-16x16.png
deleted file mode 100644
index 76cf3c4..0000000
--- a/UVtools.WPF/Assets/Icons/convert-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/cookie-16x16.png b/UVtools.WPF/Assets/Icons/cookie-16x16.png
deleted file mode 100644
index 2c90a8b..0000000
--- a/UVtools.WPF/Assets/Icons/cookie-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/copy-16x16.png b/UVtools.WPF/Assets/Icons/copy-16x16.png
deleted file mode 100644
index 562239a..0000000
--- a/UVtools.WPF/Assets/Icons/copy-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/crop-16x16.png b/UVtools.WPF/Assets/Icons/crop-16x16.png
deleted file mode 100644
index 225c344..0000000
--- a/UVtools.WPF/Assets/Icons/crop-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/crosshairs-16x16.png b/UVtools.WPF/Assets/Icons/crosshairs-16x16.png
deleted file mode 100644
index 316c36a..0000000
--- a/UVtools.WPF/Assets/Icons/crosshairs-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/cubes-16x16.png b/UVtools.WPF/Assets/Icons/cubes-16x16.png
deleted file mode 100644
index bfef992..0000000
--- a/UVtools.WPF/Assets/Icons/cubes-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/cursor-16x16.png b/UVtools.WPF/Assets/Icons/cursor-16x16.png
deleted file mode 100644
index b0eeaa0..0000000
--- a/UVtools.WPF/Assets/Icons/cursor-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/data-list-16x16.png b/UVtools.WPF/Assets/Icons/data-list-16x16.png
deleted file mode 100644
index a4e784c..0000000
--- a/UVtools.WPF/Assets/Icons/data-list-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/delete-16x16.png b/UVtools.WPF/Assets/Icons/delete-16x16.png
deleted file mode 100644
index b5842b7..0000000
--- a/UVtools.WPF/Assets/Icons/delete-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/donate-16x16.png b/UVtools.WPF/Assets/Icons/donate-16x16.png
deleted file mode 100644
index 37378b3..0000000
--- a/UVtools.WPF/Assets/Icons/donate-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/dot-circle-16x16.png b/UVtools.WPF/Assets/Icons/dot-circle-16x16.png
deleted file mode 100644
index d86c0b5..0000000
--- a/UVtools.WPF/Assets/Icons/dot-circle-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/dynamic-layers-16x16.png b/UVtools.WPF/Assets/Icons/dynamic-layers-16x16.png
deleted file mode 100644
index 1644ec4..0000000
--- a/UVtools.WPF/Assets/Icons/dynamic-layers-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/elephant-foot-16x16.png b/UVtools.WPF/Assets/Icons/elephant-foot-16x16.png
deleted file mode 100644
index bdf1b4e..0000000
--- a/UVtools.WPF/Assets/Icons/elephant-foot-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/equals-16x16.png b/UVtools.WPF/Assets/Icons/equals-16x16.png
deleted file mode 100644
index 8b7528a..0000000
--- a/UVtools.WPF/Assets/Icons/equals-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/eraser-24x24.png b/UVtools.WPF/Assets/Icons/eraser-24x24.png
deleted file mode 100644
index 0c3db2e..0000000
--- a/UVtools.WPF/Assets/Icons/eraser-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/exit-16x16.png b/UVtools.WPF/Assets/Icons/exit-16x16.png
deleted file mode 100644
index 863566c..0000000
--- a/UVtools.WPF/Assets/Icons/exit-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/expand-16x16.png b/UVtools.WPF/Assets/Icons/expand-16x16.png
deleted file mode 100644
index a8c87f4..0000000
--- a/UVtools.WPF/Assets/Icons/expand-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/expand-alt-16x16.png b/UVtools.WPF/Assets/Icons/expand-alt-16x16.png
deleted file mode 100644
index 22b181f..0000000
--- a/UVtools.WPF/Assets/Icons/expand-alt-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/extract-object-16x16.png b/UVtools.WPF/Assets/Icons/extract-object-16x16.png
deleted file mode 100644
index 9bdb1d2..0000000
--- a/UVtools.WPF/Assets/Icons/extract-object-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/eye-16x16.png b/UVtools.WPF/Assets/Icons/eye-16x16.png
deleted file mode 100644
index 4f3c7e2..0000000
--- a/UVtools.WPF/Assets/Icons/eye-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/eye-24x24.png b/UVtools.WPF/Assets/Icons/eye-24x24.png
deleted file mode 100644
index 4f3c7e2..0000000
--- a/UVtools.WPF/Assets/Icons/eye-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/eye-slash-16x16.png b/UVtools.WPF/Assets/Icons/eye-slash-16x16.png
deleted file mode 100644
index de4683b..0000000
--- a/UVtools.WPF/Assets/Icons/eye-slash-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/facebook-16x16.png b/UVtools.WPF/Assets/Icons/facebook-16x16.png
deleted file mode 100644
index e03391e..0000000
--- a/UVtools.WPF/Assets/Icons/facebook-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/file-close-16x16.png b/UVtools.WPF/Assets/Icons/file-close-16x16.png
deleted file mode 100644
index ef36711..0000000
--- a/UVtools.WPF/Assets/Icons/file-close-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/file-code-16x16.png b/UVtools.WPF/Assets/Icons/file-code-16x16.png
deleted file mode 100644
index 1d1ebd7..0000000
--- a/UVtools.WPF/Assets/Icons/file-code-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/file-export-16x16.png b/UVtools.WPF/Assets/Icons/file-export-16x16.png
deleted file mode 100644
index 885e637..0000000
--- a/UVtools.WPF/Assets/Icons/file-export-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/file-gif-16x16.png b/UVtools.WPF/Assets/Icons/file-gif-16x16.png
deleted file mode 100644
index ac06f8c..0000000
--- a/UVtools.WPF/Assets/Icons/file-gif-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/file-image-16x16.png b/UVtools.WPF/Assets/Icons/file-image-16x16.png
deleted file mode 100644
index 7e10379..0000000
--- a/UVtools.WPF/Assets/Icons/file-image-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/file-import-16x16.png b/UVtools.WPF/Assets/Icons/file-import-16x16.png
deleted file mode 100644
index 1712317..0000000
--- a/UVtools.WPF/Assets/Icons/file-import-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/file-refresh-16x16.png b/UVtools.WPF/Assets/Icons/file-refresh-16x16.png
deleted file mode 100644
index 39d6920..0000000
--- a/UVtools.WPF/Assets/Icons/file-refresh-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/filter-filled-16x16.png b/UVtools.WPF/Assets/Icons/filter-filled-16x16.png
deleted file mode 100644
index a19d124..0000000
--- a/UVtools.WPF/Assets/Icons/filter-filled-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/flask-16x16.png b/UVtools.WPF/Assets/Icons/flask-16x16.png
deleted file mode 100644
index 4cd3c0a..0000000
--- a/UVtools.WPF/Assets/Icons/flask-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/flip-16x16.png b/UVtools.WPF/Assets/Icons/flip-16x16.png
deleted file mode 100644
index 138d97a..0000000
--- a/UVtools.WPF/Assets/Icons/flip-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/folder-16x16.png b/UVtools.WPF/Assets/Icons/folder-16x16.png
deleted file mode 100644
index fb7a5b4..0000000
--- a/UVtools.WPF/Assets/Icons/folder-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/folder-open-16x16.png b/UVtools.WPF/Assets/Icons/folder-open-16x16.png
deleted file mode 100644
index f47b011..0000000
--- a/UVtools.WPF/Assets/Icons/folder-open-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/font-24x24.png b/UVtools.WPF/Assets/Icons/font-24x24.png
deleted file mode 100644
index f19f1cf..0000000
--- a/UVtools.WPF/Assets/Icons/font-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/geometry-16x16.png b/UVtools.WPF/Assets/Icons/geometry-16x16.png
deleted file mode 100644
index da022c5..0000000
--- a/UVtools.WPF/Assets/Icons/geometry-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/heart-16x16.png b/UVtools.WPF/Assets/Icons/heart-16x16.png
deleted file mode 100644
index ba3658d..0000000
--- a/UVtools.WPF/Assets/Icons/heart-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/history-16x16.png b/UVtools.WPF/Assets/Icons/history-16x16.png
deleted file mode 100644
index 17fe4c3..0000000
--- a/UVtools.WPF/Assets/Icons/history-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/info-circle-16x16.png b/UVtools.WPF/Assets/Icons/info-circle-16x16.png
deleted file mode 100644
index c1fe64d..0000000
--- a/UVtools.WPF/Assets/Icons/info-circle-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/info-circle-32x32.png b/UVtools.WPF/Assets/Icons/info-circle-32x32.png
deleted file mode 100644
index d444f2d..0000000
--- a/UVtools.WPF/Assets/Icons/info-circle-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/internet-explorer-16x16.png b/UVtools.WPF/Assets/Icons/internet-explorer-16x16.png
deleted file mode 100644
index b5b3ea6..0000000
--- a/UVtools.WPF/Assets/Icons/internet-explorer-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/island-16x16.png b/UVtools.WPF/Assets/Icons/island-16x16.png
deleted file mode 100644
index d9b45d6..0000000
--- a/UVtools.WPF/Assets/Icons/island-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/ladder-16x16.png b/UVtools.WPF/Assets/Icons/ladder-16x16.png
deleted file mode 100644
index de76c84..0000000
--- a/UVtools.WPF/Assets/Icons/ladder-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/layers-16x16.png b/UVtools.WPF/Assets/Icons/layers-16x16.png
deleted file mode 100644
index b8f66f5..0000000
--- a/UVtools.WPF/Assets/Icons/layers-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/layers-alt-16x16.png b/UVtools.WPF/Assets/Icons/layers-alt-16x16.png
deleted file mode 100644
index 230e11b..0000000
--- a/UVtools.WPF/Assets/Icons/layers-alt-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/layers-alt-32x32.png b/UVtools.WPF/Assets/Icons/layers-alt-32x32.png
deleted file mode 100644
index b1ed164..0000000
--- a/UVtools.WPF/Assets/Icons/layers-alt-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/level-up-alt-16x16.png b/UVtools.WPF/Assets/Icons/level-up-alt-16x16.png
deleted file mode 100644
index 0c18e5f..0000000
--- a/UVtools.WPF/Assets/Icons/level-up-alt-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/lightbulb-16x16.png b/UVtools.WPF/Assets/Icons/lightbulb-16x16.png
deleted file mode 100644
index 01c3427..0000000
--- a/UVtools.WPF/Assets/Icons/lightbulb-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/lightbulb-solid-16x16.png b/UVtools.WPF/Assets/Icons/lightbulb-solid-16x16.png
deleted file mode 100644
index f182d01..0000000
--- a/UVtools.WPF/Assets/Icons/lightbulb-solid-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/list-16x16.png b/UVtools.WPF/Assets/Icons/list-16x16.png
deleted file mode 100644
index 85d6795..0000000
--- a/UVtools.WPF/Assets/Icons/list-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/lock-16x16.png b/UVtools.WPF/Assets/Icons/lock-16x16.png
deleted file mode 100644
index 4ee4208..0000000
--- a/UVtools.WPF/Assets/Icons/lock-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/log-16x16.png b/UVtools.WPF/Assets/Icons/log-16x16.png
deleted file mode 100644
index 55d7785..0000000
--- a/UVtools.WPF/Assets/Icons/log-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/long-arrow-alt-up-16x16.png b/UVtools.WPF/Assets/Icons/long-arrow-alt-up-16x16.png
deleted file mode 100644
index 7b9454c..0000000
--- a/UVtools.WPF/Assets/Icons/long-arrow-alt-up-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/map-marker-16x16.png b/UVtools.WPF/Assets/Icons/map-marker-16x16.png
deleted file mode 100644
index a0523b2..0000000
--- a/UVtools.WPF/Assets/Icons/map-marker-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/mask-16x16.png b/UVtools.WPF/Assets/Icons/mask-16x16.png
deleted file mode 100644
index 0a450a4..0000000
--- a/UVtools.WPF/Assets/Icons/mask-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/microchip-16x16.png b/UVtools.WPF/Assets/Icons/microchip-16x16.png
deleted file mode 100644
index ca399d8..0000000
--- a/UVtools.WPF/Assets/Icons/microchip-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/minus-16x16.png b/UVtools.WPF/Assets/Icons/minus-16x16.png
deleted file mode 100644
index 4fd33f9..0000000
--- a/UVtools.WPF/Assets/Icons/minus-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/move-16x16.png b/UVtools.WPF/Assets/Icons/move-16x16.png
deleted file mode 100644
index a018770..0000000
--- a/UVtools.WPF/Assets/Icons/move-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/network-wired-16x16.png b/UVtools.WPF/Assets/Icons/network-wired-16x16.png
deleted file mode 100644
index 567f2cd..0000000
--- a/UVtools.WPF/Assets/Icons/network-wired-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/next-16x16.png b/UVtools.WPF/Assets/Icons/next-16x16.png
deleted file mode 100644
index 9d8f5e4..0000000
--- a/UVtools.WPF/Assets/Icons/next-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/object-group-16x16.png b/UVtools.WPF/Assets/Icons/object-group-16x16.png
deleted file mode 100644
index e5cd1e9..0000000
--- a/UVtools.WPF/Assets/Icons/object-group-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/ok-24x24.png b/UVtools.WPF/Assets/Icons/ok-24x24.png
deleted file mode 100644
index e4cc79d..0000000
--- a/UVtools.WPF/Assets/Icons/ok-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/open-16x16.png b/UVtools.WPF/Assets/Icons/open-16x16.png
deleted file mode 100644
index 40fcd9a..0000000
--- a/UVtools.WPF/Assets/Icons/open-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/pattern-16x16.png b/UVtools.WPF/Assets/Icons/pattern-16x16.png
deleted file mode 100644
index 714d8b5..0000000
--- a/UVtools.WPF/Assets/Icons/pattern-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/pencil-alt-16x16.png b/UVtools.WPF/Assets/Icons/pencil-alt-16x16.png
deleted file mode 100644
index a6002a8..0000000
--- a/UVtools.WPF/Assets/Icons/pencil-alt-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/pencil-alt-24x24.png b/UVtools.WPF/Assets/Icons/pencil-alt-24x24.png
deleted file mode 100644
index 82143e6..0000000
--- a/UVtools.WPF/Assets/Icons/pencil-alt-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/photo-16x16.png b/UVtools.WPF/Assets/Icons/photo-16x16.png
deleted file mode 100644
index af90fe0..0000000
--- a/UVtools.WPF/Assets/Icons/photo-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/photo-info-16x16.png b/UVtools.WPF/Assets/Icons/photo-info-16x16.png
deleted file mode 100644
index f6a4c2f..0000000
--- a/UVtools.WPF/Assets/Icons/photo-info-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/pixel-16x16.png b/UVtools.WPF/Assets/Icons/pixel-16x16.png
deleted file mode 100644
index d7c46a7..0000000
--- a/UVtools.WPF/Assets/Icons/pixel-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/pixel-32x32.png b/UVtools.WPF/Assets/Icons/pixel-32x32.png
deleted file mode 100644
index 15288e1..0000000
--- a/UVtools.WPF/Assets/Icons/pixel-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/plus-16x16.png b/UVtools.WPF/Assets/Icons/plus-16x16.png
deleted file mode 100644
index 7614e37..0000000
--- a/UVtools.WPF/Assets/Icons/plus-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/pointer-16x16.png b/UVtools.WPF/Assets/Icons/pointer-16x16.png
deleted file mode 100644
index fadbd93..0000000
--- a/UVtools.WPF/Assets/Icons/pointer-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/question-16x16.png b/UVtools.WPF/Assets/Icons/question-16x16.png
deleted file mode 100644
index bb4e680..0000000
--- a/UVtools.WPF/Assets/Icons/question-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/redo-16x16.png b/UVtools.WPF/Assets/Icons/redo-16x16.png
deleted file mode 100644
index 3408d68..0000000
--- a/UVtools.WPF/Assets/Icons/redo-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/refresh-16x16.png b/UVtools.WPF/Assets/Icons/refresh-16x16.png
deleted file mode 100644
index c5f7827..0000000
--- a/UVtools.WPF/Assets/Icons/refresh-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/resize-16x16.png b/UVtools.WPF/Assets/Icons/resize-16x16.png
deleted file mode 100644
index 3ba586c..0000000
--- a/UVtools.WPF/Assets/Icons/resize-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/ring-24x24.png b/UVtools.WPF/Assets/Icons/ring-24x24.png
deleted file mode 100644
index 41a18f5..0000000
--- a/UVtools.WPF/Assets/Icons/ring-24x24.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/save-16x16.png b/UVtools.WPF/Assets/Icons/save-16x16.png
deleted file mode 100644
index 38ce759..0000000
--- a/UVtools.WPF/Assets/Icons/save-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/save-as-16x16.png b/UVtools.WPF/Assets/Icons/save-as-16x16.png
deleted file mode 100644
index e22cce7..0000000
--- a/UVtools.WPF/Assets/Icons/save-as-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/search-16x16.png b/UVtools.WPF/Assets/Icons/search-16x16.png
deleted file mode 100644
index ea9d8e2..0000000
--- a/UVtools.WPF/Assets/Icons/search-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/settings-16x16.png b/UVtools.WPF/Assets/Icons/settings-16x16.png
deleted file mode 100644
index 83e560c..0000000
--- a/UVtools.WPF/Assets/Icons/settings-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/share-square-16x16.png b/UVtools.WPF/Assets/Icons/share-square-16x16.png
deleted file mode 100644
index 73b9e2f..0000000
--- a/UVtools.WPF/Assets/Icons/share-square-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/shield-virus-32x32.png b/UVtools.WPF/Assets/Icons/shield-virus-32x32.png
deleted file mode 100644
index e984a64..0000000
--- a/UVtools.WPF/Assets/Icons/shield-virus-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/sort-alpha-up-16x16.png b/UVtools.WPF/Assets/Icons/sort-alpha-up-16x16.png
deleted file mode 100644
index 765a4dd..0000000
--- a/UVtools.WPF/Assets/Icons/sort-alpha-up-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/square-root-16x16.png b/UVtools.WPF/Assets/Icons/square-root-16x16.png
deleted file mode 100644
index 4aa398b..0000000
--- a/UVtools.WPF/Assets/Icons/square-root-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/square-solid-16x16.png b/UVtools.WPF/Assets/Icons/square-solid-16x16.png
deleted file mode 100644
index d32f1a0..0000000
--- a/UVtools.WPF/Assets/Icons/square-solid-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/stroopwafel-16x16.png b/UVtools.WPF/Assets/Icons/stroopwafel-16x16.png
deleted file mode 100644
index 4fc0de6..0000000
--- a/UVtools.WPF/Assets/Icons/stroopwafel-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/sun-16x16.png b/UVtools.WPF/Assets/Icons/sun-16x16.png
deleted file mode 100644
index 997ab7e..0000000
--- a/UVtools.WPF/Assets/Icons/sun-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/sync-16x16.png b/UVtools.WPF/Assets/Icons/sync-16x16.png
deleted file mode 100644
index c5f7827..0000000
--- a/UVtools.WPF/Assets/Icons/sync-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/terminal-16x16.png b/UVtools.WPF/Assets/Icons/terminal-16x16.png
deleted file mode 100644
index 98f041d..0000000
--- a/UVtools.WPF/Assets/Icons/terminal-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/th-16x16.png b/UVtools.WPF/Assets/Icons/th-16x16.png
deleted file mode 100644
index 16bfb6d..0000000
--- a/UVtools.WPF/Assets/Icons/th-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/toolbox-16x16.png b/UVtools.WPF/Assets/Icons/toolbox-16x16.png
deleted file mode 100644
index fda6150..0000000
--- a/UVtools.WPF/Assets/Icons/toolbox-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/trash-16x16.png b/UVtools.WPF/Assets/Icons/trash-16x16.png
deleted file mode 100644
index ef9d610..0000000
--- a/UVtools.WPF/Assets/Icons/trash-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/undo-16x16.png b/UVtools.WPF/Assets/Icons/undo-16x16.png
deleted file mode 100644
index a9b83da..0000000
--- a/UVtools.WPF/Assets/Icons/undo-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/undo-alt-16x16.png b/UVtools.WPF/Assets/Icons/undo-alt-16x16.png
deleted file mode 100644
index 55f04c6..0000000
--- a/UVtools.WPF/Assets/Icons/undo-alt-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/usb-16x16.png b/UVtools.WPF/Assets/Icons/usb-16x16.png
deleted file mode 100644
index 03e109d..0000000
--- a/UVtools.WPF/Assets/Icons/usb-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/vector-square-16x16.png b/UVtools.WPF/Assets/Icons/vector-square-16x16.png
deleted file mode 100644
index a93e8ae..0000000
--- a/UVtools.WPF/Assets/Icons/vector-square-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/warning-16x16.png b/UVtools.WPF/Assets/Icons/warning-16x16.png
deleted file mode 100644
index d29b583..0000000
--- a/UVtools.WPF/Assets/Icons/warning-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/warning-32x32.png b/UVtools.WPF/Assets/Icons/warning-32x32.png
deleted file mode 100644
index ac59f08..0000000
--- a/UVtools.WPF/Assets/Icons/warning-32x32.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/wikipedia-16x16.png b/UVtools.WPF/Assets/Icons/wikipedia-16x16.png
deleted file mode 100644
index 71ea4ec..0000000
--- a/UVtools.WPF/Assets/Icons/wikipedia-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/wrench-16x16.png b/UVtools.WPF/Assets/Icons/wrench-16x16.png
deleted file mode 100644
index 2838a76..0000000
--- a/UVtools.WPF/Assets/Icons/wrench-16x16.png
+++ /dev/null
Binary files differ
diff --git a/UVtools.WPF/Assets/Styles/Styles.xaml b/UVtools.WPF/Assets/Styles/Styles.xaml
index 7e36b51..bcfcfbd 100644
--- a/UVtools.WPF/Assets/Styles/Styles.xaml
+++ b/UVtools.WPF/Assets/Styles/Styles.xaml
@@ -5,21 +5,34 @@
<Border Padding="20"></Border>
</Design.PreviewWith>
- <Style Selector="Border.GroupBox">
+
+ <Style Selector="Border.LayerNavigationToolTip">
+ <Setter Property="BorderThickness" Value="5"/>
+ <Setter Property="CornerRadius" Value="5"/>
+ </Style>
+
+ <Style Selector="Border.GroupBox">
<Setter Property="BorderThickness" Value="4" />
+ <Setter Property="BorderBrush" Value="{DynamicResource GroupBoxHeaderBackground}" />
+ </Style>
+
+ <Style Selector="Grid.GroupBoxHeader">
+ <Setter Property="Background" Value="{DynamicResource GroupBoxHeaderBackground}" />
</Style>
<Style Selector="TextBlock.GroupBoxHeader">
+ <Setter Property="Background" Value="{DynamicResource GroupBoxHeaderBackground}" />
<Setter Property="Padding" Value="10" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="Border.Header">
- <Setter Property="Padding" Value="10,10" />
- <Setter Property="Margin" Value="0,0,0,10" />
+ <Setter Property="Background" Value="{DynamicResource HeaderFooterBackground}" />
+ <Setter Property="Padding" Value="10,20" />
</Style>
<Style Selector="Border.FooterActions">
+ <Setter Property="Background" Value="{DynamicResource HeaderFooterBackground}" />
<Setter Property="Padding" Value="10,20" />
<Setter Property="Margin" Value="0,10,0, 0" />
</Style>
@@ -235,9 +248,11 @@
</Setter>
</Style>
+ <!--
<Style Selector="NumericUpDown /template/ TextBox:not(.empty)">
<Setter Property="MinWidth" Value="0"/>
</Style>
+ -->
<Style Selector="NumericUpDown">
<Setter Property="MinWidth" Value="130"/>
diff --git a/UVtools.WPF/Assets/Styles/StylesDark.xaml b/UVtools.WPF/Assets/Styles/StylesDark.xaml
new file mode 100644
index 0000000..16aabe4
--- /dev/null
+++ b/UVtools.WPF/Assets/Styles/StylesDark.xaml
@@ -0,0 +1,43 @@
+<Styles xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia">
+
+ <Design.PreviewWith>
+ <Border Padding="20"></Border>
+ </Design.PreviewWith>
+
+ <Styles.Resources>
+ <SolidColorBrush x:Key="GroupBoxHeaderBackground" Color="#4f5b62" />
+ <SolidColorBrush x:Key="HeaderFooterBackground" Color="#1E1E1E" />
+ <SolidColorBrush x:Key="LightBackground" Color="#1E1E1E" />
+
+ <SolidColorBrush x:Key="AdvancedImageBoxGridColor" Color="#263238" />
+ <SolidColorBrush x:Key="AdvancedImageBoxGridAlternateColor" Color="#1E1E1E" />
+ </Styles.Resources>
+
+ <Style Selector="GridSplitter">
+ <Setter Property="Background" Value="#263238"/>
+ </Style>
+
+ <Style Selector="Border.LayerNavigationToolTip">
+ <Setter Property="BorderBrush" Value="#4f5b62"/>
+ </Style>
+
+ <Style Selector="Canvas.IssuesTrackerCanvas">
+ <Setter Property="Background" Value="#1E1E1E"/>
+ </Style>
+
+ <Style Selector="Border.ProgressLoading">
+ <Setter Property="Background" Value="{StaticResource HeaderFooterBackground}"/>
+ <Setter Property="BorderBrush" Value="#4f5b62"/>
+ </Style>
+
+ <Style Selector="Border.BoxBackground">
+ <Setter Property="Background" Value="Transparent"/>
+ </Style>
+
+ <Style Selector="i|Icon">
+ <Setter Property="Foreground" Value="WhiteSmoke" />
+ </Style>
+
+</Styles> \ No newline at end of file
diff --git a/UVtools.WPF/Assets/Styles/StylesLight.xaml b/UVtools.WPF/Assets/Styles/StylesLight.xaml
index ca9b660..3223cb4 100644
--- a/UVtools.WPF/Assets/Styles/StylesLight.xaml
+++ b/UVtools.WPF/Assets/Styles/StylesLight.xaml
@@ -1,24 +1,43 @@
<Styles xmlns="https://github.com/avaloniaui"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia">
<Design.PreviewWith>
<Border Padding="20"></Border>
</Design.PreviewWith>
- <Style Selector="Border.GroupBox">
- <Setter Property="BorderBrush" Value="LightBlue" />
+ <Styles.Resources>
+ <SolidColorBrush x:Key="GroupBoxHeaderBackground" Color="LightBlue" />
+ <SolidColorBrush x:Key="HeaderFooterBackground" Color="LightGray" />
+ <SolidColorBrush x:Key="LightBackground" Color="WhiteSmoke" />
+
+ <SolidColorBrush x:Key="AdvancedImageBoxGridColor" Color="White" />
+ <SolidColorBrush x:Key="AdvancedImageBoxGridAlternateColor" Color="Gainsboro" />
+ </Styles.Resources>
+
+ <Style Selector="GridSplitter">
+ <Setter Property="Background" Value="Gainsboro"/>
+ </Style>
+
+ <Style Selector="Border.LayerNavigationToolTip">
+ <Setter Property="BorderBrush" Value="AliceBlue"/>
</Style>
- <Style Selector="TextBlock.GroupBoxHeader">
- <Setter Property="Background" Value="LightBlue" />
+ <Style Selector="Canvas.IssuesTrackerCanvas">
+ <Setter Property="Background" Value="{StaticResource LightBackground}"/>
</Style>
- <Style Selector="Border.Header">
- <Setter Property="Background" Value="LightGray" />
+ <Style Selector="Border.ProgressLoading">
+ <Setter Property="Background" Value="White"/>
+ <Setter Property="BorderBrush" Value="{StaticResource LightBackground}"/>
+ </Style>
+
+ <Style Selector="Border.BoxBackground">
+ <Setter Property="Background" Value="WhiteSmoke"/>
</Style>
- <Style Selector="Border.FooterActions">
- <Setter Property="Background" Value="LightGray" />
- </Style>
+ <Style Selector="i|Icon">
+ <Setter Property="Foreground" Value="#1E1E1E" />
+ </Style>
</Styles> \ No newline at end of file
diff --git a/UVtools.WPF/ConsoleArguments.cs b/UVtools.WPF/ConsoleArguments.cs
index f2cd96d..d8e99cc 100644
--- a/UVtools.WPF/ConsoleArguments.cs
+++ b/UVtools.WPF/ConsoleArguments.cs
@@ -9,355 +9,353 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using MoreLinq;
using UVtools.Core.FileFormats;
using UVtools.Core.MeshFormats;
using UVtools.Core.Operations;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public static class ConsoleArguments
{
- public static class ConsoleArguments
+ /// <summary>
+ /// Parse arguments from command line
+ /// </summary>
+ /// <param name="args"></param>
+ /// <returns>True if is a valid argument, otherwise false</returns>
+ public static bool ParseArgs(string[] args)
{
- /// <summary>
- /// Parse arguments from command line
- /// </summary>
- /// <param name="args"></param>
- /// <returns>True if is a valid argument, otherwise false</returns>
- public static bool ParseArgs(string[] args)
- {
- if(args is null || args.Length == 0) return false;
+ if(args is null || args.Length == 0) return false;
- // Convert to other file
- if (args[0] is "-c" or "--convert")
+ // Convert to other file
+ if (args[0] is "-c" or "--convert")
+ {
+ if (args.Length < 3)
{
- if (args.Length < 3)
- {
- Console.WriteLine("Invalid syntax: <input_file> <output_file1/ext1> [output_file2/ext2]");
- return true;
- }
- if (!File.Exists(args[1]))
- {
- Console.WriteLine($"Input file does not exists: {args[1]}");
- return true;
- }
+ Console.WriteLine("Invalid syntax: <input_file> <output_file1/ext1> [output_file2/ext2]");
+ return true;
+ }
+ if (!File.Exists(args[1]))
+ {
+ Console.WriteLine($"Input file does not exists: {args[1]}");
+ return true;
+ }
- var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
- if (slicerFile is null)
- {
- Console.WriteLine($"Invalid input file: {args[1]}");
- return true;
- }
+ var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
+ if (slicerFile is null)
+ {
+ Console.WriteLine($"Invalid input file: {args[1]}");
+ return true;
+ }
- var filenameNoExt = FileFormat.GetFileNameStripExtensions(args[1], out _);
+ var filenameNoExt = FileFormat.GetFileNameStripExtensions(args[1], out _);
- var filesToConvert = new List<KeyValuePair<FileFormat, string>>();
+ var filesToConvert = new List<KeyValuePair<FileFormat, string>>();
- for (int i = 2; i < args.Length; i++)
+ for (int i = 2; i < args.Length; i++)
+ {
+ var outputFile = args[i];
+ var targetFormat = FileFormat.FindByExtensionOrFilePath(args[i], true);
+ if (targetFormat is null)
{
- var outputFile = args[i];
- var targetFormat = FileFormat.FindByExtensionOrFilePath(args[i], true);
- if (targetFormat is null)
- {
- Console.WriteLine($"Invalid output file/extension: {args[i]}");
- continue;
- }
-
- if(targetFormat.IsExtensionValid(outputFile))
- outputFile = $"{filenameNoExt}.{args[i]}";
+ Console.WriteLine($"Invalid output file/extension: {args[i]}");
+ continue;
+ }
+
+ if(targetFormat.IsExtensionValid(outputFile))
+ outputFile = $"{filenameNoExt}.{args[i]}";
- filesToConvert.Add(new KeyValuePair<FileFormat, string>(targetFormat, outputFile));
- }
+ filesToConvert.Add(new KeyValuePair<FileFormat, string>(targetFormat, outputFile));
+ }
- if (filesToConvert.Count == 0)
- {
- return true;
- }
+ if (filesToConvert.Count == 0)
+ {
+ return true;
+ }
- //var workingDir = Path.GetDirectoryName(args[1]);
- //if(!string.IsNullOrWhiteSpace(workingDir)) Directory.SetCurrentDirectory(workingDir);
+ //var workingDir = Path.GetDirectoryName(args[1]);
+ //if(!string.IsNullOrWhiteSpace(workingDir)) Directory.SetCurrentDirectory(workingDir);
- Console.WriteLine($"Loading file: {args[1]}");
- slicerFile.Decode(args[1]);
+ Console.WriteLine($"Loading file: {args[1]}");
+ slicerFile.Decode(args[1]);
- foreach (var (outputSlicerFile, outputFile) in filesToConvert.DistinctBy(pair => pair.Value))
- {
- Console.WriteLine($"Converting to: {outputFile}");
- slicerFile.Convert(outputSlicerFile, outputFile);
- Console.WriteLine("Converted");
+ foreach (var (outputSlicerFile, outputFile) in filesToConvert.DistinctBy(pair => pair.Value))
+ {
+ Console.WriteLine($"Converting to: {outputFile}");
+ slicerFile.Convert(outputSlicerFile, outputFile);
+ Console.WriteLine("Converted");
- }
+ }
+
+ Console.WriteLine("OK");
- Console.WriteLine("OK");
+ return true;
+ }
+ // Extract the file
+ if (args[0] is "-e" or "--extract")
+ {
+ if (args.Length < 2)
+ {
+ Console.WriteLine("Invalid syntax: <input_file> [output_folder]");
+ return true;
+ }
+ if (!File.Exists(args[1]))
+ {
+ Console.WriteLine($"Input file does not exists: {args[1]}");
return true;
}
- // Extract the file
- if (args[0] is "-e" or "--extract")
+ var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
+ if (slicerFile is null)
{
- if (args.Length < 2)
- {
- Console.WriteLine("Invalid syntax: <input_file> [output_folder]");
- return true;
- }
- if (!File.Exists(args[1]))
+ Console.WriteLine($"Invalid input file: {args[1]}");
+ return true;
+ }
+
+ var outputFolder = FileFormat.GetFileNameStripExtensions(args[1], out _);
+
+ if (args.Length >= 3 && !string.IsNullOrWhiteSpace(args[2]))
+ {
+ try
{
- Console.WriteLine($"Input file does not exists: {args[1]}");
- return true;
+ Path.GetFullPath(outputFolder);
+ outputFolder = args[2];
}
-
- var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
- if (slicerFile is null)
+ catch (Exception)
{
- Console.WriteLine($"Invalid input file: {args[1]}");
+ Console.WriteLine($"Invalid output directory: {args[2]}");
return true;
}
+ }
- var outputFolder = FileFormat.GetFileNameStripExtensions(args[1], out _);
-
- if (args.Length >= 3 && !string.IsNullOrWhiteSpace(args[2]))
- {
- try
- {
- Path.GetFullPath(outputFolder);
- outputFolder = args[2];
- }
- catch (Exception)
- {
- Console.WriteLine($"Invalid output directory: {args[2]}");
- return true;
- }
- }
+ //var workingDir = Path.GetDirectoryName(args[1]);
+ //if(!string.IsNullOrWhiteSpace(workingDir)) Directory.SetCurrentDirectory(workingDir);
- //var workingDir = Path.GetDirectoryName(args[1]);
- //if(!string.IsNullOrWhiteSpace(workingDir)) Directory.SetCurrentDirectory(workingDir);
+ Console.WriteLine($"Loading file: {args[1]}");
+ slicerFile.Decode(args[1]);
+ Console.WriteLine($"Extracting to: {outputFolder}");
+ slicerFile.Extract(outputFolder);
+ Console.WriteLine("Extracted");
+ Console.WriteLine("OK");
+ return true;
+ }
- Console.WriteLine($"Loading file: {args[1]}");
- slicerFile.Decode(args[1]);
- Console.WriteLine($"Extracting to: {outputFolder}");
- slicerFile.Extract(outputFolder);
- Console.WriteLine("Extracted");
- Console.WriteLine("OK");
+ // Convert to other file
+ if (args[0] is "--export-mesh")
+ {
+ if (args.Length < 2)
+ {
+ Console.WriteLine("Invalid syntax: <input_file> [output_mesh_file]");
+ return true;
+ }
+ if (!File.Exists(args[1]))
+ {
+ Console.WriteLine($"Input file does not exists: {args[1]}");
return true;
}
- // Convert to other file
- if (args[0] is "--export-mesh")
+ var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
+ if (slicerFile is null)
{
- if (args.Length < 2)
- {
- Console.WriteLine("Invalid syntax: <input_file> [output_mesh_file]");
- return true;
- }
- if (!File.Exists(args[1]))
- {
- Console.WriteLine($"Input file does not exists: {args[1]}");
- return true;
- }
+ Console.WriteLine($"Invalid input file: {args[1]}");
+ return true;
+ }
- var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
- if (slicerFile is null)
- {
- Console.WriteLine($"Invalid input file: {args[1]}");
- return true;
- }
+ var outputFile = Path.Combine(Path.GetDirectoryName(args[1]), $"{Path.GetFileNameWithoutExtension(args[1])}.stl");
- var outputFile = Path.Combine(Path.GetDirectoryName(args[1]), $"{Path.GetFileNameWithoutExtension(args[1])}.stl");
+ if (args.Length >= 3 && !string.IsNullOrWhiteSpace(args[2]))
+ {
+ outputFile = args[2];
+ }
- if (args.Length >= 3 && !string.IsNullOrWhiteSpace(args[2]))
- {
- outputFile = args[2];
- }
+ var outputExtension = MeshFile.FindFileExtension(outputFile);
+ if (outputExtension is null)
+ {
+ Console.WriteLine($"Invalid output file extension: {outputFile}");
+ return true;
+ }
- var outputExtension = MeshFile.FindFileExtension(outputFile);
- if (outputExtension is null)
- {
- Console.WriteLine($"Invalid output file extension: {outputFile}");
- return true;
- }
+ Console.WriteLine($"Loading file: {args[1]}");
+ slicerFile.Decode(args[1]);
- Console.WriteLine($"Loading file: {args[1]}");
- slicerFile.Decode(args[1]);
+ Console.WriteLine($"Exporting mesh to: {outputFile}");
+ var operation = new OperationLayerExportMesh(slicerFile)
+ {
+ FilePath = outputFile
+ };
+ operation.Execute();
+ Console.WriteLine("Exported");
- Console.WriteLine($"Exporting mesh to: {outputFile}");
- var operation = new OperationLayerExportMesh(slicerFile)
- {
- FilePath = outputFile
- };
- operation.Execute();
- Console.WriteLine("Exported");
+ Console.WriteLine("OK");
- Console.WriteLine("OK");
+ return true;
+ }
+ // Run operation and save file
+ if (args[0] is "--run-operation")
+ {
+ if (args.Length < 3)
+ {
+ Console.WriteLine("Invalid syntax: <input_file> <operation_file.uvtop>");
return true;
}
- // Run operation and save file
- if (args[0] is "--run-operation")
+ if (!File.Exists(args[1]))
{
- if (args.Length < 3)
- {
- Console.WriteLine("Invalid syntax: <input_file> <operation_file.uvtop>");
- return true;
- }
+ Console.WriteLine($"Input file does not exists: {args[1]}");
+ return true;
+ }
- if (!File.Exists(args[1]))
- {
- Console.WriteLine($"Input file does not exists: {args[1]}");
- return true;
- }
+ if (!File.Exists(args[2]))
+ {
+ Console.WriteLine($"Operation file does not exists: {args[2]}");
+ return true;
+ }
- if (!File.Exists(args[2]))
- {
- Console.WriteLine($"Operation file does not exists: {args[2]}");
- return true;
- }
+ var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
+ if (slicerFile is null)
+ {
+ Console.WriteLine($"Invalid input file: {args[1]}");
+ return true;
+ }
- var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
- if (slicerFile is null)
- {
- Console.WriteLine($"Invalid input file: {args[1]}");
- return true;
- }
+ var operation = Operation.Deserialize(args[2]);
- var operation = Operation.Deserialize(args[2]);
+ if (operation is null)
+ {
+ Console.WriteLine($"Invalid operation file: {args[2]}");
+ return true;
+ }
- if (operation is null)
- {
- Console.WriteLine($"Invalid operation file: {args[2]}");
- return true;
- }
+ Console.WriteLine($"Loading file: {args[1]}");
+ slicerFile.Decode(args[1]);
- Console.WriteLine($"Loading file: {args[1]}");
- slicerFile.Decode(args[1]);
+ Console.WriteLine($"Running operation: {operation.Title}");
+ operation.SlicerFile = slicerFile;
+ operation.Execute();
+ slicerFile.Save();
+ Console.WriteLine("Saved");
+ Console.WriteLine("OK");
- Console.WriteLine($"Running operation: {operation.Title}");
- operation.SlicerFile = slicerFile;
- operation.Execute();
- slicerFile.Save();
- Console.WriteLine("Saved");
- Console.WriteLine("OK");
+ return true;
+ }
+ // Run operation and save file
+ if (args[0] is "--run-script")
+ {
+ if (args.Length < 3)
+ {
+ Console.WriteLine("Invalid syntax: <input_file> <script_file.cs>");
return true;
}
- // Run operation and save file
- if (args[0] is "--run-script")
+ if (!File.Exists(args[1]))
{
- if (args.Length < 3)
- {
- Console.WriteLine("Invalid syntax: <input_file> <script_file.cs>");
- return true;
- }
-
- if (!File.Exists(args[1]))
- {
- Console.WriteLine($"Input file does not exists: {args[1]}");
- return true;
- }
+ Console.WriteLine($"Input file does not exists: {args[1]}");
+ return true;
+ }
- if (!File.Exists(args[2]) || !(args[2].EndsWith(".csx") || args[2].EndsWith(".cs")))
- {
- Console.WriteLine($"Script file does not exists or invalid: {args[2]}");
- return true;
- }
+ if (!File.Exists(args[2]) || !(args[2].EndsWith(".csx") || args[2].EndsWith(".cs")))
+ {
+ Console.WriteLine($"Script file does not exists or invalid: {args[2]}");
+ return true;
+ }
- var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
- if (slicerFile is null)
- {
- Console.WriteLine($"Invalid input file: {args[1]}");
- return true;
- }
+ var slicerFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
+ if (slicerFile is null)
+ {
+ Console.WriteLine($"Invalid input file: {args[1]}");
+ return true;
+ }
- Console.WriteLine($"Loading file: {args[1]}");
- slicerFile.Decode(args[1]);
+ Console.WriteLine($"Loading file: {args[1]}");
+ slicerFile.Decode(args[1]);
- var operation = new OperationScripting(slicerFile);
- operation.ReloadScriptFromFile(args[2]);
+ var operation = new OperationScripting(slicerFile);
+ operation.ReloadScriptFromFile(args[2]);
- Console.WriteLine($"Running script: {operation.Title}");
- operation.Execute();
- slicerFile.Save();
- Console.WriteLine("Saved");
- Console.WriteLine("OK");
+ Console.WriteLine($"Running script: {operation.Title}");
+ operation.Execute();
+ slicerFile.Save();
+ Console.WriteLine("Saved");
+ Console.WriteLine("OK");
+
+ return true;
+ }
+ // Run operation and save file
+ if (args[0] is "--copy-parameters")
+ {
+ if (args.Length < 3)
+ {
+ Console.WriteLine("Invalid syntax: <from_file> <to_file>");
return true;
}
- // Run operation and save file
- if (args[0] is "--copy-parameters")
+ if (args[1] == args[2])
{
- if (args.Length < 3)
- {
- Console.WriteLine("Invalid syntax: <from_file> <to_file>");
- return true;
- }
-
- if (args[1] == args[2])
- {
- Console.WriteLine($"Source file must be different from target file");
- return true;
- }
+ Console.WriteLine($"Source file must be different from target file");
+ return true;
+ }
- if (!File.Exists(args[1]))
- {
- Console.WriteLine($"Source file does not exists: {args[1]}");
- return true;
- }
+ if (!File.Exists(args[1]))
+ {
+ Console.WriteLine($"Source file does not exists: {args[1]}");
+ return true;
+ }
- if (!File.Exists(args[2]))
- {
- Console.WriteLine($"Target file does not exists: {args[1]}");
- return true;
- }
+ if (!File.Exists(args[2]))
+ {
+ Console.WriteLine($"Target file does not exists: {args[1]}");
+ return true;
+ }
- var fromFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
- if (fromFile is null)
- {
- Console.WriteLine($"Invalid source file: {args[1]}");
- return true;
- }
+ var fromFile = FileFormat.FindByExtensionOrFilePath(args[1], true);
+ if (fromFile is null)
+ {
+ Console.WriteLine($"Invalid source file: {args[1]}");
+ return true;
+ }
- var toFile = FileFormat.FindByExtensionOrFilePath(args[2], true);
- if (toFile is null)
- {
- Console.WriteLine($"Invalid target file: {args[2]}");
- return true;
- }
+ var toFile = FileFormat.FindByExtensionOrFilePath(args[2], true);
+ if (toFile is null)
+ {
+ Console.WriteLine($"Invalid target file: {args[2]}");
+ return true;
+ }
- Console.WriteLine("Loading files");
- fromFile.Decode(args[1], FileFormat.FileDecodeType.Partial);
- toFile.Decode(args[2], FileFormat.FileDecodeType.Partial);
+ Console.WriteLine("Loading files");
+ fromFile.Decode(args[1], FileFormat.FileDecodeType.Partial);
+ toFile.Decode(args[2], FileFormat.FileDecodeType.Partial);
- var count = FileFormat.CopyParameters(fromFile, toFile);
- Console.WriteLine($"Modified parameters: {count}");
- if (count > 0)
- {
- toFile.Save();
- Console.WriteLine("Saved");
- }
- Console.WriteLine("OK");
-
- return true;
+ var count = FileFormat.CopyParameters(fromFile, toFile);
+ Console.WriteLine($"Modified parameters: {count}");
+ if (count > 0)
+ {
+ toFile.Save();
+ Console.WriteLine("Saved");
}
+ Console.WriteLine("OK");
- if (args[0] == "--crypt-ctb")
+ return true;
+ }
+
+ if (args[0] == "--crypt-ctb")
+ {
+ if (!File.Exists(args[1]))
{
- if (!File.Exists(args[1]))
- {
- Console.WriteLine($"Input file does not exists: {args[1]}");
- return true;
- }
-
- CTBEncryptedFile.CryptFile(args[1]);
-
+ Console.WriteLine($"Input file does not exists: {args[1]}");
return true;
}
-
- return false;
+
+ CTBEncryptedFile.CryptFile(args[1]);
+
+ return true;
}
+
+ return false;
}
} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/ButtonWithIcon.cs b/UVtools.WPF/Controls/ButtonWithIcon.cs
new file mode 100644
index 0000000..11edc87
--- /dev/null
+++ b/UVtools.WPF/Controls/ButtonWithIcon.cs
@@ -0,0 +1,123 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Layout;
+using Avalonia.Styling;
+using Avalonia.Threading;
+
+namespace UVtools.WPF.Controls;
+
+public class ButtonWithIcon : Button, IStyleable
+{
+ public enum IconPlacementType : byte
+ {
+ Left,
+ Right,
+ Top,
+ Bottom
+ }
+
+ Type IStyleable.StyleKey => typeof(Button);
+
+ public static readonly StyledProperty<string> TextProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, string>(nameof(Text));
+
+ public string Text
+ {
+ get => GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public static readonly StyledProperty<IconPlacementType> IconPlacementProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, IconPlacementType>(nameof(IconPlacement));
+
+ public IconPlacementType IconPlacement
+ {
+ get => GetValue(IconPlacementProperty);
+ set => SetValue(IconPlacementProperty, value);
+ }
+
+ public static readonly StyledProperty<string> IconProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, string>(nameof(Icon));
+
+ public string Icon
+ {
+ get => GetValue(IconProperty);
+ set => SetValue(IconProperty, value);
+ }
+
+ public static readonly StyledProperty<double> SpacingProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, double>(nameof(Spacing), 10);
+
+ public double Spacing
+ {
+ get => GetValue(SpacingProperty);
+ set => SetValue(SpacingProperty, value);
+ }
+
+ public ButtonWithIcon()
+ {
+ }
+
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ Dispatcher.UIThread.Post(() =>
+ {
+ TextProperty.Changed.Subscribe(_ => RebuildContent());
+ IconProperty.Changed.Subscribe(_ => RebuildContent());
+ IconPlacementProperty.Changed.Subscribe(_ => RebuildContent());
+ RebuildContent();
+ }, DispatcherPriority.Loaded);
+ }
+
+ public IControl MakeIcon()
+ {
+ return new Projektanker.Icons.Avalonia.Icon { Value = Icon };
+ }
+
+ private void RebuildContent()
+ {
+ if (string.IsNullOrWhiteSpace(Icon))
+ {
+ if (!string.IsNullOrWhiteSpace(Text))
+ {
+ Content = Text;
+ }
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(Text))
+ {
+ if (!string.IsNullOrWhiteSpace(Icon))
+ {
+ Content = MakeIcon();
+ }
+
+ return;
+ }
+
+ var panel = new StackPanel
+ {
+ Spacing = Spacing,
+ VerticalAlignment = VerticalAlignment.Stretch,
+ Orientation = IconPlacement is IconPlacementType.Left or IconPlacementType.Right
+ ? Orientation.Horizontal
+ : Orientation.Vertical
+ };
+
+ if(IconPlacement is IconPlacementType.Left or IconPlacementType.Top) panel.Children.Add(MakeIcon());
+ panel.Children.Add(new TextBlock{ VerticalAlignment = VerticalAlignment.Center, Text = Text});
+ if (IconPlacement is IconPlacementType.Right or IconPlacementType.Bottom) panel.Children.Add(MakeIcon());
+
+ Content = panel;
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs
index 088822d..93ee145 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs
@@ -7,74 +7,73 @@ using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Calibrators
+namespace UVtools.WPF.Controls.Calibrators;
+
+public class CalibrateElephantFootControl : ToolControl
{
- public class CalibrateElephantFootControl : ToolControl
- {
- public OperationCalibrateElephantFoot Operation => BaseOperation as OperationCalibrateElephantFoot;
+ public OperationCalibrateElephantFoot Operation => BaseOperation as OperationCalibrateElephantFoot;
- private readonly Timer _timer;
+ private readonly Timer _timer;
- private Bitmap _previewImage;
- public Bitmap PreviewImage
- {
- get => _previewImage;
- set => RaiseAndSetIfChanged(ref _previewImage, value);
- }
+ private Bitmap _previewImage;
+ public Bitmap PreviewImage
+ {
+ get => _previewImage;
+ set => RaiseAndSetIfChanged(ref _previewImage, value);
+ }
- public CalibrateElephantFootControl()
- {
- BaseOperation = new OperationCalibrateElephantFoot(SlicerFile);
- if (!ValidateSpawn()) return;
+ public CalibrateElephantFootControl()
+ {
+ BaseOperation = new OperationCalibrateElephantFoot(SlicerFile);
+ if (!ValidateSpawn()) return;
- InitializeComponent();
+ InitializeComponent();
- _timer = new Timer(20)
- {
- AutoReset = false
- };
- _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
- }
-
- private void InitializeComponent()
+ _timer = new Timer(20)
{
- AvaloniaXamlLoader.Load(this);
- }
+ AutoReset = false
+ };
+ _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (App.SlicerFile is null) return;
+ switch (callback)
{
- if (App.SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, e) =>
- {
- _timer.Stop();
- _timer.Start();
- if (e.PropertyName == nameof(Operation.ObjectCount))
- {
- ParentWindow.ButtonOkEnabled = Operation.ObjectCount > 0;
- return;
- }
- };
- if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.ObjectCount > 0;
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, e) =>
+ {
_timer.Stop();
_timer.Start();
- break;
- }
+ if (e.PropertyName == nameof(Operation.ObjectCount))
+ {
+ ParentWindow.ButtonOkEnabled = Operation.ObjectCount > 0;
+ return;
+ }
+ };
+ if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.ObjectCount > 0;
+ _timer.Stop();
+ _timer.Start();
+ break;
}
+ }
- public void UpdatePreview()
+ public void UpdatePreview()
+ {
+ var layers = Operation.GetLayers();
+ _previewImage?.Dispose();
+ PreviewImage = layers[0].ToBitmap();
+ foreach (var layer in layers)
{
- var layers = Operation.GetLayers();
- _previewImage?.Dispose();
- PreviewImage = layers[0].ToBitmap();
- foreach (var layer in layers)
- {
- layer.Dispose();
- }
+ layer.Dispose();
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml
index 8145d61..f60c1bc 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:controls="clr-namespace:UVtools.WPF.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Calibrators.CalibrateExposureFinderControl">
<Grid ColumnDefinitions="Auto,10,380">
@@ -506,13 +507,11 @@
<Expander IsExpanded="False">
<Expander.Header>
- <StackPanel Orientation="Horizontal" Cursor="Hand">
+ <StackPanel Orientation="Horizontal">
<TextBlock Text="Multiple brightness"
- IsVisible="{Binding !SlicerFile.IsAntiAliasingEmulated}"
- FontWeight="Bold"/>
+ IsVisible="{Binding !SlicerFile.IsAntiAliasingEmulated}"/>
<TextBlock Text="Multiple brightness/exposures with emulated AntiAliasing and time fractions"
- IsVisible="{Binding SlicerFile.IsAntiAliasingEmulated}"
- FontWeight="Bold"/>
+ IsVisible="{Binding SlicerFile.IsAntiAliasingEmulated}"/>
</StackPanel>
</Expander.Header>
@@ -574,19 +573,14 @@
Value="{Binding Operation.MultipleBrightnessGenExposureTime}"/>
- <Button
+ <controls:ButtonWithIcon
Grid.Row="0" Grid.Column="10" VerticalContentAlignment="Center"
IsVisible="{Binding !SlicerFile.IsAntiAliasingEmulated}"
HorizontalContentAlignment="Center"
VerticalAlignment="Stretch"
- Command="{Binding BrightnessExposureGenAdd}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="Add"/>
- </StackPanel>
- </Button>
+ Command="{Binding BrightnessExposureGenAdd}"
+ Text="Add"
+ Icon="fas fa-plus"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="Brightnesses:"
@@ -644,22 +638,7 @@
IsVisible="{Binding SlicerFile.IsAntiAliasingEmulated}"
Text="÷"
VerticalAlignment="Center"/>
-
- <!--<Button
- IsVisible="{Binding SlicerFile.IsAntiAliasingEmulated}"
- Grid.Row="4" Grid.Column="12" VerticalContentAlignment="Center"
- HorizontalContentAlignment="Center"
- VerticalAlignment="Stretch"
- Command="{Binding Operation.GenerateBrightnessExposureFractions}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/refresh-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="Gen"/>
- </StackPanel>
- </Button>
- !-->
- </Grid>
+ </Grid>
<DataGrid
@@ -888,19 +867,14 @@
FormatString="F2"
Value="{Binding Operation.ExposureGenManualNormal}"/>
- <Button
+ <controls:ButtonWithIcon
Grid.Row="0" Grid.Column="14" VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
VerticalAlignment="Stretch"
IsEnabled="{Binding Operation.ExposureGenManualLayerHeight}"
- Command="{Binding ExposureTableAddManual}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="Add"/>
- </StackPanel>
- </Button>
+ Command="{Binding ExposureTableAddManual}"
+ Text="Add"
+ Icon="fas fa-plus"/>
</Grid>
<Grid RowDefinitions="Auto" ColumnDefinitions="Auto,*,Auto"
@@ -908,35 +882,24 @@
Margin="0,10,0,0">
<TextBlock Text="{Binding Operation.ExposureTable.Count, StringFormat=Exposure table: {0} entries}" FontWeight="Bold"/>
- <Button
+ <controls:ButtonWithIcon
Grid.Column="1" VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
HorizontalAlignment="Right"
IsEnabled="{Binding #ExposureTable.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding ExposureTableRemoveSelectedEntries}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/trash-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="Remove selected entries"/>
- </StackPanel>
- </Button>
-
- <Button
+ Command="{Binding ExposureTableRemoveSelectedEntries}"
+ Text="Remove selected entries"
+ Icon="fas fa-trash-alt"/>
+
+ <controls:ButtonWithIcon
Grid.Column="2" VerticalContentAlignment="Center"
Margin="10,0,0,0"
HorizontalContentAlignment="Center"
HorizontalAlignment="Right"
IsEnabled="{Binding Operation.ExposureTable.Count}"
- Command="{Binding ExposureTableClearEntries}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="{Binding Operation.ExposureTable.Count, StringFormat=Clear {0} entries}"/>
- </StackPanel>
- </Button>
-
+ Command="{Binding ExposureTableClearEntries}"
+ Text="{Binding Operation.ExposureTable.Count, StringFormat=Clear {0} entries}"
+ Icon="fas fa-times"/>
</Grid>
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs
index c287f01..9168587 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs
@@ -14,142 +14,141 @@ using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Calibrators
+namespace UVtools.WPF.Controls.Calibrators;
+
+public class CalibrateExposureFinderControl : ToolControl
{
- public class CalibrateExposureFinderControl : ToolControl
- {
- public OperationCalibrateExposureFinder Operation => BaseOperation as OperationCalibrateExposureFinder;
+ public OperationCalibrateExposureFinder Operation => BaseOperation as OperationCalibrateExposureFinder;
- private Timer _timer;
+ private Timer _timer;
- private Bitmap _previewImage;
- private DataGrid _exposureTable;
+ private Bitmap _previewImage;
+ private DataGrid _exposureTable;
- public Bitmap PreviewImage
- {
- get => _previewImage;
- set => RaiseAndSetIfChanged(ref _previewImage, value);
- }
+ public Bitmap PreviewImage
+ {
+ get => _previewImage;
+ set => RaiseAndSetIfChanged(ref _previewImage, value);
+ }
- public bool CanSupportPerLayerSettings => SlicerFile.HaveLayerParameterModifier(FileFormat.PrintParameterModifier.ExposureTime);
+ public bool CanSupportPerLayerSettings => SlicerFile.HaveLayerParameterModifier(FileFormat.PrintParameterModifier.ExposureTime);
- public CalibrateExposureFinderControl()
- {
- BaseOperation = new OperationCalibrateExposureFinder(SlicerFile);
- if (!ValidateSpawn()) return;
+ public CalibrateExposureFinderControl()
+ {
+ BaseOperation = new OperationCalibrateExposureFinder(SlicerFile);
+ if (!ValidateSpawn()) return;
- InitializeComponent();
+ InitializeComponent();
- _exposureTable = this.FindControl<DataGrid>("ExposureTable");
-
- _timer = new Timer(100)
- {
- AutoReset = false
- };
- _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
- }
+ _exposureTable = this.FindControl<DataGrid>("ExposureTable");
- private void InitializeComponent()
+ _timer = new Timer(100)
{
- AvaloniaXamlLoader.Load(this);
- }
+ AutoReset = false
+ };
+ _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (App.SlicerFile is null) return;
+ switch (callback)
{
- if (App.SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, e) =>
- {
- _timer.Stop();
- _timer.Start();
- };
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, e) =>
+ {
_timer.Stop();
_timer.Start();
- break;
- }
+ };
+ _timer.Stop();
+ _timer.Start();
+ break;
}
+ }
- public void UpdatePreview()
+ public void UpdatePreview()
+ {
+ var layers = Operation.GetLayers(true);
+ _previewImage?.Dispose();
+ if (layers is not null)
{
- var layers = Operation.GetLayers(true);
- _previewImage?.Dispose();
- if (layers is not null)
+ PreviewImage = layers[^1].ToBitmap();
+ foreach (var layer in layers)
{
- PreviewImage = layers[^1].ToBitmap();
- foreach (var layer in layers)
- {
- layer.Dispose();
- }
+ layer.Dispose();
}
}
+ }
- public void BrightnessExposureGenAdd()
- {
- var values = Operation.MultipleBrightnessValuesArray.ToList();
- // normal exposure - 255
- // wanted - x
- byte brightness = (byte)Math.Round(Operation.MultipleBrightnessGenExposureTime * byte.MaxValue / Operation.NormalExposure).Clamp(1, byte.MaxValue);
- if (values.Contains(brightness)) return;
- values.Add(brightness);
- values.Sort((b, b1) => b1.CompareTo(b));
- Operation.MultipleBrightnessValues = string.Join(", ", values);
- }
+ public void BrightnessExposureGenAdd()
+ {
+ var values = Operation.MultipleBrightnessValuesArray.ToList();
+ // normal exposure - 255
+ // wanted - x
+ byte brightness = (byte)Math.Round(Operation.MultipleBrightnessGenExposureTime * byte.MaxValue / Operation.NormalExposure).Clamp(1, byte.MaxValue);
+ if (values.Contains(brightness)) return;
+ values.Add(brightness);
+ values.Sort((b, b1) => b1.CompareTo(b));
+ Operation.MultipleBrightnessValues = string.Join(", ", values);
+ }
- public async void GenerateExposureTable()
+ public async void GenerateExposureTable()
+ {
+ if (Operation.ExposureTable.Count > 0)
{
- if (Operation.ExposureTable.Count > 0)
- {
- if (await ParentWindow.MessageBoxQuestion(
+ if (await ParentWindow.MessageBoxQuestion(
"This automatic exposure table generation will clear the current table data!\n" +
"Do you want to continue?"
- ) != ButtonResult.Yes) return;
- }
-
- Operation.GenerateExposureTable();
+ ) != ButtonResult.Yes) return;
}
- public async void ExposureTableAddManual()
- {
- var exposure = Operation.ExposureManualEntry;
- if (!exposure.IsValid)
- {
- await ParentWindow.MessageBoxError(
- $"Layer height and exposures must be higher than zero (0).\n{exposure}");
- return;
- }
-
- if (Operation.ExposureTable.Contains(exposure))
- {
- await ParentWindow.MessageBoxError(
- $"The configured layer height and exposure data already exists on the table.\n{exposure}");
- return;
- }
+ Operation.GenerateExposureTable();
+ }
- Operation.ExposureTable.Add(exposure);
- Operation.SanitizeExposureTable();
+ public async void ExposureTableAddManual()
+ {
+ var exposure = Operation.ExposureManualEntry;
+ if (!exposure.IsValid)
+ {
+ await ParentWindow.MessageBoxError(
+ $"Layer height and exposures must be higher than zero (0).\n{exposure}");
+ return;
}
- public async void ExposureTableClearEntries()
+ if (Operation.ExposureTable.Contains(exposure))
{
- if (Operation.ExposureTable.Count <= 0) return;
- if (await ParentWindow.MessageBoxQuestion(
+ await ParentWindow.MessageBoxError(
+ $"The configured layer height and exposure data already exists on the table.\n{exposure}");
+ return;
+ }
+
+ Operation.ExposureTable.Add(exposure);
+ Operation.SanitizeExposureTable();
+ }
+
+ public async void ExposureTableClearEntries()
+ {
+ if (Operation.ExposureTable.Count <= 0) return;
+ if (await ParentWindow.MessageBoxQuestion(
$"Are you sure you want to all the {Operation.ExposureTable.Count} entries?"
) != ButtonResult.Yes) return;
- Operation.ExposureTable.Clear();
- }
+ Operation.ExposureTable.Clear();
+ }
- public async void ExposureTableRemoveSelectedEntries()
- {
- if (_exposureTable.SelectedItems.Count <= 0) return;
- if (await ParentWindow.MessageBoxQuestion(
+ public async void ExposureTableRemoveSelectedEntries()
+ {
+ if (_exposureTable.SelectedItems.Count <= 0) return;
+ if (await ParentWindow.MessageBoxQuestion(
$"Are you sure you want to remove the {_exposureTable.SelectedItems.Count} selected entries?"
) != ButtonResult.Yes) return;
- Operation.ExposureTable.RemoveRange(_exposureTable.SelectedItems.Cast<ExposureItem>());
- }
+ Operation.ExposureTable.RemoveRange(_exposureTable.SelectedItems.Cast<ExposureItem>());
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs
index 2ec4c5b..3a0e68c 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml.cs
@@ -3,27 +3,26 @@ using UVtools.Core.Operations;
using UVtools.Core.SystemOS;
using UVtools.WPF.Controls.Tools;
-namespace UVtools.WPF.Controls.Calibrators
+namespace UVtools.WPF.Controls.Calibrators;
+
+public class CalibrateExternalTestsControl : ToolControl
{
- public class CalibrateExternalTestsControl : ToolControl
+ public OperationCalibrateExternalTests Operation => BaseOperation as OperationCalibrateExternalTests;
+ public CalibrateExternalTestsControl()
{
- public OperationCalibrateExternalTests Operation => BaseOperation as OperationCalibrateExternalTests;
- public CalibrateExternalTestsControl()
- {
- BaseOperation = new OperationCalibrateExternalTests(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
+ BaseOperation = new OperationCalibrateExternalTests(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- }
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void ButtonClicked(string url)
- {
- SystemAware.OpenBrowser(url);
- }
+ public void ButtonClicked(string url)
+ {
+ SystemAware.OpenBrowser(url);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs
index 5a0987f..998013e 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs
@@ -7,73 +7,72 @@ using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Calibrators
+namespace UVtools.WPF.Controls.Calibrators;
+
+public class CalibrateGrayscaleControl : ToolControl
{
- public class CalibrateGrayscaleControl : ToolControl
- {
- public OperationCalibrateGrayscale Operation => BaseOperation as OperationCalibrateGrayscale;
+ public OperationCalibrateGrayscale Operation => BaseOperation as OperationCalibrateGrayscale;
- private Bitmap _previewImage;
- public Bitmap PreviewImage
- {
- get => _previewImage;
- set => RaiseAndSetIfChanged(ref _previewImage, value);
- }
+ private Bitmap _previewImage;
+ public Bitmap PreviewImage
+ {
+ get => _previewImage;
+ set => RaiseAndSetIfChanged(ref _previewImage, value);
+ }
- private readonly Timer _timer;
+ private readonly Timer _timer;
- public CalibrateGrayscaleControl()
- {
- BaseOperation = new OperationCalibrateGrayscale(SlicerFile);
- if (!ValidateSpawn()) return;
+ public CalibrateGrayscaleControl()
+ {
+ BaseOperation = new OperationCalibrateGrayscale(SlicerFile);
+ if (!ValidateSpawn()) return;
- InitializeComponent();
+ InitializeComponent();
- _timer = new Timer(20)
- {
- AutoReset = false
- };
- _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
- }
-
- private void InitializeComponent()
+ _timer = new Timer(20)
{
- AvaloniaXamlLoader.Load(this);
- }
+ AutoReset = false
+ };
+ _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (App.SlicerFile is null) return;
+ switch (callback)
{
- if (App.SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, e) =>
- {
- _timer.Stop();
- _timer.Start();
- if (e.PropertyName == nameof(Operation.Divisions))
- {
- ParentWindow.ButtonOkEnabled = Operation.Divisions > 0;
- return;
- }
- };
- if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.Divisions > 0;
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, e) =>
+ {
_timer.Stop();
_timer.Start();
- break;
- }
+ if (e.PropertyName == nameof(Operation.Divisions))
+ {
+ ParentWindow.ButtonOkEnabled = Operation.Divisions > 0;
+ return;
+ }
+ };
+ if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.Divisions > 0;
+ _timer.Stop();
+ _timer.Start();
+ break;
}
- public void UpdatePreview()
+ }
+ public void UpdatePreview()
+ {
+ var layers = Operation.GetLayers();
+ _previewImage?.Dispose();
+ PreviewImage = layers[2].ToBitmap();
+ foreach (var layer in layers)
{
- var layers = Operation.GetLayers();
- _previewImage?.Dispose();
- PreviewImage = layers[2].ToBitmap();
- foreach (var layer in layers)
- {
- layer.Dispose();
- }
+ layer.Dispose();
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs
index 2e0a78d..ade93a4 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs
@@ -8,68 +8,67 @@ using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Calibrators
-{
- public class CalibrateLiftHeightControl : ToolControl
- {
- public OperationCalibrateLiftHeight Operation => BaseOperation as OperationCalibrateLiftHeight;
+namespace UVtools.WPF.Controls.Calibrators;
- private readonly Timer _timer;
+public class CalibrateLiftHeightControl : ToolControl
+{
+ public OperationCalibrateLiftHeight Operation => BaseOperation as OperationCalibrateLiftHeight;
- private Bitmap _previewImage;
- public Bitmap PreviewImage
- {
- get => _previewImage;
- set => RaiseAndSetIfChanged(ref _previewImage, value);
- }
+ private readonly Timer _timer;
- public CalibrateLiftHeightControl()
- {
- BaseOperation = new OperationCalibrateLiftHeight(SlicerFile);
- if (!ValidateSpawn()) return;
+ private Bitmap _previewImage;
+ public Bitmap PreviewImage
+ {
+ get => _previewImage;
+ set => RaiseAndSetIfChanged(ref _previewImage, value);
+ }
- InitializeComponent();
+ public CalibrateLiftHeightControl()
+ {
+ BaseOperation = new OperationCalibrateLiftHeight(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- _timer = new Timer(20)
- {
- AutoReset = false
- };
- _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
- }
- private void InitializeComponent()
+ _timer = new Timer(20)
{
- AvaloniaXamlLoader.Load(this);
- }
+ AutoReset = false
+ };
+ _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (App.SlicerFile is null) return;
+ switch (callback)
{
- if (App.SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, e) =>
- {
- _timer.Stop();
- _timer.Start();
- };
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, e) =>
+ {
_timer.Stop();
_timer.Start();
- break;
- }
+ };
+ _timer.Stop();
+ _timer.Start();
+ break;
}
+ }
- public void UpdatePreview()
+ public void UpdatePreview()
+ {
+ var layers = Operation.GetLayers();
+ _previewImage?.Dispose();
+ PreviewImage = layers[0].ToBitmap();
+ foreach (var layer in layers)
{
- var layers = Operation.GetLayers();
- _previewImage?.Dispose();
- PreviewImage = layers[0].ToBitmap();
- foreach (var layer in layers)
- {
- layer.Dispose();
- }
+ layer.Dispose();
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs
index 77bcc06..22eb53d 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateStressTowerControl.axaml.cs
@@ -2,33 +2,32 @@
using UVtools.Core.Operations;
using UVtools.WPF.Controls.Tools;
-namespace UVtools.WPF.Controls.Calibrators
+namespace UVtools.WPF.Controls.Calibrators;
+
+public class CalibrateStressTowerControl : ToolControl
{
- public class CalibrateStressTowerControl : ToolControl
+ public OperationCalibrateStressTower Operation => BaseOperation as OperationCalibrateStressTower;
+
+ public CalibrateStressTowerControl()
{
- public OperationCalibrateStressTower Operation => BaseOperation as OperationCalibrateStressTower;
+ BaseOperation = new OperationCalibrateStressTower(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- public CalibrateStressTowerControl()
- {
- BaseOperation = new OperationCalibrateStressTower(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- private void InitializeComponent()
+ /*public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (App.SlicerFile is null) return;
+ switch (callback)
{
- AvaloniaXamlLoader.Load(this);
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ break;
}
-
- /*public override void Callback(ToolWindow.Callbacks callback)
- {
- if (App.SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- break;
- }
- }*/
- }
-}
+ }*/
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs
index b37ce70..9602518 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs
@@ -7,115 +7,114 @@ using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Calibrators
+namespace UVtools.WPF.Controls.Calibrators;
+
+public class CalibrateToleranceControl : ToolControl
{
- public class CalibrateToleranceControl : ToolControl
- {
- public OperationCalibrateTolerance Operation => BaseOperation as OperationCalibrateTolerance;
+ public OperationCalibrateTolerance Operation => BaseOperation as OperationCalibrateTolerance;
- private Timer _timer;
+ private Timer _timer;
- /*public string ProfileName
- {
- get => _profileName;
- set => RaiseAndSetIfChanged(ref _profileName, value);
- }*/
+ /*public string ProfileName
+ {
+ get => _profileName;
+ set => RaiseAndSetIfChanged(ref _profileName, value);
+ }*/
- //public bool IsProfileAddEnabled => Operation.ScaleXFactor != 100 || Operation.ScaleYFactor != 100;
+ //public bool IsProfileAddEnabled => Operation.ScaleXFactor != 100 || Operation.ScaleYFactor != 100;
- private Bitmap _previewImage;
- /*private string _profileName;
- private bool _isProfileNameEnabled;*/
+ private Bitmap _previewImage;
+ /*private string _profileName;
+ private bool _isProfileNameEnabled;*/
- public Bitmap PreviewImage
- {
- get => _previewImage;
- set => RaiseAndSetIfChanged(ref _previewImage, value);
- }
+ public Bitmap PreviewImage
+ {
+ get => _previewImage;
+ set => RaiseAndSetIfChanged(ref _previewImage, value);
+ }
- public bool IsDisplaySizeVisible => App.SlicerFile.DisplayWidth <= 0 && App.SlicerFile.DisplayHeight <= 0;
+ public bool IsDisplaySizeVisible => App.SlicerFile.DisplayWidth <= 0 && App.SlicerFile.DisplayHeight <= 0;
- public CalibrateToleranceControl()
- {
- BaseOperation = new OperationCalibrateTolerance(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
+ public CalibrateToleranceControl()
+ {
+ BaseOperation = new OperationCalibrateTolerance(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- _timer = new Timer(100)
- {
- AutoReset = false
- };
- _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
- }
-
- private void InitializeComponent()
+ _timer = new Timer(100)
{
- AvaloniaXamlLoader.Load(this);
- }
+ AutoReset = false
+ };
+ _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (App.SlicerFile is null) return;
+ switch (callback)
{
- if (App.SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, e) =>
- {
- _timer.Stop();
- _timer.Start();
- /*if (e.PropertyName == nameof(Operation.ScaleXFactor) || e.PropertyName == nameof(Operation.ScaleYFactor))
- {
- RaisePropertyChanged(nameof(IsProfileAddEnabled));
- return;
- }*/
- };
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, e) =>
+ {
_timer.Stop();
_timer.Start();
- break;
- }
+ /*if (e.PropertyName == nameof(Operation.ScaleXFactor) || e.PropertyName == nameof(Operation.ScaleYFactor))
+ {
+ RaisePropertyChanged(nameof(IsProfileAddEnabled));
+ return;
+ }*/
+ };
+ _timer.Stop();
+ _timer.Start();
+ break;
}
+ }
- public void UpdatePreview()
+ public void UpdatePreview()
+ {
+ var layers = Operation.GetLayers();
+ _previewImage?.Dispose();
+ PreviewImage = layers[^1].ToBitmap();
+ foreach (var layer in layers)
{
- var layers = Operation.GetLayers();
- _previewImage?.Dispose();
- PreviewImage = layers[^1].ToBitmap();
- foreach (var layer in layers)
- {
- layer.Dispose();
- }
+ layer.Dispose();
}
+ }
- /*public async void AddProfile()
+ /*public async void AddProfile()
+ {
+ OperationResize resize = new OperationResize
{
- OperationResize resize = new OperationResize
- {
- ProfileName = ProfileName,
- X = Operation.ScaleXFactor,
- Y = Operation.ScaleYFactor
- };
- var find = OperationProfiles.FindByName(resize, ProfileName);
- if (find is not null)
- {
- if (await ParentWindow.MessageBoxQuestion(
- $"A profile with same name and/or values already exists, do you want to overwrite:\n{find}\nwith:\n{resize}\n?") != ButtonResult.Yes) return;
-
- OperationProfiles.RemoveProfile(resize, false);
- }
-
- OperationProfiles.AddProfile(resize);
- await ParentWindow.MessageBoxInfo($"The resize profile has been added.\nGo to Tools - Resize and select the saved profile to load it in.\n{resize}");
- }
-
- public void AutoNameProfile()
+ ProfileName = ProfileName,
+ X = Operation.ScaleXFactor,
+ Y = Operation.ScaleYFactor
+ };
+ var find = OperationProfiles.FindByName(resize, ProfileName);
+ if (find is not null)
{
- var printerName = string.IsNullOrEmpty(App.SlicerFile.MachineName)
- ? "MyPrinterX"
- : App.SlicerFile.MachineName;
- ProfileName = $"{printerName}, MyResinX, {Operation.Microns}µm, {Operation.BottomExposure}s/{Operation.NormalExposure}s";
+ if (await ParentWindow.MessageBoxQuestion(
+ $"A profile with same name and/or values already exists, do you want to overwrite:\n{find}\nwith:\n{resize}\n?") != ButtonResult.Yes) return;
+
+ OperationProfiles.RemoveProfile(resize, false);
}
- */
+
+ OperationProfiles.AddProfile(resize);
+ await ParentWindow.MessageBoxInfo($"The resize profile has been added.\nGo to Tools - Resize and select the saved profile to load it in.\n{resize}");
+ }
+
+ public void AutoNameProfile()
+ {
+ var printerName = string.IsNullOrEmpty(App.SlicerFile.MachineName)
+ ? "MyPrinterX"
+ : App.SlicerFile.MachineName;
+ ProfileName = $"{printerName}, MyResinX, {Operation.Microns}µm, {Operation.BottomExposure}s/{Operation.NormalExposure}s";
}
-}
+ */
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml
index 9e91a2a..0c8061e 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="UVtools.WPF.Controls.Calibrators.CalibrateXYZAccuracyControl">
<Grid ColumnDefinitions="Auto,10,380">
@@ -434,9 +435,8 @@
Content="Auto name"/>
<Button Grid.Column="6" VerticalAlignment="Center"
IsEnabled="{Binding IsProfileAddEnabled}"
- Command="{Binding AddProfile}">
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- </Button>
+ Command="{Binding AddProfile}"
+ i:Attached.Icon="fas fa-plus"/>
</Grid>
</Expander>
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs
index 2d6a7f1..fa31de9 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs
@@ -9,111 +9,110 @@ using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Calibrators
+namespace UVtools.WPF.Controls.Calibrators;
+
+public class CalibrateXYZAccuracyControl : ToolControl
{
- public class CalibrateXYZAccuracyControl : ToolControl
- {
- public OperationCalibrateXYZAccuracy Operation => BaseOperation as OperationCalibrateXYZAccuracy;
+ public OperationCalibrateXYZAccuracy Operation => BaseOperation as OperationCalibrateXYZAccuracy;
- private readonly Timer _timer;
+ private readonly Timer _timer;
- public string ProfileName
- {
- get => _profileName;
- set => RaiseAndSetIfChanged(ref _profileName, value);
- }
+ public string ProfileName
+ {
+ get => _profileName;
+ set => RaiseAndSetIfChanged(ref _profileName, value);
+ }
- public bool IsProfileAddEnabled => Operation.ScaleXFactor != 100 || Operation.ScaleYFactor != 100;
+ public bool IsProfileAddEnabled => Operation.ScaleXFactor != 100 || Operation.ScaleYFactor != 100;
- private Bitmap _previewImage;
- private string _profileName;
+ private Bitmap _previewImage;
+ private string _profileName;
- public Bitmap PreviewImage
- {
- get => _previewImage;
- set => RaiseAndSetIfChanged(ref _previewImage, value);
- }
+ public Bitmap PreviewImage
+ {
+ get => _previewImage;
+ set => RaiseAndSetIfChanged(ref _previewImage, value);
+ }
- public CalibrateXYZAccuracyControl()
- {
- BaseOperation = new OperationCalibrateXYZAccuracy(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
+ public CalibrateXYZAccuracyControl()
+ {
+ BaseOperation = new OperationCalibrateXYZAccuracy(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- _timer = new Timer(20)
- {
- AutoReset = false
- };
- _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
- }
-
- private void InitializeComponent()
+ _timer = new Timer(20)
{
- AvaloniaXamlLoader.Load(this);
- }
+ AutoReset = false
+ };
+ _timer.Elapsed += (sender, e) => Dispatcher.UIThread.InvokeAsync(UpdatePreview);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (App.SlicerFile is null) return;
+ switch (callback)
{
- if (App.SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, e) =>
- {
- _timer.Stop();
- _timer.Start();
- if (e.PropertyName is nameof(Operation.ScaleXFactor) or nameof(Operation.ScaleYFactor))
- {
- RaisePropertyChanged(nameof(IsProfileAddEnabled));
- return;
- }
- };
- //ParentWindow.ButtonOkEnabled = Operation.ObjectCount > 0;
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, e) =>
+ {
_timer.Stop();
_timer.Start();
- break;
- }
+ if (e.PropertyName is nameof(Operation.ScaleXFactor) or nameof(Operation.ScaleYFactor))
+ {
+ RaisePropertyChanged(nameof(IsProfileAddEnabled));
+ return;
+ }
+ };
+ //ParentWindow.ButtonOkEnabled = Operation.ObjectCount > 0;
+ _timer.Stop();
+ _timer.Start();
+ break;
}
+ }
- public void UpdatePreview()
+ public void UpdatePreview()
+ {
+ var layers = Operation.GetLayers();
+ _previewImage?.Dispose();
+ PreviewImage = layers[1].ToBitmap();
+ foreach (var layer in layers)
{
- var layers = Operation.GetLayers();
- _previewImage?.Dispose();
- PreviewImage = layers[1].ToBitmap();
- foreach (var layer in layers)
- {
- layer.Dispose();
- }
+ layer.Dispose();
}
+ }
- public async void AddProfile()
+ public async void AddProfile()
+ {
+ OperationResize resize = new()
+ {
+ ProfileName = ProfileName,
+ X = Operation.ScaleXFactor,
+ Y = Operation.ScaleYFactor
+ };
+ var find = OperationProfiles.FindByName(resize, ProfileName);
+ if (find is not null)
{
- OperationResize resize = new()
- {
- ProfileName = ProfileName,
- X = Operation.ScaleXFactor,
- Y = Operation.ScaleYFactor
- };
- var find = OperationProfiles.FindByName(resize, ProfileName);
- if (find is not null)
- {
- if (await ParentWindow.MessageBoxQuestion(
+ if (await ParentWindow.MessageBoxQuestion(
$"A profile with same name and/or values already exists, do you want to overwrite:\n{find}\nwith:\n{resize}\n?") != ButtonResult.Yes) return;
- OperationProfiles.RemoveProfile(resize, false);
- }
-
- OperationProfiles.AddProfile(resize);
- await ParentWindow.MessageBoxInfo($"The resize profile has been added.\nGo to Tools - Resize and select the saved profile to load it in.\n{resize}");
+ OperationProfiles.RemoveProfile(resize, false);
}
- public void AutoNameProfile()
- {
- var printerName = string.IsNullOrEmpty(App.SlicerFile.MachineName)
- ? "MyPrinterX"
- : App.SlicerFile.MachineName;
- ProfileName = $"{printerName}, MyResinX, {Operation.Microns}µm, {Operation.BottomExposure}s/{Operation.NormalExposure}s";
- }
+ OperationProfiles.AddProfile(resize);
+ await ParentWindow.MessageBoxInfo($"The resize profile has been added.\nGo to Tools - Resize and select the saved profile to load it in.\n{resize}");
+ }
+
+ public void AutoNameProfile()
+ {
+ var printerName = string.IsNullOrEmpty(App.SlicerFile.MachineName)
+ ? "MyPrinterX"
+ : App.SlicerFile.MachineName;
+ ProfileName = $"{printerName}, MyResinX, {Operation.Microns}µm, {Operation.BottomExposure}s/{Operation.NormalExposure}s";
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/DummyControl.axaml.cs b/UVtools.WPF/Controls/DummyControl.axaml.cs
index a5c2cb7..16c8e23 100644
--- a/UVtools.WPF/Controls/DummyControl.axaml.cs
+++ b/UVtools.WPF/Controls/DummyControl.axaml.cs
@@ -2,18 +2,17 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
-namespace UVtools.WPF.Controls
+namespace UVtools.WPF.Controls;
+
+public class DummyControl : UserControl
{
- public class DummyControl : UserControl
+ public DummyControl()
{
- public DummyControl()
- {
- this.InitializeComponent();
- }
+ this.InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Helpers.cs b/UVtools.WPF/Controls/Helpers.cs
index bcf5973..10897f1 100644
--- a/UVtools.WPF/Controls/Helpers.cs
+++ b/UVtools.WPF/Controls/Helpers.cs
@@ -9,142 +9,141 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
-namespace UVtools.WPF.Controls
+namespace UVtools.WPF.Controls;
+
+public static class Helpers
{
- public static class Helpers
+ public static readonly List<FileDialogFilter> ImagesFileFilter = new()
{
- public static readonly List<FileDialogFilter> ImagesFileFilter = new()
+ new()
{
- new()
+ Name = "Image Files",
+ Extensions = new List<string>
{
- Name = "Image Files",
- Extensions = new List<string>
- {
- "png",
- "bmp",
- "jpeg",
- "jpg",
- "jp2",
- "tif",
- "tiff",
- "sr",
- "ras",
- }
- },
- };
+ "png",
+ "bmp",
+ "jpeg",
+ "jpg",
+ "jp2",
+ "tif",
+ "tiff",
+ "sr",
+ "ras",
+ }
+ },
+ };
- public static readonly List<FileDialogFilter> ImagesFullFileFilter = new()
+ public static readonly List<FileDialogFilter> ImagesFullFileFilter = new()
+ {
+ new()
{
- new()
- {
- Name = "PNG Files",
- Extensions = new List<string>
- {
- "png"
- }
- },
- new()
+ Name = "PNG Files",
+ Extensions = new List<string>
{
- Name = "JPG Files",
- Extensions = new List<string>
- {
- "jpg",
- "jpeg"
- }
- },
- new()
- {
- Name = "BMP Files",
- Extensions = new List<string>
- {
- "bmp",
- }
- },
- new()
+ "png"
+ }
+ },
+ new()
+ {
+ Name = "JPG Files",
+ Extensions = new List<string>
{
- Name = "TIF Files",
- Extensions = new List<string>
- {
- "tif",
- "tiff",
- }
- },
- };
-
- public static readonly List<FileDialogFilter> PngFileFilter = new()
+ "jpg",
+ "jpeg"
+ }
+ },
+ new()
{
- new()
+ Name = "BMP Files",
+ Extensions = new List<string>
{
- Name = "Image Files",
- Extensions = new List<string>
- {
- "png",
- }
+ "bmp",
}
- };
-
- public static readonly List<FileDialogFilter> TxtFileFilter = new()
+ },
+ new()
{
- new()
+ Name = "TIF Files",
+ Extensions = new List<string>
{
- Name = "Text Files",
- Extensions = new List<string>
- {
- "txt",
- }
+ "tif",
+ "tiff",
}
- };
+ },
+ };
- public static readonly List<FileDialogFilter> IniFileFilter = new()
+ public static readonly List<FileDialogFilter> PngFileFilter = new()
+ {
+ new()
{
- new()
+ Name = "Image Files",
+ Extensions = new List<string>
{
- Name = "Ini Files",
- Extensions = new List<string>
- {
- "ini",
- }
+ "png",
}
- };
+ }
+ };
- public static readonly List<FileDialogFilter> OperationSettingFileFilter = new()
+ public static readonly List<FileDialogFilter> TxtFileFilter = new()
+ {
+ new()
{
- new()
+ Name = "Text Files",
+ Extensions = new List<string>
{
- Name = "UVtools operation settings",
- Extensions = new List<string>
- {
- "uvtop",
- }
+ "txt",
}
- };
+ }
+ };
- public static readonly List<FileDialogFilter> ScriptsFileFilter = new()
+ public static readonly List<FileDialogFilter> IniFileFilter = new()
+ {
+ new()
{
- new()
+ Name = "Ini Files",
+ Extensions = new List<string>
{
- Name = "Script Files",
- Extensions = new List<string>
- {
- "csx",
- "cs",
- }
+ "ini",
}
- };
+ }
+ };
- public static List<FileDialogFilter> ToAvaloniaFileFilter(List<KeyValuePair<string, List<string>>> data)
+ public static readonly List<FileDialogFilter> OperationSettingFileFilter = new()
+ {
+ new()
{
- var result = new List<FileDialogFilter>(data.Capacity);
- result.AddRange(data.Select(kv => new FileDialogFilter {Name = kv.Key, Extensions = kv.Value}));
- return result;
+ Name = "UVtools operation settings",
+ Extensions = new List<string>
+ {
+ "uvtop",
+ }
}
+ };
- public static List<FileDialogFilter> ToAvaloniaFilter(string name, string extension)
+ public static readonly List<FileDialogFilter> ScriptsFileFilter = new()
+ {
+ new()
{
- return new(1)
+ Name = "Script Files",
+ Extensions = new List<string>
{
- new() {Name = name, Extensions = new List<string>(1) {extension}}
- };
+ "csx",
+ "cs",
+ }
}
+ };
+
+ public static List<FileDialogFilter> ToAvaloniaFileFilter(List<KeyValuePair<string, List<string>>> data)
+ {
+ var result = new List<FileDialogFilter>(data.Capacity);
+ result.AddRange(data.Select(kv => new FileDialogFilter {Name = kv.Key, Extensions = kv.Value}));
+ return result;
+ }
+
+ public static List<FileDialogFilter> ToAvaloniaFilter(string name, string extension)
+ {
+ return new(1)
+ {
+ new() {Name = name, Extensions = new List<string>(1) {extension}}
+ };
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/KernelControl.axaml.cs b/UVtools.WPF/Controls/KernelControl.axaml.cs
index 87cb211..ab52652 100644
--- a/UVtools.WPF/Controls/KernelControl.axaml.cs
+++ b/UVtools.WPF/Controls/KernelControl.axaml.cs
@@ -3,52 +3,51 @@ using Avalonia.Markup.Xaml;
using UVtools.Core.Objects;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF.Controls
+namespace UVtools.WPF.Controls;
+
+public class KernelControl : UserControlEx
{
- public class KernelControl : UserControlEx
- {
- private KernelConfiguration _kernel;
+ private KernelConfiguration _kernel;
- public static readonly DirectProperty<KernelControl, KernelConfiguration> KernelProperty =
- AvaloniaProperty.RegisterDirect<KernelControl, KernelConfiguration>(
- nameof(Kernel),
- o => o.Kernel,
- (o, v) => o.Kernel = v);
+ public static readonly DirectProperty<KernelControl, KernelConfiguration> KernelProperty =
+ AvaloniaProperty.RegisterDirect<KernelControl, KernelConfiguration>(
+ nameof(Kernel),
+ o => o.Kernel,
+ (o, v) => o.Kernel = v);
- public KernelConfiguration Kernel
- {
- get => _kernel;
- set => SetAndRaise(KernelProperty, ref _kernel, value);
- }
+ public KernelConfiguration Kernel
+ {
+ get => _kernel;
+ set => SetAndRaise(KernelProperty, ref _kernel, value);
+ }
- public KernelControl()
- {
- InitializeComponent();
- DataContext = this;
- }
+ public KernelControl()
+ {
+ InitializeComponent();
+ DataContext = this;
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void GenerateKernel()
+ public void GenerateKernel()
+ {
+ if (Kernel.MatrixWidth <= Kernel.AnchorX || Kernel.MatrixHeight <= Kernel.AnchorY)
{
- if (Kernel.MatrixWidth <= Kernel.AnchorX || Kernel.MatrixHeight <= Kernel.AnchorY)
- {
- App.MainWindow.MessageBoxError("Anchor position X/Y can't be higher or equal than size X/Y\nPlease fix the values.", "Invalid anchor position").ConfigureAwait(false);
- return;
- }
-
- Kernel.GenerateKernelText();
+ App.MainWindow.MessageBoxError("Anchor position X/Y can't be higher or equal than size X/Y\nPlease fix the values.", "Invalid anchor position").ConfigureAwait(false);
+ return;
}
- /*public Kernel GetKernel()
- {
- var matrix = GetMatrix();
- return matrix is null ? null : new Kernel(matrix, Anchor, UseDynamicKernel);
- }*/
+ Kernel.GenerateKernelText();
}
-}
+
+ /*public Kernel GetKernel()
+ {
+ var matrix = GetMatrix();
+ return matrix is null ? null : new Kernel(matrix, Anchor, UseDynamicKernel);
+ }*/
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/StaticControls.cs b/UVtools.WPF/Controls/StaticControls.cs
index 488891c..ea6e470 100644
--- a/UVtools.WPF/Controls/StaticControls.cs
+++ b/UVtools.WPF/Controls/StaticControls.cs
@@ -1,11 +1,10 @@
using Avalonia.Input;
-namespace UVtools.WPF.Controls
+namespace UVtools.WPF.Controls;
+
+public static class StaticControls
{
- public static class StaticControls
- {
- public static Cursor ArrowCursor = new(StandardCursorType.Arrow);
- public static Cursor CrossCursor = new(StandardCursorType.Cross);
- public static Cursor HandCursor = new(StandardCursorType.Hand);
- }
-}
+ public static Cursor ArrowCursor = new(StandardCursorType.Arrow);
+ public static Cursor CrossCursor = new(StandardCursorType.Cross);
+ public static Cursor HandCursor = new(StandardCursorType.Hand);
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml b/UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml
new file mode 100644
index 0000000..4047870
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml
@@ -0,0 +1,91 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="200"
+ x:Class="UVtools.WPF.Controls.Suggestions.SuggestionBottomLayerCountControl">
+ <Grid RowDefinitions="Auto,10,Auto,2,Auto,10,Auto"
+ ColumnDefinitions="Auto,10,180,5,Auto,5,180">
+
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Desired height in millimeters of the bottom layers.
+&#x0a;Since files can have different layer heights, and to be universal value,
+this is set in millimeters and then converted to layer numbers once applied"
+ Text="Bottom height:"/>
+
+ <NumericUpDown Grid.Row="0" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F3"
+ Increment="0.01"
+ Value="{Binding Suggestion.TargetBottomHeight}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Minimum:"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Maximum:"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits in millimeters for the trigger detection (Min-Max)"
+ Text="Limits (mm):"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F3"
+ Increment="0.01"
+ Value="{Binding Suggestion.MinimumBottomHeight}"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F3"
+ Increment="0.01"
+ Value="{Binding Suggestion.MaximumBottomHeight}"/>
+
+ <TextBlock Grid.Row="6" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits in layer count for the trigger detection and
+constrains the set value of the applied value to this limit (Min-Max)"
+ Text="Limits (layers):"/>
+
+ <NumericUpDown Grid.Row="6" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_layers"
+ Minimum="0"
+ Maximum="1000"
+ Increment="1"
+ Value="{Binding Suggestion.MinimumBottomLayerCount}"/>
+
+ <TextBlock Grid.Row="6" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="6" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_layers"
+ Minimum="0"
+ Maximum="1000"
+ Increment="1"
+ Value="{Binding Suggestion.MaximumBottomLayerCount}"/>
+ </Grid>
+</UserControl>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml.cs b/UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml.cs
new file mode 100644
index 0000000..e2f3655
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionBottomLayerCountControl.axaml.cs
@@ -0,0 +1,22 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Controls.Suggestions;
+
+public partial class SuggestionBottomLayerCountControl : SuggestionControl
+{
+ public SuggestionBottomLayerCountControl() : this(new SuggestionBottomLayerCount())
+ { }
+
+ public SuggestionBottomLayerCountControl(Suggestion suggestion)
+ {
+ Suggestion = suggestion;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml b/UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml
new file mode 100644
index 0000000..1ac852d
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml
@@ -0,0 +1,8 @@
+<controls:UserControlEx xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:controls="clr-namespace:UVtools.WPF.Controls"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="UVtools.WPF.Controls.Suggestions.SuggestionControl">
+</controls:UserControlEx>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml.cs b/UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml.cs
new file mode 100644
index 0000000..f8e95c9
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionControl.axaml.cs
@@ -0,0 +1,23 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Controls.Suggestions;
+
+public partial class SuggestionControl : UserControlEx
+{
+ public Suggestion Suggestion { get; set; }
+
+ public SuggestionControl() : this(null) { }
+
+ public SuggestionControl(Suggestion suggestion)
+ {
+ Suggestion = suggestion;
+ InitializeComponent();
+ DataContext = this;
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml b/UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml
new file mode 100644
index 0000000..56feb69
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml
@@ -0,0 +1,61 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="150"
+ x:Class="UVtools.WPF.Controls.Suggestions.SuggestionLayerHeightControl">
+ <Grid RowDefinitions="Auto,10,Auto,2,Auto"
+ ColumnDefinitions="Auto,10,180,5,Auto,5,180">
+
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Layer heights with more decimal plates than this defined value will raise an recommendation"
+ Text="Max decimal plates:"/>
+
+ <NumericUpDown Grid.Row="0" Grid.Column="2"
+ VerticalAlignment="Center"
+ Minimum="2"
+ Maximum="10"
+ Increment="1"
+ Value="{Binding Suggestion.MaximumLayerHeightDecimalPlates}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Minimum:"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Maximum:"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits in millimeters for the trigger detection (Min-Max)"
+ Text="Layer height limits:"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F3"
+ Increment="0.01"
+ Value="{Binding Suggestion.MinimumLayerHeight}"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F3"
+ Increment="0.01"
+ Value="{Binding Suggestion.MaximumLayerHeight}"/>
+ </Grid>
+</UserControl>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml.cs b/UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml.cs
new file mode 100644
index 0000000..f1e8fa1
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionLayerHeightControl.axaml.cs
@@ -0,0 +1,22 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Controls.Suggestions;
+
+public partial class SuggestionLayerHeightControl : SuggestionControl
+{
+ public SuggestionLayerHeightControl() : this(new SuggestionLayerHeight())
+ { }
+
+ public SuggestionLayerHeightControl(Suggestion suggestion)
+ {
+ Suggestion = suggestion;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml
new file mode 100644
index 0000000..56fa630
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml
@@ -0,0 +1,157 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="300"
+ x:Class="UVtools.WPF.Controls.Suggestions.SuggestionWaitTimeAfterCureControl">
+ <Grid RowDefinitions="Auto,10,Auto,2,Auto,10,Auto,2,Auto,10,Auto"
+ ColumnDefinitions="Auto,10,190,5,Auto,5,190">
+
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Set the way the wait time is calculated / set"
+ Text="Set type:"/>
+
+ <ComboBox Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="5"
+ HorizontalAlignment="Stretch"
+ Items="{Binding Suggestion.SetType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Suggestion.SetType, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Bottom:"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Normal:"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the wait time for (Bottom-Normal) layers"
+ IsVisible="{Binding Suggestion.IsSetTypeFixed}"
+ Text="Wait time:"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the wait time according an (wait time - exposure time) ratio"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalExposure}"
+ Text="Time - Exposure:"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ IsVisible="{Binding Suggestion.IsSetTypeFixed}"
+ Value="{Binding Suggestion.FixedBottomWaitTimeAfterCure}"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ IsVisible="{Binding Suggestion.IsSetTypeFixed}"
+ Value="{Binding Suggestion.FixedWaitTimeAfterCure}"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalExposure}"
+ Value="{Binding Suggestion.ProportionalWaitTimeAfterCure}"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalExposure}"
+ Value="{Binding Suggestion.ProportionalExposureTime}"/>
+
+
+ <TextBlock Grid.Row="6" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Minimum:"/>
+
+ <TextBlock Grid.Row="6" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Maximum:"/>
+
+ <TextBlock Grid.Row="8" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits for the bottom layers (Min-Max)"
+ Text="Limits (bottom):"/>
+
+ <NumericUpDown Grid.Row="8" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="{Binding SlicerFile.LayerHeight}"
+ Value="{Binding Suggestion.MinimumBottomWaitTimeAfterCure}"/>
+
+ <TextBlock Grid.Row="8" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="8" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.MaximumBottomWaitTimeAfterCure}"/>
+
+ <TextBlock Grid.Row="10" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits for the normal layers (Min-Max)"
+ Text="Limits (normal):"/>
+
+ <NumericUpDown Grid.Row="10" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.MinimumWaitTimeAfterCure}"/>
+
+ <TextBlock Grid.Row="10" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="10" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.MaximumWaitTimeAfterCure}"/>
+
+
+ </Grid>
+</UserControl>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml.cs b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml.cs
new file mode 100644
index 0000000..87cb498
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeAfterCureControl.axaml.cs
@@ -0,0 +1,22 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Controls.Suggestions;
+
+public partial class SuggestionWaitTimeAfterCureControl : SuggestionControl
+{
+ public SuggestionWaitTimeAfterCureControl() : this(new SuggestionWaitTimeAfterCure())
+ { }
+
+ public SuggestionWaitTimeAfterCureControl(Suggestion suggestion)
+ {
+ Suggestion = suggestion;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml
new file mode 100644
index 0000000..53f4d9f
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml
@@ -0,0 +1,232 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="UVtools.WPF.Controls.Suggestions.SuggestionWaitTimeBeforeCureControl">
+ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,2,Auto,10,Auto,10,Auto,10,Auto,2,Auto,10,Auto"
+ ColumnDefinitions="Auto,10,190,5,Auto,5,190">
+
+ <CheckBox Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="5"
+ VerticalAlignment="Center"
+ IsChecked="{Binding Suggestion.CreateEmptyFirstLayer}"
+ ToolTip.Tip="Some printers will not respect wait time for the first layer, introducing the problem once again. Use this option to by pass that"
+ Content="Create a first empty layer to bypass printer limitation"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Set the way the wait time is calculated / set"
+ Text="Set type:"/>
+
+ <ComboBox Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="5"
+ HorizontalAlignment="Stretch"
+ Items="{Binding Suggestion.SetType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Suggestion.SetType, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Set the height which will be considered as a bottom layer even if layer is already an normal layer.
+&#x0a;Set to 0 to disable this setting and use global bottom layer count as bottom layer determination."
+ Text="Bottom height:"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.10"
+ Value="{Binding Suggestion.BottomHeight}"/>
+
+
+ <TextBlock Grid.Row="6" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Bottom:"/>
+
+ <TextBlock Grid.Row="6" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Normal:"/>
+
+ <TextBlock Grid.Row="8" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the wait time for (Bottom-Normal) layers"
+ IsVisible="{Binding Suggestion.IsSetTypeFixed}"
+ Text="Wait time:"/>
+
+ <TextBlock Grid.Row="8" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the wait time for (Bottom-Normal) layers.
+&#x0a;Fallback values are used to set the global table properties when using proportional values."
+ IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
+ Text="Wait time (fallback):"/>
+
+ <NumericUpDown Grid.Row="8" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.FixedBottomWaitTimeBeforeCure}"/>
+
+ <TextBlock Grid.Row="8" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="8" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.FixedWaitTimeBeforeCure}"/>
+
+ <TextBlock Grid.Row="10" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the wait time according an (wait time - exposure time) ratio"
+ IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
+ Text="Proportional wait time:"/>
+
+ <NumericUpDown Grid.Row="10" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
+ Value="{Binding Suggestion.ProportionalBottomWaitTimeBeforeCure}"/>
+
+ <NumericUpDown Grid.Row="10" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
+ Value="{Binding Suggestion.ProportionalWaitTimeBeforeCure}"/>
+
+ <TextBlock Grid.Row="12" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the wait time according an (wait time - layer pixels) ratio"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerPixels}"
+ Text="Proportional pixels:"/>
+
+ <NumericUpDown Grid.Row="12" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_px"
+ Minimum="1"
+ Maximum="4294967295"
+ Increment="5000"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerPixels}"
+ Value="{Binding Suggestion.ProportionalBottomLayerPixels}"/>
+
+ <NumericUpDown Grid.Row="12" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_px"
+ Minimum="1"
+ Maximum="4294967295"
+ Increment="5000"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerPixels}"
+ Value="{Binding Suggestion.ProportionalLayerPixels}"/>
+
+ <TextBlock Grid.Row="12" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the wait time according an (wait time - layer area) ratio"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerArea}"
+ Text="Proportional area:"/>
+
+ <NumericUpDown Grid.Row="12" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm2"
+ Minimum="1"
+ Maximum="4294967295"
+ Increment="50"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerArea}"
+ Value="{Binding Suggestion.ProportionalBottomLayerArea}"/>
+
+ <NumericUpDown Grid.Row="12" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_mm2"
+ Minimum="1"
+ Maximum="4294967295"
+ Increment="50"
+ IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerArea}"
+ Value="{Binding Suggestion.ProportionalLayerArea}"/>
+
+
+ <TextBlock Grid.Row="14" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Minimum:"/>
+
+ <TextBlock Grid.Row="14" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Maximum:"/>
+
+ <TextBlock Grid.Row="16" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits for the bottom layers (Min-Max)"
+ Text="Limits (bottom):"/>
+
+ <NumericUpDown Grid.Row="16" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="{Binding SlicerFile.LayerHeight}"
+ Value="{Binding Suggestion.MinimumBottomWaitTimeBeforeCure}"/>
+
+ <TextBlock Grid.Row="16" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="16" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.MaximumBottomWaitTimeBeforeCure}"/>
+
+ <TextBlock Grid.Row="18" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits for the normal layers (Min-Max)"
+ Text="Limits (normal):"/>
+
+ <NumericUpDown Grid.Row="18" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.MinimumWaitTimeBeforeCure}"/>
+
+ <TextBlock Grid.Row="18" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="18" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="1000"
+ FormatString="F2"
+ Increment="0.50"
+ Value="{Binding Suggestion.MaximumWaitTimeBeforeCure}"/>
+
+
+ </Grid>
+</UserControl>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml.cs b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml.cs
new file mode 100644
index 0000000..5d19af6
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionWaitTimeBeforeCureControl.axaml.cs
@@ -0,0 +1,22 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Controls.Suggestions;
+
+public partial class SuggestionWaitTimeBeforeCureControl : SuggestionControl
+{
+ public SuggestionWaitTimeBeforeCureControl() : this(new SuggestionWaitTimeBeforeCure())
+ { }
+
+ public SuggestionWaitTimeBeforeCureControl(Suggestion suggestion)
+ {
+ Suggestion = suggestion;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/ToggleButtonWithIcon.cs b/UVtools.WPF/Controls/ToggleButtonWithIcon.cs
new file mode 100644
index 0000000..f5e146a
--- /dev/null
+++ b/UVtools.WPF/Controls/ToggleButtonWithIcon.cs
@@ -0,0 +1,109 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Layout;
+using Avalonia.Styling;
+using Avalonia.Threading;
+
+namespace UVtools.WPF.Controls;
+
+public class ToggleButtonWithIcon : ToggleButton, IStyleable
+{
+ Type IStyleable.StyleKey => typeof(ToggleButton);
+
+ public static readonly StyledProperty<string> TextProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, string>(nameof(Text));
+
+ public string Text
+ {
+ get => GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public static readonly StyledProperty<ButtonWithIcon.IconPlacementType> IconPlacementProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, ButtonWithIcon.IconPlacementType>(nameof(IconPlacement));
+
+ public ButtonWithIcon.IconPlacementType IconPlacement
+ {
+ get => GetValue(IconPlacementProperty);
+ set => SetValue(IconPlacementProperty, value);
+ }
+
+ public static readonly StyledProperty<string> IconProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, string>(nameof(Icon));
+
+ public string Icon
+ {
+ get => GetValue(IconProperty);
+ set => SetValue(IconProperty, value);
+ }
+
+ public static readonly StyledProperty<double> SpacingProperty =
+ AvaloniaProperty.Register<ButtonWithIcon, double>(nameof(Spacing), 10);
+
+ public double Spacing
+ {
+ get => GetValue(SpacingProperty);
+ set => SetValue(SpacingProperty, value);
+ }
+
+ public ToggleButtonWithIcon()
+ {
+ }
+
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ Dispatcher.UIThread.Post(() =>
+ {
+ TextProperty.Changed.Subscribe(_ => RebuildContent());
+ IconProperty.Changed.Subscribe(_ => RebuildContent());
+ IconPlacementProperty.Changed.Subscribe(_ => RebuildContent());
+ RebuildContent();
+ }, DispatcherPriority.Loaded);
+ }
+
+ public IControl MakeIcon()
+ {
+ return new Projektanker.Icons.Avalonia.Icon { Value = Icon };
+ }
+
+ private void RebuildContent()
+ {
+ if (string.IsNullOrWhiteSpace(Icon))
+ {
+ if (!string.IsNullOrWhiteSpace(Text))
+ {
+ Content = Text;
+ }
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(Text))
+ {
+ if (!string.IsNullOrWhiteSpace(Icon))
+ {
+ Content = MakeIcon();
+ }
+
+ return;
+ }
+
+ var panel = new StackPanel
+ {
+ Spacing = Spacing,
+ VerticalAlignment = VerticalAlignment.Stretch,
+ Orientation = IconPlacement is ButtonWithIcon.IconPlacementType.Left or ButtonWithIcon.IconPlacementType.Right
+ ? Orientation.Horizontal
+ : Orientation.Vertical
+ };
+
+ if (IconPlacement is ButtonWithIcon.IconPlacementType.Left or ButtonWithIcon.IconPlacementType.Top) panel.Children.Add(MakeIcon());
+ panel.Children.Add(new TextBlock { VerticalAlignment = VerticalAlignment.Center, Text = Text });
+ if (IconPlacement is ButtonWithIcon.IconPlacementType.Right or ButtonWithIcon.IconPlacementType.Bottom) panel.Children.Add(MakeIcon());
+
+ Content = panel;
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs
index 433ee97..a1fea36 100644
--- a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs
@@ -2,22 +2,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolBlurControl : ToolControl
{
- public class ToolBlurControl : ToolControl
- {
- public OperationBlur Operation => BaseOperation as OperationBlur;
+ public OperationBlur Operation => BaseOperation as OperationBlur;
- public ToolBlurControl()
- {
- BaseOperation = new OperationBlur(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolBlurControl()
+ {
+ BaseOperation = new OperationBlur(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml
index 1c8afa1..4d06407 100644
--- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml
@@ -9,7 +9,7 @@
<TabItem Header="Millimeters to pixels">
<Grid RowDefinitions="Auto,10,Auto,30,Auto">
<Border
- Background="White"
+ Background="{DynamicResource LightBackground}"
BorderBrush="Black" BorderThickness="1">
<TextBox VerticalAlignment="Center"
Classes="TransparentReadOnly"
@@ -171,7 +171,7 @@
<Grid RowDefinitions="Auto,10,Auto">
<Border
- Background="White"
+ Background="{DynamicResource LightBackground}"
BorderBrush="Black" BorderThickness="1">
<TextBox VerticalAlignment="Center"
Classes="TransparentReadOnly"
@@ -401,7 +401,7 @@
<TabItem Header="Optimal model tilt">
<Grid RowDefinitions="Auto,10,Auto,30,Auto">
<Border
- Background="White"
+ Background="{DynamicResource LightBackground}"
BorderBrush="Black" BorderThickness="1">
<TextBox VerticalAlignment="Center" Classes="TransparentReadOnly" Padding="10">
<TextBox.Text>
diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs
index 5682747..cf08d99 100644
--- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs
@@ -4,73 +4,72 @@ using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolCalculatorControl : ToolControl
{
- public class ToolCalculatorControl : ToolControl
+ private decimal _lightOffDelayPrintTimeHours;
+ public OperationCalculator Operation => BaseOperation as OperationCalculator;
+
+ public decimal LightOffDelayPrintTimeHours
{
- private decimal _lightOffDelayPrintTimeHours;
- public OperationCalculator Operation => BaseOperation as OperationCalculator;
+ get => _lightOffDelayPrintTimeHours;
+ set => RaiseAndSetIfChanged(ref _lightOffDelayPrintTimeHours, value);
+ }
- public decimal LightOffDelayPrintTimeHours
- {
- get => _lightOffDelayPrintTimeHours;
- set => RaiseAndSetIfChanged(ref _lightOffDelayPrintTimeHours, value);
- }
+ public ToolCalculatorControl()
+ {
+ BaseOperation = new OperationCalculator(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- public ToolCalculatorControl()
- {
- BaseOperation = new OperationCalculator(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- private void InitializeComponent()
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- AvaloniaXamlLoader.Load(this);
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.CalcLightOffDelay.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName != nameof(Operation.CalcLightOffDelay.LightOffDelay) &&
+ e.PropertyName != nameof(Operation.CalcLightOffDelay.BottomLightOffDelay)) return;
+ LightOffDelayPrintTimeHours = Math.Round(
+ (FileFormat.ExtraPrintTime +
+ SlicerFile.BottomLayerCount * (Operation.CalcLightOffDelay.BottomLightOffDelay + (decimal)SlicerFile.BottomExposureTime) +
+ SlicerFile.NormalLayerCount * (Operation.CalcLightOffDelay.LightOffDelay + (decimal)SlicerFile.ExposureTime))
+ / 3600, 2);
+ };
+
+ _lightOffDelayPrintTimeHours = (decimal)SlicerFile.PrintTimeHours;
+ break;
}
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public void LightOffDelaySetParameters(byte side)
+ {
+ if (side == 0) // Bottom layers
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.CalcLightOffDelay.PropertyChanged += (sender, e) =>
- {
- if (e.PropertyName != nameof(Operation.CalcLightOffDelay.LightOffDelay) &&
- e.PropertyName != nameof(Operation.CalcLightOffDelay.BottomLightOffDelay)) return;
- LightOffDelayPrintTimeHours = Math.Round(
- (FileFormat.ExtraPrintTime +
- SlicerFile.BottomLayerCount * (Operation.CalcLightOffDelay.BottomLightOffDelay + (decimal)SlicerFile.BottomExposureTime) +
- SlicerFile.NormalLayerCount * (Operation.CalcLightOffDelay.LightOffDelay + (decimal)SlicerFile.ExposureTime))
- / 3600, 2);
- };
-
- _lightOffDelayPrintTimeHours = (decimal)SlicerFile.PrintTimeHours;
- break;
- }
+ SlicerFile.BottomLiftHeight = (float)Operation.CalcLightOffDelay.BottomLiftHeight;
+ SlicerFile.BottomLiftSpeed = (float)Operation.CalcLightOffDelay.BottomLiftSpeed;
+ SlicerFile.RetractSpeed = (float)Operation.CalcLightOffDelay.RetractSpeed;
+ SlicerFile.BottomLightOffDelay = (float)Operation.CalcLightOffDelay.BottomLightOffDelay;
}
-
- public void LightOffDelaySetParameters(byte side)
+ else // Normal layers
{
- if (side == 0) // Bottom layers
- {
- SlicerFile.BottomLiftHeight = (float)Operation.CalcLightOffDelay.BottomLiftHeight;
- SlicerFile.BottomLiftSpeed = (float)Operation.CalcLightOffDelay.BottomLiftSpeed;
- SlicerFile.RetractSpeed = (float)Operation.CalcLightOffDelay.RetractSpeed;
- SlicerFile.BottomLightOffDelay = (float)Operation.CalcLightOffDelay.BottomLightOffDelay;
- }
- else // Normal layers
- {
- SlicerFile.LiftHeight = (float)Operation.CalcLightOffDelay.LiftHeight;
- SlicerFile.LiftSpeed = (float)Operation.CalcLightOffDelay.LiftSpeed;
- SlicerFile.RetractSpeed = (float)Operation.CalcLightOffDelay.RetractSpeed;
- SlicerFile.LightOffDelay = (float)Operation.CalcLightOffDelay.LightOffDelay;
- }
+ SlicerFile.LiftHeight = (float)Operation.CalcLightOffDelay.LiftHeight;
+ SlicerFile.LiftSpeed = (float)Operation.CalcLightOffDelay.LiftSpeed;
+ SlicerFile.RetractSpeed = (float)Operation.CalcLightOffDelay.RetractSpeed;
+ SlicerFile.LightOffDelay = (float)Operation.CalcLightOffDelay.LightOffDelay;
+ }
- LightOffDelayPrintTimeHours = (decimal)SlicerFile.PrintTimeHours;
+ LightOffDelayPrintTimeHours = (decimal)SlicerFile.PrintTimeHours;
- App.MainWindow.CanSave = true;
- }
+ App.MainWindow.CanSave = true;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs
index 9eac9e5..43e5044 100644
--- a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs
@@ -3,44 +3,43 @@ using System.Timers;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolChangeResolutionControl : ToolControl
{
- public class ToolChangeResolutionControl : ToolControl
- {
- private OperationChangeResolution.Resolution _selectedPresetItem;
- public OperationChangeResolution Operation => BaseOperation as OperationChangeResolution;
+ private OperationChangeResolution.Resolution _selectedPresetItem;
+ public OperationChangeResolution Operation => BaseOperation as OperationChangeResolution;
- public OperationChangeResolution.Resolution SelectedPresetItem
+ public OperationChangeResolution.Resolution SelectedPresetItem
+ {
+ get => _selectedPresetItem;
+ set
{
- get => _selectedPresetItem;
- set
- {
- RaiseAndSetIfChanged(ref _selectedPresetItem, value);
- if (_selectedPresetItem is null || _selectedPresetItem.IsEmpty) return;
- Operation.NewResolutionX = _selectedPresetItem.ResolutionX;
- Operation.NewResolutionY = _selectedPresetItem.ResolutionY;
+ RaiseAndSetIfChanged(ref _selectedPresetItem, value);
+ if (_selectedPresetItem is null || _selectedPresetItem.IsEmpty) return;
+ Operation.NewResolutionX = _selectedPresetItem.ResolutionX;
+ Operation.NewResolutionY = _selectedPresetItem.ResolutionY;
- //SelectedPresetItem = null;
- Timer timer = new(1);
- timer.Elapsed += (sender, args) =>
- {
- SelectedPresetItem = null;
- timer.Dispose();
- };
- timer.Start();
- }
+ //SelectedPresetItem = null;
+ Timer timer = new(1);
+ timer.Elapsed += (sender, args) =>
+ {
+ SelectedPresetItem = null;
+ timer.Dispose();
+ };
+ timer.Start();
}
+ }
- public ToolChangeResolutionControl()
- {
- BaseOperation = new OperationChangeResolution(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolChangeResolutionControl()
+ {
+ BaseOperation = new OperationChangeResolution(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs
index df6afbc..2211047 100644
--- a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs
@@ -5,110 +5,109 @@ using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolControl : UserControlEx
{
- public class ToolControl : UserControlEx
- {
- private Operation _baseOperation;
+ private Operation _baseOperation;
- public Operation BaseOperation
+ public Operation BaseOperation
+ {
+ get => _baseOperation;
+ set
{
- get => _baseOperation;
- set
+ bool wasNullBefore = _baseOperation is null;
+ _baseOperation = value;
+ _baseOperation.SlicerFile = SlicerFile;
+ RaisePropertyChanged();
+
+ if (!wasNullBefore)
{
- bool wasNullBefore = _baseOperation is null;
- _baseOperation = value;
- _baseOperation.SlicerFile = SlicerFile;
- RaisePropertyChanged();
-
- if (!wasNullBefore)
- {
- Callback(ToolWindow.Callbacks.Loaded);
- }
-
- if (DataContext is null) return;
- ResetDataContext();
+ Callback(ToolWindow.Callbacks.Loaded);
}
+
+ if (DataContext is null) return;
+ ResetDataContext();
}
+ }
- public ToolWindow ParentWindow { get; set; } = null;
+ public ToolWindow ParentWindow { get; set; } = null;
- public bool CanRun { get; set; } = true;
+ public bool CanRun { get; set; } = true;
- public ToolControl()
- {
- InitializeComponent();
- }
+ public ToolControl()
+ {
+ InitializeComponent();
+ }
- public ToolControl(Operation operation)
- {
- BaseOperation = operation;
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolControl(Operation operation)
+ {
+ BaseOperation = operation;
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- private void InitializeComponent()
+ public bool ValidateSpawn()
+ {
+ if(_baseOperation is null)
{
- AvaloniaXamlLoader.Load(this);
+ App.MainWindow.MessageBoxInfo("The operation does not contain a valid configuration.\n" +
+ "Please contact the support/developer.", BaseOperation.NotSupportedTitle).ConfigureAwait(false);
+ CanRun = false;
+ return false;
}
-
- public bool ValidateSpawn()
+ if (!_baseOperation.ValidateSpawn(out var message))
{
- if(_baseOperation is null)
- {
- App.MainWindow.MessageBoxInfo("The operation does not contain a valid configuration.\n" +
- "Please contact the support/developer.", BaseOperation.NotSupportedTitle).ConfigureAwait(false);
- CanRun = false;
- return false;
- }
- if (!_baseOperation.ValidateSpawn(out var message))
- {
- App.MainWindow.MessageBoxInfo(message, BaseOperation.NotSupportedTitle).ConfigureAwait(false);
- CanRun = false;
- return false;
- }
-
- return true;
+ App.MainWindow.MessageBoxInfo(message, BaseOperation.NotSupportedTitle).ConfigureAwait(false);
+ CanRun = false;
+ return false;
}
- public virtual void Callback(ToolWindow.Callbacks callback) { }
-
- public virtual bool UpdateOperation() => true;
+ return true;
+ }
- /*public virtual void SetOperation(Operation operation)
- {
- BaseOperation = operation;
- ResetDataContext();
- }*/
+ public virtual void Callback(ToolWindow.Callbacks callback) { }
- /// <summary>
- /// Validates if is safe to continue with operation
- /// </summary>
- /// <returns></returns>
- public virtual async Task<bool> ValidateForm()
- {
- if (BaseOperation is null) return true;
- if (!UpdateOperation()) return false;
- return await ValidateFormFromString(BaseOperation.Validate());
- }
+ public virtual bool UpdateOperation() => true;
- /// <summary>
- /// Validates if is safe to continue with operation, if not shows a message box with the error
- /// </summary>
- /// <param name="text"></param>
- /// <returns></returns>
- public async Task<bool> ValidateFormFromString(string text)
- {
- if (string.IsNullOrEmpty(text)) return true;
- await ParentWindow.MessageBoxError(text);
- return false;
- }
+ /*public virtual void SetOperation(Operation operation)
+ {
+ BaseOperation = operation;
+ ResetDataContext();
+ }*/
+
+ /// <summary>
+ /// Validates if is safe to continue with operation
+ /// </summary>
+ /// <returns></returns>
+ public virtual async Task<bool> ValidateForm()
+ {
+ if (BaseOperation is null) return true;
+ if (!UpdateOperation()) return false;
+ return await ValidateFormFromString(BaseOperation.Validate());
+ }
- /// <summary>
- /// Validates if is safe to continue with operation, if not shows a message box with the error
- /// </summary>
- /// <param name="text"></param>
- /// <returns></returns>
- public async Task<bool> ValidateFormFromString(ValueDescription text) => await ValidateFormFromString(text?.ToString());
+ /// <summary>
+ /// Validates if is safe to continue with operation, if not shows a message box with the error
+ /// </summary>
+ /// <param name="text"></param>
+ /// <returns></returns>
+ public async Task<bool> ValidateFormFromString(string text)
+ {
+ if (string.IsNullOrEmpty(text)) return true;
+ await ParentWindow.MessageBoxError(text);
+ return false;
}
-}
+
+ /// <summary>
+ /// Validates if is safe to continue with operation, if not shows a message box with the error
+ /// </summary>
+ /// <param name="text"></param>
+ /// <returns></returns>
+ public async Task<bool> ValidateFormFromString(ValueDescription text) => await ValidateFormFromString(text?.ToString());
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs
index 82ca7e7..fedc842 100644
--- a/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs
@@ -2,22 +2,21 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolDoubleExposureControl : ToolControl
{
- public partial class ToolDoubleExposureControl : ToolControl
- {
- public OperationDoubleExposure Operation => BaseOperation as OperationDoubleExposure;
+ public OperationDoubleExposure Operation => BaseOperation as OperationDoubleExposure;
- public ToolDoubleExposureControl()
- {
- BaseOperation = new OperationDoubleExposure(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolDoubleExposureControl()
+ {
+ BaseOperation = new OperationDoubleExposure(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs
index 15aee68..da661c0 100644
--- a/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolDynamicLayerHeightControl.axaml.cs
@@ -6,61 +6,60 @@ using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolDynamicLayerHeightControl : ToolControl
{
- public class ToolDynamicLayerHeightControl : ToolControl
- {
- public OperationDynamicLayerHeight Operation => BaseOperation as OperationDynamicLayerHeight;
+ public OperationDynamicLayerHeight Operation => BaseOperation as OperationDynamicLayerHeight;
- public double LayerHeight => SlicerFile.LayerHeight;
- public double MinimumLayerHeight => Layer.RoundHeight(SlicerFile.LayerHeight * 2);
- public double MaximumLayerHeight => FileFormat.MaximumLayerHeight;
+ public double LayerHeight => SlicerFile.LayerHeight;
+ public double MinimumLayerHeight => Layer.RoundHeight(SlicerFile.LayerHeight * 2);
+ public double MaximumLayerHeight => FileFormat.MaximumLayerHeight;
- private readonly DataGrid ExposureTable;
+ private readonly DataGrid ExposureTable;
- public ToolDynamicLayerHeightControl()
- {
- BaseOperation = new OperationDynamicLayerHeight(SlicerFile);
- if (!ValidateSpawn()) return;
+ public ToolDynamicLayerHeightControl()
+ {
+ BaseOperation = new OperationDynamicLayerHeight(SlicerFile);
+ if (!ValidateSpawn()) return;
- if (!SlicerFile.CanUseLayerExposureTime)
- {
- App.MainWindow.MessageBoxWaring($"Your printer seems to not support this tool, still you are allowed to run it for analyze, packing layers or simulation.\n" +
- $"Do not print this file after run this tool on a not compatible printer, it will result in malformed model and height violation.\n" +
- $"Run this at your own risk!",
- BaseOperation.Title).ConfigureAwait(false);
- }
+ if (!SlicerFile.CanUseLayerExposureTime)
+ {
+ App.MainWindow.MessageBoxWaring($"Your printer seems to not support this tool, still you are allowed to run it for analyze, packing layers or simulation.\n" +
+ $"Do not print this file after run this tool on a not compatible printer, it will result in malformed model and height violation.\n" +
+ $"Run this at your own risk!",
+ BaseOperation.Title).ConfigureAwait(false);
+ }
- InitializeComponent();
+ InitializeComponent();
- ExposureTable = this.FindControl<DataGrid>("ExposureTable");
- }
+ ExposureTable = this.FindControl<DataGrid>("ExposureTable");
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ if (SlicerFile is null) return;
+ switch (callback)
{
- if (SlicerFile is null) return;
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- /*Operation.PropertyChanged += (sender, e) =>
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ /*Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName.Equals(nameof(Operation.CacheObjectCount)))
{
- if (e.PropertyName.Equals(nameof(Operation.CacheObjectCount)))
- {
- RaisePropertyChanged(nameof(CacheRAMUsed));
- return;
- }
- };*/
- Operation.RebuildAutoExposureTable();
- break;
- }
+ RaisePropertyChanged(nameof(CacheRAMUsed));
+ return;
+ }
+ };*/
+ Operation.RebuildAutoExposureTable();
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs
index ae001c9..2f1561b 100644
--- a/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs
@@ -2,36 +2,35 @@ using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolDynamicLiftsControl : ToolControl
{
- public class ToolDynamicLiftsControl : ToolControl
+ public OperationDynamicLifts Operation => BaseOperation as OperationDynamicLifts;
+ public ToolDynamicLiftsControl()
{
- public OperationDynamicLifts Operation => BaseOperation as OperationDynamicLifts;
- public ToolDynamicLiftsControl()
- {
- BaseOperation = new OperationDynamicLifts(SlicerFile);
- if (!ValidateSpawn()) return;
+ BaseOperation = new OperationDynamicLifts(SlicerFile);
+ if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void ViewSmallestLayer(bool isBottom)
- {
- var layerFound = Operation.GetSmallestLayer(isBottom);
- if (layerFound is null) return;
- App.MainWindow.ActualLayer = layerFound.Index;
- }
+ public void ViewSmallestLayer(bool isBottom)
+ {
+ var layerFound = Operation.GetSmallestLayer(isBottom);
+ if (layerFound is null) return;
+ App.MainWindow.ActualLayer = layerFound.Index;
+ }
- public void ViewLargestLayer(bool isBottom)
- {
- var layerFound = Operation.GetLargestLayer(isBottom);
- if (layerFound is null) return;
- App.MainWindow.ActualLayer = layerFound.Index;
- }
+ public void ViewLargestLayer(bool isBottom)
+ {
+ var layerFound = Operation.GetLargestLayer(isBottom);
+ if (layerFound is null) return;
+ App.MainWindow.ActualLayer = layerFound.Index;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
index 69447da..c920819 100644
--- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
@@ -11,212 +11,211 @@ using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolEditParametersControl : ToolControl
{
- public class ToolEditParametersControl : ToolControl
- {
- public OperationEditParameters Operation => BaseOperation as OperationEditParameters;
+ public OperationEditParameters Operation => BaseOperation as OperationEditParameters;
- public RowControl[] RowControls;
- private Grid grid;
+ public RowControl[] RowControls;
+ private Grid grid;
- public sealed class RowControl
- {
- public FileFormat.PrintParameterModifier Modifier { get; }
+ public sealed class RowControl
+ {
+ public FileFormat.PrintParameterModifier Modifier { get; }
- public TextBlock Name { get; }
- public TextBlock OldValue { get; }
- public NumericUpDown NewValue { get; }
- public TextBlock Unit { get; }
- public Button ResetButton { get; }
+ public TextBlock Name { get; }
+ public TextBlock OldValue { get; }
+ public NumericUpDown NewValue { get; }
+ public TextBlock Unit { get; }
+ public Button ResetButton { get; }
- public RowControl(FileFormat.PrintParameterModifier modifier)
- {
- Modifier = modifier;
+ public RowControl(FileFormat.PrintParameterModifier modifier)
+ {
+ Modifier = modifier;
- modifier.NewValue = modifier.OldValue.Clamp(modifier.Minimum, modifier.Maximum);
+ modifier.NewValue = modifier.OldValue.Clamp(modifier.Minimum, modifier.Maximum);
- Name = new TextBlock
- {
- Text = $"{modifier.Name}:",
- VerticalAlignment = VerticalAlignment.Center,
- Padding = new Thickness(15, 0),
- Tag = this,
- };
-
- if(!string.IsNullOrWhiteSpace(modifier.Description)) ToolTip.SetTip(Name, modifier.Description);
-
- OldValue = new TextBlock
- {
- Text = modifier.OldValue.ToString(CultureInfo.InvariantCulture),
- VerticalAlignment = VerticalAlignment.Center,
- HorizontalAlignment = HorizontalAlignment.Center,
- //Padding = new Thickness(15, 0),
- Tag = this
- };
-
- NewValue = new NumericUpDown
- {
- //DecimalPlaces = modifier.DecimalPlates,
- VerticalAlignment = VerticalAlignment.Center,
- HorizontalAlignment = HorizontalAlignment.Stretch,
- Minimum = (double) modifier.Minimum,
- Maximum = (double) modifier.Maximum,
- Increment = modifier.Increment,
- Value = (double)modifier.NewValue,
- Tag = this,
- //Width = 100,
- ClipValueToMinMax = true
- };
- if (modifier.DecimalPlates > 0)
- {
- NewValue.FormatString = $"F{modifier.DecimalPlates}";
- }
+ Name = new TextBlock
+ {
+ Text = $"{modifier.Name}:",
+ VerticalAlignment = VerticalAlignment.Center,
+ Padding = new Thickness(15, 0),
+ Tag = this,
+ };
- Unit = new TextBlock
- {
- Text = modifier.ValueUnit,
- VerticalAlignment = VerticalAlignment.Center,
- Padding = new Thickness(10, 0, 15, 0),
- Tag = this
- };
+ if(!string.IsNullOrWhiteSpace(modifier.Description)) ToolTip.SetTip(Name, modifier.Description);
- ResetButton = new Button
- {
- IsVisible = false,
- IsEnabled = false,
- VerticalAlignment = VerticalAlignment.Center,
- Tag = this,
- Padding = new Thickness(5),
- Content = new Image {Source = App.GetBitmapFromAsset("/Assets/Icons/undo-16x16.png")},
- HorizontalAlignment = HorizontalAlignment.Stretch
- };
- ResetButton.Click += ResetButtonOnClick;
- NewValue.ValueChanged += NewValueOnValueChanged;
+ OldValue = new TextBlock
+ {
+ Text = modifier.OldValue.ToString(CultureInfo.InvariantCulture),
+ VerticalAlignment = VerticalAlignment.Center,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ //Padding = new Thickness(15, 0),
+ Tag = this
+ };
+
+ NewValue = new NumericUpDown
+ {
+ //DecimalPlaces = modifier.DecimalPlates,
+ VerticalAlignment = VerticalAlignment.Center,
+ HorizontalAlignment = HorizontalAlignment.Stretch,
+ Minimum = (double) modifier.Minimum,
+ Maximum = (double) modifier.Maximum,
+ Increment = modifier.Increment,
+ Value = (double)modifier.NewValue,
+ Tag = this,
+ //Width = 100,
+ ClipValueToMinMax = true
+ };
+ if (modifier.DecimalPlates > 0)
+ {
+ NewValue.FormatString = $"F{modifier.DecimalPlates}";
}
- private void NewValueOnValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ Unit = new TextBlock
{
- Modifier.NewValue = (decimal) NewValue.Value;
- ResetButton.IsVisible = ResetButton.IsEnabled = Modifier.HasChanged;
- }
+ Text = modifier.ValueUnit,
+ VerticalAlignment = VerticalAlignment.Center,
+ Padding = new Thickness(10, 0, 15, 0),
+ Tag = this
+ };
- private void ResetButtonOnClick(object? sender, RoutedEventArgs e)
+ ResetButton = new Button
{
- NewValue.Value = (double) Modifier.OldValue;
- NewValue.Focus();
- }
+ IsVisible = false,
+ IsEnabled = false,
+ VerticalAlignment = VerticalAlignment.Center,
+ Tag = this,
+ Padding = new Thickness(5),
+ Content = new Projektanker.Icons.Avalonia.Icon{Value = "fas fa-undo-alt"},
+ HorizontalAlignment = HorizontalAlignment.Stretch
+ };
+ ResetButton.Click += ResetButtonOnClick;
+ NewValue.ValueChanged += NewValueOnValueChanged;
}
- public ToolEditParametersControl()
+ private void NewValueOnValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
{
- BaseOperation = new OperationEditParameters(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
-
- grid = this.FindControl<Grid>("grid");
+ Modifier.NewValue = (decimal) NewValue.Value;
+ ResetButton.IsVisible = ResetButton.IsEnabled = Modifier.HasChanged;
}
- public void PopulateGrid()
+ private void ResetButtonOnClick(object? sender, RoutedEventArgs e)
{
- const byte cols = 5;
- if (grid.Children.Count > cols)
- {
- grid.Children.RemoveRange(cols, grid.Children.Count - cols);
- }
- if (grid.RowDefinitions.Count > 1)
- {
- grid.RowDefinitions.RemoveRange(1, grid.RowDefinitions.Count-1);
- }
+ NewValue.Value = (double) Modifier.OldValue;
+ NewValue.Focus();
+ }
+ }
- int rowIndex = 1;
- RowControls = new RowControl[Operation.Modifiers.Length];
- //table.RowCount = Operation.Modifiers.Length+1;
- foreach (var modifier in Operation.Modifiers)
- {
- grid.RowDefinitions.Add(new RowDefinition());
- byte column = 0;
-
- var rowControl = new RowControl(modifier);
- grid.Children.Add(rowControl.Name);
- grid.Children.Add(rowControl.OldValue);
- grid.Children.Add(rowControl.NewValue);
- grid.Children.Add(rowControl.Unit);
- grid.Children.Add(rowControl.ResetButton);
- Grid.SetRow(rowControl.Name, rowIndex);
- Grid.SetColumn(rowControl.Name, column++);
-
- Grid.SetRow(rowControl.OldValue, rowIndex);
- Grid.SetColumn(rowControl.OldValue, column++);
-
- Grid.SetRow(rowControl.NewValue, rowIndex);
- Grid.SetColumn(rowControl.NewValue, column++);
-
- Grid.SetRow(rowControl.Unit, rowIndex);
- Grid.SetColumn(rowControl.Unit, column++);
-
- Grid.SetRow(rowControl.ResetButton, rowIndex);
- Grid.SetColumn(rowControl.ResetButton, column++);
- /*table.Controls.Add(rowControl.Name, column++, rowIndex);
- table.Controls.Add(rowControl.OldValue, column++, rowIndex);
- table.Controls.Add(rowControl.NewValue, column++, rowIndex);
- table.Controls.Add(rowControl.Unit, column++, rowIndex);
- table.Controls.Add(rowControl.ResetButton, column++, rowIndex);
- */
- RowControls[rowIndex - 1] = rowControl;
-
- rowIndex++;
- }
+ public ToolEditParametersControl()
+ {
+ BaseOperation = new OperationEditParameters(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+
+ grid = this.FindControl<Grid>("grid");
+ }
+
+ public void PopulateGrid()
+ {
+ const byte cols = 5;
+ if (grid.Children.Count > cols)
+ {
+ grid.Children.RemoveRange(cols, grid.Children.Count - cols);
+ }
+ if (grid.RowDefinitions.Count > 1)
+ {
+ grid.RowDefinitions.RemoveRange(1, grid.RowDefinitions.Count-1);
}
- private void InitializeComponent()
+ int rowIndex = 1;
+ RowControls = new RowControl[Operation.Modifiers.Length];
+ //table.RowCount = Operation.Modifiers.Length+1;
+ foreach (var modifier in Operation.Modifiers)
{
- AvaloniaXamlLoader.Load(this);
+ grid.RowDefinitions.Add(new RowDefinition());
+ byte column = 0;
+
+ var rowControl = new RowControl(modifier);
+ grid.Children.Add(rowControl.Name);
+ grid.Children.Add(rowControl.OldValue);
+ grid.Children.Add(rowControl.NewValue);
+ grid.Children.Add(rowControl.Unit);
+ grid.Children.Add(rowControl.ResetButton);
+ Grid.SetRow(rowControl.Name, rowIndex);
+ Grid.SetColumn(rowControl.Name, column++);
+
+ Grid.SetRow(rowControl.OldValue, rowIndex);
+ Grid.SetColumn(rowControl.OldValue, column++);
+
+ Grid.SetRow(rowControl.NewValue, rowIndex);
+ Grid.SetColumn(rowControl.NewValue, column++);
+
+ Grid.SetRow(rowControl.Unit, rowIndex);
+ Grid.SetColumn(rowControl.Unit, column++);
+
+ Grid.SetRow(rowControl.ResetButton, rowIndex);
+ Grid.SetColumn(rowControl.ResetButton, column++);
+ /*table.Controls.Add(rowControl.Name, column++, rowIndex);
+ table.Controls.Add(rowControl.OldValue, column++, rowIndex);
+ table.Controls.Add(rowControl.NewValue, column++, rowIndex);
+ table.Controls.Add(rowControl.Unit, column++, rowIndex);
+ table.Controls.Add(rowControl.ResetButton, column++, rowIndex);
+ */
+ RowControls[rowIndex - 1] = rowControl;
+
+ rowIndex++;
}
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- if (callback is ToolWindow.Callbacks.Init)
- {
- ParentWindow.SelectCurrentLayer();
- ParentWindow.LayerRangeSync = true;
- }
-
- PopulateGrid();
- Operation.PropertyChanged += OperationOnPropertyChanged;
- break;
- }
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ if (callback is ToolWindow.Callbacks.Init)
+ {
+ ParentWindow.SelectCurrentLayer();
+ ParentWindow.LayerRangeSync = true;
+ }
+
+ PopulateGrid();
+ Operation.PropertyChanged += OperationOnPropertyChanged;
+ break;
}
+ }
- private void OperationOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ private void OperationOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Operation.LayerIndexStart) && Operation.PerLayerOverride)
+ {
+ SlicerFile.RefreshPrintParametersPerLayerModifiersValues(Operation.LayerIndexStart);
+ PopulateGrid();
+ return;
+ }
+ if (e.PropertyName == nameof(Operation.PerLayerOverride))
{
- if (e.PropertyName == nameof(Operation.LayerIndexStart) && Operation.PerLayerOverride)
+ if (Operation.PerLayerOverride)
{
+ Operation.Modifiers = App.SlicerFile.PrintParameterPerLayerModifiers;
SlicerFile.RefreshPrintParametersPerLayerModifiersValues(Operation.LayerIndexStart);
- PopulateGrid();
- return;
}
- if (e.PropertyName == nameof(Operation.PerLayerOverride))
+ else
{
- if (Operation.PerLayerOverride)
- {
- Operation.Modifiers = App.SlicerFile.PrintParameterPerLayerModifiers;
- SlicerFile.RefreshPrintParametersPerLayerModifiersValues(Operation.LayerIndexStart);
- }
- else
- {
- Operation.Modifiers = App.SlicerFile.PrintParameterModifiers;
- SlicerFile.RefreshPrintParametersModifiersValues();
- }
-
- ParentWindow.LayerRangeVisible = Operation.PerLayerOverride;
- PopulateGrid();
- return;
+ Operation.Modifiers = App.SlicerFile.PrintParameterModifiers;
+ SlicerFile.RefreshPrintParametersModifiersValues();
}
+
+ ParentWindow.LayerRangeVisible = Operation.PerLayerOverride;
+ PopulateGrid();
+ return;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs
index a75fe28..998689e 100644
--- a/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs
@@ -3,46 +3,45 @@ using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolFadeExposureTimeControl : ToolControl
{
- public partial class ToolFadeExposureTimeControl : ToolControl
- {
- public OperationFadeExposureTime Operation => BaseOperation as OperationFadeExposureTime;
+ public OperationFadeExposureTime Operation => BaseOperation as OperationFadeExposureTime;
- public ToolFadeExposureTimeControl()
- {
- BaseOperation = new OperationFadeExposureTime(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolFadeExposureTimeControl()
+ {
+ BaseOperation = new OperationFadeExposureTime(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ ParentWindow.LayerIndexEnd = Operation.LayerIndexStart + Operation.LayerCount - 1;
+ Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName != nameof(Operation.LayerCount)) return;
ParentWindow.LayerIndexEnd = Operation.LayerIndexStart + Operation.LayerCount - 1;
- Operation.PropertyChanged += (sender, e) =>
+ };
+ ParentWindow.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName is nameof(ParentWindow.LayerIndexStart) or nameof(ParentWindow.LayerIndexEnd))
{
- if (e.PropertyName != nameof(Operation.LayerCount)) return;
ParentWindow.LayerIndexEnd = Operation.LayerIndexStart + Operation.LayerCount - 1;
- };
- ParentWindow.PropertyChanged += (sender, e) =>
- {
- if (e.PropertyName is nameof(ParentWindow.LayerIndexStart) or nameof(ParentWindow.LayerIndexEnd))
- {
- ParentWindow.LayerIndexEnd = Operation.LayerIndexStart + Operation.LayerCount - 1;
- }
- };
- break;
- }
-
+ }
+ };
+ break;
}
+
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs
index e3ee20e..ee987ee 100644
--- a/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolFlipControl.axaml.cs
@@ -1,22 +1,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolFlipControl : ToolControl
{
- public class ToolFlipControl : ToolControl
- {
- public OperationFlip Operation => BaseOperation as OperationFlip;
+ public OperationFlip Operation => BaseOperation as OperationFlip;
- public ToolFlipControl()
- {
- BaseOperation = new OperationFlip(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolFlipControl()
+ {
+ BaseOperation = new OperationFlip(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolIPrintedThisFileControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolIPrintedThisFileControl.axaml.cs
index 9a751b3..ad60b0f 100644
--- a/UVtools.WPF/Controls/Tools/ToolIPrintedThisFileControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolIPrintedThisFileControl.axaml.cs
@@ -1,22 +1,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolIPrintedThisFileControl : ToolControl
{
- public class ToolIPrintedThisFileControl : ToolControl
- {
- public OperationIPrintedThisFile Operation => BaseOperation as OperationIPrintedThisFile;
+ public OperationIPrintedThisFile Operation => BaseOperation as OperationIPrintedThisFile;
- public ToolIPrintedThisFileControl()
- {
- BaseOperation = new OperationIPrintedThisFile(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolIPrintedThisFileControl()
+ {
+ BaseOperation = new OperationIPrintedThisFile(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs
index e199f59..f31b7cf 100644
--- a/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolInfillControl.axaml.cs
@@ -1,22 +1,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolInfillControl : ToolControl
{
- public class ToolInfillControl : ToolControl
- {
- public OperationInfill Operation => BaseOperation as OperationInfill;
+ public OperationInfill Operation => BaseOperation as OperationInfill;
- public ToolInfillControl()
- {
- BaseOperation = new OperationInfill(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolInfillControl()
+ {
+ BaseOperation = new OperationInfill(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs
index 1977fec..ed17629 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs
@@ -2,40 +2,39 @@
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolLayerArithmeticControl : ToolControl
{
- public class ToolLayerArithmeticControl : ToolControl
- {
- public OperationLayerArithmetic Operation => BaseOperation as OperationLayerArithmetic;
+ public OperationLayerArithmetic Operation => BaseOperation as OperationLayerArithmetic;
- public ToolLayerArithmeticControl()
- {
- BaseOperation = new OperationLayerArithmetic(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolLayerArithmeticControl()
+ {
+ BaseOperation = new OperationLayerArithmetic(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.Sentence);
- Operation.PropertyChanged += (sender, e) =>
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.Sentence);
+ Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName == nameof(Operation.Sentence))
{
- if (e.PropertyName == nameof(Operation.Sentence))
- {
- ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.Sentence);
- }
- };
- break;
- }
+ ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.Sentence);
+ }
+ };
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs
index 055afd9..352a7d5 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerCloneControl.axaml.cs
@@ -3,56 +3,55 @@ using UVtools.Core.Layers;
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolLayerCloneControl : ToolControl
{
- public class ToolLayerCloneControl : ToolControl
- {
- public OperationLayerClone Operation => BaseOperation as OperationLayerClone;
+ public OperationLayerClone Operation => BaseOperation as OperationLayerClone;
- public string InfoLayersStr
+ public string InfoLayersStr
+ {
+ get
{
- get
- {
- uint extraLayers = Operation.ExtraLayers;
- return $"Layers: {App.SlicerFile.LayerCount} → {SlicerFile.LayerCount + extraLayers} (+ {extraLayers})";
- }
+ uint extraLayers = Operation.ExtraLayers;
+ return $"Layers: {App.SlicerFile.LayerCount} → {SlicerFile.LayerCount + extraLayers} (+ {extraLayers})";
}
+ }
- public string InfoHeightsStr
+ public string InfoHeightsStr
+ {
+ get
{
- get
- {
- float extraHeight = Operation.KeepSamePositionZ ? 0 : Layer.RoundHeight(Operation.ExtraLayers * SlicerFile.LayerHeight);
- return $"Height: {App.SlicerFile.PrintHeight}mm → {Layer.RoundHeight(SlicerFile.PrintHeight + extraHeight)}mm (+ {extraHeight}mm)";
- }
+ float extraHeight = Operation.KeepSamePositionZ ? 0 : Layer.RoundHeight(Operation.ExtraLayers * SlicerFile.LayerHeight);
+ return $"Height: {App.SlicerFile.PrintHeight}mm → {Layer.RoundHeight(SlicerFile.PrintHeight + extraHeight)}mm (+ {extraHeight}mm)";
}
+ }
- public ToolLayerCloneControl()
- {
- BaseOperation = new OperationLayerClone(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolLayerCloneControl()
+ {
+ BaseOperation = new OperationLayerClone(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, args) =>
- {
- RaisePropertyChanged(nameof(InfoLayersStr));
- RaisePropertyChanged(nameof(InfoHeightsStr));
- };
- break;
- }
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, args) =>
+ {
+ RaisePropertyChanged(nameof(InfoLayersStr));
+ RaisePropertyChanged(nameof(InfoHeightsStr));
+ };
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml
index 218fe5d..7a8787a 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolLayerExportGifControl">
@@ -17,9 +18,8 @@
Text="{Binding Operation.FilePath}"/>
<Button
VerticalAlignment="Stretch"
- Command="{Binding ChooseFilePath}">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ Command="{Binding ChooseFilePath}"
+ i:Attached.Icon="fas fa-folder"/>
</StackPanel>
<CheckBox
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs
index e998a5e..047966e 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs
@@ -4,41 +4,40 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolLayerExportGifControl : ToolControl
{
- public class ToolLayerExportGifControl : ToolControl
+ public OperationLayerExportGif Operation => BaseOperation as OperationLayerExportGif;
+ public ToolLayerExportGifControl()
{
- public OperationLayerExportGif Operation => BaseOperation as OperationLayerExportGif;
- public ToolLayerExportGifControl()
- {
- BaseOperation = new OperationLayerExportGif(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationLayerExportGif(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public async void ChooseFilePath()
+ public async void ChooseFilePath()
+ {
+ var dialog = new SaveFileDialog
{
- var dialog = new SaveFileDialog
+ Filters = new List<FileDialogFilter>
{
- Filters = new List<FileDialogFilter>
+ new()
{
- new()
- {
- Extensions = new List<string>{"gif"},
- Name = "GIF files"
- }
- },
- InitialFileName = Path.GetFileName(SlicerFile.FileFullPath)+".gif",
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath)
- };
- var file = await dialog.ShowAsync(ParentWindow);
- if (string.IsNullOrWhiteSpace(file)) return;
- Operation.FilePath = file;
- }
+ Extensions = new List<string>{"gif"},
+ Name = "GIF files"
+ }
+ },
+ InitialFileName = Path.GetFileName(SlicerFile.FileFullPath)+".gif",
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath)
+ };
+ var file = await dialog.ShowAsync(ParentWindow);
+ if (string.IsNullOrWhiteSpace(file)) return;
+ Operation.FilePath = file;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml
index d5c3402..aabcdf3 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolLayerExportHeatMapControl">
@@ -15,11 +16,10 @@
IsReadOnly="True"
Width="600"
Text="{Binding Operation.FilePath}"/>
- <Button
- VerticalAlignment="Stretch"
- Command="{Binding ChooseFilePath}">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ <Button
+ VerticalAlignment="Stretch"
+ Command="{Binding ChooseFilePath}"
+ i:Attached.Icon="fas fa-folder"/>
</StackPanel>
<Grid RowDefinitions="Auto,10,Auto"
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs
index b3bf58c..549cfec 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs
@@ -5,34 +5,33 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolLayerExportHeatMapControl : ToolControl
{
- public partial class ToolLayerExportHeatMapControl : ToolControl
+ public OperationLayerExportHeatMap Operation => BaseOperation as OperationLayerExportHeatMap;
+ public ToolLayerExportHeatMapControl()
{
- public OperationLayerExportHeatMap Operation => BaseOperation as OperationLayerExportHeatMap;
- public ToolLayerExportHeatMapControl()
- {
- BaseOperation = new OperationLayerExportHeatMap(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationLayerExportHeatMap(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public async void ChooseFilePath()
+ public async void ChooseFilePath()
+ {
+ var dialog = new SaveFileDialog
{
- var dialog = new SaveFileDialog
- {
- Filters = Helpers.ImagesFullFileFilter,
- InitialFileName = Path.GetFileName(SlicerFile.FileFullPath) + ".heatmap.png",
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
- };
- var file = await dialog.ShowAsync(ParentWindow);
- if (string.IsNullOrWhiteSpace(file)) return;
- Operation.FilePath = file;
- }
+ Filters = Helpers.ImagesFullFileFilter,
+ InitialFileName = Path.GetFileName(SlicerFile.FileFullPath) + ".heatmap.png",
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
+ };
+ var file = await dialog.ShowAsync(ParentWindow);
+ if (string.IsNullOrWhiteSpace(file)) return;
+ Operation.FilePath = file;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml
index 9eb3d9d..ee7a872 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolLayerExportImageControl">
<StackPanel Orientation="Vertical" Spacing="10">
@@ -16,9 +17,8 @@
Text="{Binding Operation.OutputFolder}"/>
<Button Grid.Row="0" Grid.Column="4"
VerticalAlignment="Stretch"
- Command="{Binding ChooseFolder}">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ Command="{Binding ChooseFolder}"
+ i:Attached.Icon="fas fa-folder"/>
<TextBlock Grid.Row="2" Grid.Column="0"
VerticalAlignment="Center"
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs
index 08f5df5..4f421e8 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs
@@ -3,32 +3,31 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolLayerExportImageControl : ToolControl
{
- public partial class ToolLayerExportImageControl : ToolControl
+ public OperationLayerExportImage Operation => BaseOperation as OperationLayerExportImage;
+ public ToolLayerExportImageControl()
{
- public OperationLayerExportImage Operation => BaseOperation as OperationLayerExportImage;
- public ToolLayerExportImageControl()
- {
- BaseOperation = new OperationLayerExportImage(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationLayerExportImage(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public async void ChooseFolder()
+ public async void ChooseFolder()
+ {
+ var dialog = new OpenFolderDialog
{
- var dialog = new OpenFolderDialog
- {
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
- };
- var folder = await dialog.ShowAsync(ParentWindow);
- if (string.IsNullOrWhiteSpace(folder)) return;
- Operation.OutputFolder = folder;
- }
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
+ };
+ var folder = await dialog.ShowAsync(ParentWindow);
+ if (string.IsNullOrWhiteSpace(folder)) return;
+ Operation.OutputFolder = folder;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml
index efe8f58..ecc372a 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolLayerExportMeshControl">
<StackPanel Spacing="10">
@@ -16,9 +17,8 @@
Text="{Binding Operation.FilePath}"/>
<Button
VerticalAlignment="Stretch"
- Command="{Binding ChooseFilePath}">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ Command="{Binding ChooseFilePath}"
+ i:Attached.Icon="fas fa-folder"/>
</StackPanel>
<Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto"
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs
index 1edb781..15dbaa5 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs
@@ -6,50 +6,49 @@ using Avalonia.Markup.Xaml;
using UVtools.Core.MeshFormats;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolLayerExportMeshControl : ToolControl
{
- public partial class ToolLayerExportMeshControl : ToolControl
+ public OperationLayerExportMesh Operation => BaseOperation as OperationLayerExportMesh;
+ public ToolLayerExportMeshControl()
{
- public OperationLayerExportMesh Operation => BaseOperation as OperationLayerExportMesh;
- public ToolLayerExportMeshControl()
- {
- BaseOperation = new OperationLayerExportMesh(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationLayerExportMesh(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public async void ChooseFilePath()
+ public async void ChooseFilePath()
+ {
+ var dialog = new SaveFileDialog
{
- var dialog = new SaveFileDialog
- {
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
- DefaultExtension = ".stl",
- InitialFileName = SlicerFile.FilenameNoExt,
- Filters = GetFilters(),
- };
- var filePath = await dialog.ShowAsync(ParentWindow);
- if (string.IsNullOrWhiteSpace(filePath)) return;
- Operation.FilePath = filePath;
- }
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
+ DefaultExtension = ".stl",
+ InitialFileName = SlicerFile.FilenameNoExt,
+ Filters = GetFilters(),
+ };
+ var filePath = await dialog.ShowAsync(ParentWindow);
+ if (string.IsNullOrWhiteSpace(filePath)) return;
+ Operation.FilePath = filePath;
+ }
- public List<FileDialogFilter> GetFilters()
+ public List<FileDialogFilter> GetFilters()
+ {
+ var list = new List<FileDialogFilter>();
+ foreach (var fileExtension in MeshFile.AvailableMeshFiles)
{
- var list = new List<FileDialogFilter>();
- foreach (var fileExtension in MeshFile.AvailableMeshFiles)
+ list.Add(new FileDialogFilter
{
- list.Add(new FileDialogFilter
- {
- Name = fileExtension.Description,
- Extensions = new List<string>{fileExtension.Extension}
- });
- }
-
- return list;
+ Name = fileExtension.Description,
+ Extensions = new List<string>{fileExtension.Extension}
+ });
}
+
+ return list;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml
index df056b9..5151bf5 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolLayerExportSkeletonControl">
<StackPanel Spacing="10">
@@ -12,13 +13,12 @@
UseFloatingWatermark="True"
VerticalAlignment="Center"
IsReadOnly="True"
- Width="500"
+ Width="600"
Text="{Binding Operation.FilePath}"/>
<Button
VerticalAlignment="Stretch"
- Command="{Binding ChooseFilePath}">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ Command="{Binding ChooseFilePath}"
+ i:Attached.Icon="fas fa-folder"/>
</StackPanel>
<CheckBox
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs
index 653c304..4931f97 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs
@@ -3,34 +3,33 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolLayerExportSkeletonControl : ToolControl
{
- public partial class ToolLayerExportSkeletonControl : ToolControl
+ public OperationLayerExportSkeleton Operation => BaseOperation as OperationLayerExportSkeleton;
+ public ToolLayerExportSkeletonControl()
{
- public OperationLayerExportSkeleton Operation => BaseOperation as OperationLayerExportSkeleton;
- public ToolLayerExportSkeletonControl()
- {
- BaseOperation = new OperationLayerExportSkeleton(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationLayerExportSkeleton(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public async void ChooseFilePath()
+ public async void ChooseFilePath()
+ {
+ var dialog = new SaveFileDialog
{
- var dialog = new SaveFileDialog
- {
- Filters = Helpers.ImagesFullFileFilter,
- InitialFileName = Path.GetFileName(SlicerFile.FileFullPath) + ".skeleton.png",
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
- };
- var file = await dialog.ShowAsync(ParentWindow);
- if (string.IsNullOrWhiteSpace(file)) return;
- Operation.FilePath = file;
- }
+ Filters = Helpers.ImagesFullFileFilter,
+ InitialFileName = Path.GetFileName(SlicerFile.FileFullPath) + ".skeleton.png",
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
+ };
+ var file = await dialog.ShowAsync(ParentWindow);
+ if (string.IsNullOrWhiteSpace(file)) return;
+ Operation.FilePath = file;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml
index e06d729..8eec68b 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:controls="clr-namespace:UVtools.WPF.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolLayerImportControl">
@@ -82,43 +83,36 @@
<Border BorderBrush="Gray" BorderThickness="1" Padding="5">
<Grid>
<StackPanel Spacing="5" Orientation="Horizontal">
- <Button Padding="5" Command="{Binding AddFiles}">
- <StackPanel Spacing="5" Orientation="Horizontal">
- <TextBlock Text="Add"/>
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- </StackPanel>
- </Button>
- <Button
- Padding="5"
- IsEnabled="{Binding #FilesListBox.SelectedItems.Count}"
- Command="{Binding RemoveFiles}">
- <StackPanel Spacing="5" Orientation="Horizontal">
- <TextBlock Text="Remove"/>
- <Image Source="/Assets/Icons/minus-16x16.png"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Padding="5"
+ Command="{Binding AddFiles}"
+ Text="Add"
+ Spacing="5"
+ Icon="fas fa-plus"/>
+
+ <controls:ButtonWithIcon Padding="5"
+ IsEnabled="{Binding #FilesListBox.SelectedItems.Count}"
+ Command="{Binding RemoveFiles}"
+ Text="Remove"
+ Spacing="5"
+ Icon="fas fa-minus"/>
</StackPanel>
<StackPanel
HorizontalAlignment="Right"
Spacing="5" Orientation="Horizontal">
- <Button Padding="5"
+ <controls:ButtonWithIcon Padding="5"
IsEnabled="{Binding Operation.Files.Count}"
- Command="{Binding Operation.Sort}">
- <StackPanel Spacing="5" Orientation="Horizontal">
- <TextBlock Text="Sort by file name"/>
- <Image Source="/Assets/Icons/sort-alpha-up-16x16.png"/>
- </StackPanel>
- </Button>
-
- <Button
+ Command="{Binding Operation.Sort}"
+ Text="Sort by file name"
+ Spacing="5"
+ Icon="fas fa-sort-alpha-up"/>
+
+ <controls:ButtonWithIcon
IsEnabled="{Binding Operation.Files.Count}"
- Padding="5" Command="{Binding ClearFiles}">
- <StackPanel Spacing="5" Orientation="Horizontal">
- <TextBlock Text="Clear"/>
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- </StackPanel>
- </Button>
+ Padding="5" Command="{Binding ClearFiles}"
+ Text="Clear"
+ Spacing="5"
+ Icon="fas fa-times"/>
<TextBlock
VerticalAlignment="Center"
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
index 089a2e4..4b213a2 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
@@ -11,197 +11,196 @@ using UVtools.Core.Operations;
using UVtools.Core.SystemOS;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolLayerImportControl : ToolControl
{
- public class ToolLayerImportControl : ToolControl
- {
- private bool _isAutoSortLayersByFileNameChecked;
- private ValueDescription _selectedFile;
- private Bitmap _previewImage;
+ private bool _isAutoSortLayersByFileNameChecked;
+ private ValueDescription _selectedFile;
+ private Bitmap _previewImage;
- private ListBox FilesListBox;
+ private ListBox FilesListBox;
- public OperationLayerImport Operation => BaseOperation as OperationLayerImport;
+ public OperationLayerImport Operation => BaseOperation as OperationLayerImport;
- public uint MaximumLayer => App.SlicerFile.LastLayerIndex;
+ public uint MaximumLayer => App.SlicerFile.LastLayerIndex;
- public string InfoLayerHeightStr => $"({App.SlicerFile.GetHeightFromLayer(Operation.StartLayerIndex)}mm)";
+ public string InfoLayerHeightStr => $"({App.SlicerFile.GetHeightFromLayer(Operation.StartLayerIndex)}mm)";
- public bool IsAutoSortLayersByFileNameChecked
- {
- get => _isAutoSortLayersByFileNameChecked;
- set => RaiseAndSetIfChanged(ref _isAutoSortLayersByFileNameChecked, value);
- }
+ public bool IsAutoSortLayersByFileNameChecked
+ {
+ get => _isAutoSortLayersByFileNameChecked;
+ set => RaiseAndSetIfChanged(ref _isAutoSortLayersByFileNameChecked, value);
+ }
- public string InfoImportResult
+ public string InfoImportResult
+ {
+ get
{
- get
+ if (Operation.Files.Count <= 0) return null;
+ /*uint modelTotalLayers = (uint) Operation.Files.Count;//Operation.CalculateTotalLayers(App.SlicerFile.LayerCount);
+ string textFactor = "grow";
+ if (modelTotalLayers < App.SlicerFile.LayerCount)
{
- if (Operation.Files.Count <= 0) return null;
- /*uint modelTotalLayers = (uint) Operation.Files.Count;//Operation.CalculateTotalLayers(App.SlicerFile.LayerCount);
- string textFactor = "grow";
- if (modelTotalLayers < App.SlicerFile.LayerCount)
- {
- textFactor = "shrink";
- }
- else if (modelTotalLayers == App.SlicerFile.LayerCount)
- {
- textFactor = "keep";
- }*/
-
- return
- $"{Operation.Files.Count} files will be imported into model starting from layer {Operation.StartLayerIndex} {InfoLayerHeightStr}.";
- //$"Model will {textFactor} from layers {App.SlicerFile.LayerCount} ({App.SlicerFile.TotalHeight}mm) to {modelTotalLayers} ({App.SlicerFile.GetHeightFromLayer(modelTotalLayers, false)}mm)";
+ textFactor = "shrink";
}
-
+ else if (modelTotalLayers == App.SlicerFile.LayerCount)
+ {
+ textFactor = "keep";
+ }*/
+
+ return
+ $"{Operation.Files.Count} files will be imported into model starting from layer {Operation.StartLayerIndex} {InfoLayerHeightStr}.";
+ //$"Model will {textFactor} from layers {App.SlicerFile.LayerCount} ({App.SlicerFile.TotalHeight}mm) to {modelTotalLayers} ({App.SlicerFile.GetHeightFromLayer(modelTotalLayers, false)}mm)";
}
+
+ }
- public ValueDescription SelectedFile
+ public ValueDescription SelectedFile
+ {
+ get => _selectedFile;
+ set
{
- get => _selectedFile;
- set
+ if(!RaiseAndSetIfChanged(ref _selectedFile, value)) return;
+ if (_selectedFile is null)
{
- if(!RaiseAndSetIfChanged(ref _selectedFile, value)) return;
- if (_selectedFile is null)
- {
- PreviewImage = null;
- return;
- }
- if (!_selectedFile.ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
- !_selectedFile.ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
- !_selectedFile.ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) &&
- !_selectedFile.ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) &&
- !_selectedFile.ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) return;
- PreviewImage = new Bitmap(_selectedFile.ValueAsString);
+ PreviewImage = null;
+ return;
}
+ if (!_selectedFile.ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
+ !_selectedFile.ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
+ !_selectedFile.ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) &&
+ !_selectedFile.ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) &&
+ !_selectedFile.ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) return;
+ PreviewImage = new Bitmap(_selectedFile.ValueAsString);
}
+ }
- public Bitmap PreviewImage
- {
- get => _previewImage;
- set => RaiseAndSetIfChanged(ref _previewImage, value);
- }
-
+ public Bitmap PreviewImage
+ {
+ get => _previewImage;
+ set => RaiseAndSetIfChanged(ref _previewImage, value);
+ }
- public ToolLayerImportControl()
- {
- BaseOperation = new OperationLayerImport(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- FilesListBox = this.Find<ListBox>("FilesListBox");
- FilesListBox.DoubleTapped += (sender, args) =>
- {
- if (FilesListBox.SelectedItem is not ValueDescription file) return;
- SystemAware.StartProcess(file.ValueAsString);
- };
- FilesListBox.KeyUp += (sender, e) =>
- {
- switch (e.Key)
- {
- case Key.Escape:
- FilesListBox.SelectedItems.Clear();
- e.Handled = true;
- break;
- case Key.Delete:
- RemoveFiles();
- e.Handled = true;
- break;
- case Key.A:
- if ((e.KeyModifiers & KeyModifiers.Control) != 0)
- {
- FilesListBox.SelectAll();
- e.Handled = true;
- }
- break;
- }
- };
- }
+ public ToolLayerImportControl()
+ {
+ BaseOperation = new OperationLayerImport(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- private void InitializeComponent()
+ FilesListBox = this.Find<ListBox>("FilesListBox");
+ FilesListBox.DoubleTapped += (sender, args) =>
{
- AvaloniaXamlLoader.Load(this);
- }
-
- /*public override async Task<bool> ValidateForm()
+ if (FilesListBox.SelectedItem is not ValueDescription file) return;
+ SystemAware.StartProcess(file.ValueAsString);
+ };
+ FilesListBox.KeyUp += (sender, e) =>
{
- UpdateOperation();
- var message = Operation.Validate();
- if (message is null) return true;
-
-
- message.Content += "\nDo you want to remove all invalid files from list?";
- if (await ParentWindow.MessageBoxQuestion(message.ToString()) == ButtonResult.Yes)
+ switch (e.Key)
{
- ConcurrentBag<StringTag> result = (ConcurrentBag<StringTag>)message.Tag;
- foreach (var file in result)
- {
- Operation.Files.Remove(file);
- }
+ case Key.Escape:
+ FilesListBox.SelectedItems.Clear();
+ e.Handled = true;
+ break;
+ case Key.Delete:
+ RemoveFiles();
+ e.Handled = true;
+ break;
+ case Key.A:
+ if ((e.KeyModifiers & KeyModifiers.Control) != 0)
+ {
+ FilesListBox.SelectAll();
+ e.Handled = true;
+ }
+ break;
}
+ };
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ /*public override async Task<bool> ValidateForm()
+ {
+ UpdateOperation();
+ var message = Operation.Validate();
+ if (message is null) return true;
- return false;
- }*/
- public override void Callback(ToolWindow.Callbacks callback)
+ message.Content += "\nDo you want to remove all invalid files from list?";
+ if (await ParentWindow.MessageBoxQuestion(message.ToString()) == ButtonResult.Yes)
{
- switch (callback)
+ ConcurrentBag<StringTag> result = (ConcurrentBag<StringTag>)message.Tag;
+ foreach (var file in result)
{
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- RefreshGUI();
- Operation.Files.CollectionChanged += (sender, args) => RefreshGUI();
- Operation.PropertyChanged += (sender, args) => RefreshGUI();
- break;
+ Operation.Files.Remove(file);
}
}
- public void RefreshGUI()
- {
- RaisePropertyChanged(nameof(InfoLayerHeightStr));
- RaisePropertyChanged(nameof(InfoImportResult));
- if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.Files.Count > 0;
- }
+ return false;
+ }*/
- public async void AddFiles()
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia);
- var orderedFilters = new List<FileDialogFilter> { filters[UserSettings.Instance.General.DefaultOpenFileExtensionIndex] };
- for (int i = 0; i < filters.Count; i++)
- {
- if (i == UserSettings.Instance.General.DefaultOpenFileExtensionIndex) continue;
- orderedFilters.Add(filters[i]);
- }
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ RefreshGUI();
+ Operation.Files.CollectionChanged += (sender, args) => RefreshGUI();
+ Operation.PropertyChanged += (sender, args) => RefreshGUI();
+ break;
+ }
+ }
- var dialog = new OpenFileDialog
- {
- AllowMultiple = true,
- Filters = orderedFilters,
- Directory = UserSettings.Instance.General.DefaultDirectoryOpenFile
- };
-
- var files = await dialog.ShowAsync(ParentWindow);
- if (files is null || files.Length == 0) return;
- foreach (var filename in files)
- {
- Operation.AddFile(filename);
- }
+ public void RefreshGUI()
+ {
+ RaisePropertyChanged(nameof(InfoLayerHeightStr));
+ RaisePropertyChanged(nameof(InfoImportResult));
+ if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.Files.Count > 0;
+ }
- if (_isAutoSortLayersByFileNameChecked)
- {
- Operation.Sort();
- }
+ public async void AddFiles()
+ {
+ var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia);
+ var orderedFilters = new List<FileDialogFilter> { filters[UserSettings.Instance.General.DefaultOpenFileExtensionIndex] };
+ for (int i = 0; i < filters.Count; i++)
+ {
+ if (i == UserSettings.Instance.General.DefaultOpenFileExtensionIndex) continue;
+ orderedFilters.Add(filters[i]);
}
- public void RemoveFiles()
+ var dialog = new OpenFileDialog
+ {
+ AllowMultiple = true,
+ Filters = orderedFilters,
+ Directory = UserSettings.Instance.General.DefaultDirectoryOpenFile
+ };
+
+ var files = await dialog.ShowAsync(ParentWindow);
+ if (files is null || files.Length == 0) return;
+ foreach (var filename in files)
{
- Operation.Files.RemoveRange(FilesListBox.SelectedItems.OfType<ValueDescription>());
+ Operation.AddFile(filename);
}
- public void ClearFiles()
+ if (_isAutoSortLayersByFileNameChecked)
{
- Operation.Files.Clear();
- PreviewImage = null;
+ Operation.Sort();
}
}
-}
+
+ public void RemoveFiles()
+ {
+ Operation.Files.RemoveRange(FilesListBox.SelectedItems.OfType<ValueDescription>());
+ }
+
+ public void ClearFiles()
+ {
+ Operation.Files.Clear();
+ PreviewImage = null;
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml
index d0619f1..f2e2e4b 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml
@@ -7,40 +7,76 @@
<StackPanel Orientation="Vertical" Spacing="10">
- <TextBlock VerticalAlignment="Center" Text="{Binding CurrentLayers}"/>
+ <TextBlock VerticalAlignment="Center"
+ IsVisible="{Binding !Operation.Method}"
+ Text="{Binding CurrentLayers}"/>
- <Grid RowDefinitions="Auto,10,Auto,10,Auto"
+ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto"
ColumnDefinitions="Auto,10,Auto,20,Auto,10,Auto">
- <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="Modifier:"/>
+ <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="Method:"/>
+ <ComboBox Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="5"
+ Width="505"
+ Items="{Binding Operation.Method, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Operation.Method, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ IsVisible="{Binding Operation.IsOffsetPositionZMethod}"
+ VerticalAlignment="Center"
+ Text="Position offset:"/>
+ <NumericUpDown Grid.Row="2" Grid.Column="2"
+ Classes="ValueLabel ValueLabel_mm"
+ IsVisible="{Binding Operation.IsOffsetPositionZMethod}"
+ VerticalAlignment="Center"
+ Minimum="-1000"
+ Maximum="1000"
+ Increment="0.01"
+ FormatString="F3"
+ Value="{Binding Operation.PositionZOffset}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
+ Text="Modifier:"/>
<ComboBox
- Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="5"
+ Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="5"
Width="505"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
SelectedItem="{Binding Operation.SelectedItem}"
Items="{Binding Operation.Presets}"/>
- <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Text="Anti-Aliasing:"/>
- <ComboBox Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="5"
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
+ Text="Anti-Aliasing:"/>
+ <ComboBox Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="5"
Width="505"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
IsEnabled="{Binding Operation.CanAntiAliasing}"
Items="{Binding Operation.AntiAliasingType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
SelectedItem="{Binding Operation.AntiAliasingType, Converter={StaticResource FromValueDescriptionToEnumConverter}}">
</ComboBox>
- <TextBlock Grid.Row="4" Grid.Column="0"
+ <TextBlock Grid.Row="6" Grid.Column="0"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
VerticalAlignment="Center"
Text="Bottom exposure:"/>
- <NumericUpDown Grid.Row="4" Grid.Column="2"
+ <NumericUpDown Grid.Row="6" Grid.Column="2"
Classes="ValueLabel ValueLabel_s"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
VerticalAlignment="Center"
Minimum="0.01"
Maximum="1000"
Increment="0.5"
Value="{Binding Operation.BottomExposure}"/>
- <TextBlock Grid.Row="4" Grid.Column="4" VerticalAlignment="Center" Text="Normal exposure:"/>
- <NumericUpDown Grid.Row="4" Grid.Column="6"
+ <TextBlock Grid.Row="6" Grid.Column="4"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
+ VerticalAlignment="Center"
+ Text="Normal exposure:"/>
+ <NumericUpDown Grid.Row="6" Grid.Column="6"
Classes="ValueLabel ValueLabel_s"
+ IsVisible="{Binding Operation.IsReHeightMethod}"
VerticalAlignment="Center"
Minimum="0.01"
Maximum="1000"
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs
index 42d9582..95e8afa 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs
@@ -1,25 +1,45 @@
-using Avalonia.Markup.Xaml;
+using System;
+using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
+using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolLayerReHeightControl : ToolControl
{
- public class ToolLayerReHeightControl : ToolControl
- {
- public OperationLayerReHeight Operation => BaseOperation as OperationLayerReHeight;
+ public OperationLayerReHeight Operation => BaseOperation as OperationLayerReHeight;
- public string CurrentLayers => $"Current layers: {App.SlicerFile.LayerCount} at {App.SlicerFile.LayerHeight}mm";
+ public string CurrentLayers => $"Current layers: {App.SlicerFile.LayerCount} at {App.SlicerFile.LayerHeight}mm";
- public ToolLayerReHeightControl()
- {
- BaseOperation = new OperationLayerReHeight(SlicerFile);
- if (!ValidateSpawn()) return;
+ public ToolLayerReHeightControl()
+ {
+ BaseOperation = new OperationLayerReHeight(SlicerFile);
+ if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ InitializeComponent();
+ }
- private void InitializeComponent()
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- AvaloniaXamlLoader.Load(this);
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ if (ParentWindow is not null) ParentWindow.LayerRangeVisible = Operation.Method == OperationLayerReHeight.OperationLayerReHeightMethod.OffsetPositionZ;
+ Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName == nameof(Operation.Method))
+ {
+ ParentWindow.LayerRangeVisible = Operation.Method == OperationLayerReHeight.OperationLayerReHeightMethod.OffsetPositionZ;
+ return;
+ }
+ };
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml
index 2185926..6c00dda 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml
@@ -12,6 +12,23 @@
<TextBlock
VerticalAlignment="Center"
Text="{Binding InfoHeightsStr}"/>
+
+ <Grid RowDefinitions="Auto" ColumnDefinitions="Auto,10,200">
+ <CheckBox Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="If enabled, use a pixel threshold, where all layers with less or equal amount of pixels will be removed.
+&#x0a;Note: You still need to input a layer range, select all layers for a better effect with this mode."
+ Content="Pixel threshold:"
+ IsChecked="{Binding Operation.UseThreshold}"/>
+
+ <NumericUpDown Grid.Row="0" Grid.Column="2"
+ Classes="ValueLabel ValueLabel_px"
+ VerticalAlignment="Center"
+ Minimum="0"
+ Increment="1"
+ IsEnabled="{Binding Operation.UseThreshold}"
+ Value="{Binding Operation.PixelThreshold}"/>
+ </Grid>
</StackPanel>
</UserControl>
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs
index add49e8..38a7941 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs
@@ -5,57 +5,61 @@ using UVtools.Core.Layers;
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
-{
- public class ToolLayerRemoveControl : ToolControl
- {
- public OperationLayerRemove Operation => BaseOperation as OperationLayerRemove;
+namespace UVtools.WPF.Controls.Tools;
- public uint ExtraLayers => (uint)Math.Max(0, (int)Operation.LayerIndexEnd - Operation.LayerIndexStart + 1);
+public class ToolLayerRemoveControl : ToolControl
+{
+ public OperationLayerRemove Operation => BaseOperation as OperationLayerRemove;
- public string InfoLayersStr
+ public string InfoLayersStr
+ {
+ get
{
- get
- {
- uint extraLayers = ExtraLayers;
- return $"Layers: {SlicerFile.LayerCount} → {SlicerFile.LayerCount - extraLayers} (- {extraLayers})";
- }
+ uint extraLayers = Operation.LayerRemoveCount;
+ return $"Layers: {SlicerFile.LayerCount} → {SlicerFile.LayerCount - extraLayers} (- {extraLayers})";
}
+ }
- public string InfoHeightsStr
+ public string InfoHeightsStr
+ {
+ get
{
- get
+ float extraHeight = 0;
+ for (uint layerIndex = Operation.LayerIndexStart; layerIndex <= Operation.LayerIndexEnd; layerIndex++)
{
- float extraHeight = Layer.RoundHeight(ExtraLayers * SlicerFile.LayerHeight);
- return $"Height: {SlicerFile.PrintHeight}mm → {Layer.RoundHeight(App.SlicerFile.PrintHeight - extraHeight)}mm (- {extraHeight}mm)";
+ if (Operation.UseThreshold && SlicerFile[layerIndex].NonZeroPixelCount > Operation.PixelThreshold) continue;
+ extraHeight += SlicerFile[layerIndex].RelativePositionZ;
}
- }
- public ToolLayerRemoveControl()
- {
- BaseOperation = new OperationLayerRemove(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
+ extraHeight = Layer.RoundHeight(extraHeight);
+ return $"Height: {SlicerFile.PrintHeight}mm → {Math.Max(0, Layer.RoundHeight(SlicerFile.PrintHeight - extraHeight))}mm (- {extraHeight}mm)";
}
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ public ToolLayerRemoveControl()
+ {
+ BaseOperation = new OperationLayerRemove(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.PropertyChanged += (sender, args) =>
- {
- RaisePropertyChanged(nameof(InfoLayersStr));
- RaisePropertyChanged(nameof(InfoHeightsStr));
- };
- break;
- }
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.PropertyChanged += (sender, args) =>
+ {
+ RaisePropertyChanged(nameof(InfoLayersStr));
+ RaisePropertyChanged(nameof(InfoHeightsStr));
+ };
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolLightBleedCompensationControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLightBleedCompensationControl.axaml.cs
index baa7825..e3b6b6a 100644
--- a/UVtools.WPF/Controls/Tools/ToolLightBleedCompensationControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLightBleedCompensationControl.axaml.cs
@@ -1,21 +1,20 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolLightBleedCompensationControl : ToolControl
{
- public partial class ToolLightBleedCompensationControl : ToolControl
+ public OperationLightBleedCompensation Operation => BaseOperation as OperationLightBleedCompensation;
+ public ToolLightBleedCompensationControl()
{
- public OperationLightBleedCompensation Operation => BaseOperation as OperationLightBleedCompensation;
- public ToolLightBleedCompensationControl()
- {
- BaseOperation = new OperationLightBleedCompensation(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationLightBleedCompensation(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
index 12499ee..16d5911 100644
--- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
@@ -11,162 +11,161 @@ using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
using Bitmap = Avalonia.Media.Imaging.Bitmap;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolMaskControl : ToolControl
{
- public class ToolMaskControl : ToolControl
+ private bool _isMaskInverted;
+ private byte _genMinimumBrightness = 200;
+ private byte _genMaximumBrightness = byte.MaxValue;
+ private uint _genDiameter;
+ private Bitmap _maskImage;
+ public OperationMask Operation => BaseOperation as OperationMask;
+
+ public bool IsMaskInverted
{
- private bool _isMaskInverted;
- private byte _genMinimumBrightness = 200;
- private byte _genMaximumBrightness = byte.MaxValue;
- private uint _genDiameter;
- private Bitmap _maskImage;
- public OperationMask Operation => BaseOperation as OperationMask;
-
- public bool IsMaskInverted
+ get => _isMaskInverted;
+ set
{
- get => _isMaskInverted;
- set
- {
- if(!RaiseAndSetIfChanged(ref _isMaskInverted, value)) return;
- if (!Operation.HaveInputMask) return;
- Operation.InvertMask();
- MaskImage = Operation.Mask.ToBitmap();
- }
+ if(!RaiseAndSetIfChanged(ref _isMaskInverted, value)) return;
+ if (!Operation.HaveInputMask) return;
+ Operation.InvertMask();
+ MaskImage = Operation.Mask.ToBitmap();
}
+ }
- public byte GenMinimumBrightness
- {
- get => _genMinimumBrightness;
- set => RaiseAndSetIfChanged(ref _genMinimumBrightness, value);
- }
+ public byte GenMinimumBrightness
+ {
+ get => _genMinimumBrightness;
+ set => RaiseAndSetIfChanged(ref _genMinimumBrightness, value);
+ }
- public byte GenMaximumBrightness
- {
- get => _genMaximumBrightness;
- set => RaiseAndSetIfChanged(ref _genMaximumBrightness, value);
- }
+ public byte GenMaximumBrightness
+ {
+ get => _genMaximumBrightness;
+ set => RaiseAndSetIfChanged(ref _genMaximumBrightness, value);
+ }
- public uint GenDiameter
- {
- get => _genDiameter;
- set => RaiseAndSetIfChanged(ref _genDiameter, value);
- }
+ public uint GenDiameter
+ {
+ get => _genDiameter;
+ set => RaiseAndSetIfChanged(ref _genDiameter, value);
+ }
- public string InfoPrinterResolutionStr => $"Printer resolution: {App.SlicerFile.Resolution}";
- public string InfoMaskResolutionStr => $"Mask resolution: "+ (Operation.HaveMask ? Operation.Mask.Size.ToString() : "(Unloaded)");
+ public string InfoPrinterResolutionStr => $"Printer resolution: {App.SlicerFile.Resolution}";
+ public string InfoMaskResolutionStr => $"Mask resolution: "+ (Operation.HaveMask ? Operation.Mask.Size.ToString() : "(Unloaded)");
- public Bitmap MaskImage
+ public Bitmap MaskImage
+ {
+ get => _maskImage;
+ set
{
- get => _maskImage;
- set
- {
- if(!RaiseAndSetIfChanged(ref _maskImage, value)) return;
- RaisePropertyChanged(nameof(InfoMaskResolutionStr));
- ParentWindow.ButtonOkEnabled = Operation.HaveInputMask;
- }
+ if(!RaiseAndSetIfChanged(ref _maskImage, value)) return;
+ RaisePropertyChanged(nameof(InfoMaskResolutionStr));
+ ParentWindow.ButtonOkEnabled = Operation.HaveInputMask;
}
+ }
- public ToolMaskControl()
- {
- BaseOperation = new OperationMask(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolMaskControl()
+ {
+ BaseOperation = new OperationMask(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- ParentWindow.ButtonOkEnabled = false;
- break;
- case ToolWindow.Callbacks.Loaded:
- case ToolWindow.Callbacks.ClearROI:
- Operation.Mask = null;
- MaskImage = null;
- break;
- }
+ case ToolWindow.Callbacks.Init:
+ ParentWindow.ButtonOkEnabled = false;
+ break;
+ case ToolWindow.Callbacks.Loaded:
+ case ToolWindow.Callbacks.ClearROI:
+ Operation.Mask = null;
+ MaskImage = null;
+ break;
}
+ }
- public async void ImportImageMask()
+ public async void ImportImageMask()
+ {
+ var dialog = new OpenFileDialog
{
- var dialog = new OpenFileDialog
- {
- Filters = Helpers.ImagesFileFilter,
- AllowMultiple = false
- };
+ Filters = Helpers.ImagesFileFilter,
+ AllowMultiple = false
+ };
- var result = await dialog.ShowAsync(ParentWindow);
- if (result is null || result.Length == 0) return;
+ var result = await dialog.ShowAsync(ParentWindow);
+ if (result is null || result.Length == 0) return;
- try
+ try
+ {
+ Operation.Mask = CvInvoke.Imread(result[0], ImreadModes.Grayscale);
+ var roi = App.MainWindow.ROI;
+ if (roi.IsEmpty)
{
- Operation.Mask = CvInvoke.Imread(result[0], ImreadModes.Grayscale);
- var roi = App.MainWindow.ROI;
- if (roi.IsEmpty)
+ if (Operation.Mask.Size != App.SlicerFile.Resolution)
{
- if (Operation.Mask.Size != App.SlicerFile.Resolution)
- {
- CvInvoke.Resize(Operation.Mask, Operation.Mask, App.SlicerFile.Resolution);
- }
+ CvInvoke.Resize(Operation.Mask, Operation.Mask, App.SlicerFile.Resolution);
}
- else
- {
- if (Operation.Mask.Size != roi.Size)
- {
- CvInvoke.Resize(Operation.Mask, Operation.Mask, roi.Size);
- }
- }
-
- if (_isMaskInverted)
+ }
+ else
+ {
+ if (Operation.Mask.Size != roi.Size)
{
- Operation.InvertMask();
+ CvInvoke.Resize(Operation.Mask, Operation.Mask, roi.Size);
}
-
- MaskImage = Operation.Mask.ToBitmap();
}
- catch (Exception e)
+
+ if (_isMaskInverted)
{
- await ParentWindow.MessageBoxError(e.ToString(), "Error while trying to read the image");
+ Operation.InvertMask();
}
- }
- public void GenerateMask()
+ MaskImage = Operation.Mask.ToBitmap();
+ }
+ catch (Exception e)
{
- var roi = App.MainWindow.ROI;
- Operation.Mask = roi.IsEmpty ? App.MainWindow.LayerCache.Image.NewBlank() : new Mat(roi.Size, DepthType.Cv8U, 1);
+ await ParentWindow.MessageBoxError(e.ToString(), "Error while trying to read the image");
+ }
+ }
- int radius = (int)_genDiameter;
- if (radius == 0)
- {
- radius = Math.Min(Operation.Mask.Width, Operation.Mask.Height) / 2;
- }
- else
- {
- radius = radius.Clamp(2, Math.Min(Operation.Mask.Width, Operation.Mask.Height)) / 2;
- }
+ public void GenerateMask()
+ {
+ var roi = App.MainWindow.ROI;
+ Operation.Mask = roi.IsEmpty ? App.MainWindow.LayerCache.Image.NewBlank() : new Mat(roi.Size, DepthType.Cv8U, 1);
- var maxScalar = new MCvScalar(_genMaximumBrightness);
- Operation.Mask.SetTo(maxScalar);
+ int radius = (int)_genDiameter;
+ if (radius == 0)
+ {
+ radius = Math.Min(Operation.Mask.Width, Operation.Mask.Height) / 2;
+ }
+ else
+ {
+ radius = radius.Clamp(2, Math.Min(Operation.Mask.Width, Operation.Mask.Height)) / 2;
+ }
- var center = new Point(Operation.Mask.Width / 2, Operation.Mask.Height / 2);
- var colorDifference = _genMinimumBrightness - _genMaximumBrightness;
- //CvInvoke.Circle(Mask, center, radius, minScalar, -1);
+ var maxScalar = new MCvScalar(_genMaximumBrightness);
+ Operation.Mask.SetTo(maxScalar);
- for (decimal i = 1; i < radius; i++)
- {
- int color = (int)(_genMinimumBrightness - i / radius * colorDifference); //or some another color calculation
- CvInvoke.Circle(Operation.Mask, center, (int)i, new MCvScalar(color), 2);
- }
+ var center = new Point(Operation.Mask.Width / 2, Operation.Mask.Height / 2);
+ var colorDifference = _genMinimumBrightness - _genMaximumBrightness;
+ //CvInvoke.Circle(Mask, center, radius, minScalar, -1);
- if (_isMaskInverted)
- Operation.InvertMask();
- MaskImage = Operation.Mask.ToBitmap();
+ for (decimal i = 1; i < radius; i++)
+ {
+ int color = (int)(_genMinimumBrightness - i / radius * colorDifference); //or some another color calculation
+ CvInvoke.Circle(Operation.Mask, center, (int)i, new MCvScalar(color), 2);
}
+
+ if (_isMaskInverted)
+ Operation.InvertMask();
+ MaskImage = Operation.Mask.ToBitmap();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs
index 4751879..1feb7c7 100644
--- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs
@@ -2,22 +2,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolMorphControl : ToolControl
{
- public class ToolMorphControl : ToolControl
- {
- public OperationMorph Operation => BaseOperation as OperationMorph;
+ public OperationMorph Operation => BaseOperation as OperationMorph;
- public ToolMorphControl()
- {
- BaseOperation = new OperationMorph(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolMorphControl()
+ {
+ BaseOperation = new OperationMorph(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs
index deb4634..8e2fb3b 100644
--- a/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMoveControl.axaml.cs
@@ -2,53 +2,52 @@
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolMoveControl : ToolControl
{
- public class ToolMoveControl : ToolControl
- {
- private bool _isMiddleCenterChecked = true;
- public OperationMove Operation => BaseOperation as OperationMove;
+ private bool _isMiddleCenterChecked = true;
+ public OperationMove Operation => BaseOperation as OperationMove;
- public bool IsMiddleCenterChecked
- {
- get => _isMiddleCenterChecked;
- set => RaiseAndSetIfChanged(ref _isMiddleCenterChecked, value);
- }
+ public bool IsMiddleCenterChecked
+ {
+ get => _isMiddleCenterChecked;
+ set => RaiseAndSetIfChanged(ref _isMiddleCenterChecked, value);
+ }
- public ToolMoveControl()
- {
- BaseOperation = new OperationMove(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
+ public ToolMoveControl()
+ {
+ BaseOperation = new OperationMove(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- Operation.PropertyChanged += (sender, e) =>
+ Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName.Equals(nameof(Operation.IsWithinBoundary)))
{
- if (e.PropertyName.Equals(nameof(Operation.IsWithinBoundary)))
- {
- ParentWindow.ButtonOkEnabled = Operation.IsWithinBoundary;
- }
- };
- }
+ ParentWindow.ButtonOkEnabled = Operation.IsWithinBoundary;
+ }
+ };
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.ROI = App.MainWindow.ROI.IsEmpty ? SlicerFile.BoundingRectangle : App.MainWindow.ROI;
- break;
- case ToolWindow.Callbacks.ClearROI:
- Operation.ROI = SlicerFile.BoundingRectangle;
- Operation.Reset();
- break;
- }
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.ROI = App.MainWindow.ROI.IsEmpty ? SlicerFile.BoundingRectangle : App.MainWindow.ROI;
+ break;
+ case ToolWindow.Callbacks.ClearROI:
+ Operation.ROI = SlicerFile.BoundingRectangle;
+ Operation.Reset();
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml
index f357225..7a4e71a 100644
--- a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolPatternControl">
@@ -46,9 +47,7 @@
Padding="10,5"
ToolTip.Tip="Fill spacing given the current number of columns, object size and left over space."
Command="{Binding Operation.FillColumnSpacing}"
- >
- <Image Source="/Assets/Icons/resize-16x16.png" />
- </Button>
+ i:Attached.Icon="fas fa-expand-alt"/>
<TextBlock
Grid.Column="10"
Grid.Row="0"
@@ -60,40 +59,34 @@
Grid.Row="2"
VerticalAlignment="Center"
HorizontalAlignment="Right"
- ToolTip.Tip="Number of copies in Y direction."
- />
+ ToolTip.Tip="Number of copies in Y direction."/>
<NumericUpDown
Grid.Column="2"
Grid.Row="2"
Minimum="1"
Maximum="{Binding Operation.MaxRows}"
Value="{Binding Operation.Rows}"
- ToolTip.Tip="Number of copies in Y direction."
- />
+ ToolTip.Tip="Number of copies in Y direction."/>
<TextBlock Text="Spacing:"
Grid.Column="4"
Grid.Row="2"
VerticalAlignment="Center"
HorizontalAlignment="Right"
- ToolTip.Tip="Spacing between copies in Y direction."
- />
+ ToolTip.Tip="Spacing between copies in Y direction."/>
<NumericUpDown
Grid.Column="6"
Grid.Row="2"
Minimum="1"
Maximum="65535"
Value="{Binding Operation.RowSpacing}"
- ToolTip.Tip="Spacing between copies in Y direction"
- />
+ ToolTip.Tip="Spacing between copies in Y direction"/>
<Button
Grid.Column="8"
Grid.Row="2"
Padding="10,5"
ToolTip.Tip="Fill spacing given the current number of rows, object size and left over space."
Command="{Binding Operation.FillRowSpacing}"
- >
- <Image Source="/Assets/Icons/resize-16x16.png" />
- </Button>
+ i:Attached.Icon="fas fa-expand-alt"/>
<TextBlock
Grid.Column="10"
Grid.Row="2"
@@ -102,10 +95,8 @@
</Grid>
- <Grid
- RowDefinitions="Auto"
- ColumnDefinitions="Auto,20,Auto"
- >
+ <Grid RowDefinitions="Auto"
+ ColumnDefinitions="Auto,20,Auto">
<StackPanel Spacing="10">
<TextBlock Text="{Binding Operation.InfoWidthStr}" />
diff --git a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs
index b98f605..59e6875 100644
--- a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs
@@ -4,50 +4,49 @@ using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolPatternControl : ToolControl
{
- public class ToolPatternControl : ToolControl
- {
- public OperationPattern Operation => BaseOperation as OperationPattern;
- private bool _isDefaultAnchorChecked = true;
+ public OperationPattern Operation => BaseOperation as OperationPattern;
+ private bool _isDefaultAnchorChecked = true;
- public bool IsDefaultAnchorChecked
- {
- get => _isDefaultAnchorChecked;
- set => RaiseAndSetIfChanged(ref _isDefaultAnchorChecked, value);
- }
+ public bool IsDefaultAnchorChecked
+ {
+ get => _isDefaultAnchorChecked;
+ set => RaiseAndSetIfChanged(ref _isDefaultAnchorChecked, value);
+ }
- public ToolPatternControl()
- {
- BaseOperation = new OperationPattern(SlicerFile, App.MainWindow.ROI);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolPatternControl()
+ {
+ BaseOperation = new OperationPattern(SlicerFile, App.MainWindow.ROI);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- Operation.ROI = App.MainWindow.ROI.IsEmpty ? SlicerFile.BoundingRectangle : App.MainWindow.ROI;
- Operation.PropertyChanged += (sender, e) =>
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ Operation.ROI = App.MainWindow.ROI.IsEmpty ? SlicerFile.BoundingRectangle : App.MainWindow.ROI;
+ Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName.Equals(nameof(Operation.IsWithinBoundary)))
{
- if (e.PropertyName.Equals(nameof(Operation.IsWithinBoundary)))
- {
- ParentWindow.ButtonOkEnabled = Operation.IsWithinBoundary;
- }
- };
- break;
- case ToolWindow.Callbacks.ClearROI:
- Operation.SetRoi(SlicerFile.BoundingRectangle);
- break;
- }
+ ParentWindow.ButtonOkEnabled = Operation.IsWithinBoundary;
+ }
+ };
+ break;
+ case ToolWindow.Callbacks.ClearROI:
+ Operation.SetRoi(SlicerFile.BoundingRectangle);
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs
index 2423dc7..791ada0 100644
--- a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs
@@ -2,41 +2,40 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolPixelArithmeticControl : ToolControl
{
- public partial class ToolPixelArithmeticControl : ToolControl
+ public OperationPixelArithmetic Operation => BaseOperation as OperationPixelArithmetic;
+ public ToolPixelArithmeticControl()
{
- public OperationPixelArithmetic Operation => BaseOperation as OperationPixelArithmetic;
- public ToolPixelArithmeticControl()
- {
- BaseOperation = new OperationPixelArithmetic(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
+ BaseOperation = new OperationPixelArithmetic(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- Operation.GeneratePattern("Chessboard");
- }
+ Operation.GeneratePattern("Chessboard");
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void PresetElephantFootCompensation()
- {
- ParentWindow.SelectBottomLayers();
- Operation.PresetElephantFootCompensation();
- }
+ public void PresetElephantFootCompensation()
+ {
+ ParentWindow.SelectBottomLayers();
+ Operation.PresetElephantFootCompensation();
+ }
- public async void LoadPatternFromImage(bool isAlternatePattern = false)
+ public async void LoadPatternFromImage(bool isAlternatePattern = false)
+ {
+ var dialog = new OpenFileDialog
{
- var dialog = new OpenFileDialog
- {
- AllowMultiple = false,
- Filters = Helpers.ImagesFileFilter,
- };
- var files = await dialog.ShowAsync(ParentWindow);
- if (files is null || files.Length == 0) return;
- Operation.LoadPatternFromImage(files[0], isAlternatePattern);
- }
+ AllowMultiple = false,
+ Filters = Helpers.ImagesFileFilter,
+ };
+ var files = await dialog.ShowAsync(ParentWindow);
+ if (files is null || files.Length == 0) return;
+ Operation.LoadPatternFromImage(files[0], isAlternatePattern);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs
index 17f02cf..846dd79 100644
--- a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs
@@ -2,37 +2,36 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolPixelDimmingControl : ToolControl
{
- public class ToolPixelDimmingControl : ToolControl
- {
- public OperationPixelDimming Operation => BaseOperation as OperationPixelDimming;
+ public OperationPixelDimming Operation => BaseOperation as OperationPixelDimming;
- public ToolPixelDimmingControl()
- {
- BaseOperation = new OperationPixelDimming(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
+ public ToolPixelDimmingControl()
+ {
+ BaseOperation = new OperationPixelDimming(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
- Operation.GeneratePixelDimming("Chessboard");
- }
+ Operation.GeneratePixelDimming("Chessboard");
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public async void LoadPatternFromImage(bool isAlternatePattern = false)
+ public async void LoadPatternFromImage(bool isAlternatePattern = false)
+ {
+ var dialog = new OpenFileDialog
{
- var dialog = new OpenFileDialog
- {
- AllowMultiple = false,
- Filters = Helpers.ImagesFileFilter,
- };
- var files = await dialog.ShowAsync(ParentWindow);
- if (files is null || files.Length == 0) return;
- Operation.LoadPatternFromImage(files[0], isAlternatePattern);
- }
+ AllowMultiple = false,
+ Filters = Helpers.ImagesFileFilter,
+ };
+ var files = await dialog.ShowAsync(ParentWindow);
+ if (files is null || files.Length == 0) return;
+ Operation.LoadPatternFromImage(files[0], isAlternatePattern);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs
index 1f51d70..9fa3c4f 100644
--- a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml.cs
@@ -1,27 +1,26 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolRaftReliefControl : ToolControl
{
- public class ToolRaftReliefControl : ToolControl
- {
- public OperationRaftRelief Operation => BaseOperation as OperationRaftRelief;
+ public OperationRaftRelief Operation => BaseOperation as OperationRaftRelief;
- public ToolRaftReliefControl()
- {
- BaseOperation = new OperationRaftRelief(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolRaftReliefControl()
+ {
+ BaseOperation = new OperationRaftRelief(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void UseCurrentLayerAsMask()
- {
- Operation.MaskLayerIndex = App.MainWindow.ActualLayer;
- }
+ public void UseCurrentLayerAsMask()
+ {
+ Operation.MaskLayerIndex = App.MainWindow.ActualLayer;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml.cs
index 82a9272..4b2fff5 100644
--- a/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml.cs
@@ -1,23 +1,22 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolRaiseOnPrintFinishControl : ToolControl
{
- public partial class ToolRaiseOnPrintFinishControl : ToolControl
- {
- public OperationRaiseOnPrintFinish Operation => BaseOperation as OperationRaiseOnPrintFinish;
+ public OperationRaiseOnPrintFinish Operation => BaseOperation as OperationRaiseOnPrintFinish;
- public ToolRaiseOnPrintFinishControl()
- {
- BaseOperation = new OperationRaiseOnPrintFinish(SlicerFile);
- if (!ValidateSpawn()) return;
+ public ToolRaiseOnPrintFinishControl()
+ {
+ BaseOperation = new OperationRaiseOnPrintFinish(SlicerFile);
+ if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml
index 0c91fea..8a1d55c 100644
--- a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolRedrawModelControl">
<Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto"
@@ -21,9 +22,8 @@
<Button
VerticalAlignment="Stretch"
ToolTip.Tip="Select the sliced file without supports and raft. (Model B)"
- Command="{Binding ImportFile}">
- <Image Source="/Assets/Icons/file-import-16x16.png"/>
- </Button>
+ Command="{Binding ImportFile}"
+ i:Attached.Icon="fas fa-file-import"/>
</StackPanel>
diff --git a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs
index 1b9664f..f845884 100644
--- a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs
@@ -5,61 +5,60 @@ using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolRedrawModelControl : ToolControl
{
- public class ToolRedrawModelControl : ToolControl
+ public OperationRedrawModel Operation => BaseOperation as OperationRedrawModel;
+ public ToolRedrawModelControl()
{
- public OperationRedrawModel Operation => BaseOperation as OperationRedrawModel;
- public ToolRedrawModelControl()
- {
- BaseOperation = new OperationRedrawModel(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationRedrawModel(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.FilePath);
- Operation.PropertyChanged += (sender, e) =>
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.FilePath);
+ Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName == nameof(Operation.FilePath))
{
- if (e.PropertyName == nameof(Operation.FilePath))
- {
- ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.FilePath);
- }
- };
- break;
- }
+ ParentWindow.ButtonOkEnabled = !string.IsNullOrWhiteSpace(Operation.FilePath);
+ }
+ };
+ break;
}
+ }
- public async void ImportFile()
+ public async void ImportFile()
+ {
+ var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia);
+ var orderedFilters = new List<FileDialogFilter> { filters[UserSettings.Instance.General.DefaultOpenFileExtensionIndex] };
+ for (int i = 0; i < filters.Count; i++)
{
- var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia);
- var orderedFilters = new List<FileDialogFilter> { filters[UserSettings.Instance.General.DefaultOpenFileExtensionIndex] };
- for (int i = 0; i < filters.Count; i++)
- {
- if (i == UserSettings.Instance.General.DefaultOpenFileExtensionIndex) continue;
- orderedFilters.Add(filters[i]);
- }
-
- var dialog = new OpenFileDialog
- {
- AllowMultiple = false,
- Filters = orderedFilters,
- Directory = UserSettings.Instance.General.DefaultDirectoryOpenFile
- };
- var files = await dialog.ShowAsync(ParentWindow);
- if (files is null || files.Length <= 0) return;
- if (FileFormat.FindByExtensionOrFilePath(files[0]) is null) return;
- Operation.FilePath = files[0];
+ if (i == UserSettings.Instance.General.DefaultOpenFileExtensionIndex) continue;
+ orderedFilters.Add(filters[i]);
}
+
+ var dialog = new OpenFileDialog
+ {
+ AllowMultiple = false,
+ Filters = orderedFilters,
+ Directory = UserSettings.Instance.General.DefaultDirectoryOpenFile
+ };
+ var files = await dialog.ShowAsync(ParentWindow);
+ if (files is null || files.Length <= 0) return;
+ if (FileFormat.FindByExtensionOrFilePath(files[0]) is null) return;
+ Operation.FilePath = files[0];
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
index 07c2f53..6eac5a2 100644
--- a/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRepairLayersControl.axaml.cs
@@ -2,76 +2,75 @@
using UVtools.Core.Operations;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolRepairLayersControl : ToolControl
{
- public class ToolRepairLayersControl : ToolControl
- {
- public OperationRepairLayers Operation => BaseOperation as OperationRepairLayers;
+ public OperationRepairLayers Operation => BaseOperation as OperationRepairLayers;
- public ToolRepairLayersControl()
- {
- BaseOperation = new OperationRepairLayers(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolRepairLayersControl()
+ {
+ BaseOperation = new OperationRepairLayers(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public static OperationRepairLayers GetOperationRepairLayers() => new (App.SlicerFile)
- {
- RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands,
- RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps,
- RepairSuctionCups = UserSettings.Instance.LayerRepair.RepairSuctionCups,
- RemoveEmptyLayers = UserSettings.Instance.LayerRepair.RemoveEmptyLayers,
- RemoveIslandsBelowEqualPixelCount = UserSettings.Instance.LayerRepair.RemoveIslandsBelowEqualPixels,
- RemoveIslandsRecursiveIterations = UserSettings.Instance.LayerRepair.RemoveIslandsRecursiveIterations,
- AttachIslandsBelowLayers = UserSettings.Instance.LayerRepair.AttachIslandsBelowLayers,
- ResinTrapsOverlapBy = UserSettings.Instance.LayerRepair.ResinTrapsOverlapBy,
- SuctionCupsVentHole = UserSettings.Instance.LayerRepair.SuctionCupsVentHole,
- GapClosingIterations = UserSettings.Instance.LayerRepair.ClosingIterations,
- NoiseRemovalIterations = UserSettings.Instance.LayerRepair.OpeningIterations,
- };
+ public static OperationRepairLayers GetOperationRepairLayers() => new (App.SlicerFile)
+ {
+ RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands,
+ RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps,
+ RepairSuctionCups = UserSettings.Instance.LayerRepair.RepairSuctionCups,
+ RemoveEmptyLayers = UserSettings.Instance.LayerRepair.RemoveEmptyLayers,
+ RemoveIslandsBelowEqualPixelCount = UserSettings.Instance.LayerRepair.RemoveIslandsBelowEqualPixels,
+ RemoveIslandsRecursiveIterations = UserSettings.Instance.LayerRepair.RemoveIslandsRecursiveIterations,
+ AttachIslandsBelowLayers = UserSettings.Instance.LayerRepair.AttachIslandsBelowLayers,
+ ResinTrapsOverlapBy = UserSettings.Instance.LayerRepair.ResinTrapsOverlapBy,
+ SuctionCupsVentHole = UserSettings.Instance.LayerRepair.SuctionCupsVentHole,
+ GapClosingIterations = UserSettings.Instance.LayerRepair.ClosingIterations,
+ NoiseRemovalIterations = UserSettings.Instance.LayerRepair.OpeningIterations,
+ };
- public void SetFromUserSettings()
- {
- Operation.RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands;
- Operation.RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps;
- Operation.RepairSuctionCups = UserSettings.Instance.LayerRepair.RepairSuctionCups;
- Operation.RemoveEmptyLayers = UserSettings.Instance.LayerRepair.RemoveEmptyLayers;
- Operation.RemoveIslandsBelowEqualPixelCount = UserSettings.Instance.LayerRepair.RemoveIslandsBelowEqualPixels;
- Operation.RemoveIslandsRecursiveIterations = UserSettings.Instance.LayerRepair.RemoveIslandsRecursiveIterations;
- Operation.AttachIslandsBelowLayers = UserSettings.Instance.LayerRepair.AttachIslandsBelowLayers;
- Operation.ResinTrapsOverlapBy = UserSettings.Instance.LayerRepair.ResinTrapsOverlapBy;
- Operation.SuctionCupsVentHole = UserSettings.Instance.LayerRepair.SuctionCupsVentHole;
- Operation.GapClosingIterations = UserSettings.Instance.LayerRepair.ClosingIterations;
- Operation.NoiseRemovalIterations = UserSettings.Instance.LayerRepair.OpeningIterations;
- }
+ public void SetFromUserSettings()
+ {
+ Operation.RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands;
+ Operation.RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps;
+ Operation.RepairSuctionCups = UserSettings.Instance.LayerRepair.RepairSuctionCups;
+ Operation.RemoveEmptyLayers = UserSettings.Instance.LayerRepair.RemoveEmptyLayers;
+ Operation.RemoveIslandsBelowEqualPixelCount = UserSettings.Instance.LayerRepair.RemoveIslandsBelowEqualPixels;
+ Operation.RemoveIslandsRecursiveIterations = UserSettings.Instance.LayerRepair.RemoveIslandsRecursiveIterations;
+ Operation.AttachIslandsBelowLayers = UserSettings.Instance.LayerRepair.AttachIslandsBelowLayers;
+ Operation.ResinTrapsOverlapBy = UserSettings.Instance.LayerRepair.ResinTrapsOverlapBy;
+ Operation.SuctionCupsVentHole = UserSettings.Instance.LayerRepair.SuctionCupsVentHole;
+ Operation.GapClosingIterations = UserSettings.Instance.LayerRepair.ClosingIterations;
+ Operation.NoiseRemovalIterations = UserSettings.Instance.LayerRepair.OpeningIterations;
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- switch (callback)
- {
- case ToolWindow.Callbacks.Init:
- ParentWindow.LayerRangeVisible = false;
- ParentWindow.IsCheckBox1Visible = true;
+ case ToolWindow.Callbacks.Init:
+ ParentWindow.LayerRangeVisible = false;
+ ParentWindow.IsCheckBox1Visible = true;
- SetFromUserSettings();
- Operation.IslandDetectionConfig = App.MainWindow.GetIslandDetectionConfiguration();
- break;
- case ToolWindow.Callbacks.Loaded:
- Operation.IslandDetectionConfig = App.MainWindow.GetIslandDetectionConfiguration();
- break;
- case ToolWindow.Callbacks.Checkbox1:
- ParentWindow.LayerRangeVisible = ParentWindow.IsCheckBox1Checked;
- if (!ParentWindow.IsCheckBox1Checked)
- {
- ParentWindow.SelectAllLayers();
- }
- break;
- }
+ SetFromUserSettings();
+ Operation.IslandDetectionConfig = App.MainWindow.GetIslandDetectionConfiguration();
+ break;
+ case ToolWindow.Callbacks.Loaded:
+ Operation.IslandDetectionConfig = App.MainWindow.GetIslandDetectionConfiguration();
+ break;
+ case ToolWindow.Callbacks.Checkbox1:
+ ParentWindow.LayerRangeVisible = ParentWindow.IsCheckBox1Checked;
+ if (!ParentWindow.IsCheckBox1Checked)
+ {
+ ParentWindow.SelectAllLayers();
+ }
+ break;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs
index d062868..4a95f3b 100644
--- a/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolResizeControl.axaml.cs
@@ -1,22 +1,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolResizeControl : ToolControl
{
- public class ToolResizeControl : ToolControl
- {
- public OperationResize Operation => BaseOperation as OperationResize;
+ public OperationResize Operation => BaseOperation as OperationResize;
- public ToolResizeControl()
- {
- BaseOperation = new OperationResize(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolResizeControl()
+ {
+ BaseOperation = new OperationResize(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs
index 0883966..e04d5ab 100644
--- a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs
@@ -1,22 +1,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolRotateControl : ToolControl
{
- public class ToolRotateControl : ToolControl
- {
- public OperationRotate Operation => BaseOperation as OperationRotate;
+ public OperationRotate Operation => BaseOperation as OperationRotate;
- public ToolRotateControl()
- {
- BaseOperation = new OperationRotate(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolRotateControl()
+ {
+ BaseOperation = new OperationRotate(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml
index 1a0a0a5..c282e6f 100644
--- a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Controls.Tools.ToolScriptingControl">
<StackPanel Orientation="Vertical">
@@ -21,33 +22,29 @@
<Button
ToolTip.Tip="Loads an script file"
VerticalAlignment="Center"
- Command="{Binding LoadScript}">
- <Image Source="/Assets/Icons/file-import-16x16.png" />
- </Button>
+ Command="{Binding LoadScript}"
+ i:Attached.Icon="fas fa-file-import"/>
<Button
IsEnabled="{Binding Operation.HaveFile}"
ToolTip.Tip="Reloads the script"
VerticalAlignment="Center"
- Command="{Binding ReloadScript}">
- <Image Source="/Assets/Icons/refresh-16x16.png" />
- </Button>
+ Command="{Binding ReloadScript}"
+ i:Attached.Icon="fas fa-sync-alt"/>
<Button
IsEnabled="{Binding Operation.HaveFile}"
ToolTip.Tip="Open the script folder"
VerticalAlignment="Center"
- Command="{Binding OpenScriptFolder}">
- <Image Source="/Assets/Icons/open-16x16.png" />
- </Button>
+ Command="{Binding OpenScriptFolder}"
+ i:Attached.Icon="fas fa-folder"/>
<Button
IsEnabled="{Binding Operation.HaveFile}"
ToolTip.Tip="Open the script file"
VerticalAlignment="Center"
- Command="{Binding OpenScriptFile}">
- <Image Source="/Assets/Icons/file-code-16x16.png" />
- </Button>
+ Command="{Binding OpenScriptFile}"
+ i:Attached.Icon="fas fa-file-code"/>
</StackPanel>
</Grid>
diff --git a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs
index ac5e73e..571b7e4 100644
--- a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs
@@ -1,703 +1,707 @@
using System;
-using System.Collections.Generic;
using System.IO;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
+using UVtools.Core;
using UVtools.Core.Operations;
using UVtools.Core.Scripting;
using UVtools.Core.SystemOS;
using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolScriptingControl : ToolControl
{
- public class ToolScriptingControl : ToolControl
- {
- public OperationScripting Operation => BaseOperation as OperationScripting;
+ public OperationScripting Operation => BaseOperation as OperationScripting;
+
+ private readonly StackPanel _scriptConfigurationPanel;
+ private readonly Grid _scriptVariablesGrid;
- private readonly StackPanel _scriptConfigurationPanel;
- private readonly Grid _scriptVariablesGrid;
+ public ToolScriptingControl()
+ {
+ BaseOperation = new OperationScripting(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ _scriptConfigurationPanel = this.FindControl<StackPanel>("ScriptConfigurationPanel");
+ _scriptVariablesGrid = this.FindControl<Grid>("ScriptVariablesGrid");
+ }
- public ToolScriptingControl()
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void Callback(ToolWindow.Callbacks callback)
+ {
+ switch (callback)
{
- BaseOperation = new OperationScripting(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- _scriptConfigurationPanel = this.FindControl<StackPanel>("ScriptConfigurationPanel");
- _scriptVariablesGrid = this.FindControl<Grid>("ScriptVariablesGrid");
+ case ToolWindow.Callbacks.Init:
+ case ToolWindow.Callbacks.Loaded:
+ if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.CanExecute;
+ ReloadGUI();
+ ReloadScript();
+ Operation.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName == nameof(Operation.CanExecute))
+ {
+ ParentWindow.ButtonOkEnabled = Operation.CanExecute;
+ }
+ };
+ Operation.OnScriptReload += (sender, e) => ReloadGUI();
+ break;
}
+ }
- private void InitializeComponent()
+ public async void LoadScript()
+ {
+ var dialog = new OpenFileDialog
{
- AvaloniaXamlLoader.Load(this);
- }
+ AllowMultiple = false,
+ Directory = UserSettings.Instance.General.DefaultDirectoryScripts,
+ Filters = Helpers.ScriptsFileFilter,
+ };
+
+ var files = await dialog.ShowAsync(ParentWindow);
+ if (files is null || files.Length == 0) return;
+
+ Operation.FilePath = files[0];
+ ReloadScript();
+ }
- public override void Callback(ToolWindow.Callbacks callback)
+ public async void ReloadScript()
+ {
+ try
{
- switch (callback)
+ Operation.ReloadScriptFromFile();
+ if (Operation.ScriptGlobals is not null && About.Version.CompareTo(Operation.ScriptGlobals.Script.MinimumVersionToRun) < 0)
{
- case ToolWindow.Callbacks.Init:
- case ToolWindow.Callbacks.Loaded:
- if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.CanExecute;
- ReloadGUI();
- ReloadScript();
- Operation.PropertyChanged += (sender, e) =>
- {
- if (e.PropertyName == nameof(Operation.CanExecute))
- {
- ParentWindow.ButtonOkEnabled = Operation.CanExecute;
- }
- };
- Operation.OnScriptReload += (sender, e) => ReloadGUI();
- break;
+ await ParentWindow.MessageBoxError($"Unable to run due {About.Software} version {About.VersionStr} is lower than required {Operation.ScriptGlobals.Script.MinimumVersionToRun}\n" +
+ $"Please update {About.Software} in order to run this script.");
}
}
-
- public async void LoadScript()
+ catch (Exception e)
{
- var dialog = new OpenFileDialog
- {
- AllowMultiple = false,
- Directory = UserSettings.Instance.General.DefaultDirectoryScripts,
- Filters = Helpers.ScriptsFileFilter,
- };
+ await ParentWindow.MessageBoxError(e.Message);
+ }
+
+ }
- var files = await dialog.ShowAsync(ParentWindow);
- if (files is null || files.Length == 0) return;
+ public void OpenScriptFolder()
+ {
+ if (!Operation.HaveFile) return;
+ SystemAware.StartProcess(Path.GetDirectoryName(Operation.FilePath));
+ }
+
+ public void OpenScriptFile()
+ {
+ if (!Operation.HaveFile) return;
+ SystemAware.StartProcess(Operation.FilePath);
+ }
- Operation.FilePath = files[0];
- ReloadScript();
+ public void ReloadGUI()
+ {
+ if (!Operation.CanExecute) return;
+
+ _scriptConfigurationPanel.Children.Clear();
+ _scriptVariablesGrid.Children.Clear();
+ _scriptVariablesGrid.RowDefinitions.Clear();
+
+ TextBox tbScriptName = new()
+ {
+ IsReadOnly = true,
+ Text = $"{Operation.ScriptGlobals.Script.Name} | Version: {Operation.ScriptGlobals.Script.Version} by {Operation.ScriptGlobals.Script.Author}",
+ UseFloatingWatermark = true,
+ Watermark = "Script name, version and author"
+ };
+
+ TextBox tbScriptDescription = new()
+ {
+ IsReadOnly = true,
+ Text = Operation.ScriptGlobals.Script.Description,
+ AcceptsReturn = true,
+ UseFloatingWatermark = true,
+ Watermark = "Script description"
+ };
+
+ _scriptConfigurationPanel.Children.Add(tbScriptName);
+ _scriptConfigurationPanel.Children.Add(tbScriptDescription);
+
+ //Operation.ScriptGlobals.Script.UserInputs.Add(new ScriptBoolInput() { Label = "Hellow" });
+ //Operation.ScriptGlobals.Script.UserInputs.Add(new ScriptTextBoxInput() { Label = "Hellow", Value = "m,e", MultiLine = true});
+ if (Operation.ScriptGlobals.Script.UserInputs.Count == 0)
+ {
+ return;
}
- public async void ReloadScript()
+
+ string rowDefinitions = string.Empty;
+ for (var i = 0; i < Operation.ScriptGlobals.Script.UserInputs.Count; i++)
{
- try
+ if (i < Operation.ScriptGlobals.Script.UserInputs.Count - 1)
{
- Operation.ReloadScriptFromFile();
+ rowDefinitions += "Auto,10,";
}
- catch (Exception e)
+ else
{
- await ParentWindow.MessageBoxError(e.Message);
+ rowDefinitions += "Auto";
}
-
}
- public void OpenScriptFolder()
- {
- if (!Operation.HaveFile) return;
- SystemAware.StartProcess(Path.GetDirectoryName(Operation.FilePath));
- }
-
- public void OpenScriptFile()
- {
- if (!Operation.HaveFile) return;
- SystemAware.StartProcess(Operation.FilePath);
- }
+ _scriptVariablesGrid.RowDefinitions = RowDefinitions.Parse(rowDefinitions);
- public void ReloadGUI()
+ for (var i = 0; i < Operation.ScriptGlobals.Script.UserInputs.Count; i++)
{
- if (!Operation.CanExecute) return;
-
- _scriptConfigurationPanel.Children.Clear();
- _scriptVariablesGrid.Children.Clear();
- _scriptVariablesGrid.RowDefinitions.Clear();
+ var variable = Operation.ScriptGlobals.Script.UserInputs[i];
- TextBox tbScriptName = new()
+ if (!string.IsNullOrWhiteSpace(variable.Label) && variable is not ScriptCheckBoxInput and not ScriptToggleSwitchInput)
{
- IsReadOnly = true,
- Text = $"{Operation.ScriptGlobals.Script.Name} | Version: {Operation.ScriptGlobals.Script.Version} by {Operation.ScriptGlobals.Script.Author}",
- UseFloatingWatermark = true,
- Watermark = "Script name, version and author"
- };
+ TextBlock tbLabel = new()
+ {
+ VerticalAlignment = VerticalAlignment.Center,
+ Text = $"{variable.Label}:"
+ };
- TextBox tbScriptDescription = new()
- {
- IsReadOnly = true,
- Text = Operation.ScriptGlobals.Script.Description,
- AcceptsReturn = true,
- UseFloatingWatermark = true,
- Watermark = "Script description"
- };
-
- _scriptConfigurationPanel.Children.Add(tbScriptName);
- _scriptConfigurationPanel.Children.Add(tbScriptDescription);
-
- //Operation.ScriptGlobals.Script.UserInputs.Add(new ScriptBoolInput() { Label = "Hellow" });
- //Operation.ScriptGlobals.Script.UserInputs.Add(new ScriptTextBoxInput() { Label = "Hellow", Value = "m,e", MultiLine = true});
- if (Operation.ScriptGlobals.Script.UserInputs.Count == 0)
- {
- return;
+ if (!string.IsNullOrWhiteSpace(variable.ToolTip))
+ {
+ ToolTip.SetTip(tbLabel, variable.ToolTip);
+ }
+
+ _scriptVariablesGrid.Children.Add(tbLabel);
+ Grid.SetRow(tbLabel, i * 2);
+ Grid.SetColumn(tbLabel, 0);
}
-
- string rowDefinitions = string.Empty;
- for (var i = 0; i < Operation.ScriptGlobals.Script.UserInputs.Count; i++)
+ if (!string.IsNullOrWhiteSpace(variable.Unit))
{
- if (i < Operation.ScriptGlobals.Script.UserInputs.Count - 1)
+ TextBlock control = new()
{
- rowDefinitions += "Auto,10,";
- }
- else
- {
- rowDefinitions += "Auto";
- }
- }
+ VerticalAlignment = VerticalAlignment.Center,
+ Text = variable.Unit
+ };
- _scriptVariablesGrid.RowDefinitions = RowDefinitions.Parse(rowDefinitions);
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 4);
+ }
- for (var i = 0; i < Operation.ScriptGlobals.Script.UserInputs.Count; i++)
+ switch (variable)
{
- var variable = Operation.ScriptGlobals.Script.UserInputs[i];
-
- if (!string.IsNullOrWhiteSpace(variable.Label) && variable is not ScriptCheckBoxInput and not ScriptToggleSwitchInput)
+ case ScriptNumericalInput<sbyte> numSBYTE:
{
- TextBlock tbLabel = new()
+ NumericUpDown control = new()
{
- VerticalAlignment = VerticalAlignment.Center,
- Text = $"{variable.Label}:"
+ Minimum = numSBYTE.Minimum,
+ Maximum = numSBYTE.Maximum,
+ Value = numSBYTE.Value,
+ Increment = numSBYTE.Increment,
+ MinWidth = 150
};
- if (!string.IsNullOrWhiteSpace(variable.ToolTip))
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
{
- ToolTip.SetTip(tbLabel, variable.ToolTip);
- }
+ numSBYTE.Value = (sbyte)value;
+ control.Value = numSBYTE.Value;
+ });
- _scriptVariablesGrid.Children.Add(tbLabel);
- Grid.SetRow(tbLabel, i * 2);
- Grid.SetColumn(tbLabel, 0);
- }
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- if (!string.IsNullOrWhiteSpace(variable.Unit))
+ continue;
+ }
+ case ScriptNumericalInput<byte> numBYTE:
{
- TextBlock control = new()
+ NumericUpDown control = new()
{
- VerticalAlignment = VerticalAlignment.Center,
- Text = variable.Unit
+ Minimum = numBYTE.Minimum,
+ Maximum = numBYTE.Maximum,
+ Value = numBYTE.Value,
+ Increment = numBYTE.Increment,
+ MinWidth = 150
};
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
+ {
+ numBYTE.Value = (byte)value;
+ control.Value = numBYTE.Value;
+ });
+
_scriptVariablesGrid.Children.Add(control);
Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 4);
- }
+ Grid.SetColumn(control, 2);
- switch (variable)
+ continue;
+ }
+ case ScriptNumericalInput<short> numSHORT:
{
- case ScriptNumericalInput<sbyte> numSBYTE:
+ NumericUpDown control = new()
{
- NumericUpDown control = new()
- {
- Minimum = numSBYTE.Minimum,
- Maximum = numSBYTE.Maximum,
- Value = numSBYTE.Value,
- Increment = numSBYTE.Increment,
- MinWidth = 150
- };
+ Minimum = numSHORT.Minimum,
+ Maximum = numSHORT.Maximum,
+ Value = numSHORT.Value,
+ Increment = numSHORT.Increment,
+ MinWidth = 150
+ };
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numSBYTE.Value = (sbyte)value;
- control.Value = numSBYTE.Value;
- });
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
+ {
+ numSHORT.Value = (short)value;
+ control.Value = numSHORT.Value;
+ });
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- continue;
- }
- case ScriptNumericalInput<byte> numBYTE:
+ continue;
+ }
+ case ScriptNumericalInput<ushort> numUSHORT:
+ {
+ NumericUpDown control = new()
{
- NumericUpDown control = new()
- {
- Minimum = numBYTE.Minimum,
- Maximum = numBYTE.Maximum,
- Value = numBYTE.Value,
- Increment = numBYTE.Increment,
- MinWidth = 150
- };
+ Minimum = numUSHORT.Minimum,
+ Maximum = numUSHORT.Maximum,
+ Value = numUSHORT.Value,
+ Increment = numUSHORT.Increment,
+ MinWidth = 150
+ };
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numBYTE.Value = (byte)value;
- control.Value = numBYTE.Value;
- });
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
+ {
+ numUSHORT.Value = (ushort)value;
+ control.Value = numUSHORT.Value;
+ });
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- continue;
- }
- case ScriptNumericalInput<short> numSHORT:
+ continue;
+ }
+ case ScriptNumericalInput<int> numINT:
+ {
+ NumericUpDown control = new()
{
- NumericUpDown control = new()
- {
- Minimum = numSHORT.Minimum,
- Maximum = numSHORT.Maximum,
- Value = numSHORT.Value,
- Increment = numSHORT.Increment,
- MinWidth = 150
- };
+ Minimum = numINT.Minimum,
+ Maximum = numINT.Maximum,
+ Value = numINT.Value,
+ Increment = numINT.Increment,
+ MinWidth = 150
+ };
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numSHORT.Value = (short)value;
- control.Value = numSHORT.Value;
- });
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
+ {
+ numINT.Value = (int)value;
+ control.Value = numINT.Value;
+ });
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- continue;
- }
- case ScriptNumericalInput<ushort> numUSHORT:
+ continue;
+ }
+ case ScriptNumericalInput<uint> numUINT:
+ {
+ NumericUpDown control = new()
{
- NumericUpDown control = new()
- {
- Minimum = numUSHORT.Minimum,
- Maximum = numUSHORT.Maximum,
- Value = numUSHORT.Value,
- Increment = numUSHORT.Increment,
- MinWidth = 150
- };
+ Minimum = numUINT.Minimum,
+ Maximum = numUINT.Maximum,
+ Value = numUINT.Value,
+ Increment = numUINT.Increment,
+ MinWidth = 150
+ };
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numUSHORT.Value = (ushort)value;
- control.Value = numUSHORT.Value;
- });
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
+ {
+ numUINT.Value = (uint)value;
+ control.Value = numUINT.Value;
+ });
+
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptNumericalInput<long> numLONG:
+ {
+ NumericUpDown control = new()
+ {
+ Minimum = numLONG.Minimum,
+ Maximum = numLONG.Maximum,
+ Value = numLONG.Value,
+ Increment = numLONG.Increment,
+ MinWidth = 150
+ };
- continue;
- }
- case ScriptNumericalInput<int> numINT:
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
{
- NumericUpDown control = new()
- {
- Minimum = numINT.Minimum,
- Maximum = numINT.Maximum,
- Value = numINT.Value,
- Increment = numINT.Increment,
- MinWidth = 150
- };
+ numLONG.Value = (long)value;
+ control.Value = numLONG.Value;
+ });
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numINT.Value = (int)value;
- control.Value = numINT.Value;
- });
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptNumericalInput<ulong> numULONG:
+ {
+ NumericUpDown control = new()
+ {
+ Minimum = numULONG.Minimum,
+ Maximum = numULONG.Maximum,
+ Value = numULONG.Value,
+ Increment = numULONG.Increment,
+ MinWidth = 150
+ };
- continue;
- }
- case ScriptNumericalInput<uint> numUINT:
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
{
- NumericUpDown control = new()
- {
- Minimum = numUINT.Minimum,
- Maximum = numUINT.Maximum,
- Value = numUINT.Value,
- Increment = numUINT.Increment,
- MinWidth = 150
- };
+ numULONG.Value = (ulong)value;
+ control.Value = numULONG.Value;
+ });
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numUINT.Value = (uint)value;
- control.Value = numUINT.Value;
- });
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptNumericalInput<float> numFLOAT:
+ {
+ NumericUpDown control = new()
+ {
+ Minimum = numFLOAT.Minimum,
+ Maximum = numFLOAT.Maximum,
+ Value = numFLOAT.Value,
+ Increment = numFLOAT.Increment,
+ MinWidth = 150
+ };
- continue;
+ if (numFLOAT.DecimalPlates > 0)
+ {
+ control.FormatString = $"F{numFLOAT.DecimalPlates}";
}
- case ScriptNumericalInput<long> numLONG:
+
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
{
- NumericUpDown control = new()
- {
- Minimum = numLONG.Minimum,
- Maximum = numLONG.Maximum,
- Value = numLONG.Value,
- Increment = numLONG.Increment,
- MinWidth = 150
- };
+ numFLOAT.Value = (float) Math.Round(value, numFLOAT.DecimalPlates);
+ control.Value = numFLOAT.Value;
+ });
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numLONG.Value = (long)value;
- control.Value = numLONG.Value;
- });
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptNumericalInput<double> numDOUBLE:
+ {
+ NumericUpDown control = new()
+ {
+ Minimum = numDOUBLE.Minimum,
+ Maximum = numDOUBLE.Maximum,
+ Value = numDOUBLE.Value,
+ Increment = numDOUBLE.Increment,
+ MinWidth = 150
+ };
- continue;
+ if (numDOUBLE.DecimalPlates > 0)
+ {
+ control.FormatString = $"F{numDOUBLE.DecimalPlates}";
}
- case ScriptNumericalInput<ulong> numULONG:
+
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
{
- NumericUpDown control = new()
- {
- Minimum = numULONG.Minimum,
- Maximum = numULONG.Maximum,
- Value = numULONG.Value,
- Increment = numULONG.Increment,
- MinWidth = 150
- };
+ numDOUBLE.Value = Math.Round(value, numDOUBLE.DecimalPlates);
+ control.Value = numDOUBLE.Value;
+ });
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numULONG.Value = (ulong)value;
- control.Value = numULONG.Value;
- });
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptNumericalInput<decimal> numDECIMAL:
+ {
+ NumericUpDown control = new()
+ {
+ Minimum = (double)numDECIMAL.Minimum,
+ Maximum = (double)numDECIMAL.Maximum,
+ Value = (double)numDECIMAL.Value,
+ Increment = (double)numDECIMAL.Increment,
+ MinWidth = 150
+ };
- continue;
- }
- case ScriptNumericalInput<float> numFLOAT:
+ if (numDECIMAL.DecimalPlates > 0)
{
- NumericUpDown control = new()
- {
- Minimum = numFLOAT.Minimum,
- Maximum = numFLOAT.Maximum,
- Value = numFLOAT.Value,
- Increment = numFLOAT.Increment,
- MinWidth = 150
- };
+ control.FormatString = $"F{numDECIMAL.DecimalPlates}";
+ }
- if (numFLOAT.DecimalPlates > 0)
- {
- control.FormatString = $"F{numFLOAT.DecimalPlates}";
- }
+ var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
+ valueProperty.Subscribe(value =>
+ {
+ numDECIMAL.Value = (decimal)Math.Round(value, numDECIMAL.DecimalPlates);
+ control.Value = (double)numDECIMAL.Value;
+ });
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numFLOAT.Value = (float) Math.Round(value, numFLOAT.DecimalPlates);
- control.Value = numFLOAT.Value;
- });
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptCheckBoxInput inputCheckBox:
+ {
+ var control = new CheckBox
+ {
+ Content = variable.Label,
+ IsChecked = inputCheckBox.Value
+ };
- continue;
- }
- case ScriptNumericalInput<double> numDOUBLE:
+ var valueProperty = control.GetObservable(CheckBox.IsCheckedProperty);
+ valueProperty.Subscribe(value =>
{
- NumericUpDown control = new()
- {
- Minimum = numDOUBLE.Minimum,
- Maximum = numDOUBLE.Maximum,
- Value = numDOUBLE.Value,
- Increment = numDOUBLE.Increment,
- MinWidth = 150
- };
+ if (value != null) inputCheckBox.Value = value.Value;
+ });
- if (numDOUBLE.DecimalPlates > 0)
- {
- control.FormatString = $"F{numDOUBLE.DecimalPlates}";
- }
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numDOUBLE.Value = Math.Round(value, numDOUBLE.DecimalPlates);
- control.Value = numDOUBLE.Value;
- });
+ if (!string.IsNullOrWhiteSpace(variable.ToolTip))
+ {
+ ToolTip.SetTip(control, variable.ToolTip);
+ }
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptToggleSwitchInput inputToggleSwitch:
+ {
+ var control = new ToggleSwitch
+ {
+ OnContent = inputToggleSwitch.OnText,
+ OffContent = inputToggleSwitch.OffText,
+ IsChecked = inputToggleSwitch.Value
+ };
- continue;
- }
- case ScriptNumericalInput<decimal> numDECIMAL:
+ var valueProperty = control.GetObservable(ToggleSwitch.IsCheckedProperty);
+ valueProperty.Subscribe(value =>
{
- NumericUpDown control = new()
- {
- Minimum = (double)numDECIMAL.Minimum,
- Maximum = (double)numDECIMAL.Maximum,
- Value = (double)numDECIMAL.Value,
- Increment = (double)numDECIMAL.Increment,
- MinWidth = 150
- };
+ if (value != null) inputToggleSwitch.Value = value.Value;
+ });
- if (numDECIMAL.DecimalPlates > 0)
- {
- control.FormatString = $"F{numDECIMAL.DecimalPlates}";
- }
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
- valueProperty.Subscribe(value =>
- {
- numDECIMAL.Value = (decimal)Math.Round(value, numDECIMAL.DecimalPlates);
- control.Value = (double)numDECIMAL.Value;
- });
+ if (!string.IsNullOrWhiteSpace(variable.ToolTip))
+ {
+ ToolTip.SetTip(control, variable.ToolTip);
+ }
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptTextBoxInput inputTextBox:
+ {
+ TextBox control = new()
+ {
+ AcceptsReturn = inputTextBox.MultiLine,
+ Text = inputTextBox.Value,
+ };
- continue;
- }
- case ScriptCheckBoxInput inputCheckBox:
+ var valueProperty = control.GetObservable(TextBox.TextProperty);
+ valueProperty.Subscribe(value =>
{
- var control = new CheckBox
- {
- Content = variable.Label,
- IsChecked = inputCheckBox.Value
- };
+ inputTextBox.Value = value;
+ });
- var valueProperty = control.GetObservable(CheckBox.IsCheckedProperty);
- valueProperty.Subscribe(value =>
- {
- if (value != null) inputCheckBox.Value = value.Value;
- });
+ _scriptVariablesGrid.Children.Add(control);
+ Grid.SetRow(control, i * 2);
+ Grid.SetColumn(control, 2);
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ continue;
+ }
+ case ScriptOpenFolderDialogInput inputOpenFolder:
+ {
+ var panel = new StackPanel
+ {
+ Orientation = Orientation.Horizontal,
+ Spacing = 5
+ };
- if (!string.IsNullOrWhiteSpace(variable.ToolTip))
- {
- ToolTip.SetTip(control, variable.ToolTip);
- }
+ var control = new TextBox
+ {
+ IsReadOnly = true,
+ Text = inputOpenFolder.Value,
+ };
- continue;
- }
- case ScriptToggleSwitchInput inputToggleSwitch:
+ var button = new Button
+ {
+ Content = "Select",
+ };
+
+ button.Click += async (sender, args) =>
{
- var control = new ToggleSwitch
+ var dialog = new OpenFolderDialog
{
- OnContent = inputToggleSwitch.OnText,
- OffContent = inputToggleSwitch.OffText,
- IsChecked = inputToggleSwitch.Value
+ Directory = inputOpenFolder.Value,
+ Title = inputOpenFolder.Title
};
-
- var valueProperty = control.GetObservable(ToggleSwitch.IsCheckedProperty);
- valueProperty.Subscribe(value =>
+ var result = await dialog.ShowAsync(ParentWindow);
+ if (!string.IsNullOrWhiteSpace(result))
{
- if (value != null) inputToggleSwitch.Value = value.Value;
- });
+ inputOpenFolder.Value = result;
+ control.Text = result;
+ }
+ };
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ panel.Children.Add(control);
+ panel.Children.Add(button);
- if (!string.IsNullOrWhiteSpace(variable.ToolTip))
- {
- ToolTip.SetTip(control, variable.ToolTip);
- }
+ _scriptVariablesGrid.Children.Add(panel);
+ Grid.SetRow(panel, i * 2);
+ Grid.SetColumn(panel, 2);
- continue;
- }
- case ScriptTextBoxInput inputTextBox:
+ continue;
+ }
+ case ScriptSaveFileDialogInput inputSaveFile:
+ {
+ var panel = new StackPanel
{
- TextBox control = new()
- {
- AcceptsReturn = inputTextBox.MultiLine,
- Text = inputTextBox.Value,
- };
+ Orientation = Orientation.Horizontal,
+ Spacing = 5
+ };
- var valueProperty = control.GetObservable(TextBox.TextProperty);
- valueProperty.Subscribe(value =>
- {
- inputTextBox.Value = value;
- });
+ var control = new TextBox
+ {
+ IsReadOnly = true,
+ Text = inputSaveFile.Value,
+ };
- _scriptVariablesGrid.Children.Add(control);
- Grid.SetRow(control, i * 2);
- Grid.SetColumn(control, 2);
+ var button = new Button
+ {
+ Content = "Select",
+ };
- continue;
- }
- case ScriptOpenFolderDialogInput inputOpenFolder:
+ button.Click += async (sender, args) =>
{
- var panel = new StackPanel
+ var dialog = new SaveFileDialog
{
- Orientation = Orientation.Horizontal,
- Spacing = 5
+ Directory = inputSaveFile.Value,
+ Title = inputSaveFile.Title,
+ DefaultExtension = inputSaveFile.DefaultExtension,
+ InitialFileName = inputSaveFile.InitialFilename
};
- var control = new TextBox
+ if (inputSaveFile.Filters is not null)
{
- IsReadOnly = true,
- Text = inputOpenFolder.Value,
- };
+ foreach (var filter in inputSaveFile.Filters)
+ {
+ dialog.Filters.Add(new FileDialogFilter
+ {
+ Extensions = filter.Extensions,
+ Name = filter.Name
+ });
+ }
+ }
- var button = new Button
+ var result = await dialog.ShowAsync(ParentWindow);
+ if (!string.IsNullOrWhiteSpace(result))
{
- Content = "Select",
- };
+ inputSaveFile.Value = result;
+ control.Text = result;
+ }
+ };
- button.Click += async (sender, args) =>
- {
- var dialog = new OpenFolderDialog
- {
- Directory = inputOpenFolder.Value,
- Title = inputOpenFolder.Title
- };
- var result = await dialog.ShowAsync(ParentWindow);
- if (!string.IsNullOrWhiteSpace(result))
- {
- inputOpenFolder.Value = result;
- control.Text = result;
- }
- };
+ panel.Children.Add(control);
+ panel.Children.Add(button);
- panel.Children.Add(control);
- panel.Children.Add(button);
+ _scriptVariablesGrid.Children.Add(panel);
+ Grid.SetRow(panel, i * 2);
+ Grid.SetColumn(panel, 2);
- _scriptVariablesGrid.Children.Add(panel);
- Grid.SetRow(panel, i * 2);
- Grid.SetColumn(panel, 2);
+ continue;
+ }
+ case ScriptOpenFileDialogInput inputOpenFile:
+ {
+ var panel = new StackPanel
+ {
+ Orientation = Orientation.Horizontal,
+ Spacing = 5
+ };
- continue;
- }
- case ScriptSaveFileDialogInput inputSaveFile:
+ var control = new TextBox
{
- var panel = new StackPanel
- {
- Orientation = Orientation.Horizontal,
- Spacing = 5
- };
+ IsReadOnly = true,
+ Text = inputOpenFile.Value,
+ AcceptsReturn = true,
+ };
- var control = new TextBox
- {
- IsReadOnly = true,
- Text = inputSaveFile.Value,
- };
+ var button = new Button
+ {
+ Content = "Select",
+ };
- var button = new Button
+ button.Click += async (sender, args) =>
+ {
+ var dialog = new OpenFileDialog
{
- Content = "Select",
+ Directory = inputOpenFile.Value,
+ Title = inputOpenFile.Title,
+ AllowMultiple = inputOpenFile.AllowMultiple,
+ InitialFileName = inputOpenFile.InitialFilename
};
- button.Click += async (sender, args) =>
+ if (inputOpenFile.Filters is not null)
{
- var dialog = new SaveFileDialog
+ foreach (var filter in inputOpenFile.Filters)
{
- Directory = inputSaveFile.Value,
- Title = inputSaveFile.Title,
- DefaultExtension = inputSaveFile.DefaultExtension,
- InitialFileName = inputSaveFile.InitialFilename
- };
-
- if (inputSaveFile.Filters is not null)
- {
- foreach (var filter in inputSaveFile.Filters)
+ dialog.Filters.Add(new FileDialogFilter
{
- dialog.Filters.Add(new FileDialogFilter
- {
- Extensions = filter.Extensions,
- Name = filter.Name
- });
- }
- }
-
- var result = await dialog.ShowAsync(ParentWindow);
- if (!string.IsNullOrWhiteSpace(result))
- {
- inputSaveFile.Value = result;
- control.Text = result;
+ Extensions = filter.Extensions,
+ Name = filter.Name
+ });
}
- };
-
- panel.Children.Add(control);
- panel.Children.Add(button);
-
- _scriptVariablesGrid.Children.Add(panel);
- Grid.SetRow(panel, i * 2);
- Grid.SetColumn(panel, 2);
-
- continue;
- }
- case ScriptOpenFileDialogInput inputOpenFile:
- {
- var panel = new StackPanel
- {
- Orientation = Orientation.Horizontal,
- Spacing = 5
- };
+ }
- var control = new TextBox
- {
- IsReadOnly = true,
- Text = inputOpenFile.Value,
- AcceptsReturn = true,
- };
+ var result = await dialog.ShowAsync(ParentWindow);
+ if (result is null || result.Length == 0) return;
+ inputOpenFile.Value = result[0];
+ inputOpenFile.Files = result;
+ control.Text = string.Join('\n', result);
+ };
- var button = new Button
- {
- Content = "Select",
- };
+ panel.Children.Add(control);
+ panel.Children.Add(button);
- button.Click += async (sender, args) =>
- {
- var dialog = new OpenFileDialog
- {
- Directory = inputOpenFile.Value,
- Title = inputOpenFile.Title,
- AllowMultiple = inputOpenFile.AllowMultiple,
- InitialFileName = inputOpenFile.InitialFilename
- };
+ _scriptVariablesGrid.Children.Add(panel);
+ Grid.SetRow(panel, i * 2);
+ Grid.SetColumn(panel, 2);
- if (inputOpenFile.Filters is not null)
- {
- foreach (var filter in inputOpenFile.Filters)
- {
- dialog.Filters.Add(new FileDialogFilter
- {
- Extensions = filter.Extensions,
- Name = filter.Name
- });
- }
- }
-
- var result = await dialog.ShowAsync(ParentWindow);
- if (result is null || result.Length == 0) return;
- inputOpenFile.Value = result[0];
- inputOpenFile.Files = result;
- control.Text = string.Join('\n', result);
- };
-
- panel.Children.Add(control);
- panel.Children.Add(button);
-
- _scriptVariablesGrid.Children.Add(panel);
- Grid.SetRow(panel, i * 2);
- Grid.SetColumn(panel, 2);
-
- continue;
- }
+ continue;
}
}
-
- ParentWindow?.FitToSize();
}
+
+ ParentWindow?.FitToSize();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs
index 922dca8..72e8ce1 100644
--- a/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolSolidifyControl.axaml.cs
@@ -1,21 +1,20 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolSolidifyControl : ToolControl
{
- public class ToolSolidifyControl : ToolControl
+ public OperationSolidify Operation => BaseOperation as OperationSolidify;
+ public ToolSolidifyControl()
{
- public OperationSolidify Operation => BaseOperation as OperationSolidify;
- public ToolSolidifyControl()
- {
- BaseOperation = new OperationSolidify(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ BaseOperation = new OperationSolidify(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs
index 4287b3b..a9baeeb 100644
--- a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs
@@ -2,85 +2,84 @@
using Emgu.CV.CvEnum;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public class ToolThresholdControl : ToolControl
{
- public class ToolThresholdControl : ToolControl
+ private int _selectedPresetIndex;
+ private bool _isThresholdEnabled = true;
+ private bool _isMaximumEnabled = true;
+ private bool _isTypeEnabled = true;
+ public OperationThreshold Operation => BaseOperation as OperationThreshold;
+
+ public string[] Presets => new[]
{
- private int _selectedPresetIndex;
- private bool _isThresholdEnabled = true;
- private bool _isMaximumEnabled = true;
- private bool _isTypeEnabled = true;
- public OperationThreshold Operation => BaseOperation as OperationThreshold;
+ "Free use",
+ "Strip AntiAliasing",
+ "Set pixel brightness"
+ };
- public string[] Presets => new[]
- {
- "Free use",
- "Strip AntiAliasing",
- "Set pixel brightness"
- };
+ public bool IsThresholdEnabled
+ {
+ get => _isThresholdEnabled;
+ set => RaiseAndSetIfChanged(ref _isThresholdEnabled, value);
+ }
- public bool IsThresholdEnabled
- {
- get => _isThresholdEnabled;
- set => RaiseAndSetIfChanged(ref _isThresholdEnabled, value);
- }
+ public bool IsMaximumEnabled
+ {
+ get => _isMaximumEnabled;
+ set => RaiseAndSetIfChanged(ref _isMaximumEnabled, value);
+ }
- public bool IsMaximumEnabled
- {
- get => _isMaximumEnabled;
- set => RaiseAndSetIfChanged(ref _isMaximumEnabled, value);
- }
+ public bool IsTypeEnabled
+ {
+ get => _isTypeEnabled;
+ set => RaiseAndSetIfChanged(ref _isTypeEnabled, value);
+ }
- public bool IsTypeEnabled
+ public int SelectedPresetIndex
+ {
+ get => _selectedPresetIndex;
+ set
{
- get => _isTypeEnabled;
- set => RaiseAndSetIfChanged(ref _isTypeEnabled, value);
- }
+ if (!RaiseAndSetIfChanged(ref _selectedPresetIndex, value)) return;
- public int SelectedPresetIndex
- {
- get => _selectedPresetIndex;
- set
+ switch (_selectedPresetIndex)
{
- if (!RaiseAndSetIfChanged(ref _selectedPresetIndex, value)) return;
-
- switch (_selectedPresetIndex)
- {
- case 0:
- IsThresholdEnabled = true;
- IsMaximumEnabled = true;
- IsTypeEnabled = true;
- break;
- case 1:
- IsThresholdEnabled = true;
- IsMaximumEnabled = false;
- IsTypeEnabled = false;
- Operation.Threshold = 127;
- Operation.Maximum = 255;
- Operation.Type = ThresholdType.Binary;
- break;
- case 2:
- IsThresholdEnabled = false;
- IsMaximumEnabled = true;
- IsTypeEnabled = false;
- Operation.Threshold = 254;
- //Operation.Maximum = 254;
- Operation.Type = ThresholdType.Binary;
- break;
- }
+ case 0:
+ IsThresholdEnabled = true;
+ IsMaximumEnabled = true;
+ IsTypeEnabled = true;
+ break;
+ case 1:
+ IsThresholdEnabled = true;
+ IsMaximumEnabled = false;
+ IsTypeEnabled = false;
+ Operation.Threshold = 127;
+ Operation.Maximum = 255;
+ Operation.Type = ThresholdType.Binary;
+ break;
+ case 2:
+ IsThresholdEnabled = false;
+ IsMaximumEnabled = true;
+ IsTypeEnabled = false;
+ Operation.Threshold = 254;
+ //Operation.Maximum = 254;
+ Operation.Type = ThresholdType.Binary;
+ break;
}
}
+ }
- public ToolThresholdControl()
- {
- BaseOperation = new OperationThreshold(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolThresholdControl()
+ {
+ BaseOperation = new OperationThreshold(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml.cs
index 5b68845..ae08feb 100644
--- a/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml.cs
@@ -1,22 +1,21 @@
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Controls.Tools
+namespace UVtools.WPF.Controls.Tools;
+
+public partial class ToolTimelapseControl : ToolControl
{
- public partial class ToolTimelapseControl : ToolControl
- {
- public OperationTimelapse Operation => BaseOperation as OperationTimelapse;
+ public OperationTimelapse Operation => BaseOperation as OperationTimelapse;
- public ToolTimelapseControl()
- {
- BaseOperation = new OperationTimelapse(SlicerFile);
- if (!ValidateSpawn()) return;
- InitializeComponent();
- }
+ public ToolTimelapseControl()
+ {
+ BaseOperation = new OperationTimelapse(SlicerFile);
+ if (!ValidateSpawn()) return;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/UserControlEx.cs b/UVtools.WPF/Controls/UserControlEx.cs
index 0b1a806..123591c 100644
--- a/UVtools.WPF/Controls/UserControlEx.cs
+++ b/UVtools.WPF/Controls/UserControlEx.cs
@@ -4,58 +4,57 @@ using System.Runtime.CompilerServices;
using Avalonia.Controls;
using UVtools.Core.FileFormats;
-namespace UVtools.WPF.Controls
+namespace UVtools.WPF.Controls;
+
+public class UserControlEx : UserControl, INotifyPropertyChanged
{
- public class UserControlEx : UserControl, INotifyPropertyChanged
+ #region BindableBase
+ /// <summary>
+ /// Multicast event for property change notifications.
+ /// </summary>
+ private PropertyChangedEventHandler _propertyChanged;
+ private readonly List<string> events = new();
+
+ public new event PropertyChangedEventHandler PropertyChanged
+ {
+ add { _propertyChanged += value; events.Add("added"); }
+ remove { _propertyChanged -= value; events.Add("removed"); }
+ }
+
+ protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (EqualityComparer<T>.Default.Equals(field, value)) return false;
+ field = value;
+ RaisePropertyChanged(propertyName);
+ return true;
+ }
+
+ protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ }
+
+ /// <summary>
+ /// Notifies listeners that a property value has changed.
+ /// </summary>
+ /// <param name="propertyName">
+ /// Name of the property used to notify listeners. This
+ /// value is optional and can be provided automatically when invoked from compilers
+ /// that support <see cref="CallerMemberNameAttribute" />.
+ /// </param>
+ protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ var e = new PropertyChangedEventArgs(propertyName);
+ OnPropertyChanged(e);
+ _propertyChanged?.Invoke(this, e);
+ }
+ #endregion
+
+ public FileFormat SlicerFile => App.SlicerFile;
+
+ public void ResetDataContext()
{
- #region BindableBase
- /// <summary>
- /// Multicast event for property change notifications.
- /// </summary>
- private PropertyChangedEventHandler _propertyChanged;
- private readonly List<string> events = new();
-
- public new event PropertyChangedEventHandler PropertyChanged
- {
- add { _propertyChanged += value; events.Add("added"); }
- remove { _propertyChanged -= value; events.Add("removed"); }
- }
-
- protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
- {
- if (EqualityComparer<T>.Default.Equals(field, value)) return false;
- field = value;
- RaisePropertyChanged(propertyName);
- return true;
- }
-
- protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
- {
- }
-
- /// <summary>
- /// Notifies listeners that a property value has changed.
- /// </summary>
- /// <param name="propertyName">
- /// Name of the property used to notify listeners. This
- /// value is optional and can be provided automatically when invoked from compilers
- /// that support <see cref="CallerMemberNameAttribute" />.
- /// </param>
- protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
- {
- var e = new PropertyChangedEventArgs(propertyName);
- OnPropertyChanged(e);
- _propertyChanged?.Invoke(this, e);
- }
- #endregion
-
- public FileFormat SlicerFile => App.SlicerFile;
-
- public void ResetDataContext()
- {
- var old = DataContext;
- DataContext = new object();
- DataContext = old;
- }
+ var old = DataContext;
+ DataContext = new object();
+ DataContext = old;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Controls/WindowEx.cs b/UVtools.WPF/Controls/WindowEx.cs
index 8b529e6..9e1a94e 100644
--- a/UVtools.WPF/Controls/WindowEx.cs
+++ b/UVtools.WPF/Controls/WindowEx.cs
@@ -15,104 +15,109 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Styling;
using UVtools.Core.FileFormats;
+using UVtools.Core.SystemOS;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF.Controls
+namespace UVtools.WPF.Controls;
+
+public class WindowEx : Window, INotifyPropertyChanged, IStyleable
{
- public class WindowEx : Window, INotifyPropertyChanged, IStyleable
+ #region BindableBase
+ /// <summary>
+ /// Multicast event for property change notifications.
+ /// </summary>
+ private PropertyChangedEventHandler _propertyChanged;
+ private readonly List<string> events = new();
+
+ public new event PropertyChangedEventHandler PropertyChanged
{
- #region BindableBase
- /// <summary>
- /// Multicast event for property change notifications.
- /// </summary>
- private PropertyChangedEventHandler _propertyChanged;
- private readonly List<string> events = new();
-
- public new event PropertyChangedEventHandler PropertyChanged
- {
- add { _propertyChanged += value; events.Add("added"); }
- remove { _propertyChanged -= value; events.Add("removed"); }
- }
+ add { _propertyChanged += value; events.Add("added"); }
+ remove { _propertyChanged -= value; events.Add("removed"); }
+ }
- protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
- {
- if (EqualityComparer<T>.Default.Equals(field, value)) return false;
- field = value;
- RaisePropertyChanged(propertyName);
- return true;
- }
+ protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (EqualityComparer<T>.Default.Equals(field, value)) return false;
+ field = value;
+ RaisePropertyChanged(propertyName);
+ return true;
+ }
- protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
- {
- }
+ protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ }
- /// <summary>
- /// Notifies listeners that a property value has changed.
- /// </summary>
- /// <param name="propertyName">
- /// Name of the property used to notify listeners. This
- /// value is optional and can be provided automatically when invoked from compilers
- /// that support <see cref="CallerMemberNameAttribute" />.
- /// </param>
- protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
- {
- var e = new PropertyChangedEventArgs(propertyName);
- OnPropertyChanged(e);
- _propertyChanged?.Invoke(this, e);
- }
- #endregion
+ /// <summary>
+ /// Notifies listeners that a property value has changed.
+ /// </summary>
+ /// <param name="propertyName">
+ /// Name of the property used to notify listeners. This
+ /// value is optional and can be provided automatically when invoked from compilers
+ /// that support <see cref="CallerMemberNameAttribute" />.
+ /// </param>
+ protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ var e = new PropertyChangedEventArgs(propertyName);
+ OnPropertyChanged(e);
+ _propertyChanged?.Invoke(this, e);
+ }
+ #endregion
- Type IStyleable.StyleKey => typeof(Window);
+ Type IStyleable.StyleKey => typeof(Window);
- public DialogResults DialogResult { get; set; } = DialogResults.Unknown;
- public enum DialogResults
- {
- Unknown,
- OK,
- Cancel
- }
+ public DialogResults DialogResult { get; set; } = DialogResults.Unknown;
+ public enum DialogResults
+ {
+ Unknown,
+ OK,
+ Cancel
+ }
- public double WindowMaxWidth => this.GetScreenWorkingArea().Width - UserSettings.Instance.General.WindowsHorizontalMargin;
+ public double WindowMaxWidth => this.GetScreenWorkingArea().Width - UserSettings.Instance.General.WindowsHorizontalMargin;
- public double WindowMaxHeight => this.GetScreenWorkingArea().Height - UserSettings.Instance.General.WindowsVerticalMargin;
+ public double WindowMaxHeight => this.GetScreenWorkingArea().Height - UserSettings.Instance.General.WindowsVerticalMargin;
- public UserSettings Settings => UserSettings.Instance;
+ public UserSettings Settings => UserSettings.Instance;
- public virtual FileFormat SlicerFile
- {
- get => App.SlicerFile;
- set => App.SlicerFile = value;
- }
+ public virtual FileFormat SlicerFile
+ {
+ get => App.SlicerFile;
+ set => App.SlicerFile = value;
+ }
- public WindowEx()
- {
+ public WindowEx()
+ {
#if DEBUG
- this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Control));
+ this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Control));
#endif
- //TransparencyLevelHint = WindowTransparencyLevel.AcrylicBlur;
- }
-
- protected override void OnOpened(EventArgs e)
- {
- base.OnOpened(e);
- if (!CanResize && WindowState == WindowState.Normal)
- {
- MaxWidth = WindowMaxWidth;
- MaxHeight = WindowMaxHeight;
- }
- }
+ //TransparencyLevelHint = WindowTransparencyLevel.AcrylicBlur;
+ }
- public void CloseWithResult()
+ protected override void OnOpened(EventArgs e)
+ {
+ base.OnOpened(e);
+ if (!CanResize && WindowState == WindowState.Normal)
{
- Close(DialogResult);
+ MaxWidth = WindowMaxWidth;
+ MaxHeight = WindowMaxHeight;
}
+ }
- public virtual void ResetDataContext(object newObject = null)
- {
- var old = DataContext;
- DataContext = null;
- DataContext = newObject ?? old;
- }
+ public void CloseWithResult()
+ {
+ Close(DialogResult);
+ }
+
+ public virtual void ResetDataContext(object newObject = null)
+ {
+ var old = DataContext;
+ DataContext = null;
+ DataContext = newObject ?? old;
+ }
+
+ public void OpenWebsite(string url)
+ {
+ SystemAware.OpenBrowser(url);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Converters/EnumToCollectionConverter.cs b/UVtools.WPF/Converters/EnumToCollectionConverter.cs
index f585f86..a49f87d 100644
--- a/UVtools.WPF/Converters/EnumToCollectionConverter.cs
+++ b/UVtools.WPF/Converters/EnumToCollectionConverter.cs
@@ -10,20 +10,19 @@ using System;
using Avalonia.Data.Converters;
using UVtools.Core.Extensions;
-namespace UVtools.WPF.Converters
+namespace UVtools.WPF.Converters;
+
+public class EnumToCollectionConverter : IValueConverter
{
- public class EnumToCollectionConverter : IValueConverter
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
- public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- return EnumExtensions.GetAllValuesAndDescriptions(value.GetType());
- }
+ return EnumExtensions.GetAllValuesAndDescriptions(value.GetType());
+ }
- public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- return null;
- //string parameterString = parameter.ToString();
- //return Enum.Parse(targetType, parameterString);
- }
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ return null;
+ //string parameterString = parameter.ToString();
+ //return Enum.Parse(targetType, parameterString);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs b/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs
index e37fb58..70bf5e6 100644
--- a/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs
+++ b/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs
@@ -11,21 +11,20 @@ using System.Linq;
using Avalonia.Data.Converters;
using UVtools.Core.Extensions;
-namespace UVtools.WPF.Converters
+namespace UVtools.WPF.Converters;
+
+public class FromValueDescriptionToEnumConverter : IValueConverter
{
- public class FromValueDescriptionToEnumConverter : IValueConverter
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
- public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- var list = EnumExtensions.GetAllValuesAndDescriptions(value.GetType());
- return list.FirstOrDefault(vd => vd.Value.Equals(value));
- }
+ var list = EnumExtensions.GetAllValuesAndDescriptions(value.GetType());
+ return list.FirstOrDefault(vd => vd.Value.Equals(value));
+ }
- public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- if (value is null) return null;
- var list = EnumExtensions.GetAllValuesAndDescriptions(targetType);
- return list.FirstOrDefault(vd => vd.Description == value.ToString())?.Value;
- }
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ if (value is null) return null;
+ var list = EnumExtensions.GetAllValuesAndDescriptions(targetType);
+ return list.FirstOrDefault(vd => vd.Description == value.ToString())?.Value;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/ErrorLog.cs b/UVtools.WPF/ErrorLog.cs
index 7e2f229..17e1a4f 100644
--- a/UVtools.WPF/ErrorLog.cs
+++ b/UVtools.WPF/ErrorLog.cs
@@ -2,30 +2,29 @@
using System.Diagnostics;
using System.IO;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public static class ErrorLog
{
- public static class ErrorLog
- {
- private const string Filename = "errors.log";
+ private const string Filename = "errors.log";
- public static string FullPath = Path.Combine(UserSettings.SettingsFolder, Filename);
+ public static string FullPath = Path.Combine(UserSettings.SettingsFolder, Filename);
- public static void AppendLine(string errorType, string text)
+ public static void AppendLine(string errorType, string text)
+ {
+ try
{
- try
- {
- File.AppendAllText(FullPath,
- $"[v{App.VersionStr}] ({errorType}) @ {DateTime.Now}: {text}{Environment.NewLine}");
- }
- catch (Exception exception)
- {
- Debug.WriteLine(exception);
- }
+ File.AppendAllText(FullPath,
+ $"[v{App.VersionStr}] ({errorType}) @ {DateTime.Now}: {text}{Environment.NewLine}");
}
-
- public static StreamWriter GetStreamWriter()
+ catch (Exception exception)
{
- return File.AppendText(FullPath);
+ Debug.WriteLine(exception);
}
}
-}
+
+ public static StreamWriter GetStreamWriter()
+ {
+ return File.AppendText(FullPath);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Extensions/BitmapExtension.cs b/UVtools.WPF/Extensions/BitmapExtension.cs
index 748f190..0bf30f3 100644
--- a/UVtools.WPF/Extensions/BitmapExtension.cs
+++ b/UVtools.WPF/Extensions/BitmapExtension.cs
@@ -13,189 +13,188 @@ using Emgu.CV;
using Emgu.CV.CvEnum;
using SkiaSharp;
-namespace UVtools.WPF.Extensions
+namespace UVtools.WPF.Extensions;
+
+/// <summary>
+/// Provide extension method to convert IInputArray to and from Bitmap
+/// </summary>
+public static class BitmapExtension
{
+ public static int GetStep(this WriteableBitmap bitmap)
+ => (int)bitmap.Size.Width;
+
/// <summary>
- /// Provide extension method to convert IInputArray to and from Bitmap
+ /// Gets the total length of this <see cref="WriteableBitmap"/></param>
/// </summary>
- public static class BitmapExtension
+ /// <param name="bitmap"></param>
+ /// <returns>The total length of this <see cref="WriteableBitmap"/></returns>
+ public static int GetLength(this WriteableBitmap bitmap)
+ => (int) (bitmap.Size.Width * bitmap.Size.Height);
+
+ public static int GetPixelPos(this WriteableBitmap bitmap, int x, int y)
+ => (int)(bitmap.Size.Width * y + x);
+
+ public static int GetPixelPos(this WriteableBitmap bitmap, System.Drawing.Point location) =>
+ bitmap.GetPixelPos(location.X, location.Y);
+
+ /// <summary>
+ /// Gets a single pixel span to manipulate or read pixels
+ /// </summary>
+ /// <typeparam name="T">Pixel type</typeparam>
+ /// <param name="mat"><see cref="Mat"/> Input</param>
+ /// <returns>A <see cref="Span{T}"/> containing all pixels in data memory</returns>
+ public static unsafe Span<uint> GetPixelSpan(this WriteableBitmap bitmap)
{
- public static int GetStep(this WriteableBitmap bitmap)
- => (int)bitmap.Size.Width;
-
- /// <summary>
- /// Gets the total length of this <see cref="WriteableBitmap"/></param>
- /// </summary>
- /// <param name="bitmap"></param>
- /// <returns>The total length of this <see cref="WriteableBitmap"/></returns>
- public static int GetLength(this WriteableBitmap bitmap)
- => (int) (bitmap.Size.Width * bitmap.Size.Height);
-
- public static int GetPixelPos(this WriteableBitmap bitmap, int x, int y)
- => (int)(bitmap.Size.Width * y + x);
-
- public static int GetPixelPos(this WriteableBitmap bitmap, System.Drawing.Point location) =>
- bitmap.GetPixelPos(location.X, location.Y);
-
- /// <summary>
- /// Gets a single pixel span to manipulate or read pixels
- /// </summary>
- /// <typeparam name="T">Pixel type</typeparam>
- /// <param name="mat"><see cref="Mat"/> Input</param>
- /// <returns>A <see cref="Span{T}"/> containing all pixels in data memory</returns>
- public static unsafe Span<uint> GetPixelSpan(this WriteableBitmap bitmap)
- {
- using var l = bitmap.Lock();
- return new Span<uint>(l.Address.ToPointer(), bitmap.GetLength());
- }
+ using var l = bitmap.Lock();
+ return new Span<uint>(l.Address.ToPointer(), bitmap.GetLength());
+ }
- public static unsafe Span<uint> GetPixelSpan(this WriteableBitmap bitmap, int length, int offset = 0)
- {
- using var l = bitmap.Lock();
- return new Span<uint>(IntPtr.Add(l.Address, offset).ToPointer(), length);
- }
+ public static unsafe Span<uint> GetPixelSpan(this WriteableBitmap bitmap, int length, int offset = 0)
+ {
+ using var l = bitmap.Lock();
+ return new Span<uint>(IntPtr.Add(l.Address, offset).ToPointer(), length);
+ }
- public static Span<uint> GetSinglePixelSpan(this WriteableBitmap bitmap, int x, int y, int length = 1)
- {
- using var l = bitmap.Lock();
- return bitmap.GetPixelSpan(length, bitmap.GetPixelPos(x, y));
- }
+ public static Span<uint> GetSinglePixelSpan(this WriteableBitmap bitmap, int x, int y, int length = 1)
+ {
+ using var l = bitmap.Lock();
+ return bitmap.GetPixelSpan(length, bitmap.GetPixelPos(x, y));
+ }
- public static Span<uint> GetSinglePixelPosSpan(this WriteableBitmap bitmap, int pos, int length = 3)
- => bitmap.GetPixelSpan(length, pos);
+ public static Span<uint> GetSinglePixelPosSpan(this WriteableBitmap bitmap, int pos, int length = 3)
+ => bitmap.GetPixelSpan(length, pos);
- public static unsafe Span<uint> GetPixelRowSpan(this WriteableBitmap bitmap, int y, int length = 0, int offset = 0)
- {
- using var l = bitmap.Lock();
- return new Span<uint>(IntPtr.Add(l.Address, (int) (bitmap.Size.Width * y + offset)).ToPointer(), (int) (length == 0 ? bitmap.Size.Width : length));
- }
+ public static unsafe Span<uint> GetPixelRowSpan(this WriteableBitmap bitmap, int y, int length = 0, int offset = 0)
+ {
+ using var l = bitmap.Lock();
+ return new Span<uint>(IntPtr.Add(l.Address, (int) (bitmap.Size.Width * y + offset)).ToPointer(), (int) (length == 0 ? bitmap.Size.Width : length));
+ }
- public static SKBitmap ToSkBitmap(this Mat mat)
+ public static SKBitmap ToSkBitmap(this Mat mat)
+ {
+ SKBitmap bitmap;
+ Mat target = mat;
+ SKColorType colorType;
+ switch (mat.NumberOfChannels)
{
- SKBitmap bitmap;
- Mat target = mat;
- SKColorType colorType;
- switch (mat.NumberOfChannels)
- {
- case 1:
- colorType = SKColorType.Gray8;
- break;
- case 2:
- colorType = SKColorType.Rg1616;
- break;
- case 3:
- CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
- colorType = SKColorType.Bgra8888;
- break;
- case 4:
- colorType = SKColorType.Bgra8888;
- break;
- default:
- throw new Exception("Unknown color type");
- }
-
- bitmap = new SKBitmap(new SKImageInfo(target.Width, target.Height, colorType));
- bitmap.SetPixels(target.DataPointer);
- return bitmap;
+ case 1:
+ colorType = SKColorType.Gray8;
+ break;
+ case 2:
+ colorType = SKColorType.Rg1616;
+ break;
+ case 3:
+ CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
+ colorType = SKColorType.Bgra8888;
+ break;
+ case 4:
+ colorType = SKColorType.Bgra8888;
+ break;
+ default:
+ throw new Exception("Unknown color type");
}
- public static SKImage ToSkImage(this Mat mat)
- {
- var bitmap = mat.ToSkBitmap();
- if (bitmap is null) return null;
- return SKImage.FromBitmap(bitmap);
- }
+ bitmap = new SKBitmap(new SKImageInfo(target.Width, target.Height, colorType));
+ bitmap.SetPixels(target.DataPointer);
+ return bitmap;
+ }
- public static WriteableBitmap ToBitmap(this Mat mat)
- {
- var dataCount = mat.Width * mat.Height;
+ public static SKImage ToSkImage(this Mat mat)
+ {
+ var bitmap = mat.ToSkBitmap();
+ if (bitmap is null) return null;
+ return SKImage.FromBitmap(bitmap);
+ }
- var writableBitmap = new WriteableBitmap(new PixelSize(mat.Width, mat.Height), new Vector(96, 96),
- PixelFormat.Bgra8888, AlphaFormat.Unpremul);
+ public static WriteableBitmap ToBitmap(this Mat mat)
+ {
+ var dataCount = mat.Width * mat.Height;
+
+ var writableBitmap = new WriteableBitmap(new PixelSize(mat.Width, mat.Height), new Vector(96, 96),
+ PixelFormat.Bgra8888, AlphaFormat.Unpremul);
- using var lockBuffer = writableBitmap.Lock();
+ using var lockBuffer = writableBitmap.Lock();
- unsafe
- {
+ unsafe
+ {
- var targetPixels = (uint*) (void*) lockBuffer.Address;
- switch (mat.NumberOfChannels)
- {
- //Stopwatch sw = Stopwatch.StartNew();
- // Method 1 (Span copy)
- case 1:
- var srcPixels1 = (byte*) (void*) mat.DataPointer;
- for (var i = 0; i < dataCount; i++)
- {
- var color = srcPixels1[i];
- targetPixels[i] = (uint) (color | color << 8 | color << 16 | 0xff << 24);
- }
+ var targetPixels = (uint*) (void*) lockBuffer.Address;
+ switch (mat.NumberOfChannels)
+ {
+ //Stopwatch sw = Stopwatch.StartNew();
+ // Method 1 (Span copy)
+ case 1:
+ var srcPixels1 = (byte*) (void*) mat.DataPointer;
+ for (var i = 0; i < dataCount; i++)
+ {
+ var color = srcPixels1[i];
+ targetPixels[i] = (uint) (color | color << 8 | color << 16 | 0xff << 24);
+ }
- break;
- case 3:
- var srcPixels2 = (byte*) (void*) mat.DataPointer;
- uint pixel = 0;
- for (uint i = 0; i < dataCount; i++)
- {
- targetPixels[i] = (uint)(srcPixels2[pixel++] | srcPixels2[pixel++] << 8 | srcPixels2[pixel++] << 16 | 0xff << 24);
- }
+ break;
+ case 3:
+ var srcPixels2 = (byte*) (void*) mat.DataPointer;
+ uint pixel = 0;
+ for (uint i = 0; i < dataCount; i++)
+ {
+ targetPixels[i] = (uint)(srcPixels2[pixel++] | srcPixels2[pixel++] << 8 | srcPixels2[pixel++] << 16 | 0xff << 24);
+ }
- break;
- case 4:
+ break;
+ case 4:
- if (mat.Depth == DepthType.Cv8U)
+ if (mat.Depth == DepthType.Cv8U)
+ {
+ var srcPixels4 = (byte*)(void*)mat.DataPointer;
+ uint pixel4 = 0;
+ for (uint i = 0; i < dataCount; i++)
{
- var srcPixels4 = (byte*)(void*)mat.DataPointer;
- uint pixel4 = 0;
- for (uint i = 0; i < dataCount; i++)
- {
- targetPixels[i] = (uint)(srcPixels4[pixel4++] | srcPixels4[pixel4++] << 8 | srcPixels4[pixel4++] << 16 | srcPixels4[pixel4++] << 24);
- }
+ targetPixels[i] = (uint)(srcPixels4[pixel4++] | srcPixels4[pixel4++] << 8 | srcPixels4[pixel4++] << 16 | srcPixels4[pixel4++] << 24);
}
- else if (mat.Depth == DepthType.Cv32S)
+ }
+ else if (mat.Depth == DepthType.Cv32S)
+ {
+ var srcPixels4 = (uint*) (void*) mat.DataPointer;
+ for (uint i = 0; i < dataCount; i++)
{
- var srcPixels4 = (uint*) (void*) mat.DataPointer;
- for (uint i = 0; i < dataCount; i++)
- {
- targetPixels[i] = srcPixels4[i];
- }
+ targetPixels[i] = srcPixels4[i];
}
+ }
- break;
- }
+ break;
}
+ }
- return writableBitmap;
- /*Debug.WriteLine($"Method 1 (Span copy): {sw.ElapsedMilliseconds}ms");
-
- // Method 2 (OpenCV Convertion + Copy Marshal)
- sw.Restart();
- CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
- var buffer = target.GetBytes();
- Marshal.Copy(buffer, 0, lockBuffer.Address, buffer.Length);
- Debug.WriteLine($"Method 2 (OpenCV Convertion + Copy Marshal): {sw.ElapsedMilliseconds}ms");
+ return writableBitmap;
+ /*Debug.WriteLine($"Method 1 (Span copy): {sw.ElapsedMilliseconds}ms");
+
+ // Method 2 (OpenCV Convertion + Copy Marshal)
+ sw.Restart();
+ CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
+ var buffer = target.GetBytes();
+ Marshal.Copy(buffer, 0, lockBuffer.Address, buffer.Length);
+ Debug.WriteLine($"Method 2 (OpenCV Convertion + Copy Marshal): {sw.ElapsedMilliseconds}ms");
-
- //sw.Restart();
- CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
- unsafe
- {
- var srcAddress = (uint*)(void*)target.DataPointer;
- var targetAddress = (uint*)(void*)lockBuffer.Address;
- *targetAddress = *srcAddress;
- }
- //Debug.WriteLine($"Method 3 (OpenCV Convertion + Set Address): {sw.ElapsedMilliseconds}ms");
-
- return writableBitmap;
- */
- /* for (var y = 0; y < mat.Height; y++)
- {
- Marshal.Copy(buffer, y * lockBuffer.RowBytes, new IntPtr(lockBuffer.Address.ToInt64() + y * lockBuffer.RowBytes), lockBuffer.RowBytes);
- }*/
+
+ //sw.Restart();
+ CvInvoke.CvtColor(mat, target, ColorConversion.Bgr2Bgra);
+ unsafe
+ {
+ var srcAddress = (uint*)(void*)target.DataPointer;
+ var targetAddress = (uint*)(void*)lockBuffer.Address;
+ *targetAddress = *srcAddress;
}
+ //Debug.WriteLine($"Method 3 (OpenCV Convertion + Set Address): {sw.ElapsedMilliseconds}ms");
+
+ return writableBitmap;
+ */
+ /* for (var y = 0; y < mat.Height; y++)
+ {
+ Marshal.Copy(buffer, y * lockBuffer.RowBytes, new IntPtr(lockBuffer.Address.ToInt64() + y * lockBuffer.RowBytes), lockBuffer.RowBytes);
+ }*/
}
} \ No newline at end of file
diff --git a/UVtools.WPF/Extensions/DrawingExtensions.cs b/UVtools.WPF/Extensions/DrawingExtensions.cs
index dbaea1e..bc21f5f 100644
--- a/UVtools.WPF/Extensions/DrawingExtensions.cs
+++ b/UVtools.WPF/Extensions/DrawingExtensions.cs
@@ -9,28 +9,27 @@
using System.Drawing;
using Avalonia;
-namespace UVtools.WPF.Extensions
+namespace UVtools.WPF.Extensions;
+
+public static class DrawingExtensions
{
- public static class DrawingExtensions
+ public static Avalonia.Media.Color ToAvalonia(this System.Drawing.Color color)
{
- public static Avalonia.Media.Color ToAvalonia(this System.Drawing.Color color)
- {
- return new(color.A, color.R, color.G, color.B);
- }
+ return new(color.A, color.R, color.G, color.B);
+ }
- public static System.Drawing.Color ToDotNet(this Avalonia.Media.Color color)
- {
- return Color.FromArgb(color.A, color.R, color.G, color.B);
- }
+ public static System.Drawing.Color ToDotNet(this Avalonia.Media.Color color)
+ {
+ return Color.FromArgb(color.A, color.R, color.G, color.B);
+ }
- public static Rect ToAvalonia(this Rectangle rectangle)
- {
- return new(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
- }
+ public static Rect ToAvalonia(this Rectangle rectangle)
+ {
+ return new(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ }
- public static Rectangle ToDotNet(this Rect rectangle)
- {
- return new((int) rectangle.X, (int) rectangle.Y, (int) rectangle.Width, (int) rectangle.Height);
- }
+ public static Rectangle ToDotNet(this Rect rectangle)
+ {
+ return new((int) rectangle.X, (int) rectangle.Y, (int) rectangle.Width, (int) rectangle.Height);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Extensions/PrimitivesExtensions.cs b/UVtools.WPF/Extensions/PrimitivesExtensions.cs
index 61bcce4..9caaba2 100644
--- a/UVtools.WPF/Extensions/PrimitivesExtensions.cs
+++ b/UVtools.WPF/Extensions/PrimitivesExtensions.cs
@@ -8,18 +8,17 @@
using Avalonia;
-namespace UVtools.WPF.Extensions
+namespace UVtools.WPF.Extensions;
+
+public static class PrimitivesExtensions
{
- public static class PrimitivesExtensions
+ public static System.Drawing.Point ToDotNet(this Point point)
{
- public static System.Drawing.Point ToDotNet(this Point point)
- {
- return new System.Drawing.Point((int) point.X, (int) point.Y);
- }
+ return new System.Drawing.Point((int) point.X, (int) point.Y);
+ }
- public static Point ToAvalonia(this System.Drawing.Point point)
- {
- return new Point(point.X, point.Y);
- }
+ public static Point ToAvalonia(this System.Drawing.Point point)
+ {
+ return new Point(point.X, point.Y);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Extensions/WindowExtensions.cs b/UVtools.WPF/Extensions/WindowExtensions.cs
index 70915fd..09f4334 100644
--- a/UVtools.WPF/Extensions/WindowExtensions.cs
+++ b/UVtools.WPF/Extensions/WindowExtensions.cs
@@ -8,93 +8,91 @@
using System.Threading;
using System.Threading.Tasks;
-using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Threading;
using MessageBox.Avalonia.DTO;
using MessageBox.Avalonia.Enums;
-namespace UVtools.WPF.Extensions
+namespace UVtools.WPF.Extensions;
+
+public static class WindowExtensions
{
- public static class WindowExtensions
+ public static async Task<ButtonResult> MessageBoxGeneric(this Window window, string message, string title = null,
+ ButtonEnum buttons = ButtonEnum.Ok, Icon icon = Icon.None, bool topMost = false, WindowStartupLocation location = WindowStartupLocation.CenterOwner)
{
- public static async Task<ButtonResult> MessageBoxGeneric(this Window window, string message, string title = null,
- ButtonEnum buttons = ButtonEnum.Ok, Icon icon = Icon.None, bool topMost = false, WindowStartupLocation location = WindowStartupLocation.CenterOwner)
- {
- var messageBoxStandardWindow = MessageBox.Avalonia.MessageBoxManager.GetMessageBoxStandardWindow(
- new MessageBoxStandardParams
- {
- ButtonDefinitions = buttons,
- ContentTitle = title ?? window.Title,
- ContentMessage = message,
- Icon = icon,
- WindowIcon = new WindowIcon(App.GetAsset("/Assets/Icons/UVtools.ico")),
- WindowStartupLocation = location,
- CanResize = UserSettings.Instance.General.WindowsCanResize,
- MaxWidth = window.GetScreenWorkingArea().Width - UserSettings.Instance.General.WindowsHorizontalMargin,
- MaxHeight = window.GetScreenWorkingArea().Height - UserSettings.Instance.General.WindowsVerticalMargin,
- SizeToContent = SizeToContent.WidthAndHeight,
- ShowInCenter = true,
- Topmost = topMost
- });
- return await messageBoxStandardWindow.ShowDialog(window);
- }
+ var messageBoxStandardWindow = MessageBox.Avalonia.MessageBoxManager.GetMessageBoxStandardWindow(
+ new MessageBoxStandardParams
+ {
+ ButtonDefinitions = buttons,
+ ContentTitle = title ?? window.Title,
+ ContentMessage = message,
+ Icon = icon,
+ WindowIcon = new WindowIcon(App.GetAsset("/Assets/Icons/UVtools.ico")),
+ WindowStartupLocation = location,
+ CanResize = UserSettings.Instance.General.WindowsCanResize,
+ MaxWidth = window.GetScreenWorkingArea().Width - UserSettings.Instance.General.WindowsHorizontalMargin,
+ MaxHeight = window.GetScreenWorkingArea().Height - UserSettings.Instance.General.WindowsVerticalMargin,
+ SizeToContent = SizeToContent.WidthAndHeight,
+ ShowInCenter = true,
+ Topmost = topMost
+ });
+ return await messageBoxStandardWindow.ShowDialog(window);
+ }
- public static async Task<ButtonResult> MessageBoxInfo(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
- => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Information", buttons, Icon.Info, topMost, WindowStartupLocation.CenterOwner);
+ public static async Task<ButtonResult> MessageBoxInfo(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
+ => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Information", buttons, Icon.Info, topMost, WindowStartupLocation.CenterOwner);
- public static async Task<ButtonResult> MessageBoxError(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
- => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Error", buttons, Icon.Error, topMost, WindowStartupLocation.CenterOwner);
+ public static async Task<ButtonResult> MessageBoxError(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
+ => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Error", buttons, Icon.Error, topMost, WindowStartupLocation.CenterOwner);
- public static async Task<ButtonResult> MessageBoxQuestion(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.YesNo, bool topMost = false)
- => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, topMost, WindowStartupLocation.CenterOwner);
+ public static async Task<ButtonResult> MessageBoxQuestion(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.YesNo, bool topMost = false)
+ => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, topMost, WindowStartupLocation.CenterOwner);
- public static async Task<ButtonResult> MessageBoxWaring(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
- => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Warning, topMost, WindowStartupLocation.CenterOwner);
+ public static async Task<ButtonResult> MessageBoxWaring(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
+ => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Warning, topMost, WindowStartupLocation.CenterOwner);
- public static void ShowDialogSync(this Window window, Window parent = null)
- {
- parent ??= window;
- using var source = new CancellationTokenSource();
- window.ShowDialog(parent).ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
- Dispatcher.UIThread.MainLoop(source.Token);
- }
+ public static void ShowDialogSync(this Window window, Window parent = null)
+ {
+ parent ??= window;
+ using var source = new CancellationTokenSource();
+ window.ShowDialog(parent).ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
+ Dispatcher.UIThread.MainLoop(source.Token);
+ }
- public static T ShowDialogSync<T>(this Window window, Window parent = null)
- {
- parent ??= window;
- using var source = new CancellationTokenSource();
- var task = window.ShowDialog<T>(parent);
- task.ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
- Dispatcher.UIThread.MainLoop(source.Token);
- return task.Result;
- }
+ public static T ShowDialogSync<T>(this Window window, Window parent = null)
+ {
+ parent ??= window;
+ using var source = new CancellationTokenSource();
+ var task = window.ShowDialog<T>(parent);
+ task.ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
+ Dispatcher.UIThread.MainLoop(source.Token);
+ return task.Result;
+ }
- public static void ResetDataContext(this Window window)
- {
- var old = window.DataContext;
- window.DataContext = new object();
- window.DataContext = old;
- }
+ public static void ResetDataContext(this Window window)
+ {
+ var old = window.DataContext;
+ window.DataContext = new object();
+ window.DataContext = old;
+ }
- public static Screen GetCurrentScreen(this Window window)
- {
- return //window.Screens.ScreenFromVisual(window) ??
- window.Screens.ScreenFromVisual(App.MainWindow) ??
- window.Screens.Primary ??
- window.Screens.All[0];
- }
+ public static Screen GetCurrentScreen(this Window window)
+ {
+ return //window.Screens.ScreenFromVisual(window) ??
+ window.Screens.ScreenFromVisual(App.MainWindow) ??
+ window.Screens.Primary ??
+ window.Screens.All[0];
+ }
- public static System.Drawing.Size GetScreenWorkingArea(this Window window)
- {
- var screen = window.GetCurrentScreen();
+ public static System.Drawing.Size GetScreenWorkingArea(this Window window)
+ {
+ var screen = window.GetCurrentScreen();
- return new System.Drawing.Size(
- UserSettings.Instance.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Width / screen.PixelDensity) : screen.WorkingArea.Width,
- UserSettings.Instance.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Height / screen.PixelDensity) : screen.WorkingArea.Height);
- }
+ return new System.Drawing.Size(
+ UserSettings.Instance.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Width / screen.PixelDensity) : screen.WorkingArea.Width,
+ UserSettings.Instance.General.WindowsTakeIntoAccountScreenScaling ? (int)(screen.WorkingArea.Height / screen.PixelDensity) : screen.WorkingArea.Height);
+ }
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/LayerCache.cs b/UVtools.WPF/LayerCache.cs
index b8c84bc..5b99fc6 100644
--- a/UVtools.WPF/LayerCache.cs
+++ b/UVtools.WPF/LayerCache.cs
@@ -15,104 +15,103 @@ using SkiaSharp;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public sealed class LayerCache
{
- public sealed class LayerCache
- {
- private Layer _layer;
- private VectorOfVectorOfPoint _layerContours;
- private int[,] _layerContourHierarchy;
- //private SKCanvas _canvas;
- private WriteableBitmap _bitmap;
+ private Layer _layer;
+ private VectorOfVectorOfPoint _layerContours;
+ private int[,] _layerContourHierarchy;
+ //private SKCanvas _canvas;
+ private WriteableBitmap _bitmap;
- public bool IsCached => _layer is not null;
+ public bool IsCached => _layer is not null;
- public unsafe Layer Layer
+ public unsafe Layer Layer
+ {
+ get => _layer;
+ set
{
- get => _layer;
- set
- {
- //if (ReferenceEquals(_layer, value)) return;
- Clear();
- _layer = value;
- Image = _layer.LayerMat;
- if (Image is null) return;
- ImageBgr = new Mat();
- CvInvoke.CvtColor(Image, ImageBgr, ColorConversion.Gray2Bgr);
+ //if (ReferenceEquals(_layer, value)) return;
+ Clear();
+ _layer = value;
+ Image = _layer.LayerMat;
+ if (Image is null) return;
+ ImageBgr = new Mat();
+ CvInvoke.CvtColor(Image, ImageBgr, ColorConversion.Gray2Bgr);
- ImageSpan = Image.GetBytePointer();
- ImageBgrSpan = ImageBgr.GetBytePointer();
- }
+ ImageSpan = Image.GetBytePointer();
+ ImageBgrSpan = ImageBgr.GetBytePointer();
}
+ }
- public Mat Image { get; private set; }
+ public Mat Image { get; private set; }
- public Mat ImageBgr { get; private set; }
+ public Mat ImageBgr { get; private set; }
- public unsafe byte *ImageSpan { get; private set; }
- public unsafe byte *ImageBgrSpan { get; private set; }
+ public unsafe byte *ImageSpan { get; private set; }
+ public unsafe byte *ImageBgrSpan { get; private set; }
- public WriteableBitmap Bitmap
+ public WriteableBitmap Bitmap
+ {
+ get => _bitmap;
+ set
{
- get => _bitmap;
- set
- {
- _bitmap = value;
- //_canvas?.Dispose();
- //_canvas = null;
- }
+ _bitmap = value;
+ //_canvas?.Dispose();
+ //_canvas = null;
}
+ }
- public SKCanvas Canvas
+ public SKCanvas Canvas
+ {
+ get
{
- get
- {
- using var framebuffer = Bitmap.Lock();
- var info = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
- framebuffer.Format.ToSkColorType(), SKAlphaType.Premul);
- return SKSurface.Create(info, framebuffer.Address, framebuffer.RowBytes).Canvas;
- }
+ using var framebuffer = Bitmap.Lock();
+ var info = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
+ framebuffer.Format.ToSkColorType(), SKAlphaType.Premul);
+ return SKSurface.Create(info, framebuffer.Address, framebuffer.RowBytes).Canvas;
}
+ }
- public VectorOfVectorOfPoint LayerContours
+ public VectorOfVectorOfPoint LayerContours
+ {
+ get
{
- get
- {
- if (_layerContours is null) CacheContours();
- return _layerContours;
- }
- private set => _layerContours = value;
+ if (_layerContours is null) CacheContours();
+ return _layerContours;
}
+ private set => _layerContours = value;
+ }
- public int[,] LayerContourHierarchy
+ public int[,] LayerContourHierarchy
+ {
+ get
{
- get
- {
- if (_layerContourHierarchy is null) CacheContours();
- return _layerContourHierarchy;
- }
- private set => _layerContourHierarchy = value;
+ if (_layerContourHierarchy is null) CacheContours();
+ return _layerContourHierarchy;
}
+ private set => _layerContourHierarchy = value;
+ }
- public void CacheContours(bool refresh = false)
- {
- if(refresh) Clear();
- if (_layerContours is not null) return;
- _layerContours = Image.FindContours(out _layerContourHierarchy, RetrType.Tree);
- }
+ public void CacheContours(bool refresh = false)
+ {
+ if(refresh) Clear();
+ if (_layerContours is not null) return;
+ _layerContours = Image.FindContours(out _layerContourHierarchy, RetrType.Tree);
+ }
- /// <summary>
- /// Clears the cache
- /// </summary>
- public void Clear()
- {
- _layer = null;
- Image?.Dispose();
- ImageBgr?.Dispose();
- _layerContours?.Dispose();
- _layerContours = null;
- _layerContourHierarchy = null;
- }
+ /// <summary>
+ /// Clears the cache
+ /// </summary>
+ public void Clear()
+ {
+ _layer = null;
+ Image?.Dispose();
+ ImageBgr?.Dispose();
+ _layerContours?.Dispose();
+ _layerContours = null;
+ _layerContourHierarchy = null;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.Clipboard.cs b/UVtools.WPF/MainWindow.Clipboard.cs
index 32784c7..42f2466 100644
--- a/UVtools.WPF/MainWindow.Clipboard.cs
+++ b/UVtools.WPF/MainWindow.Clipboard.cs
@@ -15,91 +15,90 @@ using Avalonia.Threading;
using MessageBox.Avalonia.Enums;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
- {
- public ListBox ClipboardList;
+ public ListBox ClipboardList;
- public void InitClipboardLayers()
- {
- ClipboardList = this.FindControl<ListBox>(nameof(ClipboardList));
- Clipboard.PropertyChanged += ClipboardOnPropertyChanged;
- }
+ public void InitClipboardLayers()
+ {
+ ClipboardList = this.FindControl<ListBox>(nameof(ClipboardList));
+ Clipboard.PropertyChanged += ClipboardOnPropertyChanged;
+ }
- private void ClipboardOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ private void ClipboardOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Clipboard.CurrentIndex))
{
- if (e.PropertyName == nameof(Clipboard.CurrentIndex))
- {
- if (Clipboard.CurrentIndex < 0 || Clipboard.SuppressRestore) return;
+ if (Clipboard.CurrentIndex < 0 || Clipboard.SuppressRestore) return;
- AddLogVerbose($"Clipboard change: {Clipboard.CurrentIndex}");
+ AddLogVerbose($"Clipboard change: {Clipboard.CurrentIndex}");
- if (Clipboard.ReallocatedLayerCount)
+ if (Clipboard.ReallocatedLayerCount)
+ {
+ DispatcherTimer.RunOnce(() =>
{
- DispatcherTimer.RunOnce(() =>
- {
- RefreshProperties();
- ResetDataContext();
- }, TimeSpan.FromMilliseconds(1));
- }
-
- ShowLayer();
- return;
+ RefreshProperties();
+ ResetDataContext();
+ }, TimeSpan.FromMilliseconds(1));
}
+
+ ShowLayer();
+ return;
}
+ }
- public void ClipboardUndo()
+ public void ClipboardUndo()
+ {
+ CanSave = true;
+ if ((_globalModifiers & KeyModifiers.Shift) != 0)
{
- CanSave = true;
- if ((_globalModifiers & KeyModifiers.Shift) != 0)
- {
- ClipboardUndo(true);
- return;
- }
- Clipboard.Undo();
- }
+ ClipboardUndo(true);
+ return;
+ }
+ Clipboard.Undo();
+ }
- public async void ClipboardUndo(bool rerun)
+ public async void ClipboardUndo(bool rerun)
+ {
+ CanSave = true;
+ var clip = Clipboard.CurrentClip;
+ Clipboard.Undo();
+ if (!rerun)
{
- CanSave = true;
- var clip = Clipboard.CurrentClip;
- Clipboard.Undo();
- if (!rerun)
- {
- return;
- }
- if (clip?.Operation is null) return;
- /*if (clip.Operation.HaveROI)
- {
- ROI = GetTransposedRectangle(clip.Operation.ROI);
- }
-
- if (clip.Operation.HaveMask)
- {
- AddMaskPoints(clip.Operation.MaskPoints);
- }*/
-
- var operation = await ShowRunOperation(clip.Operation.GetType(), clip.Operation);
- if (operation is null)
- {
- Clipboard.Redo();
- CanSave = false;
- }
+ return;
}
-
- public void ClipboardRedo()
+ if (clip?.Operation is null) return;
+ /*if (clip.Operation.HaveROI)
{
- CanSave = true;
- Clipboard.Redo();
+ ROI = GetTransposedRectangle(clip.Operation.ROI);
}
- public async void ClipboardClear()
+ if (clip.Operation.HaveMask)
{
- if (await this.MessageBoxQuestion("Are you sure you want to clear the clipboard?\n" +
- "Current layers will be placed as original layers\n" +
- "This action is permanent!", "Clear clipboard?") != ButtonResult.Yes) return;
- Clipboard.Clear(true);
+ AddMaskPoints(clip.Operation.MaskPoints);
+ }*/
+
+ var operation = await ShowRunOperation(clip.Operation.GetType(), clip.Operation);
+ if (operation is null)
+ {
+ Clipboard.Redo();
+ CanSave = false;
}
}
-}
+
+ public void ClipboardRedo()
+ {
+ CanSave = true;
+ Clipboard.Redo();
+ }
+
+ public async void ClipboardClear()
+ {
+ if (await this.MessageBoxQuestion("Are you sure you want to clear the clipboard?\n" +
+ "Current layers will be placed as original layers\n" +
+ "This action is permanent!", "Clear clipboard?") != ButtonResult.Yes) return;
+ Clipboard.Clear(true);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.GCode.cs b/UVtools.WPF/MainWindow.GCode.cs
index bf4bab9..2b1e003 100644
--- a/UVtools.WPF/MainWindow.GCode.cs
+++ b/UVtools.WPF/MainWindow.GCode.cs
@@ -14,64 +14,63 @@ using UVtools.Core.SystemOS;
using UVtools.WPF.Extensions;
using Helpers = UVtools.WPF.Controls.Helpers;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
- {
- public bool HaveGCode => IsFileLoaded && SlicerFile.SupportsGCode;
+ public bool HaveGCode => IsFileLoaded && SlicerFile.SupportsGCode;
- public uint GCodeLines => !HaveGCode ? 0 : SlicerFile.GCode.LineCount;
+ public uint GCodeLines => !HaveGCode ? 0 : SlicerFile.GCode.LineCount;
- public void OnClickRebuildGcode()
- {
- if (!HaveGCode) return;
- var temp = SlicerFile.SuppressRebuildGCode;
- SlicerFile.SuppressRebuildGCode = false;
- SlicerFile.RebuildGCode();
- SlicerFile.SuppressRebuildGCode = temp;
- RaisePropertyChanged(nameof(GCodeLines));
-
- CanSave = true;
- }
-
- public async void OnClickGCodeSaveFile()
- {
- if (!HaveGCode) return;
+ public void OnClickRebuildGcode()
+ {
+ if (!HaveGCode) return;
+ var temp = SlicerFile.SuppressRebuildGCode;
+ SlicerFile.SuppressRebuildGCode = false;
+ SlicerFile.RebuildGCode();
+ SlicerFile.SuppressRebuildGCode = temp;
+ RaisePropertyChanged(nameof(GCodeLines));
- var dialog = new SaveFileDialog
- {
- Filters = Helpers.IniFileFilter,
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
- InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_gcode.txt"
- };
- var file = await dialog.ShowAsync(this);
+ CanSave = true;
+ }
- if (string.IsNullOrEmpty(file)) return;
+ public async void OnClickGCodeSaveFile()
+ {
+ if (!HaveGCode) return;
- try
- {
- await using TextWriter tw = new StreamWriter(file);
- await tw.WriteAsync(SlicerFile.GCodeStr);
- tw.Close();
- }
- catch (Exception e)
- {
- await this.MessageBoxError(e.ToString(), "Error occur while save gcode");
- return;
- }
+ var dialog = new SaveFileDialog
+ {
+ Filters = Helpers.IniFileFilter,
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
+ InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_gcode.txt"
+ };
+ var file = await dialog.ShowAsync(this);
- var result = await this.MessageBoxQuestion(
- "GCode save was successful. Do you want open the file in the default editor?",
- "GCode save complete");
- if (result != ButtonResult.Yes) return;
+ if (string.IsNullOrEmpty(file)) return;
- SystemAware.StartProcess(file);
+ try
+ {
+ await using TextWriter tw = new StreamWriter(file);
+ await tw.WriteAsync(SlicerFile.GCodeStr);
+ tw.Close();
}
-
- public void OnClickGCodeSaveClipboard()
+ catch (Exception e)
{
- if (!HaveGCode) return;
- Application.Current.Clipboard.SetTextAsync(SlicerFile.GCodeStr);
+ await this.MessageBoxError(e.ToString(), "Error occur while save gcode");
+ return;
}
+
+ var result = await this.MessageBoxQuestion(
+ "GCode save was successful. Do you want open the file in the default editor?",
+ "GCode save complete");
+ if (result != ButtonResult.Yes) return;
+
+ SystemAware.StartProcess(file);
+ }
+
+ public void OnClickGCodeSaveClipboard()
+ {
+ if (!HaveGCode) return;
+ Application.Current.Clipboard.SetTextAsync(SlicerFile.GCodeStr);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs
index 586c489..36d7e2b 100644
--- a/UVtools.WPF/MainWindow.Information.cs
+++ b/UVtools.WPF/MainWindow.Information.cs
@@ -25,267 +25,225 @@ using UVtools.WPF.Structures;
using Bitmap = Avalonia.Media.Imaging.Bitmap;
using Helpers = UVtools.WPF.Controls.Helpers;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
- {
- public RangeObservableCollection<SlicerProperty> SlicerProperties { get; } = new();
- public DataGrid PropertiesGrid;
- public DataGrid CurrentLayerGrid;
+ public RangeObservableCollection<SlicerProperty> SlicerProperties { get; } = new();
+ public DataGrid PropertiesGrid;
+ public DataGrid CurrentLayerGrid;
- private uint _visibleThumbnailIndex;
- private Bitmap _visibleThumbnailImage;
- private RangeObservableCollection<ValueDescription> _currentLayerProperties = new();
+ private uint _visibleThumbnailIndex;
+ private Bitmap _visibleThumbnailImage;
+ private RangeObservableCollection<ValueDescription> _currentLayerProperties = new();
- public RangeObservableCollection<ValueDescription> CurrentLayerProperties
- {
- get => _currentLayerProperties;
- set => RaiseAndSetIfChanged(ref _currentLayerProperties, value);
- }
+ public RangeObservableCollection<ValueDescription> CurrentLayerProperties
+ {
+ get => _currentLayerProperties;
+ set => RaiseAndSetIfChanged(ref _currentLayerProperties, value);
+ }
- public void InitInformation()
+ public void InitInformation()
+ {
+ PropertiesGrid = this.Find<DataGrid>(nameof(PropertiesGrid));
+ CurrentLayerGrid = this.Find<DataGrid>(nameof(CurrentLayerGrid));
+ PropertiesGrid.KeyUp += GridOnKeyUp;
+ CurrentLayerGrid.KeyUp += GridOnKeyUp;
+ /*CurrentLayerGrid.BeginningEdit += (sender, e) =>
{
- PropertiesGrid = this.Find<DataGrid>(nameof(PropertiesGrid));
- CurrentLayerGrid = this.Find<DataGrid>(nameof(CurrentLayerGrid));
- PropertiesGrid.KeyUp += GridOnKeyUp;
- CurrentLayerGrid.KeyUp += GridOnKeyUp;
- /*CurrentLayerGrid.BeginningEdit += (sender, e) =>
+ if (e.Row.DataContext is StringTag stringTag)
{
- if (e.Row.DataContext is StringTag stringTag)
- {
- if (e.Column.DisplayIndex == 0
- || e.Row.DataContext.ToString() != nameof(LayerCache.Layer.ExposureTime)
- && e.Row.DataContext.ToString() != nameof(LayerCache.Layer.LightPWM)
- )
- {
- e.Cancel = true;
- }
- }
- else
+ if (e.Column.DisplayIndex == 0
+ || e.Row.DataContext.ToString() != nameof(LayerCache.Layer.ExposureTime)
+ && e.Row.DataContext.ToString() != nameof(LayerCache.Layer.LightPWM)
+ )
{
e.Cancel = true;
}
- };
- CurrentLayerGrid.RowEditEnding += (sender, e) =>
+ }
+ else
{
- if (e.EditAction == DataGridEditAction.Cancel) return;
- if (!(e.Row.DataContext is StringTag stringTag)) return;
- if (float.TryParse(stringTag.TagString, out var result)) return;
e.Cancel = true;
- };
- CurrentLayerGrid.RowEditEnded += (sender, e) =>
+ }
+ };
+ CurrentLayerGrid.RowEditEnding += (sender, e) =>
+ {
+ if (e.EditAction == DataGridEditAction.Cancel) return;
+ if (!(e.Row.DataContext is StringTag stringTag)) return;
+ if (float.TryParse(stringTag.TagString, out var result)) return;
+ e.Cancel = true;
+ };
+ CurrentLayerGrid.RowEditEnded += (sender, e) =>
+ {
+ if (e.EditAction == DataGridEditAction.Cancel) return;
+ if (!(e.Row.DataContext is StringTag stringTag)) return;
+ switch (stringTag.Content)
{
- if (e.EditAction == DataGridEditAction.Cancel) return;
- if (!(e.Row.DataContext is StringTag stringTag)) return;
- switch (stringTag.Content)
- {
- //case nameof(LayerCache.)
- }
- };*/
+ //case nameof(LayerCache.)
+ }
+ };*/
- }
+ }
- private void GridOnKeyUp(object? sender, KeyEventArgs e)
+ private void GridOnKeyUp(object? sender, KeyEventArgs e)
+ {
+ if (sender is DataGrid dataGrid)
{
- if (sender is DataGrid dataGrid)
+ switch (e.Key)
{
- switch (e.Key)
- {
- case Key.Escape:
- dataGrid.SelectedItems.Clear();
- break;
- case Key.Multiply:
- foreach (var item in dataGrid.Items)
- {
- if (dataGrid.SelectedItems.Contains(item))
- dataGrid.SelectedItems.Remove(item);
- else
- dataGrid.SelectedItems.Add(item);
- }
+ case Key.Escape:
+ dataGrid.SelectedItems.Clear();
+ break;
+ case Key.Multiply:
+ foreach (var item in dataGrid.Items)
+ {
+ if (dataGrid.SelectedItems.Contains(item))
+ dataGrid.SelectedItems.Remove(item);
+ else
+ dataGrid.SelectedItems.Add(item);
+ }
- break;
- }
+ break;
}
}
+ }
- #region Thumbnails
- public uint VisibleThumbnailIndex
+ #region Thumbnails
+ public uint VisibleThumbnailIndex
+ {
+ get => _visibleThumbnailIndex;
+ set
{
- get => _visibleThumbnailIndex;
- set
+ if (value == 0)
{
- if (value == 0)
- {
- RaiseAndSetIfChanged(ref _visibleThumbnailIndex, value);
- RaisePropertyChanged(nameof(ThumbnailCanGoPrevious));
- RaisePropertyChanged(nameof(ThumbnailCanGoNext));
- VisibleThumbnailImage = null;
- return;
- }
-
- if (!IsFileLoaded) return;
- var index = value - 1;
- if (index >= SlicerFile.CreatedThumbnailsCount) return;
- if (SlicerFile.Thumbnails[index] is null || SlicerFile.Thumbnails[index].IsEmpty) return;
- if (!RaiseAndSetIfChanged(ref _visibleThumbnailIndex, value)) return;
-
- VisibleThumbnailImage = SlicerFile.Thumbnails[index].ToBitmap();
+ RaiseAndSetIfChanged(ref _visibleThumbnailIndex, value);
RaisePropertyChanged(nameof(ThumbnailCanGoPrevious));
RaisePropertyChanged(nameof(ThumbnailCanGoNext));
+ VisibleThumbnailImage = null;
+ return;
}
- }
- public bool ThumbnailCanGoPrevious => SlicerFile is not null && _visibleThumbnailIndex > 1;
- public bool ThumbnailCanGoNext => SlicerFile is not null && _visibleThumbnailIndex < SlicerFile.CreatedThumbnailsCount;
+ if (!IsFileLoaded) return;
+ var index = value - 1;
+ if (index >= SlicerFile.CreatedThumbnailsCount) return;
+ if (SlicerFile.Thumbnails[index] is null || SlicerFile.Thumbnails[index].IsEmpty) return;
+ if (!RaiseAndSetIfChanged(ref _visibleThumbnailIndex, value)) return;
- public void ThumbnailGoPrevious()
- {
- if (!ThumbnailCanGoPrevious) return;
- VisibleThumbnailIndex--;
+ VisibleThumbnailImage = SlicerFile.Thumbnails[index].ToBitmap();
+ RaisePropertyChanged(nameof(ThumbnailCanGoPrevious));
+ RaisePropertyChanged(nameof(ThumbnailCanGoNext));
}
+ }
- public void ThumbnailGoNext()
- {
- if (!ThumbnailCanGoNext) return;
- VisibleThumbnailIndex++;
- }
+ public bool ThumbnailCanGoPrevious => SlicerFile is not null && _visibleThumbnailIndex > 1;
+ public bool ThumbnailCanGoNext => SlicerFile is not null && _visibleThumbnailIndex < SlicerFile.CreatedThumbnailsCount;
- public Bitmap VisibleThumbnailImage
- {
- get => _visibleThumbnailImage;
- set
- {
- RaiseAndSetIfChanged(ref _visibleThumbnailImage, value);
- RaisePropertyChanged(nameof(VisibleThumbnailResolution));
- }
- }
+ public void ThumbnailGoPrevious()
+ {
+ if (!ThumbnailCanGoPrevious) return;
+ VisibleThumbnailIndex--;
+ }
- public string VisibleThumbnailResolution => _visibleThumbnailImage is null ? null : $"{{Width: {_visibleThumbnailImage.Size.Width}, Height: {_visibleThumbnailImage.Size.Height}}}";
+ public void ThumbnailGoNext()
+ {
+ if (!ThumbnailCanGoNext) return;
+ VisibleThumbnailIndex++;
+ }
- public async void OnClickThumbnailSave()
+ public Bitmap VisibleThumbnailImage
+ {
+ get => _visibleThumbnailImage;
+ set
{
- if (SlicerFile is null) return;
- if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null)
- {
- return; // This should never happen!
- }
- var dialog = new SaveFileDialog
- {
- Filters = Helpers.PngFileFilter,
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
- InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_thumbnail{_visibleThumbnailIndex}.png"
- };
+ RaiseAndSetIfChanged(ref _visibleThumbnailImage, value);
+ RaisePropertyChanged(nameof(VisibleThumbnailResolution));
+ }
+ }
- var filepath = await dialog.ShowAsync(this);
+ public string VisibleThumbnailResolution => _visibleThumbnailImage is null ? null : $"{{Width: {_visibleThumbnailImage.Size.Width}, Height: {_visibleThumbnailImage.Size.Height}}}";
- if (!string.IsNullOrEmpty(filepath))
- {
- SlicerFile.Thumbnails[_visibleThumbnailIndex - 1].Save(filepath);
- }
+ public async void OnClickThumbnailSave()
+ {
+ if (SlicerFile is null) return;
+ if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null)
+ {
+ return; // This should never happen!
}
-
- public async void OnClickThumbnailImport()
+ var dialog = new SaveFileDialog
{
- if (_visibleThumbnailIndex <= 0) return;
- if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null)
- {
- return; // This should never happen!
- }
+ Filters = Helpers.PngFileFilter,
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
+ InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_thumbnail{_visibleThumbnailIndex}.png"
+ };
- var dialog = new OpenFileDialog
- {
- Filters = Helpers.ImagesFileFilter,
- AllowMultiple = false
- };
-
- var filepath = await dialog.ShowAsync(this);
-
- if (filepath is null || filepath.Length <= 0) return;
- uint i = _visibleThumbnailIndex - 1;
- SlicerFile.SetThumbnail((int)i, filepath[0]);
- //VisibleThumbnailImage = SlicerFile.Thumbnails[i].ToBitmap();
- SlicerFile.RequireFullEncode = true;
- CanSave = true;
- }
+ var filepath = await dialog.ShowAsync(this);
- public void RefreshThumbnail()
+ if (!string.IsNullOrEmpty(filepath))
{
- if (_visibleThumbnailIndex <= 0) return;
- uint i = _visibleThumbnailIndex - 1;
- if (SlicerFile.Thumbnails?[i] is null)
- {
- return;
- }
- VisibleThumbnailImage = SlicerFile.Thumbnails[i].ToBitmap();
+ SlicerFile.Thumbnails[_visibleThumbnailIndex - 1].Save(filepath);
}
- #endregion
+ }
- #region Slicer Properties
+ public async void OnClickThumbnailImport()
+ {
+ if (_visibleThumbnailIndex <= 0) return;
+ if (SlicerFile.Thumbnails[_visibleThumbnailIndex - 1] is null)
+ {
+ return; // This should never happen!
+ }
- public async void OnClickPropertiesSaveFile()
+ var dialog = new OpenFileDialog
{
- if (SlicerFile?.Configs is null) return;
+ Filters = Helpers.ImagesFileFilter,
+ AllowMultiple = false
+ };
+
+ var filepath = await dialog.ShowAsync(this);
+
+ if (filepath is null || filepath.Length <= 0) return;
+ uint i = _visibleThumbnailIndex - 1;
+ SlicerFile.SetThumbnail((int)i, filepath[0]);
+ //VisibleThumbnailImage = SlicerFile.Thumbnails[i].ToBitmap();
+ SlicerFile.RequireFullEncode = true;
+ CanSave = true;
+ }
- var dialog = new SaveFileDialog
- {
- Filters = Helpers.IniFileFilter,
- Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
- InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_properties.ini"
- };
+ public void RefreshThumbnail()
+ {
+ if (_visibleThumbnailIndex <= 0) return;
+ uint i = _visibleThumbnailIndex - 1;
+ if (SlicerFile.Thumbnails?[i] is null)
+ {
+ return;
+ }
+ VisibleThumbnailImage = SlicerFile.Thumbnails[i].ToBitmap();
+ }
+ #endregion
- var file = await dialog.ShowAsync(this);
+ #region Slicer Properties
- if (string.IsNullOrEmpty(file)) return;
+ public async void OnClickPropertiesSaveFile()
+ {
+ if (SlicerFile?.Configs is null) return;
- try
- {
- using TextWriter tw = new StreamWriter(file);
- foreach (var config in SlicerFile.Configs)
- {
- var type = config.GetType();
- tw.WriteLine($"[{type.Name}]");
- foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- if (property.Name.Equals("Item")) continue;
- var value = property.GetValue(config);
- switch (value)
- {
- case null:
- continue;
- case IList list:
- tw.WriteLine($"{property.Name} = {list.Count}");
- break;
- default:
- tw.WriteLine($"{property.Name} = {value}");
- break;
- }
- }
- tw.WriteLine();
- }
- tw.Close();
- }
- catch (Exception e)
- {
- await this.MessageBoxError(e.ToString(), "Error occur while save properties");
- return;
- }
+ var dialog = new SaveFileDialog
+ {
+ Filters = Helpers.IniFileFilter,
+ Directory = Path.GetDirectoryName(SlicerFile.FileFullPath),
+ InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_properties.ini"
+ };
- var result = await this.MessageBoxQuestion(
- "Properties save was successful. Do you want open the file in the default editor?",
- "Properties save complete");
- if (result != ButtonResult.Yes) return;
+ var file = await dialog.ShowAsync(this);
- SystemAware.StartProcess(file);
- }
+ if (string.IsNullOrEmpty(file)) return;
- public void OnClickPropertiesSaveClipboard()
+ try
{
- if (SlicerFile?.Configs is null) return;
- var sb = new StringBuilder();
+ using TextWriter tw = new StreamWriter(file);
foreach (var config in SlicerFile.Configs)
{
var type = config.GetType();
- sb.AppendLine($"[{type.Name}]");
+ tw.WriteLine($"[{type.Name}]");
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (property.Name.Equals("Item")) continue;
@@ -295,93 +253,134 @@ namespace UVtools.WPF
case null:
continue;
case IList list:
- sb.AppendLine($"{property.Name} = {list.Count}");
+ tw.WriteLine($"{property.Name} = {list.Count}");
break;
default:
- sb.AppendLine($"{property.Name} = {value}");
+ tw.WriteLine($"{property.Name} = {value}");
break;
}
}
-
- sb.AppendLine();
+ tw.WriteLine();
}
-
- Application.Current.Clipboard.SetTextAsync(sb.ToString());
+ tw.Close();
+ }
+ catch (Exception e)
+ {
+ await this.MessageBoxError(e.ToString(), "Error occur while save properties");
+ return;
}
- public void RefreshProperties()
+ var result = await this.MessageBoxQuestion(
+ "Properties save was successful. Do you want open the file in the default editor?",
+ "Properties save complete");
+ if (result != ButtonResult.Yes) return;
+
+ SystemAware.StartProcess(file);
+ }
+
+ public void OnClickPropertiesSaveClipboard()
+ {
+ if (SlicerFile?.Configs is null) return;
+ var sb = new StringBuilder();
+ foreach (var config in SlicerFile.Configs)
{
- SlicerProperties.Clear();
- if (SlicerFile.Configs is null) return;
- foreach (var config in SlicerFile.Configs)
+ var type = config.GetType();
+ sb.AppendLine($"[{type.Name}]");
+ foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
- var type = config.GetType();
- foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
+ if (property.Name.Equals("Item")) continue;
+ var value = property.GetValue(config);
+ switch (value)
{
- if (property.Name.Equals("Item")) continue;
- var value = property.GetValue(config);
- switch (value)
- {
- case null:
- continue;
- case IList list:
- SlicerProperties.Add(new SlicerProperty(property.Name, list.Count.ToString(),
- config.GetType().Name));
- break;
- default:
- SlicerProperties.Add(
- new SlicerProperty(property.Name, value.ToString(), config.GetType().Name));
- break;
- }
+ case null:
+ continue;
+ case IList list:
+ sb.AppendLine($"{property.Name} = {list.Count}");
+ break;
+ default:
+ sb.AppendLine($"{property.Name} = {value}");
+ break;
}
}
+
+ sb.AppendLine();
}
- #endregion
- #region Current Layer
+ Application.Current.Clipboard.SetTextAsync(sb.ToString());
+ }
- public void RefreshCurrentLayerData()
+ public void RefreshProperties()
+ {
+ SlicerProperties.Clear();
+ if (SlicerFile.Configs is null) return;
+ foreach (var config in SlicerFile.Configs)
{
-
- var layer = LayerCache.Layer;
- CurrentLayerProperties.Clear();
- CurrentLayerProperties.Add(new ValueDescription($"{layer.Index}", nameof(layer.Index)));
- CurrentLayerProperties.Add(new ValueDescription($"{Layer.ShowHeight(layer.LayerHeight)}mm", nameof(layer.LayerHeight)));
- CurrentLayerProperties.Add(new ValueDescription($"{Layer.ShowHeight(layer.PositionZ)}mm", nameof(layer.PositionZ)));
- CurrentLayerProperties.Add(new ValueDescription(layer.IsBottomLayer.ToString(), nameof(layer.IsBottomLayer)));
- CurrentLayerProperties.Add(new ValueDescription(layer.IsModified.ToString(), nameof(layer.IsModified)));
-
- if (SlicerFile.CanUseExposureTime)
- CurrentLayerProperties.Add(new ValueDescription($"{layer.ExposureTime:F2}s", nameof(layer.ExposureTime)));
-
- if (SlicerFile.SupportPerLayerSettings)
+ var type = config.GetType();
+ foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
- if (SlicerFile.CanUseLayerLiftHeight)
- CurrentLayerProperties.Add(new ValueDescription($"{layer.LiftHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min", nameof(layer.LiftHeight)));
- if (SlicerFile.CanUseLayerLiftHeight2)
- CurrentLayerProperties.Add(new ValueDescription($"{layer.LiftHeight2.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed2.ToString(CultureInfo.InvariantCulture)}mm/min", nameof(layer.LiftHeight2)));
+ if (property.Name.Equals("Item")) continue;
+ var value = property.GetValue(config);
+ switch (value)
+ {
+ case null:
+ continue;
+ case IList list:
+ SlicerProperties.Add(new SlicerProperty(property.Name, list.Count.ToString(),
+ config.GetType().Name));
+ break;
+ default:
+ SlicerProperties.Add(
+ new SlicerProperty(property.Name, value.ToString(), config.GetType().Name));
+ break;
+ }
+ }
+ }
+ }
+ #endregion
- if (SlicerFile.CanUseLayerRetractSpeed)
- CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.RetractSpeed}mm/min", nameof(layer.RetractHeight)));
- if (SlicerFile.CanUseLayerRetractHeight2)
- CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractHeight2.ToString(CultureInfo.InvariantCulture)}mm @ {layer.RetractSpeed2}mm/min", nameof(layer.RetractHeight2)));
+ #region Current Layer
+
+ public void RefreshCurrentLayerData()
+ {
+
+ var layer = LayerCache.Layer;
+ CurrentLayerProperties.Clear();
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.Index}", nameof(layer.Index)));
+ CurrentLayerProperties.Add(new ValueDescription($"{Layer.ShowHeight(layer.LayerHeight)}mm", nameof(layer.LayerHeight)));
+ CurrentLayerProperties.Add(new ValueDescription($"{Layer.ShowHeight(layer.PositionZ)}mm", nameof(layer.PositionZ)));
+ CurrentLayerProperties.Add(new ValueDescription(layer.IsBottomLayer.ToString(), nameof(layer.IsBottomLayer)));
+ CurrentLayerProperties.Add(new ValueDescription(layer.IsModified.ToString(), nameof(layer.IsModified)));
+
+ if (SlicerFile.CanUseExposureTime)
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.ExposureTime:F2}s", nameof(layer.ExposureTime)));
+
+ if (SlicerFile.SupportPerLayerSettings)
+ {
+ if (SlicerFile.CanUseLayerLiftHeight)
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.LiftHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min", nameof(layer.LiftHeight)));
+ if (SlicerFile.CanUseLayerLiftHeight2)
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.LiftHeight2.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed2.ToString(CultureInfo.InvariantCulture)}mm/min", nameof(layer.LiftHeight2)));
- if (SlicerFile.CanUseLayerLightOffDelay)
- CurrentLayerProperties.Add(new ValueDescription($"{layer.LightOffDelay}s", nameof(layer.LightOffDelay)));
+ if (SlicerFile.CanUseLayerRetractSpeed)
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.RetractSpeed}mm/min", nameof(layer.RetractHeight)));
+ if (SlicerFile.CanUseLayerRetractHeight2)
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractHeight2.ToString(CultureInfo.InvariantCulture)}mm @ {layer.RetractSpeed2}mm/min", nameof(layer.RetractHeight2)));
- if (SlicerFile.CanUseLayerWaitTimeBeforeCure)
- CurrentLayerProperties.Add(new ValueDescription($"{layer.WaitTimeBeforeCure}/{layer.WaitTimeAfterCure}/{layer.WaitTimeAfterLift}s", "WaitTimes:"));
+ if (SlicerFile.CanUseLayerLightOffDelay)
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.LightOffDelay}s", nameof(layer.LightOffDelay)));
- if (SlicerFile.CanUseLayerLightPWM)
- CurrentLayerProperties.Add(new ValueDescription(layer.LightPWM.ToString(), nameof(layer.LightPWM)));
- }
- var materialMillilitersPercent = layer.MaterialMillilitersPercent;
- if (!float.IsNaN(materialMillilitersPercent))
- {
- CurrentLayerProperties.Add(new ValueDescription($"{layer.MaterialMilliliters}ml ({materialMillilitersPercent:F2}%)", nameof(layer.MaterialMilliliters)));
- }
+ if (SlicerFile.CanUseLayerWaitTimeBeforeCure)
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.WaitTimeBeforeCure}/{layer.WaitTimeAfterCure}/{layer.WaitTimeAfterLift}s", "WaitTimes:"));
+ if (SlicerFile.CanUseLayerLightPWM)
+ CurrentLayerProperties.Add(new ValueDescription(layer.LightPWM.ToString(), nameof(layer.LightPWM)));
+ }
+ var materialMillilitersPercent = layer.MaterialMillilitersPercent;
+ if (!float.IsNaN(materialMillilitersPercent))
+ {
+ CurrentLayerProperties.Add(new ValueDescription($"{layer.MaterialMilliliters}ml ({materialMillilitersPercent:F2}%)", nameof(layer.MaterialMilliliters)));
}
- #endregion
+
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs
index 88bce1f..5899de2 100644
--- a/UVtools.WPF/MainWindow.Issues.cs
+++ b/UVtools.WPF/MainWindow.Issues.cs
@@ -20,7 +20,6 @@ using Avalonia.Threading;
using Emgu.CV;
using Emgu.CV.Util;
using MessageBox.Avalonia.Enums;
-using MoreLinq;
using UVtools.Core;
using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
@@ -30,716 +29,715 @@ using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
using Brushes = Avalonia.Media.Brushes;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
- {
- #region Members
- private bool _firstTimeOnIssues = true;
- public DataGrid IssuesGrid;
+ #region Members
+ private bool _firstTimeOnIssues = true;
+ public DataGrid IssuesGrid;
- private int _issueSelectedIndex = -1;
+ private int _issueSelectedIndex = -1;
- public IEnumerable IssuesGridItems
+ public IEnumerable IssuesGridItems
+ {
+ get
{
- get
+ if (!IsFileLoaded || DataContext is null) return null;
+ if (Settings.Issues.DataGridGroupByType || Settings.Issues.DataGridGroupByLayerIndex)
{
- if (!IsFileLoaded || DataContext is null) return null;
- if (Settings.Issues.DataGridGroupByType || Settings.Issues.DataGridGroupByLayerIndex)
- {
- var groupView = new DataGridCollectionView(SlicerFile.IssueManager);
- if (Settings.Issues.DataGridGroupByType) groupView.GroupDescriptions.Add(new DataGridPathGroupDescription("Type"));
- if (Settings.Issues.DataGridGroupByLayerIndex) groupView.GroupDescriptions.Add(new DataGridPathGroupDescription("StartLayerIndex"));
+ var groupView = new DataGridCollectionView(SlicerFile.IssueManager);
+ if (Settings.Issues.DataGridGroupByType) groupView.GroupDescriptions.Add(new DataGridPathGroupDescription("Type"));
+ if (Settings.Issues.DataGridGroupByLayerIndex) groupView.GroupDescriptions.Add(new DataGridPathGroupDescription("StartLayerIndex"));
- return groupView;
- }
-
- return SlicerFile.IssueManager;
+ return groupView;
}
+
+ return SlicerFile.IssueManager;
}
- #endregion
+ }
+ #endregion
- #region Properties
+ #region Properties
- private uint _resinTrapDetectionStartLayer;
+ private uint _resinTrapDetectionStartLayer;
- public bool IssueCanGoPrevious => IsFileLoaded && SlicerFile.IssueManager.Count > 0 && _issueSelectedIndex > 0;
- public bool IssueCanGoNext => IsFileLoaded && SlicerFile.IssueManager.Count > 0 && _issueSelectedIndex < SlicerFile.IssueManager.Count - 1;
+ public bool IssueCanGoPrevious => IsFileLoaded && SlicerFile.IssueManager.Count > 0 && _issueSelectedIndex > 0;
+ public bool IssueCanGoNext => IsFileLoaded && SlicerFile.IssueManager.Count > 0 && _issueSelectedIndex < SlicerFile.IssueManager.Count - 1;
- public uint ResinTrapDetectionStartLayer
- {
- get => _resinTrapDetectionStartLayer;
- set => RaiseAndSetIfChanged(ref _resinTrapDetectionStartLayer, value);
- }
-
- public bool SuppressIssueGridSelectionEvent { get; set; }
+ public uint ResinTrapDetectionStartLayer
+ {
+ get => _resinTrapDetectionStartLayer;
+ set => RaiseAndSetIfChanged(ref _resinTrapDetectionStartLayer, value);
+ }
- #endregion
+ public bool SuppressIssueGridSelectionEvent { get; set; }
- #region Methods
+ #endregion
- public void InitIssues()
- {
- IssuesGrid = this.FindControl<DataGrid>("IssuesGrid");
- IssuesGrid.CellPointerPressed += IssuesGridOnCellPointerPressed;
- IssuesGrid.SelectionChanged += IssuesGridOnSelectionChanged;
- IssuesGrid.KeyUp += IssuesGridOnKeyUp;
- }
+ #region Methods
- public void IssueGoPrevious()
- {
- if (!IssueCanGoPrevious) return;
- IssueSelectedIndex--;
- }
+ public void InitIssues()
+ {
+ IssuesGrid = this.FindControl<DataGrid>("IssuesGrid");
+ IssuesGrid.CellPointerPressed += IssuesGridOnCellPointerPressed;
+ IssuesGrid.SelectionChanged += IssuesGridOnSelectionChanged;
+ IssuesGrid.KeyUp += IssuesGridOnKeyUp;
+ }
- public void IssueGoNext()
- {
- if (!IssueCanGoNext) return;
- IssueSelectedIndex++;
- }
+ public void IssueGoPrevious()
+ {
+ if (!IssueCanGoPrevious) return;
+ IssueSelectedIndex--;
+ }
- public List<IssueOfContours> GetOverlappingIssues(IssueOfContours targetIssue, int indexOffset)
- {
- var retValue = new List<IssueOfContours>();
+ public void IssueGoNext()
+ {
+ if (!IssueCanGoNext) return;
+ IssueSelectedIndex++;
+ }
- int targetLayerIndex = (int)targetIssue.LayerIndex + indexOffset;
- if (targetLayerIndex > SlicerFile.LayerCount - 1 || targetLayerIndex < 0) return retValue;
+ public List<IssueOfContours> GetOverlappingIssues(IssueOfContours targetIssue, int indexOffset)
+ {
+ var retValue = new List<IssueOfContours>();
- foreach (IssueOfContours candidate in SlicerFile.IssueManager.GetIssuesBy(MainIssue.IssueType.SuctionCup, (uint)targetLayerIndex))
- {
- using var vec1 = new VectorOfVectorOfPoint(targetIssue.Contours);
- using var vec2 = new VectorOfVectorOfPoint(candidate.Contours);
- if (!EmguContours.ContoursIntersect(vec1, vec2)) continue;
- retValue.Add(candidate);
- break;
- }
+ int targetLayerIndex = (int)targetIssue.LayerIndex + indexOffset;
+ if (targetLayerIndex > SlicerFile.LayerCount - 1 || targetLayerIndex < 0) return retValue;
- return retValue;
+ foreach (IssueOfContours candidate in SlicerFile.IssueManager.GetIssuesBy(MainIssue.IssueType.SuctionCup, (uint)targetLayerIndex))
+ {
+ using var vec1 = new VectorOfVectorOfPoint(targetIssue.Contours);
+ using var vec2 = new VectorOfVectorOfPoint(candidate.Contours);
+ if (!EmguContours.ContoursIntersect(vec1, vec2)) continue;
+ retValue.Add(candidate);
+ break;
}
- public async void OnClickIssueRemove()
- {
- if (IssuesGrid.SelectedItems.Count == 0) return;
+ return retValue;
+ }
- if (await this.MessageBoxQuestion($"Are you sure you want to remove all selected {IssuesGrid.SelectedItems.Count} issues?\n\n" +
- "Warning: Removing an island can cause other issues to appear if there is material present in the layers above it.\n" +
- "Always check previous and next layers before performing an island removal.", $"Remove {IssuesGrid.SelectedItems.Count} Issues?") != ButtonResult.Yes) return;
+ public async void OnClickIssueRemove()
+ {
+ if (IssuesGrid.SelectedItems.Count == 0) return;
+
+ if (await this.MessageBoxQuestion($"Are you sure you want to remove all selected {IssuesGrid.SelectedItems.Count} issues?\n\n" +
+ "Warning: Removing an island can cause other issues to appear if there is material present in the layers above it.\n" +
+ "Always check previous and next layers before performing an island removal.", $"Remove {IssuesGrid.SelectedItems.Count} Issues?") != ButtonResult.Yes) return;
- var processParallelIssues = new Dictionary<uint, List<Issue>>();
- var processSuctionCups = new List<MainIssue>();
- var layersToRemove = new List<uint>();
+ var processParallelIssues = new Dictionary<uint, List<Issue>>();
+ var processSuctionCups = new List<MainIssue>();
+ var layersToRemove = new List<uint>();
- foreach (MainIssue mainIssue in IssuesGrid.SelectedItems)
+ foreach (MainIssue mainIssue in IssuesGrid.SelectedItems)
+ {
+ switch (mainIssue.Type)
{
- switch (mainIssue.Type)
- {
- case MainIssue.IssueType.Island:
- case MainIssue.IssueType.ResinTrap:
- foreach (var issue in mainIssue)
+ case MainIssue.IssueType.Island:
+ case MainIssue.IssueType.ResinTrap:
+ foreach (var issue in mainIssue)
+ {
+ // Islands and resin traps
+ if (!processParallelIssues.TryGetValue(issue.LayerIndex, out var issueList))
{
- // Islands and resin traps
- if (!processParallelIssues.TryGetValue(issue.LayerIndex, out var issueList))
- {
- issueList = new List<Issue>();
- processParallelIssues.Add(issue.LayerIndex, issueList);
- }
+ issueList = new List<Issue>();
+ processParallelIssues.Add(issue.LayerIndex, issueList);
+ }
- issueList.Add(issue);
+ issueList.Add(issue);
- }
- continue;
- case MainIssue.IssueType.SuctionCup:
- if(mainIssue.StartLayerIndex == 0) continue;
- processSuctionCups.Add(mainIssue);
- continue;
- case MainIssue.IssueType.EmptyLayer:
- layersToRemove.AddRange(mainIssue.Select(issue => issue.LayerIndex));
- continue;
- }
+ }
+ continue;
+ case MainIssue.IssueType.SuctionCup:
+ if(mainIssue.StartLayerIndex == 0) continue;
+ processSuctionCups.Add(mainIssue);
+ continue;
+ case MainIssue.IssueType.EmptyLayer:
+ layersToRemove.AddRange(mainIssue.Select(issue => issue.LayerIndex));
+ continue;
}
+ }
- var totalIssues = processParallelIssues.Count + processSuctionCups.Count + layersToRemove.Count;
- if (totalIssues == 0) return;
+ var totalIssues = processParallelIssues.Count + processSuctionCups.Count + layersToRemove.Count;
+ if (totalIssues == 0) return;
- var issueRemoveList = new List<MainIssue>();
+ var issueRemoveList = new List<MainIssue>();
- IsGUIEnabled = false;
- ShowProgressWindow("Removing selected issues", false);
+ IsGUIEnabled = false;
+ ShowProgressWindow("Removing selected issues", false);
- Clipboard.Snapshot();
+ Clipboard.Snapshot();
- var task = await Task.Factory.StartNew(() =>
+ var task = await Task.Factory.StartNew(() =>
+ {
+ Progress.Reset("Removing selected issues", (uint)processParallelIssues.Count);
+ try
{
- Progress.Reset("Removing selected issues", (uint)processParallelIssues.Count);
- try
+ Parallel.ForEach(processParallelIssues, CoreSettings.ParallelOptions, layerIssues =>
{
- Parallel.ForEach(processParallelIssues, CoreSettings.ParallelOptions, layerIssues =>
+ if (Progress.Token.IsCancellationRequested) return;
+ using (var image = SlicerFile[layerIssues.Key].LayerMat)
{
- if (Progress.Token.IsCancellationRequested) return;
- using (var image = SlicerFile[layerIssues.Key].LayerMat)
- {
- var bytes = image.GetDataByteSpan();
+ var bytes = image.GetDataByteSpan();
- bool edited = false;
- foreach (var issue in layerIssues.Value)
+ bool edited = false;
+ foreach (var issue in layerIssues.Value)
+ {
+ if (issue.Type == MainIssue.IssueType.Island)
{
- if (issue.Type == MainIssue.IssueType.Island)
- {
- var issueOfPoints = (IssueOfPoints)issue;
- foreach (var pixel in issueOfPoints.Points)
- {
- bytes[image.GetPixelPos(pixel.X, pixel.Y)] = 0;
- }
-
- edited = true;
- }
- else if (issue.Type == MainIssue.IssueType.ResinTrap)
+ var issueOfPoints = (IssueOfPoints)issue;
+ foreach (var pixel in issueOfPoints.Points)
{
- var issueOfContours = (IssueOfContours)issue;
- using var contours = new VectorOfVectorOfPoint(issueOfContours.Contours);
- CvInvoke.DrawContours(image, contours, -1, EmguExtensions.WhiteColor, -1);
- if (Settings.LayerRepair.ResinTrapsOverlapBy > 0)
- {
- CvInvoke.DrawContours(image, contours, -1, EmguExtensions.WhiteColor, Settings.LayerRepair.ResinTrapsOverlapBy * 2 + 1);
- }
- edited = true;
+ bytes[image.GetPixelPos(pixel.X, pixel.Y)] = 0;
}
- }
- if (edited)
+ edited = true;
+ }
+ else if (issue.Type == MainIssue.IssueType.ResinTrap)
{
- SlicerFile[layerIssues.Key].LayerMat = image;
+ var issueOfContours = (IssueOfContours)issue;
+ using var contours = new VectorOfVectorOfPoint(issueOfContours.Contours);
+ CvInvoke.DrawContours(image, contours, -1, EmguExtensions.WhiteColor, -1);
+ if (Settings.LayerRepair.ResinTrapsOverlapBy > 0)
+ {
+ CvInvoke.DrawContours(image, contours, -1, EmguExtensions.WhiteColor, Settings.LayerRepair.ResinTrapsOverlapBy * 2 + 1);
+ }
+ edited = true;
}
}
- Progress.LockAndIncrement();
- });
-
- if (layersToRemove.Count > 0)
- {
- OperationLayerRemove.RemoveLayers(SlicerFile, layersToRemove);
+ if (edited)
+ {
+ SlicerFile[layerIssues.Key].LayerMat = image;
+ }
}
- issueRemoveList.AddRange(SlicerFile.IssueManager.DrillSuctionCupsForIssues(processSuctionCups, UserSettings.Instance.LayerRepair.SuctionCupsVentHole, Progress));
+ Progress.LockAndIncrement();
+ });
- }
- catch (Exception ex)
+ if (layersToRemove.Count > 0)
{
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(ex.ToString(), "Removal failed"));
-
- return false;
+ OperationLayerRemove.RemoveLayers(SlicerFile, layersToRemove);
}
- return true;
- });
+ issueRemoveList.AddRange(SlicerFile.IssueManager.DrillSuctionCupsForIssues(processSuctionCups, UserSettings.Instance.LayerRepair.SuctionCupsVentHole, Progress));
- IsGUIEnabled = true;
-
- if (!task)
- {
- Clipboard.RestoreSnapshot();
- return;
}
-
- var whiteListLayers = new List<uint>();
-
- // Update GUI
-
- foreach (MainIssue issue in IssuesGrid.SelectedItems)
+ catch (Exception ex)
{
- if (issue.Type
- is not MainIssue.IssueType.Island
- and not MainIssue.IssueType.ResinTrap
- and not MainIssue.IssueType.EmptyLayer) continue;
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(ex.ToString(), "Removal failed"));
+ return false;
+ }
- issueRemoveList.Add(issue);
+ return true;
+ });
+ IsGUIEnabled = true;
- if (issue.Type == MainIssue.IssueType.Island)
- {
- var nextLayer = issue.StartLayerIndex + 1;
- if (nextLayer >= SlicerFile.LayerCount) continue;
- if (whiteListLayers.Contains(nextLayer)) continue;
- whiteListLayers.Add(nextLayer);
- }
-
- //Issues.Remove(issue);
-
- }
+ if (!task)
+ {
+ Clipboard.RestoreSnapshot();
+ return;
+ }
- if (issueRemoveList.Count == 0) return;
+ var whiteListLayers = new List<uint>();
- Clipboard.Clip($"Manually removed {issueRemoveList.Count} issues");
+ // Update GUI
- IssuesGrid.SelectedIndex = -1;
- SlicerFile.IssueManager.RemoveRange(issueRemoveList);
+ foreach (MainIssue issue in IssuesGrid.SelectedItems)
+ {
+ if (issue.Type
+ is not MainIssue.IssueType.Island
+ and not MainIssue.IssueType.ResinTrap
+ and not MainIssue.IssueType.EmptyLayer) continue;
- if (layersToRemove.Count > 0)
- {
- ResetDataContext();
- }
- if (Settings.PixelEditor.PartialUpdateIslandsOnEditing)
+ issueRemoveList.Add(issue);
+
+
+ if (issue.Type == MainIssue.IssueType.Island)
{
- await UpdateIslandsOverhangs(whiteListLayers);
+ var nextLayer = issue.StartLayerIndex + 1;
+ if (nextLayer >= SlicerFile.LayerCount) continue;
+ if (whiteListLayers.Contains(nextLayer)) continue;
+ whiteListLayers.Add(nextLayer);
}
+
+ //Issues.Remove(issue);
- ShowLayer(); // It will call latter so its a extra call
- CanSave = true;
}
- public async void OnClickIssueIgnore()
- {
- if ((_globalModifiers & KeyModifiers.Alt) != 0)
- {
- if(SlicerFile.IssueManager.IgnoredIssues.Count == 0) return;
- if (await this.MessageBoxQuestion(
- $"Are you sure you want to re-enable {SlicerFile.IssueManager.IgnoredIssues.Count} ignored issues?\n" +
- "A full re-detect will be required to get the ignored issues.\n", $"Re-enable {SlicerFile.IssueManager.IgnoredIssues.Count} Issues?") !=
- ButtonResult.Yes) return;
+ if (issueRemoveList.Count == 0) return;
- SlicerFile.IssueManager.IgnoredIssues.Clear();
+ Clipboard.Clip($"Manually removed {issueRemoveList.Count} issues");
+
+ IssuesGrid.SelectedIndex = -1;
+ SlicerFile.IssueManager.RemoveRange(issueRemoveList);
- return;
- }
+ if (layersToRemove.Count > 0)
+ {
+ ResetDataContext();
+ }
- if (IssuesGrid.SelectedItems.Count == 0) return;
+ if (Settings.PixelEditor.PartialUpdateIslandsOnEditing)
+ {
+ await UpdateIslandsOverhangs(whiteListLayers);
+ }
+
+ ShowLayer(); // It will call latter so its a extra call
+ CanSave = true;
+ }
+ public async void OnClickIssueIgnore()
+ {
+ if ((_globalModifiers & KeyModifiers.Alt) != 0)
+ {
+ if(SlicerFile.IssueManager.IgnoredIssues.Count == 0) return;
if (await this.MessageBoxQuestion(
- $"Are you sure you want to hide and ignore all selected {IssuesGrid.SelectedItems.Count} issues?\n" +
- "The ignored issues won't be re-detected.\n", $"Ignore {IssuesGrid.SelectedItems.Count} Issues?") !=
+ $"Are you sure you want to re-enable {SlicerFile.IssueManager.IgnoredIssues.Count} ignored issues?\n" +
+ "A full re-detect will be required to get the ignored issues.\n", $"Re-enable {SlicerFile.IssueManager.IgnoredIssues.Count} Issues?") !=
ButtonResult.Yes) return;
- var list = IssuesGrid.SelectedItems.Cast<MainIssue>().ToArray();
- SlicerFile.IssueManager.IgnoredIssues.AddRange(list);
- IssuesGrid.SelectedItems.Clear();
- SlicerFile.IssueManager.RemoveRange(list);
- ShowLayer();
- }
+ SlicerFile.IssueManager.IgnoredIssues.Clear();
- private async Task UpdateIslandsOverhangs(List<uint> whiteListLayers)
- {
- if (whiteListLayers.Count == 0) return;
- var islandConfig = GetIslandDetectionConfiguration();
- var overhangConfig = GetOverhangDetectionConfiguration();
- var resinTrapConfig = new ResinTrapDetectionConfiguration(false);
- var touchingBoundConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
- islandConfig.Enabled = true;
- islandConfig.WhiteListLayers = whiteListLayers;
- overhangConfig.Enabled = true;
- overhangConfig.WhiteListLayers = whiteListLayers;
+ return;
+ }
+ if (IssuesGrid.SelectedItems.Count == 0) return;
- IsGUIEnabled = false;
- ShowProgressWindow("Updating Issues");
+ if (await this.MessageBoxQuestion(
+ $"Are you sure you want to hide and ignore all selected {IssuesGrid.SelectedItems.Count} issues?\n" +
+ "The ignored issues won't be re-detected.\n", $"Ignore {IssuesGrid.SelectedItems.Count} Issues?") !=
+ ButtonResult.Yes) return;
+ var list = IssuesGrid.SelectedItems.Cast<MainIssue>().ToArray();
+ SlicerFile.IssueManager.IgnoredIssues.AddRange(list);
+ IssuesGrid.SelectedItems.Clear();
+ SlicerFile.IssueManager.RemoveRange(list);
+ ShowLayer();
+ }
- var issueList = SlicerFile.IssueManager.ToList();
+ private async Task UpdateIslandsOverhangs(List<uint> whiteListLayers)
+ {
+ if (whiteListLayers.Count == 0) return;
+ var islandConfig = GetIslandDetectionConfiguration();
+ var overhangConfig = GetOverhangDetectionConfiguration();
+ var resinTrapConfig = new ResinTrapDetectionConfiguration(false);
+ var touchingBoundConfig = new TouchingBoundDetectionConfiguration(false);
+ var printHeightConfig = new PrintHeightDetectionConfiguration(false);
+ islandConfig.Enabled = true;
+ islandConfig.WhiteListLayers = whiteListLayers;
+ overhangConfig.Enabled = true;
+ overhangConfig.WhiteListLayers = whiteListLayers;
+
+
+ IsGUIEnabled = false;
+ ShowProgressWindow("Updating Issues");
+
+
+ var issueList = SlicerFile.IssueManager.ToList();
+ issueList.RemoveAll(issue =>
+ islandConfig.WhiteListLayers.Contains(issue.StartLayerIndex) && issue.Type is MainIssue.IssueType.Island or MainIssue.IssueType.Overhang);
+ /*foreach (var layerIndex in islandConfig.WhiteListLayers)
+ {
issueList.RemoveAll(issue =>
- islandConfig.WhiteListLayers.Contains(issue.StartLayerIndex) && issue.Type is MainIssue.IssueType.Island or MainIssue.IssueType.Overhang);
- /*foreach (var layerIndex in islandConfig.WhiteListLayers)
- {
- issueList.RemoveAll(issue =>
- issue.LayerIndex == layerIndex && (issue.Type == LayerIssue.IssueType.Island ||
- issue.Type == LayerIssue.IssueType.Overhang));
-
- }*/
+ issue.LayerIndex == layerIndex && (issue.Type == LayerIssue.IssueType.Island ||
+ issue.Type == LayerIssue.IssueType.Overhang));
+
+ }*/
- var resultIssues = await Task.Factory.StartNew(() =>
+ var resultIssues = await Task.Factory.StartNew(() =>
+ {
+ try
{
- try
- {
- var issues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapConfig,
- touchingBoundConfig, printHeightConfig, false, Progress);
+ var issues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapConfig,
+ touchingBoundConfig, printHeightConfig, false, Progress);
- issues.RemoveAll(issue => issue.Type is not MainIssue.IssueType.Island and not MainIssue.IssueType.Overhang); // Remove all non islands and overhangs
- return issues;
- }
+ issues.RemoveAll(issue => issue.Type is not MainIssue.IssueType.Island and not MainIssue.IssueType.Overhang); // Remove all non islands and overhangs
+ return issues;
+ }
- catch (OperationCanceledException)
- {
+ catch (OperationCanceledException)
+ {
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(ex.ToString(), "Error while trying to compute issues"));
- }
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(ex.ToString(), "Error while trying to compute issues"));
+ }
- return null;
- });
+ return null;
+ });
- IsGUIEnabled = true;
+ IsGUIEnabled = true;
- if (resultIssues is not null && resultIssues.Count > 0) issueList.AddRange(resultIssues);
+ if (resultIssues is not null && resultIssues.Count > 0) issueList.AddRange(resultIssues);
- issueList = issueList.OrderBy(issue => issue.Type)
- .ThenBy(issue => issue.StartLayerIndex)
- .ThenBy(issue => issue.Area).ToList();
+ issueList = issueList.OrderBy(issue => issue.Type)
+ .ThenBy(issue => issue.StartLayerIndex)
+ .ThenBy(issue => issue.Area).ToList();
- SlicerFile.IssueManager.ReplaceCollection(issueList);
- }
+ SlicerFile.IssueManager.ReplaceCollection(issueList);
+ }
- public int IssueSelectedIndex
+ public int IssueSelectedIndex
+ {
+ get => _issueSelectedIndex;
+ set
{
- get => _issueSelectedIndex;
- set
- {
- if (!RaiseAndSetIfChanged(ref _issueSelectedIndex, value)) return;
- if(_issueSelectedIndex > 0) IssuesGrid.ScrollIntoView(SlicerFile.IssueManager.FirstOrDefault(issue => ReferenceEquals(issue, IssuesGrid.SelectedItem)), null);
- RaisePropertyChanged(nameof(IssueSelectedIndexStr));
- RaisePropertyChanged(nameof(IssueCanGoPrevious));
- RaisePropertyChanged(nameof(IssueCanGoNext));
- }
+ if (!RaiseAndSetIfChanged(ref _issueSelectedIndex, value)) return;
+ if(_issueSelectedIndex > 0) IssuesGrid.ScrollIntoView(SlicerFile.IssueManager.FirstOrDefault(issue => ReferenceEquals(issue, IssuesGrid.SelectedItem)), null);
+ RaisePropertyChanged(nameof(IssueSelectedIndexStr));
+ RaisePropertyChanged(nameof(IssueCanGoPrevious));
+ RaisePropertyChanged(nameof(IssueCanGoNext));
}
+ }
- public string IssueSelectedIndexStr => IsFileLoaded
- ? (_issueSelectedIndex + 1).ToString().PadLeft(SlicerFile.IssueManager.Count.ToString().Length, '0')
- : "0";
-
- private void IssuesGridOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
- {
- if (DataContext is null || SuppressIssueGridSelectionEvent) return;
+ public string IssueSelectedIndexStr => IsFileLoaded
+ ? (_issueSelectedIndex + 1).ToString().PadLeft(SlicerFile.IssueManager.Count.ToString().Length, '0')
+ : "0";
- if (IssuesGrid.SelectedItem is not MainIssue mainIssue)
- {
- ShowLayer();
- return;
- }
+ private void IssuesGridOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ if (DataContext is null || SuppressIssueGridSelectionEvent) return;
- var issue = mainIssue.FirstOrDefault();
- ZoomToIssue(issue, true);
+ if (IssuesGrid.SelectedItem is not MainIssue mainIssue)
+ {
+ ShowLayer();
+ return;
}
+ var issue = mainIssue.FirstOrDefault();
+ ZoomToIssue(issue, true);
+ }
+
- private void IssuesGridOnCellPointerPressed(object? sender, DataGridCellPointerPressedEventArgs e)
- {
- if (e.PointerPressedEventArgs.ClickCount == 2) return;
- if (IssuesGrid.SelectedItem is not MainIssue) return;
- // Double clicking an issue will center and zoom into the
- // selected issue. Left click on an issue will zoom to fit.
+ private void IssuesGridOnCellPointerPressed(object? sender, DataGridCellPointerPressedEventArgs e)
+ {
+ if (e.PointerPressedEventArgs.ClickCount == 2) return;
+ if (IssuesGrid.SelectedItem is not MainIssue) return;
+ // Double clicking an issue will center and zoom into the
+ // selected issue. Left click on an issue will zoom to fit.
- var pointer = e.PointerPressedEventArgs.GetCurrentPoint(IssuesGrid);
+ var pointer = e.PointerPressedEventArgs.GetCurrentPoint(IssuesGrid);
- if (pointer.Properties.IsRightButtonPressed)
- {
- ZoomToFit();
- return;
- }
+ if (pointer.Properties.IsRightButtonPressed)
+ {
+ ZoomToFit();
+ return;
+ }
- //ForceUpdateActualLayer(issue.LayerIndex);
+ //ForceUpdateActualLayer(issue.LayerIndex);
- }
+ }
- private void IssuesGridOnKeyUp(object? sender, KeyEventArgs e)
+ private void IssuesGridOnKeyUp(object? sender, KeyEventArgs e)
+ {
+ switch (e.Key)
{
- switch (e.Key)
- {
- case Key.Escape:
- IssuesGrid.SelectedItems.Clear();
- break;
- case Key.Multiply:
- var selectedItems = IssuesGrid.SelectedItems.OfType<MainIssue>().ToList();
- IssuesGrid.SelectedItems.Clear();
- foreach (var item in SlicerFile.IssueManager)
- {
- if (!selectedItems.Contains(item))
- IssuesGrid.SelectedItems.Add(item);
- }
+ case Key.Escape:
+ IssuesGrid.SelectedItems.Clear();
+ break;
+ case Key.Multiply:
+ var selectedItems = IssuesGrid.SelectedItems.OfType<MainIssue>().ToList();
+ IssuesGrid.SelectedItems.Clear();
+ foreach (var item in SlicerFile.IssueManager)
+ {
+ if (!selectedItems.Contains(item))
+ IssuesGrid.SelectedItems.Add(item);
+ }
- break;
- case Key.Delete:
- OnClickIssueRemove();
- break;
- }
+ break;
+ case Key.Delete:
+ OnClickIssueRemove();
+ break;
}
+ }
- public async void OnClickRepairIssues()
- {
- await ShowRunOperation(typeof(OperationRepairLayers));
- }
+ public async void OnClickRepairIssues()
+ {
+ await ShowRunOperation(typeof(OperationRepairLayers));
+ }
- public async Task OnClickDetectIssues()
+ public async Task OnClickDetectIssues()
+ {
+ if (!IsFileLoaded) return;
+ if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial)
{
- if (!IsFileLoaded) return;
- if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial)
- {
- await this.MessageBoxError("The file was open in partial mode and the detect issues is unable to run in this mode.\n" +
- "Please reload the file in full mode in order to use detect issues.", "Unable to run in partial mode");
- return;
+ await this.MessageBoxError("The file was open in partial mode and the detect issues is unable to run in this mode.\n" +
+ "Please reload the file in full mode in order to use detect issues.", "Unable to run in partial mode");
+ return;
- }
- await ComputeIssues(
- GetIslandDetectionConfiguration(),
- GetOverhangDetectionConfiguration(),
- GetResinTrapDetectionConfiguration(),
- GetTouchingBoundsDetectionConfiguration(),
- GetPrintHeightDetectionConfiguration(),
- Settings.Issues.ComputeEmptyLayers);
}
+ await ComputeIssues(
+ GetIslandDetectionConfiguration(),
+ GetOverhangDetectionConfiguration(),
+ GetResinTrapDetectionConfiguration(),
+ GetTouchingBoundsDetectionConfiguration(),
+ GetPrintHeightDetectionConfiguration(),
+ Settings.Issues.ComputeEmptyLayers);
+ }
- private async Task ComputeIssues(IslandDetectionConfiguration islandConfig = null,
- OverhangDetectionConfiguration overhangConfig = null,
- ResinTrapDetectionConfiguration resinTrapConfig = null,
- TouchingBoundDetectionConfiguration touchingBoundConfig = null,
- PrintHeightDetectionConfiguration printHeightConfig = null,
- bool emptyLayersConfig = true)
- {
+ private async Task ComputeIssues(IslandDetectionConfiguration islandConfig = null,
+ OverhangDetectionConfiguration overhangConfig = null,
+ ResinTrapDetectionConfiguration resinTrapConfig = null,
+ TouchingBoundDetectionConfiguration touchingBoundConfig = null,
+ PrintHeightDetectionConfiguration printHeightConfig = null,
+ bool emptyLayersConfig = true)
+ {
- SlicerFile.IssueManager.Clear();
- IsGUIEnabled = false;
- ShowProgressWindow("Computing Issues");
+ SlicerFile.IssueManager.Clear();
+ IsGUIEnabled = false;
+ ShowProgressWindow("Computing Issues");
- var resultIssues = await Task.Factory.StartNew(() =>
+ var resultIssues = await Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ var issues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapConfig, touchingBoundConfig,
+ printHeightConfig, emptyLayersConfig, Progress);
+ return issues;
+ }
+ catch (OperationCanceledException)
{
- try
- {
- var issues = SlicerFile.IssueManager.DetectIssues(islandConfig, overhangConfig, resinTrapConfig, touchingBoundConfig,
- printHeightConfig, emptyLayersConfig, Progress);
- return issues;
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(ex.ToString(), "Error while trying compute issues"));
- }
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(ex.ToString(), "Error while trying compute issues"));
+ }
- return null;
- });
+ return null;
+ });
- IsGUIEnabled = true;
+ IsGUIEnabled = true;
- if (resultIssues is null)
- {
- return;
- }
- SlicerFile.IssueManager.AddRange(resultIssues);
+ if (resultIssues is null)
+ {
+ return;
+ }
+ SlicerFile.IssueManager.AddRange(resultIssues);
- ShowLayer();
+ ShowLayer();
- RaisePropertyChanged(nameof(IssueSelectedIndexStr));
- RaisePropertyChanged(nameof(IssueCanGoPrevious));
- RaisePropertyChanged(nameof(IssueCanGoNext));
+ RaisePropertyChanged(nameof(IssueSelectedIndexStr));
+ RaisePropertyChanged(nameof(IssueCanGoPrevious));
+ RaisePropertyChanged(nameof(IssueCanGoNext));
- }
+ }
- /*public Dictionary<uint, uint> GetIssuesCountPerLayer()
+ /*public Dictionary<uint, uint> GetIssuesCountPerLayer()
+ {
+ if (SlicerFile.IssueManager.Count == 0) return null;
+ Dictionary<uint, uint> layerIndexIssueCount = new();
+ foreach (var issue in Issues)
{
- if (SlicerFile.IssueManager.Count == 0) return null;
- Dictionary<uint, uint> layerIndexIssueCount = new();
- foreach (var issue in Issues)
+ if (!layerIndexIssueCount.ContainsKey(issue.StartLayerIndex))
{
- if (!layerIndexIssueCount.ContainsKey(issue.StartLayerIndex))
- {
- layerIndexIssueCount.Add(issue.LayerIndex, 1);
- }
- else
- {
- layerIndexIssueCount[issue.LayerIndex]++;
- }
+ layerIndexIssueCount.Add(issue.LayerIndex, 1);
}
-
- return layerIndexIssueCount;
- }*/
-
- public Dictionary<MainIssue.IssueType, ISolidColorBrush> GetIssueColors(bool highlightColors = false)
- {
- return new Dictionary<MainIssue.IssueType, ISolidColorBrush>
+ else
{
- {MainIssue.IssueType.Island, highlightColors ? Settings.LayerPreview.IslandHighlightBrush : Settings.LayerPreview.IslandBrush},
- {MainIssue.IssueType.Overhang, highlightColors ? Settings.LayerPreview.OverhangHighlightBrush : Settings.LayerPreview.OverhangBrush},
- {MainIssue.IssueType.ResinTrap, highlightColors ? Settings.LayerPreview.ResinTrapHighlightBrush : Settings.LayerPreview.ResinTrapBrush},
- {MainIssue.IssueType.SuctionCup, highlightColors ? Settings.LayerPreview.SuctionCupHighlightBrush : Settings.LayerPreview.SuctionCupBrush},
- {MainIssue.IssueType.TouchingBound, Settings.LayerPreview.TouchingBoundsBrush},
- {MainIssue.IssueType.EmptyLayer, Brushes.Red},
- {MainIssue.IssueType.PrintHeight, Brushes.Red},
- {MainIssue.IssueType.Debug, new ImmutableSolidColorBrush(new Color(255, 15, 112, 16))},
- };
+ layerIndexIssueCount[issue.LayerIndex]++;
+ }
}
- private void UpdateLayerTrackerHighlightIssues()
+ return layerIndexIssueCount;
+ }*/
+
+ public Dictionary<MainIssue.IssueType, ISolidColorBrush> GetIssueColors(bool highlightColors = false)
+ {
+ return new Dictionary<MainIssue.IssueType, ISolidColorBrush>
{
- _issuesSliderCanvas.Children.Clear();
- if (!IsFileLoaded || SlicerFile.IssueManager.Count == 0) return;
+ {MainIssue.IssueType.Island, highlightColors ? Settings.LayerPreview.IslandHighlightBrush : Settings.LayerPreview.IslandBrush},
+ {MainIssue.IssueType.Overhang, highlightColors ? Settings.LayerPreview.OverhangHighlightBrush : Settings.LayerPreview.OverhangBrush},
+ {MainIssue.IssueType.ResinTrap, highlightColors ? Settings.LayerPreview.ResinTrapHighlightBrush : Settings.LayerPreview.ResinTrapBrush},
+ {MainIssue.IssueType.SuctionCup, highlightColors ? Settings.LayerPreview.SuctionCupHighlightBrush : Settings.LayerPreview.SuctionCupBrush},
+ {MainIssue.IssueType.TouchingBound, Settings.LayerPreview.TouchingBoundsBrush},
+ {MainIssue.IssueType.EmptyLayer, Brushes.Red},
+ {MainIssue.IssueType.PrintHeight, Brushes.Red},
+ {MainIssue.IssueType.Debug, new ImmutableSolidColorBrush(new Color(255, 15, 112, 16))},
+ };
+ }
- var tickFrequencySize = _issuesSliderCanvas.Bounds.Height * LayerSlider.TickFrequency / LayerSlider.Maximum;
- var stroke = (int)Math.Ceiling(tickFrequencySize);
+ private void UpdateLayerTrackerHighlightIssues()
+ {
+ _issuesSliderCanvas.Children.Clear();
+ if (!IsFileLoaded || SlicerFile.IssueManager.Count == 0) return;
- var colorDictionary = GetIssueColors(true);
+ var tickFrequencySize = _issuesSliderCanvas.Bounds.Height * LayerSlider.TickFrequency / LayerSlider.Maximum;
+ var stroke = (int)Math.Ceiling(tickFrequencySize);
+ var colorDictionary = GetIssueColors(true);
- var issues = SlicerFile.IssueManager.GetIssues().OrderBy(issue => issue.Parent.Type).DistinctBy(mainIssue => mainIssue.LayerIndex);
- foreach (var issue in issues)
- {
- var color = Brushes.Red;
+ var issues = SlicerFile.IssueManager.GetIssues().OrderBy(issue => issue.Parent.Type).DistinctBy(mainIssue => mainIssue.LayerIndex);
- if (Settings.LayerPreview.UseIssueColorOnTracker) colorDictionary.TryGetValue(issue.Parent.Type, out color);
+ foreach (var issue in issues)
+ {
+ var color = Brushes.Red;
- var yPos = tickFrequencySize * issue.LayerIndex;
- if (issue.LayerIndex == 0 && stroke > 3)
- {
- yPos += tickFrequencySize / 2;
- }
- else if (issue.LayerIndex == SlicerFile.LastLayerIndex && stroke > 3)
- {
- yPos -= tickFrequencySize / 2;
- }
- var line = new Line { StrokeThickness = stroke, Stroke = color, EndPoint = new Avalonia.Point(_issuesSliderCanvas.Width, 0) };
- _issuesSliderCanvas.Children.Add(line);
- Canvas.SetBottom(line, yPos);
- }
+ if (Settings.LayerPreview.UseIssueColorOnTracker) colorDictionary.TryGetValue(issue.Parent.Type, out color);
- /*for (int layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++)
+ var yPos = tickFrequencySize * issue.LayerIndex;
+ if (issue.LayerIndex == 0 && stroke > 3)
+ {
+ yPos += tickFrequencySize / 2;
+ }
+ else if (issue.LayerIndex == SlicerFile.LastLayerIndex && stroke > 3)
{
- var color = Brushes.Red;
+ yPos -= tickFrequencySize / 2;
+ }
+ var line = new Line { StrokeThickness = stroke, Stroke = color, EndPoint = new Avalonia.Point(_issuesSliderCanvas.Width, 0) };
+ _issuesSliderCanvas.Children.Add(line);
+ Canvas.SetBottom(line, yPos);
+ }
- if(Settings.LayerPreview.UseIssueColorOnTracker) colorDictionary.TryGetValue(issue.Type, out color);
+ /*for (int layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++)
+ {
+ var color = Brushes.Red;
- var yPos = tickFrequencySize * layerIndex;
- if (layerIndex == 0 && stroke > 3)
- {
- yPos += tickFrequencySize / 2;
- }
- else if (layerIndex == SlicerFile.LastLayerIndex && stroke > 3)
- {
- yPos -= tickFrequencySize / 2;
- }
- var line = new Line { StrokeThickness = stroke, Stroke = color, EndPoint = new Avalonia.Point(_issuesSliderCanvas.Width, 0) };
- _issuesSliderCanvas.Children.Add(line);
- Canvas.SetBottom(line, yPos);
- }*/
-
- /*var issuesCountPerLayer = GetIssuesCountPerLayer();
- if (issuesCountPerLayer is null)
+ if(Settings.LayerPreview.UseIssueColorOnTracker) colorDictionary.TryGetValue(issue.Type, out color);
+
+ var yPos = tickFrequencySize * layerIndex;
+ if (layerIndex == 0 && stroke > 3)
{
- return;
+ yPos += tickFrequencySize / 2;
}
-
- //var tickFrequencySize = LayerSlider.Track.Bounds.Height * LayerSlider.TickFrequency / (LayerSlider.Maximum - LayerSlider.Minimum);
- var tickFrequencySize = _issuesSliderCanvas.Bounds.Height * LayerSlider.TickFrequency / (LayerSlider.Maximum - LayerSlider.Minimum);
- var stroke = (int)Math.Ceiling(tickFrequencySize);
- foreach (var value in issuesCountPerLayer)
+ else if (layerIndex == SlicerFile.LastLayerIndex && stroke > 3)
{
- var yPos = tickFrequencySize * value.Key;
- if (value.Key == 0 && stroke > 3)
- {
- yPos += tickFrequencySize / 2;
- }
- else if(value.Key == SlicerFile.LastLayerIndex && stroke > 3)
- {
- yPos -= tickFrequencySize / 2;
- }
- var line = new Line{StrokeThickness = stroke, Stroke = Brushes.Red, EndPoint = new Avalonia.Point(_issuesSliderCanvas.Width, 0)};
- _issuesSliderCanvas.Children.Add(line);
- Canvas.SetBottom(line, yPos);
- }*/
- }
-
- public void IssuesClear(bool clearIgnored = true)
+ yPos -= tickFrequencySize / 2;
+ }
+ var line = new Line { StrokeThickness = stroke, Stroke = color, EndPoint = new Avalonia.Point(_issuesSliderCanvas.Width, 0) };
+ _issuesSliderCanvas.Children.Add(line);
+ Canvas.SetBottom(line, yPos);
+ }*/
+
+ /*var issuesCountPerLayer = GetIssuesCountPerLayer();
+ if (issuesCountPerLayer is null)
{
- if (!IsFileLoaded) return;
- SlicerFile.IssueManager.Clear();
- if(clearIgnored) SlicerFile.IssueManager.IgnoredIssues.Clear();
+ return;
}
- public void SetResinTrapDetectionStartLayer(char which)
+ //var tickFrequencySize = LayerSlider.Track.Bounds.Height * LayerSlider.TickFrequency / (LayerSlider.Maximum - LayerSlider.Minimum);
+ var tickFrequencySize = _issuesSliderCanvas.Bounds.Height * LayerSlider.TickFrequency / (LayerSlider.Maximum - LayerSlider.Minimum);
+ var stroke = (int)Math.Ceiling(tickFrequencySize);
+ foreach (var value in issuesCountPerLayer)
{
- switch (which)
+ var yPos = tickFrequencySize * value.Key;
+ if (value.Key == 0 && stroke > 3)
+ {
+ yPos += tickFrequencySize / 2;
+ }
+ else if(value.Key == SlicerFile.LastLayerIndex && stroke > 3)
{
- case 'N':
- ResinTrapDetectionStartLayer = SlicerFile.FirstNormalLayer.Index;
- break;
- case 'C':
- ResinTrapDetectionStartLayer = ActualLayer;
- break;
+ yPos -= tickFrequencySize / 2;
}
+ var line = new Line{StrokeThickness = stroke, Stroke = Brushes.Red, EndPoint = new Avalonia.Point(_issuesSliderCanvas.Width, 0)};
+ _issuesSliderCanvas.Children.Add(line);
+ Canvas.SetBottom(line, yPos);
+ }*/
+ }
+
+ public void IssuesClear(bool clearIgnored = true)
+ {
+ if (!IsFileLoaded) return;
+ SlicerFile.IssueManager.Clear();
+ if(clearIgnored) SlicerFile.IssueManager.IgnoredIssues.Clear();
+ }
+
+ public void SetResinTrapDetectionStartLayer(char which)
+ {
+ switch (which)
+ {
+ case 'N':
+ ResinTrapDetectionStartLayer = SlicerFile.FirstNormalLayer.Index;
+ break;
+ case 'C':
+ ResinTrapDetectionStartLayer = ActualLayer;
+ break;
}
+ }
- public IslandDetectionConfiguration GetIslandDetectionConfiguration(bool enable)
+ public IslandDetectionConfiguration GetIslandDetectionConfiguration(bool enable)
+ {
+ return new()
{
- return new()
- {
- Enabled = enable,
- EnhancedDetection = Settings.Issues.IslandEnhancedDetection,
- AllowDiagonalBonds = Settings.Issues.IslandAllowDiagonalBonds,
- BinaryThreshold = Settings.Issues.IslandBinaryThreshold,
- RequiredAreaToProcessCheck = Settings.Issues.IslandRequiredAreaToProcessCheck,
- RequiredPixelBrightnessToProcessCheck = Settings.Issues.IslandRequiredPixelBrightnessToProcessCheck,
- RequiredPixelsToSupportMultiplier = Settings.Issues.IslandRequiredPixelsToSupportMultiplier,
- RequiredPixelsToSupport = Settings.Issues.IslandRequiredPixelsToSupport,
- RequiredPixelBrightnessToSupport = Settings.Issues.IslandRequiredPixelBrightnessToSupport
- };
- }
- public IslandDetectionConfiguration GetIslandDetectionConfiguration() => GetIslandDetectionConfiguration(Settings.Issues.ComputeIslands);
+ Enabled = enable,
+ EnhancedDetection = Settings.Issues.IslandEnhancedDetection,
+ AllowDiagonalBonds = Settings.Issues.IslandAllowDiagonalBonds,
+ BinaryThreshold = Settings.Issues.IslandBinaryThreshold,
+ RequiredAreaToProcessCheck = Settings.Issues.IslandRequiredAreaToProcessCheck,
+ RequiredPixelBrightnessToProcessCheck = Settings.Issues.IslandRequiredPixelBrightnessToProcessCheck,
+ RequiredPixelsToSupportMultiplier = Settings.Issues.IslandRequiredPixelsToSupportMultiplier,
+ RequiredPixelsToSupport = Settings.Issues.IslandRequiredPixelsToSupport,
+ RequiredPixelBrightnessToSupport = Settings.Issues.IslandRequiredPixelBrightnessToSupport
+ };
+ }
+ public IslandDetectionConfiguration GetIslandDetectionConfiguration() => GetIslandDetectionConfiguration(Settings.Issues.ComputeIslands);
- public OverhangDetectionConfiguration GetOverhangDetectionConfiguration(bool enable)
+ public OverhangDetectionConfiguration GetOverhangDetectionConfiguration(bool enable)
+ {
+ return new()
{
- return new()
- {
- Enabled = enable,
- IndependentFromIslands = Settings.Issues.OverhangIndependentFromIslands,
- ErodeIterations = Settings.Issues.OverhangErodeIterations,
- };
- }
- public OverhangDetectionConfiguration GetOverhangDetectionConfiguration() => GetOverhangDetectionConfiguration(Settings.Issues.ComputeOverhangs);
+ Enabled = enable,
+ IndependentFromIslands = Settings.Issues.OverhangIndependentFromIslands,
+ ErodeIterations = Settings.Issues.OverhangErodeIterations,
+ };
+ }
+ public OverhangDetectionConfiguration GetOverhangDetectionConfiguration() => GetOverhangDetectionConfiguration(Settings.Issues.ComputeOverhangs);
- public ResinTrapDetectionConfiguration GetResinTrapDetectionConfiguration(bool enable)
+ public ResinTrapDetectionConfiguration GetResinTrapDetectionConfiguration(bool enable)
+ {
+ return new()
{
- return new()
- {
- Enabled = enable,
- StartLayerIndex = _resinTrapDetectionStartLayer,
- BinaryThreshold = Settings.Issues.ResinTrapBinaryThreshold,
- RequiredAreaToProcessCheck = Settings.Issues.ResinTrapRequiredAreaToProcessCheck,
- RequiredBlackPixelsToDrain = Settings.Issues.ResinTrapRequiredBlackPixelsToDrain,
- MaximumPixelBrightnessToDrain = Settings.Issues.ResinTrapMaximumPixelBrightnessToDrain,
- DetectSuctionCups = Settings.Issues.ComputeSuctionCups,
- RequiredAreaToConsiderSuctionCup = Settings.Issues.SuctionCupRequiredAreaToConsider,
- RequiredHeightToConsiderSuctionCup = Settings.Issues.SuctionCupRequiredHeightToConsider
- };
- }
- public ResinTrapDetectionConfiguration GetResinTrapDetectionConfiguration() => GetResinTrapDetectionConfiguration(Settings.Issues.ComputeResinTraps);
+ Enabled = enable,
+ StartLayerIndex = _resinTrapDetectionStartLayer,
+ BinaryThreshold = Settings.Issues.ResinTrapBinaryThreshold,
+ RequiredAreaToProcessCheck = Settings.Issues.ResinTrapRequiredAreaToProcessCheck,
+ RequiredBlackPixelsToDrain = Settings.Issues.ResinTrapRequiredBlackPixelsToDrain,
+ MaximumPixelBrightnessToDrain = Settings.Issues.ResinTrapMaximumPixelBrightnessToDrain,
+ DetectSuctionCups = Settings.Issues.ComputeSuctionCups,
+ RequiredAreaToConsiderSuctionCup = Settings.Issues.SuctionCupRequiredAreaToConsider,
+ RequiredHeightToConsiderSuctionCup = Settings.Issues.SuctionCupRequiredHeightToConsider
+ };
+ }
+ public ResinTrapDetectionConfiguration GetResinTrapDetectionConfiguration() => GetResinTrapDetectionConfiguration(Settings.Issues.ComputeResinTraps);
- public TouchingBoundDetectionConfiguration GetTouchingBoundsDetectionConfiguration(bool enable)
+ public TouchingBoundDetectionConfiguration GetTouchingBoundsDetectionConfiguration(bool enable)
+ {
+ return new()
{
- return new()
- {
- Enabled = enable,
- MinimumPixelBrightness = UserSettings.Instance.Issues.TouchingBoundMinimumPixelBrightness,
- MarginLeft = UserSettings.Instance.Issues.TouchingBoundMarginLeft,
- MarginTop = UserSettings.Instance.Issues.TouchingBoundMarginTop,
- MarginRight = UserSettings.Instance.Issues.TouchingBoundMarginRight,
- MarginBottom = UserSettings.Instance.Issues.TouchingBoundMarginBottom,
- };
- }
- public TouchingBoundDetectionConfiguration GetTouchingBoundsDetectionConfiguration() => GetTouchingBoundsDetectionConfiguration(Settings.Issues.ComputeTouchingBounds);
+ Enabled = enable,
+ MinimumPixelBrightness = UserSettings.Instance.Issues.TouchingBoundMinimumPixelBrightness,
+ MarginLeft = UserSettings.Instance.Issues.TouchingBoundMarginLeft,
+ MarginTop = UserSettings.Instance.Issues.TouchingBoundMarginTop,
+ MarginRight = UserSettings.Instance.Issues.TouchingBoundMarginRight,
+ MarginBottom = UserSettings.Instance.Issues.TouchingBoundMarginBottom,
+ };
+ }
+ public TouchingBoundDetectionConfiguration GetTouchingBoundsDetectionConfiguration() => GetTouchingBoundsDetectionConfiguration(Settings.Issues.ComputeTouchingBounds);
- public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration(bool enable)
+ public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration(bool enable)
+ {
+ return new ()
{
- return new ()
- {
- Enabled = enable,
- Offset = (float) Settings.Issues.PrintHeightOffset
- };
- }
- public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration() => GetPrintHeightDetectionConfiguration(Settings.Issues.ComputePrintHeight);
+ Enabled = enable,
+ Offset = (float) Settings.Issues.PrintHeightOffset
+ };
+ }
+ public PrintHeightDetectionConfiguration GetPrintHeightDetectionConfiguration() => GetPrintHeightDetectionConfiguration(Settings.Issues.ComputePrintHeight);
- #endregion
- }
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs
index 3618687..dd822ca 100644
--- a/UVtools.WPF/MainWindow.LayerPreview.cs
+++ b/UVtools.WPF/MainWindow.LayerPreview.cs
@@ -37,2204 +37,2221 @@ using Helpers = UVtools.WPF.Controls.Helpers;
using Point = System.Drawing.Point;
using Size = System.Drawing.Size;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
+ #region Enum
+
+ public enum ZoomToFitType : byte
{
- #region Enum
+ Auto,
+ Image,
+ Volume,
+ Selection
+ };
+ #endregion
+
+ public AdvancedImageBox LayerImageBox { get; private set; }
+ public Slider LayerSlider;
+ public Track LayerSlicerTrack;
+ public Panel LayerNavigationTooltipPanel;
+ public Border LayerNavigationTooltipBorder;
+ private Canvas _issuesSliderCanvas;
+
+
+ private Timer _layerNavigationTooltipTimer = new(0.1) { AutoReset = false };
+ private Timer _layerNavigationSliderDebounceTimer = new(25) { AutoReset = false };
+ private uint _actualLayerSlider;
+ private uint _actualLayer;
+
+
+ private bool _showLayerImageRotated;
+ private bool _showLayerImageRotateCwDirection = true;
+ private bool _showLayerImageRotateCcwDirection;
+ private bool _showLayerImageFlipped;
+ private bool _showLayerImageFlippedHorizontally = true;
+ private bool _showLayerImageFlippedVertically;
+ private bool _showLayerImageDifference;
+ private bool _showLayerImageIssues = true;
+ private bool _showLayerImageCrosshairs = true;
+ private bool _isPixelEditorActive;
+ private bool _showLayerOutlinePrintVolumeBoundary;
+ private bool _showLayerOutlineLayerBoundary;
+ private bool _showLayerOutlineContourBoundary;
+ private bool _showLayerOutlineHollowAreas;
+ private bool _showLayerOutlineCentroids;
+ private bool _showLayerOutlineEdgeDetection;
+ private bool _showLayerOutlineDistanceDetection;
+ private bool _showLayerOutlineSkeletonize;
+
+
+ private bool _isTooltipOverlayVisible;
+ private string _tooltipOverlayText;
+
+ private long _showLayerRenderMs;
+
+ public LayerCache LayerCache = new ();
+ private Point _lastPixelMouseLocation = Point.Empty;
+ private readonly List<Point[]> _maskPoints = new ();
+
+
+ public void InitLayerPreview()
+ {
+ LayerImageBox = this.FindControl<AdvancedImageBox>("LayerImage");
+ LayerSlider = this.FindControl<Slider>("Layer.Navigation.Slider");
+ LayerNavigationTooltipPanel = this.FindControl<Panel>("Layer.Navigation.Tooltip.Panel");
+ LayerNavigationTooltipBorder = this.FindControl<Border>("Layer.Navigation.Tooltip.Border");
+ _issuesSliderCanvas = this.Find<Canvas>("Layer.Navigation.IssuesCanvas");
- public enum ZoomToFitType : byte
+ LayerSlider.TemplateApplied += (sender, e) =>
{
- Auto,
- Image,
- Volume,
- Selection
+ LayerSlicerTrack = e.NameScope.Find<Track>("PART_Track");
};
- #endregion
-
- public AdvancedImageBox LayerImageBox { get; private set; }
- public Slider LayerSlider;
- public Track LayerSlicerTrack;
- public Panel LayerNavigationTooltipPanel;
- public Border LayerNavigationTooltipBorder;
- private Canvas _issuesSliderCanvas;
-
-
- private Timer _layerNavigationTooltipTimer = new(0.1) { AutoReset = false };
- private Timer _layerNavigationSliderDebounceTimer = new(25) { AutoReset = false };
- private uint _actualLayerSlider;
- private uint _actualLayer;
-
-
- private bool _showLayerImageRotated;
- private bool _showLayerImageRotateCwDirection = true;
- private bool _showLayerImageRotateCcwDirection;
- private bool _showLayerImageFlipped;
- private bool _showLayerImageFlippedHorizontally = true;
- private bool _showLayerImageFlippedVertically;
- private bool _showLayerImageDifference;
- private bool _showLayerImageIssues = true;
- private bool _showLayerImageCrosshairs = true;
- private bool _isPixelEditorActive;
- private bool _showLayerOutlinePrintVolumeBoundary;
- private bool _showLayerOutlineLayerBoundary;
- private bool _showLayerOutlineContourBoundary;
- private bool _showLayerOutlineHollowAreas;
- private bool _showLayerOutlineCentroids;
- private bool _showLayerOutlineEdgeDetection;
- private bool _showLayerOutlineDistanceDetection;
- private bool _showLayerOutlineSkeletonize;
-
-
- private bool _isTooltipOverlayVisible;
- private string _tooltipOverlayText;
-
- private long _showLayerRenderMs;
-
- public LayerCache LayerCache = new ();
- private Point _lastPixelMouseLocation = Point.Empty;
- private readonly List<Point[]> _maskPoints = new ();
-
-
- public void InitLayerPreview()
- {
- LayerImageBox = this.FindControl<AdvancedImageBox>("LayerImage");
- LayerSlider = this.FindControl<Slider>("Layer.Navigation.Slider");
- LayerNavigationTooltipPanel = this.FindControl<Panel>("Layer.Navigation.Tooltip.Panel");
- LayerNavigationTooltipBorder = this.FindControl<Border>("Layer.Navigation.Tooltip.Border");
- _issuesSliderCanvas = this.Find<Canvas>("Layer.Navigation.IssuesCanvas");
- LayerSlider.TemplateApplied += (sender, e) =>
- {
- LayerSlicerTrack = e.NameScope.Find<Track>("PART_Track");
- };
-
- _showLayerImageDifference = Settings.LayerPreview.ShowLayerDifference;
- _showLayerOutlinePrintVolumeBoundary = Settings.LayerPreview.VolumeBoundsOutline;
- _showLayerOutlineLayerBoundary = Settings.LayerPreview.LayerBoundsOutline;
- _showLayerOutlineContourBoundary = Settings.LayerPreview.ContourBoundsOutline;
- _showLayerOutlineHollowAreas = Settings.LayerPreview.HollowOutline;
- _showLayerOutlineCentroids = Settings.LayerPreview.CentroidOutline;
+ _showLayerImageDifference = Settings.LayerPreview.ShowLayerDifference;
+ _showLayerOutlinePrintVolumeBoundary = Settings.LayerPreview.VolumeBoundsOutline;
+ _showLayerOutlineLayerBoundary = Settings.LayerPreview.LayerBoundsOutline;
+ _showLayerOutlineContourBoundary = Settings.LayerPreview.ContourBoundsOutline;
+ _showLayerOutlineHollowAreas = Settings.LayerPreview.HollowOutline;
+ _showLayerOutlineCentroids = Settings.LayerPreview.CentroidOutline;
- LayerImageBox.ZoomLevels = new AdvancedImageBox.ZoomLevelCollection(AppSettings.ZoomLevels);
+ LayerImageBox.ZoomLevels = new AdvancedImageBox.ZoomLevelCollection(AppSettings.ZoomLevels);
- LayerImageBox.GetObservable(AdvancedImageBox.ZoomProperty).Subscribe(zoom =>
- {
- if (!IsFileLoaded) return;
- var newZoom = zoom;
- var oldZoom = LayerImageBox.OldZoom;
- RaisePropertyChanged(nameof(LayerZoomStr));
- AddLogVerbose($"Zoomed from {oldZoom} to {newZoom}");
+ LayerImageBox.GetObservable(AdvancedImageBox.ZoomProperty).Subscribe(zoom =>
+ {
+ if (!IsFileLoaded) return;
+ var newZoom = zoom;
+ var oldZoom = LayerImageBox.OldZoom;
+ RaisePropertyChanged(nameof(LayerZoomStr));
+ AddLogVerbose($"Zoomed from {oldZoom} to {newZoom}");
- if (_showLayerImageCrosshairs &&
- SlicerFile.IssueManager.Count > 0 &&
- (oldZoom < 50 &&
- newZoom >= 50 // Trigger refresh as crosshair thickness increases at lower zoom levels
- || oldZoom > 100 && newZoom <= 100
- || oldZoom is >= 50 and <= 100 && (newZoom is < 50 or > 100)
- || oldZoom <= AppSettings.CrosshairFadeLevel &&
- newZoom > AppSettings.CrosshairFadeLevel // Trigger refresh as zoom level manually crosses fade threshold
- || oldZoom > AppSettings.CrosshairFadeLevel && newZoom <= AppSettings.CrosshairFadeLevel)
-
- )
+ if (_showLayerImageCrosshairs &&
+ SlicerFile.IssueManager.Count > 0 &&
+ (oldZoom < 50 &&
+ newZoom >= 50 // Trigger refresh as crosshair thickness increases at lower zoom levels
+ || oldZoom > 100 && newZoom <= 100
+ || oldZoom is >= 50 and <= 100 && (newZoom is < 50 or > 100)
+ || oldZoom <= AppSettings.CrosshairFadeLevel &&
+ newZoom > AppSettings.CrosshairFadeLevel // Trigger refresh as zoom level manually crosses fade threshold
+ || oldZoom > AppSettings.CrosshairFadeLevel && newZoom <= AppSettings.CrosshairFadeLevel)
+
+ )
+ {
+ if (Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues)
{
- if (Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues)
- {
- if (IssuesGrid.SelectedItems.Count == 0 || !IssuesGrid.SelectedItems.Cast<MainIssue>().Any(
+ if (IssuesGrid.SelectedItems.Count == 0 || !IssuesGrid.SelectedItems.Cast<MainIssue>().Any(
mainIssue => // Find a valid candidate to update layer preview, otherwise quit
mainIssue.IsIssueInBetween(_actualLayer)
&& mainIssue.Type is not MainIssue.IssueType.TouchingBound and not MainIssue.IssueType.EmptyLayer)) return;
- }
- else
- {
- if (!SlicerFile.IssueManager.Any(
+ }
+ else
+ {
+ if (!SlicerFile.IssueManager.Any(
mainIssue => // Find a valid candidate to update layer preview, otherwise quit
mainIssue.IsIssueInBetween(_actualLayer)
&& mainIssue.Type is not MainIssue.IssueType.TouchingBound and not MainIssue.IssueType.EmptyLayer)) return;
- }
-
- // A timer is used here rather than invoking ShowLayer directly to eliminate sublte visual flashing
- // that will occur on the transition when the crosshair fades or unfades if ShowLayer is called directly.
- ShowLayer();
}
- });
- LayerImageBox.GetObservable(AdvancedImageBox.SelectionRegionProperty)
- .Subscribe(rect => RaisePropertyChanged(nameof(LayerROIStr)));
+ // A timer is used here rather than invoking ShowLayer directly to eliminate sublte visual flashing
+ // that will occur on the transition when the crosshair fades or unfades if ShowLayer is called directly.
+ ShowLayer();
+ }
+ });
- LayerImageBox.PointerMoved += LayerImageBoxOnPointerMoved;
- LayerImageBox.KeyUp += LayerImageBox_KeyUp;
- LayerImageBox.PointerReleased += LayerImageBox_PointerReleased;
- LayerImageBox.PointerPressed += LayerImageBoxOnPointerPressed;
- LayerImageBox.DoubleTapped += LayerImageBoxOnDoubleTapped;
+ LayerImageBox.GetObservable(AdvancedImageBox.SelectionRegionProperty)
+ .Subscribe(rect => RaisePropertyChanged(nameof(LayerROIStr)));
- _issuesSliderCanvas.PointerWheelChanged += LayerSliderOnPointerWheelChanged;
- LayerSlider.PointerWheelChanged += LayerSliderOnPointerWheelChanged;
- //this.FindControl<Grid>("LayerNavigationSliderGrid").PointerWheelChanged += LayerSliderOnPointerWheelChanged;
+ LayerImageBox.PointerMoved += LayerImageBoxOnPointerMoved;
+ LayerImageBox.KeyDown += LayerImageBox_KeyDown;
+ LayerImageBox.KeyUp += LayerImageBox_KeyUp;
+ LayerImageBox.PointerReleased += LayerImageBox_PointerReleased;
+ LayerImageBox.PointerPressed += LayerImageBoxOnPointerPressed;
+ LayerImageBox.DoubleTapped += LayerImageBoxOnDoubleTapped;
- _layerNavigationTooltipTimer.Elapsed += (sender, args) =>
- {
- Dispatcher.UIThread.InvokeAsync(() => RaisePropertyChanged(nameof(LayerNavigationTooltipMargin)));
- };
- _layerNavigationSliderDebounceTimer.Interval = Settings.LayerPreview.LayerSliderDebounce == 0 ? 1 : Settings.LayerPreview.LayerSliderDebounce;
- _layerNavigationSliderDebounceTimer.Elapsed += (sender, args) =>
- {
- Dispatcher.UIThread.InvokeAsync(ShowLayer);
- };
- }
+ _issuesSliderCanvas.PointerWheelChanged += LayerSliderOnPointerWheelChanged;
+ LayerSlider.PointerWheelChanged += LayerSliderOnPointerWheelChanged;
+ //this.FindControl<Grid>("LayerNavigationSliderGrid").PointerWheelChanged += LayerSliderOnPointerWheelChanged;
- private void LayerSliderOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
+ _layerNavigationTooltipTimer.Elapsed += (sender, args) =>
{
- if (e.Delta.Y > 0)
- ActualLayer++;
- else if (e.Delta.Y < 0 && _actualLayer > 0)
- ActualLayer--;
- }
+ Dispatcher.UIThread.InvokeAsync(() => RaisePropertyChanged(nameof(LayerNavigationTooltipMargin)));
+ };
+ _layerNavigationSliderDebounceTimer.Interval = Settings.LayerPreview.LayerSliderDebounce == 0 ? 1 : Settings.LayerPreview.LayerSliderDebounce;
+ _layerNavigationSliderDebounceTimer.Elapsed += (sender, args) =>
+ {
+ Dispatcher.UIThread.InvokeAsync(ShowLayer);
+ };
+ }
+
+ private void LayerSliderOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
+ {
+ if (e.Delta.Y > 0)
+ ActualLayer++;
+ else if (e.Delta.Y < 0 && _actualLayer > 0)
+ ActualLayer--;
+ }
- public bool ShowLayerImageRotated
+ public bool ShowLayerImageRotated
+ {
+ get => _showLayerImageRotated;
+ set
{
- get => _showLayerImageRotated;
- set
+ var rect = LayerImageBox.SelectionRegion;
+ if (!rect.IsEmpty)
{
- var rect = LayerImageBox.SelectionRegion;
- if (!rect.IsEmpty)
- {
- rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
- }
+ rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
+ }
- if (!RaiseAndSetIfChanged(ref _showLayerImageRotated, value) || !IsFileLoaded) return;
+ if (!RaiseAndSetIfChanged(ref _showLayerImageRotated, value) || !IsFileLoaded) return;
- if (!rect.IsEmpty)
- {
- LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
- }
-
- ZoomToFit();
- ShowLayer();
+ if (!rect.IsEmpty)
+ {
+ LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
}
+
+ ZoomToFit();
+ ShowLayer();
}
+ }
- public bool ShowLayerImageRotateCWDirection
+ public bool ShowLayerImageRotateCWDirection
+ {
+ get => _showLayerImageRotateCwDirection;
+ set
{
- get => _showLayerImageRotateCwDirection;
- set
+ var rect = LayerImageBox.SelectionRegion;
+ if (!rect.IsEmpty)
{
- var rect = LayerImageBox.SelectionRegion;
- if (!rect.IsEmpty)
- {
- rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
- }
-
- if (!RaiseAndSetIfChanged(ref _showLayerImageRotateCwDirection, value)) return;
- if (!_showLayerImageRotated) return;
+ rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
+ }
- if (!rect.IsEmpty)
- {
- LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerImageRotateCwDirection, value)) return;
+ if (!_showLayerImageRotated) return;
- ZoomToFit();
- ShowLayer();
+ if (!rect.IsEmpty)
+ {
+ LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
}
+
+ ZoomToFit();
+ ShowLayer();
}
+ }
- public bool ShowLayerImageRotateCCWDirection
+ public bool ShowLayerImageRotateCCWDirection
+ {
+ get => _showLayerImageRotateCcwDirection;
+ set
{
- get => _showLayerImageRotateCcwDirection;
- set
+ var rect = LayerImageBox.SelectionRegion;
+ if (!rect.IsEmpty)
{
- var rect = LayerImageBox.SelectionRegion;
- if (!rect.IsEmpty)
- {
- rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
- }
+ rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
+ }
- if (!RaiseAndSetIfChanged(ref _showLayerImageRotateCcwDirection, value)) return;
- if (!_showLayerImageRotated) return;
+ if (!RaiseAndSetIfChanged(ref _showLayerImageRotateCcwDirection, value)) return;
+ if (!_showLayerImageRotated) return;
- if (!rect.IsEmpty)
- {
- LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
- }
-
- ZoomToFit();
- ShowLayer();
+ if (!rect.IsEmpty)
+ {
+ LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
}
+
+ ZoomToFit();
+ ShowLayer();
}
+ }
- public bool ShowLayerImageFlipped
+ public bool ShowLayerImageFlipped
+ {
+ get => _showLayerImageFlipped;
+ set
{
- get => _showLayerImageFlipped;
- set
+ var rect = LayerImageBox.SelectionRegion;
+ if (!rect.IsEmpty)
{
- var rect = LayerImageBox.SelectionRegion;
- if (!rect.IsEmpty)
- {
- rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
- }
+ rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
+ }
- if (!RaiseAndSetIfChanged(ref _showLayerImageFlipped, value)) return;
+ if (!RaiseAndSetIfChanged(ref _showLayerImageFlipped, value)) return;
- if (!rect.IsEmpty)
- {
- LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
- }
-
- ShowLayer();
+ if (!rect.IsEmpty)
+ {
+ LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
}
+
+ ShowLayer();
}
+ }
- public bool ShowLayerImageFlippedHorizontally
+ public bool ShowLayerImageFlippedHorizontally
+ {
+ get => _showLayerImageFlippedHorizontally;
+ set
{
- get => _showLayerImageFlippedHorizontally;
- set
+ var rect = LayerImageBox.SelectionRegion;
+ if (!rect.IsEmpty)
{
- var rect = LayerImageBox.SelectionRegion;
- if (!rect.IsEmpty)
- {
- rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
- }
-
- if (!RaiseAndSetIfChanged(ref _showLayerImageFlippedHorizontally, value)) return;
- if (!_showLayerImageFlipped) return;
+ rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
+ }
- if (!rect.IsEmpty)
- {
- LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerImageFlippedHorizontally, value)) return;
+ if (!_showLayerImageFlipped) return;
- ShowLayer();
+ if (!rect.IsEmpty)
+ {
+ LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
}
+
+ ShowLayer();
}
+ }
- public bool ShowLayerImageFlippedVertically
+ public bool ShowLayerImageFlippedVertically
+ {
+ get => _showLayerImageFlippedVertically;
+ set
{
- get => _showLayerImageFlippedVertically;
- set
+ var rect = LayerImageBox.SelectionRegion;
+ if (!rect.IsEmpty)
{
- var rect = LayerImageBox.SelectionRegion;
- if (!rect.IsEmpty)
- {
- rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
- }
+ rect = GetTransposedRectangle(rect.ToDotNet(), true).ToAvalonia();
+ }
- if (!RaiseAndSetIfChanged(ref _showLayerImageFlippedVertically, value)) return;
- if (!_showLayerImageFlipped) return;
+ if (!RaiseAndSetIfChanged(ref _showLayerImageFlippedVertically, value)) return;
+ if (!_showLayerImageFlipped) return;
- if (!rect.IsEmpty)
- {
- LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
- }
-
- ShowLayer();
+ if (!rect.IsEmpty)
+ {
+ LayerImageBox.SelectionRegion = GetTransposedRectangle(rect.ToDotNet()).ToAvalonia();
}
+
+ ShowLayer();
}
+ }
- public bool ShowLayerImageDifference
+ public bool ShowLayerImageDifference
+ {
+ get => _showLayerImageDifference;
+ set
{
- get => _showLayerImageDifference;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerImageDifference, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerImageDifference, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerImageIssues
+ public bool ShowLayerImageIssues
+ {
+ get => _showLayerImageIssues;
+ set
{
- get => _showLayerImageIssues;
- set
+ if (RaiseAndSetIfChanged(ref _showLayerImageIssues, value))
{
- if (RaiseAndSetIfChanged(ref _showLayerImageIssues, value))
- {
- ShowLayer();
- }
+ ShowLayer();
}
}
+ }
- public bool ShowLayerImageCrosshairs
+ public bool ShowLayerImageCrosshairs
+ {
+ get => _showLayerImageCrosshairs;
+ set
{
- get => _showLayerImageCrosshairs;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerImageCrosshairs, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerImageCrosshairs, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlinePrintVolumeBoundary
+ public bool ShowLayerOutlinePrintVolumeBoundary
+ {
+ get => _showLayerOutlinePrintVolumeBoundary;
+ set
{
- get => _showLayerOutlinePrintVolumeBoundary;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlinePrintVolumeBoundary, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlinePrintVolumeBoundary, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlineLayerBoundary
+ public bool ShowLayerOutlineLayerBoundary
+ {
+ get => _showLayerOutlineLayerBoundary;
+ set
{
- get => _showLayerOutlineLayerBoundary;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlineLayerBoundary, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlineLayerBoundary, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlineContourBoundary
+ public bool ShowLayerOutlineContourBoundary
+ {
+ get => _showLayerOutlineContourBoundary;
+ set
{
- get => _showLayerOutlineContourBoundary;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlineContourBoundary, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlineContourBoundary, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlineHollowAreas
+ public bool ShowLayerOutlineHollowAreas
+ {
+ get => _showLayerOutlineHollowAreas;
+ set
{
- get => _showLayerOutlineHollowAreas;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlineHollowAreas, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlineHollowAreas, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlineCentroids
+ public bool ShowLayerOutlineCentroids
+ {
+ get => _showLayerOutlineCentroids;
+ set
{
- get => _showLayerOutlineCentroids;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlineCentroids, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlineCentroids, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlineEdgeDetection
+ public bool ShowLayerOutlineEdgeDetection
+ {
+ get => _showLayerOutlineEdgeDetection;
+ set
{
- get => _showLayerOutlineEdgeDetection;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlineEdgeDetection, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlineEdgeDetection, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlineDistanceDetection
+ public bool ShowLayerOutlineDistanceDetection
+ {
+ get => _showLayerOutlineDistanceDetection;
+ set
{
- get => _showLayerOutlineDistanceDetection;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlineDistanceDetection, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlineDistanceDetection, value)) return;
+ ShowLayer();
}
+ }
- public bool ShowLayerOutlineSkeletonize
+ public bool ShowLayerOutlineSkeletonize
+ {
+ get => _showLayerOutlineSkeletonize;
+ set
{
- get => _showLayerOutlineSkeletonize;
- set
- {
- if (!RaiseAndSetIfChanged(ref _showLayerOutlineSkeletonize, value)) return;
- ShowLayer();
- }
+ if (!RaiseAndSetIfChanged(ref _showLayerOutlineSkeletonize, value)) return;
+ ShowLayer();
}
+ }
- public bool IsPixelEditorActive
+ public bool IsPixelEditorActive
+ {
+ get => _isPixelEditorActive;
+ set
{
- get => _isPixelEditorActive;
- set
+ if (!RaiseAndSetIfChanged(ref _isPixelEditorActive, value)) return;
+ if (_isPixelEditorActive)
{
- if (!RaiseAndSetIfChanged(ref _isPixelEditorActive, value)) return;
- if (_isPixelEditorActive)
- {
- SelectedTabItem = TabPixelEditor;
- }
- else
- {
- DrawModifications(true);
- }
+ SelectedTabItem = TabPixelEditor;
+ }
+ else
+ {
+ DrawModifications(true);
}
}
+ }
- public string MinimumLayerString => SlicerFile is null ? "???" : $"{SlicerFile.LayerHeight}mm\n0";
- public string MaximumLayerString => SlicerFile is null ? "???" : $"{SlicerFile.PrintHeight}mm\n{SlicerFile.LastLayerIndex}";
- public string ActualLayerTooltip => SlicerFile is null ? "???" : $"{Layer.ShowHeight(SlicerFile[_actualLayer]?.PositionZ ?? 0)}mm\n" +
- $"{ActualLayer}\n" +
- $"{(ActualLayer + 1) * 100 / SlicerFile.LayerCount}%";
+ public string MinimumLayerString => SlicerFile is null ? "???" : $"{SlicerFile.LayerHeight}mm\n0";
+ public string MaximumLayerString => SlicerFile is null ? "???" : $"{SlicerFile.PrintHeight}mm\n{SlicerFile.LastLayerIndex}";
+ public string ActualLayerTooltip => SlicerFile is null ? "???" : $"{Layer.ShowHeight(SlicerFile[_actualLayer]?.PositionZ ?? 0)}mm\n" +
+ $"{ActualLayer}\n" +
+ $"{(ActualLayer + 1) * 100 / SlicerFile.LayerCount}%";
- public uint SliderMaximumValue => SlicerFile?.LastLayerIndex ?? 0;
+ public uint SliderMaximumValue => SlicerFile?.LastLayerIndex ?? 0;
- public bool CanGoUp => _actualLayer < SliderMaximumValue;
- public bool CanGoDown => _actualLayer > 0;
+ public bool CanGoUp => _actualLayer < SliderMaximumValue;
+ public bool CanGoDown => _actualLayer > 0;
- public bool IsTooltipOverlayVisible
- {
- get => _isTooltipOverlayVisible;
- set => RaiseAndSetIfChanged(ref _isTooltipOverlayVisible, value);
- }
+ public bool IsTooltipOverlayVisible
+ {
+ get => _isTooltipOverlayVisible;
+ set => RaiseAndSetIfChanged(ref _isTooltipOverlayVisible, value);
+ }
- public string TooltipOverlayText
- {
- get => _tooltipOverlayText;
- set => RaiseAndSetIfChanged(ref _tooltipOverlayText, value);
- }
+ public string TooltipOverlayText
+ {
+ get => _tooltipOverlayText;
+ set => RaiseAndSetIfChanged(ref _tooltipOverlayText, value);
+ }
- public string LayerPixelCountStr
+ public string LayerPixelCountStr
+ {
+ get
{
- get
+ if (!LayerCache.IsCached) return "Pixels: 0";
+ var pixelPercent = Math.Round(LayerCache.Layer.NonZeroPixelCount * 100.0 / (SlicerFile.ResolutionX * SlicerFile.ResolutionY), 2);
+ var text = $"Pixels: {LayerCache.Layer.NonZeroPixelCount} ({pixelPercent}%)";
+ var volume = LayerCache.Layer.Volume;
+ if (volume > 0)
{
- if (!LayerCache.IsCached) return "Pixels: 0";
- var pixelPercent = Math.Round(LayerCache.Layer.NonZeroPixelCount * 100.0 / (SlicerFile.ResolutionX * SlicerFile.ResolutionY), 2);
- var text = $"Pixels: {LayerCache.Layer.NonZeroPixelCount} ({pixelPercent}%)";
- var volume = LayerCache.Layer.Volume;
- if (volume > 0)
- {
- text += $"\nVolume: {volume:F2}mm³";
- }
- return text;
+ text += $"\nVolume: {volume:F2}mm³";
}
+ return text;
}
+ }
- public string LayerBoundsStr
+ public string LayerBoundsStr
+ {
+ get
{
- get
+ if (LayerCache.Layer is null) return "Bounds: NS";
+ var text = $"Bounds: {LayerCache.Layer.BoundingRectangle} ({LayerCache.Layer.BoundingRectangle.Area()}px²)";
+ var rectMillimeters = LayerCache.Layer.BoundingRectangleMillimeters;
+ if (!rectMillimeters.IsEmpty)
{
- if (LayerCache.Layer is null) return "Bounds: NS";
- var text = $"Bounds: {LayerCache.Layer.BoundingRectangle} ({LayerCache.Layer.BoundingRectangle.Area()}px²)";
- var rectMillimeters = LayerCache.Layer.BoundingRectangleMillimeters;
- if (!rectMillimeters.IsEmpty)
- {
- text += $"\nBounds: {rectMillimeters} ({rectMillimeters.Area(2)}mm²)";
- }
-
- return text;
+ text += $"\nBounds: {rectMillimeters} ({rectMillimeters.Area(2)}mm²)";
}
- }
+
+ return text;
+ }
+ }
- public string LayerROIStr
+ public string LayerROIStr
+ {
+ get
{
- get
+ var roi = ROI;
+ if (roi.IsEmpty)
{
- var roi = ROI;
- if (roi.IsEmpty)
- {
- return _maskPoints.Count > 0 ? $"Masks: {_maskPoints.Count}" : "ROI: NS";
- }
- var text = $"ROI: {roi} ({roi.Area()}px²)";
- var roiMillimeters = ROIMillimeters;
- if (!roiMillimeters.IsEmpty)
- {
- text += $"\nROI: {roiMillimeters} ({roiMillimeters.Area(2)}mm²)";
- }
- return text;
+ return _maskPoints.Count > 0 ? $"Masks: {_maskPoints.Count}" : "ROI: NS";
}
+ var text = $"ROI: {roi} ({roi.Area()}px²)";
+ var roiMillimeters = ROIMillimeters;
+ if (!roiMillimeters.IsEmpty)
+ {
+ text += $"\nROI: {roiMillimeters} ({roiMillimeters.Area(2)}mm²)";
+ }
+ return text;
}
+ }
- public long ShowLayerRenderMs
- {
- get => _showLayerRenderMs;
- set => RaiseAndSetIfChanged(ref _showLayerRenderMs, value);
- }
+ public long ShowLayerRenderMs
+ {
+ get => _showLayerRenderMs;
+ set => RaiseAndSetIfChanged(ref _showLayerRenderMs, value);
+ }
- public PixelPicker LayerPixelPicker { get; } = new ();
+ public PixelPicker LayerPixelPicker { get; } = new ();
- public string LayerZoomStr
+ public string LayerZoomStr
+ {
+ get
{
- get
- {
- var pixelSize = SlicerFile?.PixelSizeMicronsMax;
- var text = $"Zoom: [ {LayerImageBox.Zoom / 100m}x{(AppSettings.LockedZoomLevel == LayerImageBox.Zoom ? " 🔒 ]" : " ]")}";
- if (pixelSize > 0) text += $"\nPixel: {SlicerFile.PixelSizeMicronsMax}µm";
- return text;
- }
+ var pixelSize = SlicerFile?.PixelSizeMicronsMax;
+ var text = $"Zoom: [ {LayerImageBox.Zoom / 100m}x{(AppSettings.LockedZoomLevel == LayerImageBox.Zoom ? " 🔒 ]" : " ]")}";
+ if (pixelSize > 0) text += $"\nPixel: {SlicerFile.PixelSizeMicronsMax}µm";
+ return text;
}
+ }
- public string LayerResolutionStr
+ public string LayerResolutionStr
+ {
+ get
{
- get
+ if (SlicerFile is null) return "Unloaded";
+ var text = $"{SlicerFile.Resolution} px";
+ var display = SlicerFile.Display;
+ if (!display.IsEmpty)
{
- if (SlicerFile is null) return "Unloaded";
- var text = $"{SlicerFile.Resolution} px";
- var display = SlicerFile.Display;
- if (!display.IsEmpty)
- {
- text += $"\n{SlicerFile.Display} mm";
- }
-
- return text;
+ text += $"\n{SlicerFile.Display} mm";
}
+
+ return text;
}
+ }
- public uint ActualLayerSlider
+ public uint ActualLayerSlider
+ {
+ get => _actualLayerSlider;
+ set
{
- get => _actualLayerSlider;
- set
+ if (DataContext is null) return;
+ if (!RaiseAndSetIfChanged(ref _actualLayerSlider, value)) return;
+ if (Settings.LayerPreview.LayerSliderDebounce == 0)
{
- if (DataContext is null) return;
- if (!RaiseAndSetIfChanged(ref _actualLayerSlider, value)) return;
- if (Settings.LayerPreview.LayerSliderDebounce == 0)
- {
- ActualLayer = _actualLayerSlider;
- }
- else
- {
- _layerNavigationSliderDebounceTimer.Stop();
- _layerNavigationSliderDebounceTimer.Start();
- ActualLayer = _actualLayerSlider;
- }
+ ActualLayer = _actualLayerSlider;
+ }
+ else
+ {
+ _layerNavigationSliderDebounceTimer.Stop();
+ _layerNavigationSliderDebounceTimer.Start();
+ ActualLayer = _actualLayerSlider;
}
}
+ }
- public uint ActualLayer
+ public uint ActualLayer
+ {
+ get => _actualLayer;
+ set
{
- get => _actualLayer;
- set
- {
- if (DataContext is null) return;
- if (!RaiseAndSetIfChanged(ref _actualLayer, value)) return;
+ if (DataContext is null) return;
+ if (!RaiseAndSetIfChanged(ref _actualLayer, value)) return;
- if (!_layerNavigationSliderDebounceTimer.Enabled) // Doesn't come from ActualLayerSlider timer
- {
- ActualLayerSlider = _actualLayer; // sync when required
- ShowLayer(); // Show layer only if timer is not present
- }
-
- InvalidateLayerNavigation();
+ if (!_layerNavigationSliderDebounceTimer.Enabled) // Doesn't come from ActualLayerSlider timer
+ {
+ ActualLayerSlider = _actualLayer; // sync when required
+ ShowLayer(); // Show layer only if timer is not present
}
- }
- public void ForceUpdateActualLayer(uint layerIndex = 0)
- {
- //_actualLayer = layerIndex;
- /*ShowLayer();
InvalidateLayerNavigation();
- RaisePropertyChanged(nameof(ActualLayer));*/
- _actualLayer = uint.MaxValue;
- ActualLayer = layerIndex;
}
+ }
- public void InvalidateLayerNavigation()
- {
- RaisePropertyChanged(nameof(CanGoDown));
- RaisePropertyChanged(nameof(CanGoUp));
- RaisePropertyChanged(nameof(ActualLayerTooltip));
- //RaisePropertyChanged(nameof(LayerNavigationTooltipMargin));
- RaisePropertyChanged(nameof(LayerPixelCountStr));
- RaisePropertyChanged(nameof(LayerBoundsStr));
- _layerNavigationTooltipTimer.Start();
- }
+ public void ForceUpdateActualLayer(uint layerIndex = 0)
+ {
+ //_actualLayer = layerIndex;
+ /*ShowLayer();
+ InvalidateLayerNavigation();
+ RaisePropertyChanged(nameof(ActualLayer));*/
+ _actualLayer = uint.MaxValue;
+ ActualLayer = layerIndex;
+ }
+
+ public void InvalidateLayerNavigation()
+ {
+ RaisePropertyChanged(nameof(CanGoDown));
+ RaisePropertyChanged(nameof(CanGoUp));
+ RaisePropertyChanged(nameof(ActualLayerTooltip));
+ //RaisePropertyChanged(nameof(LayerNavigationTooltipMargin));
+ RaisePropertyChanged(nameof(LayerPixelCountStr));
+ RaisePropertyChanged(nameof(LayerBoundsStr));
+ _layerNavigationTooltipTimer.Start();
+ }
- public Thickness LayerNavigationTooltipMargin
+ public Thickness LayerNavigationTooltipMargin
+ {
+ get
{
- get
+ double top = 0;
+ if (LayerSlicerTrack != null)
{
- double top = 0;
- if (LayerSlicerTrack != null)
- {
- double trackerPos = LayerSlicerTrack.Thumb.Bounds.Height / 2 + LayerSlicerTrack.Thumb.Bounds.Top;
- double halfTooltipHeight = LayerNavigationTooltipBorder.Bounds.Height / 2;
- top = (trackerPos - halfTooltipHeight).Clamp(0,
- LayerSlider.Bounds.Height - LayerNavigationTooltipBorder.Bounds.Height);
+ double trackerPos = LayerSlicerTrack.Thumb.Bounds.Height / 2 + LayerSlicerTrack.Thumb.Bounds.Top;
+ double halfTooltipHeight = LayerNavigationTooltipBorder.Bounds.Height / 2;
+ top = (trackerPos - halfTooltipHeight).Clamp(0,
+ LayerSlider.Bounds.Height - LayerNavigationTooltipBorder.Bounds.Height);
- }
- return new Thickness(
- 0,
- top,
- 5,
- 0);
}
+ return new Thickness(
+ 0,
+ top,
+ 5,
+ 0);
}
+ }
- #region ROI & Mask
- public Rectangle ROI
+ #region ROI & Mask
+ public Rectangle ROI
+ {
+ get
{
- get
- {
- var rect = LayerImageBox.SelectionRegion;
- return rect.IsEmpty ? Rectangle.Empty : GetTransposedRectangle(rect.ToDotNet(), true);
- }
- set => LayerImageBox.SelectionRegion = GetTransposedRectangle(value).ToAvalonia();
+ var rect = LayerImageBox.SelectionRegion;
+ return rect.IsEmpty ? Rectangle.Empty : GetTransposedRectangle(rect.ToDotNet(), true);
}
+ set => LayerImageBox.SelectionRegion = GetTransposedRectangle(value).ToAvalonia();
+ }
- public RectangleF ROIMillimeters
+ public RectangleF ROIMillimeters
+ {
+ get
{
- get
- {
- var roi = ROI;
- var pixelSize = SlicerFile.PixelSize;
- if(roi.IsEmpty || pixelSize.IsEmpty) return RectangleF.Empty;
- return new RectangleF(
- (float)Math.Round(roi.X * pixelSize.Width, 2),
- (float)Math.Round(roi.Y * pixelSize.Height, 2),
- (float)Math.Round(roi.Width * pixelSize.Width, 2),
- (float)Math.Round(roi.Height * pixelSize.Height, 2));
- }
+ var roi = ROI;
+ var pixelSize = SlicerFile.PixelSize;
+ if(roi.IsEmpty || pixelSize.IsEmpty) return RectangleF.Empty;
+ return new RectangleF(
+ (float)Math.Round(roi.X * pixelSize.Width, 2),
+ (float)Math.Round(roi.Y * pixelSize.Height, 2),
+ (float)Math.Round(roi.Width * pixelSize.Width, 2),
+ (float)Math.Round(roi.Height * pixelSize.Height, 2));
}
+ }
- public void SelectModelVolumeRoi()
- {
- ROI = SlicerFile.BoundingRectangle;
- }
+ public void SelectModelVolumeRoi()
+ {
+ ROI = SlicerFile.BoundingRectangle;
+ }
- public void SelectLayerVolumeRoi()
- {
- ROI = LayerCache.Layer.BoundingRectangle;
- }
+ public void SelectLayerVolumeRoi()
+ {
+ ROI = LayerCache.Layer.BoundingRectangle;
+ }
- public List<Point[]> MaskPoints => _maskPoints;
+ public List<Point[]> MaskPoints => _maskPoints;
- /*private set
- {
- if(!RaiseAndSetIfChanged(ref _maskPoints, value)) return;
- ShowLayer();
- }*/
- public void AddMaskPoints(Point[] points, bool refreshLayer = true)
+ /*private set
{
- if (_maskPoints.RemoveAll(points1 => points1.SequenceEqual(points)) <= 0)
- {
- _maskPoints.Add(points);
- }
+ if(!RaiseAndSetIfChanged(ref _maskPoints, value)) return;
+ ShowLayer();
+ }*/
+ public void AddMaskPoints(Point[] points, bool refreshLayer = true)
+ {
+ if (_maskPoints.RemoveAll(points1 => points1.SequenceEqual(points)) <= 0)
+ {
+ _maskPoints.Add(points);
+ }
- if(_maskPoints.Count > 0 && Settings.LayerPreview.MaskClearROIAfterSet) ClearROI();
+ if(_maskPoints.Count > 0 && Settings.LayerPreview.MaskClearROIAfterSet) ClearROI();
- if(refreshLayer) ShowLayer();
- RaisePropertyChanged(nameof(LayerROIStr));
- }
+ if(refreshLayer) ShowLayer();
+ RaisePropertyChanged(nameof(LayerROIStr));
+ }
- public void AddMaskPoints(IEnumerable<Point[]> pointsOfPoints, bool clear = true)
+ public void AddMaskPoints(IEnumerable<Point[]> pointsOfPoints, bool clear = true)
+ {
+ if (clear)
{
- if (clear)
- {
- _maskPoints.Clear();
- _maskPoints.AddRange(pointsOfPoints);
- }
- else
+ _maskPoints.Clear();
+ _maskPoints.AddRange(pointsOfPoints);
+ }
+ else
+ {
+ foreach (var points in pointsOfPoints)
{
- foreach (var points in pointsOfPoints)
+ if (_maskPoints.RemoveAll(points1 => points1.SequenceEqual(points)) <= 0)
{
- if (_maskPoints.RemoveAll(points1 => points1.SequenceEqual(points)) <= 0)
- {
- _maskPoints.Add(points);
- }
+ _maskPoints.Add(points);
}
-
}
-
- ShowLayer();
- RaisePropertyChanged(nameof(LayerROIStr));
+
}
+
+ ShowLayer();
+ RaisePropertyChanged(nameof(LayerROIStr));
+ }
- public void SelectLayerPositiveAreasMask()
- {
- AddMaskPoints(LayerCache.LayerContours.ToArrayOfArray());
- if (_maskPoints.Count > 0 && Settings.LayerPreview.MaskClearROIAfterSet) ClearROI();
- }
+ public void SelectLayerPositiveAreasMask()
+ {
+ AddMaskPoints(LayerCache.LayerContours.ToArrayOfArray());
+ if (_maskPoints.Count > 0 && Settings.LayerPreview.MaskClearROIAfterSet) ClearROI();
+ }
- public void SelectLayerHollowAreasMask()
- {
- var contours = EmguContours.GetNegativeContours(LayerCache.LayerContours, LayerCache.LayerContourHierarchy);
- AddMaskPoints(contours.ToArrayOfArray());
- if (_maskPoints.Count > 0 && Settings.LayerPreview.MaskClearROIAfterSet) ClearROI();
- }
+ public void SelectLayerHollowAreasMask()
+ {
+ var contours = EmguContours.GetNegativeContours(LayerCache.LayerContours, LayerCache.LayerContourHierarchy);
+ AddMaskPoints(contours.ToArrayOfArray());
+ if (_maskPoints.Count > 0 && Settings.LayerPreview.MaskClearROIAfterSet) ClearROI();
+ }
- public void ClearMask()
- {
- if (_maskPoints.Count <= 0) return;
- _maskPoints.Clear();
- ShowLayer();
- RaisePropertyChanged(nameof(LayerROIStr));
- }
+ public void ClearMask()
+ {
+ if (_maskPoints.Count <= 0) return;
+ _maskPoints.Clear();
+ ShowLayer();
+ RaisePropertyChanged(nameof(LayerROIStr));
+ }
- public void ClearROI()
- {
- ROI = Rectangle.Empty;
- }
+ public void ClearROI()
+ {
+ ROI = Rectangle.Empty;
+ }
- public void ClearROIAndMask()
- {
- ClearROI();
- ClearMask();
- }
+ public void ClearROIAndMask()
+ {
+ ClearROI();
+ ClearMask();
+ }
- public void OnROIClick()
- {
- ZoomToFit(ZoomToFitType.Selection);
- }
- #endregion
+ public void OnROIClick()
+ {
+ ZoomToFit(ZoomToFitType.Selection);
+ }
+ #endregion
- public void GoFirstLayer()
- {
- if (!IsFileLoaded) return;
- if (!CanGoDown) return;
- ActualLayer = 0;
- }
+ public void GoFirstLayer()
+ {
+ if (!IsFileLoaded) return;
+ if (!CanGoDown) return;
+ ActualLayer = 0;
+ }
- public void GoPreviousLayer()
- {
- if (!IsFileLoaded) return;
- if (!CanGoDown) return;
- ActualLayer--;
- }
+ public void GoPreviousLayer()
+ {
+ if (!IsFileLoaded) return;
+ if (!CanGoDown) return;
+ ActualLayer--;
+ }
- public void GoNextLayer()
- {
- if (!IsFileLoaded) return;
- if (!CanGoUp) return;
- ActualLayer++;
- }
+ public void GoNextLayer()
+ {
+ if (!IsFileLoaded) return;
+ if (!CanGoUp) return;
+ ActualLayer++;
+ }
- public void GoLastLayer()
- {
- if (!IsFileLoaded) return;
- if (!CanGoUp) return;
- ActualLayer = SliderMaximumValue;
- }
+ public void GoLastLayer()
+ {
+ if (!IsFileLoaded) return;
+ if (!CanGoUp) return;
+ ActualLayer = SliderMaximumValue;
+ }
+
+ public void GoMassLayer(string which)
+ {
+ if (!IsFileLoaded) return;
+ var layer = which switch
+ {
+ "SB" => SlicerFile.SmallestBottomLayer,
+ "LB" => SlicerFile.LargestBottomLayer,
+ "SN" => SlicerFile.SmallestNormalLayer,
+ "LN" => SlicerFile.LargestNormalLayer,
+ _ => null
+ };
+ if (layer is null) return;
+ ActualLayer = layer.Index;
+ }
+
+ public void RefreshLayerImage()
+ {
+ LayerImageBox.Image = LayerCache.ImageBgr.ToBitmap();
+ }
- public void GoMassLayer(string which)
+ /// <summary>
+ /// Shows a layer number
+ /// </summary>
+ unsafe void ShowLayer()
+ {
+ if (!IsFileLoaded) return;
+
+ var sanitizedLayerIndex = Math.Min(_actualLayer, SlicerFile.LastLayerIndex);
+ if (sanitizedLayerIndex != _actualLayer)
{
- if (!IsFileLoaded) return;
- var layer = which switch
- {
- "SB" => SlicerFile.LayerManager.SmallestBottomLayer,
- "LB" => SlicerFile.LayerManager.LargestBottomLayer,
- "SN" => SlicerFile.LayerManager.SmallestNormalLayer,
- "LN" => SlicerFile.LayerManager.LargestNormalLayer,
- _ => null
- };
- if (layer is null) return;
- ActualLayer = layer.Index;
+ _actualLayer = sanitizedLayerIndex;
+ InvalidateLayerNavigation();
}
- public void RefreshLayerImage()
+ var watch = Stopwatch.StartNew();
+ LayerCache.Layer = SlicerFile[_actualLayer];
+ if (LayerCache.Image is null)
{
- LayerImageBox.Image = LayerCache.ImageBgr.ToBitmap();
+ RefreshCurrentLayerData();
+ return;
}
-
- /// <summary>
- /// Shows a layer number
- /// </summary>
- unsafe void ShowLayer()
+ try
{
- if (!IsFileLoaded) return;
+ //var imageSpan = LayerCache.Image.GetPixelSpan<byte>();
+ //var imageBgrSpan = LayerCache.ImageBgr.GetPixelSpan<byte>();
- var sanitizedLayerIndex = Math.Min(_actualLayer, SlicerFile.LastLayerIndex);
- if (sanitizedLayerIndex != _actualLayer)
+ var imageSpan = LayerCache.ImageSpan;
+ var imageBgrSpan = LayerCache.ImageBgrSpan;
+
+ if (_showLayerOutlineEdgeDetection)
{
- _actualLayer = sanitizedLayerIndex;
- InvalidateLayerNavigation();
+ using var canny = new Mat();
+ CvInvoke.Canny(LayerCache.Image, canny, 80, 40, 3, true);
+ CvInvoke.CvtColor(canny, LayerCache.ImageBgr, ColorConversion.Gray2Bgr);
}
-
- var watch = Stopwatch.StartNew();
- LayerCache.Layer = SlicerFile[_actualLayer];
- if (LayerCache.Image is null)
+ else if (_showLayerOutlineDistanceDetection)
{
- RefreshCurrentLayerData();
- return;
+ using var distance = new Mat();
+ CvInvoke.DistanceTransform(LayerCache.Image, distance, null, DistType.C, 3);
+ //distance.ConvertTo(distance, DepthType.Cv8U);
+ CvInvoke.Normalize(distance, distance, byte.MinValue, byte.MaxValue, NormType.MinMax, DepthType.Cv8U);
+ CvInvoke.CvtColor(distance, LayerCache.ImageBgr, ColorConversion.Gray2Bgr);
}
- try
+ else if (_showLayerOutlineSkeletonize)
{
- //var imageSpan = LayerCache.Image.GetPixelSpan<byte>();
- //var imageBgrSpan = LayerCache.ImageBgr.GetPixelSpan<byte>();
-
- var imageSpan = LayerCache.ImageSpan;
- var imageBgrSpan = LayerCache.ImageBgrSpan;
+ using var skeletonize = LayerCache.Image.Skeletonize();
+ CvInvoke.CvtColor(skeletonize, LayerCache.ImageBgr, ColorConversion.Gray2Bgr);
+ }
+ else if (_showLayerImageDifference)
+ {
+ //if (_actualLayer > 0 && _actualLayer < SlicerFile.LayerCount - 1)
+ // {
+ var previousLayer = _actualLayer > 0 ? SlicerFile[_actualLayer - 1] : null;
+ var nextLayer = _actualLayer < SlicerFile.LastLayerIndex ? SlicerFile[_actualLayer + 1] : null;
+ Mat previousImage = null;
+ Mat nextImage = null;
- if (_showLayerOutlineEdgeDetection)
+ // Optimize empties for now...
+ var rect = Rectangle.Empty;
+ if (!LayerCache.Layer.IsEmpty)
{
- using var canny = new Mat();
- CvInvoke.Canny(LayerCache.Image, canny, 80, 40, 3, true);
- CvInvoke.CvtColor(canny, LayerCache.ImageBgr, ColorConversion.Gray2Bgr);
+ rect = LayerCache.Layer.BoundingRectangle;
}
- else if (_showLayerOutlineDistanceDetection)
+ else if (previousLayer is not null && !previousLayer.IsEmpty)
{
- using var distance = new Mat();
- CvInvoke.DistanceTransform(LayerCache.Image, distance, null, DistType.C, 3);
- //distance.ConvertTo(distance, DepthType.Cv8U);
- CvInvoke.Normalize(distance, distance, byte.MinValue, byte.MaxValue, NormType.MinMax, DepthType.Cv8U);
- CvInvoke.CvtColor(distance, LayerCache.ImageBgr, ColorConversion.Gray2Bgr);
+ rect = previousLayer.BoundingRectangle;
}
- else if (_showLayerOutlineSkeletonize)
+ else if (nextLayer is not null && !nextLayer.IsEmpty)
{
- using var skeletonize = LayerCache.Image.Skeletonize();
- CvInvoke.CvtColor(skeletonize, LayerCache.ImageBgr, ColorConversion.Gray2Bgr);
+ rect = nextLayer.BoundingRectangle;
}
- else if (_showLayerImageDifference)
+
+ if (previousLayer is not null && !previousLayer.IsEmpty)
{
- //if (_actualLayer > 0 && _actualLayer < SlicerFile.LayerCount - 1)
- // {
- var previousLayer = _actualLayer > 0 ? SlicerFile[_actualLayer - 1] : null;
- var nextLayer = _actualLayer < SlicerFile.LastLayerIndex ? SlicerFile[_actualLayer + 1] : null;
- Mat previousImage = null;
- Mat nextImage = null;
-
- // Optimize empties for now...
- var rect = Rectangle.Empty;
- if (!LayerCache.Layer.IsEmpty)
- {
- rect = LayerCache.Layer.BoundingRectangle;
- }
- else if (previousLayer is not null && !previousLayer.IsEmpty)
- {
- rect = previousLayer.BoundingRectangle;
- }
- else if (nextLayer is not null && !nextLayer.IsEmpty)
- {
- rect = nextLayer.BoundingRectangle;
- }
+ rect = Rectangle.Union(rect, previousLayer.BoundingRectangle);
+ }
+ if (nextLayer is not null && !nextLayer.IsEmpty)
+ {
+ rect = Rectangle.Union(rect, nextLayer.BoundingRectangle);
+ }
- if (previousLayer is not null && !previousLayer.IsEmpty)
- {
- rect = Rectangle.Union(rect, previousLayer.BoundingRectangle);
- }
- if (nextLayer is not null && !nextLayer.IsEmpty)
- {
- rect = Rectangle.Union(rect, nextLayer.BoundingRectangle);
- }
+ /*var rect = Rectangle.Union(
+ Rectangle.Union(LayerCache.Layer.BoundingRectangle, previousLayer.BoundingRectangle),
+ nextLayer.BoundingRectangle);*/
- /*var rect = Rectangle.Union(
- Rectangle.Union(LayerCache.Layer.BoundingRectangle, previousLayer.BoundingRectangle),
- nextLayer.BoundingRectangle);*/
+ if (!rect.IsEmpty && (previousLayer is not null || nextLayer is not null))
+ {
+ byte* previousSpan = null;
+ byte* nextSpan = null;
+ // Can improve performance on >4K images?
+ Parallel.Invoke(
+ () =>
+ {
+ if (previousLayer is null) return;
+ previousImage = previousLayer.LayerMat;
+ previousSpan = previousImage.GetBytePointer();
+ },
+ () =>
+ {
+ if (nextLayer is null) return;
+ nextImage = nextLayer.LayerMat;
+ nextSpan = nextImage.GetBytePointer();
+ });
- if (!rect.IsEmpty && (previousLayer is not null || nextLayer is not null))
+ /*using (var previousImage = SlicerFile[_actualLayer - 1].LayerMat)
+ using (var nextImage = SlicerFile[_actualLayer + 1].LayerMat)
+ {*/
+ //var previousSpan = previousImage.GetPixelSpan<byte>();
+ //var nextSpan = nextImage.GetPixelSpan<byte>();
+
+
+ int width = LayerCache.Image.GetRealStep();
+ int channels = LayerCache.ImageBgr.NumberOfChannels;
+ bool showSimilarityInstead =
+ Settings.LayerPreview.LayerDifferenceHighlightSimilarityInstead;
+
+ Parallel.For(rect.Y, rect.Bottom, CoreSettings.ParallelOptions, y =>
{
- byte* previousSpan = null;
- byte* nextSpan = null;
- // Can improve performance on >4K images?
- Parallel.Invoke(
- () =>
+ for (int x = rect.X; x < rect.Right; x++)
+ {
+ int pixel = y * width + x;
+ if (showSimilarityInstead)
{
- if (previousLayer is null) return;
- previousImage = previousLayer.LayerMat;
- previousSpan = previousImage.GetBytePointer();
- },
- () =>
+ if (imageSpan[pixel] == 0) continue;
+ }
+ else
{
- if (nextLayer is null) return;
- nextImage = nextLayer.LayerMat;
- nextSpan = nextImage.GetBytePointer();
- });
-
- /*using (var previousImage = SlicerFile[_actualLayer - 1].LayerMat)
- using (var nextImage = SlicerFile[_actualLayer + 1].LayerMat)
- {*/
- //var previousSpan = previousImage.GetPixelSpan<byte>();
- //var nextSpan = nextImage.GetPixelSpan<byte>();
-
+ if (imageSpan[pixel] != 0) continue;
+ }
- int width = LayerCache.Image.GetRealStep();
- int channels = LayerCache.ImageBgr.NumberOfChannels;
- bool showSimilarityInstead =
- Settings.LayerPreview.LayerDifferenceHighlightSimilarityInstead;
+ byte brightness = 0;
- Parallel.For(rect.Y, rect.Bottom, CoreSettings.ParallelOptions, y =>
- {
- for (int x = rect.X; x < rect.Right; x++)
+ var color = Color.Empty;
+ if (previousSpan is not null && nextSpan is not null && previousSpan[pixel] > 0 && nextSpan[pixel] > 0)
{
- int pixel = y * width + x;
- if (showSimilarityInstead)
- {
- if (imageSpan[pixel] == 0) continue;
- }
- else
- {
- if (imageSpan[pixel] != 0) continue;
- }
-
- byte brightness = 0;
-
- var color = Color.Empty;
- if (previousSpan is not null && nextSpan is not null && previousSpan[pixel] > 0 && nextSpan[pixel] > 0)
- {
- brightness = Math.Max(previousSpan[pixel], nextSpan[pixel]);
- color = Settings.LayerPreview.BothLayerDifferenceColor;
- }
- else if (previousSpan is not null && previousSpan[pixel] > 0)
- {
- brightness = previousSpan[pixel];
- color = Settings.LayerPreview.PreviousLayerDifferenceColor;
- }
- else if (nextSpan is not null && nextSpan[pixel] > 0)
- {
- brightness = nextSpan[pixel];
- color = Settings.LayerPreview.NextLayerDifferenceColor;
- }
-
- if (color.IsEmpty) continue;
-
- color = color.FactorColor(brightness);
-
- var bgrPixel = pixel * channels;
- imageBgrSpan[bgrPixel] = color.B; // B
- imageBgrSpan[bgrPixel + 1] = color.G; // G
- imageBgrSpan[bgrPixel + 2] = color.R; // R
- //imageBgrSpan[++bgrPixel] = color.A; // A
+ brightness = Math.Max(previousSpan[pixel], nextSpan[pixel]);
+ color = Settings.LayerPreview.BothLayerDifferenceColor;
}
- });
- }
+ else if (previousSpan is not null && previousSpan[pixel] > 0)
+ {
+ brightness = previousSpan[pixel];
+ color = Settings.LayerPreview.PreviousLayerDifferenceColor;
+ }
+ else if (nextSpan is not null && nextSpan[pixel] > 0)
+ {
+ brightness = nextSpan[pixel];
+ color = Settings.LayerPreview.NextLayerDifferenceColor;
+ }
+
+ if (color.IsEmpty) continue;
- previousImage?.Dispose();
- nextImage?.Dispose();
- // }
+ color = color.FactorColor(brightness);
+
+ var bgrPixel = pixel * channels;
+ imageBgrSpan[bgrPixel] = color.B; // B
+ imageBgrSpan[bgrPixel + 1] = color.G; // G
+ imageBgrSpan[bgrPixel + 2] = color.R; // R
+ //imageBgrSpan[++bgrPixel] = color.A; // A
+ }
+ });
}
+ previousImage?.Dispose();
+ nextImage?.Dispose();
+ // }
+ }
+
- var selectedIssues = IssuesGrid.SelectedItems;
+ var selectedIssues = IssuesGrid.SelectedItems;
- if (_showLayerImageIssues && SlicerFile.IssueManager.Count > 0)
+ if (_showLayerImageIssues && SlicerFile.IssueManager.Count > 0)
+ {
+ //var count = 0;
+ foreach (var issue in SlicerFile.IssueManager.GetIssuesBy(_actualLayer)
+ .Where(issue => issue.Parent.Type
+ is not MainIssue.IssueType.PrintHeight
+ and not MainIssue.IssueType.EmptyLayer))
{
- //var count = 0;
- foreach (var issue in SlicerFile.IssueManager.GetIssuesBy(_actualLayer)
- .Where(issue => issue.Parent.Type
- is not MainIssue.IssueType.PrintHeight
- and not MainIssue.IssueType.EmptyLayer))
+ //count++;
+ var color = Color.Empty;
+ bool drawCrosshair = false;
+
+ switch (issue.Parent.Type)
{
- //count++;
- var color = Color.Empty;
- bool drawCrosshair = false;
+ case MainIssue.IssueType.Island:
+ color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
+ ? Settings.LayerPreview.IslandHighlightColor
+ : Settings.LayerPreview.IslandColor;
+ drawCrosshair = true;
+
+ break;
+ case MainIssue.IssueType.Overhang:
+ color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
+ ? Settings.LayerPreview.OverhangHighlightColor
+ : Settings.LayerPreview.OverhangColor;
+ drawCrosshair = true;
+
+ break;
+ case MainIssue.IssueType.ResinTrap:
+ color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
+ ? Settings.LayerPreview.ResinTrapHighlightColor
+ : Settings.LayerPreview.ResinTrapColor;
+ drawCrosshair = true;
+ break;
+ case MainIssue.IssueType.SuctionCup:
+ color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
+ ? Settings.LayerPreview.SuctionCupHighlightColor
+ : Settings.LayerPreview.SuctionCupColor;
+ drawCrosshair = true;
+ break;
+ case MainIssue.IssueType.TouchingBound:
+ color = Settings.LayerPreview.TouchingBoundsColor;
+ break;
+ case MainIssue.IssueType.Debug:
+ color = new Color(255, 15, 112, 16);
+ break;
+ }
- switch (issue.Parent.Type)
- {
- case MainIssue.IssueType.Island:
- color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
- ? Settings.LayerPreview.IslandHighlightColor
- : Settings.LayerPreview.IslandColor;
- drawCrosshair = true;
-
- break;
- case MainIssue.IssueType.Overhang:
- color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
- ? Settings.LayerPreview.OverhangHighlightColor
- : Settings.LayerPreview.OverhangColor;
- drawCrosshair = true;
-
- break;
- case MainIssue.IssueType.ResinTrap:
- color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
- ? Settings.LayerPreview.ResinTrapHighlightColor
- : Settings.LayerPreview.ResinTrapColor;
- drawCrosshair = true;
- break;
- case MainIssue.IssueType.SuctionCup:
- color = selectedIssues.Count > 0 && selectedIssues.Contains(issue.Parent)
- ? Settings.LayerPreview.SuctionCupHighlightColor
- : Settings.LayerPreview.SuctionCupColor;
- drawCrosshair = true;
- break;
- case MainIssue.IssueType.TouchingBound:
- color = Settings.LayerPreview.TouchingBoundsColor;
- break;
- case MainIssue.IssueType.Debug:
- color = new Color(255, 15, 112, 16);
- break;
- }
+ if (color.IsEmpty) continue;
- if (color.IsEmpty) continue;
+ if (drawCrosshair && _showLayerImageCrosshairs &&
+ !Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues &&
+ LayerImageBox.Zoom <= AppSettings.CrosshairFadeLevel)
+ {
+ DrawCrosshair(issue.BoundingRectangle);
+ }
- if (drawCrosshair && _showLayerImageCrosshairs &&
- !Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues &&
- LayerImageBox.Zoom <= AppSettings.CrosshairFadeLevel)
- {
- DrawCrosshair(issue.BoundingRectangle);
- }
+ /*var point = issue.FirstPoint;
+ if (!point.IsBothNegative())
+ {
+ CvInvoke.PutText(LayerCache.ImageBgr, count.ToString(), point, FontFace.HersheyDuplex, 2, new MCvScalar(0,255,0), 2, LineType.AntiAlias);
+ }*/
- /*var point = issue.FirstPoint;
- if (!point.IsBothNegative())
+ switch (issue)
+ {
+ case IssueOfContours issueOfContours:
{
- CvInvoke.PutText(LayerCache.ImageBgr, count.ToString(), point, FontFace.HersheyDuplex, 2, new MCvScalar(0,255,0), 2, LineType.AntiAlias);
- }*/
-
- switch (issue)
+ using var vec = new VectorOfVectorOfPoint(issueOfContours.Contours);
+ CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1, new MCvScalar(color.B, color.G, color.R), -1);
+ break;
+ }
+ case IssueOfPoints issueOfPoints:
{
- case IssueOfContours issueOfContours:
- {
- using var vec = new VectorOfVectorOfPoint(issueOfContours.Contours);
- CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1, new MCvScalar(color.B, color.G, color.R), -1);
- break;
- }
- case IssueOfPoints issueOfPoints:
+ foreach (var pixel in issueOfPoints.Points)
{
- foreach (var pixel in issueOfPoints.Points)
- {
- int pixelPos = LayerCache.Image.GetPixelPos(pixel);
- byte brightness = imageSpan[pixelPos];
- if (brightness == 0) continue;
-
- int pixelBgrPos = pixelPos * LayerCache.ImageBgr.NumberOfChannels;
+ int pixelPos = LayerCache.Image.GetPixelPos(pixel);
+ byte brightness = imageSpan[pixelPos];
+ if (brightness == 0) continue;
- var newColor = color.FactorColor(brightness, 80);
+ int pixelBgrPos = pixelPos * LayerCache.ImageBgr.NumberOfChannels;
- imageBgrSpan[pixelBgrPos] = newColor.B; // B
- imageBgrSpan[pixelBgrPos + 1] = newColor.G; // G
- imageBgrSpan[pixelBgrPos + 2] = newColor.R; // R
- }
+ var newColor = color.FactorColor(brightness, 80);
- break;
+ imageBgrSpan[pixelBgrPos] = newColor.B; // B
+ imageBgrSpan[pixelBgrPos + 1] = newColor.G; // G
+ imageBgrSpan[pixelBgrPos + 2] = newColor.R; // R
}
+
+ break;
}
-
}
+
}
+ }
- if (_showLayerOutlinePrintVolumeBoundary)
- {
- CvInvoke.Rectangle(LayerCache.ImageBgr, SlicerFile.LayerManager.BoundingRectangle,
- new MCvScalar(Settings.LayerPreview.VolumeBoundsOutlineColor.B,
- Settings.LayerPreview.VolumeBoundsOutlineColor.G,
- Settings.LayerPreview.VolumeBoundsOutlineColor.R),
- Settings.LayerPreview.VolumeBoundsOutlineThickness);
- }
+ if (_showLayerOutlinePrintVolumeBoundary)
+ {
+ CvInvoke.Rectangle(LayerCache.ImageBgr, SlicerFile.BoundingRectangle,
+ new MCvScalar(Settings.LayerPreview.VolumeBoundsOutlineColor.B,
+ Settings.LayerPreview.VolumeBoundsOutlineColor.G,
+ Settings.LayerPreview.VolumeBoundsOutlineColor.R),
+ Settings.LayerPreview.VolumeBoundsOutlineThickness);
+ }
- if (_showLayerOutlineLayerBoundary && !SlicerFile[_actualLayer].BoundingRectangle.IsEmpty)
- {
- CvInvoke.Rectangle(LayerCache.ImageBgr, SlicerFile[_actualLayer].BoundingRectangle,
- new MCvScalar(Settings.LayerPreview.LayerBoundsOutlineColor.B,
- Settings.LayerPreview.LayerBoundsOutlineColor.G, Settings.LayerPreview.LayerBoundsOutlineColor.R),
- Settings.LayerPreview.LayerBoundsOutlineThickness);
- }
+ if (_showLayerOutlineLayerBoundary && !SlicerFile[_actualLayer].BoundingRectangle.IsEmpty)
+ {
+ CvInvoke.Rectangle(LayerCache.ImageBgr, SlicerFile[_actualLayer].BoundingRectangle,
+ new MCvScalar(Settings.LayerPreview.LayerBoundsOutlineColor.B,
+ Settings.LayerPreview.LayerBoundsOutlineColor.G, Settings.LayerPreview.LayerBoundsOutlineColor.R),
+ Settings.LayerPreview.LayerBoundsOutlineThickness);
+ }
- if (_showLayerOutlineContourBoundary)
+ if (_showLayerOutlineContourBoundary)
+ {
+ int lastParent = -1;
+ uint reps = 0;
+ for (int i = 0; i < LayerCache.LayerContours.Size; i++)
{
- int lastParent = -1;
- uint reps = 0;
- for (int i = 0; i < LayerCache.LayerContours.Size; i++)
+ var parent = LayerCache.LayerContourHierarchy[i, EmguContour.HierarchyParent];
+ if (parent == -1)
+ {
+ reps = 0;
+ }
+ if(parent == -1 || parent != lastParent) reps++;
+ if (reps % 2 == 0)
{
- var parent = LayerCache.LayerContourHierarchy[i, EmguContour.HierarchyParent];
- if (parent == -1)
- {
- reps = 0;
- }
- if(parent == -1 || parent != lastParent) reps++;
- if (reps % 2 == 0)
- {
- lastParent = parent;
- continue;
- }
-
- CvInvoke.Rectangle(LayerCache.ImageBgr, CvInvoke.BoundingRectangle(LayerCache.LayerContours[i]),
- new MCvScalar(
- Settings.LayerPreview.ContourBoundsOutlineColor.B,
- Settings.LayerPreview.ContourBoundsOutlineColor.G,
- Settings.LayerPreview.ContourBoundsOutlineColor.R),
- Settings.LayerPreview.ContourBoundsOutlineThickness);
-
lastParent = parent;
+ continue;
}
+
+ CvInvoke.Rectangle(LayerCache.ImageBgr, CvInvoke.BoundingRectangle(LayerCache.LayerContours[i]),
+ new MCvScalar(
+ Settings.LayerPreview.ContourBoundsOutlineColor.B,
+ Settings.LayerPreview.ContourBoundsOutlineColor.G,
+ Settings.LayerPreview.ContourBoundsOutlineColor.R),
+ Settings.LayerPreview.ContourBoundsOutlineThickness);
+
+ lastParent = parent;
}
+ }
+
+ if (_showLayerOutlineHollowAreas)
+ {
+ //CvInvoke.Threshold(ActualLayerImage, grayscale, 1, 255, ThresholdType.Binary);
- if (_showLayerOutlineHollowAreas)
+ /*
+ * hierarchy[i][0]: the index of the next contour of the same level
+ * hierarchy[i][1]: the index of the previous contour of the same level
+ * hierarchy[i][2]: the index of the first child
+ * hierarchy[i][3]: the index of the parent
+ */
+ using var vec = EmguContours.GetNegativeContours(LayerCache.LayerContours, LayerCache.LayerContourHierarchy);
+ if (vec.Size > 0)
{
- //CvInvoke.Threshold(ActualLayerImage, grayscale, 1, 255, ThresholdType.Binary);
-
- /*
- * hierarchy[i][0]: the index of the next contour of the same level
- * hierarchy[i][1]: the index of the previous contour of the same level
- * hierarchy[i][2]: the index of the first child
- * hierarchy[i][3]: the index of the parent
- */
- using var vec = EmguContours.GetNegativeContours(LayerCache.LayerContours, LayerCache.LayerContourHierarchy);
- if (vec.Size > 0)
- {
- CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1,
- new MCvScalar(Settings.LayerPreview.HollowOutlineColor.B,
- Settings.LayerPreview.HollowOutlineColor.G,
- Settings.LayerPreview.HollowOutlineColor.R),
- Settings.LayerPreview.HollowOutlineLineThickness);
- }
+ CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1,
+ new MCvScalar(Settings.LayerPreview.HollowOutlineColor.B,
+ Settings.LayerPreview.HollowOutlineColor.G,
+ Settings.LayerPreview.HollowOutlineColor.R),
+ Settings.LayerPreview.HollowOutlineLineThickness);
}
+ }
- if (_showLayerOutlineCentroids)
+ if (_showLayerOutlineCentroids)
+ {
+ int lastParent = -1;
+ uint reps = 0;
+ for (int i = 0; i < LayerCache.LayerContours.Size; i++)
{
- int lastParent = -1;
- uint reps = 0;
- for (int i = 0; i < LayerCache.LayerContours.Size; i++)
+ var parent = LayerCache.LayerContourHierarchy[i, EmguContour.HierarchyParent];
+ if (parent == -1)
{
- var parent = LayerCache.LayerContourHierarchy[i, EmguContour.HierarchyParent];
- if (parent == -1)
- {
- reps = 0;
- }
- if (parent == -1 || parent != lastParent) reps++;
- if (reps % 2 == 0)
- {
- if (Settings.LayerPreview.CentroidOutlineHollow)
- {
- CvInvoke.Circle(LayerCache.ImageBgr, EmguContour.GetCentroid(LayerCache.LayerContours[i]),
- Settings.LayerPreview.CentroidOutlineDiameter / 2, new MCvScalar(
- Settings.LayerPreview.HollowOutlineColor.B,
- Settings.LayerPreview.HollowOutlineColor.G,
- Settings.LayerPreview.HollowOutlineColor.R), 1, LineType.AntiAlias);
- }
- }
- else
+ reps = 0;
+ }
+ if (parent == -1 || parent != lastParent) reps++;
+ if (reps % 2 == 0)
+ {
+ if (Settings.LayerPreview.CentroidOutlineHollow)
{
CvInvoke.Circle(LayerCache.ImageBgr, EmguContour.GetCentroid(LayerCache.LayerContours[i]),
+ Settings.LayerPreview.CentroidOutlineDiameter / 2, new MCvScalar(
+ Settings.LayerPreview.HollowOutlineColor.B,
+ Settings.LayerPreview.HollowOutlineColor.G,
+ Settings.LayerPreview.HollowOutlineColor.R), 1, LineType.AntiAlias);
+ }
+ }
+ else
+ {
+ CvInvoke.Circle(LayerCache.ImageBgr, EmguContour.GetCentroid(LayerCache.LayerContours[i]),
Settings.LayerPreview.CentroidOutlineDiameter / 2, new MCvScalar(
Settings.LayerPreview.CentroidOutlineColor.B,
Settings.LayerPreview.CentroidOutlineColor.G,
Settings.LayerPreview.CentroidOutlineColor.R), -1, LineType.AntiAlias);
- }
+ }
- lastParent = parent;
- }
+ lastParent = parent;
}
+ }
- if (_maskPoints is not null && _maskPoints.Count > 0)
- {
- using var vec = new VectorOfVectorOfPoint(_maskPoints.ToArray());
- CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1,
- new MCvScalar(Settings.LayerPreview.MaskOutlineColor.B,
- Settings.LayerPreview.MaskOutlineColor.G,
- Settings.LayerPreview.MaskOutlineColor.R),
- Settings.LayerPreview.MaskOutlineLineThickness);
- }
+ if (_maskPoints is not null && _maskPoints.Count > 0)
+ {
+ using var vec = new VectorOfVectorOfPoint(_maskPoints.ToArray());
+ CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1,
+ new MCvScalar(Settings.LayerPreview.MaskOutlineColor.B,
+ Settings.LayerPreview.MaskOutlineColor.G,
+ Settings.LayerPreview.MaskOutlineColor.R),
+ Settings.LayerPreview.MaskOutlineLineThickness);
+ }
- for (var index = 0; index < Drawings.Count; index++)
+ for (var index = 0; index < Drawings.Count; index++)
+ {
+ if (Drawings[index].LayerIndex != ActualLayer) continue;
+ var operation = Drawings[index];
+ if (operation.OperationType == PixelOperation.PixelOperationType.Drawing)
{
- if (Drawings[index].LayerIndex != ActualLayer) continue;
- var operation = Drawings[index];
- if (operation.OperationType == PixelOperation.PixelOperationType.Drawing)
+ var operationDrawing = (PixelDrawing) operation;
+ var color = operationDrawing.IsAdd
+ ? (DrawingsGrid.SelectedItems.Contains(operation)
+ ? Settings.PixelEditor.AddPixelHighlightColor
+ : Settings.PixelEditor.AddPixelColor)
+ : (DrawingsGrid.SelectedItems.Contains(operation)
+ ? Settings.PixelEditor.RemovePixelHighlightColor
+ : Settings.PixelEditor.RemovePixelColor);
+ if (operationDrawing.BrushSize == 1)
{
- var operationDrawing = (PixelDrawing) operation;
- var color = operationDrawing.IsAdd
- ? (DrawingsGrid.SelectedItems.Contains(operation)
- ? Settings.PixelEditor.AddPixelHighlightColor
- : Settings.PixelEditor.AddPixelColor)
- : (DrawingsGrid.SelectedItems.Contains(operation)
- ? Settings.PixelEditor.RemovePixelHighlightColor
- : Settings.PixelEditor.RemovePixelColor);
- if (operationDrawing.BrushSize == 1)
- {
- LayerCache.ImageBgr.SetByte(operation.Location.X, operation.Location.Y,
- new[] {color.B, color.G, color.R});
- continue;
- }
-
- LayerCache.ImageBgr.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location,
- new MCvScalar(color.B, color.G, color.R), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType);
- /*switch (operationDrawing.BrushShape)
- {
- case PixelDrawing.BrushShapeType.Square:
- CvInvoke.Rectangle(LayerCache.ImageBgr, operationDrawing.Rectangle,
- new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
- operationDrawing.LineType);
- break;
- case PixelDrawing.BrushShapeType.Circle:
- CvInvoke.Circle(LayerCache.ImageBgr, operation.Location, operationDrawing.BrushSize / 2,
- new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
- operationDrawing.LineType);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }*/
+ LayerCache.ImageBgr.SetByte(operation.Location.X, operation.Location.Y,
+ new[] {color.B, color.G, color.R});
+ continue;
}
- else if (operation.OperationType == PixelOperation.PixelOperationType.Text)
+
+ LayerCache.ImageBgr.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location,
+ new MCvScalar(color.B, color.G, color.R), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType);
+ /*switch (operationDrawing.BrushShape)
{
- var operationText = (PixelText) operation;
- var color = operationText.IsAdd
- ? (DrawingsGrid.SelectedItems.Contains(operation)
- ? Settings.PixelEditor.AddPixelHighlightColor
- : Settings.PixelEditor.AddPixelColor)
- : (DrawingsGrid.SelectedItems.Contains(operation)
- ? Settings.PixelEditor.RemovePixelHighlightColor
- : Settings.PixelEditor.RemovePixelColor);
+ case PixelDrawing.BrushShapeType.Square:
+ CvInvoke.Rectangle(LayerCache.ImageBgr, operationDrawing.Rectangle,
+ new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
+ operationDrawing.LineType);
+ break;
+ case PixelDrawing.BrushShapeType.Circle:
+ CvInvoke.Circle(LayerCache.ImageBgr, operation.Location, operationDrawing.BrushSize / 2,
+ new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
+ operationDrawing.LineType);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }*/
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Text)
+ {
+ var operationText = (PixelText) operation;
+ var color = operationText.IsAdd
+ ? (DrawingsGrid.SelectedItems.Contains(operation)
+ ? Settings.PixelEditor.AddPixelHighlightColor
+ : Settings.PixelEditor.AddPixelColor)
+ : (DrawingsGrid.SelectedItems.Contains(operation)
+ ? Settings.PixelEditor.RemovePixelHighlightColor
+ : Settings.PixelEditor.RemovePixelColor);
- /*CvInvoke.PutText(LayerCache.ImageBgr, operationText.Text, operationText.Location,
- operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R),
- operationText.Thickness, operationText.LineType, operationText.Mirror);*/
- LayerCache.ImageBgr.PutTextRotated(operationText.Text, operationText.Location,
+ /*CvInvoke.PutText(LayerCache.ImageBgr, operationText.Text, operationText.Location,
+ operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R),
+ operationText.Thickness, operationText.LineType, operationText.Mirror);*/
+ LayerCache.ImageBgr.PutTextRotated(operationText.Text, operationText.Location,
operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R),
operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.LineAlignment, operationText.Angle);
- }
- else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser)
- {
- //var pixelBrightness = LayerCache.Image.GetPixelPos(operation.Location);
- if (imageSpan[LayerCache.Image.GetPixelPos(operation.Location)] < 10) continue;
- var color = DrawingsGrid.SelectedItems.Contains(operation)
- ? Settings.PixelEditor.RemovePixelHighlightColor
- : Settings.PixelEditor.RemovePixelColor;
-
- using var vec = EmguContours.GetContoursInside(LayerCache.LayerContours, LayerCache.LayerContourHierarchy, operation.Location);
- if (vec.Size > 0) CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1, new MCvScalar(color.B, color.G, color.R), -1);
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser)
+ {
+ //var pixelBrightness = LayerCache.Image.GetPixelPos(operation.Location);
+ if (imageSpan[LayerCache.Image.GetPixelPos(operation.Location)] < 10) continue;
+ var color = DrawingsGrid.SelectedItems.Contains(operation)
+ ? Settings.PixelEditor.RemovePixelHighlightColor
+ : Settings.PixelEditor.RemovePixelColor;
- /*var hollowGroups = EmguContours.GetPositiveContoursInGroups(LayerCache.LayerContours, LayerCache.LayerContourHierarchy);
+ using var vec = EmguContours.GetContoursInside(LayerCache.LayerContours, LayerCache.LayerContourHierarchy, operation.Location);
+ if (vec.Size > 0) CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1, new MCvScalar(color.B, color.G, color.R), -1);
- foreach (var vec in hollowGroups)
- {
- CvInvoke.PutText(LayerCache.ImageBgr, vec.Size.ToString(), vec[0][0], FontFace.HersheyDuplex, 2, new MCvScalar(255, 0, 0), 2);
- CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1,
- new MCvScalar(color.B, color.G, color.R), -1);
- vec.Dispose();
- }*/
- }
- else if (operation.OperationType == PixelOperation.PixelOperationType.Supports)
- {
- var operationSupport = (PixelSupport) operation;
- var color = DrawingsGrid.SelectedItems.Contains(operation)
- ? Settings.PixelEditor.SupportsHighlightColor
- : Settings.PixelEditor.SupportsColor;
+ /*var hollowGroups = EmguContours.GetPositiveContoursInGroups(LayerCache.LayerContours, LayerCache.LayerContourHierarchy);
- CvInvoke.Circle(LayerCache.ImageBgr, operation.Location, operationSupport.TipDiameter / 2,
- new MCvScalar(color.B, color.G, color.R), -1);
- }
- else if (operation.OperationType == PixelOperation.PixelOperationType.DrainHole)
+ foreach (var vec in hollowGroups)
{
- var operationDrainHole = (PixelDrainHole) operation;
- var color = DrawingsGrid.SelectedItems.Contains(operation)
- ? Settings.PixelEditor.DrainHoleHighlightColor
- : Settings.PixelEditor.DrainHoleColor;
-
- CvInvoke.Circle(LayerCache.ImageBgr, operation.Location, operationDrainHole.Diameter / 2,
+ CvInvoke.PutText(LayerCache.ImageBgr, vec.Size.ToString(), vec[0][0], FontFace.HersheyDuplex, 2, new MCvScalar(255, 0, 0), 2);
+ CvInvoke.DrawContours(LayerCache.ImageBgr, vec, -1,
new MCvScalar(color.B, color.G, color.R), -1);
- }
+ vec.Dispose();
+ }*/
}
-
- // Show crosshairs for selected issues if crosshair mode is enabled via toolstrip button.
- // Even when enabled, crosshairs are hidden in pixel edit mode when SHIFT is pressed.
- if (_showLayerImageCrosshairs &&
- Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues &&
- SlicerFile.IssueManager.Count > 0 &&
- IssuesGrid.SelectedItems.Count > 0 &&
- LayerImageBox.Zoom <=
- AppSettings.CrosshairFadeLevel && // Only draw crosshairs when zoom level is below the configurable crosshair fade threshold.
- !_isPixelEditorActive)
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Supports)
{
+ var operationSupport = (PixelSupport) operation;
+ var color = DrawingsGrid.SelectedItems.Contains(operation)
+ ? Settings.PixelEditor.SupportsHighlightColor
+ : Settings.PixelEditor.SupportsColor;
-
- // Don't render crosshairs for selected issue that are not on the current layer, or for
- // issue types that don't have a specific location or bounds.
- foreach (var issue in SlicerFile.IssueManager.GetIssuesBy(_actualLayer)
- .Where(issue => issue.Parent.Type
- is not MainIssue.IssueType.TouchingBound
- and not MainIssue.IssueType.PrintHeight
- and not MainIssue.IssueType.EmptyLayer))
- {
- DrawCrosshair(issue.BoundingRectangle);
- }
+ CvInvoke.Circle(LayerCache.ImageBgr, operation.Location, operationSupport.TipDiameter / 2,
+ new MCvScalar(color.B, color.G, color.R), -1);
}
-
- if (_showLayerImageFlipped && (_showLayerImageFlippedHorizontally || _showLayerImageFlippedVertically))
+ else if (operation.OperationType == PixelOperation.PixelOperationType.DrainHole)
{
- var flipType = FlipType.Both;
-
- if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
- flipType = FlipType.Both;
- else if (_showLayerImageFlippedHorizontally)
- flipType = FlipType.Horizontal;
- else if (_showLayerImageFlippedVertically)
- flipType = FlipType.Vertical;
+ var operationDrainHole = (PixelDrainHole) operation;
+ var color = DrawingsGrid.SelectedItems.Contains(operation)
+ ? Settings.PixelEditor.DrainHoleHighlightColor
+ : Settings.PixelEditor.DrainHoleColor;
- CvInvoke.Flip(LayerCache.ImageBgr, LayerCache.ImageBgr, flipType);
+ CvInvoke.Circle(LayerCache.ImageBgr, operation.Location, operationDrainHole.Diameter / 2,
+ new MCvScalar(color.B, color.G, color.R), -1);
}
+ }
- if (_showLayerImageRotated)
+ // Show crosshairs for selected issues if crosshair mode is enabled via toolstrip button.
+ // Even when enabled, crosshairs are hidden in pixel edit mode when SHIFT is pressed.
+ if (_showLayerImageCrosshairs &&
+ Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues &&
+ SlicerFile.IssueManager.Count > 0 &&
+ IssuesGrid.SelectedItems.Count > 0 &&
+ LayerImageBox.Zoom <=
+ AppSettings.CrosshairFadeLevel && // Only draw crosshairs when zoom level is below the configurable crosshair fade threshold.
+ !_isPixelEditorActive)
+ {
+
+
+ // Don't render crosshairs for selected issue that are not on the current layer, or for
+ // issue types that don't have a specific location or bounds.
+ foreach (var issue in SlicerFile.IssueManager.GetIssuesBy(_actualLayer)
+ .Where(issue => issue.Parent.Type
+ is not MainIssue.IssueType.TouchingBound
+ and not MainIssue.IssueType.PrintHeight
+ and not MainIssue.IssueType.EmptyLayer))
{
- CvInvoke.Rotate(LayerCache.ImageBgr, LayerCache.ImageBgr, _showLayerImageRotateCcwDirection ? RotateFlags.Rotate90CounterClockwise : RotateFlags.Rotate90Clockwise);
+ DrawCrosshair(issue.BoundingRectangle);
}
+ }
- LayerImageBox.Image = LayerCache.Bitmap = LayerCache.ImageBgr.ToBitmap();
-
- RefreshCurrentLayerData();
+ if (_showLayerImageFlipped && (_showLayerImageFlippedHorizontally || _showLayerImageFlippedVertically))
+ {
+ var flipType = FlipType.Both;
- watch.Stop();
- ShowLayerRenderMs = watch.ElapsedMilliseconds;
- AddLogVerbose($"Show Layer: {_actualLayer}", watch.Elapsed.TotalSeconds);
+ if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ flipType = FlipType.Both;
+ else if (_showLayerImageFlippedHorizontally)
+ flipType = FlipType.Horizontal;
+ else if (_showLayerImageFlippedVertically)
+ flipType = FlipType.Vertical;
+
+ CvInvoke.Flip(LayerCache.ImageBgr, LayerCache.ImageBgr, flipType);
}
- catch (Exception e)
+
+ if (_showLayerImageRotated)
{
- Debug.WriteLine(e);
+ CvInvoke.Rotate(LayerCache.ImageBgr, LayerCache.ImageBgr, _showLayerImageRotateCcwDirection ? RotateFlags.Rotate90CounterClockwise : RotateFlags.Rotate90Clockwise);
}
- }
-
- /// <summary>
- /// Draw a crosshair around a rectangle
- /// </summary>
- /// <param name="rect"></param>
- public void DrawCrosshair(Rectangle rect)
- {
- // Gradually increase line thickness from 1 to 3 at the lower-end of the zoom range.
- // This prevents the crosshair lines from disappearing due to being too thin to
- // render at very low zoom factors.
- var lineThickness = (LayerImageBox.Zoom > 100) ? 1 : (LayerImageBox.Zoom < 50) ? 3 : 2;
- var color = new MCvScalar(Settings.LayerPreview.CrosshairColor.B, Settings.LayerPreview.CrosshairColor.G,
- Settings.LayerPreview.CrosshairColor.R);
-
-
- // LEFT
- var startPoint = new Point(Math.Max(0, rect.X - Settings.LayerPreview.CrosshairMargin - 1),
- rect.Y + rect.Height / 2);
- var endPoint =
- new Point(
- Settings.LayerPreview.CrosshairLength == 0
- ? 0
- : (int)Math.Max(0, startPoint.X - Settings.LayerPreview.CrosshairLength + 1),
- startPoint.Y);
-
- CvInvoke.Line(LayerCache.ImageBgr,
- startPoint,
- endPoint,
- color,
- lineThickness);
-
-
- // RIGHT
- startPoint.X = Math.Min(LayerCache.ImageBgr.Width,
- rect.Right + Settings.LayerPreview.CrosshairMargin);
- endPoint.X = Settings.LayerPreview.CrosshairLength == 0
- ? LayerCache.ImageBgr.Width
- : (int)Math.Min(LayerCache.ImageBgr.Width, startPoint.X + Settings.LayerPreview.CrosshairLength - 1);
-
- CvInvoke.Line(LayerCache.ImageBgr,
- startPoint,
- endPoint,
- color,
- lineThickness);
-
- // TOP
- startPoint = new Point(rect.X + rect.Width / 2,
- Math.Max(0, rect.Y - Settings.LayerPreview.CrosshairMargin - 1));
- endPoint = new Point(startPoint.X,
- (int)(Settings.LayerPreview.CrosshairLength == 0
- ? 0
- : Math.Max(0, startPoint.Y - Settings.LayerPreview.CrosshairLength + 1)));
+ LayerImageBox.Image = LayerCache.Bitmap = LayerCache.ImageBgr.ToBitmap();
+
+ RefreshCurrentLayerData();
- CvInvoke.Line(LayerCache.ImageBgr,
- startPoint,
- endPoint,
- color,
- lineThickness);
+ watch.Stop();
+ ShowLayerRenderMs = watch.ElapsedMilliseconds;
+ AddLogVerbose($"Show Layer: {_actualLayer}", watch.Elapsed.TotalSeconds);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+ }
- // Bottom
- startPoint.Y = Math.Min(LayerCache.ImageBgr.Height, rect.Bottom + Settings.LayerPreview.CrosshairMargin);
- endPoint.Y = Settings.LayerPreview.CrosshairLength == 0
- ? LayerCache.ImageBgr.Height
- : (int)Math.Min(LayerCache.ImageBgr.Height, startPoint.Y + Settings.LayerPreview.CrosshairLength - 1);
+ /// <summary>
+ /// Draw a crosshair around a rectangle
+ /// </summary>
+ /// <param name="rect"></param>
+ public void DrawCrosshair(Rectangle rect)
+ {
+ // Gradually increase line thickness from 1 to 3 at the lower-end of the zoom range.
+ // This prevents the crosshair lines from disappearing due to being too thin to
+ // render at very low zoom factors.
+ var lineThickness = (LayerImageBox.Zoom > 100) ? 1 : (LayerImageBox.Zoom < 50) ? 3 : 2;
+ var color = new MCvScalar(Settings.LayerPreview.CrosshairColor.B, Settings.LayerPreview.CrosshairColor.G,
+ Settings.LayerPreview.CrosshairColor.R);
+
+
+ // LEFT
+ var startPoint = new Point(Math.Max(0, rect.X - Settings.LayerPreview.CrosshairMargin - 1),
+ rect.Y + rect.Height / 2);
+ var endPoint =
+ new Point(
+ Settings.LayerPreview.CrosshairLength == 0
+ ? 0
+ : (int)Math.Max(0, startPoint.X - Settings.LayerPreview.CrosshairLength + 1),
+ startPoint.Y);
+
+ CvInvoke.Line(LayerCache.ImageBgr,
+ startPoint,
+ endPoint,
+ color,
+ lineThickness);
+
+
+ // RIGHT
+ startPoint.X = Math.Min(LayerCache.ImageBgr.Width,
+ rect.Right + Settings.LayerPreview.CrosshairMargin);
+ endPoint.X = Settings.LayerPreview.CrosshairLength == 0
+ ? LayerCache.ImageBgr.Width
+ : (int)Math.Min(LayerCache.ImageBgr.Width, startPoint.X + Settings.LayerPreview.CrosshairLength - 1);
+
+ CvInvoke.Line(LayerCache.ImageBgr,
+ startPoint,
+ endPoint,
+ color,
+ lineThickness);
+
+ // TOP
+ startPoint = new Point(rect.X + rect.Width / 2,
+ Math.Max(0, rect.Y - Settings.LayerPreview.CrosshairMargin - 1));
+ endPoint = new Point(startPoint.X,
+ (int)(Settings.LayerPreview.CrosshairLength == 0
+ ? 0
+ : Math.Max(0, startPoint.Y - Settings.LayerPreview.CrosshairLength + 1)));
+
+
+ CvInvoke.Line(LayerCache.ImageBgr,
+ startPoint,
+ endPoint,
+ color,
+ lineThickness);
+
+ // Bottom
+ startPoint.Y = Math.Min(LayerCache.ImageBgr.Height, rect.Bottom + Settings.LayerPreview.CrosshairMargin);
+ endPoint.Y = Settings.LayerPreview.CrosshairLength == 0
+ ? LayerCache.ImageBgr.Height
+ : (int)Math.Min(LayerCache.ImageBgr.Height, startPoint.Y + Settings.LayerPreview.CrosshairLength - 1);
+
+ CvInvoke.Line(LayerCache.ImageBgr,
+ startPoint,
+ endPoint,
+ color,
+ lineThickness);
+ }
- CvInvoke.Line(LayerCache.ImageBgr,
- startPoint,
- endPoint,
- color,
- lineThickness);
- }
+ public Point GetTransposedPoint(Point point, bool inverse = false)
+ {
+ if (point.IsEmpty) return point;
- public Point GetTransposedPoint(Point point, bool inverse = false)
+ void Flip()
{
- if (point.IsEmpty) return point;
-
- void Flip()
+ if (!_showLayerImageFlipped) return;
+ if (_showLayerImageFlippedHorizontally)
{
- if (!_showLayerImageFlipped) return;
- if (_showLayerImageFlippedHorizontally)
- {
- point = new Point(LayerCache.Image.Width - 1 - point.X, point.Y);
- }
-
- if (_showLayerImageFlippedVertically)
- {
- point = new Point(point.X, LayerCache.Image.Height - 1 - point.Y);
- }
+ point = new Point(LayerCache.Image.Width - 1 - point.X, point.Y);
}
- void Rotate()
+ if (_showLayerImageFlippedVertically)
{
- if (!_showLayerImageRotated) return;
- if (_showLayerImageRotateCcwDirection)
- {
- point = inverse
- ? new Point(point.Y, LayerCache.Image.Width - 1 - point.X) // 90º CCW
- : new Point(LayerCache.Image.Width - 1 - point.Y, point.X); // 90º CW
-
- }
- else
- {
- point = inverse
- ? new Point(LayerCache.Image.Height - 1 - point.Y, point.X) // 90º CW
- : new Point(point.Y, LayerCache.Image.Height - 1 - point.X); // 90º CCW
- }
+ point = new Point(point.X, LayerCache.Image.Height - 1 - point.Y);
}
+ }
- if (inverse)
+ void Rotate()
+ {
+ if (!_showLayerImageRotated) return;
+ if (_showLayerImageRotateCcwDirection)
{
- Flip();
- Rotate();
+ point = inverse
+ ? new Point(point.Y, LayerCache.Image.Width - 1 - point.X) // 90º CCW
+ : new Point(LayerCache.Image.Width - 1 - point.Y, point.X); // 90º CW
+
}
else
{
- Rotate();
- Flip();
+ point = inverse
+ ? new Point(LayerCache.Image.Height - 1 - point.Y, point.X) // 90º CW
+ : new Point(point.Y, LayerCache.Image.Height - 1 - point.X); // 90º CCW
}
+ }
- return point;
+ if (inverse)
+ {
+ Flip();
+ Rotate();
+ }
+ else
+ {
+ Rotate();
+ Flip();
}
- public Rectangle GetTransposedRectangle(RectangleF rectangleF, bool inverse = true) =>
- GetTransposedRectangle(Rectangle.Round(rectangleF), inverse);
+ return point;
+ }
- public Rectangle GetTransposedRectangle(Rectangle rectangle, bool inverse = false)
- {
- if (rectangle.IsEmpty) return rectangle;
+ public Rectangle GetTransposedRectangle(RectangleF rectangleF, bool inverse = true) =>
+ GetTransposedRectangle(Rectangle.Round(rectangleF), inverse);
- void Flip()
- {
- if (!_showLayerImageFlipped) return;
- if (_showLayerImageFlippedHorizontally)
- {
- rectangle.Location = new Point(LayerCache.Image.Width - rectangle.Right, rectangle.Y);
- }
+ public Rectangle GetTransposedRectangle(Rectangle rectangle, bool inverse = false)
+ {
+ if (rectangle.IsEmpty) return rectangle;
- if (_showLayerImageFlippedVertically)
- {
- rectangle.Location = new Point(rectangle.X, LayerCache.Image.Height - 1 - rectangle.Bottom);
- }
+ void Flip()
+ {
+ if (!_showLayerImageFlipped) return;
+ if (_showLayerImageFlippedHorizontally)
+ {
+ rectangle.Location = new Point(LayerCache.Image.Width - rectangle.Right, rectangle.Y);
}
- void Rotate()
+ if (_showLayerImageFlippedVertically)
{
- if (!_showLayerImageRotated) return;
- if (_showLayerImageRotateCcwDirection)
- {
- rectangle = !inverse
- ? new Rectangle(rectangle.Y, LayerCache.Image.Width - rectangle.Right, rectangle.Height, rectangle.Width) // 90º CCW
- : new Rectangle(LayerCache.Image.Width - rectangle.Bottom, rectangle.X, rectangle.Height, rectangle.Width); // 90º CW
-
- }
- else
- {
- rectangle = !inverse
- ? new Rectangle(LayerCache.Image.Height - rectangle.Bottom, rectangle.X, rectangle.Height, rectangle.Width) // 90º CW
- : new Rectangle(rectangle.Y, LayerCache.Image.Height - rectangle.Right, rectangle.Height, rectangle.Width); // 90º CCW
- }
+ rectangle.Location = new Point(rectangle.X, LayerCache.Image.Height - 1 - rectangle.Bottom);
}
+ }
- if (!inverse)
+ void Rotate()
+ {
+ if (!_showLayerImageRotated) return;
+ if (_showLayerImageRotateCcwDirection)
{
- Flip();
- Rotate();
+ rectangle = !inverse
+ ? new Rectangle(rectangle.Y, LayerCache.Image.Width - rectangle.Right, rectangle.Height, rectangle.Width) // 90º CCW
+ : new Rectangle(LayerCache.Image.Width - rectangle.Bottom, rectangle.X, rectangle.Height, rectangle.Width); // 90º CW
+
}
else
{
- Rotate();
- Flip();
+ rectangle = !inverse
+ ? new Rectangle(LayerCache.Image.Height - rectangle.Bottom, rectangle.X, rectangle.Height, rectangle.Width) // 90º CW
+ : new Rectangle(rectangle.Y, LayerCache.Image.Height - rectangle.Right, rectangle.Height, rectangle.Width); // 90º CCW
}
-
- return rectangle;
-
- /*return inverse
- ? new Rectangle(LayerCache.Image.Height - rectangle.Bottom,
- rectangle.Left, rectangle.Height, rectangle.Width)
- //: new Rectangle(ActualLayerImage.Width - rectangle.Bottom, rectangle.Left, rectangle.Width, rectangle.Height);
- //: new Rectangle(ActualLayerImage.Width - rectangle.Bottom, ActualLayerImage.Height-rectangle.Right, rectangle.Width, rectangle.Height); // Rotate90FlipX: // = Rotate270FlipY
- //: new Rectangle(rectangle.Top, rectangle.Left, rectangle.Width, rectangle.Height); // Rotate270FlipX: // = Rotate90FlipY
- : new Rectangle(rectangle.Top, LayerCache.Image.Height - rectangle.Right, rectangle.Height, rectangle.Width); // Rotate90FlipNone: // = Rotate270FlipXY*/
}
- /// <summary>
- /// Gets the bounding rectangle of the passed issue, automatically adjusting
- /// the coordinates and width/height to account for whether or not the layer
- /// preview image is rotated. Used to ensure images are properly zoomed or
- /// centered independent of the layer preview rotation.
- /// </summary>
- private Rectangle GetTransposedIssueBounds(Issue issue)
+ if (!inverse)
{
- if (issue.BoundingRectangle.IsEmpty /*|| issue.PixelsCount == 1*/)
- {
- return GetTransposedRectangle(LayerCache.Layer.BoundingRectangle);
- }
- //return new Rectangle(GetTransposedPoint(issue.FirstPoint, true), new Size(1, 1));
-
- return GetTransposedRectangle(issue.BoundingRectangle);
+ Flip();
+ Rotate();
}
-
- public void CenterLayer(int zoomLevel = 0)
+ else
{
- if (zoomLevel < 0) zoomLevel = AppSettings.LockedZoomLevel;
- if (zoomLevel > 0) LayerImageBox.Zoom = zoomLevel;
- LayerImageBox.CenterToImage();
+ Rotate();
+ Flip();
}
- /// <summary>
- /// Centers layer view on a X,Y coordinate
- /// </summary>
- /// <param name="x">X coordinate</param>
- /// <param name="y">X coordinate</param>
- /// <param name="zoomLevel">Zoom level to set, 0 to ignore or negative value to get current locked zoom level</param>
- public void CenterLayerAt(double x, double y, int zoomLevel = 0)
- {
- if (zoomLevel < 0) zoomLevel = AppSettings.LockedZoomLevel;
- if (zoomLevel > 0) LayerImageBox.Zoom = zoomLevel;
- LayerImageBox.CenterAt(x, y);
- }
+ return rectangle;
+
+ /*return inverse
+ ? new Rectangle(LayerCache.Image.Height - rectangle.Bottom,
+ rectangle.Left, rectangle.Height, rectangle.Width)
+ //: new Rectangle(ActualLayerImage.Width - rectangle.Bottom, rectangle.Left, rectangle.Width, rectangle.Height);
+ //: new Rectangle(ActualLayerImage.Width - rectangle.Bottom, ActualLayerImage.Height-rectangle.Right, rectangle.Width, rectangle.Height); // Rotate90FlipX: // = Rotate270FlipY
+ //: new Rectangle(rectangle.Top, rectangle.Left, rectangle.Width, rectangle.Height); // Rotate270FlipX: // = Rotate90FlipY
+ : new Rectangle(rectangle.Top, LayerCache.Image.Height - rectangle.Right, rectangle.Height, rectangle.Width); // Rotate90FlipNone: // = Rotate270FlipXY*/
+ }
- /// <summary>
- /// Centers layer view on a X,Y coordinate
- /// </summary>
- /// <param name="x">X coordinate</param>
- /// <param name="y">X coordinate</param>
- /// <param name="zoomLevel">Zoom level to set, 0 to ignore or negative value to get current locked zoom level</param>
- public void CenterLayerAt(int x, int y, int zoomLevel = 0)
+ /// <summary>
+ /// Gets the bounding rectangle of the passed issue, automatically adjusting
+ /// the coordinates and width/height to account for whether or not the layer
+ /// preview image is rotated. Used to ensure images are properly zoomed or
+ /// centered independent of the layer preview rotation.
+ /// </summary>
+ private Rectangle GetTransposedIssueBounds(Issue issue)
+ {
+ if (issue.BoundingRectangle.IsEmpty /*|| issue.PixelsCount == 1*/)
{
- if (zoomLevel < 0) zoomLevel = AppSettings.LockedZoomLevel;
- if (zoomLevel > 0) LayerImageBox.Zoom = zoomLevel;
- LayerImageBox.CenterAt(x, y);
+ return GetTransposedRectangle(LayerCache.Layer.BoundingRectangle);
}
+ //return new Rectangle(GetTransposedPoint(issue.FirstPoint, true), new Size(1, 1));
+ return GetTransposedRectangle(issue.BoundingRectangle);
+ }
- public void CenterLayerAt(Rectangle rectangle, int zoomLevel = 0, bool zoomToRegion = false)
- {
- var viewPort = LayerImageBox.GetSourceImageRegion();
- if (zoomToRegion ||
- rectangle.Width * AppSettings.LockedZoomLevel / LayerImageBox.Zoom > viewPort.Width ||
- rectangle.Height * AppSettings.LockedZoomLevel / LayerImageBox.Zoom > viewPort.Height)
- {
- Debug.WriteLine("zoom to region");
- //SupressLayerZoomEvent = true;
- LayerImageBox.ZoomToRegion(rectangle, 10);
- //SupressLayerZoomEvent = false;
- //LayerImageBox.ZoomOut(true);
- return;
- }
- Debug.WriteLine($"Center at {zoomLevel}");
- CenterLayerAt(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2, zoomLevel);
- }
+ public void CenterLayer(int zoomLevel = 0)
+ {
+ if (zoomLevel < 0) zoomLevel = AppSettings.LockedZoomLevel;
+ if (zoomLevel > 0) LayerImageBox.Zoom = zoomLevel;
+ LayerImageBox.CenterToImage();
+ }
- /// <summary>
- /// Centers layer view on a <see cref="Point"/>
- /// </summary>
- /// <param name="point">Point holding X and Y coordinates</param>
- /// <param name="zoomLevel">Zoom level to set, 0 to ignore or negative value to get current locked zoom level</param>
- public void CenterLayerAt(Point point, int zoomLevel = 0) => CenterLayerAt(point.X, point.Y, zoomLevel);
+ /// <summary>
+ /// Centers layer view on a X,Y coordinate
+ /// </summary>
+ /// <param name="x">X coordinate</param>
+ /// <param name="y">X coordinate</param>
+ /// <param name="zoomLevel">Zoom level to set, 0 to ignore or negative value to get current locked zoom level</param>
+ public void CenterLayerAt(double x, double y, int zoomLevel = 0)
+ {
+ if (zoomLevel < 0) zoomLevel = AppSettings.LockedZoomLevel;
+ if (zoomLevel > 0) LayerImageBox.Zoom = zoomLevel;
+ LayerImageBox.CenterAt(x, y);
+ }
+ /// <summary>
+ /// Centers layer view on a X,Y coordinate
+ /// </summary>
+ /// <param name="x">X coordinate</param>
+ /// <param name="y">X coordinate</param>
+ /// <param name="zoomLevel">Zoom level to set, 0 to ignore or negative value to get current locked zoom level</param>
+ public void CenterLayerAt(int x, int y, int zoomLevel = 0)
+ {
+ if (zoomLevel < 0) zoomLevel = AppSettings.LockedZoomLevel;
+ if (zoomLevel > 0) LayerImageBox.Zoom = zoomLevel;
+ LayerImageBox.CenterAt(x, y);
+ }
- /// <summary>
- /// Zoom the layer preview to the passed issue, or if appropriate for issue type,
- /// Zoom to fit the plate or print bounds.
- /// </summary>
- private void ZoomToIssue(Issue issue, bool forceRefreshLayer = false)
- {
- if (issue.Type is MainIssue.IssueType.EmptyLayer || issue.BoundingRectangle.IsEmpty)
- {
- ZoomToFit();
- if (forceRefreshLayer) ForceUpdateActualLayer(issue.LayerIndex);
- return;
- }
- if (Settings.LayerPreview.ZoomIssues ^ (_globalModifiers & KeyModifiers.Alt) != 0)
- {
- CenterLayerAt(GetTransposedIssueBounds(issue), AppSettings.LockedZoomLevel);
- }
- else
- {
- //CenterLayerAt(GetTransposedIssueBounds(issue));
- // If issue is not already visible, center on it and bring it into view.
- // Issues already in view will not be centered, though their color may
- // change and the crosshair may move to reflect active selections.
+ public void CenterLayerAt(Rectangle rectangle, int zoomLevel = 0, bool zoomToRegion = false)
+ {
+ var viewPort = LayerImageBox.GetSourceImageRegion();
+ if (zoomToRegion ||
+ rectangle.Width * AppSettings.LockedZoomLevel / LayerImageBox.Zoom > viewPort.Width ||
+ rectangle.Height * AppSettings.LockedZoomLevel / LayerImageBox.Zoom > viewPort.Height)
+ {
+ Debug.WriteLine("zoom to region");
+ //SupressLayerZoomEvent = true;
+ LayerImageBox.ZoomToRegion(rectangle, 10);
+ //SupressLayerZoomEvent = false;
+ //LayerImageBox.ZoomOut(true);
+ return;
+ }
+ Debug.WriteLine($"Center at {zoomLevel}");
+ CenterLayerAt(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2, zoomLevel);
+ }
+
+ /// <summary>
+ /// Centers layer view on a <see cref="Point"/>
+ /// </summary>
+ /// <param name="point">Point holding X and Y coordinates</param>
+ /// <param name="zoomLevel">Zoom level to set, 0 to ignore or negative value to get current locked zoom level</param>
+ public void CenterLayerAt(Point point, int zoomLevel = 0) => CenterLayerAt(point.X, point.Y, zoomLevel);
- if (!LayerImageBox.GetSourceImageRegion().Contains(GetTransposedIssueBounds(issue).ToAvalonia()))
- {
- CenterAtIssue(issue);
- }
- }
- if(forceRefreshLayer) ForceUpdateActualLayer(issue.LayerIndex);
+ /// <summary>
+ /// Zoom the layer preview to the passed issue, or if appropriate for issue type,
+ /// Zoom to fit the plate or print bounds.
+ /// </summary>
+ private void ZoomToIssue(Issue issue, bool forceRefreshLayer = false)
+ {
+ if (issue.Type is MainIssue.IssueType.EmptyLayer || issue.BoundingRectangle.IsEmpty)
+ {
+ ZoomToFit();
+ if (forceRefreshLayer) ForceUpdateActualLayer(issue.LayerIndex);
+ return;
}
- /// <summary>
- /// Center the layer preview on the passed issue, or if appropriate for issue type,
- /// Zoom to fit the plate or print bounds.
- /// </summary>
- private void CenterAtIssue(Issue issue)
+ if (Settings.LayerPreview.ZoomIssues ^ (_globalModifiers & KeyModifiers.Alt) != 0)
{
- if (issue.Parent.Type is MainIssue.IssueType.EmptyLayer || issue.BoundingRectangle.IsEmpty)
- {
- ZoomToFit();
- }
+ CenterLayerAt(GetTransposedIssueBounds(issue), AppSettings.LockedZoomLevel);
+ }
+ else
+ {
+ //CenterLayerAt(GetTransposedIssueBounds(issue));
+ // If issue is not already visible, center on it and bring it into view.
+ // Issues already in view will not be centered, though their color may
+ // change and the crosshair may move to reflect active selections.
- if (!issue.BoundingRectangle.IsEmpty)
+ if (!LayerImageBox.GetSourceImageRegion().Contains(GetTransposedIssueBounds(issue).ToAvalonia()))
{
- CenterLayerAt(GetTransposedIssueBounds(issue));
+ CenterAtIssue(issue);
}
}
- public void ZoomToNormal()
- {
- LayerImageBox.Zoom = (_globalModifiers & KeyModifiers.Shift) != 0 ? AppSettings.LockedZoomLevel : 100;
- LayerImageBox.CenterToImage();
- }
+ if(forceRefreshLayer) ForceUpdateActualLayer(issue.LayerIndex);
+ }
- public void ZoomToFitSimple()
+ /// <summary>
+ /// Center the layer preview on the passed issue, or if appropriate for issue type,
+ /// Zoom to fit the plate or print bounds.
+ /// </summary>
+ private void CenterAtIssue(Issue issue)
+ {
+ if (issue.Parent.Type is MainIssue.IssueType.EmptyLayer || issue.BoundingRectangle.IsEmpty)
{
- ZoomToFit(ZoomToFitType.Image);
+ ZoomToFit();
}
- public void ZoomToFitPrintVolume()
+ if (!issue.BoundingRectangle.IsEmpty)
{
- ZoomToFit(ZoomToFitType.Volume);
+ CenterLayerAt(GetTransposedIssueBounds(issue));
}
+ }
- private void ZoomToFit(ZoomToFitType fitType = ZoomToFitType.Auto)
- {
- if (!IsFileLoaded) return;
+ public void ZoomToNormal()
+ {
+ LayerImageBox.Zoom = (_globalModifiers & KeyModifiers.Shift) != 0 ? AppSettings.LockedZoomLevel : 100;
+ LayerImageBox.CenterToImage();
+ }
- const byte margin = 10;
- // If ALT key is pressed when ZoomToFit is performed, the configured option for
- // zoom to plate vs. zoom to print bounds will be inverted.
+ public void ZoomToFitSimple()
+ {
+ ZoomToFit(ZoomToFitType.Image);
+ }
- switch (fitType)
- {
- case ZoomToFitType.Auto:
- if (Settings.LayerPreview.ZoomToFitPrintVolumeBounds ^ (_globalModifiers & KeyModifiers.Alt) != 0)
+ public void ZoomToFitPrintVolume()
+ {
+ ZoomToFit(ZoomToFitType.Volume);
+ }
+
+ private void ZoomToFit(ZoomToFitType fitType = ZoomToFitType.Auto)
+ {
+ if (!IsFileLoaded) return;
+
+ const byte margin = 10;
+ // If ALT key is pressed when ZoomToFit is performed, the configured option for
+ // zoom to plate vs. zoom to print bounds will be inverted.
+
+ switch (fitType)
+ {
+ case ZoomToFitType.Auto:
+ if (Settings.LayerPreview.ZoomToFitPrintVolumeBounds ^ (_globalModifiers & KeyModifiers.Alt) != 0)
+ {
+ /*if (!_showLayerImageRotated)
{
- /*if (!_showLayerImageRotated)
- {
- LayerImageBox.ZoomToRegion(SlicerFile.LayerManager.BoundingRectangle, margin);
- }
- else
- {
- LayerImageBox.ZoomToRegion(LayerCache.Image.Height - 1 - SlicerFile.LayerManager.BoundingRectangle.Bottom,
- SlicerFile.LayerManager.BoundingRectangle.X,
- SlicerFile.LayerManager.BoundingRectangle.Height,
- SlicerFile.LayerManager.BoundingRectangle.Width, margin
- );
- }*/
- LayerImageBox.ZoomToRegion(GetTransposedRectangle(SlicerFile.BoundingRectangle), margin);
+ LayerImageBox.ZoomToRegion(SlicerFile.LayerManager.BoundingRectangle, margin);
}
else
{
- LayerImageBox.ZoomToFit();
- }
- break;
- case ZoomToFitType.Image:
- LayerImageBox.ZoomToFit();
- break;
- case ZoomToFitType.Volume:
+ LayerImageBox.ZoomToRegion(LayerCache.Image.Height - 1 - SlicerFile.LayerManager.BoundingRectangle.Bottom,
+ SlicerFile.LayerManager.BoundingRectangle.X,
+ SlicerFile.LayerManager.BoundingRectangle.Height,
+ SlicerFile.LayerManager.BoundingRectangle.Width, margin
+ );
+ }*/
LayerImageBox.ZoomToRegion(GetTransposedRectangle(SlicerFile.BoundingRectangle), margin);
- break;
- case ZoomToFitType.Selection:
- LayerImageBox.ZoomToSelectionRegion(margin);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(fitType), fitType, null);
- }
-
+ }
+ else
+ {
+ LayerImageBox.ZoomToFit();
+ }
+ break;
+ case ZoomToFitType.Image:
+ LayerImageBox.ZoomToFit();
+ break;
+ case ZoomToFitType.Volume:
+ LayerImageBox.ZoomToRegion(GetTransposedRectangle(SlicerFile.BoundingRectangle), margin);
+ break;
+ case ZoomToFitType.Selection:
+ LayerImageBox.ZoomToSelectionRegion(margin);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(fitType), fitType, null);
}
+
+ }
- /// <summary>
- /// If there is an issue under the point location passed, that issue will be selected and
- /// scrolled into view on the IssueList.
- /// </summary>
- private void SelectIssueAtPoint(Point location)
+ /// <summary>
+ /// If there is an issue under the point location passed, that issue will be selected and
+ /// scrolled into view on the IssueList.
+ /// </summary>
+ private void SelectIssueAtPoint(Point location)
+ {
+ //location = GetTransposedPoint(location);
+ // If location clicked is within an issue, activate it.
+ var issues = SlicerFile.IssueManager.GetIssuesBy(_actualLayer);
+ for (var i = issues.Length-1; i >= 0; i--)
{
- //location = GetTransposedPoint(location);
- // If location clicked is within an issue, activate it.
- var issues = SlicerFile.IssueManager.GetIssuesBy(_actualLayer);
- for (var i = issues.Length-1; i >= 0; i--)
- {
- if (!GetTransposedIssueBounds(issues[i]).Contains(location)) continue;
+ if (!GetTransposedIssueBounds(issues[i]).Contains(location)) continue;
- SuppressIssueGridSelectionEvent = true;
- IssuesGrid.SelectedItem = issues[i].Parent;
- SuppressIssueGridSelectionEvent = false;
+ SuppressIssueGridSelectionEvent = true;
+ IssuesGrid.SelectedItem = issues[i].Parent;
+ SuppressIssueGridSelectionEvent = false;
- ZoomToIssue(issues[i], true);
+ ZoomToIssue(issues[i], true);
- SelectedTabItem = TabIssues;
- break;
- }
+ SelectedTabItem = TabIssues;
+ break;
}
+ }
- private void LayerImageBox_PointerReleased(object? sender, PointerReleasedEventArgs e)
+ private void LayerImageBox_PointerReleased(object? sender, PointerReleasedEventArgs e)
+ {
+ var pointer = e.GetCurrentPoint(LayerImageBox);
+ if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
+ Point location = LayerImageBox.PointToImage(pointer.Position).ToDotNet();
+ if (LayerImageBox.SelectionMode == AdvancedImageBox.SelectionModes.Rectangle)
{
- var pointer = e.GetCurrentPoint(LayerImageBox);
- if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
- Point location = LayerImageBox.PointToImage(pointer.Position).ToDotNet();
- if (LayerImageBox.SelectionMode == AdvancedImageBox.SelectionModes.Rectangle)
+ if (e.InitialPressMouseButton == MouseButton.Left)
{
- if (e.InitialPressMouseButton == MouseButton.Left)
- {
- if ((e.KeyModifiers & KeyModifiers.Alt) != 0)
- {
- if (SelectObjectRoi(ROI) == 0) SelectObjectRoi(location);
- return;
- }
- return;
- }
-
- if (e.InitialPressMouseButton == MouseButton.Right)
+ if ((e.KeyModifiers & KeyModifiers.Alt) != 0)
{
- if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
-
- if ((e.KeyModifiers & KeyModifiers.Alt) != 0)
- {
- SelectObjectMask(location);
- return;
- }
-
- SelectObjectRoi(location);
-
+ if (SelectObjectRoi(ROI) == 0) SelectObjectRoi(location);
return;
}
-
return;
}
- if ((e.KeyModifiers & KeyModifiers.Control) != 0)
+ if (e.InitialPressMouseButton == MouseButton.Right)
{
- // Check to see if the clicked location is an issue,
- // and if so, select it in the ListView.
- SelectIssueAtPoint(location);
+ if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
+ if ((e.KeyModifiers & KeyModifiers.Alt) != 0)
+ {
+ SelectObjectMask(location);
+ return;
+ }
+
+ SelectObjectRoi(location);
+
return;
}
- // Shift must be pressed for any pixel edit action, middle button is ignored.
- if (!IsPixelEditorActive || e.InitialPressMouseButton == MouseButton.Middle ||
- (e.KeyModifiers & KeyModifiers.Shift) == 0) return;
- _lastPixelMouseLocation = Point.Empty;
+ return;
+ }
- // Left or Alt-Right Adds pixel, Right or Alt-Left removes pixel
- DrawPixel(e.InitialPressMouseButton == MouseButton.Left ^ (e.KeyModifiers & KeyModifiers.Alt) != 0, location, e.KeyModifiers);
+ if ((e.KeyModifiers & KeyModifiers.Control) != 0)
+ {
+ // Check to see if the clicked location is an issue,
+ // and if so, select it in the ListView.
+ SelectIssueAtPoint(location);
+
+ return;
}
- private void LayerImageBoxOnPointerPressed(object? sender, PointerPressedEventArgs e)
+ // Shift must be pressed for any pixel edit action, middle button is ignored.
+ if (!IsPixelEditorActive || e.InitialPressMouseButton == MouseButton.Middle ||
+ (e.KeyModifiers & KeyModifiers.Shift) == 0) return;
+ _lastPixelMouseLocation = Point.Empty;
+
+ // Left or Alt-Right Adds pixel, Right or Alt-Left removes pixel
+ DrawPixel(e.InitialPressMouseButton == MouseButton.Left ^ (e.KeyModifiers & KeyModifiers.Alt) != 0, location, e.KeyModifiers);
+ }
+
+ private void LayerImageBoxOnPointerPressed(object? sender, PointerPressedEventArgs e)
+ {
+ if (e.ClickCount != 2 || (e.KeyModifiers & KeyModifiers.Alt) != 0 || (e.KeyModifiers & KeyModifiers.Shift) != 0) return;
+ var pointer = e.GetCurrentPoint(LayerImageBox);
+ if (pointer.Properties.IsLeftButtonPressed)
{
- if (e.ClickCount != 2 || (e.KeyModifiers & KeyModifiers.Alt) != 0 || (e.KeyModifiers & KeyModifiers.Shift) != 0) return;
- var pointer = e.GetCurrentPoint(LayerImageBox);
- if (pointer.Properties.IsLeftButtonPressed)
- {
- if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
- var location = LayerImageBox.PointToImage(pointer.Position).ToDotNet();
- CenterLayerAt(location, AppSettings.LockedZoomLevel);
+ if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
+ var location = LayerImageBox.PointToImage(pointer.Position).ToDotNet();
+ CenterLayerAt(location, AppSettings.LockedZoomLevel);
- // Check to see if the clicked location is an issue, and if so, select it in the ListView.
- SelectIssueAtPoint(location);
+ // Check to see if the clicked location is an issue, and if so, select it in the ListView.
+ SelectIssueAtPoint(location);
- return;
- }
+ return;
+ }
- if (pointer.Properties.IsRightButtonPressed)
- {
- ZoomToFit();
- return;
- }
+ if (pointer.Properties.IsRightButtonPressed)
+ {
+ ZoomToFit();
+ return;
+ }
+ e.Handled = true;
+ }
+
+ private void LayerImageBoxOnDoubleTapped(object? sender, RoutedEventArgs e)
+ {
+ if ((_globalModifiers & KeyModifiers.Alt) != 0 || (_globalModifiers & KeyModifiers.Shift) != 0) return;
+
+ e.Handled = true;
+ }
+
+ private void LayerImageBox_KeyDown(object? sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Up)
+ {
+ GoNextLayer();
e.Handled = true;
+ return;
}
- private void LayerImageBoxOnDoubleTapped(object? sender, RoutedEventArgs e)
+ if (e.Key == Key.Down)
{
- if ((_globalModifiers & KeyModifiers.Alt) != 0 || (_globalModifiers & KeyModifiers.Shift) != 0) return;
-
+ GoPreviousLayer();
e.Handled = true;
+ return;
}
+ }
- private void LayerImageBox_KeyUp(object? sender, KeyEventArgs e)
+ private void LayerImageBox_KeyUp(object? sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Escape)
{
- if (e.Key == Key.Escape)
+ if (e.KeyModifiers == KeyModifiers.Shift)
{
- if (e.KeyModifiers == KeyModifiers.Shift)
- {
- ClearROI();
- }
- /*else if(e.KeyModifiers == KeyModifiers.Alt)
- {
- ClearMask();
- }*/
- else
- {
- ClearROIAndMask();
- }
- e.Handled = true;
- return;
+ ClearROI();
+ }
+ /*else if(e.KeyModifiers == KeyModifiers.Alt)
+ {
+ ClearMask();
+ }*/
+ else
+ {
+ ClearROIAndMask();
}
+ e.Handled = true;
+ return;
+ }
- if ((e.KeyModifiers & KeyModifiers.Control) != 0)
+ if ((e.KeyModifiers & KeyModifiers.Control) != 0)
+ {
+ if (e.Key is Key.LeftShift or Key.RightShift || (e.KeyModifiers & KeyModifiers.Shift) != 0) // Ctrl + Shift
{
- if (e.Key is Key.LeftShift or Key.RightShift || (e.KeyModifiers & KeyModifiers.Shift) != 0) // Ctrl + Shift
+ if (e.Key == Key.R)
{
- if (e.Key == Key.R)
- {
- ShowLayerImageRotated = true;
- if (_showLayerImageRotateCwDirection)
- {
- ShowLayerImageRotateCWDirection = false;
- ShowLayerImageRotateCCWDirection = true;
- }
- else
- {
- ShowLayerImageRotateCCWDirection = false;
- ShowLayerImageRotateCWDirection = true;
- }
- e.Handled = true;
- return;
- }
-
- if (e.Key == Key.F)
+ ShowLayerImageRotated = true;
+ if (_showLayerImageRotateCwDirection)
{
- ShowLayerImageFlipped = true;
- if (!_showLayerImageFlippedHorizontally && !_showLayerImageFlippedVertically)
- {
- ShowLayerImageFlippedHorizontally = true;
- }
- else if (_showLayerImageFlippedHorizontally && !_showLayerImageFlippedVertically)
- {
- ShowLayerImageFlippedHorizontally = false;
- ShowLayerImageFlippedVertically = true;
- }
- else if (!_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
- {
- ShowLayerImageFlippedHorizontally = true;
- }
- else if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
- {
- ShowLayerImageFlippedVertically = false;
- }
-
- e.Handled = true;
- return;
+ ShowLayerImageRotateCWDirection = false;
+ ShowLayerImageRotateCCWDirection = true;
}
-
- if (e.Key == Key.B)
+ else
{
- SelectModelVolumeRoi();
- return;
+ ShowLayerImageRotateCCWDirection = false;
+ ShowLayerImageRotateCWDirection = true;
}
-
- }
-
- if (e.Key is Key.D0 or Key.NumPad0)
- {
- ZoomToFit();
e.Handled = true;
return;
}
- if (e.Key == Key.R)
+ if (e.Key == Key.F)
{
- ShowLayerImageRotated = !_showLayerImageRotated;
+ ShowLayerImageFlipped = true;
+ if (!_showLayerImageFlippedHorizontally && !_showLayerImageFlippedVertically)
+ {
+ ShowLayerImageFlippedHorizontally = true;
+ }
+ else if (_showLayerImageFlippedHorizontally && !_showLayerImageFlippedVertically)
+ {
+ ShowLayerImageFlippedHorizontally = false;
+ ShowLayerImageFlippedVertically = true;
+ }
+ else if (!_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ {
+ ShowLayerImageFlippedHorizontally = true;
+ }
+ else if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ {
+ ShowLayerImageFlippedVertically = false;
+ }
+
e.Handled = true;
return;
}
- if (e.Key == Key.F)
+ if (e.Key == Key.B)
{
- ShowLayerImageFlipped = !_showLayerImageFlipped;
- e.Handled = true;
+ SelectModelVolumeRoi();
return;
}
- }
- }
- private void LayerImageBoxOnPointerMoved(object? sender, PointerEventArgs e)
- {
- var pointer = e.GetCurrentPoint(LayerImageBox);
-
- if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
- var location = LayerImageBox.PointToImage(pointer.Position).ToDotNet();
+ }
- if ((e.KeyModifiers & KeyModifiers.Control) != 0)
+ if (e.Key is Key.D0 or Key.NumPad0)
{
- var realLocation = GetTransposedPoint(location);
- unsafe
- {
- var brightness = LayerCache.ImageSpan[LayerCache.Image.GetPixelPos(realLocation)];
- LayerPixelPicker.Set(realLocation, brightness, SlicerFile.PixelToDisplayPosition(realLocation, 2));
- }
-
- RaisePropertyChanged(nameof(LayerPixelPicker));
+ ZoomToFit();
+ e.Handled = true;
+ return;
}
- if ((e.KeyModifiers & KeyModifiers.Shift) == 0) return;
-
- if (_lastPixelMouseLocation == location) return;
- _lastPixelMouseLocation = location;
-
-
- // Bail here if we're not in a draw operation, if the mouse button is not either
- // left or right, or if the location of the mouse pointer is not within the image.
- if (SelectedPixelOperationTabIndex != (int)PixelOperation.PixelOperationType.Drawing) return;
- if (!IsPixelEditorActive || pointer.Properties.IsMiddleButtonPressed) return;
- //if (!pbLayer.IsPointInImage(e.Location)) return;
-
- if (pointer.Properties.IsRightButtonPressed)
+ if (e.Key == Key.R)
{
- // Right or Alt-Left will remove a pixel
- DrawPixel(false ^ (e.KeyModifiers & KeyModifiers.Alt) != 0, location, e.KeyModifiers);
+ ShowLayerImageRotated = !_showLayerImageRotated;
+ e.Handled = true;
return;
}
- if (pointer.Properties.IsLeftButtonPressed)
+ if (e.Key == Key.F)
{
- // Left or Alt-Right will add a pixel
- DrawPixel(true ^ (e.KeyModifiers & KeyModifiers.Alt) != 0, location, e.KeyModifiers);
+ ShowLayerImageFlipped = !_showLayerImageFlipped;
+ e.Handled = true;
return;
}
}
+ }
- public bool SelectObjectRoi(Point location)
- {
- var point = GetTransposedPoint(location);
+ private void LayerImageBoxOnPointerMoved(object? sender, PointerEventArgs e)
+ {
+ var pointer = e.GetCurrentPoint(LayerImageBox);
+
+ if (!LayerImageBox.IsPointInImage(pointer.Position)) return;
+ var location = LayerImageBox.PointToImage(pointer.Position).ToDotNet();
- for (int i = LayerCache.LayerContours.Size-1; i >= 0; i--)
+ if ((e.KeyModifiers & KeyModifiers.Control) != 0)
+ {
+ var realLocation = GetTransposedPoint(location);
+ unsafe
{
- if (CvInvoke.PointPolygonTest(LayerCache.LayerContours[i], point, false) < 0) continue;
- var rectangle = CvInvoke.BoundingRectangle(LayerCache.LayerContours[i]);
- ROI = rectangle;
- return true;
+ var brightness = LayerCache.ImageSpan[LayerCache.Image.GetPixelPos(realLocation)];
+ LayerPixelPicker.Set(realLocation, brightness, SlicerFile.PixelToDisplayPosition(realLocation, 2));
}
- return false;
+ RaisePropertyChanged(nameof(LayerPixelPicker));
}
- public uint SelectObjectRoi(Rectangle roiRectangle)
- {
- if (roiRectangle.IsEmpty) return 0;
- List<Rectangle> rectangles = new();
- for (int i = 0; i < LayerCache.LayerContours.Size; i++)
- {
- var rectangle = CvInvoke.BoundingRectangle(LayerCache.LayerContours[i]);
- //roi.Intersect(rectangle);
- if (roiRectangle.IntersectsWith(rectangle))
- {
- rectangles.Add(rectangle);
- }
+ if ((e.KeyModifiers & KeyModifiers.Shift) == 0) return;
- }
- roiRectangle = rectangles.Count == 0 ? Rectangle.Empty : rectangles[0];
- for (var i = 1; i < rectangles.Count; i++)
- {
- var rectangle = rectangles[i];
- roiRectangle = Rectangle.Union(roiRectangle, rectangle);
- }
+ if (_lastPixelMouseLocation == location) return;
+ _lastPixelMouseLocation = location;
- ROI = roiRectangle;
- return (uint)rectangles.Count;
- }
+ // Bail here if we're not in a draw operation, if the mouse button is not either
+ // left or right, or if the location of the mouse pointer is not within the image.
+ if (SelectedPixelOperationTabIndex != (int)PixelOperation.PixelOperationType.Drawing) return;
+ if (!IsPixelEditorActive || pointer.Properties.IsMiddleButtonPressed) return;
+ //if (!pbLayer.IsPointInImage(e.Location)) return;
- public bool SelectObjectMask(Point location)
+ if (pointer.Properties.IsRightButtonPressed)
{
- var point = GetTransposedPoint(location);
+ // Right or Alt-Left will remove a pixel
+ DrawPixel(false ^ (e.KeyModifiers & KeyModifiers.Alt) != 0, location, e.KeyModifiers);
+ return;
+ }
- using var vec = EmguContours.GetContoursInside(LayerCache.LayerContours, LayerCache.LayerContourHierarchy, point, (_globalModifiers & KeyModifiers.Control) != 0);
- AddMaskPoints(vec.ToArrayOfArray(), false);
- return vec.Size > 0;
+ if (pointer.Properties.IsLeftButtonPressed)
+ {
+ // Left or Alt-Right will add a pixel
+ DrawPixel(true ^ (e.KeyModifiers & KeyModifiers.Alt) != 0, location, e.KeyModifiers);
+ return;
}
+ }
+
+ public bool SelectObjectRoi(Point location)
+ {
+ var point = GetTransposedPoint(location);
- public void OnLayerPixelPickerClicked()
+ for (int i = LayerCache.LayerContours.Size-1; i >= 0; i--)
{
- if (!LayerPixelPicker.IsSet) return;
- CenterLayerAt(GetTransposedPoint(LayerPixelPicker.Location, true), -1);
+ if (CvInvoke.PointPolygonTest(LayerCache.LayerContours[i], point, false) < 0) continue;
+ var rectangle = CvInvoke.BoundingRectangle(LayerCache.LayerContours[i]);
+ ROI = rectangle;
+ return true;
}
- public async void SaveCurrentLayerImage()
+ return false;
+ }
+
+ public uint SelectObjectRoi(Rectangle roiRectangle)
+ {
+ if (roiRectangle.IsEmpty) return 0;
+ List<Rectangle> rectangles = new();
+ for (int i = 0; i < LayerCache.LayerContours.Size; i++)
{
- if (!IsFileLoaded) return;
- SaveFileDialog dialog = new()
+ var rectangle = CvInvoke.BoundingRectangle(LayerCache.LayerContours[i]);
+ //roi.Intersect(rectangle);
+ if (roiRectangle.IntersectsWith(rectangle))
{
- Filters = Helpers.PngFileFilter,
- DefaultExtension = ".png",
- InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_layer{ActualLayer}.png"
- };
-
- var result = await dialog.ShowAsync(this);
- if (string.IsNullOrEmpty(result)) return;
+ rectangles.Add(rectangle);
+ }
- LayerCache.ImageBgr.Save(result);
}
+ roiRectangle = rectangles.Count == 0 ? Rectangle.Empty : rectangles[0];
+ for (var i = 1; i < rectangles.Count; i++)
+ {
+ var rectangle = rectangles[i];
+ roiRectangle = Rectangle.Union(roiRectangle, rectangle);
+ }
+
+ ROI = roiRectangle;
+
+ return (uint)rectangles.Count;
+ }
- public async void SaveCurrentROIImage()
+ public bool SelectObjectMask(Point location)
+ {
+ var point = GetTransposedPoint(location);
+
+ using var vec = EmguContours.GetContoursInside(LayerCache.LayerContours, LayerCache.LayerContourHierarchy, point, (_globalModifiers & KeyModifiers.Control) != 0);
+ AddMaskPoints(vec.ToArrayOfArray(), false);
+ return vec.Size > 0;
+ }
+
+ public void OnLayerPixelPickerClicked()
+ {
+ if (!LayerPixelPicker.IsSet) return;
+ CenterLayerAt(GetTransposedPoint(LayerPixelPicker.Location, true), -1);
+ }
+
+ public async void SaveCurrentLayerImage()
+ {
+ if (!IsFileLoaded) return;
+ SaveFileDialog dialog = new()
{
- if (!IsFileLoaded || !LayerImageBox.HaveSelection) return;
- SaveFileDialog dialog = new()
- {
- Filters = Helpers.PngFileFilter,
- DefaultExtension = ".png",
- InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_layer{ActualLayer}_ROI.png"
- };
+ Filters = Helpers.PngFileFilter,
+ DefaultExtension = ".png",
+ InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_layer{ActualLayer}.png"
+ };
- var result = await dialog.ShowAsync(this);
- if (string.IsNullOrEmpty(result)) return;
+ var result = await dialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(result)) return;
- LayerImageBox.GetSelectedBitmap()?.Save(result);
- }
+ LayerCache.ImageBgr.Save(result);
+ }
- const byte _pixelEditorCursorMinDiamater = 10;
- public void UpdatePixelEditorCursor()
+ public async void SaveCurrentROIImage()
+ {
+ if (!IsFileLoaded || !LayerImageBox.HaveSelection) return;
+ SaveFileDialog dialog = new()
{
- Mat cursor = null;
- var _pixelEditorCursorColor = new MCvScalar(
- Settings.PixelEditor.CursorColor.B,
- Settings.PixelEditor.CursorColor.G,
- Settings.PixelEditor.CursorColor.R,
- Settings.PixelEditor.CursorColor.A);
- switch ((PixelOperation.PixelOperationType)SelectedPixelOperationTabIndex)
- {
- case PixelOperation.PixelOperationType.Drawing:
+ Filters = Helpers.PngFileFilter,
+ DefaultExtension = ".png",
+ InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_layer{ActualLayer}_ROI.png"
+ };
+
+ var result = await dialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(result)) return;
+
+ LayerImageBox.GetSelectedBitmap()?.Save(result);
+ }
+
+ const byte _pixelEditorCursorMinDiamater = 10;
+ public void UpdatePixelEditorCursor()
+ {
+ Mat cursor = null;
+ var _pixelEditorCursorColor = new MCvScalar(
+ Settings.PixelEditor.CursorColor.B,
+ Settings.PixelEditor.CursorColor.G,
+ Settings.PixelEditor.CursorColor.R,
+ Settings.PixelEditor.CursorColor.A);
+ switch ((PixelOperation.PixelOperationType)SelectedPixelOperationTabIndex)
+ {
+ case PixelOperation.PixelOperationType.Drawing:
- if (DrawingPixelDrawing.BrushSize > 1)
+ if (DrawingPixelDrawing.BrushSize > 1)
+ {
+ if ((byte)DrawingPixelDrawing.BrushShape >= 1)
{
- if ((byte)DrawingPixelDrawing.BrushShape >= 1)
+ int cursorSize = DrawingPixelDrawing.BrushSize;
+ if (DrawingPixelDrawing.Thickness > 1)
{
- int cursorSize = DrawingPixelDrawing.BrushSize;
- if (DrawingPixelDrawing.Thickness > 1)
+ cursorSize += DrawingPixelDrawing.Thickness;
+ }
+ cursor = EmguExtensions.InitMat(new Size(cursorSize, cursorSize), 4);
+
+ cursor.DrawPolygon((byte) DrawingPixelDrawing.BrushShape, DrawingPixelDrawing.BrushSize / 2, cursor.Size.ToPoint().Half(),
+ _pixelEditorCursorColor, DrawingPixelDrawing.RotationAngle, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType);
+
+ if (DrawingPixelDrawing.BrushShape != PixelDrawing.BrushShapeType.Circle)
+ {
+ if (_showLayerImageFlipped && (_showLayerImageFlippedHorizontally || _showLayerImageFlippedVertically))
{
- cursorSize += DrawingPixelDrawing.Thickness;
- }
- cursor = EmguExtensions.InitMat(new Size(cursorSize, cursorSize), 4);
+ var flipType = FlipType.Both;
+
+ if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ flipType = FlipType.Both;
+ else if (_showLayerImageFlippedHorizontally)
+ flipType = FlipType.Horizontal;
+ else if (_showLayerImageFlippedVertically)
+ flipType = FlipType.Vertical;
- cursor.DrawPolygon((byte) DrawingPixelDrawing.BrushShape, DrawingPixelDrawing.BrushSize / 2, cursor.Size.ToPoint().Half(),
- _pixelEditorCursorColor, DrawingPixelDrawing.RotationAngle, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType);
+ CvInvoke.Flip(cursor, cursor, flipType);
+ }
- if (DrawingPixelDrawing.BrushShape != PixelDrawing.BrushShapeType.Circle)
+ if (_showLayerImageRotated)
{
- if (_showLayerImageFlipped && (_showLayerImageFlippedHorizontally || _showLayerImageFlippedVertically))
- {
- var flipType = FlipType.Both;
-
- if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
- flipType = FlipType.Both;
- else if (_showLayerImageFlippedHorizontally)
- flipType = FlipType.Horizontal;
- else if (_showLayerImageFlippedVertically)
- flipType = FlipType.Vertical;
-
- CvInvoke.Flip(cursor, cursor, flipType);
- }
-
- if (_showLayerImageRotated)
- {
- CvInvoke.Rotate(cursor, cursor,
- _showLayerImageRotateCcwDirection
- ? RotateFlags.Rotate90CounterClockwise
- : RotateFlags.Rotate90Clockwise);
- }
+ CvInvoke.Rotate(cursor, cursor,
+ _showLayerImageRotateCcwDirection
+ ? RotateFlags.Rotate90CounterClockwise
+ : RotateFlags.Rotate90Clockwise);
}
}
-
- /*switch (DrawingPixelDrawing.BrushShape)
- {
- case PixelDrawing.BrushShapeType.Square:
- CvInvoke.Rectangle(cursor,
- new Rectangle(Point.Empty, new Size(DrawingPixelDrawing.BrushSize, DrawingPixelDrawing.BrushSize)),
- _pixelEditorCursorColor, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType);
- _pixelEditorCursorColor.V3 = 255;
- CvInvoke.Rectangle(cursor,
- new Rectangle(Point.Empty, new Size(DrawingPixelDrawing.BrushSize-1, DrawingPixelDrawing.BrushSize-1)),
- _pixelEditorCursorColor, 1, DrawingPixelDrawing.LineType);
- break;
- case PixelDrawing.BrushShapeType.Circle:
- var center = new Point(DrawingPixelDrawing.BrushSize / 2, DrawingPixelDrawing.BrushSize / 2);
- CvInvoke.Circle(cursor,
- center,
- center.X,
- _pixelEditorCursorColor,
- DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType
- );
- _pixelEditorCursorColor.V3 = 255;
- CvInvoke.Circle(cursor,
- center,
- center.X,
- _pixelEditorCursorColor,
- 1, DrawingPixelDrawing.LineType
- );
- break;
- }*/
}
- break;
- case PixelOperation.PixelOperationType.Text:
- var text = DrawingPixelText.Text;
- if (string.IsNullOrEmpty(text) || DrawingPixelText.FontScale < 0.2) return;
-
- int baseLine = 0;
- //var size = CvInvoke.GetTextSize(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine);
- var size = EmguExtensions.GetTextSizeExtended(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine, DrawingPixelText.LineAlignment);
- //var rotatedSize = size.Rotate(DrawingPixelText.Angle);
- //Point point = (rotatedSize.Inflate(rotatedSize)).Rotate(DrawingPixelText.Angle, rotatedSize.ToPoint());
- cursor = EmguExtensions.InitMat(size.Add(), 4);
- //CvInvoke.Rectangle(cursor, new Rectangle(Point.Empty, size), _pixelEditorCursorColor, -1, DrawingPixelText.LineType);
- //_pixelEditorCursorColor.V3 = 255;
- //CvInvoke.Rectangle(cursor, new Rectangle(new Point(size.Width, 0), size), _pixelEditorCursorColor, 1, DrawingPixelText.LineType);
-
- cursor.PutTextExtended(text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror, DrawingPixelText.LineAlignment);
- //CvInvoke.PutText(cursor, text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror);
- cursor.RotateAdjustBounds(DrawingPixelText.Angle);
- //cursor.Rotate(DrawingPixelText.Angle);
- //cursor.PutTextRotated(text, cursor.Size.ToPoint().Half(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror, DrawingPixelText.Angle);
- if (_showLayerImageFlipped && (_showLayerImageFlippedHorizontally || _showLayerImageFlippedVertically))
+
+ /*switch (DrawingPixelDrawing.BrushShape)
{
- var flipType = FlipType.Both;
+ case PixelDrawing.BrushShapeType.Square:
+ CvInvoke.Rectangle(cursor,
+ new Rectangle(Point.Empty, new Size(DrawingPixelDrawing.BrushSize, DrawingPixelDrawing.BrushSize)),
+ _pixelEditorCursorColor, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType);
+ _pixelEditorCursorColor.V3 = 255;
+ CvInvoke.Rectangle(cursor,
+ new Rectangle(Point.Empty, new Size(DrawingPixelDrawing.BrushSize-1, DrawingPixelDrawing.BrushSize-1)),
+ _pixelEditorCursorColor, 1, DrawingPixelDrawing.LineType);
+ break;
+ case PixelDrawing.BrushShapeType.Circle:
+ var center = new Point(DrawingPixelDrawing.BrushSize / 2, DrawingPixelDrawing.BrushSize / 2);
+ CvInvoke.Circle(cursor,
+ center,
+ center.X,
+ _pixelEditorCursorColor,
+ DrawingPixelDrawing.Thickness, DrawingPixelDrawing.LineType
+ );
+ _pixelEditorCursorColor.V3 = 255;
+ CvInvoke.Circle(cursor,
+ center,
+ center.X,
+ _pixelEditorCursorColor,
+ 1, DrawingPixelDrawing.LineType
+ );
+ break;
+ }*/
+ }
+ break;
+ case PixelOperation.PixelOperationType.Text:
+ var text = DrawingPixelText.Text;
+ if (string.IsNullOrEmpty(text) || DrawingPixelText.FontScale < 0.2) return;
- if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
- flipType = FlipType.Both;
- else if (_showLayerImageFlippedHorizontally)
- flipType = FlipType.Horizontal;
- else if (_showLayerImageFlippedVertically)
- flipType = FlipType.Vertical;
+ int baseLine = 0;
+ //var size = CvInvoke.GetTextSize(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine);
+ var size = EmguExtensions.GetTextSizeExtended(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine, DrawingPixelText.LineAlignment);
+ //var rotatedSize = size.Rotate(DrawingPixelText.Angle);
+ //Point point = (rotatedSize.Inflate(rotatedSize)).Rotate(DrawingPixelText.Angle, rotatedSize.ToPoint());
+ cursor = EmguExtensions.InitMat(size.Add(), 4);
+ //CvInvoke.Rectangle(cursor, new Rectangle(Point.Empty, size), _pixelEditorCursorColor, -1, DrawingPixelText.LineType);
+ //_pixelEditorCursorColor.V3 = 255;
+ //CvInvoke.Rectangle(cursor, new Rectangle(new Point(size.Width, 0), size), _pixelEditorCursorColor, 1, DrawingPixelText.LineType);
+
+ cursor.PutTextExtended(text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror, DrawingPixelText.LineAlignment);
+ //CvInvoke.PutText(cursor, text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror);
+ cursor.RotateAdjustBounds(DrawingPixelText.Angle);
+ //cursor.Rotate(DrawingPixelText.Angle);
+ //cursor.PutTextRotated(text, cursor.Size.ToPoint().Half(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror, DrawingPixelText.Angle);
+ if (_showLayerImageFlipped && (_showLayerImageFlippedHorizontally || _showLayerImageFlippedVertically))
+ {
+ var flipType = FlipType.Both;
- CvInvoke.Flip(cursor, cursor, flipType);
- }
+ if (_showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ flipType = FlipType.Both;
+ else if (_showLayerImageFlippedHorizontally)
+ flipType = FlipType.Horizontal;
+ else if (_showLayerImageFlippedVertically)
+ flipType = FlipType.Vertical;
- if (_showLayerImageRotated)
- {
- CvInvoke.Rotate(cursor, cursor, _showLayerImageRotateCcwDirection ? RotateFlags.Rotate90CounterClockwise : RotateFlags.Rotate90Clockwise);
- }
- break;
- case PixelOperation.PixelOperationType.Supports:
- case PixelOperation.PixelOperationType.DrainHole:
- var diameter = SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Supports ?
- DrawingPixelSupport.TipDiameter : DrawingPixelDrainHole.Diameter;
+ CvInvoke.Flip(cursor, cursor, flipType);
+ }
- if (diameter >= _pixelEditorCursorMinDiamater)
- {
- cursor = EmguExtensions.InitMat(new System.Drawing.Size(diameter, diameter), 4);
- var center = new Point(diameter / 2, diameter / 2);
- CvInvoke.Circle(cursor,
- center,
- center.X,
- _pixelEditorCursorColor,
- -1, LineType.AntiAlias
- );
- _pixelEditorCursorColor.V3 = 255;
- CvInvoke.Circle(cursor,
- center,
- center.X,
- _pixelEditorCursorColor,
- 1, LineType.AntiAlias
- );
- }
- break;
- }
+ if (_showLayerImageRotated)
+ {
+ CvInvoke.Rotate(cursor, cursor, _showLayerImageRotateCcwDirection ? RotateFlags.Rotate90CounterClockwise : RotateFlags.Rotate90Clockwise);
+ }
+ break;
+ case PixelOperation.PixelOperationType.Supports:
+ case PixelOperation.PixelOperationType.DrainHole:
+ var diameter = SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Supports ?
+ DrawingPixelSupport.TipDiameter : DrawingPixelDrainHole.Diameter;
- if (cursor is not null)
- {
- LayerImageBox.TrackerImage = cursor.ToBitmap();
- //cursor.Save("D:\\Cursor.png");
- //LayerImageBox.TrackerImage.Save("D:\\CursorAVA.png");
- }
- /*else if (tabControlPixelEditor.SelectedIndex == (byte)PixelOperation.PixelOperationType.Text)
- {
- var text = tbPixelEditorTextText.Text;
- if (string.IsNullOrEmpty(text) || nmPixelEditorTextFontScale.Value < 0.2m) return;
+ if (diameter >= _pixelEditorCursorMinDiamater)
+ {
+ cursor = EmguExtensions.InitMat(new System.Drawing.Size(diameter, diameter), 4);
+ var center = new Point(diameter / 2, diameter / 2);
+ CvInvoke.Circle(cursor,
+ center,
+ center.X,
+ _pixelEditorCursorColor,
+ -1, LineType.AntiAlias
+ );
+ _pixelEditorCursorColor.V3 = 255;
+ CvInvoke.Circle(cursor,
+ center,
+ center.X,
+ _pixelEditorCursorColor,
+ 1, LineType.AntiAlias
+ );
+ }
+ break;
+ }
- LineType lineType = (LineType)cbPixelEditorTextLineType.SelectedItem;
- FontFace fontFace = (FontFace)cbPixelEditorTextFontFace.SelectedItem;
- double scale = (double) nmPixelEditorTextFontScale.Value * pbLayer.Zoom / 100;
- int thickness = (int) nmPixelEditorTextThickness.Value;
- int baseLine = 0;
- var size = CvInvoke.GetTextSize(text, fontFace, scale, thickness, ref baseLine);
- mat = new Mat(size, DepthType.Cv8U, 4);
- CvInvoke.PutText(mat, text, new Point(0,0), fontFace, scale, new MCvScalar(255,100,255, 255), thickness, lineType, cbPixelEditorTextMirror.Checked);
- }*/
+ if (cursor is not null)
+ {
+ LayerImageBox.TrackerImage = cursor.ToBitmap();
+ //cursor.Save("D:\\Cursor.png");
+ //LayerImageBox.TrackerImage.Save("D:\\CursorAVA.png");
}
+ /*else if (tabControlPixelEditor.SelectedIndex == (byte)PixelOperation.PixelOperationType.Text)
+ {
+ var text = tbPixelEditorTextText.Text;
+ if (string.IsNullOrEmpty(text) || nmPixelEditorTextFontScale.Value < 0.2m) return;
+
+ LineType lineType = (LineType)cbPixelEditorTextLineType.SelectedItem;
+ FontFace fontFace = (FontFace)cbPixelEditorTextFontFace.SelectedItem;
+ double scale = (double) nmPixelEditorTextFontScale.Value * pbLayer.Zoom / 100;
+ int thickness = (int) nmPixelEditorTextThickness.Value;
+ int baseLine = 0;
+ var size = CvInvoke.GetTextSize(text, fontFace, scale, thickness, ref baseLine);
+ mat = new Mat(size, DepthType.Cv8U, 4);
+ CvInvoke.PutText(mat, text, new Point(0,0), fontFace, scale, new MCvScalar(255,100,255, 255), thickness, lineType, cbPixelEditorTextMirror.Checked);
+ }*/
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.Log.cs b/UVtools.WPF/MainWindow.Log.cs
index 4939772..2f94a8b 100644
--- a/UVtools.WPF/MainWindow.Log.cs
+++ b/UVtools.WPF/MainWindow.Log.cs
@@ -10,34 +10,33 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using UVtools.WPF.Structures;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
+ public RangeObservableCollection<LogItem> Logs { get; } = new();
+ private bool _isVerbose;
+
+ public bool IsVerbose
+ {
+ get => _isVerbose;
+ set => RaiseAndSetIfChanged(ref _isVerbose, value);
+ }
+
+ public void AddLog(LogItem log)
+ {
+ log.Index = Logs.Count;
+ Logs.Insert(0, log);
+ }
+
+ public void AddLog(string description, double elapsedTime = 0) =>
+ AddLog(new LogItem(Logs.Count, description, elapsedTime));
+
+
+ public void AddLogVerbose(string description, double elapsedTime = 0)
{
- public RangeObservableCollection<LogItem> Logs { get; } = new();
- private bool _isVerbose;
-
- public bool IsVerbose
- {
- get => _isVerbose;
- set => RaiseAndSetIfChanged(ref _isVerbose, value);
- }
-
- public void AddLog(LogItem log)
- {
- log.Index = Logs.Count;
- Logs.Insert(0, log);
- }
-
- public void AddLog(string description, double elapsedTime = 0) =>
- AddLog(new LogItem(Logs.Count, description, elapsedTime));
-
-
- public void AddLogVerbose(string description, double elapsedTime = 0)
- {
- Debug.WriteLine($"{description} ({elapsedTime}s)");
- if (!_isVerbose) return;
- AddLog(description, elapsedTime);
- }
+ Debug.WriteLine($"{description} ({elapsedTime}s)");
+ if (!_isVerbose) return;
+ AddLog(description, elapsedTime);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs
index a1e93da..9da4734 100644
--- a/UVtools.WPF/MainWindow.PixelEditor.cs
+++ b/UVtools.WPF/MainWindow.PixelEditor.cs
@@ -9,6 +9,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
@@ -28,583 +29,582 @@ using UVtools.Core.PixelEditor;
using UVtools.WPF.Extensions;
using DrawingExtensions = UVtools.Core.Extensions.DrawingExtensions;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
+ public RangeObservableCollection<PixelOperation> Drawings { get; } = new ();
+ public DataGrid DrawingsGrid;
+ private int _selectedPixelOperationTabIndex;
+
+ public PixelDrawing DrawingPixelDrawing { get; } = new();
+ public PixelText DrawingPixelText { get; } = new();
+ public PixelEraser DrawingPixelEraser { get; } = new();
+ public PixelSupport DrawingPixelSupport { get; } = new();
+ public PixelDrainHole DrawingPixelDrainHole { get; } = new();
+
+ public int SelectedPixelOperationTabIndex
{
- public RangeObservableCollection<PixelOperation> Drawings { get; } = new ();
- public DataGrid DrawingsGrid;
- private int _selectedPixelOperationTabIndex;
+ get => _selectedPixelOperationTabIndex;
+ set => RaiseAndSetIfChanged(ref _selectedPixelOperationTabIndex, value);
+ }
- public PixelDrawing DrawingPixelDrawing { get; } = new();
- public PixelText DrawingPixelText { get; } = new();
- public PixelEraser DrawingPixelEraser { get; } = new();
- public PixelSupport DrawingPixelSupport { get; } = new();
- public PixelDrainHole DrawingPixelDrainHole { get; } = new();
+ public void InitPixelEditor()
+ {
+ DrawingsGrid = this.FindControl<DataGrid>("DrawingsGrid");
+ DrawingsGrid.KeyUp += DrawingsGridOnKeyUp;
+ DrawingsGrid.SelectionChanged += DrawingsGridOnSelectionChanged;
+ DrawingsGrid.CellPointerPressed += DrawingsGridOnCellPointerPressed;
+ }
- public int SelectedPixelOperationTabIndex
+ private void DrawingsGridOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ if (DrawingsGrid.SelectedItem is not PixelOperation operation)
{
- get => _selectedPixelOperationTabIndex;
- set => RaiseAndSetIfChanged(ref _selectedPixelOperationTabIndex, value);
+ ShowLayer();
+ return;
}
- public void InitPixelEditor()
+ Point location = GetTransposedPoint(operation.Location, true);
+
+ if (Settings.LayerPreview.ZoomIssues ^ (_globalModifiers & KeyModifiers.Alt) != 0)
{
- DrawingsGrid = this.FindControl<DataGrid>("DrawingsGrid");
- DrawingsGrid.KeyUp += DrawingsGridOnKeyUp;
- DrawingsGrid.SelectionChanged += DrawingsGridOnSelectionChanged;
- DrawingsGrid.CellPointerPressed += DrawingsGridOnCellPointerPressed;
+ CenterLayerAt(new Rectangle(location, operation.Size), AppSettings.LockedZoomLevel);
}
-
- private void DrawingsGridOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
+ else
{
- if (DrawingsGrid.SelectedItem is not PixelOperation operation)
- {
- ShowLayer();
- return;
- }
+ CenterLayerAt(location);
+ }
- Point location = GetTransposedPoint(operation.Location, true);
- if (Settings.LayerPreview.ZoomIssues ^ (_globalModifiers & KeyModifiers.Alt) != 0)
- {
- CenterLayerAt(new Rectangle(location, operation.Size), AppSettings.LockedZoomLevel);
- }
- else
- {
- CenterLayerAt(location);
- }
+ ForceUpdateActualLayer(operation.LayerIndex);
+ }
+ private void DrawingsGridOnCellPointerPressed(object? sender, DataGridCellPointerPressedEventArgs e)
+ {
+ if (e.PointerPressedEventArgs.ClickCount == 2) return;
+ if (DrawingsGrid.SelectedItem is not MainIssue) return;
+ // Double clicking an issue will center and zoom into the
+ // selected issue. Left click on an issue will zoom to fit.
- ForceUpdateActualLayer(operation.LayerIndex);
- }
+ var pointer = e.PointerPressedEventArgs.GetCurrentPoint(DrawingsGrid);
- private void DrawingsGridOnCellPointerPressed(object? sender, DataGridCellPointerPressedEventArgs e)
+ if (pointer.Properties.IsRightButtonPressed)
{
- if (e.PointerPressedEventArgs.ClickCount == 2) return;
- if (DrawingsGrid.SelectedItem is not MainIssue) return;
- // Double clicking an issue will center and zoom into the
- // selected issue. Left click on an issue will zoom to fit.
-
- var pointer = e.PointerPressedEventArgs.GetCurrentPoint(DrawingsGrid);
-
- if (pointer.Properties.IsRightButtonPressed)
- {
- ZoomToFit();
- return;
- }
-
+ ZoomToFit();
+ return;
}
- private void DrawingsGridOnKeyUp(object? sender, KeyEventArgs e)
- {
+ }
+
+ private void DrawingsGridOnKeyUp(object? sender, KeyEventArgs e)
+ {
- switch (e.Key)
- {
- case Key.Escape:
- DrawingsGrid.SelectedItems.Clear();
- break;
- case Key.Multiply:
- var selectedItems = DrawingsGrid.SelectedItems.OfType<PixelOperation>().ToList();
- DrawingsGrid.SelectedItems.Clear();
- foreach (PixelOperation item in Drawings)
- {
- if (!selectedItems.Contains(item))
- DrawingsGrid.SelectedItems.Add(item);
- }
+ switch (e.Key)
+ {
+ case Key.Escape:
+ DrawingsGrid.SelectedItems.Clear();
+ break;
+ case Key.Multiply:
+ var selectedItems = DrawingsGrid.SelectedItems.OfType<PixelOperation>().ToList();
+ DrawingsGrid.SelectedItems.Clear();
+ foreach (PixelOperation item in Drawings)
+ {
+ if (!selectedItems.Contains(item))
+ DrawingsGrid.SelectedItems.Add(item);
+ }
- break;
- case Key.Delete:
- OnClickDrawingRemove();
- break;
- }
+ break;
+ case Key.Delete:
+ OnClickDrawingRemove();
+ break;
}
+ }
- public void OnClickDrawingRemove()
- {
- if (DrawingsGrid.SelectedItems.Count == 0) return;
- Drawings.RemoveRange(DrawingsGrid.SelectedItems.Cast<PixelOperation>());
- ShowLayer();
- }
+ public void OnClickDrawingRemove()
+ {
+ if (DrawingsGrid.SelectedItems.Count == 0) return;
+ Drawings.RemoveRange(DrawingsGrid.SelectedItems.Cast<PixelOperation>());
+ ShowLayer();
+ }
- public async void OnClickDrawingClear()
- {
- if (Drawings.Count == 0) return;
- if (await this.MessageBoxQuestion($"Are you sure you want to clear {Drawings.Count} operations?",
+ public async void OnClickDrawingClear()
+ {
+ if (Drawings.Count == 0) return;
+ if (await this.MessageBoxQuestion($"Are you sure you want to clear {Drawings.Count} operations?",
"Clear pixel editor operations?") != ButtonResult.Yes) return;
- Drawings.Clear();
- ShowLayer();
- }
+ Drawings.Clear();
+ ShowLayer();
+ }
- void DrawPixel(bool isAdd, Point location, KeyModifiers keyModifiers)
- {
- //Stopwatch sw = Stopwatch.StartNew();
- //var point = pbLayer.PointToImage(location);
+ void DrawPixel(bool isAdd, Point location, KeyModifiers keyModifiers)
+ {
+ //Stopwatch sw = Stopwatch.StartNew();
+ //var point = pbLayer.PointToImage(location);
- Point realLocation = GetTransposedPoint(location);
+ Point realLocation = GetTransposedPoint(location);
- if ((keyModifiers & KeyModifiers.Control) != 0)
- {
- if (Drawings.RemoveAll(operation =>
+ if ((keyModifiers & KeyModifiers.Control) != 0)
+ {
+ if (Drawings.RemoveAll(operation =>
{
Rectangle rect = new(operation.Location, operation.Size);
rect.X -= operation.Size.Width / 2;
rect.Y -= operation.Size.Height / 2;
return rect.Contains(realLocation);
}) > 0)
- {
- ShowLayer();
- }
- /*var removeItems = Drawings.Where(item =>
- {
- Rectangle rect = new(item.Location, item.Size);
- rect.X -= item.Size.Width / 2;
- rect.Y -= item.Size.Height / 2;
- return rect.Contains(realLocation);
- });
- if (removeItems.Any())
- {
- Drawings.RemoveMany(removeItems);
- ShowLayer();
- }*/
-
- return;
+ {
+ ShowLayer();
}
+ /*var removeItems = Drawings.Where(item =>
+ {
+ Rectangle rect = new(item.Location, item.Size);
+ rect.X -= item.Size.Width / 2;
+ rect.Y -= item.Size.Height / 2;
+ return rect.Contains(realLocation);
+ });
+ if (removeItems.Any())
+ {
+ Drawings.RemoveMany(removeItems);
+ ShowLayer();
+ }*/
+
+ return;
+ }
- WriteableBitmap bitmap = (WriteableBitmap)LayerImageBox.Image;
- //var context = CreateRenderTarget().CreateDrawingContext(bitmap);
+ WriteableBitmap bitmap = (WriteableBitmap)LayerImageBox.Image;
+ //var context = CreateRenderTarget().CreateDrawingContext(bitmap);
- //Bitmap bmp = pbLayer.Image as Bitmap;
- if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Drawing)
+ //Bitmap bmp = pbLayer.Image as Bitmap;
+ if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Drawing)
+ {
+ uint minLayer = (uint) Math.Max(0, (int)_actualLayer - DrawingPixelDrawing.LayersBelow);
+ uint maxLayer = Math.Min(SlicerFile.LastLayerIndex, _actualLayer + DrawingPixelDrawing.LayersAbove);
+ for (uint layerIndex = minLayer; layerIndex <= maxLayer; layerIndex++)
{
- uint minLayer = (uint) Math.Max(0, (int)_actualLayer - DrawingPixelDrawing.LayersBelow);
- uint maxLayer = Math.Min(SlicerFile.LastLayerIndex, _actualLayer + DrawingPixelDrawing.LayersAbove);
- for (uint layerIndex = minLayer; layerIndex <= maxLayer; layerIndex++)
- {
- var operationDrawing = new PixelDrawing(layerIndex, realLocation, DrawingPixelDrawing.LineType,
- DrawingPixelDrawing.BrushShape, DrawingPixelDrawing.RotationAngle, DrawingPixelDrawing.BrushSize, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.RemovePixelBrightness, DrawingPixelDrawing.PixelBrightness, isAdd);
+ var operationDrawing = new PixelDrawing(layerIndex, realLocation, DrawingPixelDrawing.LineType,
+ DrawingPixelDrawing.BrushShape, DrawingPixelDrawing.RotationAngle, DrawingPixelDrawing.BrushSize, DrawingPixelDrawing.Thickness, DrawingPixelDrawing.RemovePixelBrightness, DrawingPixelDrawing.PixelBrightness, isAdd);
- //if (PixelHistory.Contains(operation)) continue;
- AddDrawing(operationDrawing);
+ //if (PixelHistory.Contains(operation)) continue;
+ AddDrawing(operationDrawing);
- if (layerIndex == _actualLayer)
- {
- var color = isAdd
- ? Settings.PixelEditor.AddPixelColor
- : Settings.PixelEditor.RemovePixelColor;
+ if (layerIndex == _actualLayer)
+ {
+ var color = isAdd
+ ? Settings.PixelEditor.AddPixelColor
+ : Settings.PixelEditor.RemovePixelColor;
- if (operationDrawing.BrushSize == 1)
+ if (operationDrawing.BrushSize == 1)
+ {
+ /*unsafe
{
- /*unsafe
- {
- using var framebuffer = bitmap.Lock();
- var data = (uint*)framebuffer.Address.ToPointer();
- data[bitmap.GetPixelPos(location)] =
- color.ToUint32();
- }*/
+ using var framebuffer = bitmap.Lock();
+ var data = (uint*)framebuffer.Address.ToPointer();
+ data[bitmap.GetPixelPos(location)] =
+ color.ToUint32();
+ }*/
- LayerCache.Canvas.DrawPoint(location.X, location.Y, new SKColor(color.ToUint32()));
+ LayerCache.Canvas.DrawPoint(location.X, location.Y, new SKColor(color.ToUint32()));
- LayerImageBox.InvalidateVisual();
- // LayerCache.ImageBgr.SetByte(operationDrawing.Location.X, operationDrawing.Location.Y,
- // new[] { color.B, color.G, color.R });
- continue;
- }
+ LayerImageBox.InvalidateVisual();
+ // LayerCache.ImageBgr.SetByte(operationDrawing.Location.X, operationDrawing.Location.Y,
+ // new[] { color.B, color.G, color.R });
+ continue;
+ }
- int halfBrush = operationDrawing.BrushSize / 2;
- double angle = operationDrawing.RotationAngle;
- switch (operationDrawing.BrushShape)
- {
- case PixelDrawing.BrushShapeType.Line:
- Point point1 = new(location.X - halfBrush, location.Y);
- Point point2 = new(location.X + halfBrush, location.Y);
+ int halfBrush = operationDrawing.BrushSize / 2;
+ double angle = operationDrawing.RotationAngle;
+ switch (operationDrawing.BrushShape)
+ {
+ case PixelDrawing.BrushShapeType.Line:
+ Point point1 = new(location.X - halfBrush, location.Y);
+ Point point2 = new(location.X + halfBrush, location.Y);
- if (_showLayerImageRotated)
+ if (_showLayerImageRotated)
+ {
+ if (_showLayerImageRotateCcwDirection)
{
- if (_showLayerImageRotateCcwDirection)
- {
- angle += 90;
- }
- else
- {
- angle -= 90;
- }
+ angle += 90;
}
+ else
+ {
+ angle -= 90;
+ }
+ }
- point1 = point1.Rotate(angle, location);
- point2 = point2.Rotate(angle, location);
+ point1 = point1.Rotate(angle, location);
+ point2 = point2.Rotate(angle, location);
- if (_showLayerImageFlipped)
+ if (_showLayerImageFlipped)
+ {
+ if (_showLayerImageFlippedHorizontally)
{
- if (_showLayerImageFlippedHorizontally)
- {
- var newPoint1 = new Point(point2.X, point1.Y);
- var newPoint2 = new Point(point1.X, point2.Y);
+ var newPoint1 = new Point(point2.X, point1.Y);
+ var newPoint2 = new Point(point1.X, point2.Y);
- point1 = newPoint1;
- point2 = newPoint2;
- }
+ point1 = newPoint1;
+ point2 = newPoint2;
+ }
- if (_showLayerImageFlippedVertically)
- {
- var newPoint1 = new Point(point1.X, point2.Y);
- var newPoint2 = new Point(point2.X, point1.Y);
+ if (_showLayerImageFlippedVertically)
+ {
+ var newPoint1 = new Point(point1.X, point2.Y);
+ var newPoint2 = new Point(point2.X, point1.Y);
- point1 = newPoint1;
- point2 = newPoint2;
- }
+ point1 = newPoint1;
+ point2 = newPoint2;
}
+ }
- /*if (_showLayerImageRotated)
+ /*if (_showLayerImageRotated)
+ {
+ if (!_showLayerImageFlipped || _showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ {
+ if (_showLayerImageRotateCcwDirection)
+ {
+ angle -= 90;
+ }
+ else
+ {
+ angle += 90;
+ }
+ }
+ else
{
- if (!_showLayerImageFlipped || _showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ if (_showLayerImageRotateCcwDirection)
{
- if (_showLayerImageRotateCcwDirection)
- {
- angle -= 90;
- }
- else
- {
- angle += 90;
- }
+ angle += 90;
}
else
{
- if (_showLayerImageRotateCcwDirection)
- {
- angle += 90;
- }
- else
- {
- angle -= 90;
- }
+ angle -= 90;
}
- }*/
+ }
+ }*/
- LayerCache.Canvas.DrawLine(point1.X, point1.Y, point2.X, point2.Y, new SKPaint
+ LayerCache.Canvas.DrawLine(point1.X, point1.Y, point2.X, point2.Y, new SKPaint
+ {
+ IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
+ Color = new SKColor(color.ToUint32()),
+ IsStroke = operationDrawing.Thickness >= 0,
+ StrokeWidth = operationDrawing.Thickness,
+ StrokeCap = SKStrokeCap.Round
+ });
+ break;
+ /*case PixelDrawing.BrushShapeType.Square:
+ LayerCache.Canvas.DrawRect(location.X - halfBrush, location.Y - halfBrush,
+ operationDrawing.BrushSize,
+ operationDrawing.BrushSize,
+ new SKPaint
+ {
+ IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
+ Color = new SKColor(color.ToUint32()),
+ IsStroke = operationDrawing.Thickness >= 0,
+ StrokeWidth = operationDrawing.Thickness
+ } );
+ /*CvInvoke.Rectangle(LayerCache.ImageBgr, GetTransposedRectangle(operationDrawing.Rectangle),
+ new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
+ operationDrawing.LineType);*/
+ //break;
+ case PixelDrawing.BrushShapeType.Circle:
+ LayerCache.Canvas.DrawCircle(location.X, location.Y, operationDrawing.BrushSize / 2f,
+ new SKPaint
{
IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
Color = new SKColor(color.ToUint32()),
IsStroke = operationDrawing.Thickness >= 0,
- StrokeWidth = operationDrawing.Thickness,
- StrokeCap = SKStrokeCap.Round
+ StrokeWidth = operationDrawing.Thickness
});
- break;
- /*case PixelDrawing.BrushShapeType.Square:
- LayerCache.Canvas.DrawRect(location.X - halfBrush, location.Y - halfBrush,
- operationDrawing.BrushSize,
- operationDrawing.BrushSize,
- new SKPaint
+
+ /*CvInvoke.Circle(LayerCache.ImageBgr, location, operationDrawing.BrushSize / 2,
+ new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
+ operationDrawing.LineType);*/
+ break;
+ default:
+ if (_showLayerImageRotated)
+ {
+ if (!_showLayerImageFlipped || _showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ {
+ if (_showLayerImageRotateCcwDirection)
{
- IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
- Color = new SKColor(color.ToUint32()),
- IsStroke = operationDrawing.Thickness >= 0,
- StrokeWidth = operationDrawing.Thickness
- } );
- /*CvInvoke.Rectangle(LayerCache.ImageBgr, GetTransposedRectangle(operationDrawing.Rectangle),
- new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
- operationDrawing.LineType);*/
- //break;
- case PixelDrawing.BrushShapeType.Circle:
- LayerCache.Canvas.DrawCircle(location.X, location.Y, operationDrawing.BrushSize / 2f,
- new SKPaint
+ angle -= 90;
+ }
+ else
{
- IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
- Color = new SKColor(color.ToUint32()),
- IsStroke = operationDrawing.Thickness >= 0,
- StrokeWidth = operationDrawing.Thickness
- });
-
- /*CvInvoke.Circle(LayerCache.ImageBgr, location, operationDrawing.BrushSize / 2,
- new MCvScalar(color.B, color.G, color.R), operationDrawing.Thickness,
- operationDrawing.LineType);*/
- break;
- default:
- if (_showLayerImageRotated)
+ angle += 90;
+ }
+ }
+ else
{
- if (!_showLayerImageFlipped || _showLayerImageFlippedHorizontally && _showLayerImageFlippedVertically)
+ if (_showLayerImageRotateCcwDirection)
{
- if (_showLayerImageRotateCcwDirection)
- {
- angle -= 90;
- }
- else
- {
- angle += 90;
- }
+ angle += 90;
}
else
{
- if (_showLayerImageRotateCcwDirection)
- {
- angle += 90;
- }
- else
- {
- angle -= 90;
- }
+ angle -= 90;
}
}
+ }
- var vertices = DrawingExtensions.GetPolygonVertices((byte) operationDrawing.BrushShape,
- operationDrawing.BrushSize / 2, location, angle, _showLayerImageFlipped && _showLayerImageFlippedHorizontally, _showLayerImageFlipped && _showLayerImageFlippedVertically);
+ var vertices = DrawingExtensions.GetPolygonVertices((byte) operationDrawing.BrushShape,
+ operationDrawing.BrushSize / 2, location, angle, _showLayerImageFlipped && _showLayerImageFlippedHorizontally, _showLayerImageFlipped && _showLayerImageFlippedVertically);
- //if(angle % 360 != 0) PointExtensions.Rotate(vertices, angle, location);
+ //if(angle % 360 != 0) PointExtensions.Rotate(vertices, angle, location);
- var path = new SKPath();
- path.MoveTo(vertices[0].X, vertices[0].Y);
- for (var i = 1; i < vertices.Length; i++)
- {
- path.LineTo(vertices[i].X, vertices[i].Y);
- }
- path.Close();
+ var path = new SKPath();
+ path.MoveTo(vertices[0].X, vertices[0].Y);
+ for (var i = 1; i < vertices.Length; i++)
+ {
+ path.LineTo(vertices[i].X, vertices[i].Y);
+ }
+ path.Close();
- LayerCache.Canvas.DrawPath(path, new SKPaint
+ LayerCache.Canvas.DrawPath(path, new SKPaint
+ {
+ IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
+ Color = new SKColor(color.ToUint32()),
+ IsStroke = operationDrawing.Thickness >= 0,
+ StrokeWidth = operationDrawing.Thickness
+ });
+ /*LayerCache.Canvas.DrawPoints(SKPointMode.Polygon, points,
+ new SKPaint
{
IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
Color = new SKColor(color.ToUint32()),
IsStroke = operationDrawing.Thickness >= 0,
StrokeWidth = operationDrawing.Thickness
- });
- /*LayerCache.Canvas.DrawPoints(SKPointMode.Polygon, points,
- new SKPaint
- {
- IsAntialias = operationDrawing.LineType == LineType.AntiAlias,
- Color = new SKColor(color.ToUint32()),
- IsStroke = operationDrawing.Thickness >= 0,
- StrokeWidth = operationDrawing.Thickness
- });*/
- break;
- }
- LayerImageBox.InvalidateVisual();
- //RefreshLayerImage();
+ });*/
+ break;
}
+ LayerImageBox.InvalidateVisual();
+ //RefreshLayerImage();
}
}
- else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Text)
- {
- if (string.IsNullOrEmpty(DrawingPixelText.Text) || DrawingPixelText.FontScale < 0.2) return;
+ }
+ else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Text)
+ {
+ if (string.IsNullOrEmpty(DrawingPixelText.Text) || DrawingPixelText.FontScale < 0.2) return;
- uint minLayer = (uint) Math.Max(0, (int)ActualLayer - DrawingPixelText.LayersBelow);
- uint maxLayer = Math.Min(SlicerFile.LastLayerIndex, ActualLayer + DrawingPixelText.LayersAbove);
- for (uint layerIndex = minLayer; layerIndex <= maxLayer; layerIndex++)
- {
- var operationText = new PixelText(layerIndex, realLocation, DrawingPixelText.LineType,
- DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness,
- DrawingPixelText.Text, DrawingPixelText.Mirror, DrawingPixelText.LineAlignment, DrawingPixelText.Angle, DrawingPixelText.RemovePixelBrightness, DrawingPixelText.PixelBrightness, isAdd);
+ uint minLayer = (uint) Math.Max(0, (int)ActualLayer - DrawingPixelText.LayersBelow);
+ uint maxLayer = Math.Min(SlicerFile.LastLayerIndex, ActualLayer + DrawingPixelText.LayersAbove);
+ for (uint layerIndex = minLayer; layerIndex <= maxLayer; layerIndex++)
+ {
+ var operationText = new PixelText(layerIndex, realLocation, DrawingPixelText.LineType,
+ DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness,
+ DrawingPixelText.Text, DrawingPixelText.Mirror, DrawingPixelText.LineAlignment, DrawingPixelText.Angle, DrawingPixelText.RemovePixelBrightness, DrawingPixelText.PixelBrightness, isAdd);
- //if (PixelHistory.Contains(operation)) continue;
- //PixelHistory.Add(operation);
- AddDrawing(operationText);
+ //if (PixelHistory.Contains(operation)) continue;
+ //PixelHistory.Add(operation);
+ AddDrawing(operationText);
- /*var color = isAdd
- ? Settings.PixelEditor.AddPixelColor : Settings.PixelEditor.RemovePixelColor;
+ /*var color = isAdd
+ ? Settings.PixelEditor.AddPixelColor : Settings.PixelEditor.RemovePixelColor;
- if (layerIndex == _actualLayer)
- {
- CvInvoke.PutText(LayerCache.ImageBgr, operationText.Text, location,
- operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R),
- operationText.Thickness, operationText.LineType, operationText.Mirror);
- RefreshLayerImage();
- }*/
- }
-
- ShowLayer();
- return;
+ if (layerIndex == _actualLayer)
+ {
+ CvInvoke.PutText(LayerCache.ImageBgr, operationText.Text, location,
+ operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R),
+ operationText.Thickness, operationText.LineType, operationText.Mirror);
+ RefreshLayerImage();
+ }*/
}
- else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Eraser)
+
+ ShowLayer();
+ return;
+ }
+ else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Eraser)
+ {
+ if (LayerCache.Image.GetByte(realLocation) < 10) return;
+ uint minLayer = (uint) Math.Max(0, (int)ActualLayer - DrawingPixelEraser.LayersBelow);
+ uint maxLayer = Math.Min(SlicerFile.LastLayerIndex, ActualLayer + DrawingPixelEraser.LayersAbove);
+ for (uint layerIndex = minLayer; layerIndex <= maxLayer; layerIndex++)
{
- if (LayerCache.Image.GetByte(realLocation) < 10) return;
- uint minLayer = (uint) Math.Max(0, (int)ActualLayer - DrawingPixelEraser.LayersBelow);
- uint maxLayer = Math.Min(SlicerFile.LastLayerIndex, ActualLayer + DrawingPixelEraser.LayersAbove);
- for (uint layerIndex = minLayer; layerIndex <= maxLayer; layerIndex++)
- {
- var operationEraser = new PixelEraser(layerIndex, realLocation, DrawingPixelEraser.PixelBrightness);
+ var operationEraser = new PixelEraser(layerIndex, realLocation, DrawingPixelEraser.PixelBrightness);
- //if (PixelHistory.Contains(operation)) continue;
- AddDrawing(operationEraser);
+ //if (PixelHistory.Contains(operation)) continue;
+ AddDrawing(operationEraser);
- /*if (layerIndex == _actualLayer)
+ /*if (layerIndex == _actualLayer)
+ {
+ for (int i = 0; i < LayerCache.LayerContours.Size; i++)
{
- for (int i = 0; i < LayerCache.LayerContours.Size; i++)
+ if (CvInvoke.PointPolygonTest(LayerCache.LayerContours[i], operationEraser.Location, false) >= 0)
{
- if (CvInvoke.PointPolygonTest(LayerCache.LayerContours[i], operationEraser.Location, false) >= 0)
- {
- CvInvoke.DrawContours(LayerCache.ImageBgr, LayerCache.LayerContours, i,
- new MCvScalar(Settings.PixelEditor.RemovePixelColor.B, Settings.PixelEditor.RemovePixelColor.G, Settings.PixelEditor.RemovePixelColor.R), -1);
- RefreshLayerImage();
- break;
- }
+ CvInvoke.DrawContours(LayerCache.ImageBgr, LayerCache.LayerContours, i,
+ new MCvScalar(Settings.PixelEditor.RemovePixelColor.B, Settings.PixelEditor.RemovePixelColor.G, Settings.PixelEditor.RemovePixelColor.R), -1);
+ RefreshLayerImage();
+ break;
}
- }*/
- }
-
- ShowLayer();
- return;
+ }
+ }*/
}
- else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Supports)
- {
- if (_actualLayer == 0) return;
- var operationSupport = new PixelSupport(ActualLayer, realLocation,
- DrawingPixelSupport.TipDiameter, DrawingPixelSupport.PillarDiameter,
- DrawingPixelSupport.BaseDiameter, DrawingPixelSupport.PixelBrightness);
- //if (PixelHistory.Contains(operation)) return;
- AddDrawing(operationSupport);
+ ShowLayer();
+ return;
+ }
+ else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.Supports)
+ {
+ if (_actualLayer == 0) return;
+ var operationSupport = new PixelSupport(ActualLayer, realLocation,
+ DrawingPixelSupport.TipDiameter, DrawingPixelSupport.PillarDiameter,
+ DrawingPixelSupport.BaseDiameter, DrawingPixelSupport.PixelBrightness);
- CvInvoke.Circle(LayerCache.ImageBgr, location, operationSupport.TipDiameter / 2,
- new MCvScalar(Settings.PixelEditor.SupportsColor.B, Settings.PixelEditor.SupportsColor.G, Settings.PixelEditor.SupportsColor.R), -1);
- RefreshLayerImage();
- }
- else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.DrainHole)
- {
- if (_actualLayer == 0) return;
- var operationDrainHole = new PixelDrainHole(ActualLayer, realLocation, DrawingPixelDrainHole.Diameter);
+ //if (PixelHistory.Contains(operation)) return;
+ AddDrawing(operationSupport);
- //if (PixelHistory.Contains(operation)) return;
- AddDrawing(operationDrainHole);
+ CvInvoke.Circle(LayerCache.ImageBgr, location, operationSupport.TipDiameter / 2,
+ new MCvScalar(Settings.PixelEditor.SupportsColor.B, Settings.PixelEditor.SupportsColor.G, Settings.PixelEditor.SupportsColor.R), -1);
+ RefreshLayerImage();
+ }
+ else if (SelectedPixelOperationTabIndex == (byte)PixelOperation.PixelOperationType.DrainHole)
+ {
+ if (_actualLayer == 0) return;
+ var operationDrainHole = new PixelDrainHole(ActualLayer, realLocation, DrawingPixelDrainHole.Diameter);
- CvInvoke.Circle(LayerCache.ImageBgr, location, operationDrainHole.Diameter / 2,
- new MCvScalar(Settings.PixelEditor.DrainHoleColor.B, Settings.PixelEditor.DrainHoleColor.G, Settings.PixelEditor.DrainHoleColor.R), -1);
- RefreshLayerImage();
- }
- else
- {
- throw new NotImplementedException("Missing pixel operation");
- }
+ //if (PixelHistory.Contains(operation)) return;
+ AddDrawing(operationDrainHole);
+
+ CvInvoke.Circle(LayerCache.ImageBgr, location, operationDrainHole.Diameter / 2,
+ new MCvScalar(Settings.PixelEditor.DrainHoleColor.B, Settings.PixelEditor.DrainHoleColor.G, Settings.PixelEditor.DrainHoleColor.R), -1);
+ RefreshLayerImage();
+ }
+ else
+ {
+ throw new NotImplementedException("Missing pixel operation");
}
+ }
- public void AddDrawing(PixelOperation operation)
+ public void AddDrawing(PixelOperation operation)
+ {
+ Drawings.Insert(0, operation);
+ for (int i = 0; i < Drawings.Count; i++)
{
- Drawings.Insert(0, operation);
- for (int i = 0; i < Drawings.Count; i++)
- {
- Drawings[i].Index = (uint) (Drawings.Count - i);
- }
+ Drawings[i].Index = (uint) (Drawings.Count - i);
}
+ }
- public async void DrawModifications(bool exitEditor)
+ public async void DrawModifications(bool exitEditor)
+ {
+ if (Drawings.Count == 0)
{
- if (Drawings.Count == 0)
+ if (exitEditor && !ReferenceEquals(LastSelectedTabItem, TabPixelEditor))
{
- if (exitEditor && !ReferenceEquals(LastSelectedTabItem, TabPixelEditor))
- {
- SelectedTabItem = LastSelectedTabItem;
- }
-
- return;
+ SelectedTabItem = LastSelectedTabItem;
}
- ButtonResult result;
+ return;
+ }
- if (exitEditor)
- {
- result = await this.MessageBoxQuestion(
- "There are edit operations that have not been applied. " +
- "Would you like to apply all operations before closing the editor?",
- "Closing image editor?", ButtonEnum.YesNoCancel);
- }
- else
- {
+ ButtonResult result;
- result = await this.MessageBoxQuestion(
- "Are you sure you want to apply all operations?",
- "Apply image editor changes?");
+ if (exitEditor)
+ {
+ result = await this.MessageBoxQuestion(
+ "There are edit operations that have not been applied. " +
+ "Would you like to apply all operations before closing the editor?",
+ "Closing image editor?", ButtonEnum.YesNoCancel);
+ }
+ else
+ {
- // For the "apply" case, We aren't exiting the editor, so map "No" to "Cancel" here
- // in order to prevent pixel history from being cleared.
- result = result == ButtonResult.No ? ButtonResult.Cancel : ButtonResult.Yes;
- }
+ result = await this.MessageBoxQuestion(
+ "Are you sure you want to apply all operations?",
+ "Apply image editor changes?");
- if (result == ButtonResult.Cancel)
- {
- IsPixelEditorActive = true;
- return;
- }
+ // For the "apply" case, We aren't exiting the editor, so map "No" to "Cancel" here
+ // in order to prevent pixel history from being cleared.
+ result = result == ButtonResult.No ? ButtonResult.Cancel : ButtonResult.Yes;
+ }
- if (result == ButtonResult.Yes)
- {
- IsGUIEnabled = false;
- ShowProgressWindow("Drawing pixels");
- Clipboard.Snapshot();
+ if (result == ButtonResult.Cancel)
+ {
+ IsPixelEditorActive = true;
+ return;
+ }
+
+ if (result == ButtonResult.Yes)
+ {
+ IsGUIEnabled = false;
+ ShowProgressWindow("Drawing pixels");
+ Clipboard.Snapshot();
- var task = await Task.Factory.StartNew(async () =>
+ var task = await Task.Factory.StartNew(async () =>
+ {
+ try
+ {
+ SlicerFile.DrawModifications(Drawings, Progress);
+ return true;
+ }
+ catch (OperationCanceledException)
{
- try
- {
- SlicerFile.LayerManager.DrawModifications(Drawings, Progress);
- return true;
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception ex)
+ }
+ catch (Exception ex)
+ {
+ await Dispatcher.UIThread.InvokeAsync(async () =>
{
- await Dispatcher.UIThread.InvokeAsync(async () =>
- {
- await this.MessageBoxError(ex.ToString(), "Drawing operation failed!");
- });
- }
+ await this.MessageBoxError(ex.ToString(), "Drawing operation failed!");
+ });
+ }
- return false;
- });
+ return false;
+ });
- IsGUIEnabled = true;
+ IsGUIEnabled = true;
- if (!task.Result)
- {
- Clipboard.RestoreSnapshot();
- ShowLayer();
- return;
- }
+ if (!task.Result)
+ {
+ Clipboard.RestoreSnapshot();
+ ShowLayer();
+ return;
+ }
- Clipboard.Clip($"Draw {Drawings.Count} modifications");
+ Clipboard.Clip($"Draw {Drawings.Count} modifications");
- if (Settings.PixelEditor.PartialUpdateIslandsOnEditing)
+ if (Settings.PixelEditor.PartialUpdateIslandsOnEditing)
+ {
+ List<uint> whiteListLayers = new();
+ foreach (var item in Drawings)
{
- List<uint> whiteListLayers = new();
- foreach (var item in Drawings)
+ /*if (item.OperationType != PixelOperation.PixelOperationType.Drawing &&
+ item.OperationType != PixelOperation.PixelOperationType.Text &&
+ item.OperationType != PixelOperation.PixelOperationType.Eraser &&
+ item.OperationType != PixelOperation.PixelOperationType.Supports) continue;*/
+ if (!whiteListLayers.Contains(item.LayerIndex))
+ whiteListLayers.Add(item.LayerIndex);
+
+ uint nextLayer = item.LayerIndex + 1;
+ if (nextLayer < SlicerFile.LayerCount &&
+ !whiteListLayers.Contains(nextLayer))
{
- /*if (item.OperationType != PixelOperation.PixelOperationType.Drawing &&
- item.OperationType != PixelOperation.PixelOperationType.Text &&
- item.OperationType != PixelOperation.PixelOperationType.Eraser &&
- item.OperationType != PixelOperation.PixelOperationType.Supports) continue;*/
- if (!whiteListLayers.Contains(item.LayerIndex))
- whiteListLayers.Add(item.LayerIndex);
-
- uint nextLayer = item.LayerIndex + 1;
- if (nextLayer < SlicerFile.LayerCount &&
- !whiteListLayers.Contains(nextLayer))
- {
- whiteListLayers.Add(nextLayer);
- }
+ whiteListLayers.Add(nextLayer);
}
-
- await UpdateIslandsOverhangs(whiteListLayers);
}
+
+ await UpdateIslandsOverhangs(whiteListLayers);
}
+ }
- Drawings.Clear();
- ShowLayer();
+ Drawings.Clear();
+ ShowLayer();
- if (exitEditor || (Settings.PixelEditor.CloseEditorOnApply && result == ButtonResult.Yes))
+ if (exitEditor || (Settings.PixelEditor.CloseEditorOnApply && result == ButtonResult.Yes))
+ {
+ IsPixelEditorActive = false;
+ if (!ReferenceEquals(LastSelectedTabItem, TabPixelEditor))
{
- IsPixelEditorActive = false;
- if (!ReferenceEquals(LastSelectedTabItem, TabPixelEditor))
- {
- SelectedTabItem = LastSelectedTabItem;
- }
+ SelectedTabItem = LastSelectedTabItem;
}
-
- CanSave = true;
}
+
+ CanSave = true;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.Progress.cs b/UVtools.WPF/MainWindow.Progress.cs
index 14f5eaf..3cceffc 100644
--- a/UVtools.WPF/MainWindow.Progress.cs
+++ b/UVtools.WPF/MainWindow.Progress.cs
@@ -12,84 +12,83 @@ using Avalonia.Threading;
using UVtools.Core.Operations;
using UVtools.WPF.Structures;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
- {
- #region Members
- public OperationProgress Progress { get; } = new();
- private readonly Timer _progressTimer = new(200) { AutoReset = true };
- private long _progressLastTotalSeconds;
- private LogItem _progressLogItem;
- private bool _isProgressVisible;
+ #region Members
+ public OperationProgress Progress { get; } = new();
+ private readonly Timer _progressTimer = new(200) { AutoReset = true };
+ private long _progressLastTotalSeconds;
+ private LogItem _progressLogItem;
+ private bool _isProgressVisible;
- #endregion
+ #endregion
- #region Properties
+ #region Properties
- public bool IsProgressVisible
- {
- get => _isProgressVisible;
- set => RaiseAndSetIfChanged(ref _isProgressVisible, value);
- }
+ public bool IsProgressVisible
+ {
+ get => _isProgressVisible;
+ set => RaiseAndSetIfChanged(ref _isProgressVisible, value);
+ }
- #endregion
+ #endregion
- public void InitProgress()
+ public void InitProgress()
+ {
+ _progressTimer.Elapsed += (sender, args) =>
{
- _progressTimer.Elapsed += (sender, args) =>
- {
- var elapsedSeconds = Progress.StopWatch.ElapsedMilliseconds / 1000;
- if (_progressLastTotalSeconds == elapsedSeconds) return;
- /*Debug.WriteLine(StopWatch.ElapsedMilliseconds);
- Debug.WriteLine(elapsedSeconds);
- Debug.WriteLine(_lastTotalSeconds);*/
- _progressLastTotalSeconds = elapsedSeconds;
+ var elapsedSeconds = Progress.StopWatch.ElapsedMilliseconds / 1000;
+ if (_progressLastTotalSeconds == elapsedSeconds) return;
+ /*Debug.WriteLine(StopWatch.ElapsedMilliseconds);
+ Debug.WriteLine(elapsedSeconds);
+ Debug.WriteLine(_lastTotalSeconds);*/
+ _progressLastTotalSeconds = elapsedSeconds;
- Dispatcher.UIThread.InvokeAsync(() => Progress.TriggerRefresh(), DispatcherPriority.Render);
+ Dispatcher.UIThread.InvokeAsync(() => Progress.TriggerRefresh(), DispatcherPriority.Render);
- };
- }
+ };
+ }
- public void ProgressOnClickCancel()
- {
- if (!Progress.CanCancel) return;
- DialogResult = DialogResults.Cancel;
- Progress.CanCancel = false;
- Progress.TokenSource.Cancel();
- }
+ public void ProgressOnClickCancel()
+ {
+ if (!Progress.CanCancel) return;
+ DialogResult = DialogResults.Cancel;
+ Progress.CanCancel = false;
+ Progress.TokenSource.Cancel();
+ }
- public void ProgressShow(string title, bool canCancel = true)
- {
- IsGUIEnabled = false;
- Progress.Init(canCancel);
- Progress.Title = title;
- _progressLogItem = new(title);
+ public void ProgressShow(string title, bool canCancel = true)
+ {
+ IsGUIEnabled = false;
+ Progress.Init(canCancel);
+ Progress.Title = title;
+ _progressLogItem = new(title);
- Progress.StopWatch.Restart();
- _progressLastTotalSeconds = 0;
+ Progress.StopWatch.Restart();
+ _progressLastTotalSeconds = 0;
- if (!_progressTimer.Enabled)
- {
- _progressTimer.Start();
- }
+ if (!_progressTimer.Enabled)
+ {
+ _progressTimer.Start();
+ }
- Progress.TriggerRefresh();
+ Progress.TriggerRefresh();
- IsProgressVisible = true;
+ IsProgressVisible = true;
- InvalidateVisual();
- }
+ InvalidateVisual();
+ }
- public void ProgressFinish()
- {
- _progressTimer.Stop();
- Progress.StopWatch.Stop();
- _progressLogItem.ElapsedTime = Math.Round(Progress.StopWatch.Elapsed.TotalSeconds, 2);
- App.MainWindow.AddLog(_progressLogItem);
- IsProgressVisible = false;
- InvalidateVisual();
- }
+ public void ProgressFinish()
+ {
+ _progressTimer.Stop();
+ Progress.StopWatch.Stop();
+ _progressLogItem.ElapsedTime = Math.Round(Progress.StopWatch.Elapsed.TotalSeconds, 2);
+ App.MainWindow.AddLog(_progressLogItem);
+ IsProgressVisible = false;
+ InvalidateVisual();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.Suggestions.cs b/UVtools.WPF/MainWindow.Suggestions.cs
index be034dc..a61efae 100644
--- a/UVtools.WPF/MainWindow.Suggestions.cs
+++ b/UVtools.WPF/MainWindow.Suggestions.cs
@@ -14,87 +14,123 @@ using Avalonia.Controls;
using MessageBox.Avalonia.Enums;
using UVtools.Core.Suggestions;
using UVtools.WPF.Extensions;
+using UVtools.WPF.Structures;
+using UVtools.WPF.Windows;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow
{
- public partial class MainWindow
+ #region Members
+
+ private ListBox _suggestionsAvailableListBox;
+
+ #endregion
+
+ #region Properties
+ public Suggestion[] Suggestions
{
- #region Members
+ get => SuggestionManager.Instance.Suggestions;
+ set => SuggestionManager.Instance.Suggestions = value;
+ }
+
+ public RangeObservableCollection<Suggestion> SuggestionsAvailable { get; } = new();
+ public RangeObservableCollection<Suggestion> SuggestionsApplied { get; } = new();
- private ListBox _suggestionsAvailableListBox;
+ #endregion
- #endregion
+ #region Methods
+ public void InitSuggestions()
+ {
+ _suggestionsAvailableListBox = this.FindControl<ListBox>("SuggestionsAvailableListBox");
+ }
- #region Properties
- public Suggestion[] Suggestions { get; } =
+ public void PopulateSuggestions(bool tryToAutoApply = true)
+ {
+ var suggestionsAvailable = new List<Suggestion>();
+ var suggestionsApplied = new List<Suggestion>();
+ foreach (var suggestion in Suggestions)
{
-#if DEBUG
- //new SuggestionBottomLayerCount(),
- //new SuggestionWaitTimeAfterCure(),
- //new SuggestionLayerHeight()
-#endif
- };
+ suggestion.SlicerFile = SlicerFile;
+ if(!suggestion.Enabled || !suggestion.IsAvailable) continue;
+ if (tryToAutoApply)
+ {
+ if (suggestion.ExecuteIfAutoApply())
+ {
+ CanSave = true;
+ }
+ }
+
+ if(suggestion.IsApplied) suggestionsApplied.Add(suggestion);
+ else suggestionsAvailable.Add(suggestion);
+ }
- public RangeObservableCollection<Suggestion> SuggestionsAvailable { get; } = new();
- public RangeObservableCollection<Suggestion> SuggestionsApplied { get; } = new();
+ SuggestionsAvailable.ReplaceCollection(suggestionsAvailable);
+ SuggestionsApplied.ReplaceCollection(suggestionsApplied);
+ }
- #endregion
+ public async void ApplySuggestionsClicked()
+ {
+ if (!IsFileLoaded || _suggestionsAvailableListBox.SelectedItems.Count == 0) return;
+ var suggestions = _suggestionsAvailableListBox.SelectedItems.Cast<Suggestion>().Where(suggestion => !suggestion.IsInformativeOnly).ToArray();
+ if (suggestions.Length == 0) return;
+ var sb = new StringBuilder($"Are you sure you want to apply the following {suggestions.Length} suggestions?:\n\n");
- #region Methods
- public void InitSuggestions()
+ foreach (var suggestion in suggestions)
{
- _suggestionsAvailableListBox = this.FindControl<ListBox>("SuggestionsAvailableListBox");
+ sb.AppendLine(suggestion.ConfirmationMessage);
}
+ if (await this.MessageBoxQuestion(sb.ToString(), "Apply suggestions?") != ButtonResult.Yes) return;
- public void PopulateSuggestions(bool tryToAutoApply = true)
+ uint executed = 0;
+ foreach (var suggestion in suggestions)
{
- var suggestionsAvailable = new List<Suggestion>();
- var suggestionsApplied = new List<Suggestion>();
- foreach (var suggestion in Suggestions)
+ if (suggestion.Execute())
{
- suggestion.SlicerFile = SlicerFile;
- if(!suggestion.Enabled || !suggestion.IsAvailable) continue;
- if(tryToAutoApply) suggestion.ExecuteIfAutoApply();
-
- if(suggestion.IsApplied) suggestionsApplied.Add(suggestion);
- else suggestionsAvailable.Add(suggestion);
+ executed++;
}
-
- SuggestionsAvailable.ReplaceCollection(suggestionsAvailable);
- SuggestionsApplied.ReplaceCollection(suggestionsApplied);
}
- public async void ApplySuggestionsClicked()
+ if (executed > 0)
{
- if (!IsFileLoaded || _suggestionsAvailableListBox.SelectedItems.Count == 0) return;
- var suggestions = _suggestionsAvailableListBox.SelectedItems.Cast<Suggestion>().Where(suggestion => !suggestion.IsInformativeOnly).ToArray();
- if (suggestions.Length == 0) return;
- var sb = new StringBuilder($"Are you sure you want to apply the following {suggestions.Length} suggestions?:\n\n");
+ CanSave = true;
+ ResetDataContext();
+ ForceUpdateActualLayer();
+ }
- foreach (var suggestion in suggestions)
- {
- sb.AppendLine(suggestion.ConfirmationMessage);
- }
- if (await this.MessageBoxQuestion(sb.ToString(), "Apply suggestions?") != ButtonResult.Yes) return;
+ PopulateSuggestions(false);
+ }
- foreach (var suggestion in suggestions)
- {
- suggestion.Execute();
- }
+ public async void ApplySuggestionClicked(Suggestion suggestion)
+ {
+ if (!IsFileLoaded || suggestion is null || suggestion.IsInformativeOnly) return;
- PopulateSuggestions(false);
- }
+ if (await this.MessageBoxQuestion($"Are you sure you want to apply the following suggestion?:\n\n{suggestion.ConfirmationMessage}", "Apply the suggestion?") != ButtonResult.Yes) return;
- public async void ApplySuggestionClicked(Suggestion suggestion)
+ if (suggestion.Execute())
{
- if (!IsFileLoaded || suggestion is null || suggestion.IsInformativeOnly) return;
-
- if (await this.MessageBoxQuestion($"Are you sure you want to apply the following suggestion?:\n\n{suggestion.ConfirmationMessage}", "Apply the suggestion?") != ButtonResult.Yes) return;
-
- suggestion.Execute();
- PopulateSuggestions(false);
+ CanSave = true;
+ ResetDataContext();
+ ForceUpdateActualLayer();
}
+ PopulateSuggestions(false);
+ }
+
+ public async void ConfigureSuggestionsClicked()
+ {
+ if (!IsFileLoaded || Suggestions.Length == 0) return;
+ var window = new SuggestionSettingsWindow();
+ await window.ShowDialog(this);
+ PopulateSuggestions(false);
+ }
- #endregion
+ public async void ConfigureSuggestionClicked(Suggestion suggestion)
+ {
+ if (!IsFileLoaded || Suggestions.Length == 0) return;
+ var window = new SuggestionSettingsWindow(suggestion);
+ await window.ShowDialog(this);
+ PopulateSuggestions(false);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 7774735..4a79672 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="clr-namespace:UVtools.WPF.Controls"
xmlns:uvtava="clr-namespace:UVtools.AvaloniaControls;assembly=UVtools.AvaloniaControls"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="600"
x:Class="UVtools.WPF.MainWindow"
Title="UVtools"
@@ -16,324 +17,222 @@
<DockPanel Grid.Row="0" Grid.Column="0" IsEnabled="{Binding IsGUIEnabled}">
<Menu DockPanel.Dock="Top">
<MenuItem Name="MainMenu.File" Header="_File">
- <MenuItem
- Name="MainMenu.File.Open"
- Header="_Open"
- HotKey="Ctrl+O" InputGesture="Ctrl+O"
- Command="{Binding MenuFileOpenClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\file-import-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem Name="MainMenu.File.OpenNewWindow" Header="Open in _new window" HotKey="Ctrl+Shift+O" InputGesture="Ctrl+Shift+O" Command="{Binding MenuFileOpenNewWindowClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\file-import-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem
- Name="MainMenu.File.OpenRecent"
- Header="Open recent"
- ToolTip.ShowDelay="2000"
- ToolTip.Tip="On a file:
+ <MenuItem Name="MainMenu.File.Open"
+ Header="_Open"
+ HotKey="Ctrl+O" InputGesture="Ctrl+O"
+ Command="{Binding MenuFileOpenClicked}"
+ i:MenuItem.Icon="fas fa-file-import"/>
+
+ <MenuItem Name="MainMenu.File.OpenNewWindow"
+ Header="Open in _new window"
+ HotKey="Ctrl+Shift+O" InputGesture="Ctrl+Shift+O"
+ Command="{Binding MenuFileOpenNewWindowClicked}"
+ i:MenuItem.Icon="fas fa-file-import"/>
+
+ <MenuItem Name="MainMenu.File.OpenRecent"
+ Header="Open recent"
+ ToolTip.ShowDelay="2000"
+ ToolTip.Tip="On a file:
&#x0a;Shift + Click: Open file in a new window
&#x0a;Shift + Ctrl + Click: Remove file from list
&#x0a;Ctrl + Click: Purge non-existing files"
- Items="{Binding MenuFileOpenRecentItems}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\file-import-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem Name="MainMenu.File.OpenInPartialMode" Header="Open in partial mode" Command="{Binding MenuFileOpenInPartialModeClicked}"
+ Items="{Binding MenuFileOpenRecentItems}"
+ i:MenuItem.Icon="fas fa-file-import"/>
+
+ <MenuItem Name="MainMenu.File.OpenInPartialMode"
+ Header="Open in partial mode"
+ Command="{Binding MenuFileOpenInPartialModeClicked}"
ToolTip.Tip="Open a file only to see and/or edit properties.
-&#x0a;Layer images won't be loaded and most tools won't run in this mode.">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\file-import-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem
- Name="MainMenu.File.Reload"
- Header="_Reload"
- HotKey="Ctrl+F5" InputGesture="Ctrl+F5"
- IsEnabled="{Binding IsFileLoaded}"
- Command="{Binding ReloadFile}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\file-refresh-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem
- Name="MainMenu.File.Save"
- Header="_Save"
- HotKey="Ctrl+S" InputGesture="Ctrl+S"
- IsEnabled="{Binding CanSave}"
- Command="{Binding MenuFileSaveClicked}"
- >
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\save-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem
- Name="MainMenu.File.SaveAs"
- Header="Save _as"
- HotKey="Ctrl+Shift+S" InputGesture="Ctrl+Shift+S"
- IsEnabled="{Binding IsFileLoaded}"
- Command="{Binding MenuFileSaveAsClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\save-as-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem
- Name="MainMenu.File.SendTo"
- Header="Send to"
- IsEnabled="{Binding IsFileLoaded}"
- Items="{Binding MenuFileSendToItems}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\share-square-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
- <MenuItem
- Name="MainMenu.File.Close"
- Header="_Close"
- HotKey="Ctrl+W" InputGesture="Ctrl+W"
- IsEnabled="{Binding IsFileLoaded}"
- Command="{Binding OnMenuFileCloseFile}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\file-close-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+&#x0a;Layer images won't be loaded and most tools won't run in this mode."
+ i:MenuItem.Icon="fas fa-file-import"/>
+
+ <MenuItem Name="MainMenu.File.Reload"
+ Header="_Reload"
+ HotKey="Ctrl+F5" InputGesture="Ctrl+F5"
+ IsEnabled="{Binding IsFileLoaded}"
+ Command="{Binding ReloadFile}"
+ i:MenuItem.Icon="mdi-file-restore"/>
+
+ <MenuItem Name="MainMenu.File.Save"
+ Header="_Save"
+ HotKey="Ctrl+S" InputGesture="Ctrl+S"
+ IsEnabled="{Binding CanSave}"
+ Command="{Binding MenuFileSaveClicked}"
+ i:MenuItem.Icon="fas fa-save"/>
+
+ <MenuItem Name="MainMenu.File.SaveAs"
+ Header="Save _as"
+ HotKey="Ctrl+Shift+S" InputGesture="Ctrl+Shift+S"
+ IsEnabled="{Binding IsFileLoaded}"
+ Command="{Binding MenuFileSaveAsClicked}"
+ i:MenuItem.Icon="fas fa-save"/>
+
+ <MenuItem Name="MainMenu.File.SendTo"
+ Header="Send to"
+ IsEnabled="{Binding IsFileLoaded}"
+ Items="{Binding MenuFileSendToItems}"
+ i:MenuItem.Icon="fas fa-share-square"/>
+
+ <MenuItem Name="MainMenu.File.Close"
+ Header="_Close"
+ HotKey="Ctrl+W" InputGesture="Ctrl+W"
+ IsEnabled="{Binding IsFileLoaded}"
+ Command="{Binding OnMenuFileCloseFile}"
+ i:MenuItem.Icon="fas fa-sign-out-alt"/>
<Separator/>
- <MenuItem
- Header="I _printed this file" HotKey="Ctrl+P" InputGesture="Ctrl+P"
- IsEnabled="{Binding IsFileLoaded}"
- Command="{Binding IPrintedThisFile}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\flask-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
-
- <MenuItem Name="MainMenu.File.OpenContainingFileFolder" Header="Open containing fo_lder" HotKey="Ctrl+Shift+L" InputGesture="Ctrl+Shift+L" IsEnabled="{Binding IsFileLoaded}" Command="{Binding MenuFileOpenContainingFolderClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\folder-open-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
-
- <MenuItem
- Name="MainMenu.File.Extract"
- Header="_Extract file contents" HotKey="Ctrl+E" InputGesture="Ctrl+E"
- IsEnabled="{Binding IsFileLoaded}"
- Command="{Binding ExtractFile}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\extract-object-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="I _printed this file" HotKey="Ctrl+P" InputGesture="Ctrl+P"
+ IsEnabled="{Binding IsFileLoaded}"
+ Command="{Binding IPrintedThisFile}"
+ i:MenuItem.Icon="fas fa-flask"/>
+
+ <MenuItem Name="MainMenu.File.OpenContainingFileFolder"
+ Header="Open containing fo_lder"
+ HotKey="Ctrl+Shift+L" InputGesture="Ctrl+Shift+L"
+ IsEnabled="{Binding IsFileLoaded}"
+ Command="{Binding MenuFileOpenContainingFolderClicked}"
+ i:MenuItem.Icon="fas fa-folder-open"/>
+
+ <MenuItem Name="MainMenu.File.Extract"
+ Header="_Extract file contents" HotKey="Ctrl+E" InputGesture="Ctrl+E"
+ IsEnabled="{Binding IsFileLoaded}"
+ Command="{Binding ExtractFile}"
+ i:MenuItem.Icon="fas fa-box-open"/>
<MenuItem Name="MainMenu.File.Terminal"
HotKey="Ctrl+Shift+T" InputGesture="Ctrl+Shift+T"
Header="_Terminal"
Command="{Binding OpenTerminal}"
- IsEnabled="{Binding IsFileLoaded}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\terminal-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ IsEnabled="{Binding IsFileLoaded}"
+ i:MenuItem.Icon="fas fa-terminal"/>
- <MenuItem
- Name="MainMenu.File.Convert"
- Header="_Convert to"
- IsEnabled="{Binding IsFileLoaded}"
- IsVisible="{Binding MenuFileConvertItems, Converter={x:Static ObjectConverters.IsNotNull}}"
- Items="{Binding MenuFileConvertItems}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\convert-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Name="MainMenu.File.Convert"
+ Header="_Convert to"
+ IsEnabled="{Binding IsFileLoaded}"
+ IsVisible="{Binding MenuFileConvertItems, Converter={x:Static ObjectConverters.IsNotNull}}"
+ Items="{Binding MenuFileConvertItems}"
+ i:MenuItem.Icon="fas fa-exchange-alt"/>
<Separator/>
- <MenuItem
- Name="MainMenu.File.Fullscreen"
- Header="_Fullscreen"
- InputGesture="F11" HotKey="F11"
- Command="{Binding OnMenuFileFullscreen}"
- >
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\expand-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Name="MainMenu.File.Fullscreen"
+ Header="_Fullscreen"
+ InputGesture="F11" HotKey="F11"
+ Command="{Binding OnMenuFileFullscreen}"
+ i:MenuItem.Icon="fas fa-window-maximize"/>
- <MenuItem Name="MainMenu.File.Settings" Header="_Settings" InputGesture="F12" HotKey="F12" Command="{Binding MenuFileSettingsClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\settings-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Name="MainMenu.File.Settings"
+ Header="_Settings"
+ InputGesture="F12" HotKey="F12"
+ Command="{Binding MenuFileSettingsClicked}"
+ i:MenuItem.Icon="fas fa-cog"/>
<Separator/>
- <MenuItem
- Name="MainMenu.File.Exit"
- Header="_Exit"
- InputGesture="Alt+F4"
- Command="{Binding Close}"
- >
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\exit-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Name="MainMenu.File.Exit"
+ Header="_Exit"
+ InputGesture="Alt+F4"
+ Command="{Binding Close}"
+ i:MenuItem.Icon="fas fa-door-open"/>
</MenuItem>
- <MenuItem
- Header="_Tools"
- IsVisible="{Binding IsFileLoaded}"
- IsEnabled="{Binding IsFileLoaded}"
- Items="{Binding MenuTools}">
- </MenuItem>
+ <MenuItem Header="_Tools"
+ IsVisible="{Binding IsFileLoaded}"
+ IsEnabled="{Binding IsFileLoaded}"
+ Items="{Binding MenuTools}">
+ </MenuItem>
- <MenuItem
- Header="_Calibration"
- IsVisible="{Binding IsFileLoaded}"
- IsEnabled="{Binding IsFileLoaded}"
- Items="{Binding MenuCalibration}">
+ <MenuItem Header="_Calibration"
+ IsVisible="{Binding IsFileLoaded}"
+ IsEnabled="{Binding IsFileLoaded}"
+ Items="{Binding MenuCalibration}">
</MenuItem>
<MenuItem Header="_Help">
- <MenuItem
- Header="_About"
- InputGesture="F1" HotKey="F1"
- Command="{Binding MenuHelpAboutClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\button-info-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_About"
+ InputGesture="F1" HotKey="F1"
+ Command="{Binding MenuHelpAboutClicked}"
+ i:MenuItem.Icon="fas fa-info-circle"/>
- <MenuItem
- Header="_Website"
- InputGesture="Ctrl + F1" HotKey="Ctrl + F1"
- Command="{Binding OpenHomePage}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\internet-explorer-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
-
- <MenuItem
- Header="Wi_ki &amp; tutorials"
- Command="{Binding OpenWebsite}"
- CommandParameter="https://github.com/sn4k3/UVtools/wiki">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\wikipedia-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
-
- <MenuItem
- Header="_Facebook group"
- Command="{Binding OpenWebsite}"
- CommandParameter="https://www.facebook.com/groups/uvtools">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\facebook-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Website"
+ InputGesture="Ctrl + F1" HotKey="Ctrl + F1"
+ Command="{Binding OpenHomePage}"
+ i:MenuItem.Icon="fab fa-edge"/>
+
+ <MenuItem Header="Wi_ki &amp; tutorials"
+ Command="{Binding OpenWebsite}"
+ CommandParameter="https://github.com/sn4k3/UVtools/wiki"
+ i:MenuItem.Icon="fab fa-wikipedia-w"/>
+
+ <MenuItem Header="_Facebook group"
+ Command="{Binding OpenWebsite}"
+ CommandParameter="https://www.facebook.com/groups/uvtools"
+ i:MenuItem.Icon="fab fa-facebook"/>
- <MenuItem
- Header="_Donate"
- Command="{Binding OpenDonateWebsite}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\donate-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Donate"
+ Command="{Binding OpenDonateWebsite}"
+ i:MenuItem.Icon="fas fa-donate"/>
- <MenuItem
- Header="_Sponsor"
- Command="{Binding OpenWebsite}"
- CommandParameter="https://github.com/sponsors/sn4k3">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\heart-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Sponsor"
+ Command="{Binding OpenWebsite}"
+ CommandParameter="https://github.com/sponsors/sn4k3"
+ i:MenuItem.Icon="fas fa-heart"/>
<Separator/>
- <MenuItem
- Header="_Material manager"
- HotKey="F10"
- InputGesture="F10"
- Command="{Binding MenuHelpMaterialManagerClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\flask-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Material manager"
+ HotKey="F10"
+ InputGesture="F10"
+ Command="{Binding MenuHelpMaterialManagerClicked}"
+ i:MenuItem.Icon="fas fa-flask"/>
- <MenuItem
- Header="_Install profiles into PrusaSlicer"
- Command="{Binding MenuHelpInstallProfilesClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\CNCMachine-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Install profiles into PrusaSlicer"
+ Command="{Binding MenuHelpInstallProfilesClicked}"
+ i:MenuItem.Icon="fas fa-list"/>
<Separator/>
- <MenuItem
- Header="_Benchmark"
- Command="{Binding MenuHelpBenchmarkClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\microchip-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Benchmark"
+ Command="{Binding MenuHelpBenchmarkClicked}"
+ i:MenuItem.Icon="fas fa-microchip"/>
<Separator/>
- <MenuItem
- Header="_Open settings folder"
- Command="{Binding MenuHelpOpenSettingsFolderClicked}">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\open-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Open settings folder"
+ Command="{Binding MenuHelpOpenSettingsFolderClicked}"
+ i:MenuItem.Icon="fas fa-folder-open"/>
<Separator/>
- <MenuItem
- Header="_Report a issue"
- Command="{Binding OpenWebsite}"
- CommandParameter="https://github.com/sn4k3/UVtools/issues/new?assignees=sn4k3&amp;labels=&amp;template=bug_report.md&amp;title=%5BBUG%5D+">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\bug-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="_Report a issue"
+ Command="{Binding OpenWebsite}"
+ CommandParameter="https://github.com/sn4k3/UVtools/issues/new?assignees=sn4k3&amp;labels=&amp;template=bug_report.md&amp;title=%5BBUG%5D+"
+ i:MenuItem.Icon="fas fa-bug"/>
- <MenuItem
- Header="Ask a _question"
- Command="{Binding OpenWebsite}"
- CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/q-a">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\question-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="Ask a _question"
+ Command="{Binding OpenWebsite}"
+ CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/q-a"
+ i:MenuItem.Icon="fas fa-question"/>
- <MenuItem
- Header="Suggest an improvement or new features"
- Command="{Binding OpenWebsite}"
- CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/ideas">
- <MenuItem.Icon>
- <Image Source="\Assets\Icons\lightbulb-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Header="Suggest an improvement or new features"
+ Command="{Binding OpenWebsite}"
+ CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/ideas"
+ i:MenuItem.Icon="fas fa-lightbulb"/>
</MenuItem>
- <MenuItem
- Background="LimeGreen"
- IsVisible="{Binding VersionChecker.HaveNewVersion}"
- Header="{Binding VersionChecker.VersionAnnouncementText}"
- Command="{Binding MenuNewVersionClicked}"
- >
+ <MenuItem Background="LimeGreen"
+ IsVisible="{Binding VersionChecker.HaveNewVersion}"
+ Header="{Binding VersionChecker.VersionAnnouncementText}"
+ Command="{Binding MenuNewVersionClicked}">
</MenuItem>
</Menu>
- <Border Padding="5" DockPanel.Dock="Bottom" Background="WhiteSmoke" IsVisible="{Binding IsFileLoaded}">
+ <Border Padding="5" DockPanel.Dock="Bottom" IsVisible="{Binding IsFileLoaded}">
<WrapPanel
Orientation="Horizontal"
VerticalAlignment="Center">
@@ -393,17 +292,21 @@
</Border>
<TabControl
- Background="WhiteSmoke"
DockPanel.Dock="Left"
Width="400"
SelectedItem="{Binding SelectedTabItem}">
- <TabItem
+ <TabControl.Styles>
+ <Style Selector="TabItem">
+ <Setter Property="FontSize" Value="32"/>
+ </Style>
+ </TabControl.Styles>
+ <TabItem
Name="TabInformation"
ToolTip.Tip="Information"
IsEnabled="{Binding IsFileLoaded}">
<TabItem.Header>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/info-circle-32x32.png" Width="32"/>
+ <i:Icon Value="fas fa-info-circle"/>
<!--<TextBlock Margin="5,0,0,0">Information</TextBlock>!-->
</StackPanel>
</TabItem.Header>
@@ -425,9 +328,8 @@
Spacing="5"
VerticalAlignment="Center">
<Button IsEnabled="{Binding ThumbnailCanGoPrevious}"
- Command="{Binding ThumbnailGoPrevious}">
- <Image Source="/Assets/Icons/back-16x16.png" Width="16"/>
- </Button>
+ i:Attached.Icon="fas fa-caret-left"
+ Command="{Binding ThumbnailGoPrevious}"/>
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
@@ -439,9 +341,8 @@
</TextBlock>
<Button IsEnabled="{Binding ThumbnailCanGoNext}"
- Command="{Binding ThumbnailGoNext}">
- <Image Source="/Assets/Icons/next-16x16.png" Width="16"/>
- </Button>
+ i:Attached.Icon="fas fa-caret-right"
+ Command="{Binding ThumbnailGoNext}"/>
</StackPanel>
@@ -456,15 +357,13 @@
<Button IsEnabled="{Binding VisibleThumbnailIndex}"
ToolTip.Tip="Replace the current preview image"
- Command="{Binding OnClickThumbnailImport}">
- <Image Source="/Assets/Icons/photo-16x16.png" />
- </Button>
+ i:Attached.Icon="fas fa-file-image"
+ Command="{Binding OnClickThumbnailImport}"/>
<Button IsEnabled="{Binding VisibleThumbnailIndex}"
ToolTip.Tip="Save thumbnail image to a file"
- Command="{Binding OnClickThumbnailSave}">
- <Image Source="/Assets/Icons/save-16x16.png" />
- </Button>
+ i:Attached.Icon="fas fa-save"
+ Command="{Binding OnClickThumbnailSave}"/>
</StackPanel>
@@ -474,7 +373,7 @@
Stretch="Uniform"
Source="{Binding VisibleThumbnailImage}"/>
- <GridSplitter Grid.Row="2" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" Background="White" Height="5"/>
+ <GridSplitter Grid.Row="2" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" Height="5"/>
<!-- Properties -->
<StackPanel
@@ -504,52 +403,44 @@
HorizontalAlignment="Right"
VerticalAlignment="Center">
- <Button
+ <uc:ButtonWithIcon
Name="PropertiesSaveButton"
IsEnabled="{Binding SlicerFile.CreatedThumbnailsCount}"
ToolTip.Tip="Save properties to a file or clipboard"
+ Icon="fas fa-save"
+ Spacing="3"
+ Text="⮟"
Command="{Binding OpenContextMenu}"
CommandParameter="PropertiesSave">
- <Button.ContextMenu>
+ <uc:ButtonWithIcon.ContextMenu>
<ContextMenu Name="PropertiesSaveContextMenu" PlacementMode="Bottom">
<MenuItem
Command="{Binding OnClickPropertiesSaveFile}"
- Header="To File">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/file-image-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ Header="To File"
+ i:MenuItem.Icon="far fa-save"/>
<MenuItem
Command="{Binding OnClickPropertiesSaveClipboard}"
- Header="To Clipboard">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/clipboard-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ Header="To Clipboard"
+ i:MenuItem.Icon="far fa-clipboard"/>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/save-16x16.png" />
- <TextBlock Text=" ⮟"/>
- </StackPanel>
- </Button>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
</StackPanel>
- <DataGrid
- IsVisible="{Binding SlicerProperties.Count}"
- Name="PropertiesGrid"
- Grid.Row="4"
- CanUserReorderColumns="True"
- CanUserResizeColumns="True"
- CanUserSortColumns="True"
- GridLinesVisibility="Horizontal"
- IsReadOnly="True"
- SelectionMode="Extended"
- ClipboardCopyMode="IncludeHeader"
- Items="{Binding SlicerProperties}">
+ <DataGrid IsVisible="{Binding SlicerProperties.Count}"
+ Name="PropertiesGrid"
+ Grid.Row="4"
+ CanUserReorderColumns="True"
+ CanUserResizeColumns="True"
+ CanUserSortColumns="True"
+ GridLinesVisibility="Horizontal"
+ IsReadOnly="True"
+ SelectionMode="Extended"
+ ClipboardCopyMode="IncludeHeader"
+ Items="{Binding SlicerProperties}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Name}"
@@ -564,37 +455,36 @@
</DataGrid>
- <GridSplitter Grid.Row="5" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" Background="White" Height="5"/>
+ <GridSplitter Grid.Row="5" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext"/>
+
+ <TextBlock Grid.Row="5"
+ Text="Layer data"
+ ToolTip.Tip="Shows the properties for the current selected layer"
+ FontWeight="Bold"
+ TextAlignment="Center"/>
<!-- Layer data -->
- <Grid Grid.Row="6"
- RowDefinitions="Auto,5,*" ColumnDefinitions="*">
- <TextBlock Grid.Row="0"
- Text="Layer data"
- ToolTip.Tip="Shows the properties for the current selected layer"
- FontWeight="Bold"
- TextAlignment="Center"/>
- <DataGrid Grid.Row="2"
- IsVisible="{Binding IsFileLoaded}"
- Name="CurrentLayerGrid"
- CanUserReorderColumns="False"
- CanUserResizeColumns="False"
- CanUserSortColumns="False"
- GridLinesVisibility="Horizontal"
- IsReadOnly="True"
- ClipboardCopyMode="IncludeHeader"
- Items="{Binding CurrentLayerProperties}">
- <DataGrid.Columns>
- <DataGridTextColumn Header="Name"
- Binding="{Binding Description}"
- Width="Auto" />
- <DataGridTextColumn Header="Value"
- Binding="{Binding Value}"
- Width="Auto" />
- </DataGrid.Columns>
-
- </DataGrid>
- </Grid>
+
+ <DataGrid Grid.Row="6"
+ IsVisible="{Binding IsFileLoaded}"
+ Name="CurrentLayerGrid"
+ CanUserReorderColumns="False"
+ CanUserResizeColumns="False"
+ CanUserSortColumns="False"
+ GridLinesVisibility="Horizontal"
+ IsReadOnly="True"
+ ClipboardCopyMode="IncludeHeader"
+ Items="{Binding CurrentLayerProperties}">
+ <DataGrid.Columns>
+ <DataGridTextColumn Header="Name"
+ Binding="{Binding Description}"
+ Width="Auto" />
+ <DataGridTextColumn Header="Value"
+ Binding="{Binding Value}"
+ Width="Auto" />
+ </DataGrid.Columns>
+
+ </DataGrid>
@@ -609,7 +499,7 @@
IsEnabled="{Binding HaveGCode}">
<TabItem.Header>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/code-32x32.png" Width="32"/>
+ <i:Icon Value="fas fa-code"/>
<!--<TextBlock
IsVisible="{Binding $parent[TabItem].IsSelected}"
VerticalAlignment="Center"
@@ -640,45 +530,36 @@
ToolTip.Tip="Enable this to directly edit and use custom gcode.
&#x0a;While this is active, UVtools won't update/generate the gcode, meaning any future change won't be replicated to gcode, unless you press the 'Refresh' button.
&#x0a;To save the file with your custom gcode this setting must remain active while saving the file or else it will be re-generated.
-&#x0a;Use with caution and only if you know what you are doing!">
- <Image Source="/Assets/Icons/pencil-alt-16x16.png"/>
- </ToggleButton>
+&#x0a;Use with caution and only if you know what you are doing!"
+ i:Attached.Icon="far fa-edit"/>
<Button
ToolTip.Tip="Rebuild GCode with current settings"
- Command="{Binding OnClickRebuildGcode}">
- <Image Source="/Assets/Icons/refresh-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-sync-alt"
+ Command="{Binding OnClickRebuildGcode}"/>
- <Button Name="GcodeSaveButton"
+ <uc:ButtonWithIcon Name="GcodeSaveButton"
ToolTip.Tip="Save gcode to a file or clipboard"
+ Icon="fas fa-save"
+ Spacing="3"
+ Text="⮟"
Command="{Binding OpenContextMenu}"
CommandParameter="GcodeSave">
- <Button.ContextMenu>
+ <uc:ButtonWithIcon.ContextMenu>
<ContextMenu Name="GcodeSaveContextMenu" PlacementMode="Bottom">
<MenuItem
Command="{Binding OnClickGCodeSaveFile}"
- Header="To File">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/file-image-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ Header="To File"
+ i:MenuItem.Icon="far fa-save"/>
<MenuItem
Command="{Binding OnClickGCodeSaveClipboard}"
- Header="To Clipboard">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/clipboard-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ Header="To Clipboard"
+ i:MenuItem.Icon="far fa-clipboard"/>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/save-16x16.png" />
- <TextBlock Text=" ⮟"/>
- </StackPanel>
- </Button>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
</StackPanel>
@@ -701,7 +582,7 @@
IsEnabled="{Binding IsFileLoaded}" >
<TabItem.Header>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/warning-32x32.png" Width="32"/>
+ <i:Icon Value="fas fa-radiation-alt"/>
<!--<TextBlock Margin="5,0,0,0">Issues</TextBlock>!-->
</StackPanel>
</TabItem.Header>
@@ -714,16 +595,13 @@
Orientation="Horizontal"
Spacing="2"
VerticalAlignment="Center">
- <RepeatButton
- VerticalAlignment="Stretch"
- HotKey="Ctrl + Shift + Down"
- ToolTip.Tip="Go to the previous issue [Ctrl + Shift + Down]"
- IsEnabled="{Binding IssueCanGoPrevious}"
- Command="{Binding IssueGoPrevious}"
- Interval="100"
- >
- <Image Source="/Assets/Icons/back-16x16.png" Width="16"/>
- </RepeatButton>
+ <RepeatButton VerticalAlignment="Stretch"
+ HotKey="Ctrl + Shift + Down"
+ ToolTip.Tip="Go to the previous issue [Ctrl + Shift + Down]"
+ IsEnabled="{Binding IssueCanGoPrevious}"
+ Command="{Binding IssueGoPrevious}"
+ Interval="100"
+ i:Attached.Icon="fas fa-caret-left"/>
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
@@ -734,40 +612,30 @@
</TextBlock.Text>
</TextBlock>
- <RepeatButton
- VerticalAlignment="Stretch"
- HotKey="Ctrl + Shift + Up"
- ToolTip.Tip="Go to the next issue [Ctrl + Shift + Up]"
- IsEnabled="{Binding IssueCanGoNext}"
- Command="{Binding IssueGoNext}"
- Interval="100"
- >
- <Image Source="/Assets/Icons/next-16x16.png" Width="16"/>
- </RepeatButton>
+ <RepeatButton VerticalAlignment="Stretch"
+ HotKey="Ctrl + Shift + Up"
+ ToolTip.Tip="Go to the next issue [Ctrl + Shift + Up]"
+ IsEnabled="{Binding IssueCanGoNext}"
+ Command="{Binding IssueGoNext}"
+ Interval="100"
+ i:Attached.Icon="fas fa-caret-right"/>
- <Button
- VerticalAlignment="Stretch"
- ToolTip.Tip="Hides and ignores the selected issues, they won't be re-detected.
+ <Button VerticalAlignment="Stretch"
+ ToolTip.Tip="Hides and ignores the selected issues, they won't be re-detected.
&#x0a;ALT + Click to re-enable the ignored issues."
- IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding OnClickIssueIgnore}">
- <Image Source="/Assets/Icons/eye-slash-16x16.png" />
- </Button>
+ IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
+ i:Attached.Icon="fas fa-eye-slash"
+ Command="{Binding OnClickIssueIgnore}"/>
- <Button
- VerticalAlignment="Stretch"
- ToolTip.Tip="Remove the selected issue(s) when possible.
+ <Button VerticalAlignment="Stretch"
+ ToolTip.Tip="Remove the selected issue(s) when possible.
&#x0a;Islands: All pixels are removed (turn black).
&#x0a;ResinTrap: All areas are filled with white pixels.
&#x0a;SuctionCup: Drills a vertical vent hole.
&#x0a;EmptyLayers: Layers are removed."
- IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding OnClickIssueRemove}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/trash-16x16.png" />
- <!--<TextBlock VerticalAlignment="Center" Text="Remove"/>!-->
- </StackPanel>
- </Button>
+ IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
+ i:Attached.Icon="fas fa-trash-alt"
+ Command="{Binding OnClickIssueRemove}"/>
</StackPanel>
@@ -782,20 +650,18 @@
IsEnabled="{Binding IsFileLoaded}"
ToolTip.Tip="Attempt to repair issues"
VerticalAlignment="Stretch"
- Command="{Binding OnClickRepairIssues}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/toolbox-16x16.png" />
- <!--<TextBlock VerticalAlignment="Center" Text="Repair"/>!-->
- </StackPanel>
- </Button>
+ i:Attached.Icon="fas fa-toolbox"
+ Command="{Binding OnClickRepairIssues}"/>
- <Button
- VerticalAlignment="Stretch"
- ToolTip.Tip="Compute Issues.
+ <uc:ButtonWithIcon VerticalAlignment="Stretch"
+ ToolTip.Tip="Compute Issues.
&#x0a;Right click to access settings."
- Command="{Binding OnClickDetectIssues}">
+ Icon="fas fa-sync-alt"
+ Spacing="5"
+ Text="Detect ⮟"
+ Command="{Binding OnClickDetectIssues}">
- <Button.ContextMenu>
+ <uc:ButtonWithIcon.ContextMenu>
<ContextMenu PlacementMode="Bottom">
<CheckBox
IsChecked="{Binding Settings.Issues.ComputeIslands}"
@@ -852,14 +718,8 @@
IsChecked="{Binding Settings.Issues.ComputeEmptyLayers}"
Content="Empty layers"/>
</ContextMenu>
- </Button.ContextMenu>
-
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/refresh-16x16.png" />
- <TextBlock Text="Detect ⮟"/>
- </StackPanel>
-
- </Button>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
</StackPanel>
@@ -906,40 +766,46 @@
IsEnabled="{Binding IsFileLoaded}" >
<TabItem.Header>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/shield-virus-32x32.png" Width="32"/>
+ <i:Icon Value="fas fa-shield-virus"/>
<!--<TextBlock Margin="5,0,0,0">Suggestions</TextBlock>!-->
</StackPanel>
</TabItem.Header>
- <Grid RowDefinitions="Auto,2*,Auto,*" ColumnDefinitions="*">
+ <Grid ColumnDefinitions="*">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="2*" MinHeight="200" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" MinHeight="200" />
+ </Grid.RowDefinitions>
<Grid Grid.Row="0" RowDefinitions="Auto" ColumnDefinitions="Auto,2,Auto,*">
- <Button Grid.Column="0"
- Command="{Binding #SuggestionsAvailableListBox.UnselectAll}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-unmarked-16x16.png"/>
- <TextBlock Text="Unselect all"/>
- </StackPanel>
- </Button>
-
- <Button Grid.Column="2"
- Command="{Binding #SuggestionsAvailableListBox.SelectAll}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-marked-16x16.png"/>
- <TextBlock Text="Select all"/>
- </StackPanel>
- </Button>
-
- <Button Grid.Column="3"
- HorizontalAlignment="Right"
- IsEnabled="{Binding #SuggestionsAvailableListBox.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding ApplySuggestionsClicked}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- <TextBlock Text="Apply"/>
- </StackPanel>
- </Button>
- </Grid>
+ <uc:ButtonWithIcon Grid.Column="0"
+ Text="Unselect all"
+ Spacing="5"
+ Icon="far fa-square"
+ Command="{Binding #SuggestionsAvailableListBox.UnselectAll}"/>
+
+ <uc:ButtonWithIcon Grid.Column="2"
+ Text="Select all"
+ Spacing="5"
+ Icon="far fa-check-square"
+ Command="{Binding #SuggestionsAvailableListBox.SelectAll}"/>
+
+ <StackPanel Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="2">
+ <uc:ButtonWithIcon IsEnabled="{Binding #SuggestionsAvailableListBox.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
+ Icon="fas fa-check"
+ Spacing="5"
+ Text="Apply"
+ Command="{Binding ApplySuggestionsClicked}"/>
+
+ <Button VerticalAlignment="Stretch"
+ ToolTip.Tip="Configure suggestions"
+ Command="{Binding ConfigureSuggestionsClicked}"
+ i:Attached.Icon="fas fa-cog"/>
+ </StackPanel>
+
+ </Grid>
<ListBox
Grid.Row="1"
@@ -964,20 +830,19 @@
Command="{Binding $parent[uc:WindowEx].DataContext.ApplySuggestionClicked}"
CommandParameter="{Binding .}"
Header="Apply"
- IsVisible="{Binding !IsInformativeOnly}">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ IsVisible="{Binding !IsInformativeOnly}"
+ i:MenuItem.Icon="fas fa-check"/>
<MenuItem
Command="{Binding $parent[uc:WindowEx].DataContext.OpenWebsite}"
CommandParameter="{Binding InformationUrl}"
Header="More information (Web)"
- IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/info-circle-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}"
+ i:MenuItem.Icon="fas fa-info-circle"/>
+ <MenuItem
+ Command="{Binding $parent[uc:WindowEx].DataContext.ConfigureSuggestionClicked}"
+ CommandParameter="{Binding .}"
+ Header="Configure"
+ i:MenuItem.Icon="fas fa-cog"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
@@ -985,6 +850,8 @@
</ListBox.ItemTemplate>
</ListBox>
+ <GridSplitter Grid.Row="2" ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"/>
+
<TextBlock Grid.Row="2" Text="Applied suggestions:" HorizontalAlignment="Center" FontWeight="Bold"/>
<ListBox
@@ -1009,11 +876,13 @@
Command="{Binding $parent[uc:WindowEx].DataContext.OpenWebsite}"
CommandParameter="{Binding InformationUrl}"
Header="More information (Web)"
- IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/info-circle-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}"
+ i:MenuItem.Icon="fas fa-info-circle"/>
+ <MenuItem
+ Command="{Binding $parent[uc:WindowEx].DataContext.ConfigureSuggestionClicked}"
+ CommandParameter="{Binding .}"
+ Header="Configure"
+ i:MenuItem.Icon="fas fa-cog"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
@@ -1032,7 +901,7 @@
IsEnabled="{Binding IsPixelEditorActive}">
<TabItem.Header>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/pixel-32x32.png" Width="32"/>
+ <i:Icon Value="fas fa-drafting-compass"/>
<!--<TextBlock Margin="5,0,0,0">Pixel editor</TextBlock>!-->
</StackPanel>
</TabItem.Header>
@@ -1042,19 +911,21 @@
Padding="10,0"
SelectedIndex="{Binding SelectedPixelOperationTabIndex}">
<!-- Drawing !-->
+ <TabControl.Styles>
+ <Style Selector="TabItem">
+ <Setter Property="FontSize" Value="24"/>
+ </Style>
+ </TabControl.Styles>
<TabItem ToolTip.Tip="Drawing">
<TabItem.Header>
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/pencil-alt-24x24.png" Width="24"/>
- <TextBlock
- IsVisible="{Binding $parent[TabItem].IsSelected}"
- Margin="5,0,0,0"
- Text="Drawing" />
+ <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
+ <i:Icon Value="fas fa-paint-brush"/>
+ <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Drawing" />
</StackPanel>
</TabItem.Header>
<StackPanel Spacing="10">
- <Border Background="LightGray" BorderThickness="1" BorderBrush="Black">
+ <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black">
<TextBlock Padding="10" Text="Shift+Left click to add white pixels
&#x0a;Shift+Right click to remove pixels"/>
</Border>
@@ -1184,17 +1055,14 @@
<!-- Text !-->
<TabItem ToolTip.Tip="Text">
<TabItem.Header>
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/font-24x24.png" Width="24"/>
- <TextBlock
- IsVisible="{Binding $parent[TabItem].IsSelected}"
- Margin="5,0,0,0"
- Text="Text" />
+ <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
+ <i:Icon Value="fas fa-font"/>
+ <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Text" />
</StackPanel>
</TabItem.Header>
<StackPanel Spacing="10">
- <Border Background="LightGray" BorderThickness="1" BorderBrush="Black">
+ <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black">
<TextBlock Padding="10" Text="Shift+Left click to add text
&#x0a;Shift+Right click to remove text"/>
</Border>
@@ -1369,17 +1237,14 @@
<!-- Eraser !-->
<TabItem ToolTip.Tip="Eraser">
<TabItem.Header>
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/eraser-24x24.png" Width="24"/>
- <TextBlock
- IsVisible="{Binding $parent[TabItem].IsSelected}"
- Margin="5,0,0,0"
- Text="Eraser" />
+ <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
+ <i:Icon Value="fas fa-eraser"/>
+ <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Eraser" />
</StackPanel>
</TabItem.Header>
<StackPanel Spacing="10">
- <Border Background="LightGray" BorderThickness="1" BorderBrush="Black">
+ <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black">
<TextBlock Padding="10" Text="Shift+click over a white pixel to remove the linked area.
&#x0a;(Fill linked area with black)"/>
</Border>
@@ -1437,17 +1302,14 @@
ToolTip.Tip="Supports"
>
<TabItem.Header>
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/code-branch-24x24.png" Width="24"/>
- <TextBlock
- IsVisible="{Binding $parent[TabItem].IsSelected}"
- Margin="5,0,0,0"
- Text="Supports" />
+ <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
+ <i:Icon Value="fas fa-code-branch"/>
+ <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Supports" />
</StackPanel>
</TabItem.Header>
<StackPanel Spacing="10" >
- <Border Background="LightGray" BorderThickness="1" BorderBrush="Black">
+ <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black">
<TextBlock Padding="10" Text="Shift+click under a island to add primitive support.
&#x0a;Note: this operation can't be previewed."/>
</Border>
@@ -1514,17 +1376,14 @@
ToolTip.Tip="Drain holes"
>
<TabItem.Header>
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/ring-24x24.png" Width="24"/>
- <TextBlock
- IsVisible="{Binding $parent[TabItem].IsSelected}"
- Margin="5,0,0,0"
- Text="Drain holes" />
+ <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
+ <i:Icon Value="fas fa-ring"/>
+ <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Drain holes" />
</StackPanel>
</TabItem.Header>
<StackPanel Spacing="10">
- <Border Background="LightGray" BorderThickness="1" BorderBrush="Black">
+ <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black">
<TextBlock Padding="10" Text="Shift+click to add a vertical drain hole.
&#x0a;Note: this operation can't be previewed."/>
</Border>
@@ -1552,44 +1411,31 @@
<StackPanel
Grid.Row="2"
Orientation="Horizontal" Spacing="1">
- <Button IsEnabled="{Binding #DrawingsGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding OnClickDrawingRemove}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/trash-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="Remove"/>
- </StackPanel>
- </Button>
-
- <Button IsEnabled="{Binding Drawings.Count}"
- Command="{Binding OnClickDrawingClear}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="Clear"
- />
- </StackPanel>
- </Button>
+ <uc:ButtonWithIcon IsEnabled="{Binding #DrawingsGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
+ Icon="fas fa-trash-alt"
+ Spacing="5"
+ Text="Remove"
+ Command="{Binding OnClickDrawingRemove}"/>
+
+ <uc:ButtonWithIcon IsEnabled="{Binding Drawings.Count}"
+ Icon="fas fa-times"
+ Spacing="5"
+ Text="Clear"
+ Command="{Binding OnClickDrawingClear}"/>
</StackPanel>
<StackPanel
Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal"
- Spacing="10"
- >
+ Spacing="10" >
- <Button IsEnabled="{Binding Drawings.Count}"
- Command="{Binding DrawModifications}"
- CommandParameter="false">
- <StackPanel Orientation="Horizontal" Spacing="1">
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- <TextBlock Text="{Binding Drawings.Count, StringFormat=Apply \{0\} operations}"/>
- </StackPanel>
- </Button>
-
+ <uc:ButtonWithIcon IsEnabled="{Binding Drawings.Count}"
+ Icon="fas fa-check"
+ Spacing="5"
+ Text="{Binding Drawings.Count, StringFormat=Apply \{0\} operations}"
+ Command="{Binding DrawModifications}"
+ CommandParameter="false"/>
</StackPanel>
<DataGrid
@@ -1631,30 +1477,26 @@
ToolTip.Tip="Clipboard">
<TabItem.Header>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/clipboard-32x32.png" Width="32"/>
+ <i:Icon Value="fas fa-clipboard-list"/>
<!--<TextBlock Margin="5,0,0,0">Clipboard</TextBlock>!-->
- </StackPanel>
+ </StackPanel>
</TabItem.Header>
- <Grid RowDefinitions="Auto,*">
- <StackPanel Orientation="Horizontal" Spacing="2">
- <!--
- <Button
- IsEnabled="{Binding Clipboard.Items.Count}"
- Command="{Binding Clipboard.SetToOldest}"
- ToolTip.Tip="Go to first clip">
- <Image Source="/Assets/Icons/arrow-end-16x16.png" Width="16"/>
- </Button>
- !-->
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*" MinHeight="140"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*" MinHeight="180"/>
+ </Grid.RowDefinitions>
+ <StackPanel Orientation="Horizontal" Spacing="2">
- <Button
- IsEnabled="{Binding Clipboard.CanUndo}"
- Command="{Binding ClipboardUndo}"
- HotKey="Ctrl + Z"
- ToolTip.Tip="Undo [Ctrl + Z]
-&#x0a;Shift + Click to Undo and edit last operation [Ctrl + Shift + Z]">
- <Image Source="/Assets/Icons/undo-16x16.png" Width="16"/>
- </Button>
+ <Button IsEnabled="{Binding Clipboard.CanUndo}"
+ Command="{Binding ClipboardUndo}"
+ HotKey="Ctrl + Z"
+ ToolTip.Tip="Undo [Ctrl + Z]
+&#x0a;Shift + Click to Undo and edit last operation [Ctrl + Shift + Z]"
+ i:Attached.Icon="fas fa-undo-alt"/>
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
@@ -1670,29 +1512,16 @@
Command="{Binding ClipboardRedo}"
HotKey="Ctrl + Y"
ToolTip.Tip="Redo [Ctrl + Y]"
- >
- <Image Source="/Assets/Icons/redo-16x16.png" Width="16"/>
- </Button>
-
- <!--
- <Button
- IsEnabled="{Binding Clipboard.Items.Count}"
- Command="{Binding Clipboard.SetToNewest}"
- ToolTip.Tip="Go to first clip">
- <Image Source="/Assets/Icons/arrow-top-16x16.png" Width="16"/>
- </Button>
- !-->
-
+ i:Attached.Icon="fas fa-redo-alt"/>
+
</StackPanel>
- <StackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Right">
+ <StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="2" HorizontalAlignment="Right">
<Button
IsEnabled="{Binding Clipboard.Items.Count}"
Command="{Binding ClipboardClear}"
ToolTip.Tip="Clear all clips"
- >
- <Image Source="/Assets/Icons/delete-16x16.png" Width="16"/>
- </Button>
+ i:Attached.Icon="fas fa-times"/>
</StackPanel>
<ListBox
@@ -1702,74 +1531,70 @@
AutoScrollToSelectedItem="True"
SelectedIndex="{Binding Clipboard.CurrentIndex}"
Items="{Binding Clipboard.Items}" />
- </Grid>
- </TabItem>
-
- <TabItem
- Name="TabLog"
- ToolTip.Tip="Log">
- <TabItem.Header>
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/book-32x32.png" Width="32"/>
- <!--<TextBlock Margin="5,0,0,0">Log</TextBlock>!-->
- </StackPanel>
- </TabItem.Header>
-
- <StackPanel Orientation="Vertical">
-
- <Grid RowDefinitions="Auto"
- ColumnDefinitions="Auto,Auto,*">
- <Button Grid.Row="0" Grid.Column="0"
- Command="{Binding Logs.Clear}">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/trash-16x16.png" Width="16"/>
- <TextBlock Margin="5,0,0,0">Clear</TextBlock>
- </StackPanel>
- </Button>
+ <GridSplitter Grid.Row="2" ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"/>
- <CheckBox Grid.Row="0" Grid.Column="1"
- VerticalAlignment="Center"
- ToolTip.Tip="Shows extra information usefull to debug problems."
- IsChecked="{Binding IsVerbose}"
- Content="Verbose"
- Margin="5,0,0,0"/>
-
- <TextBlock Grid.Row="0" Grid.Column="2"
- HorizontalAlignment="Right"
- VerticalAlignment="Center"
- Text="{Binding Logs.Count, StringFormat=Operations: \{0\}}"/>
+ <Grid Grid.Row="3" RowDefinitions="Auto,*" ColumnDefinitions="*">
+ <Grid Grid.Row="0"
+ RowDefinitions="Auto"
+ ColumnDefinitions="Auto,*,Auto">
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="{Binding Logs.Count, StringFormat=Operations: {0}}"/>
+
+ <TextBlock Grid.Row="0" Grid.Column="1"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center"
+ FontWeight="Bold" Text="Logs"/>
+
+ <StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal"
+ Spacing="5"
+ HorizontalAlignment="Right">
+ <CheckBox VerticalAlignment="Center"
+ ToolTip.Tip="Shows extra information useful to debug problems."
+ IsChecked="{Binding IsVerbose}"
+ Content="Verbose"/>
+
+ <Button VerticalAlignment="Center"
+ Command="{Binding Logs.Clear}"
+ ToolTip.Tip="Clear all logs"
+ i:Attached.Icon="fas fa-times"/>
+ </StackPanel>
+
+ </Grid>
+
+ <DataGrid Grid.Row="1" Items="{Binding Logs}"
+ VerticalAlignment="Stretch"
+ CanUserReorderColumns="True"
+ CanUserResizeColumns="True"
+ CanUserSortColumns="True"
+ GridLinesVisibility="All"
+ IsReadOnly="True"
+ ClipboardCopyMode="IncludeHeader"
+ SelectionMode="Extended">
+ <DataGrid.Columns>
+ <DataGridTextColumn Header="#"
+ Binding="{Binding Index}"
+ Width="Auto" />
+ <DataGridTextColumn Header="Started"
+ Binding="{Binding StartTime}"
+ Width="Auto" />
+ <DataGridTextColumn Header="Time(s)"
+ Binding="{Binding ElapsedTime}"
+ Width="Auto" />
+ <DataGridTextColumn Header="Description"
+ Binding="{Binding Description}"
+ Width="Auto" />
+ </DataGrid.Columns>
+ </DataGrid>
</Grid>
- <DataGrid Items="{Binding Logs}"
- VerticalAlignment="Stretch"
- CanUserReorderColumns="True"
- CanUserResizeColumns="True"
- CanUserSortColumns="True"
- GridLinesVisibility="All"
- IsReadOnly="True"
- ClipboardCopyMode="IncludeHeader"
- SelectionMode="Extended">
- <DataGrid.Columns>
- <DataGridTextColumn Header="#"
- Binding="{Binding Index}"
- Width="Auto" />
- <DataGridTextColumn Header="Started"
- Binding="{Binding StartTime}"
- Width="Auto" />
- <DataGridTextColumn Header="Time(s)"
- Binding="{Binding ElapsedTime}"
- Width="Auto" />
- <DataGridTextColumn Header="Description"
- Binding="{Binding Description}"
- Width="Auto" />
- </DataGrid.Columns>
- </DataGrid>
+ </Grid>
- </StackPanel>
+
</TabItem>
- </TabControl>
+ </TabControl>
<Grid
IsEnabled="{Binding IsFileLoaded}"
@@ -1790,9 +1615,8 @@
Interval="100"
HorizontalAlignment="Stretch"
IsEnabled="{Binding CanGoUp}"
- Command="{Binding GoNextLayer}">
- <Image Width="16" Height="16" Source="/Assets/Icons/arrow-up-16x16.png"/>
- </RepeatButton>
+ Command="{Binding GoNextLayer}"
+ i:Attached.Icon="fas fa-angle-up"/>
<Grid
Name="LayerNavigationSliderGrid"
@@ -1804,11 +1628,8 @@
HorizontalAlignment="Stretch">
<Border
Name="Layer.Navigation.Tooltip.Border"
- VerticalAlignment="Top"
- BorderBrush="AliceBlue"
- BorderThickness="5"
- CornerRadius="5"
- >
+ Classes="LayerNavigationToolTip"
+ VerticalAlignment="Top">
<TextBlock Padding="2" Text="{Binding ActualLayerTooltip}"/>
</Border>
</Panel>
@@ -1820,9 +1641,8 @@
<Canvas
Grid.Column="1"
Margin="0,15"
- Background="WhiteSmoke"
- Name="Layer.Navigation.IssuesCanvas" Width="20"
- />
+ Classes="IssuesTrackerCanvas"
+ Name="Layer.Navigation.IssuesCanvas" Width="20"/>
<Slider
Grid.Column="2"
@@ -1848,27 +1668,21 @@
HorizontalAlignment="Stretch"
IsEnabled="{Binding CanGoDown}"
Command="{Binding GoPreviousLayer}"
- >
- <Image Width="16" Height="16" Source="/Assets/Icons/arrow-down-16x16.png"/>
- </RepeatButton>
+ i:Attached.Icon="fas fa-angle-down"/>
- <NumericUpDown
- Grid.Row="4"
- Margin="0,5"
- Minimum="0"
- Maximum="{Binding SliderMaximumValue}"
- Value="{Binding ActualLayer}" />
+ <NumericUpDown Grid.Row="4"
+ Margin="0,5"
+ Minimum="0"
+ Maximum="{Binding SliderMaximumValue}"
+ Value="{Binding ActualLayer}" />
<Grid Grid.Row="5" RowDefinitions="*" ColumnDefinitions="30,*,30">
- <Button
- Grid.Column="0"
- ToolTip.Tip="Navigate to first layer [Ctrl + Left]"
- HotKey="Ctrl + Left"
- IsEnabled="{Binding CanGoDown}"
- Command="{Binding GoFirstLayer}"
- >
- <Image Width="16" Height="16" Source="/Assets/Icons/arrow-end-16x16.png"/>
- </Button>
+ <Button Grid.Column="0"
+ ToolTip.Tip="Navigate to first layer [Ctrl + Left]"
+ HotKey="Ctrl + Left"
+ IsEnabled="{Binding CanGoDown}"
+ Command="{Binding GoFirstLayer}"
+ i:Attached.Icon="fas fa-angle-double-down"/>
<TextBlock
Grid.Column="1"
@@ -1877,25 +1691,13 @@
HorizontalAlignment="Center"
TextAlignment="Center"/>
- <!--
- <Button
- Grid.Column="1"
- Margin="5,0,5,0"
- ToolTip.Tip="Navigate to a selected layer [Ctrl + F]"
- HotKey="Ctrl + F"
- >
- <Image Source="/Assets/Icons/search-16x16.png"/>
- </Button>
- !-->
<Button
Grid.Column="2"
ToolTip.Tip="Navigate to last layer [Ctrl + Right]"
HotKey="Ctrl + Right"
IsEnabled="{Binding CanGoUp}"
Command="{Binding GoLastLayer}"
- >
- <Image Width="16" Height="16" Source="/Assets/Icons/arrow-top-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-angle-double-up"/>
</Grid>
<StackPanel Grid.Row="6"
@@ -1943,12 +1745,16 @@
IsEnabled="{Binding IsFileLoaded}"
ColumnDefinitions="*,Auto" RowDefinitions="Auto" Margin="5">
<WrapPanel HorizontalAlignment="Left" Grid.Row="0" Orientation="Horizontal">
- <ToggleButton
+ <uc:ToggleButtonWithIcon
IsChecked="{Binding ShowLayerImageRotated}"
HotKey="Ctrl + R"
VerticalAlignment="Stretch"
- ToolTip.Tip="Auto rotate layer preview image at 90º (This can slow down the layer preview) [CTRL+R]">
- <Button.ContextMenu>
+ ToolTip.Tip="Auto rotate layer preview image at 90º (This can slow down the layer preview) [CTRL+R]"
+ Margin="0,0,1,0"
+ Text="Rotate ⮟"
+ Spacing="5"
+ Icon="fas fa-sync-alt">
+ <uc:ToggleButtonWithIcon.ContextMenu>
<ContextMenu PlacementMode="Bottom">
<RadioButton
GroupName="ShowLayerImageRotateDirection"
@@ -1959,20 +1765,20 @@
IsChecked="{Binding ShowLayerImageRotateCCWDirection}"
Content="90º Counter-clockwise (CCW)"/>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/sync-16x16.png"/>
- <TextBlock Margin="5,0,5,0" Text="Rotate ⮟"/>
- </StackPanel>
- </ToggleButton>
+ </uc:ToggleButtonWithIcon.ContextMenu>
+ </uc:ToggleButtonWithIcon>
- <ToggleButton
+ <uc:ToggleButtonWithIcon
IsChecked="{Binding ShowLayerImageFlipped}"
HotKey="Ctrl + F"
VerticalAlignment="Stretch"
- ToolTip.Tip="Auto flip layer preview image (This can slow down the layer preview) [CTRL+F]">
- <Button.ContextMenu>
+ ToolTip.Tip="Auto flip layer preview image (This can slow down the layer preview) [CTRL+F]"
+ Margin="0,0,1,0"
+ Text="Flip ⮟"
+ Spacing="5"
+ Icon="mdi-flip-horizontal">
+ <uc:ToggleButtonWithIcon.ContextMenu>
<ContextMenu PlacementMode="Bottom">
<CheckBox
IsChecked="{Binding ShowLayerImageFlippedHorizontally}"
@@ -1981,77 +1787,62 @@
IsChecked="{Binding ShowLayerImageFlippedVertically}"
Content="Vertically"/>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/flip-16x16.png"/>
- <TextBlock Margin="5,0,5,0" Text="Flip ⮟"/>
- </StackPanel>
- </ToggleButton>
+ </uc:ToggleButtonWithIcon.ContextMenu>
+ </uc:ToggleButtonWithIcon>
- <ToggleButton
+ <uc:ToggleButtonWithIcon
IsChecked="{Binding ShowLayerImageDifference}"
ToolTip.Tip="Show layer differences where darker pixels were also present on previous layer and the white pixels the difference between previous and current layer."
VerticalAlignment="Stretch"
- Margin="1,0,0,0">
- <Button.ContextMenu>
+ Margin="0,0,1,0"
+ Text="Difference ⮟"
+ Spacing="5"
+ Icon="fas fa-layer-group">
+ <uc:ToggleButtonWithIcon.ContextMenu>
<ContextMenu PlacementMode="Bottom">
<CheckBox
Content="Show layer similarity instead of difference"
ToolTip.Tip="If enabled, it will recolor the current layer pixels in common with the previous and next layer"
IsChecked="{Binding Settings.LayerPreview.LayerDifferenceHighlightSimilarityInstead}"/>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/layers-16x16.png"/>
- <TextBlock Margin="5,0,5,0" Text="Difference ⮟"/>
- </StackPanel>
- </ToggleButton>
+ </uc:ToggleButtonWithIcon.ContextMenu>
+ </uc:ToggleButtonWithIcon>
- <ToggleButton
+ <uc:ToggleButtonWithIcon
IsChecked="{Binding ShowLayerImageIssues}"
ToolTip.Tip="Highlight Issues on current layer. Valid only if Issues are calculated."
VerticalAlignment="Stretch"
- Margin="1,0,0,0">
+ Margin="0,0,1,0"
+ Text="Issues ⮟"
+ Spacing="5"
+ Icon="fas fa-radiation-alt">
<Button.ContextMenu>
<ContextMenu PlacementMode="Bottom">
<CheckBox
ToolTip.Tip="Show crosshairs for selected issues on the current layer"
IsChecked="{Binding ShowLayerImageCrosshairs}">
<StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/crosshairs-16x16.png"/>
+ <i:Icon Value="fas fa-crosshairs"/>
<TextBlock Margin="5,0,5,0" Text="Crosshairs"/>
</StackPanel>
</CheckBox>
</ContextMenu>
</Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/warning-16x16.png"/>
- <TextBlock Margin="5,0,5,0" Text="Issues ⮟"/>
- </StackPanel>
- </ToggleButton>
-
- <!--
- <ToggleButton
- IsChecked="{Binding ShowLayerImageCrosshairs}"
- ToolTip.Tip="Show crosshairs for selected issues on the current layer"
- VerticalAlignment="Stretch"
- Margin="1,0,0,0">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/crosshairs-16x16.png"/>
- <TextBlock Margin="5,0,5,0" Text="Crosshairs"/>
- </StackPanel>
- </ToggleButton>
- !-->
-
- <Button Name="LayerPreviewOutlineButton"
+ </uc:ToggleButtonWithIcon>
+
+
+ <uc:ButtonWithIcon Name="LayerPreviewOutlineButton"
ToolTip.Tip="Click to access the various outlines."
Command="{Binding OpenContextMenu}"
CommandParameter="LayerPreviewOutline"
VerticalAlignment="Stretch"
- Margin="1,0,0,0">
- <Button.ContextMenu>
+ Margin="1,0,0,0"
+ Text="Outline ⮟"
+ Spacing="5"
+ Icon="fas fa-vector-square">
+ <uc:ButtonWithIcon.ContextMenu>
<ContextMenu Name="LayerPreviewOutlineContextMenu" PlacementMode="Bottom">
<CheckBox
IsChecked="{Binding ShowLayerOutlinePrintVolumeBoundary}"
@@ -2100,74 +1891,62 @@
</CheckBox.IsEnabled>
</CheckBox>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/vector-square-16x16.png"/>
- <TextBlock Margin="5,0,5,0" Text="Outline ⮟"/>
- </StackPanel>
- </Button>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
- <ToggleButton HorizontalAlignment="Right"
+ <uc:ToggleButtonWithIcon HorizontalAlignment="Right"
IsChecked="{Binding IsPixelEditorActive}"
ToolTip.Tip="Edit layer image: Draw pixels, add supports and/or drain holes."
VerticalAlignment="Stretch"
- Margin="0,0,0,0">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/pixel-16x16.png"/>
- <TextBlock Margin="5,0,5,0" Text="Pixel editor"/>
- </StackPanel>
- </ToggleButton>
+ Margin="0,0,0,0"
+ Text="Pixel editor"
+ Spacing="5"
+ Icon="fas fa-drafting-compass"/>
</WrapPanel>
<StackPanel HorizontalAlignment="Right" Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
- <Button Name="LayerActionsButton"
+ <uc:ButtonWithIcon Name="LayerActionsButton"
Command="{Binding OpenContextMenu}"
VerticalAlignment="Stretch"
- CommandParameter="LayerActions">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/layers-alt-16x16.png"/>
- <TextBlock VerticalAlignment="Center" Margin="5,0,5,0" Text="Actions ⮟"/>
- </StackPanel>
- <Button.ContextMenu>
+ VerticalContentAlignment="Center"
+ CommandParameter="LayerActions"
+ Text="Actions ⮟"
+ Spacing="5"
+ Icon="mdi-layers-edit">
+ <uc:ButtonWithIcon.ContextMenu>
<ContextMenu Name="LayerActionsContextMenu" PlacementMode="Bottom" Items="{Binding LayerActionsMenu}"/>
- </Button.ContextMenu>
- </Button>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
<Button
Command="{Binding ShowLayer}"
HotKey="F5"
ToolTip.Tip="Refresh current layer [F5]"
VerticalAlignment="Stretch"
- Margin="1,0,0,0">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/refresh-16x16.png"/>
- </StackPanel>
- </Button>
+ Margin="1,0,0,0"
+ i:Attached.Icon="fas fa-sync-alt"/>
- <Button
+ <uc:ButtonWithIcon
Command="{Binding SaveCurrentLayerImage}"
ToolTip.Tip="Save layer image to a file"
VerticalAlignment="Stretch"
- Margin="1,0,0,0">
- <Button.ContextMenu>
+ VerticalContentAlignment="Center"
+ Margin="1,0,0,0"
+ Text="⮟"
+ Spacing="3"
+ Icon="fas fa-save">
+ <uc:ButtonWithIcon.ContextMenu>
<ContextMenu PlacementMode="Bottom">
<MenuItem
Command="{Binding SaveCurrentROIImage}"
- Header="Save the selected region (ROI)">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/object-group-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ Header="Save the selected region (ROI)"
+ i:MenuItem.Icon="far fa-object-group"/>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/save-16x16.png"/>
- <TextBlock VerticalAlignment="Center" Text=" ⮟"/>
- </StackPanel>
- </Button>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
</StackPanel>
@@ -2175,26 +1954,25 @@
<Border
Grid.Row="1"
- BorderBrush="WhiteSmoke"
- BorderThickness="5"
- >
+ BorderBrush="{DynamicResource LightBackground}"
+ BorderThickness="5">
<uvtava:AdvancedImageBox
ShowGrid="{Binding Settings.LayerPreview.ShowBackgroudGrid}"
GridCellSize="15"
+ GridColor="{DynamicResource AdvancedImageBoxGridColor}"
+ GridColorAlternate="{DynamicResource AdvancedImageBoxGridAlternateColor}"
Name="LayerImage"/>
</Border>
<Canvas Grid.Row="1" Margin="20"
IsVisible="{Binding IsTooltipOverlayVisible}">
- <Border
- BorderThickness="1"
- BorderBrush="Black"
- Background="{Binding Settings.LayerPreview.TooltipOverlayBackgroundBrush}"
- Padding="10"
- CornerRadius="5"
- >
- <TextBlock Text="{Binding TooltipOverlayText}" />
+ <Border BorderThickness="1"
+ BorderBrush="Black"
+ Background="{Binding Settings.LayerPreview.TooltipOverlayBackgroundBrush}"
+ Padding="10"
+ CornerRadius="5">
+ <TextBlock Text="{Binding TooltipOverlayText}" Foreground="Black" />
</Border>
</Canvas>
@@ -2208,22 +1986,20 @@
<StackPanel
ToolTip.Tip="Number of pixels to cure on this layer image and the percentage of them against total lcd pixels and the total cured millimeters."
VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/pixel-16x16.png"/>
+ <i:Icon Value="mdi-google-downasaur"/>
<TextBlock Text="{Binding LayerPixelCountStr}"/>
</StackPanel>
- <Button
+ <uc:ButtonWithIcon
ToolTip.Tip="Object volume bounds for current layer, position and size in pixels and millimeters.
&#x0a;Click: go to region"
Command="{Binding ZoomToFitPrintVolume}"
- Margin="5,0,0,0">
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/expand-16x16.png"/>
- <TextBlock Text="{Binding LayerBoundsStr}"/>
- </StackPanel>
- </Button>
+ Margin="5,0,0,0"
+ Text="{Binding LayerBoundsStr}"
+ Spacing="5"
+ Icon="fas fa-expand"/>
- <Button
+ <uc:ButtonWithIcon
IsEnabled="{Binding IsFileLoaded}"
Margin="2,0,0,0"
Command="{Binding OnROIClick}"
@@ -2231,8 +2007,10 @@
&#x0a;(NS): Not selected
&#x0a;Click: go to region
&#x0a;ESC: Clear ROI &amp; Masks | ESC + Shift: Clear ROI"
- >
- <Button.ContextMenu>
+ Text="{Binding LayerROIStr}"
+ Spacing="5"
+ Icon="far fa-object-group">
+ <uc:ButtonWithIcon.ContextMenu>
<ContextMenu Name="ROIContextMenu" PlacementMode="Top">
<MenuItem
Command="{Binding SelectLayerPositiveAreasMask}"
@@ -2255,54 +2033,41 @@
Command="{Binding ClearROI}"
Header="Clear ROI"/>
</ContextMenu>
- </Button.ContextMenu>
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/object-group-16x16.png"/>
- <TextBlock Text="{Binding LayerROIStr}"/>
- </StackPanel>
- </Button>
+ </uc:ButtonWithIcon.ContextMenu>
+ </uc:ButtonWithIcon>
</WrapPanel>
<WrapPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Horizontal">
- <Button
+ <uc:ButtonWithIcon
IsEnabled="{Binding LayerPixelPicker.IsSet}"
Command="{Binding OnLayerPixelPickerClicked}"
ToolTip.Tip="Pixel picker:
&#x0a;Use CONTROL and over a pixel to get his position and brightness.
&#x0a;Click: Center at position"
- >
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/map-marker-16x16.png"/>
- <TextBlock Text="{Binding LayerPixelPicker}"/>
- </StackPanel>
- </Button>
+ Text="{Binding LayerPixelPicker}"
+ Spacing="5"
+ Icon="fas fa-map-marker-alt"/>
- <Button
- ToolTip.Tip="Layer image zoom level, use mouse scroll to zoom in/out into image.
+ <uc:ButtonWithIcon
+ ToolTip.Tip="Layer image zoom level, use mouse scroll to zoom in/out into image.
&#x0a;Click to set zoom to 100%
&#x0a;Shift+Click to set zoom to defined value
&#x0a;Ctrl + 0 OR double right click to scale to fit"
Margin="2,0,0,0"
Command="{Binding ZoomToNormal}"
- >
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/search-16x16.png"/>
- <TextBlock Text="{Binding LayerZoomStr}"/>
- </StackPanel>
- </Button>
+ Text="{Binding LayerZoomStr}"
+ Spacing="5"
+ Icon="fas fa-search-plus"/>
- <Button
- ToolTip.Tip="Layer Resolution and display size.
+ <uc:ButtonWithIcon
+ ToolTip.Tip="Layer Resolution and display size.
&#x0a;Click: Zoom to fit"
Command="{Binding ZoomToFitSimple}"
Margin="2,0,0,0"
- >
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/expand-16x16.png"/>
- <TextBlock Text="{Binding LayerResolutionStr}"/>
- </StackPanel>
- </Button>
+ Text="{Binding LayerResolutionStr}"
+ Spacing="5"
+ Icon="fas fa-expand"/>
<TextBlock
@@ -2320,8 +2085,8 @@
IsEnabled="{Binding IsProgressVisible}"
IsVisible="{Binding IsProgressVisible}">
<Border Grid.Row="1" Grid.Column="1" MinWidth="450"
- Background="White"
- BorderBrush="WhiteSmoke"
+ Classes="ProgressLoading"
+
BorderThickness="5"
CornerRadius="5">
<Grid RowDefinitions="Auto,Auto,Auto,Auto"
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index a96ebaa..e8eaeef 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -43,2480 +43,2231 @@ using Helpers = UVtools.WPF.Controls.Helpers;
using Path = System.IO.Path;
using Point = Avalonia.Point;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+public partial class MainWindow : WindowEx
{
- public partial class MainWindow : WindowEx
- {
- #region Redirects
+ #region Redirects
- public AppVersionChecker VersionChecker => App.VersionChecker;
- public static ClipboardManager Clipboard => ClipboardManager.Instance;
- #endregion
+ public AppVersionChecker VersionChecker => App.VersionChecker;
+ public static ClipboardManager Clipboard => ClipboardManager.Instance;
+ #endregion
- #region Controls
+ #region Controls
- //public ProgressWindow ProgressWindow = new ();
+ //public ProgressWindow ProgressWindow = new ();
- public static MenuItem[] MenuTools { get; } =
+ public static MenuItem[] MenuTools { get; } =
+ {
+ new()
{
- new()
- {
- Tag = new OperationEditParameters(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/wrench-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationRepairLayers(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/toolbox-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationMove(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/move-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationResize(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/expand-alt-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationFlip(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/flip-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationRotate(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/sync-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationSolidify(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/square-solid-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationMorph(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/geometry-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationRaftRelief(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/cookie-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationRedrawModel(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/code-branch-16x16.png"))
- }
- },
- /*new()
- {
- Tag = new OperationThreshold(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/th-16x16.png"))
- }
- },*/
- new()
- {
- Tag = new OperationLayerArithmetic(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/square-root-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationPixelArithmetic(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/pixel-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationMask(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/mask-16x16.png"))
- }
- },
- /*new()
- {
- Tag = new OperationPixelDimming(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/pixel-16x16.png"))
- }
- },*/
- new()
- {
- Tag = new OperationLightBleedCompensation(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/lightbulb-solid-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationInfill(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/stroopwafel-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationBlur(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/blur-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationPattern(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/pattern-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationFadeExposureTime(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/history-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationDoubleExposure(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/equals-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationDynamicLifts(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/angle-double-up-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationDynamicLayerHeight(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/dynamic-layers-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerReHeight(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/ladder-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationRaiseOnPrintFinish(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/level-up-alt-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationChangeResolution(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/resize-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationTimelapse(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/camera-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationScripting(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/code-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalculator(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/calculator-16x16.png"))
- }
- },
- };
+ Tag = new OperationEditParameters(),
+ },
+ new()
+ {
+ Tag = new OperationRepairLayers(),
+ },
+ new()
+ {
+ Tag = new OperationMove(),
+ },
+ new()
+ {
+ Tag = new OperationResize(),
+ },
+ new()
+ {
+ Tag = new OperationFlip(),
+ },
+ new()
+ {
+ Tag = new OperationRotate(),
+ },
+ new()
+ {
+ Tag = new OperationSolidify(),
+ },
+ new()
+ {
+ Tag = new OperationMorph(),
+ },
+ new()
+ {
+ Tag = new OperationRaftRelief(),
+ },
+ new()
+ {
+ Tag = new OperationRedrawModel(),
+ },
+ /*new()
+ {
+ Tag = new OperationThreshold(),
+ },*/
+ new()
+ {
+ Tag = new OperationLayerArithmetic(),
+ },
+ new()
+ {
+ Tag = new OperationPixelArithmetic(),
+ },
+ new()
+ {
+ Tag = new OperationMask(),
+ },
+ /*new()
+ {
+ Tag = new OperationPixelDimming(),
+ },*/
+ new()
+ {
+ Tag = new OperationLightBleedCompensation(),
+ },
+ new()
+ {
+ Tag = new OperationInfill(),
+ },
+ new()
+ {
+ Tag = new OperationBlur(),
+ },
+ new()
+ {
+ Tag = new OperationPattern(),
+ },
+ new()
+ {
+ Tag = new OperationFadeExposureTime(),
+ },
+ new()
+ {
+ Tag = new OperationDoubleExposure(),
+ },
+ new()
+ {
+ Tag = new OperationDynamicLifts(),
+ },
+ new()
+ {
+ Tag = new OperationDynamicLayerHeight(),
+ },
+ new()
+ {
+ Tag = new OperationLayerReHeight(),
+ },
+ new()
+ {
+ Tag = new OperationRaiseOnPrintFinish(),
+ },
+ new()
+ {
+ Tag = new OperationChangeResolution(),
+ },
+ new()
+ {
+ Tag = new OperationTimelapse(),
+ },
+ new()
+ {
+ Tag = new OperationScripting(),
+ },
+ new()
+ {
+ Tag = new OperationCalculator(),
+ },
+ };
- public static MenuItem[] MenuCalibration { get; } =
+ public static MenuItem[] MenuCalibration { get; } =
+ {
+ new()
{
- new()
- {
- Tag = new OperationCalibrateExposureFinder(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/sun-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalibrateElephantFoot(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/elephant-foot-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalibrateXYZAccuracy(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/cubes-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalibrateLiftHeight(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/long-arrow-alt-up-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalibrateTolerance(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/dot-circle-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalibrateGrayscale(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/chart-pie-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalibrateStressTower(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/chess-rook-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationCalibrateExternalTests(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/bookmark-16x16.png"))
- }
- },
- };
+ Tag = new OperationCalibrateExposureFinder(),
+ },
+ new()
+ {
+ Tag = new OperationCalibrateElephantFoot(),
+ },
+ new()
+ {
+ Tag = new OperationCalibrateXYZAccuracy(),
+ },
+ new()
+ {
+ Tag = new OperationCalibrateLiftHeight(),
+ },
+ new()
+ {
+ Tag = new OperationCalibrateTolerance(),
+ },
+ new()
+ {
+ Tag = new OperationCalibrateGrayscale(),
+ },
+ new()
+ {
+ Tag = new OperationCalibrateStressTower(),
+ },
+ new()
+ {
+ Tag = new OperationCalibrateExternalTests(),
+ },
+ };
- public static MenuItem[] LayerActionsMenu { get; } =
+ public static MenuItem[] LayerActionsMenu { get; } =
+ {
+ new()
{
- new()
- {
- Tag = new OperationLayerImport(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/file-import-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerClone(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/copy-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerRemove(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/trash-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerExportImage(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/file-image-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerExportGif(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/file-gif-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerExportSkeleton(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/file-image-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerExportHeatMap(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/file-image-16x16.png"))
- }
- },
- new()
- {
- Tag = new OperationLayerExportMesh(),
- Icon = new Avalonia.Controls.Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/cubes-16x16.png"))
- }
- },
- };
+ Tag = new OperationLayerImport(),
+ },
+ new()
+ {
+ Tag = new OperationLayerClone(),
+ },
+ new()
+ {
+ Tag = new OperationLayerRemove(),
+ },
+ new()
+ {
+ Tag = new OperationLayerExportImage(),
+ },
+ new()
+ {
+ Tag = new OperationLayerExportGif(),
+ },
+ new()
+ {
+ Tag = new OperationLayerExportSkeleton(),
+ },
+ new()
+ {
+ Tag = new OperationLayerExportHeatMap(),
+ },
+ new()
+ {
+ Tag = new OperationLayerExportMesh(),
+ },
+ };
- #region DataSets
-
+ #endregion
- #endregion
+ #region Members
- #endregion
+ public Stopwatch LastStopWatch = new();
+
+ private bool _isGUIEnabled = true;
+ private uint _savesCount;
+ private bool _canSave;
+ private readonly MenuItem _menuFileSendTo;
+ private MenuItem[] _menuFileOpenRecentItems;
+ private MenuItem[] _menuFileSendToItems;
+ private MenuItem[] _menuFileConvertItems;
+
+ private PointerEventArgs _globalPointerEventArgs;
+ private PointerPoint _globalPointerPoint;
+ private KeyModifiers _globalModifiers = KeyModifiers.None;
+ private TabItem _selectedTabItem;
+ private TabItem _lastSelectedTabItem;
- #region Members
+ #endregion
- public Stopwatch LastStopWatch = new();
-
- private bool _isGUIEnabled = true;
- private uint _savesCount;
- private bool _canSave;
- private MenuItem _menuFileSendTo;
- private MenuItem[] _menuFileOpenRecentItems;
- private MenuItem[] _menuFileSendToItems;
- private MenuItem[] _menuFileConvertItems;
-
- private PointerEventArgs _globalPointerEventArgs;
- private PointerPoint _globalPointerPoint;
- private KeyModifiers _globalModifiers = KeyModifiers.None;
- private TabItem _selectedTabItem;
- private TabItem _lastSelectedTabItem;
-
- #endregion
-
- #region GUI Models
- public bool IsGUIEnabled
- {
- get => _isGUIEnabled;
- set
- {
- if (!RaiseAndSetIfChanged(ref _isGUIEnabled, value)) return;
- if (!_isGUIEnabled)
- {
- DragDrop.SetAllowDrop(this, false);
- //ProgressWindow = new ProgressWindow();
- return;
- }
- else
- {
- DragDrop.SetAllowDrop(this, true);
- }
+ #region GUI Models
+ public bool IsGUIEnabled
+ {
+ get => _isGUIEnabled;
+ set
+ {
+ if (!RaiseAndSetIfChanged(ref _isGUIEnabled, value)) return;
+ if (!_isGUIEnabled)
+ {
+ DragDrop.SetAllowDrop(this, false);
+ //ProgressWindow = new ProgressWindow();
+ return;
+ }
+ else
+ {
+ DragDrop.SetAllowDrop(this, true);
+ }
- LastStopWatch = Progress.StopWatch;
- ProgressFinish();
- //ProgressWindow.Close(DialogResults.OK);
- //ProgressWindow.Dispose();
- /*if (Dispatcher.UIThread.CheckAccess())
+ LastStopWatch = Progress.StopWatch;
+ ProgressFinish();
+ //ProgressWindow.Close(DialogResults.OK);
+ //ProgressWindow.Dispose();
+ /*if (Dispatcher.UIThread.CheckAccess())
+ {
+ ProgressWindow.Close();
+ ProgressWindow.Dispose();
+ }
+ else
+ {
+ Dispatcher.UIThread.InvokeAsync(() =>
{
ProgressWindow.Close();
ProgressWindow.Dispose();
- }
- else
- {
- Dispatcher.UIThread.InvokeAsync(() =>
- {
- ProgressWindow.Close();
- ProgressWindow.Dispose();
- });
- }*/
- }
+ });
+ }*/
}
+ }
- public bool IsFileLoaded
- {
- get => SlicerFile is not null;
- set => RaisePropertyChanged();
- }
+ public bool IsFileLoaded
+ {
+ get => SlicerFile is not null;
+ set => RaisePropertyChanged();
+ }
- public TabItem TabInformation { get; }
- public TabItem TabGCode { get; }
- public TabItem TabIssues { get; }
- public TabItem TabPixelEditor { get; }
- public TabItem TabLog { get; }
+ public TabItem TabInformation { get; }
+ public TabItem TabGCode { get; }
+ public TabItem TabIssues { get; }
+ public TabItem TabPixelEditor { get; }
+ public TabItem TabLog { get; }
- public TabItem SelectedTabItem
+ public TabItem SelectedTabItem
+ {
+ get => _selectedTabItem;
+ set
{
- get => _selectedTabItem;
- set
+ var lastTab = _selectedTabItem;
+ if (!RaiseAndSetIfChanged(ref _selectedTabItem, value)) return;
+ LastSelectedTabItem = lastTab;
+ if (_firstTimeOnIssues)
{
- var lastTab = _selectedTabItem;
- if (!RaiseAndSetIfChanged(ref _selectedTabItem, value)) return;
- LastSelectedTabItem = lastTab;
- if (_firstTimeOnIssues)
+ _firstTimeOnIssues = false;
+ if (ReferenceEquals(_selectedTabItem, TabIssues) && Settings.Issues.ComputeIssuesOnClickTab)
{
- _firstTimeOnIssues = false;
- if (ReferenceEquals(_selectedTabItem, TabIssues) && Settings.Issues.ComputeIssuesOnClickTab)
- {
- OnClickDetectIssues().ConfigureAwait(false);
- }
+ OnClickDetectIssues().ConfigureAwait(false);
}
}
}
+ }
- public TabItem LastSelectedTabItem
- {
- get => _lastSelectedTabItem;
- set => RaiseAndSetIfChanged(ref _lastSelectedTabItem, value);
- }
+ public TabItem LastSelectedTabItem
+ {
+ get => _lastSelectedTabItem;
+ set => RaiseAndSetIfChanged(ref _lastSelectedTabItem, value);
+ }
- #endregion
+ #endregion
- public uint SavesCount
- {
- get => _savesCount;
- set => RaiseAndSetIfChanged(ref _savesCount, value);
- }
+ public uint SavesCount
+ {
+ get => _savesCount;
+ set => RaiseAndSetIfChanged(ref _savesCount, value);
+ }
- public bool CanSave
- {
- get => IsFileLoaded && _canSave;
- set => RaiseAndSetIfChanged(ref _canSave, value);
- }
+ public bool CanSave
+ {
+ get => IsFileLoaded && _canSave;
+ set => RaiseAndSetIfChanged(ref _canSave, value);
+ }
- public MenuItem[] MenuFileOpenRecentItems
- {
- get => _menuFileOpenRecentItems;
- set => RaiseAndSetIfChanged(ref _menuFileOpenRecentItems, value);
- }
+ public MenuItem[] MenuFileOpenRecentItems
+ {
+ get => _menuFileOpenRecentItems;
+ set => RaiseAndSetIfChanged(ref _menuFileOpenRecentItems, value);
+ }
- public MenuItem[] MenuFileSendToItems
- {
- get => _menuFileSendToItems;
- set => RaiseAndSetIfChanged(ref _menuFileSendToItems, value);
- }
+ public MenuItem[] MenuFileSendToItems
+ {
+ get => _menuFileSendToItems;
+ set => RaiseAndSetIfChanged(ref _menuFileSendToItems, value);
+ }
- public MenuItem[] MenuFileConvertItems
- {
- get => _menuFileConvertItems;
- set => RaiseAndSetIfChanged(ref _menuFileConvertItems, value);
- }
-
+ public MenuItem[] MenuFileConvertItems
+ {
+ get => _menuFileConvertItems;
+ set => RaiseAndSetIfChanged(ref _menuFileConvertItems, value);
+ }
- #region Constructors
- public MainWindow()
- {
- InitializeComponent();
+ #region Constructors
- //App.ThemeSelector?.EnableThemes(this);
- InitProgress();
- InitInformation();
- InitIssues();
- InitPixelEditor();
- InitClipboardLayers();
- InitLayerPreview();
- InitSuggestions();
-
- RefreshRecentFiles(true);
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ //App.ThemeSelector?.EnableThemes(this);
+ InitProgress();
+ InitInformation();
+ InitIssues();
+ InitPixelEditor();
+ InitClipboardLayers();
+ InitLayerPreview();
+ InitSuggestions();
+
+ RefreshRecentFiles(true);
- TabInformation = this.FindControl<TabItem>("TabInformation");
- TabGCode = this.FindControl<TabItem>("TabGCode");
- TabIssues = this.FindControl<TabItem>("TabIssues");
- TabPixelEditor = this.FindControl<TabItem>("TabPixelEditor");
- TabLog = this.FindControl<TabItem>("TabLog");
+ TabInformation = this.FindControl<TabItem>("TabInformation");
+ TabGCode = this.FindControl<TabItem>("TabGCode");
+ TabIssues = this.FindControl<TabItem>("TabIssues");
+ TabPixelEditor = this.FindControl<TabItem>("TabPixelEditor");
+ TabLog = this.FindControl<TabItem>("TabLog");
- foreach (var menuItem in new[] { MenuTools, MenuCalibration, LayerActionsMenu })
+ foreach (var menuItem in new[] { MenuTools, MenuCalibration, LayerActionsMenu })
+ {
+ foreach (var menuTool in menuItem)
{
- foreach (var menuTool in menuItem)
- {
- if (menuTool.Tag is not Operation operation) continue;
- menuTool.Header = operation.Title;
- menuTool.Click += async (sender, args) => await ShowRunOperation(operation.GetType());
- }
+ if (menuTool.Tag is not Operation operation) continue;
+ if (!string.IsNullOrWhiteSpace(operation.IconClass)) menuTool.Icon = new Projektanker.Icons.Avalonia.Icon{Value = operation.IconClass};
+ menuTool.Header = operation.Title;
+ menuTool.Click += async (sender, args) => await ShowRunOperation(operation.GetType());
}
+ }
- /*LayerSlider.PropertyChanged += (sender, args) =>
+ /*LayerSlider.PropertyChanged += (sender, args) =>
+ {
+ Debug.WriteLine(args.Property.Name);
+ if (args.Property.Name == nameof(LayerSlider.Value))
{
- Debug.WriteLine(args.Property.Name);
- if (args.Property.Name == nameof(LayerSlider.Value))
- {
- LayerNavigationTooltipPanel.Margin = LayerNavigationTooltipMargin;
- return;
- }
- };*/
- //PropertyChanged += OnPropertyChanged;
+ LayerNavigationTooltipPanel.Margin = LayerNavigationTooltipMargin;
+ return;
+ }
+ };*/
+ //PropertyChanged += OnPropertyChanged;
- UpdateTitle();
+ UpdateTitle();
- var clientSizeObs = this.GetObservable(ClientSizeProperty);
- clientSizeObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
- var windowStateObs = this.GetObservable(WindowStateProperty);
- windowStateObs.Subscribe(windowsState => UpdateLayerTrackerHighlightIssues());
+ var clientSizeObs = this.GetObservable(ClientSizeProperty);
+ clientSizeObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
+ var windowStateObs = this.GetObservable(WindowStateProperty);
+ windowStateObs.Subscribe(windowsState => UpdateLayerTrackerHighlightIssues());
- DataContext = this;
+ DataContext = this;
- AddHandler(DragDrop.DropEvent, (sender, e) =>
- {
- if (!_isGUIEnabled) return;
- ProcessFiles(e.Data.GetFileNames()?.ToArray());
- });
+ AddHandler(DragDrop.DropEvent, (sender, e) =>
+ {
+ if (!_isGUIEnabled) return;
+ ProcessFiles(e.Data.GetFileNames()?.ToArray());
+ });
- _menuFileSendTo = this.FindControl<MenuItem>("MainMenu.File.SendTo");
- this.FindControl<MenuItem>("MainMenu.File").SubmenuOpened += (sender, e) =>
- {
- if (!IsFileLoaded) return;
+ _menuFileSendTo = this.FindControl<MenuItem>("MainMenu.File.SendTo");
+ this.FindControl<MenuItem>("MainMenu.File").SubmenuOpened += (sender, e) =>
+ {
+ if (!IsFileLoaded) return;
- var menuItems = new List<MenuItem>();
+ var menuItems = new List<MenuItem>();
- var drives = DriveInfo.GetDrives();
+ var drives = DriveInfo.GetDrives();
- if (drives.Length > 0)
+ if (drives.Length > 0)
+ {
+ foreach (var drive in drives)
{
- foreach (var drive in drives)
- {
- if (drive.DriveType != DriveType.Removable || !drive.IsReady) continue; // Not our target, skip
- if (SlicerFile.FileFullPath.StartsWith(drive.Name))
- continue; // File already on this device, skip
+ if (drive.DriveType != DriveType.Removable || !drive.IsReady) continue; // Not our target, skip
+ if (SlicerFile.FileFullPath.StartsWith(drive.Name))
+ continue; // File already on this device, skip
- var header = drive.Name;
- if (!string.IsNullOrWhiteSpace(drive.VolumeLabel))
- {
- header += $" {drive.VolumeLabel}";
- }
+ var header = drive.Name;
+ if (!string.IsNullOrWhiteSpace(drive.VolumeLabel))
+ {
+ header += $" {drive.VolumeLabel}";
+ }
- header += $" ({SizeExtensions.SizeSuffix(drive.AvailableFreeSpace)}) [{drive.DriveFormat}]";
+ header += $" ({SizeExtensions.SizeSuffix(drive.AvailableFreeSpace)}) [{drive.DriveFormat}]";
- var menuItem = new MenuItem
- {
- Header = header,
- Tag = drive,
- Icon = new Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/usb-16x16.png"))
- }
- };
- menuItem.Click += FileSendToItemClick;
+ var menuItem = new MenuItem
+ {
+ Header = header,
+ Tag = drive,
+ Icon = new Projektanker.Icons.Avalonia.Icon{ Value = "fab fa-usb" }
+ };
+ menuItem.Click += FileSendToItemClick;
- menuItems.Add(menuItem);
- }
+ menuItems.Add(menuItem);
}
+ }
- if (Settings.General.SendToCustomLocations is not null && Settings.General.SendToCustomLocations.Count > 0)
+ if (Settings.General.SendToCustomLocations is not null && Settings.General.SendToCustomLocations.Count > 0)
+ {
+ foreach (var location in Settings.General.SendToCustomLocations)
{
- foreach (var location in Settings.General.SendToCustomLocations)
+ if(!location.IsEnabled) continue;
+
+ if (!string.IsNullOrWhiteSpace(location.CompatibleExtensions))
{
- if(!location.IsEnabled) continue;
+ var extensions = location.CompatibleExtensions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- if (!string.IsNullOrWhiteSpace(location.CompatibleExtensions))
+ var found = false;
+ foreach (var ext in extensions)
{
- var extensions = location.CompatibleExtensions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
-
- var found = false;
- foreach (var ext in extensions)
- {
- found = SlicerFile.FileFullPath.EndsWith($".{ext}");
- if (found) break;
- }
- if(!found) continue;
+ found = SlicerFile.FileFullPath.EndsWith($".{ext}");
+ if (found) break;
}
+ if(!found) continue;
+ }
- var menuItem = new MenuItem
- {
- Header = location.ToString(),
- Tag = location,
- Icon = new Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/folder-16x16.png"))
- }
- };
- menuItem.Click += FileSendToItemClick;
+ var menuItem = new MenuItem
+ {
+ Header = location.ToString(),
+ Tag = location,
+ Icon = new Projektanker.Icons.Avalonia.Icon { Value = "fas fa-folder" }
+ };
+ menuItem.Click += FileSendToItemClick;
- menuItems.Add(menuItem);
- }
+ menuItems.Add(menuItem);
}
+ }
- if (Settings.Network.RemotePrinters is not null && Settings.Network.RemotePrinters.Count > 0)
+ if (Settings.Network.RemotePrinters is not null && Settings.Network.RemotePrinters.Count > 0)
+ {
+ foreach (var remotePrinter in Settings.Network.RemotePrinters)
{
- foreach (var remotePrinter in Settings.Network.RemotePrinters)
+ if(!remotePrinter.IsEnabled || !remotePrinter.IsValid) continue;
+
+ if (!string.IsNullOrWhiteSpace(remotePrinter.CompatibleExtensions))
{
- if(!remotePrinter.IsEnabled || !remotePrinter.IsValid) continue;
+ var extensions = remotePrinter.CompatibleExtensions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- if (!string.IsNullOrWhiteSpace(remotePrinter.CompatibleExtensions))
+ var found = false;
+ foreach (var ext in extensions)
{
- var extensions = remotePrinter.CompatibleExtensions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
-
- var found = false;
- foreach (var ext in extensions)
- {
- found = SlicerFile.FileFullPath.EndsWith($".{ext}");
- if (found) break;
- }
- if (!found) continue;
+ found = SlicerFile.FileFullPath.EndsWith($".{ext}");
+ if (found) break;
}
+ if (!found) continue;
+ }
- var menuItem = new MenuItem
- {
- Header = remotePrinter.ToString(),
- Tag = remotePrinter,
- Icon = new Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/network-wired-16x16.png"))
- }
- };
- menuItem.Click += FileSendToItemClick;
+ var menuItem = new MenuItem
+ {
+ Header = remotePrinter.ToString(),
+ Tag = remotePrinter,
+ Icon = new Projektanker.Icons.Avalonia.Icon { Value = "fas fa-network-wired" }
+ };
+ menuItem.Click += FileSendToItemClick;
- menuItems.Add(menuItem);
- }
+ menuItems.Add(menuItem);
}
+ }
- if (Settings.General.SendToProcess is not null && Settings.General.SendToProcess.Count > 0)
+ if (Settings.General.SendToProcess is not null && Settings.General.SendToProcess.Count > 0)
+ {
+ foreach (var application in Settings.General.SendToProcess)
{
- foreach (var application in Settings.General.SendToProcess)
+ if (!application.IsEnabled ) continue;
+
+ if (!string.IsNullOrWhiteSpace(application.CompatibleExtensions))
{
- if (!application.IsEnabled ) continue;
+ var extensions = application.CompatibleExtensions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- if (!string.IsNullOrWhiteSpace(application.CompatibleExtensions))
+ var found = false;
+ foreach (var ext in extensions)
{
- var extensions = application.CompatibleExtensions.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
-
- var found = false;
- foreach (var ext in extensions)
- {
- found = SlicerFile.FileFullPath.EndsWith($".{ext}");
- if (found) break;
- }
- if (!found) continue;
+ found = SlicerFile.FileFullPath.EndsWith($".{ext}");
+ if (found) break;
}
+ if (!found) continue;
+ }
- var menuItem = new MenuItem
- {
- Header = application.Name,
- Tag = application,
- Icon = new Image
- {
- Source = new Bitmap(App.GetAsset("/Assets/Icons/cog-16x16.png"))
- }
- };
- menuItem.Click += FileSendToItemClick;
+ var menuItem = new MenuItem
+ {
+ Header = application.Name,
+ Tag = application,
+ Icon = new Projektanker.Icons.Avalonia.Icon { Value = "fas fa-cog" }
+ };
+ menuItem.Click += FileSendToItemClick;
- menuItems.Add(menuItem);
- }
+ menuItems.Add(menuItem);
}
+ }
- MenuFileSendToItems = menuItems.ToArray();
- _menuFileSendTo.IsVisible = _menuFileSendTo.IsEnabled = menuItems.Count > 0;
- };
- }
-
- private async void FileSendToItemClick(object? sender, RoutedEventArgs e)
- {
- if (sender is not MenuItem menuItem) return;
+ MenuFileSendToItems = menuItems.ToArray();
+ _menuFileSendTo.IsVisible = _menuFileSendTo.IsEnabled = menuItems.Count > 0;
+ };
+ }
+ private async void FileSendToItemClick(object? sender, RoutedEventArgs e)
+ {
+ if (sender is not MenuItem menuItem) return;
- string path;
- if (menuItem.Tag is DriveInfo drive)
- {
- if (!drive.IsReady)
- {
- await this.MessageBoxError($"The device {drive.Name} is not ready/available at this time.",
- "Unable to copy the file");
- return;
- }
+ string path;
+ if (menuItem.Tag is DriveInfo drive)
+ {
- path = drive.Name;
- }
- else if (menuItem.Tag is MappedDevice device)
- {
- path = device.Path;
- }
- else if (menuItem.Tag is RemotePrinter preRemotePrinter)
- {
- path = preRemotePrinter.HostUrl;
- }
- else if (menuItem.Tag is MappedProcess process)
- {
- path = process.Name;
- }
- else
+ if (!drive.IsReady)
{
+ await this.MessageBoxError($"The device {drive.Name} is not ready/available at this time.",
+ "Unable to copy the file");
return;
}
- if (CanSave)
+ path = drive.Name;
+ }
+ else if (menuItem.Tag is MappedDevice device)
+ {
+ path = device.Path;
+ }
+ else if (menuItem.Tag is RemotePrinter preRemotePrinter)
+ {
+ path = preRemotePrinter.HostUrl;
+ }
+ else if (menuItem.Tag is MappedProcess process)
+ {
+ path = process.Name;
+ }
+ else
+ {
+ return;
+ }
+
+ if (CanSave)
+ {
+ switch (await this.MessageBoxQuestion("There are unsaved changes. Do you want to save the current file before copy it over?\n\n" +
+ "Yes: Save the current file and copy it over.\n" +
+ "No: Copy the file without current modifications.\n" +
+ "Cancel: Abort the operation.", "Send to - Unsaved changes", ButtonEnum.YesNoCancel))
{
- switch (await this.MessageBoxQuestion("There are unsaved changes. Do you want to save the current file before copy it over?\n\n" +
- "Yes: Save the current file and copy it over.\n" +
- "No: Copy the file without current modifications.\n" +
- "Cancel: Abort the operation.", "Send to - Unsaved changes", ButtonEnum.YesNoCancel))
- {
- case ButtonResult.Yes:
- await SaveFile(true);
- break;
- case ButtonResult.No:
- break;
- default:
- return;
- }
+ case ButtonResult.Yes:
+ await SaveFile(true);
+ break;
+ case ButtonResult.No:
+ break;
+ default:
+ return;
}
+ }
- ShowProgressWindow($"Sending: {SlicerFile.Filename} to {path}");
- Progress.ItemName = "Sending";
+ ShowProgressWindow($"Sending: {SlicerFile.Filename} to {path}");
+ Progress.ItemName = "Sending";
- if (menuItem.Tag is RemotePrinter remotePrinter)
+ if (menuItem.Tag is RemotePrinter remotePrinter)
+ {
+ var startPrint = (_globalModifiers & KeyModifiers.Shift) != 0 && remotePrinter.RequestPrintFile is not null && remotePrinter.RequestPrintFile.IsValid;
+ if (startPrint)
{
- var startPrint = (_globalModifiers & KeyModifiers.Shift) != 0 && remotePrinter.RequestPrintFile is not null && remotePrinter.RequestPrintFile.IsValid;
- if (startPrint)
- {
- if (await this.MessageBoxQuestion(
+ if (await this.MessageBoxQuestion(
"If supported, you are about to send the file and start the print with it.\n" +
"Keep in mind there is no guarantee that the file will start to print.\n" +
"Are you sure you want to continue?\n\n" +
"Yes: Send file and print it.\n" +
"No: Cancel file sending and print.", "Send and print the file?") != ButtonResult.Yes) return;
- }
+ }
- HttpResponseMessage response = null;
- try
- {
- await using var stream = File.OpenRead(SlicerFile.FileFullPath);
- using var httpContent = new StreamContent(stream);
+ HttpResponseMessage response = null;
+ try
+ {
+ await using var stream = File.OpenRead(SlicerFile.FileFullPath);
+ using var httpContent = new StreamContent(stream);
- Progress.ItemCount = (uint)(stream.Length / 1000000);
- bool isCopying = true;
- try
+ Progress.ItemCount = (uint)(stream.Length / 1000000);
+ bool isCopying = true;
+ try
+ {
+ var task = new Task(() =>
{
- var task = new Task(() =>
+ while (isCopying)
{
- while (isCopying)
- {
- Progress.ProcessedItems = (uint)(stream.Position / 1000000);
- Thread.Sleep(200);
- }
- });
- }
- catch (Exception)
- {
- // ignored
- }
-
- response = await remotePrinter.RequestUploadFile.SendRequest(remotePrinter, Progress, SlicerFile.Filename, httpContent);
- isCopying = false;
- if (!response.IsSuccessStatusCode)
- {
- await this.MessageBoxError(response.ToString(), "Send to printer");
- }
+ Progress.ProcessedItems = (uint)(stream.Position / 1000000);
+ Thread.Sleep(200);
+ }
+ });
}
- catch (OperationCanceledException) { }
- catch (Exception ex)
+ catch (Exception)
{
- await this.MessageBoxError(ex.Message, "Send to printer");
+ // ignored
}
-
- if (
- response is not null && response.IsSuccessStatusCode &&
- startPrint)
+ response = await remotePrinter.RequestUploadFile.SendRequest(remotePrinter, Progress, SlicerFile.Filename, httpContent);
+ isCopying = false;
+ if (!response.IsSuccessStatusCode)
{
- response.Dispose();
- Progress.Title = "Waiting 2 seconds...";
- await Task.Delay(2000);
- try
- {
- response = await remotePrinter.RequestPrintFile.SendRequest(remotePrinter, Progress, SlicerFile.Filename);
- if (!response.IsSuccessStatusCode)
- {
- await this.MessageBoxError(response.ToString(), "Unable to send the print command");
- }
- /*else
- {
- await this.MessageBoxInfo(response.ToString(), "Print send command report");
- }*/
- }
- catch (OperationCanceledException) { }
- catch (Exception ex)
- {
- await this.MessageBoxError(ex.Message, "Unable to send the print command");
- }
+ await this.MessageBoxError(response.ToString(), "Send to printer");
}
}
- else if (menuItem.Tag is MappedProcess process)
+ catch (OperationCanceledException) { }
+ catch (Exception ex)
+ {
+ await this.MessageBoxError(ex.Message, "Send to printer");
+ }
+
+
+ if (
+ response is not null && response.IsSuccessStatusCode &&
+ startPrint)
{
- Progress.ItemName = "Waiting for completion";
+ response.Dispose();
+ Progress.Title = "Waiting 2 seconds...";
+ await Task.Delay(2000);
try
{
- await process.StartProcess(SlicerFile, Progress.Token);
+ response = await remotePrinter.RequestPrintFile.SendRequest(remotePrinter, Progress, SlicerFile.Filename);
+ if (!response.IsSuccessStatusCode)
+ {
+ await this.MessageBoxError(response.ToString(), "Unable to send the print command");
+ }
+ /*else
+ {
+ await this.MessageBoxInfo(response.ToString(), "Print send command report");
+ }*/
}
- catch (OperationCanceledException){}
+ catch (OperationCanceledException) { }
catch (Exception ex)
{
- await this.MessageBoxError(ex.Message, $"Unable to start the process {process.Name}");
+ await this.MessageBoxError(ex.Message, "Unable to send the print command");
}
-
}
- else
+ }
+ else if (menuItem.Tag is MappedProcess process)
+ {
+ Progress.ItemName = "Waiting for completion";
+ try
+ {
+ await process.StartProcess(SlicerFile, Progress.Token);
+ }
+ catch (OperationCanceledException){}
+ catch (Exception ex)
+ {
+ await this.MessageBoxError(ex.Message, $"Unable to start the process {process.Name}");
+ }
+
+ }
+ else
+ {
+ /*var copyResult = await Task.Factory.StartNew(() =>
{
- /*var copyResult = await Task.Factory.StartNew(() =>
+ try
{
- try
- {
- var fileDest = Path.Combine(path, SlicerFile.Filename);
- //File.Copy(SlicerFile.FileFullPath, $"{Path.Combine(path, SlicerFile.Filename)}", true);
- var buffer = new byte[1024 * 1024]; // 1MB buffer
+ var fileDest = Path.Combine(path, SlicerFile.Filename);
+ //File.Copy(SlicerFile.FileFullPath, $"{Path.Combine(path, SlicerFile.Filename)}", true);
+ var buffer = new byte[1024 * 1024]; // 1MB buffer
- using var source = File.OpenRead(SlicerFile.FileFullPath);
- using var dest = new FileStream(fileDest, FileMode.Create, FileAccess.Write);
- //long totalBytes = 0;
- //int currentBlockSize;
+ using var source = File.OpenRead(SlicerFile.FileFullPath);
+ using var dest = new FileStream(fileDest, FileMode.Create, FileAccess.Write);
+ //long totalBytes = 0;
+ //int currentBlockSize;
- Progress.Reset("Megabyte(s)", (uint)(source.Length / 1000000));
- var copyProgress = new Progress<long>(copiedBytes => Progress.ProcessedItems = (uint)(copiedBytes / 1000000));
- source.CopyToAsync(dest, 0, copyProgress, Progress.Token).ConfigureAwait(false);
+ Progress.Reset("Megabyte(s)", (uint)(source.Length / 1000000));
+ var copyProgress = new Progress<long>(copiedBytes => Progress.ProcessedItems = (uint)(copiedBytes / 1000000));
+ source.CopyToAsync(dest, 0, copyProgress, Progress.Token).ConfigureAwait(false);
- /*while ((currentBlockSize = source.Read(buffer)) > 0)
- {
- totalBytes += currentBlockSize;
+ /*while ((currentBlockSize = source.Read(buffer)) > 0)
+ {
+ totalBytes += currentBlockSize;
- dest.Write(buffer, 0, currentBlockSize);
+ dest.Write(buffer, 0, currentBlockSize);
- if (Progress.Token.IsCancellationRequested)
- {
- // Delete dest file here
- dest.Dispose();
- File.Delete(fileDest);
- return false;
- }
+ if (Progress.Token.IsCancellationRequested)
+ {
+ // Delete dest file here
+ dest.Dispose();
+ File.Delete(fileDest);
+ return false;
+ }
- Progress.ProcessedItems = (uint)(totalBytes / 1000000);
- }*/
+ Progress.ProcessedItems = (uint)(totalBytes / 1000000);
+ }*/
- /* return true;
- }
- catch (OperationCanceledException) { }
- catch (Exception exception)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(exception.ToString(), "Unable to copy the file"));
- }
+ /* return true;
+ }
+ catch (OperationCanceledException) { }
+ catch (Exception exception)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(exception.ToString(), "Unable to copy the file"));
+ }
- return false;
- });*/
+ return false;
+ });*/
- bool copyResult = false;
- var fileDest = Path.Combine(path, SlicerFile.Filename);
- try
- {
- await using var source = File.OpenRead(SlicerFile.FileFullPath);
- await using var dest = new FileStream(fileDest, FileMode.Create, FileAccess.Write);
+ bool copyResult = false;
+ var fileDest = Path.Combine(path, SlicerFile.Filename);
+ try
+ {
+ await using var source = File.OpenRead(SlicerFile.FileFullPath);
+ await using var dest = new FileStream(fileDest, FileMode.Create, FileAccess.Write);
- Progress.Reset("Megabyte(s)", (uint)(source.Length / 1000000));
- var copyProgress = new Progress<long>(copiedBytes => Progress.ProcessedItems = (uint)(copiedBytes / 1000000));
- await source.CopyToAsync(dest, copyProgress, Progress.Token);
+ Progress.Reset("Megabyte(s)", (uint)(source.Length / 1000000));
+ var copyProgress = new Progress<long>(copiedBytes => Progress.ProcessedItems = (uint)(copiedBytes / 1000000));
+ await source.CopyToAsync(dest, copyProgress, Progress.Token);
- copyResult = true;
- }
- catch (OperationCanceledException)
+ copyResult = true;
+ }
+ catch (OperationCanceledException)
+ {
+ try
{
- try
- {
- if (File.Exists(fileDest)) File.Delete(fileDest);
- }
- catch (Exception ex)
- {
- Debug.WriteLine(ex);
- }
-
+ if (File.Exists(fileDest)) File.Delete(fileDest);
}
- catch (Exception exception)
+ catch (Exception ex)
{
- await this.MessageBoxError(exception.Message, "Unable to copy the file");
+ Debug.WriteLine(ex);
}
+
+ }
+ catch (Exception exception)
+ {
+ await this.MessageBoxError(exception.Message, "Unable to copy the file");
+ }
- if(copyResult && menuItem.Tag is DriveInfo removableDrive && OperatingSystem.IsWindows() && Settings.General.SendToPromptForRemovableDeviceEject)
- {
- if (await this.MessageBoxQuestion(
+ if(copyResult && menuItem.Tag is DriveInfo removableDrive && OperatingSystem.IsWindows() && Settings.General.SendToPromptForRemovableDeviceEject)
+ {
+ if (await this.MessageBoxQuestion(
$"File '{SlicerFile.Filename}' has copied successfully into {removableDrive.Name}\n" +
$"Do you want to eject the {removableDrive.Name} drive now?", "Copied ok, eject the drive?") == ButtonResult.Yes)
+ {
+ Progress.ResetAll($"Ejecting {removableDrive.Name}");
+ var ejectResult = await Task.Factory.StartNew(() =>
{
- Progress.ResetAll($"Ejecting {removableDrive.Name}");
- var ejectResult = await Task.Factory.StartNew(() =>
+ try
{
- try
- {
- return Core.SystemOS.Windows.USB.USBEject(removableDrive.Name);
- }
- catch (OperationCanceledException) { }
- catch (Exception exception)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(exception.Message, $"Unable to eject the drive {removableDrive.Name}"));
- }
-
- return false;
- });
-
- if (!ejectResult)
+ return Core.SystemOS.Windows.USB.USBEject(removableDrive.Name);
+ }
+ catch (OperationCanceledException) { }
+ catch (Exception exception)
{
- await this.MessageBoxError($"Unable to eject the drive {removableDrive.Name}\n\n" +
- "Possible causes:\n" +
- "- Drive may be busy or locked\n" +
- "- Drive was already ejected\n" +
- "- No permission to eject the drive\n" +
- "- Another error while trying to eject the drive\n\n" +
- "Please try to eject the drive manually.", $"Unable to eject the drive {removableDrive.Name}");
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(exception.Message, $"Unable to eject the drive {removableDrive.Name}"));
}
- }
- }
- }
-
-
- IsGUIEnabled = true;
- }
-
- protected override void OnOpened(EventArgs e)
- {
- base.OnOpened(e);
- var windowSize = this.GetScreenWorkingArea();
-
- if (Settings.General.StartMaximized
- || ClientSize.Width > windowSize.Width
- || ClientSize.Height > windowSize.Height)
- {
- WindowState = WindowState.Maximized;
- }
- AddLog($"{About.Software} start", Program.ProgramStartupTime.Elapsed.TotalSeconds);
-
- if (Settings.General.CheckForUpdatesOnStartup)
- {
- Task.Factory.StartNew(VersionChecker.Check);
- }
-
- ProcessFiles(Program.Args);
+ return false;
+ });
- if (!IsFileLoaded && Settings.General.LoadLastRecentFileOnStartup)
- {
- RecentFiles.Load();
- if (RecentFiles.Instance.Count > 0)
- {
- ProcessFile(Path.Combine(App.ApplicationPath, RecentFiles.Instance[0]));
+ if (!ejectResult)
+ {
+ await this.MessageBoxError($"Unable to eject the drive {removableDrive.Name}\n\n" +
+ "Possible causes:\n" +
+ "- Drive may be busy or locked\n" +
+ "- Drive was already ejected\n" +
+ "- No permission to eject the drive\n" +
+ "- Another error while trying to eject the drive\n\n" +
+ "Please try to eject the drive manually.", $"Unable to eject the drive {removableDrive.Name}");
+ }
}
}
+ }
- if (!IsFileLoaded && Settings.General.LoadDemoFileOnStartup)
- {
- ProcessFile(Path.Combine(App.ApplicationPath, About.DemoFile));
- }
- DispatcherTimer.Run(() =>
- {
- UpdateTitle();
- return true;
- }, TimeSpan.FromSeconds(1));
- Program.ProgramStartupTime.Stop();
- }
+ IsGUIEnabled = true;
+ }
- private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- Debug.WriteLine(e.PropertyName);
- /*if (e.PropertyName == nameof(ActualLayer))
- {
- LayerSlider.Value = ActualLayer;
- ShowLayer();
- return;
- }*/
- }
+ protected override void OnOpened(EventArgs e)
+ {
+ base.OnOpened(e);
+ var windowSize = this.GetScreenWorkingArea();
- private void InitializeComponent()
+ if (Settings.General.StartMaximized
+ || ClientSize.Width > windowSize.Width
+ || ClientSize.Height > windowSize.Height)
{
- AvaloniaXamlLoader.Load(this);
+ WindowState = WindowState.Maximized;
}
- #endregion
-
- #region Overrides
+ AddLog($"{About.Software} start", Program.ProgramStartupTime.Elapsed.TotalSeconds);
- protected override void OnPointerMoved(PointerEventArgs e)
+ if (Settings.General.CheckForUpdatesOnStartup)
{
- base.OnPointerMoved(e);
- _globalPointerEventArgs = e;
- _globalModifiers = e.KeyModifiers;
+ Task.Factory.StartNew(VersionChecker.Check);
}
- protected override void OnPointerPressed(PointerPressedEventArgs e)
+ ProcessFiles(Program.Args);
+
+ if (!IsFileLoaded && Settings.General.LoadLastRecentFileOnStartup)
{
- base.OnPointerPressed(e);
- _globalPointerPoint = e.GetCurrentPoint(this);
+ RecentFiles.Load();
+ if (RecentFiles.Instance.Count > 0)
+ {
+ ProcessFile(Path.Combine(App.ApplicationPath, RecentFiles.Instance[0]));
+ }
}
- protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ if (!IsFileLoaded && Settings.General.LoadDemoFileOnStartup)
{
- base.OnPointerReleased(e);
- _globalPointerPoint = e.GetCurrentPoint(this);
+ ProcessFile(Path.Combine(App.ApplicationPath, About.DemoFile));
}
+
+ DispatcherTimer.Run(() =>
+ {
+ UpdateTitle();
+ return true;
+ }, TimeSpan.FromSeconds(1));
+ Program.ProgramStartupTime.Stop();
+ }
- protected override void OnKeyDown(KeyEventArgs e)
+ private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ Debug.WriteLine(e.PropertyName);
+ /*if (e.PropertyName == nameof(ActualLayer))
{
- base.OnKeyDown(e);
- _globalModifiers = e.KeyModifiers;
- if (e.Handled
- || !IsFileLoaded
- || LayerImageBox.IsPanning
- || LayerImageBox.TrackerImage is not null
- || LayerImageBox.Cursor == StaticControls.CrossCursor
- || LayerImageBox.Cursor == StaticControls.HandCursor
- || LayerImageBox.SelectionMode == AdvancedImageBox.SelectionModes.Rectangle
- ) return;
+ LayerSlider.Value = ActualLayer;
+ ShowLayer();
+ return;
+ }*/
+ }
- var imageBoxMousePosition = _globalPointerEventArgs?.GetPosition(LayerImageBox) ?? new Point(-1, -1);
- if (imageBoxMousePosition.X < 0 || imageBoxMousePosition.Y < 0) return;
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ #endregion
- // Pixel Edit is active, Shift is down, and the cursor is over the image region.
- if (e.KeyModifiers == KeyModifiers.Shift)
- {
- if (IsPixelEditorActive)
- {
- LayerImageBox.AutoPan = false;
- LayerImageBox.Cursor = StaticControls.CrossCursor;
- TooltipOverlayText = "Pixel editing is on (SHIFT):\n" +
- "» Click over a pixel to draw\n" +
- "» Hold CTRL to clear pixels";
- UpdatePixelEditorCursor();
- }
- else
- {
- LayerImageBox.Cursor = StaticControls.CrossCursor;
- LayerImageBox.SelectionMode = AdvancedImageBox.SelectionModes.Rectangle;
- TooltipOverlayText = "ROI & Mask selection mode (SHIFT):\n" +
- "» Left-click drag to select a fixed ROI region\n" +
- "» Left-click + ALT drag to select specific objects ROI\n" +
- "» Right-click on a specific object to select it ROI\n" +
- "» Right-click + ALT on a specific object to select it as a Mask\n" +
- "» Right-click + ALT + CTRL on a specific object to select all it enclosing areas as a Mask\n" +
- "Press Esc to clear the ROI & Masks";
- }
+ #region Overrides
- IsTooltipOverlayVisible = Settings.LayerPreview.TooltipOverlay;
- e.Handled = true;
- return;
- }
- if (e.KeyModifiers == KeyModifiers.Control)
- {
- LayerImageBox.Cursor = StaticControls.HandCursor;
- LayerImageBox.AutoPan = false;
- TooltipOverlayText = "Issue selection mode:\n" +
- "» Click over an issue to select it";
+ protected override void OnPointerMoved(PointerEventArgs e)
+ {
+ base.OnPointerMoved(e);
+ _globalPointerEventArgs = e;
+ _globalModifiers = e.KeyModifiers;
+ }
- IsTooltipOverlayVisible = Settings.LayerPreview.TooltipOverlay;
- e.Handled = true;
- return;
- }
- }
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ base.OnPointerPressed(e);
+ _globalPointerPoint = e.GetCurrentPoint(this);
+ }
- protected override void OnKeyUp(KeyEventArgs e)
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ base.OnPointerReleased(e);
+ _globalPointerPoint = e.GetCurrentPoint(this);
+ }
+
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ _globalModifiers = e.KeyModifiers;
+ if (e.Handled
+ || !IsFileLoaded
+ || LayerImageBox.IsPanning
+ || LayerImageBox.TrackerImage is not null
+ || LayerImageBox.Cursor == StaticControls.CrossCursor
+ || LayerImageBox.Cursor == StaticControls.HandCursor
+ || LayerImageBox.SelectionMode == AdvancedImageBox.SelectionModes.Rectangle
+ ) return;
+
+ var imageBoxMousePosition = _globalPointerEventArgs?.GetPosition(LayerImageBox) ?? new Point(-1, -1);
+ if (imageBoxMousePosition.X < 0 || imageBoxMousePosition.Y < 0) return;
+
+ // Pixel Edit is active, Shift is down, and the cursor is over the image region.
+ if (e.KeyModifiers == KeyModifiers.Shift)
{
- _globalModifiers = e.KeyModifiers;
- if ((e.Key is Key.LeftShift or Key.RightShift || (e.KeyModifiers & KeyModifiers.Shift) != 0) &&
- (e.KeyModifiers & KeyModifiers.Control) != 0 &&
- e.Key == Key.Z)
+ if (IsPixelEditorActive)
{
- e.Handled = true;
- ClipboardUndo(true);
- return;
+ LayerImageBox.AutoPan = false;
+ LayerImageBox.Cursor = StaticControls.CrossCursor;
+ TooltipOverlayText = "Pixel editing is on (SHIFT):\n" +
+ "» Click over a pixel to draw\n" +
+ "» Hold CTRL to clear pixels";
+ UpdatePixelEditorCursor();
}
-
- if (e.Key is Key.LeftShift or Key.RightShift
- || (e.KeyModifiers & KeyModifiers.Shift) == 0 || (e.KeyModifiers & KeyModifiers.Control) == 0)
+ else
{
- LayerImageBox.TrackerImage = null;
- LayerImageBox.Cursor = StaticControls.ArrowCursor;
- LayerImageBox.AutoPan = true;
- LayerImageBox.SelectionMode = AdvancedImageBox.SelectionModes.None;
- IsTooltipOverlayVisible = false;
- e.Handled = true;
- return;
+ LayerImageBox.Cursor = StaticControls.CrossCursor;
+ LayerImageBox.SelectionMode = AdvancedImageBox.SelectionModes.Rectangle;
+ TooltipOverlayText = "ROI & Mask selection mode (SHIFT):\n" +
+ "» Left-click drag to select a fixed ROI region\n" +
+ "» Left-click + ALT drag to select specific objects ROI\n" +
+ "» Right-click on a specific object to select it ROI\n" +
+ "» Right-click + ALT on a specific object to select it as a Mask\n" +
+ "» Right-click + ALT + CTRL on a specific object to select all it enclosing areas as a Mask\n" +
+ "Press Esc to clear the ROI & Masks";
}
-
+ IsTooltipOverlayVisible = Settings.LayerPreview.TooltipOverlay;
+ e.Handled = true;
+ return;
+ }
- base.OnKeyUp(e);
+ if (e.KeyModifiers == KeyModifiers.Control)
+ {
+ LayerImageBox.Cursor = StaticControls.HandCursor;
+ LayerImageBox.AutoPan = false;
+ TooltipOverlayText = "Issue selection mode:\n" +
+ "» Click over an issue to select it";
+
+ IsTooltipOverlayVisible = Settings.LayerPreview.TooltipOverlay;
+ e.Handled = true;
+ return;
}
-
- public void OpenContextMenu(string name)
+ }
+
+ protected override void OnKeyUp(KeyEventArgs e)
+ {
+ _globalModifiers = e.KeyModifiers;
+ if ((e.Key is Key.LeftShift or Key.RightShift || (e.KeyModifiers & KeyModifiers.Shift) != 0) &&
+ (e.KeyModifiers & KeyModifiers.Control) != 0 &&
+ e.Key == Key.Z)
{
- var menu = this.FindControl<ContextMenu>($"{name}ContextMenu");
- if (menu is null) return;
- var parent = this.FindControl<Button>($"{name}Button");
- if (parent is null) return;
- menu.Open(parent);
+ e.Handled = true;
+ ClipboardUndo(true);
+ return;
}
+ if (e.Key is Key.LeftShift or Key.RightShift
+ || (e.KeyModifiers & KeyModifiers.Shift) == 0 || (e.KeyModifiers & KeyModifiers.Control) == 0)
+ {
+ LayerImageBox.TrackerImage = null;
+ LayerImageBox.Cursor = StaticControls.ArrowCursor;
+ LayerImageBox.AutoPan = true;
+ LayerImageBox.SelectionMode = AdvancedImageBox.SelectionModes.None;
+ IsTooltipOverlayVisible = false;
+ e.Handled = true;
+ return;
+ }
+
- #endregion
+ base.OnKeyUp(e);
+ }
+
+ public void OpenContextMenu(string name)
+ {
+ var menu = this.FindControl<ContextMenu>($"{name}ContextMenu");
+ if (menu is null) return;
+ var parent = this.FindControl<Button>($"{name}Button");
+ if (parent is null) return;
+ menu.Open(parent);
+ }
- #region Events
- public void MenuFileOpenClicked() => OpenFile();
- public void MenuFileOpenNewWindowClicked() => OpenFile(true);
- public void MenuFileOpenInPartialModeClicked() => OpenFile(false, FileFormat.FileDecodeType.Partial);
- public void MenuFileOpenContainingFolderClicked()
- {
- if (!IsFileLoaded) return;
- SystemAware.SelectFileOnExplorer(SlicerFile.FileFullPath);
- }
+ #endregion
+
+ #region Events
+
+ public void MenuFileOpenClicked() => OpenFile();
+ public void MenuFileOpenNewWindowClicked() => OpenFile(true);
+ public void MenuFileOpenInPartialModeClicked() => OpenFile(false, FileFormat.FileDecodeType.Partial);
+
+ public void MenuFileOpenContainingFolderClicked()
+ {
+ if (!IsFileLoaded) return;
+ SystemAware.SelectFileOnExplorer(SlicerFile.FileFullPath);
+ }
- public async void MenuFileSaveClicked()
+ public async void MenuFileSaveClicked()
+ {
+ if (!CanSave) return;
+ await SaveFile();
+ }
+
+ public async void MenuFileSaveAsClicked()
+ {
+ //await this.MessageBoxInfo(Path.Combine(App.ApplicationPath, "Assets", "Themes"));
+ if (!IsFileLoaded) return;
+ var filename = FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath, out var ext);
+ //var ext = Path.GetExtension(SlicerFile.FileFullPath);
+ //var extNoDot = ext.Remove(0, 1);
+ var extension = FileExtension.Find(ext);
+ if (extension is null)
{
- if (!CanSave) return;
- await SaveFile();
+ await this.MessageBoxError("Unable to find the target extension.", "Invalid extension");
+ return;
}
-
- public async void MenuFileSaveAsClicked()
+ SaveFileDialog dialog = new()
{
- //await this.MessageBoxInfo(Path.Combine(App.ApplicationPath, "Assets", "Themes"));
- if (!IsFileLoaded) return;
- var filename = FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath, out var ext);
- //var ext = Path.GetExtension(SlicerFile.FileFullPath);
- //var extNoDot = ext.Remove(0, 1);
- var extension = FileExtension.Find(ext);
- if (extension is null)
- {
- await this.MessageBoxError("Unable to find the target extension.", "Invalid extension");
- return;
- }
- SaveFileDialog dialog = new()
+ DefaultExtension = extension.Extension,
+ Filters = new List<FileDialogFilter>
{
- DefaultExtension = extension.Extension,
- Filters = new List<FileDialogFilter>
+ new()
{
- new()
+ Name = extension.Description,
+ Extensions = new List<string>
{
- Name = extension.Description,
- Extensions = new List<string>
- {
- ext
- }
+ ext
}
- },
- Directory = string.IsNullOrEmpty(Settings.General.DefaultDirectorySaveFile)
- ? Path.GetDirectoryName(SlicerFile.FileFullPath)
- : Settings.General.DefaultDirectorySaveFile,
- InitialFileName = $"{Settings.General.FileSaveNamePrefix}{filename}{Settings.General.FileSaveNameSuffix}"
- };
- var file = await dialog.ShowAsync(this);
- if (string.IsNullOrEmpty(file)) return;
- await SaveFile(file);
- }
+ }
+ },
+ Directory = string.IsNullOrEmpty(Settings.General.DefaultDirectorySaveFile)
+ ? Path.GetDirectoryName(SlicerFile.FileFullPath)
+ : Settings.General.DefaultDirectorySaveFile,
+ InitialFileName = $"{Settings.General.FileSaveNamePrefix}{filename}{Settings.General.FileSaveNameSuffix}"
+ };
+ var file = await dialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(file)) return;
+ await SaveFile(file);
+ }
- public async void OpenFile(bool newWindow = false, FileFormat.FileDecodeType fileDecodeType = FileFormat.FileDecodeType.Full)
+ public async void OpenFile(bool newWindow = false, FileFormat.FileDecodeType fileDecodeType = FileFormat.FileDecodeType.Full)
+ {
+ var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia);
+ var orderedFilters = new List<FileDialogFilter> {filters[Settings.General.DefaultOpenFileExtensionIndex]};
+ for (int i = 0; i < filters.Count; i++)
{
- var filters = Helpers.ToAvaloniaFileFilter(FileFormat.AllFileFiltersAvalonia);
- var orderedFilters = new List<FileDialogFilter> {filters[Settings.General.DefaultOpenFileExtensionIndex]};
- for (int i = 0; i < filters.Count; i++)
- {
- if(i == Settings.General.DefaultOpenFileExtensionIndex) continue;
- orderedFilters.Add(filters[i]);
- }
-
- var dialog = new OpenFileDialog
- {
- AllowMultiple = true,
- Filters = orderedFilters,
- Directory = Settings.General.DefaultDirectoryOpenFile
- };
- var files = await dialog.ShowAsync(this);
- ProcessFiles(files, newWindow, fileDecodeType);
+ if(i == Settings.General.DefaultOpenFileExtensionIndex) continue;
+ orderedFilters.Add(filters[i]);
}
- public async void OnMenuFileCloseFile()
+ var dialog = new OpenFileDialog
{
- if (CanSave && await this.MessageBoxQuestion("There are unsaved changes. Do you want close this file without saving?") !=
- ButtonResult.Yes)
- {
- return;
- }
-
- CloseFile();
- }
+ AllowMultiple = true,
+ Filters = orderedFilters,
+ Directory = Settings.General.DefaultDirectoryOpenFile
+ };
+ var files = await dialog.ShowAsync(this);
+ ProcessFiles(files, newWindow, fileDecodeType);
+ }
- public void CloseFile()
+ public async void OnMenuFileCloseFile()
+ {
+ if (CanSave && await this.MessageBoxQuestion("There are unsaved changes. Do you want close this file without saving?") !=
+ ButtonResult.Yes)
{
- if (SlicerFile is null) return;
+ return;
+ }
- MenuFileConvertItems = null;
+ CloseFile();
+ }
- ClipboardManager.Instance.Reset();
+ public void CloseFile()
+ {
+ if (SlicerFile is null) return;
- TabGCode.IsVisible = false;
+ MenuFileConvertItems = null;
- IssuesClear(true);
- SlicerFile?.Dispose();
- SlicerFile = null;
+ ClipboardManager.Instance.Reset();
- SlicerProperties.Clear();
- Drawings.Clear();
+ TabGCode.IsVisible = false;
- SelectedTabItem = TabInformation;
- _firstTimeOnIssues = true;
- IsPixelEditorActive = false;
- CanSave = false;
+ IssuesClear(true);
+ SlicerFile?.Dispose();
+ SlicerFile = null;
- _actualLayer = 0;
- LayerCache.Clear();
- _resinTrapDetectionStartLayer = 0;
+ SlicerProperties.Clear();
+ Drawings.Clear();
- VisibleThumbnailIndex = 0;
+ SelectedTabItem = TabInformation;
+ _firstTimeOnIssues = true;
+ IsPixelEditorActive = false;
+ CanSave = false;
- LayerImageBox.Image = null;
- LayerPixelPicker.Reset();
+ _actualLayer = 0;
+ LayerCache.Clear();
+ _resinTrapDetectionStartLayer = 0;
- ClearROIAndMask();
+ VisibleThumbnailIndex = 0;
- if(!Settings.Tools.LastUsedSettingsKeepOnCloseFile) OperationSessionManager.Instance.Clear();
- if(_menuFileOpenRecentItems.Length > 0)
- {
- _menuFileOpenRecentItems[0].IsEnabled = true; // Re-enable last file
- }
+ LayerImageBox.Image = null;
+ LayerPixelPicker.Reset();
- ResetDataContext();
- }
+ ClearROIAndMask();
- public void OnMenuFileFullscreen()
+ if(!Settings.Tools.LastUsedSettingsKeepOnCloseFile) OperationSessionManager.Instance.Clear();
+ if(_menuFileOpenRecentItems.Length > 0)
{
- WindowState = WindowState == WindowState.FullScreen ? WindowState.Maximized : WindowState.FullScreen;
+ _menuFileOpenRecentItems[0].IsEnabled = true; // Re-enable last file
}
- public async void MenuFileSettingsClicked()
+ ResetDataContext();
+ }
+
+ public void OnMenuFileFullscreen()
+ {
+ WindowState = WindowState == WindowState.FullScreen ? WindowState.Maximized : WindowState.FullScreen;
+ }
+
+ public async void MenuFileSettingsClicked()
+ {
+ var oldTheme = Settings.General.Theme;
+ var settingsWindow = new SettingsWindow();
+ await settingsWindow.ShowDialog(this);
+ if (settingsWindow.DialogResult == DialogResults.OK)
{
- var settingsWindow = new SettingsWindow();
- await settingsWindow.ShowDialog(this);
- if (settingsWindow.DialogResult == DialogResults.OK)
+ if (oldTheme != Settings.General.Theme)
{
- _layerNavigationSliderDebounceTimer.Interval = Settings.LayerPreview.LayerSliderDebounce == 0 ? 1 : Settings.LayerPreview.LayerSliderDebounce;
- RaisePropertyChanged(nameof(IssuesGridItems));
+ App.ApplyTheme();
}
- }
- public void OpenHomePage()
- {
- SystemAware.OpenBrowser(About.Website);
+ _layerNavigationSliderDebounceTimer.Interval = Settings.LayerPreview.LayerSliderDebounce == 0 ? 1 : Settings.LayerPreview.LayerSliderDebounce;
+ RaisePropertyChanged(nameof(IssuesGridItems));
}
+ }
- public void OpenDonateWebsite()
- {
- SystemAware.OpenBrowser(About.Donate);
- }
+ public void OpenHomePage()
+ {
+ SystemAware.OpenBrowser(About.Website);
+ }
- public void OpenWebsite(string url)
- {
- SystemAware.OpenBrowser(url);
- }
+ public void OpenDonateWebsite()
+ {
+ SystemAware.OpenBrowser(About.Donate);
+ }
- public async void MenuHelpAboutClicked()
- {
- await new AboutWindow().ShowDialog(this);
- }
+ public async void MenuHelpAboutClicked()
+ {
+ await new AboutWindow().ShowDialog(this);
+ }
- public async void MenuHelpBenchmarkClicked()
- {
- await new BenchmarkWindow().ShowDialog(this);
- }
+ public async void MenuHelpBenchmarkClicked()
+ {
+ await new BenchmarkWindow().ShowDialog(this);
+ }
- public void MenuHelpOpenSettingsFolderClicked()
- {
- SystemAware.StartProcess(UserSettings.SettingsFolder);
- }
+ public void MenuHelpOpenSettingsFolderClicked()
+ {
+ SystemAware.StartProcess(UserSettings.SettingsFolder);
+ }
- private async void MenuHelpMaterialManagerClicked()
- {
- await new MaterialManagerWindow().ShowDialog(this);
- }
+ private async void MenuHelpMaterialManagerClicked()
+ {
+ await new MaterialManagerWindow().ShowDialog(this);
+ }
- public async void MenuHelpInstallProfilesClicked()
+ public async void MenuHelpInstallProfilesClicked()
+ {
+ var PSFolder = App.GetPrusaSlicerDirectory();
+ if (string.IsNullOrEmpty(PSFolder) || !Directory.Exists(PSFolder))
{
- var PSFolder = App.GetPrusaSlicerDirectory();
- if (string.IsNullOrEmpty(PSFolder) || !Directory.Exists(PSFolder))
- {
- var SSFolder = App.GetPrusaSlicerDirectory(true);
- if (string.IsNullOrEmpty(SSFolder) || !Directory.Exists(SSFolder))
- {
- if (await this.MessageBoxQuestion(
- "Unable to detect PrusaSlicer nor SuperSlicer on your system, please ensure you have latest version installed.\n" +
- $"Was looking on: {PSFolder} and {SSFolder}\n\n" +
- "Click 'Yes' to open the PrusaSlicer webpage for download\n" +
- "Click 'No' to dismiss",
- "Unable to detect PrusaSlicer") == ButtonResult.Yes)
- SystemAware.OpenBrowser("https://www.prusa3d.com/prusaslicer/");
- return;
- }
+ var SSFolder = App.GetPrusaSlicerDirectory(true);
+ if (string.IsNullOrEmpty(SSFolder) || !Directory.Exists(SSFolder))
+ {
+ if (await this.MessageBoxQuestion(
+ "Unable to detect PrusaSlicer nor SuperSlicer on your system, please ensure you have latest version installed.\n" +
+ $"Was looking on: {PSFolder} and {SSFolder}\n\n" +
+ "Click 'Yes' to open the PrusaSlicer webpage for download\n" +
+ "Click 'No' to dismiss",
+ "Unable to detect PrusaSlicer") == ButtonResult.Yes)
+ SystemAware.OpenBrowser("https://www.prusa3d.com/prusaslicer/");
+ return;
}
- await new PrusaSlicerManagerWindow().ShowDialog(this);
}
+ await new PrusaSlicerManagerWindow().ShowDialog(this);
+ }
- public async void MenuNewVersionClicked()
- {
- var result =
- await this.MessageBoxQuestion(
- $"Do you like to auto-update {About.Software} v{App.VersionStr} to v{VersionChecker.Version}?\n" +
- "Yes: Auto update\n" +
- "No: Manual update\n" +
- "Cancel: No action\n\n" +
- "Changelog:\n" +
- $"{VersionChecker.Changelog}", $"Update UVtools to v{VersionChecker.Version}?", ButtonEnum.YesNoCancel);
+ public async void MenuNewVersionClicked()
+ {
+ var result =
+ await this.MessageBoxQuestion(
+ $"Do you like to auto-update {About.Software} v{App.VersionStr} to v{VersionChecker.Version}?\n" +
+ "Yes: Auto update\n" +
+ "No: Manual update\n" +
+ "Cancel: No action\n\n" +
+ "Changelog:\n" +
+ $"{VersionChecker.Changelog}", $"Update UVtools to v{VersionChecker.Version}?", ButtonEnum.YesNoCancel);
- if (result == ButtonResult.No)
- {
- SystemAware.OpenBrowser(VersionChecker.UrlLatestRelease);
- return;
- }
- if (result == ButtonResult.Yes)
- {
- IsGUIEnabled = false;
- ShowProgressWindow($"Downloading: {VersionChecker.Filename}");
+ if (result == ButtonResult.No)
+ {
+ SystemAware.OpenBrowser(VersionChecker.UrlLatestRelease);
+ return;
+ }
+ if (result == ButtonResult.Yes)
+ {
+ IsGUIEnabled = false;
+ ShowProgressWindow($"Downloading: {VersionChecker.Filename}");
- await VersionChecker.AutoUpgrade(Progress);
+ await VersionChecker.AutoUpgrade(Progress);
- /*var task = await Task.Factory.StartNew( () =>
+ /*var task = await Task.Factory.StartNew( () =>
+ {
+ try
{
- try
- {
- VersionChecker.AutoUpgrade(Progress);
- return true;
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception exception)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(exception.ToString(), "Error opening the file"));
- }
+ VersionChecker.AutoUpgrade(Progress);
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception exception)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(exception.ToString(), "Error opening the file"));
+ }
- return false;
- });
- */
- IsGUIEnabled = true;
+ return false;
+ });
+ */
+ IsGUIEnabled = true;
- return;
- }
+ return;
+ }
- }
+ }
+
+ #endregion
- #endregion
+ #region Methods
- #region Methods
+ private void UpdateTitle()
+ {
+ var title = $"{About.Software} ";
- private void UpdateTitle()
+ if (IsFileLoaded)
{
- var title = $"{About.Software} ";
+ title += $"File: {SlicerFile.Filename} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) ";
+ }
+
+ title += $"Version: {App.VersionStr} RAM: {SizeExtensions.SizeSuffix(Environment.WorkingSet)}";
- if (IsFileLoaded)
+ if (IsFileLoaded)
+ {
+ if (CanSave)
{
- title += $"File: {SlicerFile.Filename} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) ";
+ title += " [UNSAVED]";
}
- title += $"Version: {App.VersionStr} RAM: {SizeExtensions.SizeSuffix(Environment.WorkingSet)}";
-
- if (IsFileLoaded)
+ if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial)
{
- if (CanSave)
- {
- title += " [UNSAVED]";
- }
-
- if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial)
- {
- title += " [PARTIAL MODE]";
- }
+ title += " [PARTIAL MODE]";
}
+ }
#if DEBUG
- title += " [DEBUG]";
+ title += " [DEBUG]";
#endif
- Title = title;
- }
+ Title = title;
+ }
- public async void ProcessFiles(string[] files, bool openNewWindow = false, FileFormat.FileDecodeType fileDecodeType = FileFormat.FileDecodeType.Full)
+ public async void ProcessFiles(string[] files, bool openNewWindow = false, FileFormat.FileDecodeType fileDecodeType = FileFormat.FileDecodeType.Full)
+ {
+ if (files is null || files.Length == 0) return;
+
+ for (int i = 0; i < files.Length; i++)
{
- if (files is null || files.Length == 0) return;
+ if (!File.Exists(files[i])) continue;
- for (int i = 0; i < files.Length; i++)
+ if (files[i].EndsWith(".uvtop"))
{
- if (!File.Exists(files[i])) continue;
-
- if (files[i].EndsWith(".uvtop"))
+ if(!IsFileLoaded) continue;
+ try
{
- if(!IsFileLoaded) continue;
- try
- {
- var operation = Operation.Deserialize(files[i]);
- await ShowRunOperation(operation);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
-
- continue;
+ var operation = Operation.Deserialize(files[i]);
+ await ShowRunOperation(operation);
}
-
-
- if (i == 0 && !openNewWindow && (_globalModifiers & KeyModifiers.Shift) == 0)
+ catch (Exception e)
{
- ProcessFile(files[i], fileDecodeType);
- continue;
+ Debug.WriteLine(e);
+ throw;
}
+
+ continue;
+ }
- App.NewInstance(files[i]);
+ if (i == 0 && !openNewWindow && (_globalModifiers & KeyModifiers.Shift) == 0)
+ {
+ ProcessFile(files[i], fileDecodeType);
+ continue;
}
- }
- void ReloadFile() => ReloadFile(_actualLayer);
+ App.NewInstance(files[i]);
- void ReloadFile(uint actualLayer)
- {
- if (!IsFileLoaded) return;
- ProcessFile(SlicerFile.FileFullPath, SlicerFile.DecodeType, _actualLayer);
}
+ }
- void ProcessFile(string fileName, uint actualLayer = 0) => ProcessFile(fileName, FileFormat.FileDecodeType.Full, actualLayer);
- async void ProcessFile(string fileName, FileFormat.FileDecodeType fileDecodeType, uint actualLayer = 0)
- {
- if (!File.Exists(fileName)) return;
- CloseFile();
- var fileNameOnly = Path.GetFileName(fileName);
- SlicerFile = FileFormat.FindByExtensionOrFilePath(fileName, true);
- if (SlicerFile is null) return;
+ void ReloadFile() => ReloadFile(_actualLayer);
- IsGUIEnabled = false;
- ShowProgressWindow($"Opening: {fileNameOnly}");
-
- var task = await Task.Factory.StartNew( () =>
- {
- try
- {
- SlicerFile.Decode(fileName, fileDecodeType, Progress);
- return true;
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception exception)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(exception.ToString(), "Error opening the file"));
- }
+ void ReloadFile(uint actualLayer)
+ {
+ if (!IsFileLoaded) return;
+ ProcessFile(SlicerFile.FileFullPath, SlicerFile.DecodeType, _actualLayer);
+ }
- return false;
- });
+ void ProcessFile(string fileName, uint actualLayer = 0) => ProcessFile(fileName, FileFormat.FileDecodeType.Full, actualLayer);
+ async void ProcessFile(string fileName, FileFormat.FileDecodeType fileDecodeType, uint actualLayer = 0)
+ {
+ if (!File.Exists(fileName)) return;
+ CloseFile();
+ var fileNameOnly = Path.GetFileName(fileName);
+ SlicerFile = FileFormat.FindByExtensionOrFilePath(fileName, true);
+ if (SlicerFile is null) return;
- IsGUIEnabled = true;
+ IsGUIEnabled = false;
+ ShowProgressWindow($"Opening: {fileNameOnly}");
- if (!task)
+ var task = await Task.Factory.StartNew( () =>
+ {
+ try
{
- SlicerFile.Dispose();
- SlicerFile = null;
- return;
+ SlicerFile.Decode(fileName, fileDecodeType, Progress);
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception exception)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(exception.ToString(), "Error opening the file"));
}
- AddRecentFile(fileName);
+ return false;
+ });
+
+ IsGUIEnabled = true;
+
+ if (!task)
+ {
+ SlicerFile.Dispose();
+ SlicerFile = null;
+ return;
+ }
+
+ AddRecentFile(fileName);
+
+ if (SlicerFile.LayerCount == 0)
+ {
+ await this.MessageBoxError("It seems this file has no layers. Possible causes could be:\n" +
+ "- File is empty\n" +
+ "- File is corrupted\n" +
+ "- File has not been sliced\n" +
+ "- An internal programing error\n\n" +
+ "Please check your file and retry", "Error reading file");
+ SlicerFile.Dispose();
+ SlicerFile = null;
+ return;
+ }
- if (SlicerFile.LayerCount == 0)
+ if (Settings.Automations.AutoConvertFiles && SlicerFile.DecodeType == FileFormat.FileDecodeType.Full)
+ {
+ string convertFileExtension = null;
+ switch (SlicerFile)
{
- await this.MessageBoxError("It seems this file has no layers. Possible causes could be:\n" +
- "- File is empty\n" +
- "- File is corrupted\n" +
- "- File has not been sliced\n" +
- "- An internal programing error\n\n" +
- "Please check your file and retry", "Error reading file");
- SlicerFile.Dispose();
- SlicerFile = null;
- return;
+ case SL1File sl1File:
+ convertFileExtension = sl1File.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null);
+ break;
+ case VDTFile vdtFile:
+ if (string.IsNullOrWhiteSpace(vdtFile.ManifestFile.Machine.UVToolsConvertTo) || vdtFile.ManifestFile.Machine.UVToolsConvertTo == "None")
+ convertFileExtension = vdtFile.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null);
+ else
+ convertFileExtension = vdtFile.ManifestFile.Machine.UVToolsConvertTo;
+ break;
}
- if (Settings.Automations.AutoConvertFiles && SlicerFile.DecodeType == FileFormat.FileDecodeType.Full)
+ if (!string.IsNullOrWhiteSpace(convertFileExtension))
{
- string convertFileExtension = null;
- switch (SlicerFile)
+ convertFileExtension = convertFileExtension.ToLower(CultureInfo.InvariantCulture);
+ var fileExtension = FileFormat.FindExtension(convertFileExtension);
+ //var convertToFormat = FileFormat.FindByExtensionOrFilePath(convertFileExtension);
+ var convertToFormat = fileExtension?.GetFileFormat();
+ if (fileExtension is not null && convertToFormat is not null)
{
- case SL1File sl1File:
- convertFileExtension = sl1File.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null);
- break;
- case VDTFile vdtFile:
- if (string.IsNullOrWhiteSpace(vdtFile.ManifestFile.Machine.UVToolsConvertTo) || vdtFile.ManifestFile.Machine.UVToolsConvertTo == "None")
- convertFileExtension = vdtFile.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null);
- else
- convertFileExtension = vdtFile.ManifestFile.Machine.UVToolsConvertTo;
- break;
- }
+ var directory = SlicerFile.DirectoryPath;
+ var filename = FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath);
+ var targetFilename = $"{filename}.{convertFileExtension}";
+ var outputFile = Path.Combine(directory, targetFilename);
+ FileFormat convertedFile = null;
- if (!string.IsNullOrWhiteSpace(convertFileExtension))
- {
- convertFileExtension = convertFileExtension.ToLower(CultureInfo.InvariantCulture);
- var fileExtension = FileFormat.FindExtension(convertFileExtension);
- //var convertToFormat = FileFormat.FindByExtensionOrFilePath(convertFileExtension);
- var convertToFormat = fileExtension?.GetFileFormat();
- if (fileExtension is not null && convertToFormat is not null)
+ bool canConvert = true;
+ if (File.Exists(outputFile))
{
- var directory = SlicerFile.DirectoryPath;
- var filename = FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath);
- var targetFilename = $"{filename}.{convertFileExtension}";
- var outputFile = Path.Combine(directory, targetFilename);
- FileFormat convertedFile = null;
-
- bool canConvert = true;
- if (File.Exists(outputFile))
+ var result = await this.MessageBoxQuestion(
+ $"The file '{SlicerFile.Filename}' is about to get auto-converted to '{targetFilename}'.\n" +
+ $"But a file with same name already exists on the output directory '{directory}'.\n" +
+ "Do you want to overwrite the existing file?\n\n" +
+ "Yes: Overwrite the file.\n" +
+ "No: Choose a location for the file.\n" +
+ "Cancel: Do not auto-convert the file.",
+
+ $"File '{SlicerFile.Filename}' already exists",
+ ButtonEnum.YesNoCancel);
+
+ if (result is ButtonResult.Cancel or ButtonResult.Abort)
{
- var result = await this.MessageBoxQuestion(
- $"The file '{SlicerFile.Filename}' is about to get auto-converted to '{targetFilename}'.\n" +
- $"But a file with same name already exists on the output directory '{directory}'.\n" +
- "Do you want to overwrite the existing file?\n\n" +
- "Yes: Overwrite the file.\n" +
- "No: Choose a location for the file.\n" +
- "Cancel: Do not auto-convert the file.",
-
- $"File '{SlicerFile.Filename}' already exists",
- ButtonEnum.YesNoCancel);
-
- if (result is ButtonResult.Cancel or ButtonResult.Abort)
- {
- canConvert = false;
- }
- else if (result == ButtonResult.No)
+ canConvert = false;
+ }
+ else if (result == ButtonResult.No)
+ {
+ var dialog = new SaveFileDialog
{
- var dialog = new SaveFileDialog
+ Directory = directory,
+ InitialFileName = filename,
+ DefaultExtension = $".{convertFileExtension}",
+ Filters = new List<FileDialogFilter>
{
- Directory = directory,
- InitialFileName = filename,
- DefaultExtension = $".{convertFileExtension}",
- Filters = new List<FileDialogFilter>
+ new()
{
- new()
- {
- Name = fileExtension.Description,
- Extensions = new List<string>{ convertFileExtension }
- }
+ Name = fileExtension.Description,
+ Extensions = new List<string>{ convertFileExtension }
}
- };
-
- var saveResult = await dialog.ShowAsync(this);
- if (string.IsNullOrWhiteSpace(saveResult))
- {
- canConvert = false;
}
- else
- {
- outputFile = saveResult;
- }
- }
- }
-
- if (canConvert)
- {
- IsGUIEnabled = false;
- ShowProgressWindow(
- $"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {convertFileExtension}");
+ };
- task = await Task.Factory.StartNew(() =>
+ var saveResult = await dialog.ShowAsync(this);
+ if (string.IsNullOrWhiteSpace(saveResult))
{
- try
- {
- convertedFile = SlicerFile.Convert(convertToFormat, outputFile, 0, Progress);
- return true;
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception exception)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(exception.ToString(),
- "Error while converting the file"));
- }
-
- return false;
- });
-
- IsGUIEnabled = true;
-
- if (task && convertedFile is not null)
+ canConvert = false;
+ }
+ else
{
- SlicerFile = convertedFile;
- AddRecentFile(SlicerFile.FileFullPath);
+ outputFile = saveResult;
}
}
}
- }
- }
- bool modified = false;
- if (Settings.Automations.LightOffDelaySetMode != Enumerations.LightOffDelaySetMode.NoAction &&
- SlicerFile.CanUseBottomLightOffDelay &&
- (Settings.Automations.ChangeOnlyLightOffDelayIfZero && SlicerFile.BottomLightOffDelay == 0 || !Settings.Automations.ChangeOnlyLightOffDelayIfZero))
- {
- if ((SlicerFile.CanUseAnyWaitTimeBeforeCure || SlicerFile.CanUseAnyWaitTimeAfterCure || SlicerFile.CanUseAnyWaitTimeAfterLift) &&
- (SlicerFile.BottomWaitTimeBeforeCure > 0 || SlicerFile.BottomWaitTimeAfterCure > 0 || SlicerFile.BottomWaitTimeAfterLift > 0))
- {
- // Ignore this automation
- }
- else
- {
- float lightOff = Settings.Automations.LightOffDelaySetMode switch
- {
- Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay => SlicerFile.CalculateBottomLightOffDelay((float)Settings.Automations.BottomLightOffDelay),
- Enumerations.LightOffDelaySetMode.UpdateWithoutExtraDelay => SlicerFile.CalculateBottomLightOffDelay(),
- _ => 0
- };
- if (lightOff != SlicerFile.BottomLightOffDelay)
+ if (canConvert)
{
- modified = true;
- SlicerFile.BottomLightOffDelay = lightOff;
- }
- }
-
- }
+ IsGUIEnabled = false;
+ ShowProgressWindow(
+ $"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {convertFileExtension}");
- if (Settings.Automations.LightOffDelaySetMode != Enumerations.LightOffDelaySetMode.NoAction &&
- SlicerFile.CanUseLightOffDelay &&
- (Settings.Automations.ChangeOnlyLightOffDelayIfZero && SlicerFile.LightOffDelay == 0 || !Settings.Automations.ChangeOnlyLightOffDelayIfZero))
- {
- if ((SlicerFile.CanUseAnyWaitTimeBeforeCure || SlicerFile.CanUseAnyWaitTimeAfterCure || SlicerFile.CanUseAnyWaitTimeAfterLift) &&
- (SlicerFile.WaitTimeBeforeCure > 0 || SlicerFile.WaitTimeAfterCure > 0 || SlicerFile.WaitTimeAfterLift > 0))
- {
- // Ignore this automation
- }
- else
- {
- float lightOff = Settings.Automations.LightOffDelaySetMode switch
- {
- Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay => SlicerFile.CalculateNormalLightOffDelay((float)Settings.Automations.LightOffDelay),
- Enumerations.LightOffDelaySetMode.UpdateWithoutExtraDelay => SlicerFile.CalculateNormalLightOffDelay(),
- _ => 0
- };
- if (lightOff != SlicerFile.LightOffDelay)
- {
- modified = true;
- SlicerFile.LightOffDelay = lightOff;
+ task = await Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ convertedFile = SlicerFile.Convert(convertToFormat, outputFile, 0, Progress);
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception exception)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(exception.ToString(),
+ "Error while converting the file"));
+ }
+
+ return false;
+ });
+
+ IsGUIEnabled = true;
+
+ if (task && convertedFile is not null)
+ {
+ SlicerFile = convertedFile;
+ AddRecentFile(SlicerFile.FileFullPath);
+ }
}
}
}
+ }
- if (modified)
+ /*bool modified = false;
+ if (modified)
+ {
+ CanSave = true;
+ if (Settings.Automations.SaveFileAfterModifications)
{
- CanSave = true;
- if (Settings.Automations.SaveFileAfterModifications)
- {
- var saveCount = _savesCount;
- await SaveFile(null, true);
- _savesCount = saveCount;
- }
+ var saveCount = _savesCount;
+ await SaveFile(null, true);
+ _savesCount = saveCount;
}
+ }*/
- Clipboard.Init(SlicerFile);
+ Clipboard.Init(SlicerFile);
- if (SlicerFile is not ImageFile && SlicerFile.DecodeType == FileFormat.FileDecodeType.Full)
+ if (SlicerFile is not ImageFile && SlicerFile.DecodeType == FileFormat.FileDecodeType.Full)
+ {
+ List<MenuItem> menuItems = new();
+ foreach (var fileFormat in FileFormat.AvailableFormats)
{
- List<MenuItem> menuItems = new();
- foreach (var fileFormat in FileFormat.AvailableFormats)
+ if(fileFormat is ImageFile) continue;
+ foreach (var fileExtension in fileFormat.FileExtensions)
{
- if(fileFormat is ImageFile) continue;
- foreach (var fileExtension in fileFormat.FileExtensions)
- {
- if(!fileExtension.IsVisibleOnConvertMenu) continue;
-
- var menuItem = new MenuItem
- {
- Header = fileExtension.Description,
- Tag = fileExtension
- };
-
- menuItem.Tapped += ConvertToOnTapped;
-
- menuItems.Add(menuItem);
- }
- /*string extensions = fileFormat.FileExtensions.Length > 0
- ? $" ({fileFormat.GetFileExtensions()})"
- : string.Empty;
+ if(!fileExtension.IsVisibleOnConvertMenu) continue;
var menuItem = new MenuItem
{
- Header = fileFormat.GetType().Name.Replace("File", extensions),
- Tag = fileFormat
+ Header = fileExtension.Description,
+ Tag = fileExtension
};
menuItem.Tapped += ConvertToOnTapped;
- menuItems.Add(menuItem);*/
+ menuItems.Add(menuItem);
}
+ /*string extensions = fileFormat.FileExtensions.Length > 0
+ ? $" ({fileFormat.GetFileExtensions()})"
+ : string.Empty;
- MenuFileConvertItems = menuItems.ToArray();
+ var menuItem = new MenuItem
+ {
+ Header = fileFormat.GetType().Name.Replace("File", extensions),
+ Tag = fileFormat
+ };
+
+ menuItem.Tapped += ConvertToOnTapped;
+
+ menuItems.Add(menuItem);*/
}
- using var mat = SlicerFile.FirstLayer?.LayerMat;
+ MenuFileConvertItems = menuItems.ToArray();
+ }
+
+ using var mat = SlicerFile.FirstLayer?.LayerMat;
- VisibleThumbnailIndex = 1;
+ VisibleThumbnailIndex = 1;
- UpdateTitle();
+ UpdateTitle();
- if (mat is not null)
+ if (mat is not null)
+ {
+ if (Settings.LayerPreview.AutoRotateLayerBestView)
{
- if (Settings.LayerPreview.AutoRotateLayerBestView)
+ _showLayerImageRotated = mat.Height > mat.Width;
+ }
+
+ if (Settings.LayerPreview.AutoFlipLayerIfMirrored)
+ {
+ if (SlicerFile.DisplayMirror == Enumerations.FlipDirection.None)
{
- _showLayerImageRotated = mat.Height > mat.Width;
+ _showLayerImageFlipped = false;
}
-
- if (Settings.LayerPreview.AutoFlipLayerIfMirrored)
+ else
{
- if (SlicerFile.DisplayMirror == Enumerations.FlipDirection.None)
- {
- _showLayerImageFlipped = false;
- }
- else
- {
- _showLayerImageFlipped = true;
- _showLayerImageFlippedHorizontally = SlicerFile.DisplayMirror is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both;
- _showLayerImageFlippedVertically = SlicerFile.DisplayMirror is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both;
- }
+ _showLayerImageFlipped = true;
+ _showLayerImageFlippedHorizontally = SlicerFile.DisplayMirror is Enumerations.FlipDirection.Horizontally or Enumerations.FlipDirection.Both;
+ _showLayerImageFlippedVertically = SlicerFile.DisplayMirror is Enumerations.FlipDirection.Vertically or Enumerations.FlipDirection.Both;
}
}
+ }
- if (mat is not null && mat.Size != SlicerFile.Resolution)
- {
- var result = await this.MessageBoxWaring($"Layer image resolution of {mat.Size} mismatch with printer resolution of {SlicerFile.Resolution}.\n" +
- "1) Printing this file can lead to problems or malformed model, please verify your slicer printer settings;\n" +
- "2) Processing this file with some of the tools can lead to program crash or misfunction;\n" +
- "3) If you used PrusaSlicer to slice this file, you must use it with compatible UVtools printer profiles (Help - Install profiles into PrusaSlicer).\n\n" +
- "Click 'Yes' to auto fix and set the file resolution with the layer resolution, but only use this option if you are sure it's ok to!\n" +
- "Click 'No' to continue as it is and ignore this warning, you can still repair issues and use some of the tools.",
- "File and layer resolution mismatch!", ButtonEnum.YesNo);
- if(result == ButtonResult.Yes)
- {
- SlicerFile.Resolution = mat.Size;
- RaisePropertyChanged(nameof(LayerResolutionStr));
- }
+ if (mat is not null && mat.Size != SlicerFile.Resolution)
+ {
+ var result = await this.MessageBoxWaring($"Layer image resolution of {mat.Size} mismatch with printer resolution of {SlicerFile.Resolution}.\n" +
+ "1) Printing this file can lead to problems or malformed model, please verify your slicer printer settings;\n" +
+ "2) Processing this file with some of the tools can lead to program crash or misfunction;\n" +
+ "3) If you used PrusaSlicer to slice this file, you must use it with compatible UVtools printer profiles (Help - Install profiles into PrusaSlicer).\n\n" +
+ "Click 'Yes' to auto fix and set the file resolution with the layer resolution, but only use this option if you are sure it's ok to!\n" +
+ "Click 'No' to continue as it is and ignore this warning, you can still repair issues and use some of the tools.",
+ "File and layer resolution mismatch!", ButtonEnum.YesNo);
+ if(result == ButtonResult.Yes)
+ {
+ SlicerFile.Resolution = mat.Size;
+ RaisePropertyChanged(nameof(LayerResolutionStr));
}
+ }
- if (SlicerFile.LayerHeight <= 0)
- {
- /*await this.MessageBoxWaring(
- $"This file have a incorrect or not present layer height of {SlicerFile.LayerHeight}mm\n" +
- $"It may not be required for some printers to work properly, but this information is crucial to {About.Software}.\n",
- "Incorrect layer height detected");*/
+ if (SlicerFile.LayerHeight <= 0)
+ {
+ /*await this.MessageBoxWaring(
+ $"This file have a incorrect or not present layer height of {SlicerFile.LayerHeight}mm\n" +
+ $"It may not be required for some printers to work properly, but this information is crucial to {About.Software}.\n",
+ "Incorrect layer height detected");*/
- await new MissingInformationWindow().ShowDialog(this);
- }
+ await new MissingInformationWindow().ShowDialog(this);
+ }
- if (SlicerFile.LayerHeight <= 0)
- {
- await this.MessageBoxWaring(
- $"This file have a incorrect or not present layer height of {SlicerFile.LayerHeight}mm\n" +
- $"It may not be required for some printers to work properly, but this information is crucial to {About.Software}.\n",
- "Incorrect layer height detected");
- }
+ if (SlicerFile.LayerHeight <= 0)
+ {
+ await this.MessageBoxWaring(
+ $"This file have a incorrect or not present layer height of {SlicerFile.LayerHeight}mm\n" +
+ $"It may not be required for some printers to work properly, but this information is crucial to {About.Software}.\n",
+ "Incorrect layer height detected");
+ }
- var display = SlicerFile.Display;
- if (!display.HaveZero() &&
- //SlicerFile is not LGSFile &&
- ((SlicerFile.ResolutionX > SlicerFile.ResolutionY &&
- SlicerFile.DisplayWidth < SlicerFile.DisplayHeight)
- || (SlicerFile.ResolutionX < SlicerFile.ResolutionY &&
- SlicerFile.DisplayWidth > SlicerFile.DisplayHeight)))
- {
- var ppm = SlicerFile.Ppmm;
- var ppmMax = ppm.Max();
- var xRatio = Math.Round(ppmMax - ppm.Width + 1);
- var yRatio = Math.Round(ppmMax - ppm.Height + 1);
- await this.MessageBoxWaring(
- "It looks like this file was sliced with an incorrect image ratio.\n" +
- "Printing this file may produce a stretched model with wrong proportions or a failed print.\n" +
- "Please go back to Slicer configuration and validate the printer resolution and display size.\n\n" +
- $"Resolution: {SlicerFile.Resolution}\n" +
- $"Display: {display}\n" +
- $"Ratio: {xRatio}:{yRatio}\n",
- "Incorrect image ratio detected");
- }
- RefreshProperties();
- ResetDataContext();
+ var display = SlicerFile.Display;
+ if (!display.HaveZero() &&
+ //SlicerFile is not LGSFile &&
+ ((SlicerFile.ResolutionX > SlicerFile.ResolutionY &&
+ SlicerFile.DisplayWidth < SlicerFile.DisplayHeight)
+ || (SlicerFile.ResolutionX < SlicerFile.ResolutionY &&
+ SlicerFile.DisplayWidth > SlicerFile.DisplayHeight)))
+ {
+ var ppm = SlicerFile.Ppmm;
+ var ppmMax = ppm.Max();
+ var xRatio = Math.Round(ppmMax - ppm.Width + 1);
+ var yRatio = Math.Round(ppmMax - ppm.Height + 1);
+ await this.MessageBoxWaring(
+ "It looks like this file was sliced with an incorrect image ratio.\n" +
+ "Printing this file may produce a stretched model with wrong proportions or a failed print.\n" +
+ "Please go back to Slicer configuration and validate the printer resolution and display size.\n\n" +
+ $"Resolution: {SlicerFile.Resolution}\n" +
+ $"Display: {display}\n" +
+ $"Ratio: {xRatio}:{yRatio}\n",
+ "Incorrect image ratio detected");
+ }
+ RefreshProperties();
+ ResetDataContext();
- ForceUpdateActualLayer(actualLayer.Clamp(actualLayer, SliderMaximumValue));
+ ForceUpdateActualLayer(actualLayer.Clamp(actualLayer, SliderMaximumValue));
- if (Settings.LayerPreview.ZoomToFitPrintVolumeBounds)
- {
- ZoomToFit();
- }
+ if (Settings.LayerPreview.ZoomToFitPrintVolumeBounds)
+ {
+ ZoomToFit();
+ }
- SlicerFile.IssueManager.CollectionChanged += (sender, e) =>
- {
- UpdateLayerTrackerHighlightIssues();
- };
+ SlicerFile.IssueManager.CollectionChanged += (sender, e) =>
+ {
+ UpdateLayerTrackerHighlightIssues();
+ };
- if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Full)
+ if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Full)
+ {
+ if (Settings.Issues.ComputeIssuesOnLoad)
{
- if (Settings.Issues.ComputeIssuesOnLoad)
+ _firstTimeOnIssues = false;
+ await OnClickDetectIssues();
+ if (SlicerFile.IssueManager.Count > 0)
{
- _firstTimeOnIssues = false;
- await OnClickDetectIssues();
- if (SlicerFile.IssueManager.Count > 0)
- {
- SelectedTabItem = TabIssues;
- if (Settings.Issues.AutoRepairIssuesOnLoad)
- await RunOperation(ToolRepairLayersControl.GetOperationRepairLayers());
- }
+ SelectedTabItem = TabIssues;
+ if (Settings.Issues.AutoRepairIssuesOnLoad)
+ await RunOperation(ToolRepairLayersControl.GetOperationRepairLayers());
}
- else
+ }
+ else
+ {
+ await ComputeIssues(
+ GetIslandDetectionConfiguration(false),
+ GetOverhangDetectionConfiguration(false),
+ GetResinTrapDetectionConfiguration(false),
+ GetTouchingBoundsDetectionConfiguration(false),
+ GetPrintHeightDetectionConfiguration(true),
+ true);
+ if (SlicerFile.IssueManager.Count > 0)
{
- await ComputeIssues(
- GetIslandDetectionConfiguration(false),
- GetOverhangDetectionConfiguration(false),
- GetResinTrapDetectionConfiguration(false),
- GetTouchingBoundsDetectionConfiguration(false),
- GetPrintHeightDetectionConfiguration(true),
- true);
- if (SlicerFile.IssueManager.Count > 0)
- {
- SelectedTabItem = TabIssues;
- }
+ SelectedTabItem = TabIssues;
}
}
+ }
- TabGCode.IsVisible = HaveGCode;
+ TabGCode.IsVisible = HaveGCode;
- SlicerFile.PropertyChanged += SlicerFileOnPropertyChanged;
+ SlicerFile.PropertyChanged += SlicerFileOnPropertyChanged;
- PopulateSuggestions();
+ PopulateSuggestions();
-#if !DEBUG
- if (SlicerFile is CTBEncryptedFile)
- {
- await this.MessageBoxInfo(CTBEncryptedFile.Preamble, "Information");
- }
-#endif
+ if (SlicerFile is CTBEncryptedFile)
+ {
+ await this.MessageBoxInfo(CTBEncryptedFile.Preamble, "Information");
}
+ }
- private void SlicerFileOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ private void SlicerFileOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(SlicerFile.Thumbnails))
{
- if (e.PropertyName == nameof(SlicerFile.Thumbnails))
- {
- RefreshThumbnail();
- return;
- }
- if (e.PropertyName == nameof(SlicerFile.Resolution))
- {
- RaisePropertyChanged(nameof(LayerResolutionStr));
- return;
- }
- if (e.PropertyName == nameof(SlicerFile.Ppmm))
- {
- RaisePropertyChanged(nameof(LayerZoomStr));
- return;
- }
+ RefreshThumbnail();
+ return;
+ }
+ if (e.PropertyName == nameof(SlicerFile.Resolution))
+ {
+ RaisePropertyChanged(nameof(LayerResolutionStr));
+ return;
+ }
+ if (e.PropertyName == nameof(SlicerFile.Ppmm))
+ {
+ RaisePropertyChanged(nameof(LayerZoomStr));
+ return;
}
+ }
- private async void ShowProgressWindow(string title, bool canCancel = true)
+ private async void ShowProgressWindow(string title, bool canCancel = true)
+ {
+ if (Dispatcher.UIThread.CheckAccess())
{
- if (Dispatcher.UIThread.CheckAccess())
- {
- ProgressShow(title, canCancel);
+ ProgressShow(title, canCancel);
- //ProgressWindow.SetTitle(title);
- //await ProgressWindow.ShowDialog(this);
- }
- else
+ //ProgressWindow.SetTitle(title);
+ //await ProgressWindow.ShowDialog(this);
+ }
+ else
+ {
+ await Dispatcher.UIThread.InvokeAsync(() =>
{
- await Dispatcher.UIThread.InvokeAsync(() =>
+ ProgressShow(title, canCancel);
+ /*try
{
- ProgressShow(title, canCancel);
- /*try
- {
-
- //ProgressWindow.SetTitle(title);
- //await ProgressWindow.ShowDialog(this);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }*/
- });
- }
+ //ProgressWindow.SetTitle(title);
+ //await ProgressWindow.ShowDialog(this);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }*/
+
+ });
}
+ }
- private async void ConvertToOnTapped(object? sender, RoutedEventArgs e)
- {
- if (sender is not MenuItem item) return;
- if (item.Tag is not FileExtension fileExtension) return;
+ private async void ConvertToOnTapped(object? sender, RoutedEventArgs e)
+ {
+ if (sender is not MenuItem item) return;
+ if (item.Tag is not FileExtension fileExtension) return;
- var fileFormat = fileExtension.GetFileFormat();
- uint version = fileFormat.DefaultVersion;
- if (fileFormat.AvailableVersionsCount > 1)
+ var fileFormat = fileExtension.GetFileFormat();
+ uint version = fileFormat.DefaultVersion;
+ if (fileFormat.AvailableVersionsCount > 1)
+ {
+ var versionSelectorWindow = new VersionSelectorWindow(fileFormat, fileExtension);
+ await versionSelectorWindow.ShowDialog(this);
+ switch (versionSelectorWindow.DialogResult)
{
- var versionSelectorWindow = new VersionSelectorWindow(fileFormat, fileExtension);
- await versionSelectorWindow.ShowDialog(this);
- switch (versionSelectorWindow.DialogResult)
- {
- case DialogResults.OK:
- version = versionSelectorWindow.Version;
- break;
- case DialogResults.Cancel:
- return;
- }
+ case DialogResults.OK:
+ version = versionSelectorWindow.Version;
+ break;
+ case DialogResults.Cancel:
+ return;
}
+ }
- SaveFileDialog saveDialog = new()
- {
- InitialFileName = Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath),
- Filters = Helpers.ToAvaloniaFilter(fileExtension.Description, fileExtension.Extension),
- Directory = string.IsNullOrEmpty(Settings.General.DefaultDirectoryConvertFile)
- ? Path.GetDirectoryName(SlicerFile.FileFullPath)
- : Settings.General.DefaultDirectoryConvertFile
- };
+ SaveFileDialog saveDialog = new()
+ {
+ InitialFileName = Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath),
+ Filters = Helpers.ToAvaloniaFilter(fileExtension.Description, fileExtension.Extension),
+ Directory = string.IsNullOrEmpty(Settings.General.DefaultDirectoryConvertFile)
+ ? Path.GetDirectoryName(SlicerFile.FileFullPath)
+ : Settings.General.DefaultDirectoryConvertFile
+ };
- var result = await saveDialog.ShowAsync(this);
- if (string.IsNullOrEmpty(result)) return;
+ var result = await saveDialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(result)) return;
- IsGUIEnabled = false;
- ShowProgressWindow($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {Path.GetExtension(result)}");
+ IsGUIEnabled = false;
+ ShowProgressWindow($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {Path.GetExtension(result)}");
- var task = await Task.Factory.StartNew(() =>
+ var task = await Task.Factory.StartNew(() =>
+ {
+ try
{
- try
- {
- return SlicerFile.Convert(fileFormat, result, version, Progress) is not null;
- }
- catch (OperationCanceledException)
+ return SlicerFile.Convert(fileFormat, result, version, Progress) is not null;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ string extraMessage = string.Empty;
+ if (SlicerFile.FileFullPath.EndsWith(".sl1"))
{
+ extraMessage = "Note: When converting from SL1 make sure you have the correct printer selected, you MUST use a UVtools base printer.\n" +
+ "Go to \"Help\" -> \"Install profiles into PrusaSlicer\" to install printers.\n";
}
- catch (Exception ex)
- {
- string extraMessage = string.Empty;
- if (SlicerFile.FileFullPath.EndsWith(".sl1"))
- {
- extraMessage = "Note: When converting from SL1 make sure you have the correct printer selected, you MUST use a UVtools base printer.\n" +
- "Go to \"Help\" -> \"Install profiles into PrusaSlicer\" to install printers.\n";
- }
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError($"{extraMessage}{ex}", "Convertion unsuccessful"));
- }
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError($"{extraMessage}{ex}", "Convertion unsuccessful"));
+ }
- return false;
- });
+ return false;
+ });
- IsGUIEnabled = true;
+ IsGUIEnabled = true;
- if (task)
- {
- var question = await this.MessageBoxQuestion(
- $"Conversion completed in {LastStopWatch.ElapsedMilliseconds / 1000}s\n\n" +
- $"Do you want to open '{Path.GetFileName(result)}' in a new window?\n" +
- "Yes: Open in a new window.\n" +
- "No: Open in this window.\n" +
- "Cancel: Do not perform any action.\n",
- "Conversion complete", ButtonEnum.YesNoCancel);
- switch (question)
- {
- case ButtonResult.No:
- ProcessFile(result, _actualLayer);
- break;
- case ButtonResult.Yes:
- App.NewInstance(result);
- break;
- }
+ if (task)
+ {
+ var question = await this.MessageBoxQuestion(
+ $"Conversion completed in {LastStopWatch.ElapsedMilliseconds / 1000}s\n\n" +
+ $"Do you want to open '{Path.GetFileName(result)}' in a new window?\n" +
+ "Yes: Open in a new window.\n" +
+ "No: Open in this window.\n" +
+ "Cancel: Do not perform any action.\n",
+ "Conversion complete", ButtonEnum.YesNoCancel);
+ switch (question)
+ {
+ case ButtonResult.No:
+ ProcessFile(result, _actualLayer);
+ break;
+ case ButtonResult.Yes:
+ App.NewInstance(result);
+ break;
}
- else
+ }
+ else
+ {
+ try
{
- try
- {
- if (File.Exists(result))
- {
- File.Delete(result);
- }
- }
- catch (Exception ex)
+ if (File.Exists(result))
{
- Debug.WriteLine(ex);
+ File.Delete(result);
}
}
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ }
}
+ }
- public async Task<bool> SaveFile(bool ignoreOverwriteWarning) => await SaveFile(null, ignoreOverwriteWarning);
+ public async Task<bool> SaveFile(bool ignoreOverwriteWarning) => await SaveFile(null, ignoreOverwriteWarning);
- public async Task<bool> SaveFile(string filepath = null, bool ignoreOverwriteWarning = false)
+ public async Task<bool> SaveFile(string filepath = null, bool ignoreOverwriteWarning = false)
+ {
+ if (filepath is null)
{
- if (filepath is null)
+ if (!ignoreOverwriteWarning && SavesCount == 0 && Settings.General.PromptOverwriteFileSave)
{
- if (!ignoreOverwriteWarning && SavesCount == 0 && Settings.General.PromptOverwriteFileSave)
- {
- var result = await this.MessageBoxQuestion(
- "Original input file will be overwritten. Do you wish to proceed?", "Overwrite file?");
+ var result = await this.MessageBoxQuestion(
+ "Original input file will be overwritten. Do you wish to proceed?", "Overwrite file?");
- if(result != ButtonResult.Yes) return false;
- }
-
- filepath = SlicerFile.FileFullPath;
+ if(result != ButtonResult.Yes) return false;
}
- var oldFile = SlicerFile.FileFullPath;
- var tempFile = filepath + FileFormat.TemporaryFileAppend;
+ filepath = SlicerFile.FileFullPath;
+ }
- IsGUIEnabled = false;
- ShowProgressWindow($"Saving {Path.GetFileName(filepath)}");
+ var oldFile = SlicerFile.FileFullPath;
+ var tempFile = filepath + FileFormat.TemporaryFileAppend;
+
+ IsGUIEnabled = false;
+ ShowProgressWindow($"Saving {Path.GetFileName(filepath)}");
- var task = await Task.Factory.StartNew( () =>
+ var task = await Task.Factory.StartNew( () =>
+ {
+ try
{
- try
+ SlicerFile.SaveAs(tempFile, Progress);
+ if (File.Exists(filepath))
{
- SlicerFile.SaveAs(tempFile, Progress);
- if (File.Exists(filepath))
- {
- File.Delete(filepath);
- }
- File.Move(tempFile, filepath);
- SlicerFile.FileFullPath = filepath;
- return true;
+ File.Delete(filepath);
}
- catch (OperationCanceledException)
+ File.Move(tempFile, filepath);
+ SlicerFile.FileFullPath = filepath;
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ SlicerFile.FileFullPath = oldFile;
+ if (File.Exists(tempFile))
{
- SlicerFile.FileFullPath = oldFile;
- if (File.Exists(tempFile))
- {
- File.Delete(tempFile);
- }
+ File.Delete(tempFile);
}
- catch (Exception ex)
+ }
+ catch (Exception ex)
+ {
+ SlicerFile.FileFullPath = oldFile;
+ if (File.Exists(tempFile))
{
- SlicerFile.FileFullPath = oldFile;
- if (File.Exists(tempFile))
- {
- File.Delete(tempFile);
- }
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(ex.ToString(), "Error while saving the file"));
+ File.Delete(tempFile);
}
-
- return false;
- });
-
- IsGUIEnabled = true;
-
- if (task)
- {
- SavesCount++;
- CanSave = false;
- UpdateTitle();
- if(oldFile != SlicerFile.FileFullPath) AddRecentFile(SlicerFile.FileFullPath);
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(ex.ToString(), "Error while saving the file"));
}
- return task;
- }
+ return false;
+ });
+
+ IsGUIEnabled = true;
- public async void IPrintedThisFile()
+ if (task)
{
- await ShowRunOperation(typeof(OperationIPrintedThisFile));
+ SavesCount++;
+ CanSave = false;
+ UpdateTitle();
+ if(oldFile != SlicerFile.FileFullPath) AddRecentFile(SlicerFile.FileFullPath);
}
- public async void ExtractFile()
+ return task;
+ }
+
+ public async void IPrintedThisFile()
+ {
+ await ShowRunOperation(typeof(OperationIPrintedThisFile));
+ }
+
+ public async void ExtractFile()
+ {
+ if (!IsFileLoaded) return;
+ string fileNameNoExt = Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath);
+ OpenFolderDialog dialog = new()
{
- if (!IsFileLoaded) return;
- string fileNameNoExt = Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath);
- OpenFolderDialog dialog = new()
- {
- Directory = string.IsNullOrEmpty(Settings.General.DefaultDirectoryExtractFile)
- ? Path.GetDirectoryName(SlicerFile.FileFullPath)
- : Settings.General.DefaultDirectoryExtractFile,
- Title =
- $"A \"{fileNameNoExt}\" folder will be created within your selected folder to dump the contents."
- };
+ Directory = string.IsNullOrEmpty(Settings.General.DefaultDirectoryExtractFile)
+ ? Path.GetDirectoryName(SlicerFile.FileFullPath)
+ : Settings.General.DefaultDirectoryExtractFile,
+ Title =
+ $"A \"{fileNameNoExt}\" folder will be created within your selected folder to dump the contents."
+ };
- var result = await dialog.ShowAsync(this);
- if (string.IsNullOrEmpty(result)) return;
+ var result = await dialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(result)) return;
- string finalPath = Path.Combine(result, fileNameNoExt);
+ string finalPath = Path.Combine(result, fileNameNoExt);
- IsGUIEnabled = false;
- ShowProgressWindow($"Extracting {Path.GetFileName(SlicerFile.FileFullPath)}");
+ IsGUIEnabled = false;
+ ShowProgressWindow($"Extracting {Path.GetFileName(SlicerFile.FileFullPath)}");
- await Task.Factory.StartNew(() =>
+ await Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ SlicerFile.Extract(finalPath, true, true, Progress);
+ }
+ catch (OperationCanceledException)
{
- try
- {
- SlicerFile.Extract(finalPath, true, true, Progress);
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(ex.ToString(), "Error while try extracting the file"));
- }
- });
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(ex.ToString(), "Error while try extracting the file"));
+ }
+ });
- IsGUIEnabled = true;
+ IsGUIEnabled = true;
- if (await this.MessageBoxQuestion(
+ if (await this.MessageBoxQuestion(
$"Extraction to {finalPath} completed in ({LastStopWatch.ElapsedMilliseconds / 1000}s)\n\n" +
"'Yes' to open target folder, 'No' to continue.",
"Extraction complete") == ButtonResult.Yes)
- {
- SystemAware.StartProcess(finalPath);
- }
-
- }
-
- public void OpenTerminal()
{
- new TerminalWindow().Show(this);
+ SystemAware.StartProcess(finalPath);
}
- #region Operations
+ }
+
+ public void OpenTerminal()
+ {
+ new TerminalWindow().Show(this);
+ }
+
+ #region Operations
- public async Task<Operation> ShowRunOperation(Operation loadOperation) =>
- await ShowRunOperation(loadOperation.GetType(), loadOperation);
+ public async Task<Operation> ShowRunOperation(Operation loadOperation) =>
+ await ShowRunOperation(loadOperation.GetType(), loadOperation);
+
+ public async Task<Operation> ShowRunOperation(Type type, Operation loadOperation = null)
+ {
+ var operation = await ShowOperation(type, loadOperation);
+ await RunOperation(operation);
+ return operation;
+ }
- public async Task<Operation> ShowRunOperation(Type type, Operation loadOperation = null)
+ public async Task<Operation> ShowOperation(Type type, Operation loadOperation = null)
+ {
+ var toolTypeBase = typeof(ToolControl);
+ var calibrateTypeBase = typeof(CalibrateElephantFootControl);
+ var classname = type.Name.StartsWith("OperationCalibrate") ?
+ $"{calibrateTypeBase.Namespace}.{type.Name.Remove(0, Operation.ClassNameLength)}Control" :
+ $"{toolTypeBase.Namespace}.Tool{type.Name.Remove(0, Operation.ClassNameLength)}Control"; ;
+ var controlType = Type.GetType(classname);
+ ToolControl control;
+
+ bool removeContent = false;
+ if (controlType is null)
{
- var operation = await ShowOperation(type, loadOperation);
- await RunOperation(operation);
- return operation;
+ //controlType = toolTypeBase;
+ removeContent = true;
+ control = new ToolControl(type.CreateInstance<Operation>(SlicerFile));
}
-
- public async Task<Operation> ShowOperation(Type type, Operation loadOperation = null)
+ else
{
- var toolTypeBase = typeof(ToolControl);
- var calibrateTypeBase = typeof(CalibrateElephantFootControl);
- var classname = type.Name.StartsWith("OperationCalibrate") ?
- $"{calibrateTypeBase.Namespace}.{type.Name.Remove(0, Operation.ClassNameLength)}Control" :
- $"{toolTypeBase.Namespace}.Tool{type.Name.Remove(0, Operation.ClassNameLength)}Control"; ;
- var controlType = Type.GetType(classname);
- ToolControl control;
-
- bool removeContent = false;
- if (controlType is null)
- {
- //controlType = toolTypeBase;
- removeContent = true;
- control = new ToolControl(type.CreateInstance<Operation>(SlicerFile));
- }
- else
- {
- control = controlType.CreateInstance<ToolControl>();
- if (control is null) return null;
- }
+ control = controlType.CreateInstance<ToolControl>();
+ if (control is null) return null;
+ }
- if (!control.CanRun)
- {
- return null;
- }
+ if (!control.CanRun)
+ {
+ return null;
+ }
- if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial && !control.BaseOperation.CanRunInPartialMode)
- {
- await this.MessageBoxError($"The file was open in partial mode and the tool \"{control.BaseOperation.Title}\" is unable to run in this mode.\n" +
- "Please reload the file in full mode in order to use this tool.", "Unable to run in partial mode");
- return null;
- }
+ if (SlicerFile.DecodeType == FileFormat.FileDecodeType.Partial && !control.BaseOperation.CanRunInPartialMode)
+ {
+ await this.MessageBoxError($"The file was open in partial mode and the tool \"{control.BaseOperation.Title}\" is unable to run in this mode.\n" +
+ "Please reload the file in full mode in order to use this tool.", "Unable to run in partial mode");
+ return null;
+ }
- if (removeContent)
- {
- control.IsVisible = false;
- }
+ if (removeContent)
+ {
+ control.IsVisible = false;
+ }
- if (loadOperation is not null)
- {
- control.BaseOperation = loadOperation;
- }
- var window = new ToolWindow(control);
- await window.ShowDialog(this);
- if (window.DialogResult != DialogResults.OK) return null;
- var operation = control.BaseOperation;
- return operation;
+ if (loadOperation is not null)
+ {
+ control.BaseOperation = loadOperation;
}
+ var window = new ToolWindow(control);
+ await window.ShowDialog(this);
+ if (window.DialogResult != DialogResults.OK) return null;
+ var operation = control.BaseOperation;
+ return operation;
+ }
- public async Task<bool> RunOperation(Operation baseOperation)
- {
- if (baseOperation is null) return false;
+ public async Task<bool> RunOperation(Operation baseOperation)
+ {
+ if (baseOperation is null) return false;
- switch (baseOperation)
- {
- case OperationEditParameters operation:
- operation.Execute();
- RefreshProperties();
- RefreshCurrentLayerData();
- ResetDataContext();
+ switch (baseOperation)
+ {
+ case OperationEditParameters operation:
+ operation.Execute();
+ RefreshProperties();
+ RefreshCurrentLayerData();
+ ResetDataContext();
- PopulateSuggestions();
+ PopulateSuggestions();
- CanSave = true;
- return true;
- case OperationIPrintedThisFile operation:
- operation.Execute();
- return true;
- case OperationRepairLayers operation:
- if (SlicerFile.IssueManager.Count == 0)
+ CanSave = true;
+ return true;
+ case OperationIPrintedThisFile operation:
+ operation.Execute();
+ return true;
+ case OperationRepairLayers operation:
+ if (SlicerFile.IssueManager.Count == 0)
+ {
+ var islandConfig = GetIslandDetectionConfiguration();
+ islandConfig.Enabled = operation.RepairIslands && operation.RemoveIslandsBelowEqualPixelCount > 0;
+ var overhangConfig = new OverhangDetectionConfiguration(false);
+ var resinTrapConfig = GetResinTrapDetectionConfiguration();
+ resinTrapConfig.Enabled = operation.RepairResinTraps;
+ var touchingBoundConfig = new TouchingBoundDetectionConfiguration(false);
+ var printHeightConfig = new PrintHeightDetectionConfiguration(false);
+
+ if (islandConfig.Enabled || resinTrapConfig.Enabled)
{
- var islandConfig = GetIslandDetectionConfiguration();
- islandConfig.Enabled = operation.RepairIslands && operation.RemoveIslandsBelowEqualPixelCount > 0;
- var overhangConfig = new OverhangDetectionConfiguration(false);
- var resinTrapConfig = GetResinTrapDetectionConfiguration();
- resinTrapConfig.Enabled = operation.RepairResinTraps;
- var touchingBoundConfig = new TouchingBoundDetectionConfiguration(false);
- var printHeightConfig = new PrintHeightDetectionConfiguration(false);
-
- if (islandConfig.Enabled || resinTrapConfig.Enabled)
- {
- await ComputeIssues(islandConfig, overhangConfig, resinTrapConfig, touchingBoundConfig, printHeightConfig, Settings.Issues.ComputeEmptyLayers);
- }
+ await ComputeIssues(islandConfig, overhangConfig, resinTrapConfig, touchingBoundConfig, printHeightConfig, Settings.Issues.ComputeEmptyLayers);
}
+ }
- break;
- }
+ break;
+ }
- IsGUIEnabled = false;
- ShowProgressWindow(baseOperation.ProgressTitle, baseOperation.CanCancel);
- OperationSessionManager.Instance.Add(baseOperation);
+ IsGUIEnabled = false;
+ ShowProgressWindow(baseOperation.ProgressTitle, baseOperation.CanCancel);
+ OperationSessionManager.Instance.Add(baseOperation);
- Clipboard.Snapshot();
+ Clipboard.Snapshot();
- var result = await Task.Factory.StartNew(() =>
+ var result = await Task.Factory.StartNew(() =>
+ {
+ try
{
- try
- {
- return baseOperation.Execute(Progress);
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(async () =>
- await this.MessageBoxError(ex.ToString(), $"{baseOperation.Title} Error"));
- }
-
- return false;
- });
+ return baseOperation.Execute(Progress);
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ await this.MessageBoxError(ex.ToString(), $"{baseOperation.Title} Error"));
+ }
- IsGUIEnabled = true;
+ return false;
+ });
- if (result)
- {
- Clipboard.Clip(baseOperation);
+ IsGUIEnabled = true;
- ShowLayer();
- RefreshProperties();
- ResetDataContext();
+ if (result)
+ {
+ Clipboard.Clip(baseOperation);
- PopulateSuggestions();
+ ShowLayer();
+ RefreshProperties();
+ ResetDataContext();
- CanSave = true;
+ PopulateSuggestions();
- if(baseOperation.GetType().Name.StartsWith("OperationCalibrate"))
- {
- IssuesClear();
- }
+ CanSave = true;
- switch (baseOperation)
- {
- // Tools
- case OperationRepairLayers operation:
- await OnClickDetectIssues();
- break;
- }
+ if(baseOperation.GetType().Name.StartsWith("OperationCalibrate"))
+ {
+ IssuesClear();
}
- else
+
+ switch (baseOperation)
{
- Clipboard.RestoreSnapshot();
+ // Tools
+ case OperationRepairLayers operation:
+ await OnClickDetectIssues();
+ break;
}
+ }
+ else
+ {
+ Clipboard.RestoreSnapshot();
+ }
- if (baseOperation.Tag is not null)
+ if (baseOperation.Tag is not null)
+ {
+ var message = baseOperation.Tag.ToString();
+ if (!string.IsNullOrWhiteSpace(message))
{
- var message = baseOperation.Tag.ToString();
- if (!string.IsNullOrWhiteSpace(message))
- {
- //message += $"\nExecution time: ";
+ //message += $"\nExecution time: ";
- await this.MessageBoxInfo(message, $"{baseOperation.Title} report ({LastStopWatch.Elapsed.Hours}h{LastStopWatch.Elapsed.Minutes}m{LastStopWatch.Elapsed.Seconds}s)");
- }
+ await this.MessageBoxInfo(message, $"{baseOperation.Title} report ({LastStopWatch.Elapsed.Hours}h{LastStopWatch.Elapsed.Minutes}m{LastStopWatch.Elapsed.Seconds}s)");
}
-
- return result;
}
- private void RefreshRecentFiles(bool reloadFiles = false)
- {
- if(reloadFiles) RecentFiles.Load();
+ return result;
+ }
- var items = new List<MenuItem>();
+ private void RefreshRecentFiles(bool reloadFiles = false)
+ {
+ if(reloadFiles) RecentFiles.Load();
- foreach (var file in RecentFiles.Instance)
- {
- var item = new MenuItem
- {
- Header = Path.GetFileName(file),
- Tag = file,
- IsEnabled = !IsFileLoaded || SlicerFile.FileFullPath != file
- };
- ToolTip.SetTip(item, file);
- ToolTip.SetPlacement(item, PlacementMode.Right);
- ToolTip.SetShowDelay(item, 100);
- items.Add(item);
+ var items = new List<MenuItem>();
- item.Click += MenuFileOpenRecentItemOnClick;
- }
+ foreach (var file in RecentFiles.Instance)
+ {
+ var item = new MenuItem
+ {
+ Header = Path.GetFileName(file),
+ Tag = file,
+ IsEnabled = !IsFileLoaded || SlicerFile.FileFullPath != file
+ };
+ ToolTip.SetTip(item, file);
+ ToolTip.SetPlacement(item, PlacementMode.Right);
+ ToolTip.SetShowDelay(item, 100);
+ items.Add(item);
- MenuFileOpenRecentItems = items.ToArray();
+ item.Click += MenuFileOpenRecentItemOnClick;
}
- private void AddRecentFile(string file)
- {
- if (file == Path.Combine(App.ApplicationPath, About.DemoFile)) return;
- RecentFiles.Load();
- RecentFiles.Instance.Insert(0, file);
- RecentFiles.Save();
- RefreshRecentFiles();
- }
+ MenuFileOpenRecentItems = items.ToArray();
+ }
- private async void MenuFileOpenRecentItemOnClick(object? sender, RoutedEventArgs e)
- {
- if (sender is not MenuItem { Tag: string file }) return;
- if (IsFileLoaded && SlicerFile.FileFullPath == file) return;
+ private void AddRecentFile(string file)
+ {
+ if (file == Path.Combine(App.ApplicationPath, About.DemoFile)) return;
+ RecentFiles.Load();
+ RecentFiles.Instance.Insert(0, file);
+ RecentFiles.Save();
+ RefreshRecentFiles();
+ }
- if (_globalModifiers == KeyModifiers.Control)
- {
- if (await this.MessageBoxQuestion("Are you sure you want to purge the non-existing files from the recent list?",
+ private async void MenuFileOpenRecentItemOnClick(object? sender, RoutedEventArgs e)
+ {
+ if (sender is not MenuItem { Tag: string file }) return;
+ if (IsFileLoaded && SlicerFile.FileFullPath == file) return;
+
+ if (_globalModifiers == KeyModifiers.Control)
+ {
+ if (await this.MessageBoxQuestion("Are you sure you want to purge the non-existing files from the recent list?",
"Purge the non-existing files?") == ButtonResult.Yes)
+ {
+ /*if (_globalModifiers == KeyModifiers.Shift)
{
- /*if (_globalModifiers == KeyModifiers.Shift)
- {
- RecentFiles.ClearFiles(true);
- RefreshRecentFiles();
- return;
- }*/
- if (RecentFiles.PurgenNonExistingFiles(true) > 0) RefreshRecentFiles();
- }
-
- return;
+ RecentFiles.ClearFiles(true);
+ RefreshRecentFiles();
+ return;
+ }*/
+ if (RecentFiles.PurgenNonExistingFiles(true) > 0) RefreshRecentFiles();
}
- if ((_globalModifiers & KeyModifiers.Control) != 0 &&
- (_globalModifiers & KeyModifiers.Shift) != 0)
- {
- if (await this.MessageBoxQuestion($"Are you sure you want to remove the selected file from the recent list?\n{file}",
- "Remove the file from recent list?") == ButtonResult.Yes)
- {
- RecentFiles.Load();
- RecentFiles.Instance.Remove(file);
- RecentFiles.Save();
+ return;
+ }
- RefreshRecentFiles();
- }
+ if ((_globalModifiers & KeyModifiers.Control) != 0 &&
+ (_globalModifiers & KeyModifiers.Shift) != 0)
+ {
+ if (await this.MessageBoxQuestion($"Are you sure you want to remove the selected file from the recent list?\n{file}",
+ "Remove the file from recent list?") == ButtonResult.Yes)
+ {
+ RecentFiles.Load();
+ RecentFiles.Instance.Remove(file);
+ RecentFiles.Save();
- return;
+ RefreshRecentFiles();
}
- if (!File.Exists(file))
- {
- if (await this.MessageBoxQuestion($"The file: {file} does not exists anymore.\n" +
- "Do you want to remove this file from recent list?",
- "The file does not exists") == ButtonResult.Yes)
- {
- RecentFiles.Load();
- RecentFiles.Instance.Remove(file);
- RecentFiles.Save();
+ return;
+ }
- RefreshRecentFiles();
- }
+ if (!File.Exists(file))
+ {
+ if (await this.MessageBoxQuestion($"The file: {file} does not exists anymore.\n" +
+ "Do you want to remove this file from recent list?",
+ "The file does not exists") == ButtonResult.Yes)
+ {
+ RecentFiles.Load();
+ RecentFiles.Instance.Remove(file);
+ RecentFiles.Save();
- return;
+ RefreshRecentFiles();
}
- if (_globalModifiers == KeyModifiers.Shift) App.NewInstance(file);
- else ProcessFile(file);
+ return;
}
+ if (_globalModifiers == KeyModifiers.Shift) App.NewInstance(file);
+ else ProcessFile(file);
+ }
+
#endregion
#endregion
- }
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs
index bd608d7..5b518d4 100644
--- a/UVtools.WPF/Program.cs
+++ b/UVtools.WPF/Program.cs
@@ -1,91 +1,94 @@
using System;
using System.Diagnostics;
using System.Globalization;
-using System.IO;
using System.Runtime.ExceptionServices;
using Avalonia;
-using UVtools.Core.FileFormats;
+using Projektanker.Icons.Avalonia;
+using Projektanker.Icons.Avalonia.FontAwesome;
+using Projektanker.Icons.Avalonia.MaterialDesign;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF
-{
- public static class Program
- {
- public static string[] Args;
+namespace UVtools.WPF;
- public static Stopwatch ProgramStartupTime;
- // Initialization code. Don't use any Avalonia, third-party APIs or any
- // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
- // yet and stuff might break.
- [STAThread]
- public static void Main(string[] args)
- {
- CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
- CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
+#nullable enable
- ProgramStartupTime = Stopwatch.StartNew();
- Args = args;
- try
- {
- if (ConsoleArguments.ParseArgs(args)) return;
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- return;
- }
+public static class Program
+{
+ public static string[] Args = null!;
- /*Slicer slicer = new(Size.Empty, SizeF.Empty, "D:\\Cube100x100x100.stl");
- var slices = slicer.SliceModel(0.05f);
-
- foreach (var slice in slices)
- {
- using var mat = EmguExtensions.InitMat(new Size(1000, 1000));
- var contour = slice.Value.ToContour();
- using var vec = new VectorOfPoint(contour);
- CvInvoke.FillPoly(mat, vec, EmguExtensions.WhiteColor, LineType.AntiAlias);
- mat.Save(@$"D:\SLICE\{slice.Key}.png");
- }*/
+ public static Stopwatch ProgramStartupTime = null!;
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args)
+ {
+ CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
+ CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
- // Add the event handler for handling non-UI thread exceptions to the event.
- AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
- //AppDomain.CurrentDomain.FirstChanceException += CurrentDomainOnFirstChanceException;
- BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+ ProgramStartupTime = Stopwatch.StartNew();
+ Args = args;
+ try
+ {
+ if (ConsoleArguments.ParseArgs(args)) return;
}
-
- private static void CurrentDomainOnFirstChanceException(object? sender, FirstChanceExceptionEventArgs e)
+ catch (Exception e)
{
- ErrorLog.AppendLine("First chance exception", e.Exception.ToString());
+ Console.WriteLine(e);
+ return;
}
-
- private static async void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
+ /*Slicer slicer = new(Size.Empty, SizeF.Empty, "D:\\Cube100x100x100.stl");
+ var slices = slicer.SliceModel(0.05f);
+
+ foreach (var slice in slices)
{
- Exception ex = (Exception)e.ExceptionObject;
- ErrorLog.AppendLine("Fatal Non-UI Error", ex.ToString());
+ using var mat = EmguExtensions.InitMat(new Size(1000, 1000));
+ var contour = slice.Value.ToContour();
+ using var vec = new VectorOfPoint(contour);
+ CvInvoke.FillPoly(mat, vec, EmguExtensions.WhiteColor, LineType.AntiAlias);
+ mat.Save(@$"D:\SLICE\{slice.Key}.png");
+ }*/
- try
- {
- string errorMsg = "An application error occurred. Please contact the administrator with the following information:\n\n" +
- $"{ex}";
+ // Add the event handler for handling non-UI thread exceptions to the event.
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
+ //AppDomain.CurrentDomain.FirstChanceException += CurrentDomainOnFirstChanceException;
+ BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+ }
- await App.MainWindow.MessageBoxError(errorMsg, "Fatal Non-UI Error");
- }
- catch (Exception exception)
- {
- Debug.WriteLine(exception);
- }
- }
+ private static void CurrentDomainOnFirstChanceException(object? sender, FirstChanceExceptionEventArgs e)
+ {
+ ErrorLog.AppendLine("First chance exception", e.Exception.ToString());
+ }
+
+
+ private static async void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ var ex = (Exception)e.ExceptionObject;
+ ErrorLog.AppendLine("Fatal Non-UI Error", ex.ToString());
- // Avalonia configuration, don't remove; also used by visual designer.
- public static AppBuilder BuildAvaloniaApp()
- => AppBuilder.Configure<App>()
- .UsePlatformDetect()
- .With(new Win32PlatformOptions { AllowEglInitialization = true/*, UseWgl = true*/})
- .With(new X11PlatformOptions { UseGpu = true/*, UseEGL = true*/ })
- .With(new MacOSPlatformOptions { ShowInDock = true })
- .With(new AvaloniaNativePlatformOptions { UseGpu = true })
- .UseSkia()
- .LogToTrace();
+ try
+ {
+ var errorMsg = $"An application error occurred. Please contact the administrator with the following information:\n\n{ex}";
+ await App.MainWindow.MessageBoxError(errorMsg, "Fatal Non-UI Error");
+ }
+ catch (Exception exception)
+ {
+ Debug.WriteLine(exception);
+ }
}
-}
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure<App>()
+ .UsePlatformDetect()
+ .With(new Win32PlatformOptions { AllowEglInitialization = false/*, UseWgl = true*/})
+ .With(new X11PlatformOptions { UseGpu = true/*, UseEGL = true*/ })
+ .With(new MacOSPlatformOptions { ShowInDock = true })
+ .With(new AvaloniaNativePlatformOptions { UseGpu = true })
+ .WithIcons(container => container
+ .Register<FontAwesomeIconProvider>()
+ .Register<MaterialDesignIconProvider>())
+ //.UseSkia()
+ .LogToTrace();
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 2f34467..ef4da40 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -23,247 +23,246 @@ using UVtools.Core.Operations;
using UVtools.Core.SystemOS;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+public class AppVersionChecker : BindableBase
{
- public class AppVersionChecker : BindableBase
- {
- public const string GitHubReleaseApi = "https://api.github.com/repos/sn4k3/UVtools/releases/latest";
- public const string RuntimePackageFile = "runtime_package.dat";
- private string _version;
- private string _changelog;
+ public const string GitHubReleaseApi = "https://api.github.com/repos/sn4k3/UVtools/releases/latest";
+ public const string RuntimePackageFile = "runtime_package.dat";
+ private string _version;
+ private string _changelog;
- public string Filename
+ public string Filename
+ {
+ get
{
- get
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- return $"{About.Software}_win-x64_v{_version}.msi";
- }
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ return $"{About.Software}_win-x64_v{_version}.msi";
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ var file = Path.Combine(App.ApplicationPath, RuntimePackageFile);
+ if (File.Exists(file))
{
- var file = Path.Combine(App.ApplicationPath, RuntimePackageFile);
- if (File.Exists(file))
+ try
{
- try
- {
- var package = File.ReadAllText(file);
- if (!string.IsNullOrWhiteSpace(package) && package.EndsWith("-x64"))
- {
- return $"{About.Software}_{package}_v{_version}.zip";
- }
- }
- catch (Exception e)
+ var package = File.ReadAllText(file);
+ if (!string.IsNullOrWhiteSpace(package) && package.EndsWith("-x64"))
{
- Debug.WriteLine(e);
+ return $"{About.Software}_{package}_v{_version}.zip";
}
-
}
- return $"{About.Software}_linux-x64_v{_version}.zip";
- }
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- return $"{About.Software}_osx-x64_v{_version}.zip";
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+
}
-
- return $"{About.Software}_universal-x86-x64_v{_version}.zip";
+ return $"{About.Software}_linux-x64_v{_version}.zip";
}
- }
-
- public string Runtime
- {
- get
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
- if (OperatingSystem.IsWindows())
- {
- return "win-x64";
- }
- if (OperatingSystem.IsLinux())
- {
- return "linux-x64";
- }
- if (OperatingSystem.IsMacOS())
- {
- return "osx-x64";
- }
-
- return "universal-x86-x64";
+ return $"{About.Software}_osx-x64_v{_version}.zip";
}
+
+ return $"{About.Software}_universal-x86-x64_v{_version}.zip";
}
+ }
- public string Version
+ public string Runtime
+ {
+ get
{
- get => _version;
- set
+ if (OperatingSystem.IsWindows())
{
- if(!RaiseAndSetIfChanged(ref _version, value)) return;
- RaisePropertyChanged(nameof(VersionAnnouncementText));
- RaisePropertyChanged(nameof(HaveNewVersion));
+ return "win-x64";
}
+ if (OperatingSystem.IsLinux())
+ {
+ return "linux-x64";
+ }
+ if (OperatingSystem.IsMacOS())
+ {
+ return "osx-x64";
+ }
+
+ return "universal-x86-x64";
}
+ }
- public string Changelog
+ public string Version
+ {
+ get => _version;
+ set
{
- get => _changelog;
- set => RaiseAndSetIfChanged(ref _changelog, value);
+ if(!RaiseAndSetIfChanged(ref _version, value)) return;
+ RaisePropertyChanged(nameof(VersionAnnouncementText));
+ RaisePropertyChanged(nameof(HaveNewVersion));
}
+ }
+
+ public string Changelog
+ {
+ get => _changelog;
+ set => RaiseAndSetIfChanged(ref _changelog, value);
+ }
- public string VersionAnnouncementText => $"New version v{_version} is available!";
+ public string VersionAnnouncementText => $"New version v{_version} is available!";
- public string UrlLatestRelease = $"{About.Website}/releases/latest";
+ public string UrlLatestRelease = $"{About.Website}/releases/latest";
- public string DownloadLink => string.IsNullOrEmpty(Filename) ? null : $"{About.Website}/releases/download/v{_version}/{Filename}";
+ public string DownloadLink => string.IsNullOrEmpty(Filename) ? null : $"{About.Website}/releases/download/v{_version}/{Filename}";
- public bool HaveNewVersion => !string.IsNullOrEmpty(Version);
+ public bool HaveNewVersion => !string.IsNullOrEmpty(Version);
- public string DownloadedFile { get; private set; }
+ public string DownloadedFile { get; private set; }
- public bool Check()
+ public bool Check()
+ {
+ try
{
- try
+ var request = new HttpRequestMessage
{
- var request = new HttpRequestMessage
+ RequestUri = new Uri(GitHubReleaseApi),
+ };
+
+ request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
+
+ var result= NetworkExtensions.HttpClient.Send(request);
+
+ var json = JObject.Parse(result.Content.ReadAsStringAsync().Result);
+ string tag_name = json["tag_name"]?.ToString();
+ if (string.IsNullOrEmpty(tag_name)) return false;
+ tag_name = tag_name.Trim(' ', 'v', 'V');
+ Debug.WriteLine($"Version checker: v{App.VersionStr} <=> v{tag_name}");
+ Version checkVersion = new(tag_name);
+ Changelog = json["body"]?.ToString();
+ //if (string.Compare(tag_name, App.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
+ if (App.Version.CompareTo(checkVersion) < 0)
+ {
+ Debug.WriteLine($"New version detected: {DownloadLink}\n" +
+ $"{_changelog}");
+ Dispatcher.UIThread.InvokeAsync(() =>
{
- RequestUri = new Uri(GitHubReleaseApi),
- };
-
- request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
-
- var result= NetworkExtensions.HttpClient.Send(request);
-
- var json = JObject.Parse(result.Content.ReadAsStringAsync().Result);
- string tag_name = json["tag_name"]?.ToString();
- if (string.IsNullOrEmpty(tag_name)) return false;
- tag_name = tag_name.Trim(' ', 'v', 'V');
- Debug.WriteLine($"Version checker: v{App.VersionStr} <=> v{tag_name}");
- Version checkVersion = new(tag_name);
- Changelog = json["body"]?.ToString();
- //if (string.Compare(tag_name, App.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
- if (App.Version.CompareTo(checkVersion) < 0)
+ Version = tag_name;
+ });
+ return true;
+ }
+ /*string htmlCode = client.DownloadString($"{About.Website}/releases");
+ const string searchFor = "/releases/tag/";
+ var startIndex = htmlCode.IndexOf(searchFor, StringComparison.InvariantCultureIgnoreCase) +
+ searchFor.Length;
+ var endIndex = htmlCode.IndexOf("\"", startIndex, StringComparison.InvariantCultureIgnoreCase);
+ var version = htmlCode.Substring(startIndex, endIndex - startIndex);
+ if (string.Compare(version, $"v{AppSettings.VersionStr}", StringComparison.OrdinalIgnoreCase) > 0)
{
- Debug.WriteLine($"New version detected: {DownloadLink}\n" +
- $"{_changelog}");
Dispatcher.UIThread.InvokeAsync(() =>
{
- Version = tag_name;
+ Version = version;;
});
return true;
- }
- /*string htmlCode = client.DownloadString($"{About.Website}/releases");
- const string searchFor = "/releases/tag/";
- var startIndex = htmlCode.IndexOf(searchFor, StringComparison.InvariantCultureIgnoreCase) +
- searchFor.Length;
- var endIndex = htmlCode.IndexOf("\"", startIndex, StringComparison.InvariantCultureIgnoreCase);
- var version = htmlCode.Substring(startIndex, endIndex - startIndex);
- if (string.Compare(version, $"v{AppSettings.VersionStr}", StringComparison.OrdinalIgnoreCase) > 0)
- {
- Dispatcher.UIThread.InvokeAsync(() =>
- {
- Version = version;;
- });
- return true;
- }*/
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.ToString());
- }
-
- return false;
+ }*/
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
}
- public async Task<bool> AutoUpgrade(OperationProgress progress)
+ return false;
+ }
+
+ public async Task<bool> AutoUpgrade(OperationProgress progress)
+ {
+ if (!HaveNewVersion) return false;
+ progress.ItemName = "Megabytes";
+ try
{
- if (!HaveNewVersion) return false;
+ var path = Path.GetTempPath();
+ DownloadedFile = Path.Combine(path, Filename);
+ Debug.WriteLine($"Downloading to: {DownloadedFile}");
progress.ItemName = "Megabytes";
- try
- {
- var path = Path.GetTempPath();
- DownloadedFile = Path.Combine(path, Filename);
- Debug.WriteLine($"Downloading to: {DownloadedFile}");
- progress.ItemName = "Megabytes";
- var iprogress = new Progress<(long total, long bytes)>();
- iprogress.ProgressChanged += (_, tuple) =>
- {
- progress.ItemCount = (uint)(tuple.total / 1000000);
- progress.ProcessedItems = (uint)(tuple.bytes / 1000000);
- };
- using var result = await NetworkExtensions.DownloadAsync(DownloadLink, DownloadedFile, iprogress, progress.Token);
+ var iprogress = new Progress<(long total, long bytes)>();
+ iprogress.ProgressChanged += (_, tuple) =>
+ {
+ progress.ItemCount = (uint)(tuple.total / 1000000);
+ progress.ProcessedItems = (uint)(tuple.bytes / 1000000);
+ };
+ using var result = await NetworkExtensions.DownloadAsync(DownloadLink, DownloadedFile, iprogress, progress.Token);
- progress.Reset("Extracting");
+ progress.Reset("Extracting");
- if (OperatingSystem.IsWindows())
+ if (OperatingSystem.IsWindows())
+ {
+ SystemAware.StartProcess(DownloadedFile);
+ Environment.Exit(0);
+ }
+ else
+ {
+ string upgradeFolder = "UPDATED_VERSION";
+ var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder);
+ await using (var stream = File.Open(DownloadedFile, FileMode.Open))
{
- SystemAware.StartProcess(DownloadedFile);
- Environment.Exit(0);
+ using ZipArchive zip = new(stream, ZipArchiveMode.Read);
+ zip.ExtractToDirectory(targetDir, true);
}
- else
- {
- string upgradeFolder = "UPDATED_VERSION";
- var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder);
- await using (var stream = File.Open(DownloadedFile, FileMode.Open))
- {
- using ZipArchive zip = new(stream, ZipArchiveMode.Read);
- zip.ExtractToDirectory(targetDir, true);
- }
- File.Delete(DownloadedFile);
-
- string upgradeFileName = $"{About.Software}_upgrade.sh";
- var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName);
- await using (var stream = File.CreateText(upgradeFile))
- {
- await stream.WriteLineAsync("#!/bin/bash");
- await stream.WriteLineAsync($"echo {About.Software} v{App.Version} updater script");
- await stream.WriteLineAsync($"cd '{App.ApplicationPath}'");
- await stream.WriteLineAsync($"killall {About.Software}");
- await stream.WriteLineAsync("sleep 0.5");
+ File.Delete(DownloadedFile);
+ string upgradeFileName = $"{About.Software}_upgrade.sh";
+ var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName);
+ await using (var stream = File.CreateText(upgradeFile))
+ {
+ await stream.WriteLineAsync("#!/bin/bash");
+ await stream.WriteLineAsync($"echo {About.Software} v{App.Version} updater script");
+ await stream.WriteLineAsync($"cd '{App.ApplicationPath}'");
+ await stream.WriteLineAsync($"killall {About.Software}");
+ await stream.WriteLineAsync("sleep 0.5");
- //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &");
- if (OperatingSystem.IsMacOS() && App.ApplicationPath.EndsWith(".app/Contents/MacOS"))
- {
- await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ../../../");
- await stream.WriteLineAsync($"open ../../../{About.Software}.app");
- }
- else
- {
- await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* .");
- await stream.WriteLineAsync($"if [ -f '{About.Software}' ]; then");
- await stream.WriteLineAsync($" ./{About.Software} &");
- await stream.WriteLineAsync("else");
- await stream.WriteLineAsync($" dotnet {About.Software}.dll &");
- await stream.WriteLineAsync("fi");
- }
- await stream.WriteLineAsync($"rm -fr {upgradeFolder}");
- await stream.WriteLineAsync("sleep 0.5");
- await stream.WriteLineAsync($"rm -f {upgradeFileName}");
- //stream.WriteLine("exit");
- stream.Close();
+ //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &");
+ if (OperatingSystem.IsMacOS() && App.ApplicationPath.EndsWith(".app/Contents/MacOS"))
+ {
+ await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ../../../");
+ await stream.WriteLineAsync($"open ../../../{About.Software}.app");
+ }
+ else
+ {
+ await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* .");
+ await stream.WriteLineAsync($"if [ -f '{About.Software}' ]; then");
+ await stream.WriteLineAsync($" ./{About.Software} &");
+ await stream.WriteLineAsync("else");
+ await stream.WriteLineAsync($" dotnet {About.Software}.dll &");
+ await stream.WriteLineAsync("fi");
}
- SystemAware.StartProcess("bash", $"\"{upgradeFile}\"");
-
- //App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
- Environment.Exit(0);
+ await stream.WriteLineAsync($"rm -fr {upgradeFolder}");
+ await stream.WriteLineAsync("sleep 0.5");
+ await stream.WriteLineAsync($"rm -f {upgradeFileName}");
+ //stream.WriteLine("exit");
+ stream.Close();
}
- }
- catch (OperationCanceledException)
- {
- if(File.Exists(DownloadedFile)) File.Delete(DownloadedFile);
- }
- catch (Exception e)
- {
- await App.MainWindow.MessageBoxError(e.ToString(), "Error downloading the file");
- if (File.Exists(DownloadedFile)) File.Delete(DownloadedFile);
- return false;
- }
+ SystemAware.StartProcess("bash", $"\"{upgradeFile}\"");
+ //App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
+ Environment.Exit(0);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ if(File.Exists(DownloadedFile)) File.Delete(DownloadedFile);
+ }
+ catch (Exception e)
+ {
+ await App.MainWindow.MessageBoxError(e.ToString(), "Error downloading the file");
+ if (File.Exists(DownloadedFile)) File.Delete(DownloadedFile);
return false;
}
+
+
+ return false;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/BenchmarkTest.cs b/UVtools.WPF/Structures/BenchmarkTest.cs
index b349bbb..80df779 100644
--- a/UVtools.WPF/Structures/BenchmarkTest.cs
+++ b/UVtools.WPF/Structures/BenchmarkTest.cs
@@ -5,30 +5,29 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+public sealed class BenchmarkTest
{
- public sealed class BenchmarkTest
- {
- public const string DEVCPU = "Intel® Core™ i9-9900K @ 3.60 GHz";
- public const string DEVRAM = "G.SKILL Trident Z 32GB DDR4-3200MHz CL14";
+ public const string DEVCPU = "Intel® Core™ i9-9900K @ 3.60 GHz";
+ public const string DEVRAM = "G.SKILL Trident Z 32GB DDR4-3200MHz CL14";
- public BenchmarkTest(string name, string functionName, float devSingleThreadResult = 0, float devMultiThreadResult = 0)
- {
- Name = name;
- FunctionName = functionName;
- DevSingleThreadResult = devSingleThreadResult;
- DevMultiThreadResult = devMultiThreadResult;
- }
+ public BenchmarkTest(string name, string functionName, float devSingleThreadResult = 0, float devMultiThreadResult = 0)
+ {
+ Name = name;
+ FunctionName = functionName;
+ DevSingleThreadResult = devSingleThreadResult;
+ DevMultiThreadResult = devMultiThreadResult;
+ }
- public string Name { get; }
- public string FunctionName { get; }
+ public string Name { get; }
+ public string FunctionName { get; }
- public float DevSingleThreadResult { get; }
- public float DevMultiThreadResult { get; }
+ public float DevSingleThreadResult { get; }
+ public float DevMultiThreadResult { get; }
- public override string ToString()
- {
- return $"{Name}";
- }
+ public override string ToString()
+ {
+ return $"{Name}";
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/Color.cs b/UVtools.WPF/Structures/Color.cs
index 9370653..af6e38d 100644
--- a/UVtools.WPF/Structures/Color.cs
+++ b/UVtools.WPF/Structures/Color.cs
@@ -9,121 +9,120 @@ using System;
using Avalonia.Media;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+[Serializable]
+public class Color
{
- [Serializable]
- public class Color
- {
- public byte A;
- public byte R;
- public byte G;
- public byte B;
+ public byte A;
+ public byte R;
+ public byte G;
+ public byte B;
- public Color()
- { }
+ public Color()
+ { }
- public Color(byte a, byte r, byte g, byte b)
- {
- A = a;
- R = r;
- G = g;
- B = b;
- }
+ public Color(byte a, byte r, byte g, byte b)
+ {
+ A = a;
+ R = r;
+ G = g;
+ B = b;
+ }
- public Color(Color color)
- : this(color.A, color.R, color.G, color.B)
- {
- }
+ public Color(Color color)
+ : this(color.A, color.R, color.G, color.B)
+ {
+ }
- public Color(Avalonia.Media.Color color)
- : this(color.A, color.R, color.G, color.B)
- {
- }
+ public Color(Avalonia.Media.Color color)
+ : this(color.A, color.R, color.G, color.B)
+ {
+ }
- public Color(SolidColorBrush brush)
- : this(brush.Color)
- {
- }
+ public Color(SolidColorBrush brush)
+ : this(brush.Color)
+ {
+ }
- public System.Drawing.Color ToDotNet()
- {
- return System.Drawing.Color.FromArgb(A, R, G, B);
- }
+ public System.Drawing.Color ToDotNet()
+ {
+ return System.Drawing.Color.FromArgb(A, R, G, B);
+ }
- public Avalonia.Media.Color ToAvalonia()
- {
- return new Avalonia.Media.Color(A, R, G, B);
- }
+ public Avalonia.Media.Color ToAvalonia()
+ {
+ return new Avalonia.Media.Color(A, R, G, B);
+ }
- public bool IsEmpty => ReferenceEquals(this, Empty);
+ public bool IsEmpty => ReferenceEquals(this, Empty);
- public static Color FromArgb(byte a, byte r, byte g, byte b)
- {
- return new Color(a, r, g, b);
- }
+ public static Color FromArgb(byte a, byte r, byte g, byte b)
+ {
+ return new Color(a, r, g, b);
+ }
- public static Color Empty => new(0,0,0,0);
+ public static Color Empty => new(0,0,0,0);
- public Color FactorColor(byte pixelColor, byte min = 0, byte max = byte.MaxValue) =>
- FactorColor(pixelColor / 255f, min, max);
+ public Color FactorColor(byte pixelColor, byte min = 0, byte max = byte.MaxValue) =>
+ FactorColor(pixelColor / 255f, min, max);
- public Color FactorColor(float factor, byte min = 0, byte max = byte.MaxValue)
- {
- byte r = (byte)(R == 0 ? 0 :
- Math.Min(Math.Max(min, R * factor), max));
+ public Color FactorColor(float factor, byte min = 0, byte max = byte.MaxValue)
+ {
+ byte r = (byte)(R == 0 ? 0 :
+ Math.Min(Math.Max(min, R * factor), max));
- byte g = (byte)(G == 0 ? 0 :
- Math.Min(Math.Max(min, G * factor), max));
+ byte g = (byte)(G == 0 ? 0 :
+ Math.Min(Math.Max(min, G * factor), max));
- byte b = (byte)(B == 0 ? 0 :
- Math.Min(Math.Max(min, B * factor), max));
- return Color.FromArgb(A, r, g, b);
- }
+ byte b = (byte)(B == 0 ? 0 :
+ Math.Min(Math.Max(min, B * factor), max));
+ return Color.FromArgb(A, r, g, b);
+ }
- /// <summary>
- /// Returns the integer representation of the color.
- /// </summary>
- /// <returns>
- /// The integer representation of the color.
- /// </returns>
- public uint ToUint32()
- {
- return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B;
- }
+ /// <summary>
+ /// Returns the integer representation of the color.
+ /// </summary>
+ /// <returns>
+ /// The integer representation of the color.
+ /// </returns>
+ public uint ToUint32()
+ {
+ return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B;
+ }
- /// <summary>
- /// Check if two colors are equal.
- /// </summary>
- public bool Equals(Color other)
- {
- return A == other.A && R == other.R && G == other.G && B == other.B;
- }
+ /// <summary>
+ /// Check if two colors are equal.
+ /// </summary>
+ public bool Equals(Color other)
+ {
+ return A == other.A && R == other.R && G == other.G && B == other.B;
+ }
- public override bool Equals(object obj)
- {
- return obj is Color other && Equals(other);
- }
+ public override bool Equals(object obj)
+ {
+ return obj is Color other && Equals(other);
+ }
- public override int GetHashCode()
+ public override int GetHashCode()
+ {
+ unchecked
{
- unchecked
- {
- int hashCode = A.GetHashCode();
- hashCode = (hashCode * 397) ^ R.GetHashCode();
- hashCode = (hashCode * 397) ^ G.GetHashCode();
- hashCode = (hashCode * 397) ^ B.GetHashCode();
- return hashCode;
- }
+ int hashCode = A.GetHashCode();
+ hashCode = (hashCode * 397) ^ R.GetHashCode();
+ hashCode = (hashCode * 397) ^ G.GetHashCode();
+ hashCode = (hashCode * 397) ^ B.GetHashCode();
+ return hashCode;
}
+ }
- public static bool operator ==(Color left, Color right)
- {
- return left.Equals(right);
- }
+ public static bool operator ==(Color left, Color right)
+ {
+ return left.Equals(right);
+ }
- public static bool operator !=(Color left, Color right)
- {
- return !left.Equals(right);
- }
+ public static bool operator !=(Color left, Color right)
+ {
+ return !left.Equals(right);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/LogItem.cs b/UVtools.WPF/Structures/LogItem.cs
index afd192b..5a1773a 100644
--- a/UVtools.WPF/Structures/LogItem.cs
+++ b/UVtools.WPF/Structures/LogItem.cs
@@ -9,53 +9,52 @@
using System;
using UVtools.Core.Objects;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+public sealed class LogItem : BindableBase
{
- public sealed class LogItem : BindableBase
+ private int _index;
+ private string _startTime;
+ private double _elapsedTime;
+ private string _description;
+
+ public int Index
+ {
+ get => _index;
+ set => RaiseAndSetIfChanged(ref _index, value);
+ }
+
+ public string StartTime
+ {
+ get => _startTime;
+ set => RaiseAndSetIfChanged(ref _startTime, value);
+ }
+
+ public double ElapsedTime
+ {
+ get => _elapsedTime;
+ set => RaiseAndSetIfChanged(ref _elapsedTime, Math.Round(value, 2));
+ }
+
+ public string Description
+ {
+ get => _description;
+ set => RaiseAndSetIfChanged(ref _description, value);
+ }
+
+ public LogItem(int index, string description, double elapsedTime = 0)
+ {
+ _index = index;
+ _description = description;
+ ElapsedTime = elapsedTime;
+ _startTime = DateTime.Now.ToString("HH:mm:ss");
+ }
+
+ public LogItem(string description = null, uint elapsedTime = 0) : this(0, description, elapsedTime)
+ { }
+
+ public override string ToString()
{
- private int _index;
- private string _startTime;
- private double _elapsedTime;
- private string _description;
-
- public int Index
- {
- get => _index;
- set => RaiseAndSetIfChanged(ref _index, value);
- }
-
- public string StartTime
- {
- get => _startTime;
- set => RaiseAndSetIfChanged(ref _startTime, value);
- }
-
- public double ElapsedTime
- {
- get => _elapsedTime;
- set => RaiseAndSetIfChanged(ref _elapsedTime, Math.Round(value, 2));
- }
-
- public string Description
- {
- get => _description;
- set => RaiseAndSetIfChanged(ref _description, value);
- }
-
- public LogItem(int index, string description, double elapsedTime = 0)
- {
- _index = index;
- _description = description;
- ElapsedTime = elapsedTime;
- _startTime = DateTime.Now.ToString("HH:mm:ss");
- }
-
- public LogItem(string description = null, uint elapsedTime = 0) : this(0, description, elapsedTime)
- { }
-
- public override string ToString()
- {
- return Description;
- }
+ return Description;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs
index 1fdbe3e..f63251d 100644
--- a/UVtools.WPF/Structures/OperationProfiles.cs
+++ b/UVtools.WPF/Structures/OperationProfiles.cs
@@ -11,279 +11,275 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
+using UVtools.Core.Extensions;
using UVtools.Core.Operations;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+[Serializable]
+public class OperationProfiles //: IList<Operation>
{
- [Serializable]
- public class OperationProfiles //: IList<Operation>
- {
- #region Properties
- /// <summary>
- /// Default filepath for store <see cref="UserSettings"/>
- /// </summary>
- private static string FilePath => Path.Combine(UserSettings.SettingsFolder, "operation_profiles.xml");
-
- [XmlElement(typeof(OperationRepairLayers))]
-
- [XmlElement(typeof(OperationResize))]
- [XmlElement(typeof(OperationFlip))]
- [XmlElement(typeof(OperationRotate))]
- [XmlElement(typeof(OperationSolidify))]
- [XmlElement(typeof(OperationMorph))]
- [XmlElement(typeof(OperationRaftRelief))]
- [XmlElement(typeof(OperationRedrawModel))]
- [XmlElement(typeof(OperationThreshold))]
- [XmlElement(typeof(OperationLayerArithmetic))]
- [XmlElement(typeof(OperationPixelArithmetic))]
- [XmlElement(typeof(OperationPixelDimming))]
- [XmlElement(typeof(OperationInfill))]
- [XmlElement(typeof(OperationBlur))]
- [XmlElement(typeof(OperationFadeExposureTime))]
- [XmlElement(typeof(OperationDoubleExposure))]
- [XmlElement(typeof(OperationDynamicLayerHeight))]
- [XmlElement(typeof(OperationDynamicLifts))]
- [XmlElement(typeof(OperationRaiseOnPrintFinish))]
- [XmlElement(typeof(OperationChangeResolution))]
- [XmlElement(typeof(OperationTimelapse))]
+ #region Properties
+ /// <summary>
+ /// Default filepath for store <see cref="UserSettings"/>
+ /// </summary>
+ private static string FilePath => Path.Combine(UserSettings.SettingsFolder, "operation_profiles.xml");
+
+ [XmlElement(typeof(OperationRepairLayers))]
+
+ [XmlElement(typeof(OperationResize))]
+ [XmlElement(typeof(OperationFlip))]
+ [XmlElement(typeof(OperationRotate))]
+ [XmlElement(typeof(OperationSolidify))]
+ [XmlElement(typeof(OperationMorph))]
+ [XmlElement(typeof(OperationRaftRelief))]
+ [XmlElement(typeof(OperationRedrawModel))]
+ [XmlElement(typeof(OperationThreshold))]
+ [XmlElement(typeof(OperationLayerArithmetic))]
+ [XmlElement(typeof(OperationPixelArithmetic))]
+ [XmlElement(typeof(OperationPixelDimming))]
+ [XmlElement(typeof(OperationInfill))]
+ [XmlElement(typeof(OperationBlur))]
+ [XmlElement(typeof(OperationFadeExposureTime))]
+ [XmlElement(typeof(OperationDoubleExposure))]
+ [XmlElement(typeof(OperationDynamicLayerHeight))]
+ [XmlElement(typeof(OperationDynamicLifts))]
+ [XmlElement(typeof(OperationRaiseOnPrintFinish))]
+ [XmlElement(typeof(OperationChangeResolution))]
+ [XmlElement(typeof(OperationTimelapse))]
- [XmlElement(typeof(OperationLayerExportGif))]
-
- [XmlElement(typeof(OperationCalibrateExposureFinder))]
- [XmlElement(typeof(OperationCalibrateElephantFoot))]
- [XmlElement(typeof(OperationCalibrateXYZAccuracy))]
- [XmlElement(typeof(OperationCalibrateLiftHeight))]
- [XmlElement(typeof(OperationCalibrateTolerance))]
- [XmlElement(typeof(OperationCalibrateGrayscale))]
- [XmlElement(typeof(OperationCalibrateStressTower))]
- public List<Operation> Operations { get; internal set; } = new();
-
- [XmlIgnore]
- public static List<Operation> Profiles
- {
- get => Instance.Operations;
- internal set => Instance.Operations = value;
- }
+ [XmlElement(typeof(OperationLayerExportGif))]
+
+ [XmlElement(typeof(OperationCalibrateExposureFinder))]
+ [XmlElement(typeof(OperationCalibrateElephantFoot))]
+ [XmlElement(typeof(OperationCalibrateXYZAccuracy))]
+ [XmlElement(typeof(OperationCalibrateLiftHeight))]
+ [XmlElement(typeof(OperationCalibrateTolerance))]
+ [XmlElement(typeof(OperationCalibrateGrayscale))]
+ [XmlElement(typeof(OperationCalibrateStressTower))]
+ public List<Operation> Operations { get; internal set; } = new();
+
+ [XmlIgnore]
+ public static List<Operation> Profiles
+ {
+ get => Instance.Operations;
+ internal set => Instance.Operations = value;
+ }
- #endregion
+ #endregion
- #region Singleton
+ #region Singleton
- private static Lazy<OperationProfiles> _instanceHolder =
- new(() => new OperationProfiles());
+ private static Lazy<OperationProfiles> _instanceHolder =
+ new(() => new OperationProfiles());
- /// <summary>
- /// Instance of <see cref="UserSettings"/> (singleton)
- /// </summary>
- public static OperationProfiles Instance => _instanceHolder.Value;
+ /// <summary>
+ /// Instance of <see cref="UserSettings"/> (singleton)
+ /// </summary>
+ public static OperationProfiles Instance => _instanceHolder.Value;
- //public static List<Operation> Operations => _instance.Operations;
- #endregion
+ //public static List<Operation> Operations => _instance.Operations;
+ #endregion
- #region Constructor
+ #region Constructor
- private OperationProfiles()
- { }
+ private OperationProfiles()
+ { }
- #endregion
+ #endregion
- #region Indexers
+ #region Indexers
- public Operation this[uint index]
- {
- get => Operations[(int) index];
- set => Operations[(int) index] = value;
- }
+ public Operation this[uint index]
+ {
+ get => Operations[(int) index];
+ set => Operations[(int) index] = value;
+ }
- public Operation this[int index]
- {
- get => Operations[index];
- set => Operations[index] = value;
- }
+ public Operation this[int index]
+ {
+ get => Operations[index];
+ set => Operations[index] = value;
+ }
- #endregion
+ #endregion
- #region Enumerable
+ #region Enumerable
- public IEnumerator<Operation> GetEnumerator()
- {
- return Operations.GetEnumerator();
- }
+ public IEnumerator<Operation> GetEnumerator()
+ {
+ return Operations.GetEnumerator();
+ }
- /*IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }*/
- #endregion
+ /*IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }*/
+ #endregion
- #region List Implementation
- public void Add(Operation item)
- {
- Operations.Add(item);
- }
+ #region List Implementation
+ public void Add(Operation item)
+ {
+ Operations.Add(item);
+ }
- public int IndexOf(Operation item)
- {
- return Operations.IndexOf(item);
- }
+ public int IndexOf(Operation item)
+ {
+ return Operations.IndexOf(item);
+ }
- public void Insert(int index, Operation item)
- {
- Operations.Insert(index, item);
- }
+ public void Insert(int index, Operation item)
+ {
+ Operations.Insert(index, item);
+ }
- public bool Remove(Operation item)
- {
- return Operations.Remove(item);
- }
+ public bool Remove(Operation item)
+ {
+ return Operations.Remove(item);
+ }
- public void RemoveAt(int index)
- {
- Operations.RemoveAt(index);
- }
+ public void RemoveAt(int index)
+ {
+ Operations.RemoveAt(index);
+ }
- public void Clear()
- {
- Operations.Clear();
- }
+ public void Clear()
+ {
+ Operations.Clear();
+ }
- public bool Contains(Operation item)
- {
- return Operations.Contains(item);
- }
+ public bool Contains(Operation item)
+ {
+ return Operations.Contains(item);
+ }
- public void CopyTo(Operation[] array, int arrayIndex)
- {
- Operations.CopyTo(array, arrayIndex);
- }
+ public void CopyTo(Operation[] array, int arrayIndex)
+ {
+ Operations.CopyTo(array, arrayIndex);
+ }
+
+ public int Count => Operations.Count;
+ public bool IsReadOnly => false;
+ #endregion
+
+ #region Static Methods
+ /// <summary>
+ /// Clear all profiles
+ /// </summary>
+ /// <param name="save">True to save settings on file, otherwise false</param>
+ public static void ClearProfiles(bool save = true)
+ {
+ Instance.Clear();
+ if (save) Save();
+ }
- public int Count => Operations.Count;
- public bool IsReadOnly => false;
- #endregion
+ /// <summary>
+ /// Clear all profiles given a type
+ /// </summary>
+ /// <param name="type">Type profiles to clear</param>
+ /// <param name="save">True to save settings on file, otherwise false</param>
+ public static void ClearProfiles(Type type, bool save = true)
+ {
+ Profiles.RemoveAll(operation => operation.GetType() == type);
+ if (save) Save();
+ }
- #region Static Methods
- /// <summary>
- /// Clear all profiles
- /// </summary>
- /// <param name="save">True to save settings on file, otherwise false</param>
- public static void ClearProfiles(bool save = true)
+ /// <summary>
+ /// Load settings from file
+ /// </summary>
+ public static void Load()
+ {
+ if (!File.Exists(FilePath))
{
- Instance.Clear();
- if (save) Save();
+ return;
}
- /// <summary>
- /// Clear all profiles given a type
- /// </summary>
- /// <param name="type">Type profiles to clear</param>
- /// <param name="save">True to save settings on file, otherwise false</param>
- public static void ClearProfiles(Type type, bool save = true)
+ try
{
- Profiles.RemoveAll(operation => operation.GetType() == type);
- if (save) Save();
+ var instance = XmlExtensions.DeserializeFromFile<OperationProfiles>(FilePath);
+ _instanceHolder = new Lazy<OperationProfiles>(() => instance);
}
-
- /// <summary>
- /// Load settings from file
- /// </summary>
- public static void Load()
+ catch (Exception e)
{
- if (!File.Exists(FilePath))
- {
- return;
- }
-
- var serializer = new XmlSerializer(Instance.GetType());
- try
- {
- using var myXmlReader = new StreamReader(FilePath);
- var instance = (OperationProfiles) serializer.Deserialize(myXmlReader);
- _instanceHolder = new Lazy<OperationProfiles>(() => instance);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.ToString());
- ClearProfiles();
- }
+ Debug.WriteLine(e.ToString());
+ ClearProfiles();
}
+ }
- /// <summary>
- /// Save settings to file
- /// </summary>
- public static void Save()
+ /// <summary>
+ /// Save settings to file
+ /// </summary>
+ public static void Save()
+ {
+ try
{
- var serializer = new XmlSerializer(Instance.GetType());
- try
- {
- using var myXmlWriter = new StreamWriter(FilePath);
- serializer.Serialize(myXmlWriter, Instance);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.ToString());
- }
+ XmlExtensions.SerializeToFile(Instance, FilePath, XmlExtensions.SettingsIndent);
}
-
- public static Operation FindByName(Operation baseOperation, string profileName)
+ catch (Exception e)
{
- return Profiles.Find(operation =>
- operation.GetType() == baseOperation.GetType() &&
- (
- !string.IsNullOrWhiteSpace(profileName) && operation.ProfileName == profileName
- || operation.Equals(baseOperation)
- ));
+ Debug.WriteLine(e.ToString());
}
+ }
+ public static Operation FindByName(Operation baseOperation, string profileName)
+ {
+ return Profiles.Find(operation =>
+ operation.GetType() == baseOperation.GetType() &&
+ (
+ !string.IsNullOrWhiteSpace(profileName) && operation.ProfileName == profileName
+ || operation.Equals(baseOperation)
+ ));
+ }
- /// <summary>
- /// Adds a profile
- /// </summary>
- /// <param name="operation"></param>
- /// <param name="save"></param>
- public static void AddProfile(Operation operation, bool save = true)
- {
- Profiles.Insert(0, operation);
- if(save) Save();
- }
- /// <summary>
- /// Removes a profile
- /// </summary>
- /// <param name="operation"></param>
- /// <param name="save"></param>
- public static void RemoveProfile(Operation operation, bool save = true)
- {
- Instance.Remove(operation);
- if (save) Save();
- }
+ /// <summary>
+ /// Adds a profile
+ /// </summary>
+ /// <param name="operation"></param>
+ /// <param name="save"></param>
+ public static void AddProfile(Operation operation, bool save = true)
+ {
+ Profiles.Insert(0, operation);
+ if(save) Save();
+ }
- /// <summary>
- /// Get all profiles within a type
- /// </summary>
- /// <param name="type"></param>
- /// <returns></returns>
- public static List<Operation> GetOperations(Type type)
- => Profiles.Where(operation => operation.GetType() == type).ToList();
-
- /// <summary>
- /// Get all profiles within a type
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns></returns>
- public static List<T> GetOperations<T>()
+ /// <summary>
+ /// Removes a profile
+ /// </summary>
+ /// <param name="operation"></param>
+ /// <param name="save"></param>
+ public static void RemoveProfile(Operation operation, bool save = true)
+ {
+ Instance.Remove(operation);
+ if (save) Save();
+ }
+
+ /// <summary>
+ /// Get all profiles within a type
+ /// </summary>
+ /// <param name="type"></param>
+ /// <returns></returns>
+ public static List<Operation> GetOperations(Type type)
+ => Profiles.Where(operation => operation.GetType() == type).ToList();
+
+ /// <summary>
+ /// Get all profiles within a type
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns></returns>
+ public static List<T> GetOperations<T>()
+ {
+ var result = new List<T>();
+ foreach (var operation in Instance)
{
- var result = new List<T>();
- foreach (var operation in Instance)
+ if (operation is T operationCast)
{
- if (operation is T operationCast)
- {
- result.Add(operationCast);
- }
+ result.Add(operationCast);
}
-
- return result;
}
- #endregion
+ return result;
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/PSProfileFolder.cs b/UVtools.WPF/Structures/PSProfileFolder.cs
index 751640d..a8aaa2b 100644
--- a/UVtools.WPF/Structures/PSProfileFolder.cs
+++ b/UVtools.WPF/Structures/PSProfileFolder.cs
@@ -7,142 +7,141 @@ using Avalonia.Controls;
using Avalonia.Media;
using UVtools.Core.Objects;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+public class PSProfileFolder : BindableBase
{
- public class PSProfileFolder : BindableBase
- {
- public static string AssetsPrusaSlicer => Path.Combine(App.ApplicationPath, "Assets", "PrusaSlicer");
- private RangeObservableCollection<CheckBox> _items = new ();
- private ushort _installed;
- private ushort _updates;
+ public static string AssetsPrusaSlicer => Path.Combine(App.ApplicationPath, "Assets", "PrusaSlicer");
+ private RangeObservableCollection<CheckBox> _items = new ();
+ private ushort _installed;
+ private ushort _updates;
- public enum FolderType
- {
- Print,
- Printer
- }
+ public enum FolderType
+ {
+ Print,
+ Printer
+ }
- public FolderType Type { get; }
+ public FolderType Type { get; }
- public bool IsSuperSlicer { get; }
+ public bool IsSuperSlicer { get; }
- public string SourcePath { get; }
- public string TargetPath { get; }
+ public string SourcePath { get; }
+ public string TargetPath { get; }
- public RangeObservableCollection<CheckBox> Items
- {
- get => _items;
- set => RaiseAndSetIfChanged(ref _items, value);
- }
+ public RangeObservableCollection<CheckBox> Items
+ {
+ get => _items;
+ set => RaiseAndSetIfChanged(ref _items, value);
+ }
- public ushort Installed
- {
- get => _installed;
- set => RaiseAndSetIfChanged(ref _installed, value);
- }
+ public ushort Installed
+ {
+ get => _installed;
+ set => RaiseAndSetIfChanged(ref _installed, value);
+ }
- public ushort Updates
- {
- get => _updates;
- set => RaiseAndSetIfChanged(ref _updates, value);
- }
+ public ushort Updates
+ {
+ get => _updates;
+ set => RaiseAndSetIfChanged(ref _updates, value);
+ }
- public string SelectedFiles
+ public string SelectedFiles
+ {
+ get
{
- get
+ StringBuilder sb = new();
+ foreach (var item in Items)
{
- StringBuilder sb = new();
- foreach (var item in Items)
- {
- if (!item.IsChecked.HasValue || !item.IsChecked.Value) continue;
- sb.AppendLine(item.Content.ToString());
- }
-
- return sb.ToString();
+ if (!item.IsChecked.HasValue || !item.IsChecked.Value) continue;
+ sb.AppendLine(item.Content.ToString());
}
- }
- public PSProfileFolder(FolderType type, bool isSuperSlicer = false)
- {
- Type = type;
- IsSuperSlicer = isSuperSlicer;
+ return sb.ToString();
+ }
+ }
- switch (type)
- {
- case FolderType.Print:
- SourcePath = Path.Combine(AssetsPrusaSlicer, "sla_print");
- TargetPath = Path.Combine(App.GetPrusaSlicerDirectory(isSuperSlicer), "sla_print");
- break;
- case FolderType.Printer:
- SourcePath = Path.Combine(AssetsPrusaSlicer, "printer");
- TargetPath = Path.Combine(App.GetPrusaSlicerDirectory(isSuperSlicer), "printer");
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, null);
- }
+ public PSProfileFolder(FolderType type, bool isSuperSlicer = false)
+ {
+ Type = type;
+ IsSuperSlicer = isSuperSlicer;
- Reset();
+ switch (type)
+ {
+ case FolderType.Print:
+ SourcePath = Path.Combine(AssetsPrusaSlicer, "sla_print");
+ TargetPath = Path.Combine(App.GetPrusaSlicerDirectory(isSuperSlicer), "sla_print");
+ break;
+ case FolderType.Printer:
+ SourcePath = Path.Combine(AssetsPrusaSlicer, "printer");
+ TargetPath = Path.Combine(App.GetPrusaSlicerDirectory(isSuperSlicer), "printer");
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
- public void Reset()
- {
- Items.Clear();
- Updates = 0;
- Installed = 0;
- if (!Directory.Exists(SourcePath)) return;
- DirectoryInfo di = new(SourcePath);
- var files = di.GetFiles("*.ini");
- if (files.Length == 0) return;
+ Reset();
+ }
+
+ public void Reset()
+ {
+ Items.Clear();
+ Updates = 0;
+ Installed = 0;
+ if (!Directory.Exists(SourcePath)) return;
+ DirectoryInfo di = new(SourcePath);
+ var files = di.GetFiles("*.ini");
+ if (files.Length == 0) return;
- bool folderExists = Directory.Exists(TargetPath);
+ bool folderExists = Directory.Exists(TargetPath);
- for (int i = 0; i < files.Length; i++)
+ for (int i = 0; i < files.Length; i++)
+ {
+ Items.Add(new CheckBox
{
- Items.Add(new CheckBox
- {
- Content = Path.GetFileNameWithoutExtension(files[i].Name),
- Tag = files[i]
- });
-
- if (!folderExists) continue;
- var targetFile = $"{TargetPath}{Path.DirectorySeparatorChar}{files[i].Name}";
- FileInfo targetFileInfo = new(targetFile);
- if (targetFileInfo.Exists)
+ Content = Path.GetFileNameWithoutExtension(files[i].Name),
+ Tag = files[i]
+ });
+
+ if (!folderExists) continue;
+ var targetFile = $"{TargetPath}{Path.DirectorySeparatorChar}{files[i].Name}";
+ FileInfo targetFileInfo = new(targetFile);
+ if (targetFileInfo.Exists)
+ {
+ Installed++;
+ if (targetFileInfo.Length != files[i].Length || !StaticObjects.GetHashSha256(targetFileInfo.FullName).SequenceEqual(StaticObjects.GetHashSha256(files[i].FullName)))
{
- Installed++;
- if (targetFileInfo.Length != files[i].Length || !StaticObjects.GetHashSha256(targetFileInfo.FullName).SequenceEqual(StaticObjects.GetHashSha256(files[i].FullName)))
- {
- Items[i].Foreground = Brushes.Red;
- Items[i].IsChecked = true;
- Updates++;
- }
- else
- {
- Items[i].Foreground = Brushes.Green;
- Items[i].IsEnabled = false;
- }
+ Items[i].Foreground = Brushes.Red;
+ Items[i].IsChecked = true;
+ Updates++;
}
else
{
- Updates++;
+ Items[i].Foreground = Brushes.Green;
+ Items[i].IsEnabled = false;
}
}
+ else
+ {
+ Updates++;
+ }
}
+ }
- public void SelectNone()
+ public void SelectNone()
+ {
+ foreach (var checkBox in Items)
{
- foreach (var checkBox in Items)
- {
- checkBox.IsChecked = false;
- }
+ checkBox.IsChecked = false;
}
+ }
- public void SelectAll()
+ public void SelectAll()
+ {
+ foreach (var checkBox in Items)
{
- foreach (var checkBox in Items)
- {
- checkBox.IsChecked = checkBox.IsEnabled;
- }
+ checkBox.IsChecked = checkBox.IsEnabled;
}
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/PixelPicker.cs b/UVtools.WPF/Structures/PixelPicker.cs
index e7c78b3..cd4c30d 100644
--- a/UVtools.WPF/Structures/PixelPicker.cs
+++ b/UVtools.WPF/Structures/PixelPicker.cs
@@ -1,64 +1,63 @@
using System.Drawing;
using UVtools.Core.Objects;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+public class PixelPicker : BindableBase
{
- public class PixelPicker : BindableBase
+ private bool _isSet;
+ private Point _location;
+ private PointF _lcdLocation;
+ private byte _brightness;
+
+ public bool IsSet
{
- private bool _isSet;
- private Point _location;
- private PointF _lcdLocation;
- private byte _brightness;
+ get => _isSet;
+ private set => RaiseAndSetIfChanged(ref _isSet, value);
+ }
- public bool IsSet
- {
- get => _isSet;
- private set => RaiseAndSetIfChanged(ref _isSet, value);
- }
+ public Point Location
+ {
+ get => _location;
+ set => RaiseAndSetIfChanged(ref _location, value);
+ }
- public Point Location
- {
- get => _location;
- set => RaiseAndSetIfChanged(ref _location, value);
- }
+ public PointF LcdLocation
+ {
+ get => _lcdLocation;
+ set => RaiseAndSetIfChanged(ref _lcdLocation, value);
+ }
- public PointF LcdLocation
- {
- get => _lcdLocation;
- set => RaiseAndSetIfChanged(ref _lcdLocation, value);
- }
+ public byte Brightness
+ {
+ get => _brightness;
+ private set => RaiseAndSetIfChanged(ref _brightness, value);
+ }
- public byte Brightness
- {
- get => _brightness;
- private set => RaiseAndSetIfChanged(ref _brightness, value);
- }
+ public void Set(Point location, byte brightness, PointF lcdLocation = default)
+ {
+ Location = location;
+ LcdLocation = lcdLocation;
+ Brightness = brightness;
+ IsSet = true;
+ }
- public void Set(Point location, byte brightness, PointF lcdLocation = default)
- {
- Location = location;
- LcdLocation = lcdLocation;
- Brightness = brightness;
- IsSet = true;
- }
+ public void Reset()
+ {
+ Location = Point.Empty;
+ LcdLocation = PointF.Empty;
+ Brightness = 0;
+ IsSet = false;
+ }
- public void Reset()
+ public override string ToString()
+ {
+ var text = $"{{X={_location.X}, Y={_location.Y}, B={_brightness}}}";
+ if (!_lcdLocation.IsEmpty)
{
- Location = Point.Empty;
- LcdLocation = PointF.Empty;
- Brightness = 0;
- IsSet = false;
+ text += $"\n{{X={_lcdLocation.X}, Y={_lcdLocation.Y} mm}}";
}
- public override string ToString()
- {
- var text = $"{{X={_location.X}, Y={_location.Y}, B={_brightness}}}";
- if (!_lcdLocation.IsEmpty)
- {
- text += $"\n{{X={_lcdLocation.X}, Y={_lcdLocation.Y} mm}}";
- }
-
- return text;
- }
+ return text;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/RecentFiles.cs b/UVtools.WPF/Structures/RecentFiles.cs
index f7dc1df..eb80e6c 100644
--- a/UVtools.WPF/Structures/RecentFiles.cs
+++ b/UVtools.WPF/Structures/RecentFiles.cs
@@ -11,192 +11,191 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+public class RecentFiles : IList<string>
{
- public class RecentFiles : IList<string>
- {
- #region Properties
- /// <summary>
- /// Default filepath for store
- /// </summary>
- private static string FilePath => Path.Combine(UserSettings.SettingsFolder, "recentfiles.dat");
+ #region Properties
+ /// <summary>
+ /// Default filepath for store
+ /// </summary>
+ private static string FilePath => Path.Combine(UserSettings.SettingsFolder, "recentfiles.dat");
+
+ private readonly List<string> _files = new();
- private readonly List<string> _files = new();
+ public byte MaxEntries { get; set; } = 40;
- public byte MaxEntries { get; set; } = 40;
+ #endregion
- #endregion
+ #region Singleton
- #region Singleton
+ private static Lazy<RecentFiles> _instanceHolder =
+ new(() => new RecentFiles());
- private static Lazy<RecentFiles> _instanceHolder =
- new(() => new RecentFiles());
+ /// <summary>
+ /// Instance of <see cref="UserSettings"/> (singleton)
+ /// </summary>
+ public static RecentFiles Instance => _instanceHolder.Value;
- /// <summary>
- /// Instance of <see cref="UserSettings"/> (singleton)
- /// </summary>
- public static RecentFiles Instance => _instanceHolder.Value;
+ //public static List<Operation> Operations => _instance.Operations;
+ #endregion
- //public static List<Operation> Operations => _instance.Operations;
- #endregion
+ #region Constructor
- #region Constructor
+ private RecentFiles()
+ { }
- private RecentFiles()
- { }
+ #endregion
- #endregion
+ #region Static Methods
+ /// <summary>
+ /// Clear all files
+ /// </summary>
+ /// <param name="save">True to save settings on file, otherwise false</param>
+ public static void ClearFiles(bool save = true)
+ {
+ Instance.Clear();
+ if (save) Save();
+ }
- #region Static Methods
- /// <summary>
- /// Clear all files
- /// </summary>
- /// <param name="save">True to save settings on file, otherwise false</param>
- public static void ClearFiles(bool save = true)
+ /// <summary>
+ /// Load settings from file
+ /// </summary>
+ public static void Load()
+ {
+ if (!File.Exists(FilePath))
{
- Instance.Clear();
- if (save) Save();
+ return;
}
- /// <summary>
- /// Load settings from file
- /// </summary>
- public static void Load()
- {
- if (!File.Exists(FilePath))
- {
- return;
- }
+ Instance.Clear();
- Instance.Clear();
+ try
+ {
+ using var tr = new StreamReader(FilePath);
- try
+ string path;
+ while ((path = tr.ReadLine()) is not null)
{
- using var tr = new StreamReader(FilePath);
+ if(string.IsNullOrWhiteSpace(path)) continue;
- string path;
- while ((path = tr.ReadLine()) is not null)
+ try
{
- if(string.IsNullOrWhiteSpace(path)) continue;
-
- try
- {
- Path.GetFullPath(path);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.ToString());
- continue;
- }
-
- Instance.Add(path);
+ Path.GetFullPath(path);
}
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ continue;
+ }
+
+ Instance.Add(path);
}
- catch (Exception e)
- {
- Debug.WriteLine(e.ToString());
- }
}
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ }
+ }
- /// <summary>
- /// Save settings to file
- /// </summary>
- public static void Save()
+ /// <summary>
+ /// Save settings to file
+ /// </summary>
+ public static void Save()
+ {
+ try
{
- try
- {
- using var tw = new StreamWriter(FilePath);
+ using var tw = new StreamWriter(FilePath);
- foreach (var file in Instance)
- {
- tw.WriteLine(file);
- }
- }
- catch (Exception e)
+ foreach (var file in Instance)
{
- Debug.WriteLine(e.ToString());
+ tw.WriteLine(file);
}
}
-
- public static int PurgenNonExistingFiles(bool save = true)
+ catch (Exception e)
{
- Load();
- var count = Instance.RemoveAll(s => !File.Exists(s));
- if(save && count > 0) Save();
- return count;
+ Debug.WriteLine(e.ToString());
}
+ }
- #endregion
+ public static int PurgenNonExistingFiles(bool save = true)
+ {
+ Load();
+ var count = Instance.RemoveAll(s => !File.Exists(s));
+ if(save && count > 0) Save();
+ return count;
+ }
- #region List Implementation
- public IEnumerator<string> GetEnumerator()
- {
- return _files.GetEnumerator();
- }
+ #endregion
- IEnumerator IEnumerable.GetEnumerator()
- {
- return ((IEnumerable)_files).GetEnumerator();
- }
+ #region List Implementation
+ public IEnumerator<string> GetEnumerator()
+ {
+ return _files.GetEnumerator();
+ }
- public void Add(string item)
- {
- _files.RemoveAll(s => s == item);
- if (Count >= MaxEntries) return;
- _files.Add(item);
- }
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)_files).GetEnumerator();
+ }
- public void Clear()
- {
- _files.Clear();
- }
+ public void Add(string item)
+ {
+ _files.RemoveAll(s => s == item);
+ if (Count >= MaxEntries) return;
+ _files.Add(item);
+ }
- public bool Contains(string item)
- {
- return _files.Contains(item);
- }
+ public void Clear()
+ {
+ _files.Clear();
+ }
- public void CopyTo(string[] array, int arrayIndex)
- {
- _files.CopyTo(array, arrayIndex);
- }
+ public bool Contains(string item)
+ {
+ return _files.Contains(item);
+ }
- public bool Remove(string item)
- {
- return _files.Remove(item);
- }
+ public void CopyTo(string[] array, int arrayIndex)
+ {
+ _files.CopyTo(array, arrayIndex);
+ }
- public int Count => _files.Count;
+ public bool Remove(string item)
+ {
+ return _files.Remove(item);
+ }
- public bool IsReadOnly => ((ICollection<string>)_files).IsReadOnly;
+ public int Count => _files.Count;
- public int IndexOf(string item)
- {
- return _files.IndexOf(item);
- }
+ public bool IsReadOnly => ((ICollection<string>)_files).IsReadOnly;
- public void Insert(int index, string item)
- {
- _files.RemoveAll(s => s == item);
- _files.Insert(index, item);
- while (Count > MaxEntries)
- {
- RemoveAt(Count-1);
- }
- }
+ public int IndexOf(string item)
+ {
+ return _files.IndexOf(item);
+ }
- public void RemoveAt(int index)
+ public void Insert(int index, string item)
+ {
+ _files.RemoveAll(s => s == item);
+ _files.Insert(index, item);
+ while (Count > MaxEntries)
{
- _files.RemoveAt(index);
+ RemoveAt(Count-1);
}
+ }
+
+ public void RemoveAt(int index)
+ {
+ _files.RemoveAt(index);
+ }
- public int RemoveAll(Predicate<string> match) => _files.RemoveAll(match);
+ public int RemoveAll(Predicate<string> match) => _files.RemoveAll(match);
- public string this[int index]
- {
- get => _files[index];
- set => _files[index] = value;
- }
- #endregion
+ public string this[int index]
+ {
+ get => _files[index];
+ set => _files[index] = value;
}
-}
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/Structures/SlicerProperty.cs b/UVtools.WPF/Structures/SlicerProperty.cs
index 29b0814..af9fe0b 100644
--- a/UVtools.WPF/Structures/SlicerProperty.cs
+++ b/UVtools.WPF/Structures/SlicerProperty.cs
@@ -1,36 +1,35 @@
using UVtools.Core.Objects;
-namespace UVtools.WPF.Structures
+namespace UVtools.WPF.Structures;
+
+public class SlicerProperty : BindableBase
{
- public class SlicerProperty : BindableBase
- {
- private string _name;
- private string _value;
- private string _group;
+ private string _name;
+ private string _value;
+ private string _group;
- public string Name
- {
- get => _name;
- set => RaiseAndSetIfChanged(ref _name, value);
- }
+ public string Name
+ {
+ get => _name;
+ set => RaiseAndSetIfChanged(ref _name, value);
+ }
- public string Value
- {
- get => _value;
- set => RaiseAndSetIfChanged(ref _value, value);
- }
+ public string Value
+ {
+ get => _value;
+ set => RaiseAndSetIfChanged(ref _value, value);
+ }
- public string Group
- {
- get => _group;
- set => RaiseAndSetIfChanged(ref _group, value);
- }
+ public string Group
+ {
+ get => _group;
+ set => RaiseAndSetIfChanged(ref _group, value);
+ }
- public SlicerProperty(string name, string value, string group = null)
- {
- _name = name;
- _value = value;
- _group = group;
- }
+ public SlicerProperty(string name, string value, string group = null)
+ {
+ _name = name;
+ _value = value;
+ _group = group;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 30a1c47..3a34f57 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<AssemblyName>UVtools</AssemblyName>
<ApplicationIcon>UVtools.ico</ApplicationIcon>
<Authors>Tiago Conceição</Authors>
@@ -12,12 +12,13 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>2.29.0</Version>
+ <Version>3.0.0</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageTags>msla, dlp, resin, printer, slicer, 3d printing, image processing, layers</PackageTags>
+ <Nullable>disable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -38,13 +39,15 @@
<NoWarn>1701;1702;</NoWarn>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="0.10.12" />
+ <PackageReference Include="Avalonia" Version="0.10.13" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
- <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.12" />
- <PackageReference Include="Avalonia.Desktop" Version="0.10.12" />
- <PackageReference Include="Avalonia.Diagnostics" Version="0.10.12" />
+ <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.13" />
+ <PackageReference Include="Avalonia.Desktop" Version="0.10.13" />
+ <PackageReference Include="Avalonia.Diagnostics" Version="0.10.13" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.5.5.4823" />
<PackageReference Include="MessageBox.Avalonia" Version="1.8.1-night" />
+ <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="4.1.0" />
+ <PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="4.1.0" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.12" />
</ItemGroup>
<ItemGroup>
@@ -65,9 +68,6 @@
<None Update="UVtools.sh">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Update="x64\libcvextern.so">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
<None Include="..\CHANGELOG.md" Link="CHANGELOG.md" />
<None Include="..\CREDITS.md" Link="CREDITS.md" />
<None Include="..\LICENSE">
@@ -96,4 +96,9 @@
</AvaloniaResource>
<AvaloniaResource Include="Assets\Icons\*" />
</ItemGroup>
+ <ItemGroup>
+ <Compile Update="Windows\SuggestionSettingsWindow.axaml.cs">
+ <DependentUpon>SuggestionSettingsWindow.axaml</DependentUpon>
+ </Compile>
+ </ItemGroup>
</Project>
diff --git a/UVtools.WPF/UVtools.WPF.csproj.DotSettings b/UVtools.WPF/UVtools.WPF.csproj.DotSettings
deleted file mode 100644
index 6162834..0000000
--- a/UVtools.WPF/UVtools.WPF.csproj.DotSettings
+++ /dev/null
@@ -1,2 +0,0 @@
-<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
- <s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp90</s:String></wpf:ResourceDictionary> \ No newline at end of file
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index 9532110..d73d924 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -21,1681 +21,1622 @@ using UVtools.Core.Network;
using UVtools.Core.Objects;
using Color=UVtools.WPF.Structures.Color;
-namespace UVtools.WPF
+namespace UVtools.WPF;
+
+[Serializable]
+public sealed class UserSettings : BindableBase
{
+ #region Constants
+ public const ushort SETTINGS_VERSION = 6;
+ #endregion
+
+ #region Sub classes
+
+ #region General
[Serializable]
- public sealed class UserSettings : BindableBase
+ public sealed class GeneralUserSettings : BindableBase
{
- #region Constants
- public const ushort SETTINGS_VERSION = 6;
- #endregion
-
- #region Sub classes
-
- #region General
- [Serializable]
- public sealed class GeneralUserSettings : BindableBase
- {
- private bool _startMaximized = true;
- private bool _checkForUpdatesOnStartup = true;
- private bool _loadDemoFileOnStartup = true;
- private bool _loadLastRecentFileOnStartup;
- private int _maxDegreeOfParallelism = -1;
-
- private bool _windowsCanResize;
- private bool _windowsTakeIntoAccountScreenScaling = true;
- private ushort _windowsHorizontalMargin = 100;
- private ushort _windowsVerticalMargin = 60;
- private bool _expandDescriptions = true;
- private byte _defaultOpenFileExtensionIndex;
- private string _defaultDirectoryOpenFile;
- private string _defaultDirectorySaveFile;
- private string _defaultDirectoryExtractFile;
- private string _defaultDirectoryConvertFile;
- private string _defaultDirectoryScripts;
- private bool _promptOverwriteFileSave = true;
- private string _fileSaveNamePrefix;
- private string _fileSaveNameSuffix = "_copy";
- private bool _sendToPromptForRemovableDeviceEject = true;
- private RangeObservableCollection<MappedDevice> _sendToCustomLocations = new();
- private RangeObservableCollection<MappedProcess> _sendToProcess = new();
-
-
- public bool StartMaximized
- {
- get => _startMaximized;
- set => RaiseAndSetIfChanged(ref _startMaximized, value);
- }
+ private App.ApplicationTheme _theme = App.ApplicationTheme.FluentLight;
+ private bool _startMaximized = true;
+ private bool _checkForUpdatesOnStartup = true;
+ private bool _loadDemoFileOnStartup = true;
+ private bool _loadLastRecentFileOnStartup;
+ private int _maxDegreeOfParallelism = -1;
+
+ private bool _windowsCanResize;
+ private bool _windowsTakeIntoAccountScreenScaling = true;
+ private ushort _windowsHorizontalMargin = 100;
+ private ushort _windowsVerticalMargin = 60;
+ private bool _expandDescriptions = true;
+ private byte _defaultOpenFileExtensionIndex;
+ private string _defaultDirectoryOpenFile;
+ private string _defaultDirectorySaveFile;
+ private string _defaultDirectoryExtractFile;
+ private string _defaultDirectoryConvertFile;
+ private string _defaultDirectoryScripts;
+ private bool _promptOverwriteFileSave = true;
+ private string _fileSaveNamePrefix;
+ private string _fileSaveNameSuffix = "_copy";
+ private bool _sendToPromptForRemovableDeviceEject = true;
+ private RangeObservableCollection<MappedDevice> _sendToCustomLocations = new();
+ private RangeObservableCollection<MappedProcess> _sendToProcess = new();
+
+ public App.ApplicationTheme Theme
+ {
+ get => _theme;
+ set => RaiseAndSetIfChanged(ref _theme, value);
+ }
- public bool CheckForUpdatesOnStartup
- {
- get => _checkForUpdatesOnStartup;
- set => RaiseAndSetIfChanged(ref _checkForUpdatesOnStartup, value);
- }
+ public bool StartMaximized
+ {
+ get => _startMaximized;
+ set => RaiseAndSetIfChanged(ref _startMaximized, value);
+ }
- public bool LoadDemoFileOnStartup
- {
- get => _loadDemoFileOnStartup;
- set => RaiseAndSetIfChanged(ref _loadDemoFileOnStartup, value);
- }
+ public bool CheckForUpdatesOnStartup
+ {
+ get => _checkForUpdatesOnStartup;
+ set => RaiseAndSetIfChanged(ref _checkForUpdatesOnStartup, value);
+ }
- public bool LoadLastRecentFileOnStartup
- {
- get => _loadLastRecentFileOnStartup;
- set => RaiseAndSetIfChanged(ref _loadLastRecentFileOnStartup, value);
- }
+ public bool LoadDemoFileOnStartup
+ {
+ get => _loadDemoFileOnStartup;
+ set => RaiseAndSetIfChanged(ref _loadDemoFileOnStartup, value);
+ }
- /// <summary>
- /// Gets or sets the maximum number of concurrent tasks enabled by a ParallelOptions instance.
- /// </summary>
- public int MaxDegreeOfParallelism
- {
- get => _maxDegreeOfParallelism;
- set => RaiseAndSetIfChanged(ref _maxDegreeOfParallelism, Math.Min(value, Environment.ProcessorCount));
- }
+ public bool LoadLastRecentFileOnStartup
+ {
+ get => _loadLastRecentFileOnStartup;
+ set => RaiseAndSetIfChanged(ref _loadLastRecentFileOnStartup, value);
+ }
- public bool WindowsCanResize
- {
- get => _windowsCanResize;
- set => RaiseAndSetIfChanged(ref _windowsCanResize, value);
- }
+ /// <summary>
+ /// Gets or sets the maximum number of concurrent tasks enabled by a ParallelOptions instance.
+ /// </summary>
+ public int MaxDegreeOfParallelism
+ {
+ get => _maxDegreeOfParallelism;
+ set => RaiseAndSetIfChanged(ref _maxDegreeOfParallelism, Math.Min(value, Environment.ProcessorCount));
+ }
- public bool WindowsTakeIntoAccountScreenScaling
- {
- get => _windowsTakeIntoAccountScreenScaling;
- set => RaiseAndSetIfChanged(ref _windowsTakeIntoAccountScreenScaling, value);
- }
+ public bool WindowsCanResize
+ {
+ get => _windowsCanResize;
+ set => RaiseAndSetIfChanged(ref _windowsCanResize, value);
+ }
- public ushort WindowsHorizontalMargin
- {
- get => _windowsHorizontalMargin;
- set => RaiseAndSetIfChanged(ref _windowsHorizontalMargin, value);
- }
+ public bool WindowsTakeIntoAccountScreenScaling
+ {
+ get => _windowsTakeIntoAccountScreenScaling;
+ set => RaiseAndSetIfChanged(ref _windowsTakeIntoAccountScreenScaling, value);
+ }
- public ushort WindowsVerticalMargin
- {
- get => _windowsVerticalMargin;
- set => RaiseAndSetIfChanged(ref _windowsVerticalMargin, value);
- }
+ public ushort WindowsHorizontalMargin
+ {
+ get => _windowsHorizontalMargin;
+ set => RaiseAndSetIfChanged(ref _windowsHorizontalMargin, value);
+ }
- public bool ExpandDescriptions
- {
- get => _expandDescriptions;
- set => RaiseAndSetIfChanged(ref _expandDescriptions, value);
- }
+ public ushort WindowsVerticalMargin
+ {
+ get => _windowsVerticalMargin;
+ set => RaiseAndSetIfChanged(ref _windowsVerticalMargin, value);
+ }
- public byte DefaultOpenFileExtensionIndex
- {
- get => _defaultOpenFileExtensionIndex;
- set => RaiseAndSetIfChanged(ref _defaultOpenFileExtensionIndex, value);
- }
+ public bool ExpandDescriptions
+ {
+ get => _expandDescriptions;
+ set => RaiseAndSetIfChanged(ref _expandDescriptions, value);
+ }
- public string DefaultDirectoryOpenFile
- {
- get => _defaultDirectoryOpenFile;
- set => RaiseAndSetIfChanged(ref _defaultDirectoryOpenFile, value);
- }
+ public byte DefaultOpenFileExtensionIndex
+ {
+ get => _defaultOpenFileExtensionIndex;
+ set => RaiseAndSetIfChanged(ref _defaultOpenFileExtensionIndex, value);
+ }
- public string DefaultDirectorySaveFile
- {
- get => _defaultDirectorySaveFile;
- set => RaiseAndSetIfChanged(ref _defaultDirectorySaveFile, value);
- }
+ public string DefaultDirectoryOpenFile
+ {
+ get => _defaultDirectoryOpenFile;
+ set => RaiseAndSetIfChanged(ref _defaultDirectoryOpenFile, value);
+ }
- public string DefaultDirectoryExtractFile
- {
- get => _defaultDirectoryExtractFile;
- set => RaiseAndSetIfChanged(ref _defaultDirectoryExtractFile, value);
- }
+ public string DefaultDirectorySaveFile
+ {
+ get => _defaultDirectorySaveFile;
+ set => RaiseAndSetIfChanged(ref _defaultDirectorySaveFile, value);
+ }
- public string DefaultDirectoryConvertFile
- {
- get => _defaultDirectoryConvertFile;
- set => RaiseAndSetIfChanged(ref _defaultDirectoryConvertFile, value);
- }
+ public string DefaultDirectoryExtractFile
+ {
+ get => _defaultDirectoryExtractFile;
+ set => RaiseAndSetIfChanged(ref _defaultDirectoryExtractFile, value);
+ }
- public string DefaultDirectoryScripts
- {
- get => _defaultDirectoryScripts;
- set => RaiseAndSetIfChanged(ref _defaultDirectoryScripts, value);
- }
+ public string DefaultDirectoryConvertFile
+ {
+ get => _defaultDirectoryConvertFile;
+ set => RaiseAndSetIfChanged(ref _defaultDirectoryConvertFile, value);
+ }
+ public string DefaultDirectoryScripts
+ {
+ get => _defaultDirectoryScripts;
+ set => RaiseAndSetIfChanged(ref _defaultDirectoryScripts, value);
+ }
- public bool PromptOverwriteFileSave
- {
- get => _promptOverwriteFileSave;
- set => RaiseAndSetIfChanged(ref _promptOverwriteFileSave, value);
- }
- public string FileSaveNamePrefix
- {
- get => _fileSaveNamePrefix;
- set => RaiseAndSetIfChanged(ref _fileSaveNamePrefix, value);
- }
+ public bool PromptOverwriteFileSave
+ {
+ get => _promptOverwriteFileSave;
+ set => RaiseAndSetIfChanged(ref _promptOverwriteFileSave, value);
+ }
- public string FileSaveNameSuffix
- {
- get => _fileSaveNameSuffix;
- set => RaiseAndSetIfChanged(ref _fileSaveNameSuffix, value);
- }
+ public string FileSaveNamePrefix
+ {
+ get => _fileSaveNamePrefix;
+ set => RaiseAndSetIfChanged(ref _fileSaveNamePrefix, value);
+ }
- public bool SendToPromptForRemovableDeviceEject
- {
- get => _sendToPromptForRemovableDeviceEject;
- set => RaiseAndSetIfChanged(ref _sendToPromptForRemovableDeviceEject, value);
- }
+ public string FileSaveNameSuffix
+ {
+ get => _fileSaveNameSuffix;
+ set => RaiseAndSetIfChanged(ref _fileSaveNameSuffix, value);
+ }
- public RangeObservableCollection<MappedDevice> SendToCustomLocations
- {
- get => _sendToCustomLocations;
- set => RaiseAndSetIfChanged(ref _sendToCustomLocations, value);
- }
+ public bool SendToPromptForRemovableDeviceEject
+ {
+ get => _sendToPromptForRemovableDeviceEject;
+ set => RaiseAndSetIfChanged(ref _sendToPromptForRemovableDeviceEject, value);
+ }
- public RangeObservableCollection<MappedProcess> SendToProcess
- {
- get => _sendToProcess;
- set => RaiseAndSetIfChanged(ref _sendToProcess, value);
- }
+ public RangeObservableCollection<MappedDevice> SendToCustomLocations
+ {
+ get => _sendToCustomLocations;
+ set => RaiseAndSetIfChanged(ref _sendToCustomLocations, value);
+ }
+
+ public RangeObservableCollection<MappedProcess> SendToProcess
+ {
+ get => _sendToProcess;
+ set => RaiseAndSetIfChanged(ref _sendToProcess, value);
+ }
- public GeneralUserSettings() { }
+ public GeneralUserSettings() { }
- public GeneralUserSettings Clone()
+ public GeneralUserSettings Clone()
+ {
+ return this.CloneByXmlSerialization();
+ /*var clone = MemberwiseClone() as GeneralUserSettings;
+ _sendToCustomLocations ??= new();
+ clone.SendToCustomLocations = new RangeObservableCollection<MappedDevice>();
+ foreach (var location in _sendToCustomLocations)
{
- return this.CloneByXmlSerialization();
- /*var clone = MemberwiseClone() as GeneralUserSettings;
- _sendToCustomLocations ??= new();
- clone.SendToCustomLocations = new RangeObservableCollection<MappedDevice>();
- foreach (var location in _sendToCustomLocations)
- {
- clone.SendToCustomLocations.Add(location.Clone());
- }
- return clone;*/
+ clone.SendToCustomLocations.Add(location.Clone());
}
+ return clone;*/
}
- #endregion
-
- #region Layer Preview
- [Serializable]
- public sealed class LayerPreviewUserSettings : BindableBase
- {
- private Color _tooltipOverlayBackgroundColor = new(210, 255, 255, 192);
- private bool _tooltipOverlay = true;
- private Color _volumeBoundsOutlineColor = new(255, 0, 255, 0);
- private byte _volumeBoundsOutlineThickness = 3;
- private bool _volumeBoundsOutline = true;
- private Color _layerBoundsOutlineColor = new(255, 45, 150, 45);
- private byte _layerBoundsOutlineThickness = 3;
- private bool _layerBoundsOutline = false;
- private Color _contourBoundsOutlineColor = new(255, 50, 100, 50);
- private byte _contourBoundsOutlineThickness = 2;
- private bool _contourBoundsOutline = false;
- private Color _hollowOutlineColor = new(255, 255, 165, 0);
- private sbyte _hollowOutlineLineThickness = 5;
- private bool _hollowOutline = false;
- private Color _centroidOutlineColor = new(255, 255, 0, 0);
- private byte _centroidOutlineDiameter = 8;
- private bool _centroidOutlineHollow = false;
- private bool _centroidOutline = false;
- private Color _maskOutlineColor = new(255, 42, 157, 244);
- private sbyte _maskOutlineLineThickness = 10;
- private bool _maskClearRoiAfterSet = true;
- private Color _previousLayerDifferenceColor = new(255, 81, 131, 82);
- private Color _nextLayerDifferenceColor = new(255, 81, 249, 252);
- private Color _bothLayerDifferenceColor = new(255, 246, 240, 216);
- private bool _showLayerDifference = false;
- private bool _layerDifferenceHighlightSimilarityInstead = false;
- private bool _useIssueColorOnTracker = true;
- private Color _islandColor = new(255, 255, 255, 0);
- private Color _islandHighlightColor = new(255, 255, 215, 0);
- private Color _overhangColor = new(255, 255, 105, 180);
- private Color _overhangHighlightColor = new(255, 255, 20, 147);
- private Color _resinTrapColor = new(255, 255, 165, 0);
- private Color _resinTrapHighlightColor = new(255, 255, 127, 0);
- private Color _suctionCupColor = new(255, 180, 235, 255);
- private Color _suctionCupHighlightColor = new(255, 77, 207, 255);
- private Color _touchingBoundsColor = new(255, 255, 0, 0);
- private Color _crosshairColor = new(255, 255, 0, 0);
- private bool _zoomToFitPrintVolumeBounds = true;
- private byte _zoomLockLevelIndex = 7;
- private bool _zoomIssues = true;
- private bool _crosshairShowOnlyOnSelectedIssues = false;
- private byte _crosshairFadeLevelIndex = 5;
- private uint _crosshairLength = 20;
- private byte _crosshairMargin = 5;
- private bool _autoRotateLayerBestView = true;
- private bool _autoFlipLayerIfMirrored = true;
- private bool _layerZoomToFitOnLoad = true;
- private bool _showBackgroudGrid;
- private ushort _layerSliderDebounce;
-
- public Color TooltipOverlayBackgroundColor
- {
- get => _tooltipOverlayBackgroundColor;
- set
- {
- RaiseAndSetIfChanged(ref _tooltipOverlayBackgroundColor, value);
- RaisePropertyChanged(nameof(TooltipOverlayBackgroundBrush));
- }
- }
+ }
+ #endregion
- [XmlIgnore]
- public SolidColorBrush TooltipOverlayBackgroundBrush
+ #region Layer Preview
+ [Serializable]
+ public sealed class LayerPreviewUserSettings : BindableBase
+ {
+ private Color _tooltipOverlayBackgroundColor = new(210, 226, 223, 215);
+ private bool _tooltipOverlay = true;
+ private Color _volumeBoundsOutlineColor = new(255, 0, 255, 0);
+ private byte _volumeBoundsOutlineThickness = 3;
+ private bool _volumeBoundsOutline = true;
+ private Color _layerBoundsOutlineColor = new(255, 45, 150, 45);
+ private byte _layerBoundsOutlineThickness = 3;
+ private bool _layerBoundsOutline = false;
+ private Color _contourBoundsOutlineColor = new(255, 50, 100, 50);
+ private byte _contourBoundsOutlineThickness = 2;
+ private bool _contourBoundsOutline = false;
+ private Color _hollowOutlineColor = new(255, 255, 165, 0);
+ private sbyte _hollowOutlineLineThickness = 5;
+ private bool _hollowOutline = false;
+ private Color _centroidOutlineColor = new(255, 255, 0, 0);
+ private byte _centroidOutlineDiameter = 8;
+ private bool _centroidOutlineHollow = false;
+ private bool _centroidOutline = false;
+ private Color _maskOutlineColor = new(255, 42, 157, 244);
+ private sbyte _maskOutlineLineThickness = 10;
+ private bool _maskClearRoiAfterSet = true;
+ private Color _previousLayerDifferenceColor = new(255, 81, 131, 82);
+ private Color _nextLayerDifferenceColor = new(255, 81, 249, 252);
+ private Color _bothLayerDifferenceColor = new(255, 246, 240, 216);
+ private bool _showLayerDifference = false;
+ private bool _layerDifferenceHighlightSimilarityInstead = false;
+ private bool _useIssueColorOnTracker = true;
+ private Color _islandColor = new(255, 255, 255, 0);
+ private Color _islandHighlightColor = new(255, 255, 215, 0);
+ private Color _overhangColor = new(255, 255, 105, 180);
+ private Color _overhangHighlightColor = new(255, 255, 20, 147);
+ private Color _resinTrapColor = new(255, 255, 165, 0);
+ private Color _resinTrapHighlightColor = new(255, 255, 127, 0);
+ private Color _suctionCupColor = new(255, 180, 235, 255);
+ private Color _suctionCupHighlightColor = new(255, 77, 207, 255);
+ private Color _touchingBoundsColor = new(255, 255, 0, 0);
+ private Color _crosshairColor = new(255, 255, 0, 0);
+ private bool _zoomToFitPrintVolumeBounds = true;
+ private byte _zoomLockLevelIndex = 7;
+ private bool _zoomIssues = true;
+ private bool _crosshairShowOnlyOnSelectedIssues = false;
+ private byte _crosshairFadeLevelIndex = 5;
+ private uint _crosshairLength = 20;
+ private byte _crosshairMargin = 5;
+ private bool _autoRotateLayerBestView = true;
+ private bool _autoFlipLayerIfMirrored = true;
+ private bool _layerZoomToFitOnLoad = true;
+ private bool _showBackgroudGrid;
+ private ushort _layerSliderDebounce;
+
+ public Color TooltipOverlayBackgroundColor
+ {
+ get => _tooltipOverlayBackgroundColor;
+ set
{
- get => new(_tooltipOverlayBackgroundColor.ToAvalonia());
- set => TooltipOverlayBackgroundColor = new Color(value);
+ RaiseAndSetIfChanged(ref _tooltipOverlayBackgroundColor, value);
+ RaisePropertyChanged(nameof(TooltipOverlayBackgroundBrush));
}
+ }
- public bool TooltipOverlay
- {
- get => _tooltipOverlay;
- set => RaiseAndSetIfChanged(ref _tooltipOverlay, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush TooltipOverlayBackgroundBrush
+ {
+ get => new(_tooltipOverlayBackgroundColor.ToAvalonia());
+ set => TooltipOverlayBackgroundColor = new Color(value);
+ }
- public Color VolumeBoundsOutlineColor
- {
- get => _volumeBoundsOutlineColor;
- set
- {
- RaiseAndSetIfChanged(ref _volumeBoundsOutlineColor, value);
- RaisePropertyChanged(nameof(VolumeBoundsOutlineBrush));
- }
- }
+ public bool TooltipOverlay
+ {
+ get => _tooltipOverlay;
+ set => RaiseAndSetIfChanged(ref _tooltipOverlay, value);
+ }
- [XmlIgnore]
- public SolidColorBrush VolumeBoundsOutlineBrush
+ public Color VolumeBoundsOutlineColor
+ {
+ get => _volumeBoundsOutlineColor;
+ set
{
- get => new(_volumeBoundsOutlineColor.ToAvalonia());
- set => VolumeBoundsOutlineColor = new Color(value);
+ RaiseAndSetIfChanged(ref _volumeBoundsOutlineColor, value);
+ RaisePropertyChanged(nameof(VolumeBoundsOutlineBrush));
}
+ }
- public byte VolumeBoundsOutlineThickness
- {
- get => _volumeBoundsOutlineThickness;
- set => RaiseAndSetIfChanged(ref _volumeBoundsOutlineThickness, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush VolumeBoundsOutlineBrush
+ {
+ get => new(_volumeBoundsOutlineColor.ToAvalonia());
+ set => VolumeBoundsOutlineColor = new Color(value);
+ }
- public bool VolumeBoundsOutline
- {
- get => _volumeBoundsOutline;
- set => RaiseAndSetIfChanged(ref _volumeBoundsOutline, value);
- }
+ public byte VolumeBoundsOutlineThickness
+ {
+ get => _volumeBoundsOutlineThickness;
+ set => RaiseAndSetIfChanged(ref _volumeBoundsOutlineThickness, value);
+ }
- public Color LayerBoundsOutlineColor
- {
- get => _layerBoundsOutlineColor;
- set
- {
- RaiseAndSetIfChanged(ref _layerBoundsOutlineColor, value);
- RaisePropertyChanged(nameof(LayerBoundsOutlineBrush));
- }
- }
+ public bool VolumeBoundsOutline
+ {
+ get => _volumeBoundsOutline;
+ set => RaiseAndSetIfChanged(ref _volumeBoundsOutline, value);
+ }
- [XmlIgnore]
- public SolidColorBrush LayerBoundsOutlineBrush
+ public Color LayerBoundsOutlineColor
+ {
+ get => _layerBoundsOutlineColor;
+ set
{
- get => new(_layerBoundsOutlineColor.ToAvalonia());
- set => LayerBoundsOutlineColor = new Color(value);
+ RaiseAndSetIfChanged(ref _layerBoundsOutlineColor, value);
+ RaisePropertyChanged(nameof(LayerBoundsOutlineBrush));
}
+ }
- public byte LayerBoundsOutlineThickness
- {
- get => _layerBoundsOutlineThickness;
- set => RaiseAndSetIfChanged(ref _layerBoundsOutlineThickness, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush LayerBoundsOutlineBrush
+ {
+ get => new(_layerBoundsOutlineColor.ToAvalonia());
+ set => LayerBoundsOutlineColor = new Color(value);
+ }
- public bool LayerBoundsOutline
- {
- get => _layerBoundsOutline;
- set => RaiseAndSetIfChanged(ref _layerBoundsOutline, value);
- }
+ public byte LayerBoundsOutlineThickness
+ {
+ get => _layerBoundsOutlineThickness;
+ set => RaiseAndSetIfChanged(ref _layerBoundsOutlineThickness, value);
+ }
- public Color ContourBoundsOutlineColor
- {
- get => _contourBoundsOutlineColor;
- set
- {
- RaiseAndSetIfChanged(ref _contourBoundsOutlineColor, value);
- RaisePropertyChanged(nameof(ContourBoundsOutlineBrush));
- }
- }
+ public bool LayerBoundsOutline
+ {
+ get => _layerBoundsOutline;
+ set => RaiseAndSetIfChanged(ref _layerBoundsOutline, value);
+ }
- [XmlIgnore]
- public SolidColorBrush ContourBoundsOutlineBrush
+ public Color ContourBoundsOutlineColor
+ {
+ get => _contourBoundsOutlineColor;
+ set
{
- get => new(_contourBoundsOutlineColor.ToAvalonia());
- set => ContourBoundsOutlineColor = new Color(value);
+ RaiseAndSetIfChanged(ref _contourBoundsOutlineColor, value);
+ RaisePropertyChanged(nameof(ContourBoundsOutlineBrush));
}
+ }
- public byte ContourBoundsOutlineThickness
- {
- get => _contourBoundsOutlineThickness;
- set => RaiseAndSetIfChanged(ref _contourBoundsOutlineThickness, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush ContourBoundsOutlineBrush
+ {
+ get => new(_contourBoundsOutlineColor.ToAvalonia());
+ set => ContourBoundsOutlineColor = new Color(value);
+ }
- public bool ContourBoundsOutline
- {
- get => _contourBoundsOutline;
- set => RaiseAndSetIfChanged(ref _contourBoundsOutline, value);
- }
+ public byte ContourBoundsOutlineThickness
+ {
+ get => _contourBoundsOutlineThickness;
+ set => RaiseAndSetIfChanged(ref _contourBoundsOutlineThickness, value);
+ }
- public Color HollowOutlineColor
- {
- get => _hollowOutlineColor;
- set
- {
- RaiseAndSetIfChanged(ref _hollowOutlineColor, value);
- RaisePropertyChanged(nameof(HollowOutlineBrush));
- }
- }
+ public bool ContourBoundsOutline
+ {
+ get => _contourBoundsOutline;
+ set => RaiseAndSetIfChanged(ref _contourBoundsOutline, value);
+ }
- [XmlIgnore]
- public SolidColorBrush HollowOutlineBrush
+ public Color HollowOutlineColor
+ {
+ get => _hollowOutlineColor;
+ set
{
- get => new(_hollowOutlineColor.ToAvalonia());
- set => HollowOutlineColor = new Color(value);
+ RaiseAndSetIfChanged(ref _hollowOutlineColor, value);
+ RaisePropertyChanged(nameof(HollowOutlineBrush));
}
+ }
- public sbyte HollowOutlineLineThickness
- {
- get => _hollowOutlineLineThickness;
- set => RaiseAndSetIfChanged(ref _hollowOutlineLineThickness, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush HollowOutlineBrush
+ {
+ get => new(_hollowOutlineColor.ToAvalonia());
+ set => HollowOutlineColor = new Color(value);
+ }
- public bool HollowOutline
- {
- get => _hollowOutline;
- set => RaiseAndSetIfChanged(ref _hollowOutline, value);
- }
+ public sbyte HollowOutlineLineThickness
+ {
+ get => _hollowOutlineLineThickness;
+ set => RaiseAndSetIfChanged(ref _hollowOutlineLineThickness, value);
+ }
- public Color CentroidOutlineColor
- {
- get => _centroidOutlineColor;
- set
- {
- RaiseAndSetIfChanged(ref _centroidOutlineColor, value);
- RaisePropertyChanged(nameof(CentroidOutlineBrush));
- }
- }
+ public bool HollowOutline
+ {
+ get => _hollowOutline;
+ set => RaiseAndSetIfChanged(ref _hollowOutline, value);
+ }
- [XmlIgnore]
- public SolidColorBrush CentroidOutlineBrush
+ public Color CentroidOutlineColor
+ {
+ get => _centroidOutlineColor;
+ set
{
- get => new(_centroidOutlineColor.ToAvalonia());
- set => CentroidOutlineColor = new Color(value);
+ RaiseAndSetIfChanged(ref _centroidOutlineColor, value);
+ RaisePropertyChanged(nameof(CentroidOutlineBrush));
}
+ }
- public byte CentroidOutlineDiameter
- {
- get => _centroidOutlineDiameter;
- set => RaiseAndSetIfChanged(ref _centroidOutlineDiameter, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush CentroidOutlineBrush
+ {
+ get => new(_centroidOutlineColor.ToAvalonia());
+ set => CentroidOutlineColor = new Color(value);
+ }
- public bool CentroidOutlineHollow
- {
- get => _centroidOutlineHollow;
- set => RaiseAndSetIfChanged(ref _centroidOutlineHollow, value);
- }
+ public byte CentroidOutlineDiameter
+ {
+ get => _centroidOutlineDiameter;
+ set => RaiseAndSetIfChanged(ref _centroidOutlineDiameter, value);
+ }
- public bool CentroidOutline
- {
- get => _centroidOutline;
- set => RaiseAndSetIfChanged(ref _centroidOutline, value);
- }
+ public bool CentroidOutlineHollow
+ {
+ get => _centroidOutlineHollow;
+ set => RaiseAndSetIfChanged(ref _centroidOutlineHollow, value);
+ }
- public Color MaskOutlineColor
- {
- get => _maskOutlineColor;
- set
- {
- RaiseAndSetIfChanged(ref _maskOutlineColor, value);
- RaisePropertyChanged(nameof(MaskOutlineBrush));
- }
- }
+ public bool CentroidOutline
+ {
+ get => _centroidOutline;
+ set => RaiseAndSetIfChanged(ref _centroidOutline, value);
+ }
- [XmlIgnore]
- public SolidColorBrush MaskOutlineBrush
+ public Color MaskOutlineColor
+ {
+ get => _maskOutlineColor;
+ set
{
- get => new(_maskOutlineColor.ToAvalonia());
- set => MaskOutlineColor = new Color(value);
+ RaiseAndSetIfChanged(ref _maskOutlineColor, value);
+ RaisePropertyChanged(nameof(MaskOutlineBrush));
}
+ }
- public sbyte MaskOutlineLineThickness
- {
- get => _maskOutlineLineThickness;
- set => RaiseAndSetIfChanged(ref _maskOutlineLineThickness, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush MaskOutlineBrush
+ {
+ get => new(_maskOutlineColor.ToAvalonia());
+ set => MaskOutlineColor = new Color(value);
+ }
- public bool MaskClearROIAfterSet
- {
- get => _maskClearRoiAfterSet;
- set => RaiseAndSetIfChanged(ref _maskClearRoiAfterSet, value);
- }
+ public sbyte MaskOutlineLineThickness
+ {
+ get => _maskOutlineLineThickness;
+ set => RaiseAndSetIfChanged(ref _maskOutlineLineThickness, value);
+ }
- public Color PreviousLayerDifferenceColor
- {
- get => _previousLayerDifferenceColor;
- set
- {
- RaiseAndSetIfChanged(ref _previousLayerDifferenceColor, value);
- RaisePropertyChanged(nameof(PreviousLayerDifferenceBrush));
- }
- }
+ public bool MaskClearROIAfterSet
+ {
+ get => _maskClearRoiAfterSet;
+ set => RaiseAndSetIfChanged(ref _maskClearRoiAfterSet, value);
+ }
- [XmlIgnore]
- public SolidColorBrush PreviousLayerDifferenceBrush
+ public Color PreviousLayerDifferenceColor
+ {
+ get => _previousLayerDifferenceColor;
+ set
{
- get => new(_previousLayerDifferenceColor.ToAvalonia());
- set => PreviousLayerDifferenceColor = new Color(value);
+ RaiseAndSetIfChanged(ref _previousLayerDifferenceColor, value);
+ RaisePropertyChanged(nameof(PreviousLayerDifferenceBrush));
}
+ }
- public Color NextLayerDifferenceColor
- {
- get => _nextLayerDifferenceColor;
- set
- {
- RaiseAndSetIfChanged(ref _nextLayerDifferenceColor, value);
- RaisePropertyChanged(nameof(NextLayerDifferenceBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush PreviousLayerDifferenceBrush
+ {
+ get => new(_previousLayerDifferenceColor.ToAvalonia());
+ set => PreviousLayerDifferenceColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush NextLayerDifferenceBrush
+ public Color NextLayerDifferenceColor
+ {
+ get => _nextLayerDifferenceColor;
+ set
{
- get => new(_nextLayerDifferenceColor.ToAvalonia());
- set => NextLayerDifferenceColor = new Color(value);
+ RaiseAndSetIfChanged(ref _nextLayerDifferenceColor, value);
+ RaisePropertyChanged(nameof(NextLayerDifferenceBrush));
}
+ }
- public Color BothLayerDifferenceColor
- {
- get => _bothLayerDifferenceColor;
- set
- {
- RaiseAndSetIfChanged(ref _bothLayerDifferenceColor, value);
- RaisePropertyChanged(nameof(BothLayerDifferenceBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush NextLayerDifferenceBrush
+ {
+ get => new(_nextLayerDifferenceColor.ToAvalonia());
+ set => NextLayerDifferenceColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush BothLayerDifferenceBrush
+ public Color BothLayerDifferenceColor
+ {
+ get => _bothLayerDifferenceColor;
+ set
{
- get => new(_bothLayerDifferenceColor.ToAvalonia());
- set => BothLayerDifferenceColor = new Color(value);
+ RaiseAndSetIfChanged(ref _bothLayerDifferenceColor, value);
+ RaisePropertyChanged(nameof(BothLayerDifferenceBrush));
}
+ }
- public bool ShowLayerDifference
- {
- get => _showLayerDifference;
- set => RaiseAndSetIfChanged(ref _showLayerDifference, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush BothLayerDifferenceBrush
+ {
+ get => new(_bothLayerDifferenceColor.ToAvalonia());
+ set => BothLayerDifferenceColor = new Color(value);
+ }
- public bool LayerDifferenceHighlightSimilarityInstead
- {
- get => _layerDifferenceHighlightSimilarityInstead;
- set => RaiseAndSetIfChanged(ref _layerDifferenceHighlightSimilarityInstead, value);
- }
+ public bool ShowLayerDifference
+ {
+ get => _showLayerDifference;
+ set => RaiseAndSetIfChanged(ref _showLayerDifference, value);
+ }
- public bool UseIssueColorOnTracker
- {
- get => _useIssueColorOnTracker;
- set => RaiseAndSetIfChanged(ref _useIssueColorOnTracker, value);
- }
+ public bool LayerDifferenceHighlightSimilarityInstead
+ {
+ get => _layerDifferenceHighlightSimilarityInstead;
+ set => RaiseAndSetIfChanged(ref _layerDifferenceHighlightSimilarityInstead, value);
+ }
- public Color IslandColor
- {
- get => _islandColor;
- set
- {
- RaiseAndSetIfChanged(ref _islandColor, value);
- RaisePropertyChanged(nameof(IslandBrush));
- }
- }
+ public bool UseIssueColorOnTracker
+ {
+ get => _useIssueColorOnTracker;
+ set => RaiseAndSetIfChanged(ref _useIssueColorOnTracker, value);
+ }
- [XmlIgnore]
- public SolidColorBrush IslandBrush
+ public Color IslandColor
+ {
+ get => _islandColor;
+ set
{
- get => new(_islandColor.ToAvalonia());
- set => IslandColor = new Color(value);
+ RaiseAndSetIfChanged(ref _islandColor, value);
+ RaisePropertyChanged(nameof(IslandBrush));
}
+ }
- public Color IslandHighlightColor
- {
- get => _islandHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _islandHighlightColor, value);
- RaisePropertyChanged(nameof(IslandHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush IslandBrush
+ {
+ get => new(_islandColor.ToAvalonia());
+ set => IslandColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush IslandHighlightBrush
+ public Color IslandHighlightColor
+ {
+ get => _islandHighlightColor;
+ set
{
- get => new(_islandHighlightColor.ToAvalonia());
- set => IslandHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _islandHighlightColor, value);
+ RaisePropertyChanged(nameof(IslandHighlightBrush));
}
+ }
- public Color OverhangColor
- {
- get => _overhangColor;
- set
- {
- RaiseAndSetIfChanged(ref _overhangColor, value);
- RaisePropertyChanged(nameof(OverhangBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush IslandHighlightBrush
+ {
+ get => new(_islandHighlightColor.ToAvalonia());
+ set => IslandHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush OverhangBrush
+ public Color OverhangColor
+ {
+ get => _overhangColor;
+ set
{
- get => new(_overhangColor.ToAvalonia());
- set => OverhangColor = new Color(value);
+ RaiseAndSetIfChanged(ref _overhangColor, value);
+ RaisePropertyChanged(nameof(OverhangBrush));
}
+ }
- public Color OverhangHighlightColor
- {
- get => _overhangHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _overhangHighlightColor, value);
- RaisePropertyChanged(nameof(OverhangHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush OverhangBrush
+ {
+ get => new(_overhangColor.ToAvalonia());
+ set => OverhangColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush OverhangHighlightBrush
+ public Color OverhangHighlightColor
+ {
+ get => _overhangHighlightColor;
+ set
{
- get => new(_overhangHighlightColor.ToAvalonia());
- set => OverhangHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _overhangHighlightColor, value);
+ RaisePropertyChanged(nameof(OverhangHighlightBrush));
}
+ }
- public Color ResinTrapColor
- {
- get => _resinTrapColor;
- set
- {
- RaiseAndSetIfChanged(ref _resinTrapColor, value);
- RaisePropertyChanged(nameof(ResinTrapBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush OverhangHighlightBrush
+ {
+ get => new(_overhangHighlightColor.ToAvalonia());
+ set => OverhangHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush ResinTrapBrush
+ public Color ResinTrapColor
+ {
+ get => _resinTrapColor;
+ set
{
- get => new(_resinTrapColor.ToAvalonia());
- set => ResinTrapColor = new Color(value);
+ RaiseAndSetIfChanged(ref _resinTrapColor, value);
+ RaisePropertyChanged(nameof(ResinTrapBrush));
}
+ }
- public Color ResinTrapHighlightColor
- {
- get => _resinTrapHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _resinTrapHighlightColor, value);
- RaisePropertyChanged(nameof(ResinTrapHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush ResinTrapBrush
+ {
+ get => new(_resinTrapColor.ToAvalonia());
+ set => ResinTrapColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush ResinTrapHighlightBrush
+ public Color ResinTrapHighlightColor
+ {
+ get => _resinTrapHighlightColor;
+ set
{
- get => new(_resinTrapHighlightColor.ToAvalonia());
- set => ResinTrapHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _resinTrapHighlightColor, value);
+ RaisePropertyChanged(nameof(ResinTrapHighlightBrush));
}
+ }
- public Color SuctionCupColor
- {
- get => _suctionCupColor;
- set
- {
- RaiseAndSetIfChanged(ref _suctionCupColor, value);
- RaisePropertyChanged(nameof(SuctionCupBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush ResinTrapHighlightBrush
+ {
+ get => new(_resinTrapHighlightColor.ToAvalonia());
+ set => ResinTrapHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush SuctionCupBrush
+ public Color SuctionCupColor
+ {
+ get => _suctionCupColor;
+ set
{
- get => new(_suctionCupColor.ToAvalonia());
- set => SuctionCupColor = new Color(value);
+ RaiseAndSetIfChanged(ref _suctionCupColor, value);
+ RaisePropertyChanged(nameof(SuctionCupBrush));
}
+ }
- public Color SuctionCupHighlightColor
- {
- get => _suctionCupHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _suctionCupHighlightColor, value);
- RaisePropertyChanged(nameof(SuctionCupHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush SuctionCupBrush
+ {
+ get => new(_suctionCupColor.ToAvalonia());
+ set => SuctionCupColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush SuctionCupHighlightBrush
+ public Color SuctionCupHighlightColor
+ {
+ get => _suctionCupHighlightColor;
+ set
{
- get => new(_suctionCupHighlightColor.ToAvalonia());
- set => SuctionCupHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _suctionCupHighlightColor, value);
+ RaisePropertyChanged(nameof(SuctionCupHighlightBrush));
}
+ }
- public Color TouchingBoundsColor
- {
- get => _touchingBoundsColor;
- set
- {
- RaiseAndSetIfChanged(ref _touchingBoundsColor, value);
- RaisePropertyChanged(nameof(TouchingBoundsBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush SuctionCupHighlightBrush
+ {
+ get => new(_suctionCupHighlightColor.ToAvalonia());
+ set => SuctionCupHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush TouchingBoundsBrush
+ public Color TouchingBoundsColor
+ {
+ get => _touchingBoundsColor;
+ set
{
- get => new(_touchingBoundsColor.ToAvalonia());
- set => TouchingBoundsColor = new Color(value);
+ RaiseAndSetIfChanged(ref _touchingBoundsColor, value);
+ RaisePropertyChanged(nameof(TouchingBoundsBrush));
}
+ }
- public Color CrosshairColor
- {
- get => _crosshairColor;
- set
- {
- RaiseAndSetIfChanged(ref _crosshairColor, value);
- RaisePropertyChanged(nameof(CrosshairBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush TouchingBoundsBrush
+ {
+ get => new(_touchingBoundsColor.ToAvalonia());
+ set => TouchingBoundsColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush CrosshairBrush
+ public Color CrosshairColor
+ {
+ get => _crosshairColor;
+ set
{
- get => new(_crosshairColor.ToAvalonia());
- set => CrosshairColor = new Color(value);
+ RaiseAndSetIfChanged(ref _crosshairColor, value);
+ RaisePropertyChanged(nameof(CrosshairBrush));
}
+ }
- public bool ZoomToFitPrintVolumeBounds
- {
- get => _zoomToFitPrintVolumeBounds;
- set => RaiseAndSetIfChanged(ref _zoomToFitPrintVolumeBounds, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush CrosshairBrush
+ {
+ get => new(_crosshairColor.ToAvalonia());
+ set => CrosshairColor = new Color(value);
+ }
- public byte ZoomLockLevelIndex
- {
- get => _zoomLockLevelIndex;
- set => RaiseAndSetIfChanged(ref _zoomLockLevelIndex, value);
- }
+ public bool ZoomToFitPrintVolumeBounds
+ {
+ get => _zoomToFitPrintVolumeBounds;
+ set => RaiseAndSetIfChanged(ref _zoomToFitPrintVolumeBounds, value);
+ }
- public bool ZoomIssues
- {
- get => _zoomIssues;
- set => RaiseAndSetIfChanged(ref _zoomIssues, value);
- }
+ public byte ZoomLockLevelIndex
+ {
+ get => _zoomLockLevelIndex;
+ set => RaiseAndSetIfChanged(ref _zoomLockLevelIndex, value);
+ }
- public bool CrosshairShowOnlyOnSelectedIssues
- {
- get => _crosshairShowOnlyOnSelectedIssues;
- set => RaiseAndSetIfChanged(ref _crosshairShowOnlyOnSelectedIssues, value);
- }
+ public bool ZoomIssues
+ {
+ get => _zoomIssues;
+ set => RaiseAndSetIfChanged(ref _zoomIssues, value);
+ }
- public byte CrosshairFadeLevelIndex
- {
- get => _crosshairFadeLevelIndex;
- set => RaiseAndSetIfChanged(ref _crosshairFadeLevelIndex, value);
- }
+ public bool CrosshairShowOnlyOnSelectedIssues
+ {
+ get => _crosshairShowOnlyOnSelectedIssues;
+ set => RaiseAndSetIfChanged(ref _crosshairShowOnlyOnSelectedIssues, value);
+ }
- public uint CrosshairLength
- {
- get => _crosshairLength;
- set => RaiseAndSetIfChanged(ref _crosshairLength, value);
- }
+ public byte CrosshairFadeLevelIndex
+ {
+ get => _crosshairFadeLevelIndex;
+ set => RaiseAndSetIfChanged(ref _crosshairFadeLevelIndex, value);
+ }
- public byte CrosshairMargin
- {
- get => _crosshairMargin;
- set => RaiseAndSetIfChanged(ref _crosshairMargin, value);
- }
+ public uint CrosshairLength
+ {
+ get => _crosshairLength;
+ set => RaiseAndSetIfChanged(ref _crosshairLength, value);
+ }
- public bool AutoRotateLayerBestView
- {
- get => _autoRotateLayerBestView;
- set => RaiseAndSetIfChanged(ref _autoRotateLayerBestView, value);
- }
+ public byte CrosshairMargin
+ {
+ get => _crosshairMargin;
+ set => RaiseAndSetIfChanged(ref _crosshairMargin, value);
+ }
- public bool AutoFlipLayerIfMirrored
- {
- get => _autoFlipLayerIfMirrored;
- set => RaiseAndSetIfChanged(ref _autoFlipLayerIfMirrored, value);
- }
+ public bool AutoRotateLayerBestView
+ {
+ get => _autoRotateLayerBestView;
+ set => RaiseAndSetIfChanged(ref _autoRotateLayerBestView, value);
+ }
- public bool LayerZoomToFitOnLoad
- {
- get => _layerZoomToFitOnLoad;
- set => RaiseAndSetIfChanged(ref _layerZoomToFitOnLoad, value);
- }
+ public bool AutoFlipLayerIfMirrored
+ {
+ get => _autoFlipLayerIfMirrored;
+ set => RaiseAndSetIfChanged(ref _autoFlipLayerIfMirrored, value);
+ }
- public bool ShowBackgroudGrid
- {
- get => _showBackgroudGrid;
- set => RaiseAndSetIfChanged(ref _showBackgroudGrid, value);
- }
+ public bool LayerZoomToFitOnLoad
+ {
+ get => _layerZoomToFitOnLoad;
+ set => RaiseAndSetIfChanged(ref _layerZoomToFitOnLoad, value);
+ }
- public ushort LayerSliderDebounce
- {
- get => _layerSliderDebounce;
- set => RaiseAndSetIfChanged(ref _layerSliderDebounce, value);
- }
+ public bool ShowBackgroudGrid
+ {
+ get => _showBackgroudGrid;
+ set => RaiseAndSetIfChanged(ref _showBackgroudGrid, value);
+ }
- public LayerPreviewUserSettings Clone()
- {
- return MemberwiseClone() as LayerPreviewUserSettings;
- }
+ public ushort LayerSliderDebounce
+ {
+ get => _layerSliderDebounce;
+ set => RaiseAndSetIfChanged(ref _layerSliderDebounce, value);
+ }
+ public LayerPreviewUserSettings Clone()
+ {
+ return MemberwiseClone() as LayerPreviewUserSettings;
}
- #endregion
-
- #region Issues
- [Serializable]
- public sealed class IssuesUserSettings : BindableBase
- {
- private bool _computeIssuesOnLoad;
- private bool _autoRepairIssuesOnLoad;
- private bool _computeIssuesOnClickTab = true;
- private bool _computeIslands = true;
- private bool _computeOverhangs = true;
- private bool _computeResinTraps = true;
- private bool _computeSuctionCups = true;
- private bool _computeTouchingBounds = true;
- private bool _computePrintHeight = true;
- private bool _computeEmptyLayers = true;
- private bool _dataGridGroupByType = true;
- private bool _dataGridGroupByLayerIndex;
- private bool _islandEnhancedDetection = true;
- private bool _islandAllowDiagonalBonds;
- private byte _islandBinaryThreshold;
- private byte _islandRequiredAreaToProcessCheck = 1;
- private byte _islandRequiredPixelBrightnessToProcessCheck = 1;
- private byte _islandRequiredPixelsToSupport = 10;
- private decimal _islandRequiredPixelsToSupportMultiplier = 0.25m;
- private byte _islandRequiredPixelBrightnessToSupport = 150;
- private bool _overhangIndependentFromIslands = true;
- private byte _overhangErodeIterations = 49;
- private byte _resinTrapBinaryThreshold = 127;
- private byte _resinTrapRequiredAreaToProcessCheck = 17;
- private byte _resinTrapRequiredBlackPixelsToDrain = 10;
- private byte _resinTrapMaximumPixelBrightnessToDrain = 30;
- private uint _suctionCupRequiredAreaToConsider = 10000;
- private decimal _suctionCupRequiredHeightToConsider = 0.5m;
- private byte _touchingBoundMinimumPixelBrightness = 127;
- private byte _touchingBoundMarginLeft = 5;
- private byte _touchingBoundMarginTop = 5;
- private byte _touchingBoundMarginRight = 5;
- private byte _touchingBoundMarginBottom = 5;
- private bool _touchingBoundSyncMargins = true;
- private decimal _printHeightOffset;
-
- public bool ComputeIssuesOnLoad
- {
- get => _computeIssuesOnLoad;
- set => RaiseAndSetIfChanged(ref _computeIssuesOnLoad, value);
- }
- public bool AutoRepairIssuesOnLoad
- {
- get => _autoRepairIssuesOnLoad;
- set => RaiseAndSetIfChanged(ref _autoRepairIssuesOnLoad, value);
- }
+ }
+ #endregion
- public bool ComputeIssuesOnClickTab
- {
- get => _computeIssuesOnClickTab;
- set => RaiseAndSetIfChanged(ref _computeIssuesOnClickTab, value);
- }
+ #region Issues
+ [Serializable]
+ public sealed class IssuesUserSettings : BindableBase
+ {
+ private bool _computeIssuesOnLoad;
+ private bool _autoRepairIssuesOnLoad;
+ private bool _computeIssuesOnClickTab = true;
+ private bool _computeIslands = true;
+ private bool _computeOverhangs = true;
+ private bool _computeResinTraps = true;
+ private bool _computeSuctionCups = true;
+ private bool _computeTouchingBounds = true;
+ private bool _computePrintHeight = true;
+ private bool _computeEmptyLayers = true;
+ private bool _dataGridGroupByType = true;
+ private bool _dataGridGroupByLayerIndex;
+ private bool _islandEnhancedDetection = true;
+ private bool _islandAllowDiagonalBonds;
+ private byte _islandBinaryThreshold;
+ private byte _islandRequiredAreaToProcessCheck = 1;
+ private byte _islandRequiredPixelBrightnessToProcessCheck = 1;
+ private byte _islandRequiredPixelsToSupport = 10;
+ private decimal _islandRequiredPixelsToSupportMultiplier = 0.25m;
+ private byte _islandRequiredPixelBrightnessToSupport = 150;
+ private bool _overhangIndependentFromIslands = true;
+ private byte _overhangErodeIterations = 49;
+ private byte _resinTrapBinaryThreshold = 127;
+ private byte _resinTrapRequiredAreaToProcessCheck = 17;
+ private byte _resinTrapRequiredBlackPixelsToDrain = 10;
+ private byte _resinTrapMaximumPixelBrightnessToDrain = 30;
+ private uint _suctionCupRequiredAreaToConsider = 10000;
+ private decimal _suctionCupRequiredHeightToConsider = 0.5m;
+ private byte _touchingBoundMinimumPixelBrightness = 127;
+ private byte _touchingBoundMarginLeft = 5;
+ private byte _touchingBoundMarginTop = 5;
+ private byte _touchingBoundMarginRight = 5;
+ private byte _touchingBoundMarginBottom = 5;
+ private bool _touchingBoundSyncMargins = true;
+ private decimal _printHeightOffset;
+
+ public bool ComputeIssuesOnLoad
+ {
+ get => _computeIssuesOnLoad;
+ set => RaiseAndSetIfChanged(ref _computeIssuesOnLoad, value);
+ }
- public bool ComputeIslands
- {
- get => _computeIslands;
- set => RaiseAndSetIfChanged(ref _computeIslands, value);
- }
+ public bool AutoRepairIssuesOnLoad
+ {
+ get => _autoRepairIssuesOnLoad;
+ set => RaiseAndSetIfChanged(ref _autoRepairIssuesOnLoad, value);
+ }
- public bool ComputeOverhangs
- {
- get => _computeOverhangs;
- set => RaiseAndSetIfChanged(ref _computeOverhangs, value);
- }
+ public bool ComputeIssuesOnClickTab
+ {
+ get => _computeIssuesOnClickTab;
+ set => RaiseAndSetIfChanged(ref _computeIssuesOnClickTab, value);
+ }
- public bool ComputeResinTraps
- {
- get => _computeResinTraps;
- set => RaiseAndSetIfChanged(ref _computeResinTraps, value);
- }
+ public bool ComputeIslands
+ {
+ get => _computeIslands;
+ set => RaiseAndSetIfChanged(ref _computeIslands, value);
+ }
- public bool ComputeSuctionCups
- {
- get => _computeSuctionCups;
- set => RaiseAndSetIfChanged(ref _computeSuctionCups, value);
- }
+ public bool ComputeOverhangs
+ {
+ get => _computeOverhangs;
+ set => RaiseAndSetIfChanged(ref _computeOverhangs, value);
+ }
- public bool ComputeTouchingBounds
- {
- get => _computeTouchingBounds;
- set => RaiseAndSetIfChanged(ref _computeTouchingBounds, value);
- }
+ public bool ComputeResinTraps
+ {
+ get => _computeResinTraps;
+ set => RaiseAndSetIfChanged(ref _computeResinTraps, value);
+ }
- public bool ComputePrintHeight
- {
- get => _computePrintHeight;
- set => RaiseAndSetIfChanged(ref _computePrintHeight, value);
- }
+ public bool ComputeSuctionCups
+ {
+ get => _computeSuctionCups;
+ set => RaiseAndSetIfChanged(ref _computeSuctionCups, value);
+ }
- public bool ComputeEmptyLayers
- {
- get => _computeEmptyLayers;
- set => RaiseAndSetIfChanged(ref _computeEmptyLayers, value);
- }
+ public bool ComputeTouchingBounds
+ {
+ get => _computeTouchingBounds;
+ set => RaiseAndSetIfChanged(ref _computeTouchingBounds, value);
+ }
- public bool DataGridGroupByType
- {
- get => _dataGridGroupByType;
- set => RaiseAndSetIfChanged(ref _dataGridGroupByType, value);
- }
+ public bool ComputePrintHeight
+ {
+ get => _computePrintHeight;
+ set => RaiseAndSetIfChanged(ref _computePrintHeight, value);
+ }
- public bool DataGridGroupByLayerIndex
- {
- get => _dataGridGroupByLayerIndex;
- set => RaiseAndSetIfChanged(ref _dataGridGroupByLayerIndex, value);
- }
+ public bool ComputeEmptyLayers
+ {
+ get => _computeEmptyLayers;
+ set => RaiseAndSetIfChanged(ref _computeEmptyLayers, value);
+ }
- public bool IslandEnhancedDetection
- {
- get => _islandEnhancedDetection;
- set => RaiseAndSetIfChanged(ref _islandEnhancedDetection, value);
- }
+ public bool DataGridGroupByType
+ {
+ get => _dataGridGroupByType;
+ set => RaiseAndSetIfChanged(ref _dataGridGroupByType, value);
+ }
- public bool IslandAllowDiagonalBonds
- {
- get => _islandAllowDiagonalBonds;
- set => RaiseAndSetIfChanged(ref _islandAllowDiagonalBonds, value);
- }
+ public bool DataGridGroupByLayerIndex
+ {
+ get => _dataGridGroupByLayerIndex;
+ set => RaiseAndSetIfChanged(ref _dataGridGroupByLayerIndex, value);
+ }
- public byte IslandBinaryThreshold
- {
- get => _islandBinaryThreshold;
- set => RaiseAndSetIfChanged(ref _islandBinaryThreshold, value);
- }
+ public bool IslandEnhancedDetection
+ {
+ get => _islandEnhancedDetection;
+ set => RaiseAndSetIfChanged(ref _islandEnhancedDetection, value);
+ }
- public byte IslandRequiredAreaToProcessCheck
- {
- get => _islandRequiredAreaToProcessCheck;
- set => RaiseAndSetIfChanged(ref _islandRequiredAreaToProcessCheck, value);
- }
+ public bool IslandAllowDiagonalBonds
+ {
+ get => _islandAllowDiagonalBonds;
+ set => RaiseAndSetIfChanged(ref _islandAllowDiagonalBonds, value);
+ }
- public decimal IslandRequiredPixelsToSupportMultiplier
- {
- get => _islandRequiredPixelsToSupportMultiplier;
- set => RaiseAndSetIfChanged(ref _islandRequiredPixelsToSupportMultiplier, value);
- }
+ public byte IslandBinaryThreshold
+ {
+ get => _islandBinaryThreshold;
+ set => RaiseAndSetIfChanged(ref _islandBinaryThreshold, value);
+ }
- public byte IslandRequiredPixelsToSupport
- {
- get => _islandRequiredPixelsToSupport;
- set => RaiseAndSetIfChanged(ref _islandRequiredPixelsToSupport, value);
- }
+ public byte IslandRequiredAreaToProcessCheck
+ {
+ get => _islandRequiredAreaToProcessCheck;
+ set => RaiseAndSetIfChanged(ref _islandRequiredAreaToProcessCheck, value);
+ }
+
+ public decimal IslandRequiredPixelsToSupportMultiplier
+ {
+ get => _islandRequiredPixelsToSupportMultiplier;
+ set => RaiseAndSetIfChanged(ref _islandRequiredPixelsToSupportMultiplier, value);
+ }
+
+ public byte IslandRequiredPixelsToSupport
+ {
+ get => _islandRequiredPixelsToSupport;
+ set => RaiseAndSetIfChanged(ref _islandRequiredPixelsToSupport, value);
+ }
- public byte IslandRequiredPixelBrightnessToProcessCheck
- {
- get => _islandRequiredPixelBrightnessToProcessCheck;
- set => RaiseAndSetIfChanged(ref _islandRequiredPixelBrightnessToProcessCheck, value);
- }
+ public byte IslandRequiredPixelBrightnessToProcessCheck
+ {
+ get => _islandRequiredPixelBrightnessToProcessCheck;
+ set => RaiseAndSetIfChanged(ref _islandRequiredPixelBrightnessToProcessCheck, value);
+ }
- public byte IslandRequiredPixelBrightnessToSupport
- {
- get => _islandRequiredPixelBrightnessToSupport;
- set => RaiseAndSetIfChanged(ref _islandRequiredPixelBrightnessToSupport, value);
- }
+ public byte IslandRequiredPixelBrightnessToSupport
+ {
+ get => _islandRequiredPixelBrightnessToSupport;
+ set => RaiseAndSetIfChanged(ref _islandRequiredPixelBrightnessToSupport, value);
+ }
- public bool OverhangIndependentFromIslands
- {
- get => _overhangIndependentFromIslands;
- set => RaiseAndSetIfChanged(ref _overhangIndependentFromIslands, value);
- }
+ public bool OverhangIndependentFromIslands
+ {
+ get => _overhangIndependentFromIslands;
+ set => RaiseAndSetIfChanged(ref _overhangIndependentFromIslands, value);
+ }
- public byte OverhangErodeIterations
- {
- get => _overhangErodeIterations;
- set => RaiseAndSetIfChanged(ref _overhangErodeIterations, value);
- }
+ public byte OverhangErodeIterations
+ {
+ get => _overhangErodeIterations;
+ set => RaiseAndSetIfChanged(ref _overhangErodeIterations, value);
+ }
- public byte ResinTrapBinaryThreshold
- {
- get => _resinTrapBinaryThreshold;
- set => RaiseAndSetIfChanged(ref _resinTrapBinaryThreshold, value);
- }
+ public byte ResinTrapBinaryThreshold
+ {
+ get => _resinTrapBinaryThreshold;
+ set => RaiseAndSetIfChanged(ref _resinTrapBinaryThreshold, value);
+ }
- public byte ResinTrapRequiredAreaToProcessCheck
- {
- get => _resinTrapRequiredAreaToProcessCheck;
- set => RaiseAndSetIfChanged(ref _resinTrapRequiredAreaToProcessCheck, value);
- }
+ public byte ResinTrapRequiredAreaToProcessCheck
+ {
+ get => _resinTrapRequiredAreaToProcessCheck;
+ set => RaiseAndSetIfChanged(ref _resinTrapRequiredAreaToProcessCheck, value);
+ }
- public byte ResinTrapRequiredBlackPixelsToDrain
- {
- get => _resinTrapRequiredBlackPixelsToDrain;
- set => RaiseAndSetIfChanged(ref _resinTrapRequiredBlackPixelsToDrain, value);
- }
+ public byte ResinTrapRequiredBlackPixelsToDrain
+ {
+ get => _resinTrapRequiredBlackPixelsToDrain;
+ set => RaiseAndSetIfChanged(ref _resinTrapRequiredBlackPixelsToDrain, value);
+ }
- public byte ResinTrapMaximumPixelBrightnessToDrain
- {
- get => _resinTrapMaximumPixelBrightnessToDrain;
- set => RaiseAndSetIfChanged(ref _resinTrapMaximumPixelBrightnessToDrain, value);
- }
+ public byte ResinTrapMaximumPixelBrightnessToDrain
+ {
+ get => _resinTrapMaximumPixelBrightnessToDrain;
+ set => RaiseAndSetIfChanged(ref _resinTrapMaximumPixelBrightnessToDrain, value);
+ }
- public uint SuctionCupRequiredAreaToConsider
- {
- get => _suctionCupRequiredAreaToConsider;
- set => RaiseAndSetIfChanged(ref _suctionCupRequiredAreaToConsider, value);
- }
+ public uint SuctionCupRequiredAreaToConsider
+ {
+ get => _suctionCupRequiredAreaToConsider;
+ set => RaiseAndSetIfChanged(ref _suctionCupRequiredAreaToConsider, value);
+ }
- public decimal SuctionCupRequiredHeightToConsider
- {
- get => _suctionCupRequiredHeightToConsider;
- set => RaiseAndSetIfChanged(ref _suctionCupRequiredHeightToConsider, value);
- }
+ public decimal SuctionCupRequiredHeightToConsider
+ {
+ get => _suctionCupRequiredHeightToConsider;
+ set => RaiseAndSetIfChanged(ref _suctionCupRequiredHeightToConsider, value);
+ }
- public byte TouchingBoundMinimumPixelBrightness
- {
- get => _touchingBoundMinimumPixelBrightness;
- set => RaiseAndSetIfChanged(ref _touchingBoundMinimumPixelBrightness, value);
- }
+ public byte TouchingBoundMinimumPixelBrightness
+ {
+ get => _touchingBoundMinimumPixelBrightness;
+ set => RaiseAndSetIfChanged(ref _touchingBoundMinimumPixelBrightness, value);
+ }
- public byte TouchingBoundMarginLeft
+ public byte TouchingBoundMarginLeft
+ {
+ get => _touchingBoundMarginLeft;
+ set
{
- get => _touchingBoundMarginLeft;
- set
+ if(!RaiseAndSetIfChanged(ref _touchingBoundMarginLeft, value)) return;
+ if (_touchingBoundSyncMargins)
{
- if(!RaiseAndSetIfChanged(ref _touchingBoundMarginLeft, value)) return;
- if (_touchingBoundSyncMargins)
- {
- TouchingBoundMarginRight = value;
- }
+ TouchingBoundMarginRight = value;
}
}
+ }
- public byte TouchingBoundMarginTop
+ public byte TouchingBoundMarginTop
+ {
+ get => _touchingBoundMarginTop;
+ set
{
- get => _touchingBoundMarginTop;
- set
+ if (!RaiseAndSetIfChanged(ref _touchingBoundMarginTop, value)) return;
+ if (_touchingBoundSyncMargins)
{
- if (!RaiseAndSetIfChanged(ref _touchingBoundMarginTop, value)) return;
- if (_touchingBoundSyncMargins)
- {
- TouchingBoundMarginBottom = value;
- }
+ TouchingBoundMarginBottom = value;
}
}
+ }
- public byte TouchingBoundMarginRight
+ public byte TouchingBoundMarginRight
+ {
+ get => _touchingBoundMarginRight;
+ set
{
- get => _touchingBoundMarginRight;
- set
+ if(!RaiseAndSetIfChanged(ref _touchingBoundMarginRight, value)) return;
+ if (_touchingBoundSyncMargins)
{
- if(!RaiseAndSetIfChanged(ref _touchingBoundMarginRight, value)) return;
- if (_touchingBoundSyncMargins)
- {
- TouchingBoundMarginLeft = value;
- }
+ TouchingBoundMarginLeft = value;
}
}
+ }
- public byte TouchingBoundMarginBottom
+ public byte TouchingBoundMarginBottom
+ {
+ get => _touchingBoundMarginBottom;
+ set
{
- get => _touchingBoundMarginBottom;
- set
+ if(!RaiseAndSetIfChanged(ref _touchingBoundMarginBottom, value)) return;
+ if (_touchingBoundSyncMargins)
{
- if(!RaiseAndSetIfChanged(ref _touchingBoundMarginBottom, value)) return;
- if (_touchingBoundSyncMargins)
- {
- TouchingBoundMarginTop = value;
- }
+ TouchingBoundMarginTop = value;
}
}
+ }
- public bool TouchingBoundSyncMargins
- {
- get => _touchingBoundSyncMargins;
- set => RaiseAndSetIfChanged(ref _touchingBoundSyncMargins, value);
- }
-
- public decimal PrintHeightOffset
- {
- get => _printHeightOffset;
- set => RaiseAndSetIfChanged(ref _printHeightOffset, value);
- }
-
- public IssuesUserSettings Clone()
- {
- return MemberwiseClone() as IssuesUserSettings;
- }
+ public bool TouchingBoundSyncMargins
+ {
+ get => _touchingBoundSyncMargins;
+ set => RaiseAndSetIfChanged(ref _touchingBoundSyncMargins, value);
+ }
+ public decimal PrintHeightOffset
+ {
+ get => _printHeightOffset;
+ set => RaiseAndSetIfChanged(ref _printHeightOffset, value);
}
- #endregion
- #region Pixel Editor
- [Serializable]
- public sealed class PixelEditorUserSettings : BindableBase
+ public IssuesUserSettings Clone()
{
- private Color _addPixelColor = new(255, 144, 238, 144);
- private Color _addPixelHighlightColor = new(255, 0, 255, 0);
- private Color _removePixelColor = new(255, 219, 112, 147);
- private Color _removePixelHighlightColor = new(255, 139, 0, 0);
- private Color _supportsColor = new(255, 0, 255, 255);
- private Color _supportsHighlightColor = new(255, 0, 139, 139);
- private Color _drainHoleColor = new(255, 142, 69, 133);
- private Color _drainHoleHighlightColor = new(255, 159, 0, 197);
- private Color _cursorColor = new(150, 52, 152, 219);
- private bool _partialUpdateIslandsOnEditing = true;
- private bool _closeEditorOnApply;
+ return MemberwiseClone() as IssuesUserSettings;
+ }
- public Color AddPixelColor
- {
- get => _addPixelColor;
- set
- {
- RaiseAndSetIfChanged(ref _addPixelColor, value);
- RaisePropertyChanged(nameof(AddPixelBrush));
- }
- }
+ }
+ #endregion
- [XmlIgnore]
- public SolidColorBrush AddPixelBrush
+ #region Pixel Editor
+ [Serializable]
+ public sealed class PixelEditorUserSettings : BindableBase
+ {
+ private Color _addPixelColor = new(255, 144, 238, 144);
+ private Color _addPixelHighlightColor = new(255, 0, 255, 0);
+ private Color _removePixelColor = new(255, 219, 112, 147);
+ private Color _removePixelHighlightColor = new(255, 139, 0, 0);
+ private Color _supportsColor = new(255, 0, 255, 255);
+ private Color _supportsHighlightColor = new(255, 0, 139, 139);
+ private Color _drainHoleColor = new(255, 142, 69, 133);
+ private Color _drainHoleHighlightColor = new(255, 159, 0, 197);
+ private Color _cursorColor = new(150, 52, 152, 219);
+ private bool _partialUpdateIslandsOnEditing = true;
+ private bool _closeEditorOnApply;
+
+ public Color AddPixelColor
+ {
+ get => _addPixelColor;
+ set
{
- get => new(_addPixelColor.ToAvalonia());
- set => AddPixelColor = new Color(value);
+ RaiseAndSetIfChanged(ref _addPixelColor, value);
+ RaisePropertyChanged(nameof(AddPixelBrush));
}
+ }
- public Color AddPixelHighlightColor
- {
- get => _addPixelHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _addPixelHighlightColor, value);
- RaisePropertyChanged(nameof(AddPixelHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush AddPixelBrush
+ {
+ get => new(_addPixelColor.ToAvalonia());
+ set => AddPixelColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush AddPixelHighlightBrush
+ public Color AddPixelHighlightColor
+ {
+ get => _addPixelHighlightColor;
+ set
{
- get => new(_addPixelHighlightColor.ToAvalonia());
- set => AddPixelHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _addPixelHighlightColor, value);
+ RaisePropertyChanged(nameof(AddPixelHighlightBrush));
}
+ }
- public Color RemovePixelColor
- {
- get => _removePixelColor;
- set
- {
- RaiseAndSetIfChanged(ref _removePixelColor, value);
- RaisePropertyChanged(nameof(RemovePixelBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush AddPixelHighlightBrush
+ {
+ get => new(_addPixelHighlightColor.ToAvalonia());
+ set => AddPixelHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush RemovePixelBrush
+ public Color RemovePixelColor
+ {
+ get => _removePixelColor;
+ set
{
- get => new(_removePixelColor.ToAvalonia());
- set => RemovePixelColor = new Color(value);
+ RaiseAndSetIfChanged(ref _removePixelColor, value);
+ RaisePropertyChanged(nameof(RemovePixelBrush));
}
+ }
- public Color RemovePixelHighlightColor
- {
- get => _removePixelHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _removePixelHighlightColor, value);
- RaisePropertyChanged(nameof(RemovePixelHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush RemovePixelBrush
+ {
+ get => new(_removePixelColor.ToAvalonia());
+ set => RemovePixelColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush RemovePixelHighlightBrush
+ public Color RemovePixelHighlightColor
+ {
+ get => _removePixelHighlightColor;
+ set
{
- get => new(_removePixelHighlightColor.ToAvalonia());
- set => RemovePixelHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _removePixelHighlightColor, value);
+ RaisePropertyChanged(nameof(RemovePixelHighlightBrush));
}
+ }
- public Color SupportsColor
- {
- get => _supportsColor;
- set
- {
- RaiseAndSetIfChanged(ref _supportsColor, value);
- RaisePropertyChanged(nameof(SupportsBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush RemovePixelHighlightBrush
+ {
+ get => new(_removePixelHighlightColor.ToAvalonia());
+ set => RemovePixelHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush SupportsBrush
+ public Color SupportsColor
+ {
+ get => _supportsColor;
+ set
{
- get => new(_supportsColor.ToAvalonia());
- set => SupportsColor = new Color(value);
+ RaiseAndSetIfChanged(ref _supportsColor, value);
+ RaisePropertyChanged(nameof(SupportsBrush));
}
+ }
- public Color SupportsHighlightColor
- {
- get => _supportsHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _supportsHighlightColor, value);
- RaisePropertyChanged(nameof(SupportsHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush SupportsBrush
+ {
+ get => new(_supportsColor.ToAvalonia());
+ set => SupportsColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush SupportsHighlightBrush
+ public Color SupportsHighlightColor
+ {
+ get => _supportsHighlightColor;
+ set
{
- get => new(_supportsHighlightColor.ToAvalonia());
- set => SupportsHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _supportsHighlightColor, value);
+ RaisePropertyChanged(nameof(SupportsHighlightBrush));
}
+ }
- public Color DrainHoleColor
- {
- get => _drainHoleColor;
- set
- {
- RaiseAndSetIfChanged(ref _drainHoleColor, value);
- RaisePropertyChanged(nameof(DrainHoleBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush SupportsHighlightBrush
+ {
+ get => new(_supportsHighlightColor.ToAvalonia());
+ set => SupportsHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush DrainHoleBrush
+ public Color DrainHoleColor
+ {
+ get => _drainHoleColor;
+ set
{
- get => new(_drainHoleColor.ToAvalonia());
- set => DrainHoleColor = new Color(value);
+ RaiseAndSetIfChanged(ref _drainHoleColor, value);
+ RaisePropertyChanged(nameof(DrainHoleBrush));
}
+ }
- public Color DrainHoleHighlightColor
- {
- get => _drainHoleHighlightColor;
- set
- {
- RaiseAndSetIfChanged(ref _drainHoleHighlightColor, value);
- RaisePropertyChanged(nameof(DrainHoleHighlightBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush DrainHoleBrush
+ {
+ get => new(_drainHoleColor.ToAvalonia());
+ set => DrainHoleColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush DrainHoleHighlightBrush
+ public Color DrainHoleHighlightColor
+ {
+ get => _drainHoleHighlightColor;
+ set
{
- get => new(_drainHoleHighlightColor.ToAvalonia());
- set => DrainHoleHighlightColor = new Color(value);
+ RaiseAndSetIfChanged(ref _drainHoleHighlightColor, value);
+ RaisePropertyChanged(nameof(DrainHoleHighlightBrush));
}
+ }
- public Color CursorColor
- {
- get => _cursorColor;
- set
- {
- RaiseAndSetIfChanged(ref _cursorColor, value);
- RaisePropertyChanged(nameof(CursorBrush));
- }
- }
+ [XmlIgnore]
+ public SolidColorBrush DrainHoleHighlightBrush
+ {
+ get => new(_drainHoleHighlightColor.ToAvalonia());
+ set => DrainHoleHighlightColor = new Color(value);
+ }
- [XmlIgnore]
- public SolidColorBrush CursorBrush
+ public Color CursorColor
+ {
+ get => _cursorColor;
+ set
{
- get => new(_cursorColor.ToAvalonia());
- set => CursorColor = new Color(value);
+ RaiseAndSetIfChanged(ref _cursorColor, value);
+ RaisePropertyChanged(nameof(CursorBrush));
}
+ }
- public bool PartialUpdateIslandsOnEditing
- {
- get => _partialUpdateIslandsOnEditing;
- set => RaiseAndSetIfChanged(ref _partialUpdateIslandsOnEditing, value);
- }
+ [XmlIgnore]
+ public SolidColorBrush CursorBrush
+ {
+ get => new(_cursorColor.ToAvalonia());
+ set => CursorColor = new Color(value);
+ }
- public bool CloseEditorOnApply
- {
- get => _closeEditorOnApply;
- set => RaiseAndSetIfChanged(ref _closeEditorOnApply, value);
- }
+ public bool PartialUpdateIslandsOnEditing
+ {
+ get => _partialUpdateIslandsOnEditing;
+ set => RaiseAndSetIfChanged(ref _partialUpdateIslandsOnEditing, value);
+ }
- public PixelEditorUserSettings Clone()
- {
- return MemberwiseClone() as PixelEditorUserSettings;
- }
+ public bool CloseEditorOnApply
+ {
+ get => _closeEditorOnApply;
+ set => RaiseAndSetIfChanged(ref _closeEditorOnApply, value);
}
- #endregion
-
- #region Layer Repair
- [Serializable]
- public sealed class LayerRepairUserSettings : BindableBase
- {
- private bool _repairIslands = true;
- private bool _repairResinTraps = true;
- private bool _repairSuctionCups;
- private bool _removeEmptyLayers = true;
- private ushort _removeIslandsBelowEqualPixels = 5;
- private ushort _removeIslandsRecursiveIterations = 4;
- private ushort _attachIslandsBelowLayers = 2;
- private byte _resinTrapsOverlapBy = 0;
- private byte _suctionCupsVentHole = 16;
- private byte _closingIterations = 2;
- private byte _openingIterations = 0;
-
- public bool RepairIslands
- {
- get => _repairIslands;
- set => RaiseAndSetIfChanged(ref _repairIslands, value);
- }
- public bool RepairResinTraps
- {
- get => _repairResinTraps;
- set => RaiseAndSetIfChanged(ref _repairResinTraps, value);
- }
+ public PixelEditorUserSettings Clone()
+ {
+ return MemberwiseClone() as PixelEditorUserSettings;
+ }
+ }
+ #endregion
- public bool RepairSuctionCups
- {
- get => _repairSuctionCups;
- set => RaiseAndSetIfChanged(ref _repairSuctionCups, value);
- }
+ #region Layer Repair
+ [Serializable]
+ public sealed class LayerRepairUserSettings : BindableBase
+ {
+ private bool _repairIslands = true;
+ private bool _repairResinTraps = true;
+ private bool _repairSuctionCups;
+ private bool _removeEmptyLayers = true;
+ private ushort _removeIslandsBelowEqualPixels = 5;
+ private ushort _removeIslandsRecursiveIterations = 4;
+ private ushort _attachIslandsBelowLayers = 2;
+ private byte _resinTrapsOverlapBy = 0;
+ private byte _suctionCupsVentHole = 16;
+ private byte _closingIterations = 2;
+ private byte _openingIterations = 0;
+
+ public bool RepairIslands
+ {
+ get => _repairIslands;
+ set => RaiseAndSetIfChanged(ref _repairIslands, value);
+ }
- public bool RemoveEmptyLayers
- {
- get => _removeEmptyLayers;
- set => RaiseAndSetIfChanged(ref _removeEmptyLayers, value);
- }
+ public bool RepairResinTraps
+ {
+ get => _repairResinTraps;
+ set => RaiseAndSetIfChanged(ref _repairResinTraps, value);
+ }
- public ushort RemoveIslandsBelowEqualPixels
- {
- get => _removeIslandsBelowEqualPixels;
- set => RaiseAndSetIfChanged(ref _removeIslandsBelowEqualPixels, value);
- }
+ public bool RepairSuctionCups
+ {
+ get => _repairSuctionCups;
+ set => RaiseAndSetIfChanged(ref _repairSuctionCups, value);
+ }
- public ushort RemoveIslandsRecursiveIterations
- {
- get => _removeIslandsRecursiveIterations;
- set => RaiseAndSetIfChanged(ref _removeIslandsRecursiveIterations, value);
- }
+ public bool RemoveEmptyLayers
+ {
+ get => _removeEmptyLayers;
+ set => RaiseAndSetIfChanged(ref _removeEmptyLayers, value);
+ }
- public ushort AttachIslandsBelowLayers
- {
- get => _attachIslandsBelowLayers;
- set => RaiseAndSetIfChanged(ref _attachIslandsBelowLayers, value);
- }
+ public ushort RemoveIslandsBelowEqualPixels
+ {
+ get => _removeIslandsBelowEqualPixels;
+ set => RaiseAndSetIfChanged(ref _removeIslandsBelowEqualPixels, value);
+ }
- public byte ResinTrapsOverlapBy
- {
- get => _resinTrapsOverlapBy;
- set => RaiseAndSetIfChanged(ref _resinTrapsOverlapBy, value);
- }
+ public ushort RemoveIslandsRecursiveIterations
+ {
+ get => _removeIslandsRecursiveIterations;
+ set => RaiseAndSetIfChanged(ref _removeIslandsRecursiveIterations, value);
+ }
- public byte SuctionCupsVentHole
- {
- get => _suctionCupsVentHole;
- set => RaiseAndSetIfChanged(ref _suctionCupsVentHole, value);
- }
+ public ushort AttachIslandsBelowLayers
+ {
+ get => _attachIslandsBelowLayers;
+ set => RaiseAndSetIfChanged(ref _attachIslandsBelowLayers, value);
+ }
- public byte ClosingIterations
- {
- get => _closingIterations;
- set => RaiseAndSetIfChanged(ref _closingIterations, value);
- }
+ public byte ResinTrapsOverlapBy
+ {
+ get => _resinTrapsOverlapBy;
+ set => RaiseAndSetIfChanged(ref _resinTrapsOverlapBy, value);
+ }
- public byte OpeningIterations
- {
- get => _openingIterations;
- set => RaiseAndSetIfChanged(ref _openingIterations, value);
- }
+ public byte SuctionCupsVentHole
+ {
+ get => _suctionCupsVentHole;
+ set => RaiseAndSetIfChanged(ref _suctionCupsVentHole, value);
+ }
- public LayerRepairUserSettings Clone()
- {
- return MemberwiseClone() as LayerRepairUserSettings;
- }
+ public byte ClosingIterations
+ {
+ get => _closingIterations;
+ set => RaiseAndSetIfChanged(ref _closingIterations, value);
}
- #endregion
- #region Tools
+ public byte OpeningIterations
+ {
+ get => _openingIterations;
+ set => RaiseAndSetIfChanged(ref _openingIterations, value);
+ }
- [Serializable]
- public sealed class ToolsUserSettings : BindableBase
+ public LayerRepairUserSettings Clone()
{
- private bool _restoreLastUsedSettings;
- private bool _lastUsedSettingsKeepOnCloseFile = true;
- private bool _lastUsedSettingsPriorityOverDefaultProfile = true;
+ return MemberwiseClone() as LayerRepairUserSettings;
+ }
+ }
+ #endregion
- public bool RestoreLastUsedSettings
- {
- get => _restoreLastUsedSettings;
- set => RaiseAndSetIfChanged(ref _restoreLastUsedSettings, value);
- }
+ #region Tools
- public bool LastUsedSettingsKeepOnCloseFile
- {
- get => _lastUsedSettingsKeepOnCloseFile;
- set => RaiseAndSetIfChanged(ref _lastUsedSettingsKeepOnCloseFile, value);
- }
+ [Serializable]
+ public sealed class ToolsUserSettings : BindableBase
+ {
+ private bool _restoreLastUsedSettings;
+ private bool _lastUsedSettingsKeepOnCloseFile = true;
+ private bool _lastUsedSettingsPriorityOverDefaultProfile = true;
- public bool LastUsedSettingsPriorityOverDefaultProfile
- {
- get => _lastUsedSettingsPriorityOverDefaultProfile;
- set => RaiseAndSetIfChanged(ref _lastUsedSettingsPriorityOverDefaultProfile, value);
- }
+ public bool RestoreLastUsedSettings
+ {
+ get => _restoreLastUsedSettings;
+ set => RaiseAndSetIfChanged(ref _restoreLastUsedSettings, value);
}
- #endregion
-
- #region Automations
+ public bool LastUsedSettingsKeepOnCloseFile
+ {
+ get => _lastUsedSettingsKeepOnCloseFile;
+ set => RaiseAndSetIfChanged(ref _lastUsedSettingsKeepOnCloseFile, value);
+ }
- [Serializable]
- public sealed class AutomationsUserSettings : BindableBase
+ public bool LastUsedSettingsPriorityOverDefaultProfile
{
- private bool _saveFileAfterModifications = true;
- private bool _autoConvertFiles = true;
- private Enumerations.LightOffDelaySetMode _lightOffDelayDelaySetMode = Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay;
- private bool _changeOnlyLightOffDelayIfZero = true;
- private decimal _lightOffDelay = 2.5m;
- private decimal _bottomLightOffDelay = 3m;
+ get => _lastUsedSettingsPriorityOverDefaultProfile;
+ set => RaiseAndSetIfChanged(ref _lastUsedSettingsPriorityOverDefaultProfile, value);
+ }
+ }
- public bool SaveFileAfterModifications
- {
- get => _saveFileAfterModifications;
- set => RaiseAndSetIfChanged(ref _saveFileAfterModifications, value);
- }
+ #endregion
- public bool AutoConvertFiles
- {
- get => _autoConvertFiles;
- set => RaiseAndSetIfChanged(ref _autoConvertFiles, value);
- }
+ #region Automations
- public Enumerations.LightOffDelaySetMode LightOffDelaySetMode
- {
- get => _lightOffDelayDelaySetMode;
- set
- {
- if(!RaiseAndSetIfChanged(ref _lightOffDelayDelaySetMode, value)) return;
- RaisePropertyChanged(nameof(ChangeOnlyLightOffDelayIfZeroVisible));
- RaisePropertyChanged(nameof(LightOffDelayExtraTimeVisible));
- }
- }
+ [Serializable]
+ public sealed class AutomationsUserSettings : BindableBase
+ {
+ private bool _saveFileAfterModifications = true;
+ private bool _autoConvertFiles = true;
- public bool ChangeOnlyLightOffDelayIfZero
- {
- get => _changeOnlyLightOffDelayIfZero;
- set => RaiseAndSetIfChanged(ref _changeOnlyLightOffDelayIfZero, value);
- }
+ public bool SaveFileAfterModifications
+ {
+ get => _saveFileAfterModifications;
+ set => RaiseAndSetIfChanged(ref _saveFileAfterModifications, value);
+ }
- public bool ChangeOnlyLightOffDelayIfZeroVisible =>
- _lightOffDelayDelaySetMode
- is not Enumerations.LightOffDelaySetMode.NoAction
- and not Enumerations.LightOffDelaySetMode.SetToZero;
+ public bool AutoConvertFiles
+ {
+ get => _autoConvertFiles;
+ set => RaiseAndSetIfChanged(ref _autoConvertFiles, value);
+ }
+ public AutomationsUserSettings Clone()
+ {
+ return MemberwiseClone() as AutomationsUserSettings;
+ }
+ }
- public decimal LightOffDelay
- {
- get => _lightOffDelay;
- set => RaiseAndSetIfChanged(ref _lightOffDelay, value);
- }
+ #endregion
- public decimal BottomLightOffDelay
- {
- get => _bottomLightOffDelay;
- set => RaiseAndSetIfChanged(ref _bottomLightOffDelay, value);
- }
+ #region Network
- public bool LightOffDelayExtraTimeVisible => _lightOffDelayDelaySetMode
- is Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay;
+ [Serializable]
+ public sealed class NetworkUserSettings : BindableBase
+ {
+ private RangeObservableCollection<RemotePrinter> _remotePrinters = new();
- public AutomationsUserSettings Clone()
- {
- return MemberwiseClone() as AutomationsUserSettings;
- }
+ public RangeObservableCollection<RemotePrinter> RemotePrinters
+ {
+ get => _remotePrinters;
+ set => RaiseAndSetIfChanged(ref _remotePrinters, value);
}
- #endregion
-
- #region Network
-
- [Serializable]
- public sealed class NetworkUserSettings : BindableBase
+ public NetworkUserSettings Clone()
{
- private RangeObservableCollection<RemotePrinter> _remotePrinters = new();
-
- public RangeObservableCollection<RemotePrinter> RemotePrinters
- {
- get => _remotePrinters;
- set => RaiseAndSetIfChanged(ref _remotePrinters, value);
- }
-
- public NetworkUserSettings Clone()
- {
- return this.CloneByXmlSerialization();
- }
+ return this.CloneByXmlSerialization();
}
+ }
- #endregion
+ #endregion
- #endregion
+ #endregion
- #region Singleton
+ #region Singleton
- public static string SettingsFolder
- {
- get
- {
- var folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
- if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
- if (string.IsNullOrWhiteSpace(folder)) folder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
- var path = Path.Combine(folder, About.Software);
- try
- {
- if (!Directory.Exists(path))
- Directory.CreateDirectory(path);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
-
- return path;
- }
- }
+ public static string SettingsFolder => CoreSettings.DefaultSettingsFolderAndEnsureCreation;
- /// <summary>
- /// Default filepath for store <see cref="UserSettings"/>
- /// </summary>
- private static string FilePath => Path.Combine(SettingsFolder, "usersettings.xml");
+ /// <summary>
+ /// Default filepath for store <see cref="UserSettings"/>
+ /// </summary>
+ private static string FilePath => Path.Combine(SettingsFolder, "usersettings.xml");
- private static UserSettings _instance;
- /// <summary>
- /// Instance of <see cref="UserSettings"/> (singleton)
- /// </summary>
- public static UserSettings Instance
- {
- get => _instance ??= new UserSettings();
- internal set => _instance = value;
- }
- #endregion
+ private static UserSettings _instance;
+ /// <summary>
+ /// Instance of <see cref="UserSettings"/> (singleton)
+ /// </summary>
+ public static UserSettings Instance
+ {
+ get => _instance ??= new UserSettings();
+ internal set => _instance = value;
+ }
+ #endregion
- #region Properties
+ #region Properties
- private GeneralUserSettings _general;
- private LayerPreviewUserSettings _layerPreview;
- private IssuesUserSettings _issues;
- private PixelEditorUserSettings _pixelEditor;
- private LayerRepairUserSettings _layerRepair;
- private ToolsUserSettings _tools;
- private AutomationsUserSettings _automations;
- private NetworkUserSettings _network;
+ private GeneralUserSettings _general;
+ private LayerPreviewUserSettings _layerPreview;
+ private IssuesUserSettings _issues;
+ private PixelEditorUserSettings _pixelEditor;
+ private LayerRepairUserSettings _layerRepair;
+ private ToolsUserSettings _tools;
+ private AutomationsUserSettings _automations;
+ private NetworkUserSettings _network;
- private ushort _settingsVersion = SETTINGS_VERSION;
- private string _appVersion;
- private uint _savesCount;
- private DateTime _modifiedDateTime;
+ private ushort _settingsVersion = SETTINGS_VERSION;
+ private string _appVersion;
+ private uint _savesCount;
+ private DateTime _modifiedDateTime;
- public GeneralUserSettings General
- {
- get => _general ??= new GeneralUserSettings();
- set => _general = value;
- }
+ public GeneralUserSettings General
+ {
+ get => _general ??= new GeneralUserSettings();
+ set => _general = value;
+ }
- public LayerPreviewUserSettings LayerPreview
- {
- get => _layerPreview ??= new LayerPreviewUserSettings();
- set => _layerPreview = value;
- }
+ public LayerPreviewUserSettings LayerPreview
+ {
+ get => _layerPreview ??= new LayerPreviewUserSettings();
+ set => _layerPreview = value;
+ }
- public IssuesUserSettings Issues
- {
- get => _issues ??= new IssuesUserSettings();
- set => _issues = value;
- }
+ public IssuesUserSettings Issues
+ {
+ get => _issues ??= new IssuesUserSettings();
+ set => _issues = value;
+ }
- public PixelEditorUserSettings PixelEditor
- {
- get => _pixelEditor ??= new PixelEditorUserSettings();
- set => _pixelEditor = value;
- }
+ public PixelEditorUserSettings PixelEditor
+ {
+ get => _pixelEditor ??= new PixelEditorUserSettings();
+ set => _pixelEditor = value;
+ }
- public LayerRepairUserSettings LayerRepair
- {
- get => _layerRepair ??= new LayerRepairUserSettings();
- set => _layerRepair = value;
- }
+ public LayerRepairUserSettings LayerRepair
+ {
+ get => _layerRepair ??= new LayerRepairUserSettings();
+ set => _layerRepair = value;
+ }
- public ToolsUserSettings Tools
- {
- get => _tools ??= new ToolsUserSettings();
- set => _tools = value;
- }
+ public ToolsUserSettings Tools
+ {
+ get => _tools ??= new ToolsUserSettings();
+ set => _tools = value;
+ }
- public AutomationsUserSettings Automations
- {
- get => _automations ??= new AutomationsUserSettings();
- set => _automations = value;
- }
+ public AutomationsUserSettings Automations
+ {
+ get => _automations ??= new AutomationsUserSettings();
+ set => _automations = value;
+ }
- public NetworkUserSettings Network
- {
- get => _network ??= new NetworkUserSettings();
- set => _network = value;
- }
+ public NetworkUserSettings Network
+ {
+ get => _network ??= new NetworkUserSettings();
+ set => _network = value;
+ }
- /*
- /// <summary>
- /// Gets or sets the number of times this file has been reset to defaults
- /// </summary>
- public uint ResetCount { get; set; }
- */
+ /*
+ /// <summary>
+ /// Gets or sets the number of times this file has been reset to defaults
+ /// </summary>
+ public uint ResetCount { get; set; }
+ */
- public ushort SettingsVersion
- {
- get => _settingsVersion;
- set => RaiseAndSetIfChanged(ref _settingsVersion, value);
- }
+ public ushort SettingsVersion
+ {
+ get => _settingsVersion;
+ set => RaiseAndSetIfChanged(ref _settingsVersion, value);
+ }
- /// <summary>
- /// Gets or sets the last running version of UVtools with these settings
- /// </summary>
- [NotNull]
- public string AppVersion
- {
- get => _appVersion ??= App.Version.ToString();
- set => RaiseAndSetIfChanged(ref _appVersion, value);
- }
+ /// <summary>
+ /// Gets or sets the last running version of UVtools with these settings
+ /// </summary>
+ [NotNull]
+ public string AppVersion
+ {
+ get => _appVersion ??= App.Version.ToString();
+ set => RaiseAndSetIfChanged(ref _appVersion, value);
+ }
- /// <summary>
- /// Gets or sets the number of times this file has been saved
- /// </summary>
- public uint SavesCount
- {
- get => _savesCount;
- set => RaiseAndSetIfChanged(ref _savesCount, value);
- }
+ /// <summary>
+ /// Gets or sets the number of times this file has been saved
+ /// </summary>
+ public uint SavesCount
+ {
+ get => _savesCount;
+ set => RaiseAndSetIfChanged(ref _savesCount, value);
+ }
- /// <summary>
- /// Gets or sets the last time this file has been modified
- /// </summary>
- public DateTime ModifiedDateTime
- {
- get => _modifiedDateTime;
- set => RaiseAndSetIfChanged(ref _modifiedDateTime, value);
- }
+ /// <summary>
+ /// Gets or sets the last time this file has been modified
+ /// </summary>
+ public DateTime ModifiedDateTime
+ {
+ get => _modifiedDateTime;
+ set => RaiseAndSetIfChanged(ref _modifiedDateTime, value);
+ }
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- private UserSettings()
+ private UserSettings()
+ {
+ if (OperatingSystem.IsMacOS()) // Fix macOS scaling information
{
- if (OperatingSystem.IsMacOS()) // Fix macOS scaling information
+ var monjave = new Version(10, 14, 6);
+ if (Environment.OSVersion.Version.CompareTo(monjave) >= 0)
{
- var monjave = new Version(10, 14, 6);
- if (Environment.OSVersion.Version.CompareTo(monjave) >= 0)
- {
- General.WindowsTakeIntoAccountScreenScaling = false;
- }
+ General.WindowsTakeIntoAccountScreenScaling = false;
}
}
- #endregion
+ }
+ #endregion
+
+ #region Static Methods
+ /// <summary>
+ /// Reset settings to defaults
+ /// </summary>
+ /// <param name="save">True to save settings on file, otherwise false</param>
+ public static void Reset(bool save = false)
+ {
+ _instance = new UserSettings();
+ if (save) Save();
+ }
- #region Static Methods
- /// <summary>
- /// Reset settings to defaults
- /// </summary>
- /// <param name="save">True to save settings on file, otherwise false</param>
- public static void Reset(bool save = false)
+ /// <summary>
+ /// Load settings from file
+ /// </summary>
+ public static void Load()
+ {
+ if (!File.Exists(FilePath))
{
- _instance = new UserSettings();
- if (save) Save();
+ return;
}
- /// <summary>
- /// Load settings from file
- /// </summary>
- public static void Load()
+ try
{
- if (!File.Exists(FilePath))
+ _instance = XmlExtensions.DeserializeFromFile<UserSettings>(FilePath);
+ if (_instance.General.MaxDegreeOfParallelism <= 0)
{
- return;
+ _instance.General.MaxDegreeOfParallelism = -1;
}
-
- var serializer = new XmlSerializer(typeof(UserSettings));
- try
+ else
{
- using var myXmlReader = new StreamReader(FilePath);
- _instance = (UserSettings)serializer.Deserialize(myXmlReader);
- if (_instance.General.MaxDegreeOfParallelism <= 0)
- {
- _instance.General.MaxDegreeOfParallelism = -1;
- }
- else
- {
- _instance.General.MaxDegreeOfParallelism = Math.Min(_instance.General.MaxDegreeOfParallelism, Environment.ProcessorCount);
- }
+ _instance.General.MaxDegreeOfParallelism = Math.Min(_instance.General.MaxDegreeOfParallelism, Environment.ProcessorCount);
+ }
- if (_instance.SettingsVersion < SETTINGS_VERSION)
+ if (_instance.SettingsVersion < SETTINGS_VERSION)
+ {
+ // Upgrade
+ if (_instance.SettingsVersion <= 4)
{
- // Upgrade
- if (_instance.SettingsVersion <= 4)
- {
- _instance.General.MaxDegreeOfParallelism = -1;
- }
- _instance.SettingsVersion = SETTINGS_VERSION;
+ _instance.General.MaxDegreeOfParallelism = -1;
}
+ _instance.SettingsVersion = SETTINGS_VERSION;
+ }
- CoreSettings.MaxDegreeOfParallelism = _instance.General.MaxDegreeOfParallelism;
+ CoreSettings.MaxDegreeOfParallelism = _instance.General.MaxDegreeOfParallelism;
- if (_instance.Network.RemotePrinters.Count == 0)
- {
- _instance.Network.RemotePrinters.AddRange(
+ if (_instance.Network.RemotePrinters.Count == 0)
+ {
+ _instance.Network.RemotePrinters.AddRange(
new[]
{
new RemotePrinter("0.0.0.0", 8081, "Nova3D")
@@ -1725,242 +1666,239 @@ namespace UVtools.WPF
//RequestPrinterInfo = new (RemotePrinterRequest.RequestType.PrinterInfo, RemotePrinterRequest.RequestMethod.GET, "setting/printerInfo"),
}*/
});
- }
+ }
- if (_instance.General.SendToProcess.Count == 0)
+ if (_instance.General.SendToProcess.Count == 0)
+ {
+ if (OperatingSystem.IsWindows())
{
- if (OperatingSystem.IsWindows())
+ var findDirectories = new List<string>
{
- var findDirectories = new List<string>
- {
- Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
- };
- if (Environment.Is64BitOperatingSystem)
- {
- findDirectories.Add(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86));
- }
+ Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
+ };
+ if (Environment.Is64BitOperatingSystem)
+ {
+ findDirectories.Add(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86));
+ }
- foreach (var path in findDirectories)
+ foreach (var path in findDirectories)
+ {
+ var directories = Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles));
+ foreach (var directory in directories)
{
- var directories = Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles));
- foreach (var directory in directories)
+ /*if (directory.StartsWith($"{path}\\Prusa3D", StringComparison.InvariantCultureIgnoreCase))
{
- /*if (directory.StartsWith($"{path}\\Prusa3D", StringComparison.InvariantCultureIgnoreCase))
+ var executable = $"{directory}\\PrusaSlicer\\prusa-slicer.exe";
+ if (File.Exists(executable))
{
- var executable = $"{directory}\\PrusaSlicer\\prusa-slicer.exe";
- if (File.Exists(executable))
+ var application = new MappedApplication(executable, "Slicer: PrusaSlicer");
+ foreach (var slicerFile in FileFormat.AvailableFormats)
{
- var application = new MappedApplication(executable, "Slicer: PrusaSlicer");
- foreach (var slicerFile in FileFormat.AvailableFormats)
- {
- if (slicerFile is not SL1File) continue;
- application.CompatibleExtensions += slicerFile.GetFileExtensions(string.Empty, ";");
- }
-
- _instance.General.SendToApplications.Add(application);
+ if (slicerFile is not SL1File) continue;
+ application.CompatibleExtensions += slicerFile.GetFileExtensions(string.Empty, ";");
}
-
- continue;
- }*/
- if (directory.StartsWith($"{path}\\Chitubox", StringComparison.InvariantCultureIgnoreCase))
+ _instance.General.SendToApplications.Add(application);
+ }
+
+ continue;
+ }*/
+
+ if (directory.StartsWith($"{path}\\Chitubox", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var executable = $"{directory}\\CHITUBOX.exe";
+ //var executablePro = $"{directory}\\CHITUBOXPro.exe";
+ if (File.Exists(executable))
{
- var executable = $"{directory}\\CHITUBOX.exe";
- //var executablePro = $"{directory}\\CHITUBOXPro.exe";
- if (File.Exists(executable))
+ _instance.General.SendToProcess.Add(new MappedProcess(executable, "Slicer: Chitubox")
{
- _instance.General.SendToProcess.Add(new MappedProcess(executable, "Slicer: Chitubox")
- {
- CompatibleExtensions = "cbddlp;ctb;phz;photon;photons;fdg"
- });
- }
- /*else if (File.Exists(executablePro))
- {
- _instance.General.SendToApplications.Add(new MappedApplication(executablePro, "Slicer: Chitubox Pro")
- {
- CompatibleExtensions = "cbddlp;ctb;phz;photon;photons;fdg"
- });
- }*/
- continue;
+ CompatibleExtensions = "cbddlp;ctb;phz;photon;photons;fdg"
+ });
}
+ /*else if (File.Exists(executablePro))
+ {
+ _instance.General.SendToApplications.Add(new MappedApplication(executablePro, "Slicer: Chitubox Pro")
+ {
+ CompatibleExtensions = "cbddlp;ctb;phz;photon;photons;fdg"
+ });
+ }*/
+ continue;
+ }
- if (directory.StartsWith($"{path}\\Photon_WorkShop", StringComparison.InvariantCultureIgnoreCase))
+ if (directory.StartsWith($"{path}\\Photon_WorkShop", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var directoryName = Path.GetFileName(directory);
+ var executable = $"{directory}\\{directoryName}.exe";
+ if (File.Exists(executable))
{
- var directoryName = Path.GetFileName(directory);
- var executable = $"{directory}\\{directoryName}.exe";
- if (File.Exists(executable))
+ var application = new MappedProcess(executable, $"Slicer: {directoryName.Replace('_', ' ')}")
+ {
+ CompatibleExtensions = "photon;photons;"
+ };
+ foreach (var slicerFile in FileFormat.AvailableFormats)
{
- var application = new MappedProcess(executable, $"Slicer: {directoryName.Replace('_', ' ')}")
- {
- CompatibleExtensions = "photon;photons;"
- };
- foreach (var slicerFile in FileFormat.AvailableFormats)
- {
- if (slicerFile is not PhotonWorkshopFile) continue;
- application.CompatibleExtensions += slicerFile.GetFileExtensions(string.Empty, ";");
- }
+ if (slicerFile is not PhotonWorkshopFile) continue;
+ application.CompatibleExtensions += slicerFile.GetFileExtensions(string.Empty, ";");
+ }
- _instance.General.SendToProcess.Add(application);
- }
- continue;
+ _instance.General.SendToProcess.Add(application);
}
+ continue;
+ }
- if (directory.StartsWith($"{path}\\UNIZ", StringComparison.InvariantCultureIgnoreCase))
+ if (directory.StartsWith($"{path}\\UNIZ", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var executable = $"{directory}\\UnizMaker\\UnizMaker.exe";
+ if (File.Exists(executable))
{
- var executable = $"{directory}\\UnizMaker\\UnizMaker.exe";
- if (File.Exists(executable))
+ _instance.General.SendToProcess.Add(new MappedProcess(executable, "Slicer: UnizMaker")
{
- _instance.General.SendToProcess.Add(new MappedProcess(executable, "Slicer: UnizMaker")
- {
- CompatibleExtensions = "zcode"
- });
- }
- continue;
+ CompatibleExtensions = "zcode"
+ });
}
+ continue;
+ }
- if (directory.StartsWith($"{path}\\Zortrax", StringComparison.InvariantCultureIgnoreCase))
+ if (directory.StartsWith($"{path}\\Zortrax", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var executable = $"{directory}\\Z-Suite\\Z-SUITE.exe";
+ if (File.Exists(executable))
{
- var executable = $"{directory}\\Z-Suite\\Z-SUITE.exe";
- if (File.Exists(executable))
+ _instance.General.SendToProcess.Add(new MappedProcess(executable, "Slicer: Z-SUITE")
{
- _instance.General.SendToProcess.Add(new MappedProcess(executable, "Slicer: Z-SUITE")
- {
- CompatibleExtensions = "zcodex"
- });
- }
- continue;
+ CompatibleExtensions = "zcodex"
+ });
}
+ continue;
+ }
- if (directory.StartsWith($"{path}\\WinRAR", StringComparison.InvariantCultureIgnoreCase))
+ if (directory.StartsWith($"{path}\\WinRAR", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var executable = $"{directory}\\WinRAR.exe";
+ if (File.Exists(executable))
{
- var executable = $"{directory}\\WinRAR.exe";
- if (File.Exists(executable))
+ var application = new MappedProcess(executable, "Open archive: WinRAR")
{
- var application = new MappedProcess(executable, "Open archive: WinRAR")
- {
- CompatibleExtensions = "zip;sl1;sl1s;cws;zcode;zcodex;vdt;uvt"
- };
- _instance.General.SendToProcess.Add(application);
- }
-
- continue;
+ CompatibleExtensions = "zip;sl1;sl1s;cws;zcode;zcodex;vdt;uvj"
+ };
+ _instance.General.SendToProcess.Add(application);
}
- if (directory.StartsWith($"{path}\\7-Zip", StringComparison.InvariantCultureIgnoreCase))
+ continue;
+ }
+
+ if (directory.StartsWith($"{path}\\7-Zip", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var executable = $"{directory}\\7zFM.exe";
+ if (File.Exists(executable))
{
- var executable = $"{directory}\\7zFM.exe";
- if (File.Exists(executable))
+ var application = new MappedProcess(executable, "Open archive: 7-Zip")
{
- var application = new MappedProcess(executable, "Open archive: 7-Zip")
- {
- CompatibleExtensions = "zip;sl1;sl1s;cws;zcode;zcodex;vdt;uvt"
- };
- _instance.General.SendToProcess.Add(application);
- }
-
- continue;
+ CompatibleExtensions = "zip;sl1;sl1s;cws;zcode;zcodex;vdt;uvj"
+ };
+ _instance.General.SendToProcess.Add(application);
}
+ continue;
+ }
- if (directory.StartsWith($"{path}\\010 Editor", StringComparison.InvariantCultureIgnoreCase))
- {
- var executable = $"{directory}\\010Editor.exe";
- if (File.Exists(executable))
- {
- var application = new MappedProcess(true, executable, "Hex editor: 010");
- _instance.General.SendToProcess.Add(application);
- }
- continue;
+ if (directory.StartsWith($"{path}\\010 Editor", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var executable = $"{directory}\\010Editor.exe";
+ if (File.Exists(executable))
+ {
+ var application = new MappedProcess(true, executable, "Hex editor: 010");
+ _instance.General.SendToProcess.Add(application);
}
- if (directory.StartsWith($"{path}\\HxD", StringComparison.InvariantCultureIgnoreCase))
- {
- var executable = $"{directory}\\HxD.exe";
- if (File.Exists(executable))
- {
- var application = new MappedProcess(false, executable, "Hex editor: HxD");
- _instance.General.SendToProcess.Add(application);
- }
+ continue;
+ }
- continue;
+ if (directory.StartsWith($"{path}\\HxD", StringComparison.InvariantCultureIgnoreCase))
+ {
+ var executable = $"{directory}\\HxD.exe";
+ if (File.Exists(executable))
+ {
+ var application = new MappedProcess(false, executable, "Hex editor: HxD");
+ _instance.General.SendToProcess.Add(application);
}
+
+ continue;
}
}
+ }
- if (_instance.General.SendToProcess.Count > 0)
- {
- _instance.General.SendToProcess.Sort((process, mappedProcess) => string.Compare(process.Name, mappedProcess.Name, StringComparison.Ordinal));
- }
+ if (_instance.General.SendToProcess.Count > 0)
+ {
+ _instance.General.SendToProcess.Sort((process, mappedProcess) => string.Compare(process.Name, mappedProcess.Name, StringComparison.Ordinal));
}
}
}
- catch (Exception e)
- {
- Debug.WriteLine(e.Message);
- Console.WriteLine(e.Message);
- //File.WriteAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "uvtools.txt"), e.Message);
- Reset();
- }
}
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message);
+ Console.WriteLine(e.Message);
+ //File.WriteAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "uvtools.txt"), e.Message);
+ Reset();
+ }
+ }
- /// <summary>
- /// Save settings to file
- /// </summary>
- public static void Save()
+ /// <summary>
+ /// Save settings to file
+ /// </summary>
+ public static void Save()
+ {
+ Instance.SavesCount++;
+ _instance.ModifiedDateTime = DateTime.Now;
+ CoreSettings.MaxDegreeOfParallelism = _instance.General.MaxDegreeOfParallelism;
+ try
{
- Instance.SavesCount++;
- _instance.ModifiedDateTime = DateTime.Now;
- CoreSettings.MaxDegreeOfParallelism = _instance.General.MaxDegreeOfParallelism;
- var serializer = new XmlSerializer(_instance.GetType());
- try
- {
- using var myXmlWriter = new StreamWriter(FilePath);
- serializer.Serialize(myXmlWriter, _instance);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.Message);
- }
+ XmlExtensions.SerializeToFile(_instance, FilePath, XmlExtensions.SettingsIndent);
}
-
- public static void SetVersion()
+ catch (Exception e)
{
- Instance.AppVersion = App.Version.ToString();
+ Debug.WriteLine(e.Message);
}
+ }
- public static object[] PackObjects =>
- new object[]
- {
- Instance.General,
- Instance.LayerPreview,
- Instance.Issues,
- Instance.PixelEditor,
- Instance.LayerRepair,
- Instance.Automations,
- Instance.Network
- };
- #endregion
-
- #region Methods
-
- public UserSettings Clone()
- {
- /*var clone = MemberwiseClone() as UserSettings;
- clone.General = General.Clone();
- clone.LayerPreview = LayerPreview.Clone();
- clone.Issues = Issues.Clone();
- clone.PixelEditor = PixelEditor.Clone();
- clone.LayerRepair = LayerRepair.Clone();
- clone.Automations = Automations.Clone();
- clone.Network = Network.Clone();
- return clone;*/
-
- return this.CloneByXmlSerialization();
- }
+ public static void SetVersion()
+ {
+ Instance.AppVersion = App.Version.ToString();
+ }
- #endregion
+ public static object[] PackObjects =>
+ new object[]
+ {
+ Instance.General,
+ Instance.LayerPreview,
+ Instance.Issues,
+ Instance.PixelEditor,
+ Instance.LayerRepair,
+ Instance.Automations,
+ Instance.Network
+ };
+ #endregion
+
+ #region Methods
+
+ public UserSettings Clone()
+ {
+ /*var clone = MemberwiseClone() as UserSettings;
+ clone.General = General.Clone();
+ clone.LayerPreview = LayerPreview.Clone();
+ clone.Issues = Issues.Clone();
+ clone.PixelEditor = PixelEditor.Clone();
+ clone.LayerRepair = LayerRepair.Clone();
+ clone.Automations = Automations.Clone();
+ clone.Network = Network.Clone();
+ return clone;*/
+
+ return this.CloneByXmlSerialization();
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/AboutWindow.axaml b/UVtools.WPF/Windows/AboutWindow.axaml
index 209a967..0e85338 100644
--- a/UVtools.WPF/Windows/AboutWindow.axaml
+++ b/UVtools.WPF/Windows/AboutWindow.axaml
@@ -14,18 +14,16 @@
Icon="/Assets/Icons/UVtools.ico">
<DockPanel>
- <Border DockPanel.Dock="Bottom" Background="WhiteSmoke">
- <Grid ColumnDefinitions="Auto,*" Margin="15">
- <Button
+ <Border DockPanel.Dock="Bottom" Classes="FooterActions">
+ <Grid ColumnDefinitions="Auto,*">
+ <controls:ButtonWithIcon
Grid.Column="0"
Command="{Binding CopyInformationToClipboard}"
Padding="10"
VerticalContentAlignment="Center"
- HorizontalAlignment="Right">
- <StackPanel Spacing="10" VerticalAlignment="Center" Orientation="Horizontal">
- <Image Width="16" Height="16" Source="/Assets/Icons/clipboard-16x16.png"/>
- <TextBlock Text="Copy information ⮝"/>
- </StackPanel>
+ HorizontalAlignment="Right"
+ Icon="far fa-clipboard"
+ Text="Copy information ⮝">
<Button.ContextMenu>
<ContextMenu PlacementAnchor="Top" PlacementMode="Top">
<MenuItem Header="Copy the essential information" Command="{Binding CopyEssentialInformation}"/>
@@ -34,20 +32,17 @@
<MenuItem Header="All information" Command="{Binding CopyInformationToClipboard}"/>
</ContextMenu>
</Button.ContextMenu>
- </Button>
+ </controls:ButtonWithIcon>
- <Button
- Grid.Column="1"
- Command="{Binding Close}"
- IsCancel="True"
- Padding="10"
- VerticalContentAlignment="Center"
- HorizontalAlignment="Right">
- <StackPanel Spacing="10" VerticalAlignment="Center" Orientation="Horizontal">
- <Image Width="16" Height="16" Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Text="Close"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Grid.Column="1"
+ Command="{Binding Close}"
+ IsCancel="True"
+ Padding="10"
+ VerticalContentAlignment="Center"
+ HorizontalAlignment="Right"
+ Icon="fas fa-sign-out-alt"
+ Text="Close">
+ </controls:ButtonWithIcon>
</Grid>
</Border>
@@ -135,12 +130,6 @@
UseFloatingWatermark="True"/>
</Grid>
-
-
-
-
-
-
<TextBox
Text="{Binding ScreensDescription}"
IsReadOnly="True"
@@ -148,27 +137,6 @@
CaretBrush="Transparent"
Watermark="Screens, resolution, working area, usable area:"
UseFloatingWatermark="True"/>
-
- <!--<Button
- Margin="0,20,0,0"
- HorizontalAlignment="Stretch"
- Command="{Binding CopyOpenCVInformationToClipboard}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/clipboard-16x16.png"/>
- <TextBlock VerticalAlignment="Center" Text="Copy OpenCV build information"/>
- </StackPanel>
- </Button>
-
- <Button
- Margin="0,0,0,0"
- HorizontalAlignment="Stretch"
- Command="{Binding CopyLoadedAssembliesToClipboard}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/clipboard-16x16.png"/>
- <TextBlock VerticalAlignment="Center" Text="Copy loaded assemblies"/>
- </StackPanel>
- </Button>
- !-->
</StackPanel>
</ScrollViewer>
@@ -183,13 +151,10 @@
<TextBlock Grid.Row="2" Text="{Binding Version}"/>
<TextBlock Grid.Row="4" Text="{Binding Copyright}"/>
<TextBlock Grid.Row="6" Text="{Binding Company}"/>
- <Button Grid.Row="8" VerticalAlignment="Center"
- Command="{Binding OpenLicense}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/balance-scale-16x16.png"></Image>
- <TextBlock VerticalAlignment="Center" Text="{Binding License}"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Grid.Row="8" VerticalAlignment="Center"
+ Command="{Binding OpenLicense}"
+ Text="{Binding License}"
+ Icon="fas fa-balance-scale"/>
<TabControl Grid.Row="10">
<TabItem Header="Description">
@@ -215,129 +180,9 @@
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{Binding LoadedAssemblies}"/>
</TabItem>
-
</TabControl>
-
-
</Grid>
</Border>
</DockPanel>
- <!--
- <StackPanel Orientation="Vertical">
- <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Border Margin="20,20,0,20">
- <StackPanel Spacing="5" MaxWidth="360">
- <Image Margin="0,0,0,15"
- HorizontalAlignment="Center"
- Source="/Assets/Icons/UVtools_alt.ico"
- Width="256"/>
- <TextBox
- Text="{Binding OSDescription}"
- IsReadOnly="True"
- BorderBrush="Transparent"
- CaretBrush="Transparent"
- Watermark="Operative System:"
- UseFloatingWatermark="True"/>
-
- <TextBox
- Text="{Binding RuntimeDescription}"
- IsReadOnly="True"
- BorderBrush="Transparent"
- CaretBrush="Transparent"
- Watermark="Runtime:"
- UseFloatingWatermark="True"/>
-
- <TextBox
- Text="{Binding FrameworkDescription}"
- IsReadOnly="True"
- BorderBrush="Transparent"
- CaretBrush="Transparent"
- Watermark="Framework:"
- UseFloatingWatermark="True"/>
-
- <TextBox
- Text="{Binding AvaloniaUIDescription}"
- IsReadOnly="True"
- BorderBrush="Transparent"
- CaretBrush="Transparent"
- Watermark="AvaloniaUI:"
- UseFloatingWatermark="True"/>
-
- <TextBox
- Text="{Binding OpenCVDescription}"
- IsReadOnly="True"
- BorderBrush="Transparent"
- CaretBrush="Transparent"
- Watermark="OpenCV:"
- UseFloatingWatermark="True"/>
-
- <TextBox
- Text="{Binding ProcessorCount}"
- IsReadOnly="True"
- BorderBrush="Transparent"
- CaretBrush="Transparent"
- Watermark="Processor Count:"
- UseFloatingWatermark="True"/>
-
- <TextBox
- Text="{Binding ScreensDescription}"
- IsReadOnly="True"
- BorderBrush="Transparent"
- CaretBrush="Transparent"
- Watermark="Screens, resolution, working area, usable area:"
- UseFloatingWatermark="True"/>
-
- <Button
- Margin="0,20,0,0"
- HorizontalAlignment="Stretch"
- Content="Copy OpenCV build information to clipboard"
- Command="{Binding CopyOpenCVInformationToClipboard}" />
-
- <Button
- Margin="0,10,0,0"
- HorizontalAlignment="Stretch"
- Content="Copy loaded assemblies to clipboard"
- Command="{Binding CopyLoadedAssembliesToClipboard}" />
- </StackPanel>
- </Border>
- <Grid
- RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,*"
- Margin="20">
-
- <TextBlock Grid.Row="0" Text="{Binding Software}" FontWeight="Bold"/>
- <TextBlock Grid.Row="2" Text="{Binding Version}"/>
- <TextBlock Grid.Row="4" Text="{Binding Copyright}"/>
- <TextBlock Grid.Row="6" Text="{Binding Company}"/>
- <Button Grid.Row="8" VerticalAlignment="Center"
- Command="{Binding OpenLicense}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/balance-scale-16x16.png"></Image>
- <TextBlock VerticalAlignment="Center" Text="{Binding License}"/>
- </StackPanel>
- </Button>
-
- <TextBox Grid.Row="10"
- IsReadOnly="True"
- MaxHeight="560"
- Text="{Binding Description}"/>
- </Grid>
- </StackPanel>
-
- <Border Background="WhiteSmoke">
- <Button
- Command="{Binding Close}"
- IsCancel="True"
- Padding="10"
- Margin="20"
- HorizontalAlignment="Right">
- <StackPanel Spacing="10" VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Grid.Row="6" Text="Close"/>
- </StackPanel>
- </Button>
- </Border>
-
- </StackPanel>
- -->
</controls:WindowEx>
diff --git a/UVtools.WPF/Windows/AboutWindow.axaml.cs b/UVtools.WPF/Windows/AboutWindow.axaml.cs
index c8d201f..c0c3919 100644
--- a/UVtools.WPF/Windows/AboutWindow.axaml.cs
+++ b/UVtools.WPF/Windows/AboutWindow.axaml.cs
@@ -9,154 +9,153 @@ using UVtools.Core;
using UVtools.Core.SystemOS;
using UVtools.WPF.Controls;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class AboutWindow : WindowEx
{
- public class AboutWindow : WindowEx
+ public string Software => About.Software;
+ public string Version => $"Version: {App.VersionStr} {RuntimeInformation.ProcessArchitecture}";
+ public string Copyright => App.AssemblyCopyright;
+ public string Company => App.AssemblyCompany;
+ public string License => About.License;
+ public string Description => App.AssemblyDescription;
+ public string OpenCVBuildInformation => CvInvoke.BuildInformation;
+ public string LoadedAssemblies
{
- public string Software => About.Software;
- public string Version => $"Version: {App.VersionStr} {RuntimeInformation.ProcessArchitecture}";
- public string Copyright => App.AssemblyCopyright;
- public string Company => App.AssemblyCompany;
- public string License => About.License;
- public string Description => App.AssemblyDescription;
- public string OpenCVBuildInformation => CvInvoke.BuildInformation;
- public string LoadedAssemblies
+ get
{
- get
+ var sb = new StringBuilder();
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ for (var i = 0; i < assemblies.Length; i++)
{
- var sb = new StringBuilder();
- var assemblies = AppDomain.CurrentDomain.GetAssemblies();
- for (var i = 0; i < assemblies.Length; i++)
- {
- var assembly = assemblies[i].GetName();
- sb.AppendLine($"{(i + 1).ToString().PadLeft(assemblies.Length.ToString().Length, '0')}: {assembly.Name}, Version={assembly.Version}");
- }
-
- return sb.ToString();
+ var assembly = assemblies[i].GetName();
+ sb.AppendLine($"{(i + 1).ToString().PadLeft(assemblies.Length.ToString().Length, '0')}: {assembly.Name}, Version={assembly.Version}");
}
+
+ return sb.ToString();
}
+ }
- public string OSDescription => $"{RuntimeInformation.OSDescription} {RuntimeInformation.OSArchitecture}";
+ public string OSDescription => $"{RuntimeInformation.OSDescription} {RuntimeInformation.OSArchitecture}";
- public string RuntimeDescription => RuntimeInformation.RuntimeIdentifier;
+ public string RuntimeDescription => RuntimeInformation.RuntimeIdentifier;
- public string FrameworkDescription => RuntimeInformation.FrameworkDescription;
- public string AvaloniaUIDescription => typeof(AvaloniaObject).Assembly.GetName().Version.ToString(3);
+ public string FrameworkDescription => RuntimeInformation.FrameworkDescription;
+ public string AvaloniaUIDescription => typeof(AvaloniaObject).Assembly.GetName().Version.ToString(3);
- public string OpenCVVersion
+ public string OpenCVVersion
+ {
+ get
{
- get
- {
- var match = Regex.Match(CvInvoke.BuildInformation, @"(?:Version control:\s*)(\S*)");
- if (!match.Success) return "Not found!";
- var index = match.Groups[1].Value.LastIndexOf('-');
- if (index < 0) return match.Groups[1].Value;
- return match.Groups[1].Value[..index];
- //return match.Groups[1].Value;
- }
+ var match = Regex.Match(CvInvoke.BuildInformation, @"(?:Version control:\s*)(\S*)");
+ if (!match.Success) return "Not found!";
+ var index = match.Groups[1].Value.LastIndexOf('-');
+ if (index < 0) return match.Groups[1].Value;
+ return match.Groups[1].Value[..index];
+ //return match.Groups[1].Value;
}
+ }
- public string ProcessorName => SystemAware.GetProcessorName();
+ public string ProcessorName => SystemAware.GetProcessorName();
- public int ProcessorCount => Environment.ProcessorCount;
+ public int ProcessorCount => Environment.ProcessorCount;
- public string MemoryRAMDescription
+ public string MemoryRAMDescription
+ {
+ get
{
- get
+ var memory = SystemAware.GetMemoryStatus();
+ if (memory.ullTotalPhys == 0)
{
- var memory = SystemAware.GetMemoryStatus();
- if (memory.ullTotalPhys == 0)
- {
- return "Unknown";
- }
-
- var factor = Math.Pow(1024, 3);
- return $"{(memory.ullTotalPhys-memory.ullAvailPhys) / factor:F2} / {memory.ullTotalPhys / factor:F2} GB";
+ return "Unknown";
}
+
+ var factor = Math.Pow(1024, 3);
+ return $"{(memory.ullTotalPhys-memory.ullAvailPhys) / factor:F2} / {memory.ullTotalPhys / factor:F2} GB";
}
+ }
- public int ScreenCount => Screens.ScreenCount;
- //public string ScreenResolution => $"{Screens.Primary.Bounds.Width} x {Screens.Primary.Bounds.Height} @ {Screens.Primary.PixelDensity*100}%";
- //public string WorkingArea => $"{Screens.Primary.WorkingArea.Width} x {Screens.Primary.WorkingArea.Height}";
- //public string RealWorkingArea => $"{App.MaxWindowSize.Width} x {App.MaxWindowSize.Height}";
+ public int ScreenCount => Screens.ScreenCount;
+ //public string ScreenResolution => $"{Screens.Primary.Bounds.Width} x {Screens.Primary.Bounds.Height} @ {Screens.Primary.PixelDensity*100}%";
+ //public string WorkingArea => $"{Screens.Primary.WorkingArea.Width} x {Screens.Primary.WorkingArea.Height}";
+ //public string RealWorkingArea => $"{App.MaxWindowSize.Width} x {App.MaxWindowSize.Height}";
- public string ScreensDescription
+ public string ScreensDescription
+ {
+ get
{
- get
+ var result = new StringBuilder();
+ for (int i = 0; i < Screens.All.Count; i++)
{
- var result = new StringBuilder();
- for (int i = 0; i < Screens.All.Count; i++)
- {
- var onScreen = Screens.ScreenFromVisual(App.MainWindow);
- var screen = Screens.All[i];
- result.AppendLine($"{i+1}: {screen.Bounds.Width} x {screen.Bounds.Height} @ {screen.PixelDensity * 100}%" +
- (screen.Primary ? " (Primary)" : string.Empty) +
- (onScreen == screen ? " (On this)" : string.Empty)
- );
- result.AppendLine($" WA: {screen.WorkingArea.Width} x {screen.WorkingArea.Height} UA: {Math.Round(screen.WorkingArea.Width / screen.PixelDensity)} x {Math.Round(screen.WorkingArea.Height / screen.PixelDensity)}");
- }
- return result.ToString().TrimEnd();
+ var onScreen = Screens.ScreenFromVisual(App.MainWindow);
+ var screen = Screens.All[i];
+ result.AppendLine($"{i+1}: {screen.Bounds.Width} x {screen.Bounds.Height} @ {screen.PixelDensity * 100}%" +
+ (screen.Primary ? " (Primary)" : string.Empty) +
+ (onScreen == screen ? " (On this)" : string.Empty)
+ );
+ result.AppendLine($" WA: {screen.WorkingArea.Width} x {screen.WorkingArea.Height} UA: {Math.Round(screen.WorkingArea.Width / screen.PixelDensity)} x {Math.Round(screen.WorkingArea.Height / screen.PixelDensity)}");
}
+ return result.ToString().TrimEnd();
}
+ }
- public AboutWindow()
- {
- InitializeComponent();
- DataContext = this;
- Title = $"About {About.SoftwareWithVersion}";
- }
+ public AboutWindow()
+ {
+ InitializeComponent();
+ DataContext = this;
+ Title = $"About {About.SoftwareWithVersion}";
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void OpenLicense() => SystemAware.OpenBrowser(About.LicenseUrl);
+ public void OpenLicense() => SystemAware.OpenBrowser(About.LicenseUrl);
- private string GetEssentialInformation()
- {
- var message = new StringBuilder();
- message.AppendLine($"{About.SoftwareWithVersion}");
- message.AppendLine($"Operative system: {OSDescription}");
- message.AppendLine($"Processor: {ProcessorName}");
- message.AppendLine($"Processor cores: {ProcessorCount}");
- message.AppendLine($"Memory RAM: {MemoryRAMDescription}");
- message.AppendLine($"Runtime: {RuntimeDescription}");
- message.AppendLine($"Framework: {FrameworkDescription}");
- message.AppendLine($"AvaloniaUI: {AvaloniaUIDescription}");
- message.AppendLine($"OpenCV: {OpenCVVersion}");
- message.AppendLine();
- message.AppendLine("Sreens, resolution, working area, usable area:");
- message.AppendLine(ScreensDescription);
- message.AppendLine();
- message.AppendLine($"Path: {App.ApplicationPath}");
- return message.ToString();
- }
+ private string GetEssentialInformation()
+ {
+ var message = new StringBuilder();
+ message.AppendLine($"{About.SoftwareWithVersion}");
+ message.AppendLine($"Operative system: {OSDescription}");
+ message.AppendLine($"Processor: {ProcessorName}");
+ message.AppendLine($"Processor cores: {ProcessorCount}");
+ message.AppendLine($"Memory RAM: {MemoryRAMDescription}");
+ message.AppendLine($"Runtime: {RuntimeDescription}");
+ message.AppendLine($"Framework: {FrameworkDescription}");
+ message.AppendLine($"AvaloniaUI: {AvaloniaUIDescription}");
+ message.AppendLine($"OpenCV: {OpenCVVersion}");
+ message.AppendLine();
+ message.AppendLine("Sreens, resolution, working area, usable area:");
+ message.AppendLine(ScreensDescription);
+ message.AppendLine();
+ message.AppendLine($"Path: {App.ApplicationPath}");
+ return message.ToString();
+ }
- public void CopyEssentialInformation()
- {
- Application.Current.Clipboard.SetTextAsync(GetEssentialInformation());
- }
+ public void CopyEssentialInformation()
+ {
+ Application.Current.Clipboard.SetTextAsync(GetEssentialInformation());
+ }
- public void CopyOpenCVInformationToClipboard()
- {
- Application.Current.Clipboard.SetTextAsync(CvInvoke.BuildInformation);
- }
+ public void CopyOpenCVInformationToClipboard()
+ {
+ Application.Current.Clipboard.SetTextAsync(CvInvoke.BuildInformation);
+ }
- public void CopyLoadedAssembliesToClipboard()
- {
- Application.Current.Clipboard.SetTextAsync(LoadedAssemblies);
- }
+ public void CopyLoadedAssembliesToClipboard()
+ {
+ Application.Current.Clipboard.SetTextAsync(LoadedAssemblies);
+ }
- public async void CopyInformationToClipboard()
- {
- var message = new StringBuilder();
- message.Append(GetEssentialInformation());
- message.AppendLine(CvInvoke.BuildInformation);
- message.AppendLine("Loaded Assemblies:");
- message.AppendLine(LoadedAssemblies);
- await Application.Current.Clipboard.SetTextAsync(message.ToString());
- }
+ public async void CopyInformationToClipboard()
+ {
+ var message = new StringBuilder();
+ message.Append(GetEssentialInformation());
+ message.AppendLine(CvInvoke.BuildInformation);
+ message.AppendLine("Loaded Assemblies:");
+ message.AppendLine(LoadedAssemblies);
+ await Application.Current.Clipboard.SetTextAsync(message.ToString());
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/BenchmarkWindow.axaml b/UVtools.WPF/Windows/BenchmarkWindow.axaml
index 8af0308..a4778a6 100644
--- a/UVtools.WPF/Windows/BenchmarkWindow.axaml
+++ b/UVtools.WPF/Windows/BenchmarkWindow.axaml
@@ -22,7 +22,7 @@
Auto"
>
<Border
- Background="WhiteSmoke"
+ Background="{DynamicResource LightBackground}"
Padding="10"
BorderBrush="Black"
BorderThickness="1"
diff --git a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs
index 2e0c5e3..718401f 100644
--- a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs
+++ b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs
@@ -13,453 +13,452 @@ using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class BenchmarkWindow : WindowEx
{
- public class BenchmarkWindow : WindowEx
+ private int _testSelectedIndex;
+ private string _singleThreadTdps = $"0 {RunsAbbreviation}";
+ private string _multiThreadTdps = $"0 {RunsAbbreviation}";
+ private string _devSingleThreadTdps = $"{Tests[0].DevSingleThreadResult} {RunsAbbreviation} ({SingleThreadTests} tests / {Math.Round(SingleThreadTests / Tests[0].DevSingleThreadResult, 2)}s)";
+ private string _devMultiThreadTdps = $"{Tests[0].DevMultiThreadResult} {RunsAbbreviation} ({MultiThreadTests} tests / {Math.Round(MultiThreadTests / Tests[0].DevMultiThreadResult, 2)}s)";
+ private bool _isRunning;
+ private string _startStopButtonText = "Start";
+ private const ushort SingleThreadTests = 100;
+ private const ushort MultiThreadTests = 1000;
+
+ public const string RunsAbbreviation = "TDPS";
+ public const string StressCPUTestName = "Stress CPU (Run until stop)";
+
+ //private readonly RNGCryptoServiceProvider _randomProvider = new();
+
+ private CancellationTokenSource _tokenSource;
+ private CancellationToken _token => _tokenSource.Token;
+
+ public static BenchmarkTest[] Tests =>
+ new[]
+ {
+ new BenchmarkTest("4K Random CBBDLP Enconde", "Test4KRandomCBBDLPEncode", 57.14f, 401.61f),
+ new BenchmarkTest("8K Random CBBDLP Enconde", "Test8KRandomCBBDLPEncode", 12.03f, 99.80f),
+ new BenchmarkTest("4K Random CBT Enconde", "Test4KRandomCBTEncode", 19.05f, 124.38f),
+ new BenchmarkTest("8K Random CBT Enconde", "Test8KRandomCBTEncode", 4.03f, 35.64f),
+ new BenchmarkTest("4K Random PW0 Enconde", "Test4KRandomPW0Encode", 18.85f, 103.00f),
+ new BenchmarkTest("8K Random PW0 Enconde", "Test8KRandomPW0Encode", 4.07f, 26.65f),
+ new BenchmarkTest(StressCPUTestName, "Test4KRandomCBTEncode", 0, 0),
+ };
+
+ public string Description => "Benchmark your machine against pre-defined tests.\n" +
+ "This will use all compution power avaliable, CPU will be exhausted.\n" +
+ "Run the test while your PC is idle or not in heavy load.\n" +
+ "Results are in 'tests done per second' (TDPS)\n" +
+ "\n" +
+ "To your reference you are competing against developer system:\n"+
+ $"CPU: {BenchmarkTest.DEVCPU}\n" +
+ $"RAM: {BenchmarkTest.DEVRAM}";
+
+ public int TestSelectedIndex
{
- private int _testSelectedIndex;
- private string _singleThreadTdps = $"0 {RunsAbbreviation}";
- private string _multiThreadTdps = $"0 {RunsAbbreviation}";
- private string _devSingleThreadTdps = $"{Tests[0].DevSingleThreadResult} {RunsAbbreviation} ({SingleThreadTests} tests / {Math.Round(SingleThreadTests / Tests[0].DevSingleThreadResult, 2)}s)";
- private string _devMultiThreadTdps = $"{Tests[0].DevMultiThreadResult} {RunsAbbreviation} ({MultiThreadTests} tests / {Math.Round(MultiThreadTests / Tests[0].DevMultiThreadResult, 2)}s)";
- private bool _isRunning;
- private string _startStopButtonText = "Start";
- private const ushort SingleThreadTests = 100;
- private const ushort MultiThreadTests = 1000;
-
- public const string RunsAbbreviation = "TDPS";
- public const string StressCPUTestName = "Stress CPU (Run until stop)";
-
- //private readonly RNGCryptoServiceProvider _randomProvider = new();
-
- private CancellationTokenSource _tokenSource;
- private CancellationToken _token => _tokenSource.Token;
-
- public static BenchmarkTest[] Tests =>
- new[]
- {
- new BenchmarkTest("4K Random CBBDLP Enconde", "Test4KRandomCBBDLPEncode", 57.14f, 401.61f),
- new BenchmarkTest("8K Random CBBDLP Enconde", "Test8KRandomCBBDLPEncode", 12.03f, 99.80f),
- new BenchmarkTest("4K Random CBT Enconde", "Test4KRandomCBTEncode", 19.05f, 124.38f),
- new BenchmarkTest("8K Random CBT Enconde", "Test8KRandomCBTEncode", 4.03f, 35.64f),
- new BenchmarkTest("4K Random PW0 Enconde", "Test4KRandomPW0Encode", 18.85f, 103.00f),
- new BenchmarkTest("8K Random PW0 Enconde", "Test8KRandomPW0Encode", 4.07f, 26.65f),
- new BenchmarkTest(StressCPUTestName, "Test4KRandomCBTEncode", 0, 0),
- };
-
- public string Description => "Benchmark your machine against pre-defined tests.\n" +
- "This will use all compution power avaliable, CPU will be exhausted.\n" +
- "Run the test while your PC is idle or not in heavy load.\n" +
- "Results are in 'tests done per second' (TDPS)\n" +
- "\n" +
- "To your reference you are competing against developer system:\n"+
- $"CPU: {BenchmarkTest.DEVCPU}\n" +
- $"RAM: {BenchmarkTest.DEVRAM}";
-
- public int TestSelectedIndex
+ get => _testSelectedIndex;
+ set
{
- get => _testSelectedIndex;
- set
- {
- if(!RaiseAndSetIfChanged(ref _testSelectedIndex, value)) return;
- DevSingleThreadTDPS = $"{Tests[_testSelectedIndex].DevSingleThreadResult} {RunsAbbreviation} ({SingleThreadTests} tests / {Math.Round(SingleThreadTests / Tests[_testSelectedIndex].DevSingleThreadResult, 2)}s)";
- DevMultiThreadTDPS = $"{Tests[_testSelectedIndex].DevMultiThreadResult} {RunsAbbreviation} ({MultiThreadTests} tests / {Math.Round(MultiThreadTests / Tests[_testSelectedIndex].DevMultiThreadResult, 2)}s)";
- }
+ if(!RaiseAndSetIfChanged(ref _testSelectedIndex, value)) return;
+ DevSingleThreadTDPS = $"{Tests[_testSelectedIndex].DevSingleThreadResult} {RunsAbbreviation} ({SingleThreadTests} tests / {Math.Round(SingleThreadTests / Tests[_testSelectedIndex].DevSingleThreadResult, 2)}s)";
+ DevMultiThreadTDPS = $"{Tests[_testSelectedIndex].DevMultiThreadResult} {RunsAbbreviation} ({MultiThreadTests} tests / {Math.Round(MultiThreadTests / Tests[_testSelectedIndex].DevMultiThreadResult, 2)}s)";
}
+ }
- public string SingleThreadTDPS
- {
- get => _singleThreadTdps;
- set => RaiseAndSetIfChanged(ref _singleThreadTdps, value);
- }
+ public string SingleThreadTDPS
+ {
+ get => _singleThreadTdps;
+ set => RaiseAndSetIfChanged(ref _singleThreadTdps, value);
+ }
- public string MultiThreadTDPS
- {
- get => _multiThreadTdps;
- set => RaiseAndSetIfChanged(ref _multiThreadTdps, value);
- }
+ public string MultiThreadTDPS
+ {
+ get => _multiThreadTdps;
+ set => RaiseAndSetIfChanged(ref _multiThreadTdps, value);
+ }
- public string DevSingleThreadTDPS
- {
- get => _devSingleThreadTdps;
- set => RaiseAndSetIfChanged(ref _devSingleThreadTdps, value);
- }
+ public string DevSingleThreadTDPS
+ {
+ get => _devSingleThreadTdps;
+ set => RaiseAndSetIfChanged(ref _devSingleThreadTdps, value);
+ }
- public string DevMultiThreadTDPS
- {
- get => _devMultiThreadTdps;
- set => RaiseAndSetIfChanged(ref _devMultiThreadTdps, value);
- }
+ public string DevMultiThreadTDPS
+ {
+ get => _devMultiThreadTdps;
+ set => RaiseAndSetIfChanged(ref _devMultiThreadTdps, value);
+ }
- public string StartStopButtonText
- {
- get => _startStopButtonText;
- set => RaiseAndSetIfChanged(ref _startStopButtonText, value);
- }
+ public string StartStopButtonText
+ {
+ get => _startStopButtonText;
+ set => RaiseAndSetIfChanged(ref _startStopButtonText, value);
+ }
- public bool IsRunning
+ public bool IsRunning
+ {
+ get => _isRunning;
+ set
{
- get => _isRunning;
- set
- {
- if(!RaiseAndSetIfChanged(ref _isRunning, value)) return;
- StartStopButtonText = _isRunning ? "Stop" : "Start";
- }
+ if(!RaiseAndSetIfChanged(ref _isRunning, value)) return;
+ StartStopButtonText = _isRunning ? "Stop" : "Start";
}
+ }
- public BenchmarkWindow()
- {
- InitializeComponent();
+ public BenchmarkWindow()
+ {
+ InitializeComponent();
- DataContext = this;
- }
+ DataContext = this;
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- protected override bool HandleClosing() => IsRunning;
+ protected override bool HandleClosing() => IsRunning;
- public void StartStop()
+ public void StartStop()
+ {
+ if (IsRunning)
{
- if (IsRunning)
- {
- if (!_token.CanBeCanceled || _token.IsCancellationRequested) return;
- _tokenSource.Cancel();
- }
- else
- {
- var benchmark = Tests[_testSelectedIndex];
- SingleThreadTDPS = $"Running {SingleThreadTests} tests";
- MultiThreadTDPS = $"Running {MultiThreadTests} tests";
+ if (!_token.CanBeCanceled || _token.IsCancellationRequested) return;
+ _tokenSource.Cancel();
+ }
+ else
+ {
+ var benchmark = Tests[_testSelectedIndex];
+ SingleThreadTDPS = $"Running {SingleThreadTests} tests";
+ MultiThreadTDPS = $"Running {MultiThreadTests} tests";
- _tokenSource = new CancellationTokenSource();
- var theMethod = GetType().GetMethod(benchmark.FunctionName);
+ _tokenSource = new CancellationTokenSource();
+ var theMethod = GetType().GetMethod(benchmark.FunctionName);
- Task.Factory.StartNew(() =>
+ Task.Factory.StartNew(() =>
+ {
+ var sw = Stopwatch.StartNew();
+ try
{
- var sw = Stopwatch.StartNew();
- try
+ if (benchmark.Name.Equals(StressCPUTestName))
{
- if (benchmark.Name.Equals(StressCPUTestName))
+ while (true)
{
- while (true)
+ if (_token.IsCancellationRequested) break;
+ Parallel.For(0, MultiThreadTests, i =>
{
- if (_token.IsCancellationRequested) break;
- Parallel.For(0, MultiThreadTests, i =>
- {
- if (_token.IsCancellationRequested) return;
- theMethod.Invoke(this, null);
- });
- }
-
- return;
- }
-
-
- for (int i = 0; i < SingleThreadTests; i++)
- {
- if (_token.IsCancellationRequested) _token.ThrowIfCancellationRequested();
- theMethod.Invoke(this, null);
+ if (_token.IsCancellationRequested) return;
+ theMethod.Invoke(this, null);
+ });
}
- sw.Stop();
- var singleMiliseconds = sw.ElapsedMilliseconds;
- Dispatcher.UIThread.InvokeAsync(() => UpdateResults(true, singleMiliseconds));
+ return;
+ }
+
+ for (int i = 0; i < SingleThreadTests; i++)
+ {
if (_token.IsCancellationRequested) _token.ThrowIfCancellationRequested();
+ theMethod.Invoke(this, null);
+ }
- sw.Restart();
- Parallel.For(0, MultiThreadTests, i =>
- {
- if (_token.IsCancellationRequested) return;
- theMethod.Invoke(this, null);
- });
+ sw.Stop();
+ var singleMiliseconds = sw.ElapsedMilliseconds;
+ Dispatcher.UIThread.InvokeAsync(() => UpdateResults(true, singleMiliseconds));
- sw.Stop();
+ if (_token.IsCancellationRequested) _token.ThrowIfCancellationRequested();
- if (_token.IsCancellationRequested) _token.ThrowIfCancellationRequested();
- var multiMiliseconds = sw.ElapsedMilliseconds;
- Dispatcher.UIThread.InvokeAsync(() => UpdateResults(false, multiMiliseconds));
- }
- catch (OperationCanceledException)
+ sw.Restart();
+ Parallel.For(0, MultiThreadTests, i =>
{
+ if (_token.IsCancellationRequested) return;
+ theMethod.Invoke(this, null);
+ });
- }
- catch (Exception ex)
- {
- Dispatcher.UIThread.InvokeAsync(() => this.MessageBoxError(ex.ToString(), "Error"));
-
- }
- finally
- {
- Dispatcher.UIThread.InvokeAsync(() => IsRunning = !IsRunning);
- }
- }, _token);
+ sw.Stop();
- IsRunning = !IsRunning;
- }
- }
+ if (_token.IsCancellationRequested) _token.ThrowIfCancellationRequested();
+ var multiMiliseconds = sw.ElapsedMilliseconds;
+ Dispatcher.UIThread.InvokeAsync(() => UpdateResults(false, multiMiliseconds));
+ }
+ catch (OperationCanceledException)
+ {
- private void UpdateResults(bool isSingleThread, long milliseconds)
- {
- decimal seconds = Math.Round(milliseconds / 1000m, 2);
- //var text = (isSingleThread ? "Single" : "Multi") + $" Thread: {Math.Round(SingleThreadTests / seconds, 2)} OPS ({seconds}s)";
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(() => this.MessageBoxError(ex.ToString(), "Error"));
+
+ }
+ finally
+ {
+ Dispatcher.UIThread.InvokeAsync(() => IsRunning = !IsRunning);
+ }
+ }, _token);
- if (isSingleThread)
- SingleThreadTDPS = $"{Math.Round(SingleThreadTests / seconds, 2)} {RunsAbbreviation} ({SingleThreadTests} tests / {seconds}s)";
- else
- MultiThreadTDPS = $"{Math.Round(MultiThreadTests / seconds, 2)} {RunsAbbreviation} ({MultiThreadTests} tests / {seconds}s)";
+ IsRunning = !IsRunning;
}
+ }
- #region Tests
- public byte[] EncodeCbddlpImage(Mat image, byte bit = 0)
- {
- List<byte> rawData = new();
- var span = image.GetDataByteSpan();
+ private void UpdateResults(bool isSingleThread, long milliseconds)
+ {
+ decimal seconds = Math.Round(milliseconds / 1000m, 2);
+ //var text = (isSingleThread ? "Single" : "Multi") + $" Thread: {Math.Round(SingleThreadTests / seconds, 2)} OPS ({seconds}s)";
- bool obit = false;
- int rep = 0;
+ if (isSingleThread)
+ SingleThreadTDPS = $"{Math.Round(SingleThreadTests / seconds, 2)} {RunsAbbreviation} ({SingleThreadTests} tests / {seconds}s)";
+ else
+ MultiThreadTDPS = $"{Math.Round(MultiThreadTests / seconds, 2)} {RunsAbbreviation} ({MultiThreadTests} tests / {seconds}s)";
+ }
- //ngrey:= uint16(r | g | b)
- // thresholds:
- // aa 1: 127
- // aa 2: 255 127
- // aa 4: 255 191 127 63
- // aa 8: 255 223 191 159 127 95 63 31
- byte threshold = (byte)(256 / 1 * bit - 1);
+ #region Tests
+ public byte[] EncodeCbddlpImage(Mat image, byte bit = 0)
+ {
+ List<byte> rawData = new();
+ var span = image.GetDataByteSpan();
- void AddRep()
- {
- if (rep <= 0) return;
+ bool obit = false;
+ int rep = 0;
- byte by = (byte)rep;
+ //ngrey:= uint16(r | g | b)
+ // thresholds:
+ // aa 1: 127
+ // aa 2: 255 127
+ // aa 4: 255 191 127 63
+ // aa 8: 255 223 191 159 127 95 63 31
+ byte threshold = (byte)(256 / 1 * bit - 1);
- if (obit)
- {
- by |= 0x80;
- //bitsOn += uint(rep)
- }
+ void AddRep()
+ {
+ if (rep <= 0) return;
- rawData.Add(by);
- }
+ byte by = (byte)rep;
- for (int pixel = 0; pixel < span.Length; pixel++)
+ if (obit)
{
- var nbit = span[pixel] >= threshold;
-
- if (nbit == obit)
- {
- rep++;
-
- if (rep == 0x7d)
- {
- AddRep();
- rep = 0;
- }
- }
- else
- {
- AddRep();
- obit = nbit;
- rep = 1;
- }
+ by |= 0x80;
+ //bitsOn += uint(rep)
}
- // Collect stragglers
- AddRep();
-
-
- return rawData.ToArray();
+ rawData.Add(by);
}
- private byte[] EncodeCbtImage(Mat image)
+ for (int pixel = 0; pixel < span.Length; pixel++)
{
- List<byte> rawData = new();
- byte color = byte.MaxValue >> 1;
- uint stride = 0;
- var span = image.GetDataByteSpan();
+ var nbit = span[pixel] >= threshold;
- void AddRep()
+ if (nbit == obit)
{
- if (stride == 0)
- {
- return;
- }
+ rep++;
- if (stride > 1)
+ if (rep == 0x7d)
{
- color |= 0x80;
+ AddRep();
+ rep = 0;
}
- rawData.Add(color);
+ }
+ else
+ {
+ AddRep();
+ obit = nbit;
+ rep = 1;
+ }
+ }
- if (stride <= 1)
- {
- // no run needed
- return;
- }
+ // Collect stragglers
+ AddRep();
- if (stride <= 0x7f)
- {
- rawData.Add((byte)stride);
- return;
- }
- if (stride <= 0x3fff)
- {
- rawData.Add((byte)((stride >> 8) | 0x80));
- rawData.Add((byte)stride);
- return;
- }
+ return rawData.ToArray();
+ }
- if (stride <= 0x1fffff)
- {
- rawData.Add((byte)((stride >> 16) | 0xc0));
- rawData.Add((byte)(stride >> 8));
- rawData.Add((byte)stride);
- return;
- }
+ private byte[] EncodeCbtImage(Mat image)
+ {
+ List<byte> rawData = new();
+ byte color = byte.MaxValue >> 1;
+ uint stride = 0;
+ var span = image.GetDataByteSpan();
- if (stride <= 0xfffffff)
- {
- rawData.Add((byte)((stride >> 24) | 0xe0));
- rawData.Add((byte)(stride >> 16));
- rawData.Add((byte)(stride >> 8));
- rawData.Add((byte)stride);
- }
+ void AddRep()
+ {
+ if (stride == 0)
+ {
+ return;
+ }
+ if (stride > 1)
+ {
+ color |= 0x80;
}
+ rawData.Add(color);
+ if (stride <= 1)
+ {
+ // no run needed
+ return;
+ }
- for (int pixel = 0; pixel < span.Length; pixel++)
+ if (stride <= 0x7f)
{
- var grey7 = (byte)(span[pixel] >> 1);
+ rawData.Add((byte)stride);
+ return;
+ }
- if (grey7 == color)
- {
- stride++;
- }
- else
- {
- AddRep();
- color = grey7;
- stride = 1;
- }
+ if (stride <= 0x3fff)
+ {
+ rawData.Add((byte)((stride >> 8) | 0x80));
+ rawData.Add((byte)stride);
+ return;
}
- AddRep();
+ if (stride <= 0x1fffff)
+ {
+ rawData.Add((byte)((stride >> 16) | 0xc0));
+ rawData.Add((byte)(stride >> 8));
+ rawData.Add((byte)stride);
+ return;
+ }
+ if (stride <= 0xfffffff)
+ {
+ rawData.Add((byte)((stride >> 24) | 0xe0));
+ rawData.Add((byte)(stride >> 16));
+ rawData.Add((byte)(stride >> 8));
+ rawData.Add((byte)stride);
+ }
- return rawData.ToArray();
}
- public byte[] EncodePW0Image(Mat image)
- {
- List<byte> rawData = new();
- var span = image.GetDataByteSpan();
- int lastColor = -1;
- int reps = 0;
+ for (int pixel = 0; pixel < span.Length; pixel++)
+ {
+ var grey7 = (byte)(span[pixel] >> 1);
- void PutReps()
+ if (grey7 == color)
{
- while (reps > 0)
- {
- int done = reps;
+ stride++;
+ }
+ else
+ {
+ AddRep();
+ color = grey7;
+ stride = 1;
+ }
+ }
- if (lastColor == 0 || lastColor == 0xf)
- {
- if (done > 0xfff)
- {
- done = 0xfff;
- }
- //more:= []byte{ 0, 0}
- //binary.BigEndian.PutUint16(more, uint16(done | (color << 12)))
+ AddRep();
- //rle = append(rle, more...)
- ushort more = (ushort)(done | (lastColor << 12));
- rawData.Add((byte)(more >> 8));
- rawData.Add((byte)more);
- }
- else
- {
- if (done > 0xf)
- {
- done = 0xf;
- }
- rawData.Add((byte)(done | lastColor << 4));
- }
+ return rawData.ToArray();
+ }
- reps -= done;
- }
- }
+ public byte[] EncodePW0Image(Mat image)
+ {
+ List<byte> rawData = new();
+ var span = image.GetDataByteSpan();
- for (int i = 0; i < span.Length; i++)
+ int lastColor = -1;
+ int reps = 0;
+
+ void PutReps()
+ {
+ while (reps > 0)
{
- int color = span[i] >> 4;
+ int done = reps;
- if (color == lastColor)
+ if (lastColor == 0 || lastColor == 0xf)
{
- reps++;
+ if (done > 0xfff)
+ {
+ done = 0xfff;
+ }
+ //more:= []byte{ 0, 0}
+ //binary.BigEndian.PutUint16(more, uint16(done | (color << 12)))
+
+ //rle = append(rle, more...)
+
+ ushort more = (ushort)(done | (lastColor << 12));
+ rawData.Add((byte)(more >> 8));
+ rawData.Add((byte)more);
}
else
{
- PutReps();
- lastColor = color;
- reps = 1;
+ if (done > 0xf)
+ {
+ done = 0xf;
+ }
+ rawData.Add((byte)(done | lastColor << 4));
}
- }
- PutReps();
-
- return rawData.ToArray();
+ reps -= done;
+ }
}
- public Mat RandomMat(int width, int height)
+ for (int i = 0; i < span.Length; i++)
{
- Mat mat = new(new Size(width, height), DepthType.Cv8U, 1);
- CvInvoke.Randu(mat, EmguExtensions.BlackColor, EmguExtensions.WhiteColor);
- return mat;
- }
+ int color = span[i] >> 4;
- public void Test4KRandomCBBDLPEncode()
- {
- using var mat = RandomMat(3840, 2160);
- EncodeCbddlpImage(mat);
+ if (color == lastColor)
+ {
+ reps++;
+ }
+ else
+ {
+ PutReps();
+ lastColor = color;
+ reps = 1;
+ }
}
- public void Test8KRandomCBBDLPEncode()
- {
- using var mat = RandomMat(7680, 4320);
- EncodeCbddlpImage(mat);
- }
+ PutReps();
- public void Test4KRandomCBTEncode()
- {
- using var mat = RandomMat(3840, 2160);
- EncodeCbtImage(mat);
- }
+ return rawData.ToArray();
+ }
- public void Test8KRandomCBTEncode()
- {
- using var mat = RandomMat(7680, 4320);
- EncodeCbtImage(mat);
- }
+ public Mat RandomMat(int width, int height)
+ {
+ Mat mat = new(new Size(width, height), DepthType.Cv8U, 1);
+ CvInvoke.Randu(mat, EmguExtensions.BlackColor, EmguExtensions.WhiteColor);
+ return mat;
+ }
- public void Test4KRandomPW0Encode()
- {
- using var mat = RandomMat(3840, 2160);
- EncodePW0Image(mat);
- }
+ public void Test4KRandomCBBDLPEncode()
+ {
+ using var mat = RandomMat(3840, 2160);
+ EncodeCbddlpImage(mat);
+ }
- public void Test8KRandomPW0Encode()
- {
- using var mat = RandomMat(7680, 4320);
- EncodePW0Image(mat);
- }
+ public void Test8KRandomCBBDLPEncode()
+ {
+ using var mat = RandomMat(7680, 4320);
+ EncodeCbddlpImage(mat);
+ }
+
+ public void Test4KRandomCBTEncode()
+ {
+ using var mat = RandomMat(3840, 2160);
+ EncodeCbtImage(mat);
+ }
+
+ public void Test8KRandomCBTEncode()
+ {
+ using var mat = RandomMat(7680, 4320);
+ EncodeCbtImage(mat);
+ }
+
+ public void Test4KRandomPW0Encode()
+ {
+ using var mat = RandomMat(3840, 2160);
+ EncodePW0Image(mat);
+ }
- #endregion
+ public void Test8KRandomPW0Encode()
+ {
+ using var mat = RandomMat(7680, 4320);
+ EncodePW0Image(mat);
}
-}
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/ColorPickerWindow.axaml b/UVtools.WPF/Windows/ColorPickerWindow.axaml
index 47164ce..bab973d 100644
--- a/UVtools.WPF/Windows/ColorPickerWindow.axaml
+++ b/UVtools.WPF/Windows/ColorPickerWindow.axaml
@@ -16,26 +16,22 @@
<StackPanel Orientation="Vertical">
<cp:ColorPicker Color="{Binding ResultColor, Mode=TwoWay}" />
- <Panel Background="LightGray">
- <StackPanel HorizontalAlignment="Right" Margin="15" Orientation="Horizontal">
- <Button HorizontalAlignment="Right" Name="Actions.Save" Margin="0,0,10,0" Padding="10" IsDefault="True"
+ <Border Classes="FooterActions">
+ <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Spacing="10">
+ <controls:ButtonWithIcon HorizontalAlignment="Right" Name="Actions.Save" Padding="10" IsDefault="True"
+ Icon="fas fa-check"
+ Text="Select"
Command="{Binding OnClickOk}">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- <TextBlock Margin="10,0,0,0">Select</TextBlock>
- </StackPanel>
- </Button>
+ <StackPanel Orientation="Horizontal"/>
+ </controls:ButtonWithIcon>
- <Button HorizontalAlignment="Right"
+ <controls:ButtonWithIcon HorizontalAlignment="Right"
Name="Actions.Cancel" Padding="10" IsCancel="True"
- Command="{Binding Close}">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Margin="10,0,0,0">Cancel</TextBlock>
- </StackPanel>
- </Button>
+ Icon="fas fa-sign-out-alt"
+ Text="Cancel"
+ Command="{Binding Close}"/>
</StackPanel>
- </Panel>
+ </Border>
</StackPanel>
diff --git a/UVtools.WPF/Windows/ColorPickerWindow.axaml.cs b/UVtools.WPF/Windows/ColorPickerWindow.axaml.cs
index 64027c1..61ddd74 100644
--- a/UVtools.WPF/Windows/ColorPickerWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ColorPickerWindow.axaml.cs
@@ -2,34 +2,33 @@
using Avalonia.Media;
using UVtools.WPF.Controls;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class ColorPickerWindow : WindowEx
{
- public class ColorPickerWindow : WindowEx
- {
- public Color ResultColor { get; set; }
+ public Color ResultColor { get; set; }
- public ColorPickerWindow()
- {
- DataContext = this;
- InitializeComponent();
+ public ColorPickerWindow()
+ {
+ DataContext = this;
+ InitializeComponent();
- }
- public ColorPickerWindow(Color defaultColor)
- {
- ResultColor = defaultColor;
- DataContext = this;
- InitializeComponent();
- }
+ }
+ public ColorPickerWindow(Color defaultColor)
+ {
+ ResultColor = defaultColor;
+ DataContext = this;
+ InitializeComponent();
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void OnClickOk()
- {
- DialogResult = DialogResults.OK;
- CloseWithResult();
- }
+ public void OnClickOk()
+ {
+ DialogResult = DialogResults.OK;
+ CloseWithResult();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/MaterialManagerWindow.axaml b/UVtools.WPF/Windows/MaterialManagerWindow.axaml
index f59dda3..53b2cf3 100644
--- a/UVtools.WPF/Windows/MaterialManagerWindow.axaml
+++ b/UVtools.WPF/Windows/MaterialManagerWindow.axaml
@@ -18,15 +18,6 @@
IsExpanded="True">
<StackPanel>
- <!--<Button VerticalAlignment="Center"
- Command="{Binding RefreshStatistics}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/refresh-16x16.png"/>
- <TextBlock Text="Refresh statistics"/>
- </StackPanel>
- </Button>
- !-->
-
<WrapPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Bottles in stock:" VerticalAlignment="Center" FontWeight="Bold"/>
@@ -91,19 +82,14 @@
Watermark="A descriptive material name, eg: Epax Hard Grey"
Text="{Binding Material.Name}"/>
- <Button
+ <controls:ButtonWithIcon
Grid.Row="0" Grid.Column="14" Grid.ColumnSpan="3"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Stretch"
- Command="{Binding AddNewMaterial}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="Add new material"/>
- </StackPanel>
- </Button>
+ Icon="fas fa-plus"
+ Text="Add new material"
+ Command="{Binding AddNewMaterial}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
VerticalAlignment="Center"
@@ -164,28 +150,20 @@
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="1">
- <Button VerticalAlignment="Center"
+ <controls:ButtonWithIcon VerticalAlignment="Center"
IsEnabled="{Binding #MaterialsTable.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding RemoveSelectedMaterials}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/trash-16x16.png"/>
- <TextBlock Text="Remove selected materials"/>
- </StackPanel>
- </Button>
-
- <Button VerticalAlignment="Center"
+ Icon="fas fa-trash-alt"
+ Text="Remove selected materials"
+ Command="{Binding RemoveSelectedMaterials}"/>
+
+ <controls:ButtonWithIcon VerticalAlignment="Center"
IsEnabled="{Binding Manager.Count}"
- Command="{Binding ClearMaterials}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- <TextBlock Text="{Binding Manager.Count, StringFormat=Clear {0} materials}"/>
- </StackPanel>
- </Button>
+ Icon="fas fa-times"
+ Text="{Binding Manager.Count, StringFormat=Clear {0} materials}"
+ Command="{Binding ClearMaterials}"/>
</StackPanel>
-
-
<DataGrid Grid.Row="3"
Name="MaterialsTable"
CanUserReorderColumns="True"
diff --git a/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs b/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs
index 925574d..d221059 100644
--- a/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs
+++ b/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs
@@ -9,88 +9,87 @@ using UVtools.Core.Objects;
using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class MaterialManagerWindow : WindowEx
{
- public class MaterialManagerWindow : WindowEx
- {
- private Material _material = new();
- private readonly DataGrid _grid;
- public MaterialManager Manager => MaterialManager.Instance;
+ private Material _material = new();
+ private readonly DataGrid _grid;
+ public MaterialManager Manager => MaterialManager.Instance;
- public Material Material
- {
- get => _material;
- set => RaiseAndSetIfChanged(ref _material, value);
- }
+ public Material Material
+ {
+ get => _material;
+ set => RaiseAndSetIfChanged(ref _material, value);
+ }
- public MaterialManagerWindow()
- {
- InitializeComponent();
+ public MaterialManagerWindow()
+ {
+ InitializeComponent();
#if DEBUG
- this.AttachDevTools();
+ this.AttachDevTools();
#endif
- _grid = this.FindControl<DataGrid>("MaterialsTable");
+ _grid = this.FindControl<DataGrid>("MaterialsTable");
- MaterialManager.Load(); // Reload
+ MaterialManager.Load(); // Reload
- DataContext = this;
- }
+ DataContext = this;
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- protected override void OnClosed(EventArgs e)
- {
- base.OnClosed(e);
- MaterialManager.Save(); // Apply changes
- }
+ protected override void OnClosed(EventArgs e)
+ {
+ base.OnClosed(e);
+ MaterialManager.Save(); // Apply changes
+ }
- public void RefreshStatistics()
- {
- Manager.RaisePropertiesChanged();
- }
+ public void RefreshStatistics()
+ {
+ Manager.RaisePropertiesChanged();
+ }
- public async void AddNewMaterial()
+ public async void AddNewMaterial()
+ {
+ if (string.IsNullOrWhiteSpace(Material.Name))
{
- if (string.IsNullOrWhiteSpace(Material.Name))
- {
- await this.MessageBoxError("Material name can't be empty");
- return;
- }
-
- if (Manager.Contains(Material))
- {
- await this.MessageBoxError("A material with same name already exists.");
- return;
- }
-
- Material.BottleRemainingVolume = Material.BottleVolume;
-
- if (await this.MessageBoxQuestion("Are you sure you want to add the following material:\n" +
- $"{Material}") != ButtonResult.Yes) return;
-
- Manager.Add(Material);
- Manager.SortByName();
- MaterialManager.Save();
- Material = new();
+ await this.MessageBoxError("Material name can't be empty");
+ return;
}
- public async void RemoveSelectedMaterials()
+ if (Manager.Contains(Material))
{
- if (_grid.SelectedItems.Count <= 0) return;
- if (await this.MessageBoxQuestion($"Are you sure you want to remove {_grid.SelectedItems.Count} materials?") != ButtonResult.Yes) return;
- Manager.RemoveRange(_grid.SelectedItems.Cast<Material>());
- MaterialManager.Save();
+ await this.MessageBoxError("A material with same name already exists.");
+ return;
}
- public async void ClearMaterials()
- {
- if (Manager.Count == 0) return;
- if (await this.MessageBoxQuestion($"Are you sure you want to clear {Manager.Count} materials?") != ButtonResult.Yes) return;
- Manager.Clear(true);
- }
+ Material.BottleRemainingVolume = Material.BottleVolume;
+
+ if (await this.MessageBoxQuestion("Are you sure you want to add the following material:\n" +
+ $"{Material}") != ButtonResult.Yes) return;
+
+ Manager.Add(Material);
+ Manager.SortByName();
+ MaterialManager.Save();
+ Material = new();
+ }
+
+ public async void RemoveSelectedMaterials()
+ {
+ if (_grid.SelectedItems.Count <= 0) return;
+ if (await this.MessageBoxQuestion($"Are you sure you want to remove {_grid.SelectedItems.Count} materials?") != ButtonResult.Yes) return;
+ Manager.RemoveRange(_grid.SelectedItems.Cast<Material>());
+ MaterialManager.Save();
+ }
+
+ public async void ClearMaterials()
+ {
+ if (Manager.Count == 0) return;
+ if (await this.MessageBoxQuestion($"Are you sure you want to clear {Manager.Count} materials?") != ButtonResult.Yes) return;
+ Manager.Clear(true);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/MissingInformationWindow.axaml b/UVtools.WPF/Windows/MissingInformationWindow.axaml
index 3c134da..7c36adc 100644
--- a/UVtools.WPF/Windows/MissingInformationWindow.axaml
+++ b/UVtools.WPF/Windows/MissingInformationWindow.axaml
@@ -21,7 +21,7 @@
</Border>
- <Border Margin="20,0">
+ <Border Margin="20,10,20,0">
<Grid RowDefinitions="Auto,10,Auto,10,Auto"
ColumnDefinitions="Auto,10,220,10,Auto">
@@ -84,23 +84,17 @@
<Border Classes="FooterActions">
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
- <Button Padding="10"
+ <uc:ButtonWithIcon Padding="10"
IsDefault="True"
- Command="{Binding Apply}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- <TextBlock Text="Apply"/>
- </StackPanel>
- </Button>
+ Icon="fas fa-check"
+ Text="Apply"
+ Command="{Binding Apply}"/>
- <Button Padding="10"
+ <uc:ButtonWithIcon Padding="10"
IsCancel="True"
- Command="{Binding Close}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Text="Cancel"/>
- </StackPanel>
- </Button>
+ Icon="fas fa-sign-out-alt"
+ Text="Cancel"
+ Command="{Binding Close}"/>
</StackPanel>
</Border>
</StackPanel>
diff --git a/UVtools.WPF/Windows/MissingInformationWindow.axaml.cs b/UVtools.WPF/Windows/MissingInformationWindow.axaml.cs
index 13dfcd7..608cfb0 100644
--- a/UVtools.WPF/Windows/MissingInformationWindow.axaml.cs
+++ b/UVtools.WPF/Windows/MissingInformationWindow.axaml.cs
@@ -11,83 +11,82 @@ using MessageBox.Avalonia.Enums;
using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public partial class MissingInformationWindow : WindowEx
{
- public partial class MissingInformationWindow : WindowEx
+ #region Members
+ private decimal _layerHeight;
+ private decimal _displayWidth;
+ private decimal _displayHeight;
+ #endregion
+
+ #region Properties
+ public decimal LayerHeight
{
- #region Members
- private decimal _layerHeight;
- private decimal _displayWidth;
- private decimal _displayHeight;
- #endregion
+ get => _layerHeight;
+ set => RaiseAndSetIfChanged(ref _layerHeight, value);
+ }
- #region Properties
- public decimal LayerHeight
- {
- get => _layerHeight;
- set => RaiseAndSetIfChanged(ref _layerHeight, value);
- }
+ public bool LayerHeightIsVisible => SlicerFile?.LayerHeight <= 0;
- public bool LayerHeightIsVisible => SlicerFile?.LayerHeight <= 0;
+ public decimal DisplayWidth
+ {
+ get => _displayWidth;
+ set => RaiseAndSetIfChanged(ref _displayWidth, value);
+ }
- public decimal DisplayWidth
- {
- get => _displayWidth;
- set => RaiseAndSetIfChanged(ref _displayWidth, value);
- }
+ public bool DisplayWidthIsVisible => SlicerFile?.DisplayWidth <= 0;
+
+ public decimal DisplayHeight
+ {
+ get => _displayHeight;
+ set => RaiseAndSetIfChanged(ref _displayHeight, value);
+ }
- public bool DisplayWidthIsVisible => SlicerFile?.DisplayWidth <= 0;
+ public bool DisplayHeightIsVisible => SlicerFile?.DisplayHeight <= 0;
+ #endregion
- public decimal DisplayHeight
+ public MissingInformationWindow()
+ {
+ InitializeComponent();
+#if DEBUG
+ this.AttachDevTools();
+#endif
+ if (SlicerFile is not null)
{
- get => _displayHeight;
- set => RaiseAndSetIfChanged(ref _displayHeight, value);
+ _layerHeight = (decimal) SlicerFile.LayerHeight;
+ _displayWidth = (decimal) SlicerFile.DisplayWidth;
+ _displayHeight = (decimal) SlicerFile.DisplayHeight;
}
- public bool DisplayHeightIsVisible => SlicerFile?.DisplayHeight <= 0;
- #endregion
+ DataContext = this;
+ }
- public MissingInformationWindow()
- {
- InitializeComponent();
-#if DEBUG
- this.AttachDevTools();
-#endif
- if (SlicerFile is not null)
- {
- _layerHeight = (decimal) SlicerFile.LayerHeight;
- _displayWidth = (decimal) SlicerFile.DisplayWidth;
- _displayHeight = (decimal) SlicerFile.DisplayHeight;
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- DataContext = this;
- }
+ public async void Apply()
+ {
+ if (await this.MessageBoxQuestion("Are you sure you want to submit and apply the information?", "Submit and apply the information?") != ButtonResult.Yes) return;
- private void InitializeComponent()
+ if ((decimal)SlicerFile.DisplayWidth != _displayWidth && _displayWidth > 0)
{
- AvaloniaXamlLoader.Load(this);
+ SlicerFile.DisplayWidth = (float) _displayWidth;
}
-
- public async void Apply()
+ if ((decimal)SlicerFile.DisplayHeight != _displayHeight && _displayHeight > 0)
{
- if (await this.MessageBoxQuestion("Are you sure you want to submit and apply the information?", "Submit and apply the information?") != ButtonResult.Yes) return;
-
- if ((decimal)SlicerFile.DisplayWidth != _displayWidth && _displayWidth > 0)
- {
- SlicerFile.DisplayWidth = (float) _displayWidth;
- }
- if ((decimal)SlicerFile.DisplayHeight != _displayHeight && _displayHeight > 0)
- {
- SlicerFile.DisplayHeight = (float)_displayHeight;
- }
- if ((decimal)SlicerFile.LayerHeight != _layerHeight && _layerHeight > 0)
- {
- SlicerFile.LayerHeight = (float)_layerHeight;
- SlicerFile.LayerManager.RebuildLayersProperties();
- }
-
- DialogResult = DialogResults.OK;
- Close();
+ SlicerFile.DisplayHeight = (float)_displayHeight;
}
+ if ((decimal)SlicerFile.LayerHeight != _layerHeight && _layerHeight > 0)
+ {
+ SlicerFile.LayerHeight = (float)_layerHeight;
+ SlicerFile.RebuildLayersProperties();
+ }
+
+ DialogResult = DialogResults.OK;
+ Close();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/ProgressWindow.axaml.cs b/UVtools.WPF/Windows/ProgressWindow.axaml.cs
index cbfa574..1525697 100644
--- a/UVtools.WPF/Windows/ProgressWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ProgressWindow.axaml.cs
@@ -16,104 +16,103 @@ using UVtools.Core.Operations;
using UVtools.WPF.Controls;
using UVtools.WPF.Structures;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class ProgressWindow : WindowEx, IDisposable
{
- public class ProgressWindow : WindowEx, IDisposable
- {
- public Stopwatch StopWatch => Progress.StopWatch;
- public OperationProgress Progress { get; } = new ();
+ public Stopwatch StopWatch => Progress.StopWatch;
+ public OperationProgress Progress { get; } = new ();
- private LogItem _logItem = new ();
+ private LogItem _logItem = new ();
- private Timer _timer = new (200) { AutoReset = true };
+ private Timer _timer = new (200) { AutoReset = true };
- private long _lastTotalSeconds = 0;
+ private long _lastTotalSeconds = 0;
- public bool CanCancel
+ public bool CanCancel
+ {
+ get => Progress?.CanCancel ?? false;
+ set
{
- get => Progress?.CanCancel ?? false;
- set
- {
- Progress.CanCancel = value;
- RaisePropertyChanged();
- }
+ Progress.CanCancel = value;
+ RaisePropertyChanged();
}
+ }
- public ProgressWindow()
- {
- InitializeComponent();
+ public ProgressWindow()
+ {
+ InitializeComponent();
- Cursor = new Cursor(StandardCursorType.AppStarting);
+ Cursor = new Cursor(StandardCursorType.AppStarting);
- _timer.Elapsed += (sender, args) =>
- {
- var elapsedSeconds = StopWatch.ElapsedMilliseconds / 1000;
- if (_lastTotalSeconds == elapsedSeconds) return;
- /*Debug.WriteLine(StopWatch.ElapsedMilliseconds);
- Debug.WriteLine(elapsedSeconds);
- Debug.WriteLine(_lastTotalSeconds);*/
- _lastTotalSeconds = elapsedSeconds;
+ _timer.Elapsed += (sender, args) =>
+ {
+ var elapsedSeconds = StopWatch.ElapsedMilliseconds / 1000;
+ if (_lastTotalSeconds == elapsedSeconds) return;
+ /*Debug.WriteLine(StopWatch.ElapsedMilliseconds);
+ Debug.WriteLine(elapsedSeconds);
+ Debug.WriteLine(_lastTotalSeconds);*/
+ _lastTotalSeconds = elapsedSeconds;
- Dispatcher.UIThread.InvokeAsync(() => Progress.TriggerRefresh(), DispatcherPriority.Render);
+ Dispatcher.UIThread.InvokeAsync(() => Progress.TriggerRefresh(), DispatcherPriority.Render);
- };
+ };
- DataContext = this;
- }
-
- public ProgressWindow(string title) : this()
- {
- SetTitle(title);
- }
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ DataContext = this;
+ }
- protected override void OnClosed(EventArgs e)
- {
- base.OnClosed(e);
- _timer.Stop();
- Progress.StopWatch.Stop();
- _logItem.ElapsedTime = Math.Round(Progress.StopWatch.Elapsed.TotalSeconds, 2);
- App.MainWindow.AddLog(_logItem);
- }
+ public ProgressWindow(string title) : this()
+ {
+ SetTitle(title);
+ }
- public void OnClickCancel()
- {
- if (!CanCancel) return;
- DialogResult = DialogResults.Cancel;
- RaisePropertyChanged(nameof(CanCancel));
- Progress.TokenSource.Cancel();
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void SetTitle(string title)
- {
- Progress.Title = title;
- _logItem.Description = title;
- }
+ protected override void OnClosed(EventArgs e)
+ {
+ base.OnClosed(e);
+ _timer.Stop();
+ Progress.StopWatch.Stop();
+ _logItem.ElapsedTime = Math.Round(Progress.StopWatch.Elapsed.TotalSeconds, 2);
+ App.MainWindow.AddLog(_logItem);
+ }
- public OperationProgress RestartProgress(bool canCancel = true)
- {
- CanCancel = canCancel;
- Progress.StopWatch.Restart();
- _lastTotalSeconds = 0;
+ public void OnClickCancel()
+ {
+ if (!CanCancel) return;
+ DialogResult = DialogResults.Cancel;
+ RaisePropertyChanged(nameof(CanCancel));
+ Progress.TokenSource.Cancel();
+ }
- if (!_timer.Enabled)
- {
- _timer.Enabled = true;
- _timer.Start();
- }
+ public void SetTitle(string title)
+ {
+ Progress.Title = title;
+ _logItem.Description = title;
+ }
- return Progress;
- }
+ public OperationProgress RestartProgress(bool canCancel = true)
+ {
+ CanCancel = canCancel;
+ Progress.StopWatch.Restart();
+ _lastTotalSeconds = 0;
- public void Dispose()
+ if (!_timer.Enabled)
{
- _timer.Close();
- _timer.Dispose();
+ _timer.Enabled = true;
+ _timer.Start();
}
+
+ return Progress;
+ }
+
+ public void Dispose()
+ {
+ _timer.Close();
+ _timer.Dispose();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml b/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml
index 0663d04..ecfd49a 100644
--- a/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml
+++ b/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml
@@ -90,17 +90,13 @@
Classes="FooterActions">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto">
- <Button
+ <controls:ButtonWithIcon
Grid.Row="0"
Grid.Column="0"
Padding="10"
- Command="{Binding RefreshProfiles}"
- >
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/refresh-16x16.png"/>
- <TextBlock Text="Refresh profiles"/>
- </StackPanel>
- </Button>
+ Icon="fas fa-sync-alt"
+ Text="Refresh profiles"
+ Command="{Binding RefreshProfiles}"/>
<StackPanel
Margin="0,0,5,5"
@@ -109,23 +105,17 @@
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="5">
- <Button Padding="10"
+ <controls:ButtonWithIcon Padding="10"
IsDefault="True"
- Command="{Binding InstallProfiles}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- <TextBlock Text="Install selected profiles"/>
- </StackPanel>
- </Button>
-
- <Button Padding="10"
+ Icon="fas fa-check"
+ Text="Install selected profiles"
+ Command="{Binding InstallProfiles}"/>
+
+ <controls:ButtonWithIcon Padding="10"
IsCancel="True"
- Command="{Binding Close}">
- <StackPanel Orientation="Horizontal" Spacing="10">
- <Image Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Text="Close"/>
- </StackPanel>
- </Button>
+ Icon="fas fa-sign-out-alt"
+ Text="Close"
+ Command="{Binding Close}"/>
</StackPanel>
</Grid>
@@ -133,8 +123,7 @@
</Border>
<TabControl SelectedIndex="{Binding TabSlicerSelectedIndex}">
- <TabItem
- IsVisible="{Binding HavePrusaSlicer}">
+ <TabItem IsVisible="{Binding HavePrusaSlicer}">
<TabItem.Header>
<StackPanel Orientation="Horizontal" Spacing="5">
<Image Stretch="None" Source="/Assets/Icons/PrusaSlicer-32.png" />
@@ -168,23 +157,17 @@
<StackPanel
Grid.Row="1"
Orientation="Horizontal" Spacing="1">
- <Button Padding="10"
- IsEnabled="{Binding PrusaSlicerProfiles[0].Updates}"
- Command="{Binding PrusaSlicerProfiles[0].SelectNone}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-unmarked-16x16.png"/>
- <TextBlock Text="Unselect all"/>
- </StackPanel>
- </Button>
-
- <Button Padding="10"
+ <controls:ButtonWithIcon Padding="10"
IsEnabled="{Binding PrusaSlicerProfiles[0].Updates}"
- Command="{Binding PrusaSlicerProfiles[0].SelectAll}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-marked-16x16.png"/>
- <TextBlock Text="Select all"/>
- </StackPanel>
- </Button>
+ Icon="far fa-square"
+ Text="Unselect all"
+ Command="{Binding PrusaSlicerProfiles[0].SelectNone}"/>
+
+ <controls:ButtonWithIcon Padding="10"
+ IsEnabled="{Binding PrusaSlicerProfiles[0].Updates}"
+ Icon="far fa-check-square"
+ Text="Select all"
+ Command="{Binding PrusaSlicerProfiles[0].SelectAll}"/>
</StackPanel>
@@ -217,23 +200,17 @@
<StackPanel
Grid.Row="1"
Orientation="Horizontal" Spacing="1">
- <Button Padding="10"
- IsEnabled="{Binding PrusaSlicerProfiles[1].Updates}"
- Command="{Binding PrusaSlicerProfiles[1].SelectNone}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-unmarked-16x16.png"/>
- <TextBlock Text="Unselect all"/>
- </StackPanel>
- </Button>
-
- <Button Padding="10"
- IsEnabled="{Binding PrusaSlicerProfiles[1].Updates}"
- Command="{Binding PrusaSlicerProfiles[1].SelectAll}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-marked-16x16.png"/>
- <TextBlock Text="Select all"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Padding="10"
+ IsEnabled="{Binding PrusaSlicerProfiles[1].Updates}"
+ Icon="far fa-square"
+ Text="Unselect all"
+ Command="{Binding PrusaSlicerProfiles[1].SelectNone}"/>
+
+ <controls:ButtonWithIcon Padding="10"
+ IsEnabled="{Binding PrusaSlicerProfiles[1].Updates}"
+ Icon="far fa-check-square"
+ Text="Select all"
+ Command="{Binding PrusaSlicerProfiles[1].SelectAll}"/>
</StackPanel>
@@ -282,23 +259,17 @@
<StackPanel
Grid.Row="1"
Orientation="Horizontal" Spacing="1">
- <Button Padding="10"
- IsEnabled="{Binding SuperSlicerProfiles[0].Updates}"
- Command="{Binding SuperSlicerProfiles[0].SelectNone}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-unmarked-16x16.png"/>
- <TextBlock Text="Unselect all"/>
- </StackPanel>
- </Button>
-
- <Button Padding="10"
- IsEnabled="{Binding SuperSlicerProfiles[0].Updates}"
- Command="{Binding SuperSlicerProfiles[0].SelectAll}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-marked-16x16.png"/>
- <TextBlock Text="Select all"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Padding="10"
+ IsEnabled="{Binding SuperSlicerProfiles[0].Updates}"
+ Icon="far fa-square"
+ Text="Unselect all"
+ Command="{Binding SuperSlicerProfiles[0].SelectNone}"/>
+
+ <controls:ButtonWithIcon Padding="10"
+ IsEnabled="{Binding SuperSlicerProfiles[0].Updates}"
+ Icon="far fa-check-square"
+ Text="Select all"
+ Command="{Binding SuperSlicerProfiles[0].SelectAll}"/>
</StackPanel>
@@ -331,23 +302,17 @@
<StackPanel
Grid.Row="1"
Orientation="Horizontal" Spacing="1">
- <Button Padding="10"
- IsEnabled="{Binding SuperSlicerProfiles[1].Updates}"
- Command="{Binding SuperSlicerProfiles[1].SelectNone}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-unmarked-16x16.png"/>
- <TextBlock Text="Unselect all"/>
- </StackPanel>
- </Button>
-
- <Button Padding="10"
- IsEnabled="{Binding SuperSlicerProfiles[1].Updates}"
- Command="{Binding SuperSlicerProfiles[1].SelectAll}">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/checkbox-marked-16x16.png"/>
- <TextBlock Text="Select all"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Padding="10"
+ IsEnabled="{Binding SuperSlicerProfiles[1].Updates}"
+ Icon="far fa-square"
+ Text="Unselect all"
+ Command="{Binding SuperSlicerProfiles[1].SelectNone}"/>
+
+ <controls:ButtonWithIcon Padding="10"
+ IsEnabled="{Binding SuperSlicerProfiles[1].Updates}"
+ Icon="far fa-check-square"
+ Text="Select all"
+ Command="{Binding SuperSlicerProfiles[1].SelectAll}"/>
</StackPanel>
diff --git a/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs b/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs
index 29e09ac..28ea8d7 100644
--- a/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs
+++ b/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs
@@ -7,77 +7,77 @@ using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class PrusaSlicerManagerWindow : WindowEx
{
- public class PrusaSlicerManagerWindow : WindowEx
+ public PSProfileFolder[] PrusaSlicerProfiles { get; } = {
+ new (PSProfileFolder.FolderType.Print),
+ new (PSProfileFolder.FolderType.Printer),
+ };
+ public PSProfileFolder[] SuperSlicerProfiles { get; } = {
+ new (PSProfileFolder.FolderType.Print, true),
+ new (PSProfileFolder.FolderType.Printer, true),
+ };
+
+ public bool HavePrusaSlicer
{
- public PSProfileFolder[] PrusaSlicerProfiles { get; } = {
- new (PSProfileFolder.FolderType.Print),
- new (PSProfileFolder.FolderType.Printer),
- };
- public PSProfileFolder[] SuperSlicerProfiles { get; } = {
- new (PSProfileFolder.FolderType.Print, true),
- new (PSProfileFolder.FolderType.Printer, true),
- };
-
- public bool HavePrusaSlicer
+ get
{
- get
- {
- var PSFolder = App.GetPrusaSlicerDirectory();
- return !string.IsNullOrEmpty(PSFolder) && Directory.Exists(PSFolder);
- }
+ var PSFolder = App.GetPrusaSlicerDirectory();
+ return !string.IsNullOrEmpty(PSFolder) && Directory.Exists(PSFolder);
}
+ }
- public bool HaveSuperSlicer
+ public bool HaveSuperSlicer
+ {
+ get
{
- get
- {
- var SSFolder = App.GetPrusaSlicerDirectory(true);
- return !string.IsNullOrEmpty(SSFolder) && Directory.Exists(SSFolder);
- }
+ var SSFolder = App.GetPrusaSlicerDirectory(true);
+ return !string.IsNullOrEmpty(SSFolder) && Directory.Exists(SSFolder);
}
+ }
- public int TabSlicerSelectedIndex { get; set; }
+ public int TabSlicerSelectedIndex { get; set; }
- public PrusaSlicerManagerWindow()
- {
- InitializeComponent();
- DataContext = this;
- }
+ public PrusaSlicerManagerWindow()
+ {
+ InitializeComponent();
+ DataContext = this;
+ }
- private void InitializeComponent()
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public void RefreshProfiles()
+ {
+ foreach (var profile in PrusaSlicerProfiles)
{
- AvaloniaXamlLoader.Load(this);
+ profile.Reset();
}
- public void RefreshProfiles()
+ foreach (var profile in SuperSlicerProfiles)
{
- foreach (var profile in PrusaSlicerProfiles)
- {
- profile.Reset();
- }
-
- foreach (var profile in SuperSlicerProfiles)
- {
- profile.Reset();
- }
+ profile.Reset();
}
+ }
- public async void InstallProfiles()
- {
- bool isSuperSlicer = TabSlicerSelectedIndex == 1;
- var printProfiles = isSuperSlicer ? SuperSlicerProfiles[0].SelectedFiles : PrusaSlicerProfiles[0].SelectedFiles;
- var printerProfiles = isSuperSlicer ? SuperSlicerProfiles[1].SelectedFiles : PrusaSlicerProfiles[1].SelectedFiles;
- var slicerName = isSuperSlicer ? "SuperSlicer" : "PrusaSlicer";
+ public async void InstallProfiles()
+ {
+ bool isSuperSlicer = TabSlicerSelectedIndex == 1;
+ var printProfiles = isSuperSlicer ? SuperSlicerProfiles[0].SelectedFiles : PrusaSlicerProfiles[0].SelectedFiles;
+ var printerProfiles = isSuperSlicer ? SuperSlicerProfiles[1].SelectedFiles : PrusaSlicerProfiles[1].SelectedFiles;
+ var slicerName = isSuperSlicer ? "SuperSlicer" : "PrusaSlicer";
- if (string.IsNullOrEmpty(printProfiles) && string.IsNullOrEmpty(printerProfiles))
- {
- await this.MessageBoxError("Select at least one profile to install", "None profile was selected");
- return;
- }
+ if (string.IsNullOrEmpty(printProfiles) && string.IsNullOrEmpty(printerProfiles))
+ {
+ await this.MessageBoxError("Select at least one profile to install", "None profile was selected");
+ return;
+ }
- if (await this.MessageBoxQuestion(
+ if (await this.MessageBoxQuestion(
$"This action will install and override the following profiles into {slicerName}:\n" +
"---------- PRINT PROFILES ----------\n" +
printProfiles +
@@ -88,35 +88,34 @@ namespace UVtools.WPF.Windows
"Click 'No' to cancel this operation",
$"Install printers into {slicerName}") != ButtonResult.Yes) return;
- ushort count = 0;
+ ushort count = 0;
- try
+ try
+ {
+ foreach (var profile in isSuperSlicer ? SuperSlicerProfiles : PrusaSlicerProfiles)
{
- foreach (var profile in isSuperSlicer ? SuperSlicerProfiles : PrusaSlicerProfiles)
+ foreach (var item in profile.Items)
{
- foreach (var item in profile.Items)
- {
- if (!item.IsChecked.HasValue || !item.IsChecked.Value) continue;
- var fi = item.Tag as FileInfo;
- if (fi is null) continue;
- fi.CopyTo(Path.Combine(profile.TargetPath, fi.Name), true);
- count++;
- }
+ if (!item.IsChecked.HasValue || !item.IsChecked.Value) continue;
+ var fi = item.Tag as FileInfo;
+ if (fi is null) continue;
+ fi.CopyTo(Path.Combine(profile.TargetPath, fi.Name), true);
+ count++;
}
}
- catch (Exception exception)
- {
- await this.MessageBoxError(exception.Message, "Unable to install the profiles");
- return;
- }
+ }
+ catch (Exception exception)
+ {
+ await this.MessageBoxError(exception.Message, "Unable to install the profiles");
+ return;
+ }
- await this.MessageBoxInfo(
- $"{count} profiles were installed.\nRestart {slicerName} and check if profiles are present.",
- "Operation completed");
+ await this.MessageBoxInfo(
+ $"{count} profiles were installed.\nRestart {slicerName} and check if profiles are present.",
+ "Operation completed");
- RefreshProfiles();
+ RefreshProfiles();
- }
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml
index 93de17a..a83605f 100644
--- a/UVtools.WPF/Windows/SettingsWindow.axaml
+++ b/UVtools.WPF/Windows/SettingsWindow.axaml
@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:UVtools.WPF.Controls"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="700" d:DesignHeight="450"
x:Class="UVtools.WPF.Windows.SettingsWindow"
WindowStartupLocation="Manual"
@@ -22,12 +23,21 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Startup"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Startup"/>
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
- <CheckBox IsChecked="{Binding Settings.General.StartMaximized}" Content="Start maximized"/>
- <CheckBox IsChecked="{Binding Settings.General.CheckForUpdatesOnStartup}" Content="Check for updates on startup"/>
- <CheckBox IsChecked="{Binding Settings.General.LoadDemoFileOnStartup}" Content="Loads a demo file on startup if no file was specified"/>
- <CheckBox IsChecked="{Binding Settings.General.LoadLastRecentFileOnStartup}" Content="Loads the last recent file on startup if no file was specified"/>
+ <StackPanel Orientation="Horizontal" Spacing="10">
+ <TextBlock VerticalAlignment="Center" Text="Theme:" />
+ <ComboBox
+ HorizontalAlignment="Stretch"
+ Width="150"
+ Items="{Binding Settings.General.Theme, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Settings.General.Theme, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+ </StackPanel>
+ <CheckBox IsChecked="{Binding Settings.General.StartMaximized}" Content="Start maximized"/>
+ <CheckBox IsChecked="{Binding Settings.General.CheckForUpdatesOnStartup}" Content="Check for updates on startup"/>
+ <CheckBox IsChecked="{Binding Settings.General.LoadDemoFileOnStartup}" Content="Loads a demo file on startup if no file was specified"/>
+ <CheckBox IsChecked="{Binding Settings.General.LoadLastRecentFileOnStartup}" Content="Loads the last recent file on startup if no file was specified"/>
</StackPanel>
</StackPanel>
@@ -38,7 +48,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Tasks"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Tasks"/>
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
<Grid RowDefinitions="Auto"
ColumnDefinitions="Auto,10,150,5,Auto,20,Auto,2,Auto,2,Auto,2,Auto,2,Auto,2,Auto,2,Auto">
@@ -125,7 +135,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Windows / dialogs"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Windows / dialogs"/>
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
<CheckBox IsChecked="{Binding Settings.General.WindowsCanResize}"
@@ -181,7 +191,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="File dialog"/>
+ <TextBlock Classes="GroupBoxHeader" Text="File dialog"/>
<Grid Margin="10"
RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto"
ColumnDefinitions="Auto,10,449,Auto,Auto">
@@ -206,17 +216,15 @@
<Button Grid.Row="2" Grid.Column="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-folder"
Command="{Binding GeneralOpenFolderField}"
- CommandParameter="DefaultDirectoryOpenFile">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectoryOpenFile"/>
<Button Grid.Row="2" Grid.Column="4"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-times"
Command="{Binding GeneralClearField}"
- CommandParameter="DefaultDirectoryOpenFile">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectoryOpenFile"/>
<TextBlock VerticalAlignment="Center"
Grid.Row="4" Grid.Column="0"
@@ -228,17 +236,16 @@
<Button Grid.Row="4" Grid.Column="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-folder"
Command="{Binding GeneralOpenFolderField}"
- CommandParameter="DefaultDirectorySaveFile">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectorySaveFile"/>
<Button Grid.Row="4" Grid.Column="4"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-times"
Command="{Binding GeneralClearField}"
- CommandParameter="DefaultDirectorySaveFile">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectorySaveFile"/>
+
<TextBlock VerticalAlignment="Center"
Grid.Row="6" Grid.Column="0"
@@ -250,17 +257,15 @@
<Button Grid.Row="6" Grid.Column="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-folder"
Command="{Binding GeneralOpenFolderField}"
- CommandParameter="DefaultDirectoryExtractFile">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectoryExtractFile"/>
<Button Grid.Row="6" Grid.Column="4"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-times"
Command="{Binding GeneralClearField}"
- CommandParameter="DefaultDirectoryExtractFile">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectoryExtractFile"/>
<TextBlock VerticalAlignment="Center"
Grid.Row="8" Grid.Column="0"
@@ -272,18 +277,15 @@
<Button Grid.Row="8" Grid.Column="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-folder"
Command="{Binding GeneralOpenFolderField}"
- CommandParameter="DefaultDirectoryConvertFile">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
- <Button
- Command="{Binding GeneralClearField}"
- CommandParameter="DefaultDirectoryConvertFile"
- VerticalAlignment="Stretch"
- HorizontalAlignment="Stretch"
- Grid.Row="8" Grid.Column="4">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectoryConvertFile"/>
+ <Button Grid.Row="8" Grid.Column="4"
+ Command="{Binding GeneralClearField}"
+ CommandParameter="DefaultDirectoryConvertFile"
+ VerticalAlignment="Stretch"
+ HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-times"/>
<TextBlock VerticalAlignment="Center"
Grid.Row="10" Grid.Column="0"
@@ -295,18 +297,15 @@
<Button Grid.Row="10" Grid.Column="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-folder"
Command="{Binding GeneralOpenFolderField}"
- CommandParameter="DefaultDirectoryScripts">
- <Image Source="/Assets/Icons/open-16x16.png"/>
- </Button>
- <Button
- Command="{Binding GeneralClearField}"
- CommandParameter="DefaultDirectoryScripts"
- VerticalAlignment="Stretch"
- HorizontalAlignment="Stretch"
- Grid.Row="10" Grid.Column="4">
- <Image Source="/Assets/Icons/delete-16x16.png"/>
- </Button>
+ CommandParameter="DefaultDirectoryScripts"/>
+ <Button Grid.Row="10" Grid.Column="4"
+ Command="{Binding GeneralClearField}"
+ CommandParameter="DefaultDirectoryScripts"
+ VerticalAlignment="Stretch"
+ HorizontalAlignment="Stretch"
+ i:Attached.Icon="fas fa-times"/>
</Grid>
<CheckBox IsChecked="{Binding Settings.General.PromptOverwriteFileSave}" Margin="10,0" Content="On file 'Save' prompt for file overwrite for the first time" />
@@ -324,12 +323,10 @@
</Border>
- <Border
- Classes="GroupBox"
- Margin="5">
+ <Border Classes="GroupBox" Margin="5">
<StackPanel Orientation="Vertical">
- <Grid RowDefinitions="Auto" ColumnDefinitions="*,Auto" Background="LightBlue">
+ <Grid RowDefinitions="Auto" ColumnDefinitions="*,Auto" Classes="GroupBoxHeader">
<TextBlock Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center"
Padding="10" FontWeight="Bold" Text="Send to - Custom location"/>
@@ -337,15 +334,13 @@
<StackPanel Grid.Row="0" Grid.Column="1"
HorizontalAlignment="Right" Orientation="Horizontal" Spacing="2">
<Button ToolTip.Tip="Add location" VerticalAlignment="Center"
- Command="{Binding SendToAddCustomLocation}">
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-plus"
+ Command="{Binding SendToAddCustomLocation}"/>
<Button ToolTip.Tip="Remove selected locations" VerticalAlignment="Center"
IsEnabled="{Binding #SendToCustomLocationsGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding SendToRemoveCustomLocations}">
- <Image Source="/Assets/Icons/trash-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-trash-alt"
+ Command="{Binding SendToRemoveCustomLocations}"/>
</StackPanel>
</Grid>
@@ -397,7 +392,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <Grid RowDefinitions="Auto" ColumnDefinitions="*,Auto" Background="LightBlue">
+ <Grid RowDefinitions="Auto" ColumnDefinitions="*,Auto" Classes="GroupBoxHeader">
<TextBlock Grid.Row="0" Grid.Column="0"
ToolTip.Tip="Allow to open a new process with arguments and pass the current file.&#x0a;
&#x0a;Wait exit: Interrupt the UI and wait for process completion/exit.
@@ -408,15 +403,13 @@
<StackPanel Grid.Row="0" Grid.Column="1"
HorizontalAlignment="Right" Orientation="Horizontal" Spacing="2">
<Button ToolTip.Tip="Add location" VerticalAlignment="Center"
- Command="{Binding SendToAddProcess}">
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-plus"
+ Command="{Binding SendToAddProcess}"/>
<Button ToolTip.Tip="Remove selected process(es)" VerticalAlignment="Center"
IsEnabled="{Binding #SendToProcessGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding SendToRemoveProcess}">
- <Image Source="/Assets/Icons/trash-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-trash-alt"
+ Command="{Binding SendToRemoveProcess}"/>
</StackPanel>
</Grid>
@@ -479,7 +472,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Layer colors"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Layer colors"/>
<Grid Margin="10" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,Auto,Auto,*">
@@ -909,7 +902,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Zoom"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Zoom"/>
<Grid Margin="10" RowDefinitions="Auto" ColumnDefinitions="Auto,250,*,100">
<TextBlock Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center"
@@ -958,7 +951,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Crosshairs"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Crosshairs"/>
<Grid Margin="10" RowDefinitions="Auto,10,Auto" ColumnDefinitions="Auto,150,*,Auto">
<CheckBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
VerticalAlignment="Center"
@@ -1010,7 +1003,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Miscellaneous"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Miscellaneous"/>
<StackPanel Orientation="Horizontal">
<CheckBox
@@ -1073,7 +1066,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Startup"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Startup"/>
<StackPanel Orientation="Horizontal" Spacing="20">
@@ -1135,7 +1128,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Issues list"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Issues list"/>
<Grid
RowDefinitions="Auto"
@@ -1168,7 +1161,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold"
+ <TextBlock Classes="GroupBoxHeader"
ToolTip.Tip="Unsupported pixels"
Text="Islands"/>
@@ -1302,7 +1295,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold"
+ <TextBlock Classes="GroupBoxHeader"
ToolTip.Tip="Unsupported pixels that goes beyond a certain angle/threshold"
Text="Overhangs"/>
@@ -1336,7 +1329,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold"
+ <TextBlock Classes="GroupBoxHeader"
ToolTip.Tip="Trapped resin inside hollow objects"
Text="Resin traps"/>
@@ -1403,7 +1396,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold"
+ <TextBlock Classes="GroupBoxHeader"
ToolTip.Tip="Hollow areas that aren't resin traps but lacks a venting hole at that level to prevent suction and pressure"
Text="Suction cups"/>
@@ -1440,7 +1433,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold"
+ <TextBlock Classes="GroupBoxHeader"
ToolTip.Tip="White pixels that nearly or touch the build plate edges"
Text="Touching boundary"/>
@@ -1576,7 +1569,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold"
+ <TextBlock Classes="GroupBoxHeader"
ToolTip.Tip="Layers that surpass printer maximum height"
Text="Print height"/>
@@ -1611,7 +1604,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Colors &amp; highlights"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Colors &amp; highlights"/>
<Grid Margin="10" RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto" ColumnDefinitions="Auto,Auto,Auto">
<TextBlock Grid.Row="0" Grid.Column="0"
@@ -1721,7 +1714,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Actions"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Actions"/>
<CheckBox
IsChecked="{Binding Settings.PixelEditor.PartialUpdateIslandsOnEditing}"
@@ -1746,7 +1739,7 @@
Margin="5">
<StackPanel Orientation="Vertical" Spacing="10">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Default values"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Default values"/>
<CheckBox IsChecked="{Binding Settings.LayerRepair.RepairIslands}"
Margin="10,0" Content="Attempt to repair islands by default"/>
@@ -1842,7 +1835,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Common"/>
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
<CheckBox IsChecked="{Binding Settings.Tools.RestoreLastUsedSettings}"
Content="Keep and restore the last used settings on operations per user session/instance"/>
@@ -1864,104 +1857,44 @@
</ScrollViewer>
</TabItem>
- <TabItem Header="Automations" VerticalContentAlignment="Center">
- <ScrollViewer Name="ScrollViewer6">
- <StackPanel Orientation="Vertical" Spacing="5">
- <Border
- Classes="GroupBox"
- Margin="5">
-
- <StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/>
- <StackPanel Margin="10" Orientation="Vertical" Spacing="10">
- <CheckBox IsChecked="{Binding Settings.Automations.SaveFileAfterModifications}" Content="Auto save the file after apply any automation(s)"/>
- </StackPanel>
-
- </StackPanel>
- </Border>
-
- <Border
- Margin="5"
- BorderBrush="LightBlue"
- BorderThickness="4"
- >
-
- <StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="File convertion"/>
- <StackPanel Margin="10" Orientation="Vertical" Spacing="10">
- <CheckBox IsChecked="{Binding Settings.Automations.AutoConvertFiles}"
- ToolTip.Tip="1) Converts the SL1 files to the format specified on 'PrusaSlicer - Printer - Notes' on 'FILEFORMAT_XXX' variable.
+ <TabItem Header="Automations" VerticalContentAlignment="Center">
+ <ScrollViewer Name="ScrollViewer6">
+ <StackPanel Orientation="Vertical" Spacing="5">
+ <Border
+ Classes="GroupBox"
+ Margin="5">
+
+ <StackPanel Orientation="Vertical">
+ <TextBlock Classes="GroupBoxHeader" Text="Common"/>
+ <StackPanel Margin="10" Orientation="Vertical" Spacing="10">
+ <CheckBox IsChecked="{Binding Settings.Automations.SaveFileAfterModifications}" Content="Auto save the file after apply any automation(s)"/>
+ </StackPanel>
+
+ </StackPanel>
+ </Border>
+
+ <Border
+ Margin="5"
+ Classes="GroupBox">
+
+ <StackPanel Orientation="Vertical">
+ <TextBlock Classes="GroupBoxHeader" Text="File convertion"/>
+ <StackPanel Margin="10" Orientation="Vertical" Spacing="10">
+ <CheckBox IsChecked="{Binding Settings.Automations.AutoConvertFiles}"
+ ToolTip.Tip="1) Converts the SL1 files to the format specified on 'PrusaSlicer - Printer - Notes' on 'FILEFORMAT_XXX' variable.
&#x0a;2) Converts the VDT files to the format specified on 'Voxeldance Tango - Printer settings - Notes' on 'FILEFORMAT_XXX' variable.
&#x0a;A new file with same name but a new extension will be created and overwrite any previous file.
&#x0a;After a successful conversion the new file will automatically load in instead of the loaded SL1 file."
- Content="Auto convert SL1 and VDT files to the target format when possible and load it back"/>
- </StackPanel>
-
- </StackPanel>
- </Border>
-
-
- <Border
- Classes="GroupBox"
- Margin="5">
-
- <StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Light-off delay"/>
-
- <ComboBox
- Margin="10"
- HorizontalAlignment="Stretch"
- Items="{Binding Settings.Automations.LightOffDelaySetMode, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
- SelectedItem="{Binding Settings.Automations.LightOffDelaySetMode, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
-
- <CheckBox Margin="10,0,10,10"
- Content="Change only light-off delay if value is zero"
- ToolTip.Tip="If enabled, it will only change light-off delay to the defined values if the original value is zero.
-&#x0a;If disabled, it will always change the light-off delay to the defined values."
- IsVisible="{Binding Settings.Automations.ChangeOnlyLightOffDelayIfZeroVisible}"
- IsChecked="{Binding Settings.Automations.ChangeOnlyLightOffDelayIfZero}"/>
-
- <Grid Margin="10,0,10,10"
- IsVisible="{Binding Settings.Automations.LightOffDelayExtraTimeVisible}"
- RowDefinitions="Auto"
- ColumnDefinitions="Auto,10,Auto,30,Auto,10,Auto">
-
- <TextBlock Grid.Row="0" Grid.Column="0"
- VerticalAlignment="Center"
- ToolTip.Tip="Auto set the extra 'light-off delay' based on lift height and speed."
- Text="Light-off delay:" />
+ Content="Auto convert SL1 and VDT files to the target format when possible and load it back"/>
+ </StackPanel>
- <NumericUpDown Grid.Row="0" Grid.Column="2"
- Classes="ValueLabel ValueLabel_s"
- Minimum="0"
- Maximum="255"
- Increment="0.5"
- FormatString="F2"
- Value="{Binding Settings.Automations.LightOffDelay}"/>
-
- <TextBlock Grid.Row="0" Grid.Column="4"
- VerticalAlignment="Center"
- ToolTip.Tip="Auto set the extra 'bottom light-off delay' based on bottom lift height and speed."
- Text="Bottom light-off delay:" />
- <NumericUpDown Grid.Row="0" Grid.Column="6"
- Classes="ValueLabel ValueLabel_s"
- Minimum="0"
- Maximum="255"
- Increment="0.5"
- FormatString="F2"
- Value="{Binding Settings.Automations.BottomLightOffDelay}"/>
-
- </Grid>
-
- </StackPanel>
- </Border>
+ </StackPanel>
+ </Border>
-
-
- </StackPanel>
- </ScrollViewer>
- </TabItem>
+ </StackPanel>
+ </ScrollViewer>
+ </TabItem>
<TabItem Header="Network" VerticalContentAlignment="Center">
@@ -1972,7 +1905,7 @@
Margin="5">
<StackPanel Orientation="Vertical">
- <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Remote printers"/>
+ <TextBlock Classes="GroupBoxHeader" Text="Remote printers"/>
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
<Grid RowDefinitions="Auto" ColumnDefinitions="*,2,Auto">
@@ -1984,21 +1917,18 @@
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="2">
<Button
VerticalAlignment="Stretch"
- Command="{Binding AddNetworkRemotePrinter}">
- <Image Source="/Assets/Icons/plus-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-plus"
+ Command="{Binding AddNetworkRemotePrinter}"/>
<Button
VerticalAlignment="Stretch"
IsEnabled="{Binding #NetworkRemotePrinterComboBox.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding DuplicateSelectedNetworkRemotePrinter}">
- <Image Source="/Assets/Icons/copy-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-copy"
+ Command="{Binding DuplicateSelectedNetworkRemotePrinter}"/>
<Button
VerticalAlignment="Stretch"
IsEnabled="{Binding #NetworkRemotePrinterComboBox.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}"
- Command="{Binding RemoveSelectedNetworkRemotePrinter}">
- <Image Source="/Assets/Icons/trash-16x16.png"/>
- </Button>
+ i:Attached.Icon="fas fa-trash-alt"
+ Command="{Binding RemoveSelectedNetworkRemotePrinter}"/>
</StackPanel>
</Grid>
@@ -2113,34 +2043,25 @@
ColumnDefinitions="*,*">
<StackPanel Grid.Column="0" Orientation="Horizontal">
- <Button Name="Actions.ResetAllSettings" Padding="10"
- Command="{Binding OnClickResetAllDefaults}">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/undo-alt-16x16.png"/>
- <TextBlock Margin="10,0,0,0">Reset all settings</TextBlock>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Name="Actions.ResetAllSettings" Padding="10"
+ Icon="fas fa-undo-alt"
+ Text="Reset all settings"
+ Command="{Binding OnClickResetAllDefaults}"/>
</StackPanel>
<StackPanel Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal">
- <Button Name="Actions.Save" Margin="0,0,10,0" Padding="10" IsDefault="True"
- Command="{Binding OnClickSave}">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/save-16x16.png"/>
- <TextBlock Margin="10,0,0,0">Save</TextBlock>
- </StackPanel>
- </Button>
-
- <Button Name="Actions.Cancel" Padding="10" IsCancel="True"
- Command="{Binding Close}">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Margin="10,0,0,0">Cancel</TextBlock>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Name="Actions.Save" Margin="0,0,10,0" Padding="10" IsDefault="True"
+ Text="Save"
+ Icon="fas fa-save"
+ Command="{Binding OnClickSave}"/>
+
+ <controls:ButtonWithIcon Name="Actions.Cancel" Padding="10" IsCancel="True"
+ Icon="fas fa-sign-out-alt"
+ Text="Cancel"
+ Command="{Binding Close}"/>
</StackPanel>
</Grid>
</Border>
diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml.cs b/UVtools.WPF/Windows/SettingsWindow.axaml.cs
index e4e3675..ca2f68b 100644
--- a/UVtools.WPF/Windows/SettingsWindow.axaml.cs
+++ b/UVtools.WPF/Windows/SettingsWindow.axaml.cs
@@ -15,289 +15,288 @@ using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
using Color = UVtools.WPF.Structures.Color;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class SettingsWindow : WindowEx
{
- public class SettingsWindow : WindowEx
- {
- private double _scrollViewerMaxHeight;
- private int _selectedTabIndex;
- private DataGrid _sendToCustomLocationsGrid;
- private DataGrid _sendToProcessGrid;
- private ComboBox _networkRemotePrinterComboBox;
+ private double _scrollViewerMaxHeight;
+ private int _selectedTabIndex;
+ private DataGrid _sendToCustomLocationsGrid;
+ private DataGrid _sendToProcessGrid;
+ private ComboBox _networkRemotePrinterComboBox;
- public int MaxProcessorCount => Environment.ProcessorCount;
+ public int MaxProcessorCount => Environment.ProcessorCount;
- public UserSettings SettingsBackup { get; }
+ public UserSettings SettingsBackup { get; }
- public string[] FileOpenDialogFilters { get; }
- public string[] ZoomRanges { get; }
+ public string[] FileOpenDialogFilters { get; }
+ public string[] ZoomRanges { get; }
- public int SelectedTabIndex
+ public int SelectedTabIndex
+ {
+ get => _selectedTabIndex;
+ set
{
- get => _selectedTabIndex;
- set
- {
- if(!RaiseAndSetIfChanged(ref _selectedTabIndex, value)) return;
+ if(!RaiseAndSetIfChanged(ref _selectedTabIndex, value)) return;
- var scrollViewer = this.FindControl<ScrollViewer>($"ScrollViewer{_selectedTabIndex}");
- SizeToContent = SizeToContent.Manual;
- Height = MaxHeight;
+ var scrollViewer = this.FindControl<ScrollViewer>($"ScrollViewer{_selectedTabIndex}");
+ SizeToContent = SizeToContent.Manual;
+ Height = MaxHeight;
- DispatcherTimer.RunOnce(() =>
+ Dispatcher.UIThread.Post(() =>
+ {
+ if (Math.Max((int)scrollViewer.Extent.Height - (int)scrollViewer.Viewport.Height, 0) == 0)
{
- if (Math.Max((int)scrollViewer.Extent.Height - (int)scrollViewer.Viewport.Height, 0) == 0)
- {
- Height = 10;
- SizeToContent = SizeToContent.Height;
- }
- }, TimeSpan.FromMilliseconds(2));
+ Height = 10;
+ SizeToContent = SizeToContent.Height;
+ }
+ }, DispatcherPriority.Loaded);
- }
}
+ }
- public double ScrollViewerMaxHeight
- {
- get => _scrollViewerMaxHeight;
- set => RaiseAndSetIfChanged(ref _scrollViewerMaxHeight, value);
- }
+ public double ScrollViewerMaxHeight
+ {
+ get => _scrollViewerMaxHeight;
+ set => RaiseAndSetIfChanged(ref _scrollViewerMaxHeight, value);
+ }
- public SettingsWindow()
+ public SettingsWindow()
+ {
+ Title += $" [v{App.VersionStr}]";
+ SettingsBackup = UserSettings.Instance.Clone();
+
+ var fileFormats = new List<string>
{
- Title += $" [v{App.VersionStr}]";
- SettingsBackup = UserSettings.Instance.Clone();
+ FileFormat.AllSlicerFiles.Replace("*", string.Empty)
+ };
+ fileFormats.AddRange(from format in FileFormat.AvailableFormats from extension in format.FileExtensions where extension.IsVisibleOnFileFilters select $"{extension.Description} (.{extension.Extension})");
+ FileOpenDialogFilters = fileFormats.ToArray();
- var fileFormats = new List<string>
- {
- FileFormat.AllSlicerFiles.Replace("*", string.Empty)
- };
- fileFormats.AddRange(from format in FileFormat.AvailableFormats from extension in format.FileExtensions where extension.IsVisibleOnFileFilters select $"{extension.Description} (.{extension.Extension})");
- FileOpenDialogFilters = fileFormats.ToArray();
+ // Derive strings for the zoom lock and crosshair fade combo-boxes from the
+ // ZoomLevels constant array, and add those strings to the comboboxes.
+ ZoomRanges = AppSettings.ZoomLevels.Skip(AppSettings.ZoomLevelSkipCount).Select(
+ s => Convert.ToString(s / 100, CultureInfo.InvariantCulture) + "x").ToArray();
- // Derive strings for the zoom lock and crosshair fade combo-boxes from the
- // ZoomLevels constant array, and add those strings to the comboboxes.
- ZoomRanges = AppSettings.ZoomLevels.Skip(AppSettings.ZoomLevelSkipCount).Select(
- s => Convert.ToString(s / 100, CultureInfo.InvariantCulture) + "x").ToArray();
+ ScrollViewerMaxHeight = this.GetScreenWorkingArea().Height - Settings.General.WindowsVerticalMargin;
- ScrollViewerMaxHeight = this.GetScreenWorkingArea().Height - Settings.General.WindowsVerticalMargin;
+ DataContext = this;
+ InitializeComponent();
- DataContext = this;
- InitializeComponent();
+ _sendToCustomLocationsGrid = this.FindControl<DataGrid>("SendToCustomLocationsGrid");
+ _sendToProcessGrid = this.FindControl<DataGrid>("SendToProcessGrid");
+ _networkRemotePrinterComboBox = this.FindControl<ComboBox>("NetworkRemotePrinterComboBox");
+ }
- _sendToCustomLocationsGrid = this.FindControl<DataGrid>("SendToCustomLocationsGrid");
- _sendToProcessGrid = this.FindControl<DataGrid>("SendToProcessGrid");
- _networkRemotePrinterComboBox = this.FindControl<ComboBox>("NetworkRemotePrinterComboBox");
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- private void InitializeComponent()
+ protected override void OnOpened(EventArgs e)
+ {
+ base.OnOpened(e);
+ //SizeToContent = SizeToContent.Manual;
+
+ Position = new PixelPoint(
+ Math.Max(0, (int)(Math.Max(0, App.MainWindow.Position.X) + App.MainWindow.Width / 2 - Width / 2)),
+ Math.Max(0, App.MainWindow.Position.Y) + 20
+ );
+
+ SelectedTabIndex = 1;
+ Dispatcher.UIThread.Post(() => SelectedTabIndex = 0, DispatcherPriority.Loaded);
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ base.OnClosed(e);
+
+ if (DialogResult != DialogResults.OK)
{
- AvaloniaXamlLoader.Load(this);
+ UserSettings.Instance = SettingsBackup;
}
-
- protected override void OnOpened(EventArgs e)
+ else
{
- base.OnOpened(e);
- //SizeToContent = SizeToContent.Manual;
-
- Position = new PixelPoint(
- (int)(App.MainWindow.Position.X + App.MainWindow.Width / 2 - Width / 2),
- App.MainWindow.Position.Y + 20
- );
-
- SelectedTabIndex = 1;
- DispatcherTimer.RunOnce(() => SelectedTabIndex = 0, TimeSpan.FromMilliseconds(1));
+ UserSettings.Save();
}
+ }
- protected override void OnClosed(EventArgs e)
+ public void SetMaxDegreeOfParallelism(string to)
+ {
+ if (to == "*")
{
- base.OnClosed(e);
-
- if (DialogResult != DialogResults.OK)
- {
- UserSettings.Instance = SettingsBackup;
- }
- else
- {
- UserSettings.Save();
- }
+ Settings.General.MaxDegreeOfParallelism = MaxProcessorCount;
+ return;
}
- public void SetMaxDegreeOfParallelism(string to)
+ if (to == "!")
{
- if (to == "*")
- {
- Settings.General.MaxDegreeOfParallelism = MaxProcessorCount;
- return;
- }
-
- if (to == "!")
- {
- Settings.General.MaxDegreeOfParallelism = Math.Max(1, MaxProcessorCount-2);
- return;
- }
-
- if (int.TryParse(to, out var i))
- {
- Settings.General.MaxDegreeOfParallelism = i;
- return;
- }
-
- if (decimal.TryParse(to, out var d))
- {
- Settings.General.MaxDegreeOfParallelism = (int)(MaxProcessorCount * d);
- return;
- }
+ Settings.General.MaxDegreeOfParallelism = Math.Max(1, MaxProcessorCount-2);
+ return;
}
-
- public async void GeneralOpenFolderField(string field)
+ if (int.TryParse(to, out var i))
{
- foreach (var propertyInfo in Settings.General.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
- {
- if (propertyInfo.Name != field) continue;
- OpenFolderDialog dialog = new();
- var filename = await dialog.ShowAsync(this);
- if (string.IsNullOrEmpty(filename)) return;
- propertyInfo.SetValue(Settings.General, filename);
- return;
- }
+ Settings.General.MaxDegreeOfParallelism = i;
+ return;
+ }
+ if (decimal.TryParse(to, out var d))
+ {
+ Settings.General.MaxDegreeOfParallelism = (int)(MaxProcessorCount * d);
+ return;
}
+ }
- public void GeneralClearField(string field)
+ public async void GeneralOpenFolderField(string field)
+ {
+ foreach (var propertyInfo in Settings.General.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
- var properties = Settings.General.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
- foreach (var propertyInfo in properties)
- {
- if (propertyInfo.Name != field) continue;
- propertyInfo.SetValue(Settings.General, null);
- return;
- }
-
+ if (propertyInfo.Name != field) continue;
+ OpenFolderDialog dialog = new();
+ var filename = await dialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(filename)) return;
+ propertyInfo.SetValue(Settings.General, filename);
+ return;
}
- public async void SendToAddCustomLocation()
- {
- var openFolder = new OpenFolderDialog();
- var result = await openFolder.ShowAsync(this);
+ }
- if (string.IsNullOrWhiteSpace(result)) return;
- var directory = new MappedDevice(result);
- if (Settings.General.SendToCustomLocations.Contains(directory))
- {
- await this.MessageBoxError("The selected location already exists on the list:\n" +
- $"{result}");
- return;
- }
- Settings.General.SendToCustomLocations.Add(directory);
+ public void GeneralClearField(string field)
+ {
+ var properties = Settings.General.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ foreach (var propertyInfo in properties)
+ {
+ if (propertyInfo.Name != field) continue;
+ propertyInfo.SetValue(Settings.General, null);
+ return;
}
+
+ }
+
+ public async void SendToAddCustomLocation()
+ {
+ var openFolder = new OpenFolderDialog();
+ var result = await openFolder.ShowAsync(this);
- public async void SendToRemoveCustomLocations()
+ if (string.IsNullOrWhiteSpace(result)) return;
+ var directory = new MappedDevice(result);
+ if (Settings.General.SendToCustomLocations.Contains(directory))
{
- if (_sendToCustomLocationsGrid.SelectedItems.Count == 0) return;
+ await this.MessageBoxError("The selected location already exists on the list:\n" +
+ $"{result}");
+ return;
+ }
- if (await this.MessageBoxQuestion(
- $"Are you sure you want to remove the {_sendToCustomLocationsGrid.SelectedItems.Count} selected entries?") !=
- ButtonResult.Yes) return;
+ Settings.General.SendToCustomLocations.Add(directory);
+ }
- Settings.General.SendToCustomLocations.RemoveRange(_sendToCustomLocationsGrid.SelectedItems.Cast<MappedDevice>());
- }
+ public async void SendToRemoveCustomLocations()
+ {
+ if (_sendToCustomLocationsGrid.SelectedItems.Count == 0) return;
- public async void SendToAddProcess()
- {
- var openFolder = new OpenFileDialog();
- var result = await openFolder.ShowAsync(this);
+ if (await this.MessageBoxQuestion(
+ $"Are you sure you want to remove the {_sendToCustomLocationsGrid.SelectedItems.Count} selected entries?") !=
+ ButtonResult.Yes) return;
- if (result is null || result.Length == 0) return;
- var file = new MappedProcess(result[0]);
- if (Settings.General.SendToProcess.Contains(file))
- {
- await this.MessageBoxError("The selected process already exists on the list:\n" +
- $"{result}");
- return;
- }
+ Settings.General.SendToCustomLocations.RemoveRange(_sendToCustomLocationsGrid.SelectedItems.Cast<MappedDevice>());
+ }
- Settings.General.SendToProcess.Add(file);
- }
+ public async void SendToAddProcess()
+ {
+ var openFolder = new OpenFileDialog();
+ var result = await openFolder.ShowAsync(this);
- public async void SendToRemoveProcess()
+ if (result is null || result.Length == 0) return;
+ var file = new MappedProcess(result[0]);
+ if (Settings.General.SendToProcess.Contains(file))
{
- if (_sendToProcessGrid.SelectedItems.Count == 0) return;
+ await this.MessageBoxError("The selected process already exists on the list:\n" +
+ $"{result}");
+ return;
+ }
- if (await this.MessageBoxQuestion(
- $"Are you sure you want to remove the {_sendToProcessGrid.SelectedItems.Count} selected entries?") !=
- ButtonResult.Yes) return;
+ Settings.General.SendToProcess.Add(file);
+ }
- Settings.General.SendToProcess.RemoveRange(_sendToProcessGrid.SelectedItems.Cast<MappedProcess>());
- }
+ public async void SendToRemoveProcess()
+ {
+ if (_sendToProcessGrid.SelectedItems.Count == 0) return;
- public async void SelectColor(string property)
+ if (await this.MessageBoxQuestion(
+ $"Are you sure you want to remove the {_sendToProcessGrid.SelectedItems.Count} selected entries?") !=
+ ButtonResult.Yes) return;
+
+ Settings.General.SendToProcess.RemoveRange(_sendToProcessGrid.SelectedItems.Cast<MappedProcess>());
+ }
+
+ public async void SelectColor(string property)
+ {
+ foreach (var packObject in UserSettings.PackObjects)
{
- foreach (var packObject in UserSettings.PackObjects)
+ var properties = packObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ foreach (var propertyInfo in properties)
{
- var properties = packObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
- foreach (var propertyInfo in properties)
- {
- if (propertyInfo.Name != property) continue;
- var color = (Color)propertyInfo.GetValue(packObject, null) ?? new Color(255,255,255,255);
- var window = new ColorPickerWindow(color.ToAvalonia());
- var result = await window.ShowDialog<DialogResults>(this);
- if (result != DialogResults.OK) return;
- propertyInfo.SetValue(packObject, new Color(window.ResultColor));
- return;
- }
+ if (propertyInfo.Name != property) continue;
+ var color = (Color)propertyInfo.GetValue(packObject, null) ?? new Color(255,255,255,255);
+ var window = new ColorPickerWindow(color.ToAvalonia());
+ var result = await window.ShowDialog<DialogResults>(this);
+ if (result != DialogResults.OK) return;
+ propertyInfo.SetValue(packObject, new Color(window.ResultColor));
+ return;
}
}
+ }
- public async void AddNetworkRemotePrinter()
- {
- var result = await this.MessageBoxQuestion("Are you sure you want to add a new remote printer", "Add new remote printer?");
- if (result != ButtonResult.Yes) return;
+ public async void AddNetworkRemotePrinter()
+ {
+ var result = await this.MessageBoxQuestion("Are you sure you want to add a new remote printer", "Add new remote printer?");
+ if (result != ButtonResult.Yes) return;
- var remotePrinter = new RemotePrinter
- {
- Name = "My new remote printer"
- };
+ var remotePrinter = new RemotePrinter
+ {
+ Name = "My new remote printer"
+ };
- Settings.Network.RemotePrinters.Add(remotePrinter);
- _networkRemotePrinterComboBox.SelectedItem = remotePrinter;
- }
+ Settings.Network.RemotePrinters.Add(remotePrinter);
+ _networkRemotePrinterComboBox.SelectedItem = remotePrinter;
+ }
- public async void RemoveSelectedNetworkRemotePrinter()
- {
- if (_networkRemotePrinterComboBox.SelectedItem is not RemotePrinter remotePrinter) return;
- var result = await this.MessageBoxQuestion("Are you sure you want to remove the following remote printer?\n" +
- remotePrinter, "Remove remote printer?");
- if (result != ButtonResult.Yes) return;
- Settings.Network.RemotePrinters.Remove(remotePrinter);
- }
+ public async void RemoveSelectedNetworkRemotePrinter()
+ {
+ if (_networkRemotePrinterComboBox.SelectedItem is not RemotePrinter remotePrinter) return;
+ var result = await this.MessageBoxQuestion("Are you sure you want to remove the following remote printer?\n" +
+ remotePrinter, "Remove remote printer?");
+ if (result != ButtonResult.Yes) return;
+ Settings.Network.RemotePrinters.Remove(remotePrinter);
+ }
- public async void DuplicateSelectedNetworkRemotePrinter()
- {
- if (_networkRemotePrinterComboBox.SelectedItem is not RemotePrinter remotePrinter) return;
- var result = await this.MessageBoxQuestion("Are you sure you want to duplicate the following remote printer?\n" +
- remotePrinter, "Duplicate remote printer?");
- if (result != ButtonResult.Yes) return;
- var clone = remotePrinter.Clone();
- clone.Name += " Duplicated";
- Settings.Network.RemotePrinters.Add(clone);
- _networkRemotePrinterComboBox.SelectedItem = clone;
- }
+ public async void DuplicateSelectedNetworkRemotePrinter()
+ {
+ if (_networkRemotePrinterComboBox.SelectedItem is not RemotePrinter remotePrinter) return;
+ var result = await this.MessageBoxQuestion("Are you sure you want to duplicate the following remote printer?\n" +
+ remotePrinter, "Duplicate remote printer?");
+ if (result != ButtonResult.Yes) return;
+ var clone = remotePrinter.Clone();
+ clone.Name += " Duplicated";
+ Settings.Network.RemotePrinters.Add(clone);
+ _networkRemotePrinterComboBox.SelectedItem = clone;
+ }
- public async void OnClickResetAllDefaults()
- {
- var result = await this.MessageBoxQuestion("Are you sure you want to reset all settings to the default values?", "Reset settings?");
- if (result != ButtonResult.Yes) return;
- UserSettings.Reset();
- ResetDataContext();
- }
+ public async void OnClickResetAllDefaults()
+ {
+ var result = await this.MessageBoxQuestion("Are you sure you want to reset all settings to the default values?", "Reset settings?");
+ if (result != ButtonResult.Yes) return;
+ UserSettings.Reset();
+ ResetDataContext();
+ }
- public void OnClickSave()
- {
- DialogResult = DialogResults.OK;
- CloseWithResult();
- }
+ public void OnClickSave()
+ {
+ DialogResult = DialogResults.OK;
+ CloseWithResult();
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/ShortcutsWindow.axaml.cs b/UVtools.WPF/Windows/ShortcutsWindow.axaml.cs
index 45f8ce7..0a8e176 100644
--- a/UVtools.WPF/Windows/ShortcutsWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ShortcutsWindow.axaml.cs
@@ -9,21 +9,20 @@ using Avalonia;
using Avalonia.Markup.Xaml;
using UVtools.WPF.Controls;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public partial class ShortcutsWindow : WindowEx
{
- public partial class ShortcutsWindow : WindowEx
+ public ShortcutsWindow()
{
- public ShortcutsWindow()
- {
- InitializeComponent();
+ InitializeComponent();
#if DEBUG
- this.AttachDevTools();
+ this.AttachDevTools();
#endif
- }
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml b/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml
new file mode 100644
index 0000000..feaac1d
--- /dev/null
+++ b/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml
@@ -0,0 +1,137 @@
+<uc:WindowEx xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
+ xmlns:uc="clr-namespace:UVtools.WPF.Controls"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="550"
+ x:Class="UVtools.WPF.Windows.SuggestionSettingsWindow"
+ WindowStartupLocation="CenterOwner"
+ SizeToContent="Height"
+ MinWidth="600"
+ MinHeight="400"
+ Width="820"
+ Height="550"
+ Title="Suggestions settings"
+ Icon="/Assets/Icons/UVtools.ico">
+ <Grid RowDefinitions="*">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="200" MinWidth="100"/>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*" MinWidth="200"/>
+ </Grid.ColumnDefinitions>
+
+ <Grid Grid.Row="0" Grid.Column="0"
+ RowDefinitions="*, Auto" ColumnDefinitions="*">
+
+ <ListBox Grid.Row="0" Grid.Column="0"
+ Name="SuggestionsListBox"
+ Items="{Binding Suggestions}"
+ SelectionMode="AlwaysSelected"
+ SelectedItem="{Binding SelectedSuggestion}">
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <TextBlock Text="{Binding Title}"
+ TextWrapping="Wrap"
+ ToolTip.Tip="{Binding Description}"/>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+
+ <Button Grid.Row="1" Grid.Column="0"
+ Content="Reset to defaults"
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Center"
+ Command="{Binding ResetDefaults}"/>
+ </Grid>
+
+ <GridSplitter Grid.Row="0" Grid.Column="1" ResizeBehavior="PreviousAndNext" ResizeDirection="Columns"/>
+
+
+ <TextBlock Grid.Column="2"
+ IsVisible="{Binding ActiveSuggestion, Converter={x:Static ObjectConverters.IsNull}}"
+ Text="Select an item from the box to configure"/>
+
+ <Grid Grid.Column="2" RowDefinitions="Auto,*" ColumnDefinitions="*"
+ IsVisible="{Binding ActiveSuggestion, Converter={x:Static ObjectConverters.IsNotNull}}">
+ <Border Grid.Row="0" Classes="Header">
+ <StackPanel Spacing="10">
+ <StackPanel Orientation="Horizontal" Spacing="10">
+ <ToggleSwitch FontWeight="Bold"
+ FontSize="24"
+ VerticalAlignment="Center"
+ IsChecked="{Binding ActiveSuggestion.Enabled}"
+ OffContent="{Binding ActiveSuggestion.Title}"
+ OnContent="{Binding ActiveSuggestion.Title}"/>
+
+ <uc:ButtonWithIcon Icon="fab fa-edge"
+ ToolTip.Tip="More information"
+ IsVisible="{Binding ActiveSuggestion.InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}"
+ Command="{Binding OpenWebsite}"
+ CommandParameter="{Binding ActiveSuggestion.InformationUrl}"/>
+ </StackPanel>
+
+ <TextBox Classes="TransparentReadOnly"
+ Margin="-10,0,0,5"
+ Text="{Binding ActiveSuggestion.Description}"
+ TextWrapping="Wrap"
+ IsVisible="{Binding ActiveSuggestion.ToolTip, Converter={x:Static ObjectConverters.IsNotNull}}"/>
+
+ <StackPanel Orientation="Horizontal" Spacing="5">
+ <uc:ButtonWithIcon Text="Save"
+ Spacing="5"
+ Icon="fas fa-check"
+ IsEnabled="{Binding PendingChanges}"
+ Command="{Binding SaveSuggestionClicked}"/>
+
+ <uc:ButtonWithIcon Text="Discard"
+ Spacing="5"
+ Icon="fas fa-undo-alt"
+ Margin="20,0,0,0"
+ IsEnabled="{Binding PendingChanges}"
+ Command="{Binding DiscardSuggestionClicked}"/>
+
+ <uc:ButtonWithIcon Text="Reset to default"
+ Spacing="5"
+ Icon="fas fa-times"
+ Command="{Binding ResetSuggestionClicked}"/>
+ </StackPanel>
+
+ </StackPanel>
+ </Border>
+
+ <ScrollViewer Name="ActiveSuggestionContent" Grid.Row="1"
+ Padding="20"
+ VerticalScrollBarVisibility="Auto"
+ HorizontalScrollBarVisibility="Auto">
+ <StackPanel Orientation="Vertical" Spacing="10">
+
+ <Grid RowDefinitions="Auto,10,Auto" ColumnDefinitions="Auto,10,400">
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="If enable, it will auto apply the suggestion whatever possible and on any change"
+ Text="Auto apply:"/>
+ <ToggleSwitch Grid.Row="0" Grid.Column="2"
+ IsChecked="{Binding ActiveSuggestion.AutoApply}"
+ ToolTip.Tip="If enable, it will auto apply the suggestion whatever possible and on any change"
+ OnContent="Auto apply once detect off values"
+ OffContent="Must apply manually by the user"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Apply when:"/>
+ <ComboBox Grid.Row="2" Grid.Column="2"
+ Items="{Binding ActiveSuggestion.ApplyWhen, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding ActiveSuggestion.ApplyWhen, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+ </Grid>
+
+ <ContentControl Name="ActiveSuggestionContentPanel"
+ Margin="0,10,0,0"/>
+
+ </StackPanel>
+
+ </ScrollViewer>
+ </Grid>
+
+ </Grid>
+</uc:WindowEx>
diff --git a/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs b/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs
new file mode 100644
index 0000000..bd43fa8
--- /dev/null
+++ b/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs
@@ -0,0 +1,204 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Threading;
+using MessageBox.Avalonia.Enums;
+using UVtools.Core.Extensions;
+using UVtools.Core.Suggestions;
+using UVtools.WPF.Controls;
+using UVtools.WPF.Controls.Suggestions;
+using UVtools.WPF.Extensions;
+using UVtools.WPF.Structures;
+
+namespace UVtools.WPF.Windows;
+
+public partial class SuggestionSettingsWindow : WindowEx
+{
+ private ContentControl _activeSuggestionContentPanel;
+
+ private Suggestion _activeSuggestion;
+ private Suggestion _selectedSuggestion;
+ private bool _pendingChanges;
+
+ public Suggestion[] Suggestions => App.MainWindow.Suggestions;
+
+ public Suggestion SelectedSuggestion
+ {
+ get => _selectedSuggestion;
+ set
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ bool recoverSuggestion = false;
+ if (_pendingChanges && _activeSuggestion is not null)
+ {
+ switch (await this.MessageBoxQuestion(
+ $"You have pending changes on '{_activeSuggestion.Title}' and you are about to discard them.\n" +
+ "Do you want to continue?\n\n" +
+ "Yes: Discard all changes and continue\n" +
+ "No: Save changes and continue\n" +
+ "Cancel: Do not continue",
+ "Pending changes", ButtonEnum.YesNoCancel))
+ {
+ case ButtonResult.Yes:
+ break;
+ case ButtonResult.No:
+ if (!await SaveSuggestion(false))
+ {
+ recoverSuggestion = true;
+ break;
+ }
+ SuggestionManager.SetSuggestion(_activeSuggestion.Clone(), true);
+ break;
+ case ButtonResult.Cancel:
+ case ButtonResult.Abort:
+ case ButtonResult.None:
+ recoverSuggestion = true;
+ break;
+ }
+ }
+
+
+ var oldSuggestion = _selectedSuggestion;
+ if (!RaiseAndSetIfChanged(ref _selectedSuggestion, value)) return;
+ if (recoverSuggestion)
+ {
+ RaiseAndSetIfChanged(ref _selectedSuggestion, oldSuggestion);
+ return;
+ }
+
+ ActiveSuggestion = value is null ? null : SuggestionManager.GetSuggestion(_selectedSuggestion.GetType()).Clone();
+ });
+
+ }
+ }
+
+ public Suggestion ActiveSuggestion
+ {
+ get => _activeSuggestion;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _activeSuggestion, value) || value is null) return;
+ PendingChanges = false;
+ _activeSuggestion.PropertyChanged += (sender, e) => PendingChanges = true;
+
+ var type = typeof(SuggestionControl);
+ var classname = $"{type.Namespace}.{_activeSuggestion.GetType().Name}Control"; ;
+ var controlType = Type.GetType(classname);
+ SuggestionControl control;
+
+ if (controlType is null)
+ {
+ control = new SuggestionControl(_activeSuggestion);
+ }
+ else
+ {
+ control = controlType.CreateInstance<SuggestionControl>(_activeSuggestion);
+ if (control is null) return;
+ }
+
+ _activeSuggestionContentPanel.Content = null;
+ _activeSuggestionContentPanel.Content = control;
+ }
+ }
+
+ public bool PendingChanges
+ {
+ get => _pendingChanges;
+ set => RaiseAndSetIfChanged(ref _pendingChanges, value);
+ }
+
+ public SuggestionSettingsWindow() : this(null) { }
+
+ public SuggestionSettingsWindow(Suggestion highlightSuggestion)
+ {
+ if (Design.IsDesignMode)
+ {
+ _activeSuggestion = new SuggestionBottomLayerCount();
+ }
+
+ DataContext = this;
+
+ InitializeComponent();
+#if DEBUG
+ this.AttachDevTools();
+#endif
+
+ _activeSuggestionContentPanel = this.FindControl<ContentControl>("ActiveSuggestionContentPanel");
+
+ if (highlightSuggestion is not null)
+ {
+ SelectedSuggestion = Suggestions.FirstOrDefault(suggestion => suggestion.GetType() == highlightSuggestion.GetType());
+ }
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+
+ public async void ResetDefaults()
+ {
+ if (await this.MessageBoxQuestion(
+ "Are you sure you want to reset all suggestions to the default settings?",
+ "Reset all settings?") != ButtonResult.Yes) return;
+
+ SuggestionManager.Reset();
+ if (_activeSuggestion is null) return;
+
+ ActiveSuggestion = (Suggestion)Activator.CreateInstance(_activeSuggestion.GetType());
+ }
+
+
+ public async Task<bool> SaveSuggestion(bool promptBeforeSave = true)
+ {
+ if (_activeSuggestion is null) return false;
+
+ var result = _activeSuggestion.Validate();
+ if (!string.IsNullOrWhiteSpace(result))
+ {
+ if (await this.MessageBoxError(result,
+ $"{_activeSuggestion.Title} - Error") != ButtonResult.Yes) return false;
+ }
+
+ if (promptBeforeSave && await this.MessageBoxQuestion(
+ "Are you sure you want to save and overwrite previous settings?",
+ "Save suggestion changes?") != ButtonResult.Yes) return false;
+
+ SuggestionManager.SetSuggestion(_activeSuggestion.Clone(), true);
+ PendingChanges = false;
+ return true;
+ }
+
+ public async void SaveSuggestionClicked()
+ {
+ await SaveSuggestion();
+ }
+
+ public async void DiscardSuggestionClicked()
+ {
+ if (_activeSuggestion is null) return;
+
+ if (await this.MessageBoxQuestion(
+ "Are you sure you want to discard all changes and return to the last saved state?",
+ "Discard suggestion changes?") != ButtonResult.Yes) return;
+
+ ActiveSuggestion = SuggestionManager.GetSuggestion(_activeSuggestion.GetType()).Clone();
+ }
+
+ public async void ResetSuggestionClicked()
+ {
+ if (_activeSuggestion is null) return;
+
+ if (await this.MessageBoxQuestion(
+ "Are you sure you want to reset to the default settings?",
+ "Reset suggestion settings?") != ButtonResult.Yes) return;
+
+ ActiveSuggestion = (Suggestion)Activator.CreateInstance(_activeSuggestion.GetType());
+ SuggestionManager.SetSuggestion(_activeSuggestion.Clone(), true);
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/TerminalWindow.axaml b/UVtools.WPF/Windows/TerminalWindow.axaml
index fe88998..61d48d3 100644
--- a/UVtools.WPF/Windows/TerminalWindow.axaml
+++ b/UVtools.WPF/Windows/TerminalWindow.axaml
@@ -14,7 +14,14 @@
Icon="/Assets/Icons/UVtools.ico"
x:Class="UVtools.WPF.Windows.TerminalWindow"
Title="UVtools interactive terminal">
- <Grid RowDefinitions="4*,5,*,5,Auto">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="4*" MinHeight="100"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*" MinHeight="50"/>
+ <RowDefinition Height="5"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
<TextBox Grid.Row="0"
Name="TerminalTextBox"
AcceptsReturn="True"
diff --git a/UVtools.WPF/Windows/TerminalWindow.axaml.cs b/UVtools.WPF/Windows/TerminalWindow.axaml.cs
index 8e937c6..46500d3 100644
--- a/UVtools.WPF/Windows/TerminalWindow.axaml.cs
+++ b/UVtools.WPF/Windows/TerminalWindow.axaml.cs
@@ -17,184 +17,190 @@ using Microsoft.CodeAnalysis.Scripting;
using UVtools.Core;
using UVtools.WPF.Controls;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public partial class TerminalWindow : WindowEx
{
- public partial class TerminalWindow : WindowEx
- {
- private static readonly string DefaultTerminalText = $"> Welcome to {About.Software} interactive terminal.\n" +
- "> Type in some commands in C# language to inject code.\n" +
- "> Example 1: SlicerFile.FirstLayer\n" +
- "> Example 2: SlicerFile.ExposureTime = 3\n\n";
-
- private string _terminalText = DefaultTerminalText;
- private string _commandText = string.Empty;
-
- private bool _multiLine = true;
- private bool _autoScroll = true;
- private bool _verbose;
- private bool _clearCommandAfterSend = true;
+ private static readonly string DefaultTerminalText = $"> Welcome to {About.Software} interactive terminal.\n" +
+ "> Type in some commands in C# language to inject code.\n" +
+ "> Example 1: SlicerFile.FirstLayer\n" +
+ "> Example 2: SlicerFile.ExposureTime = 3\n\n";
+
+ private string _terminalText = DefaultTerminalText;
+ private string _commandText = string.Empty;
+
+ private bool _multiLine = true;
+ private bool _autoScroll = true;
+ private bool _verbose = true;
+ private bool _clearCommandAfterSend = true;
- private readonly TextBox _terminalTextBox;
- public ScriptState _scriptState;
+ private readonly TextBox _terminalTextBox;
+ public ScriptState _scriptState;
- #region Properties
+ #region Properties
- public string TerminalText
+ public string TerminalText
+ {
+ get => _terminalText;
+ set
{
- get => _terminalText;
- set
- {
- if(!RaiseAndSetIfChanged(ref _terminalText, value)) return;
- if(_autoScroll) _terminalTextBox.CaretIndex = _terminalText.Length - 1;
- }
+ if(!RaiseAndSetIfChanged(ref _terminalText, value)) return;
+ if(_autoScroll) _terminalTextBox.CaretIndex = _terminalText.Length - 1;
}
+ }
- public string CommandText
- {
- get => _commandText;
- set => RaiseAndSetIfChanged(ref _commandText, value ?? string.Empty);
- }
+ public string CommandText
+ {
+ get => _commandText;
+ set => RaiseAndSetIfChanged(ref _commandText, value ?? string.Empty);
+ }
- public bool MultiLine
- {
- get => _multiLine;
- set => RaiseAndSetIfChanged(ref _multiLine, value);
- }
+ public bool MultiLine
+ {
+ get => _multiLine;
+ set => RaiseAndSetIfChanged(ref _multiLine, value);
+ }
- public bool AutoScroll
- {
- get => _autoScroll;
- set => RaiseAndSetIfChanged(ref _autoScroll, value);
- }
+ public bool AutoScroll
+ {
+ get => _autoScroll;
+ set => RaiseAndSetIfChanged(ref _autoScroll, value);
+ }
- public bool Verbose
- {
- get => _verbose;
- set => RaiseAndSetIfChanged(ref _verbose, value);
- }
+ public bool Verbose
+ {
+ get => _verbose;
+ set => RaiseAndSetIfChanged(ref _verbose, value);
+ }
- public bool ClearCommandAfterSend
- {
- get => _clearCommandAfterSend;
- set => RaiseAndSetIfChanged(ref _clearCommandAfterSend, value);
- }
+ public bool ClearCommandAfterSend
+ {
+ get => _clearCommandAfterSend;
+ set => RaiseAndSetIfChanged(ref _clearCommandAfterSend, value);
+ }
- #endregion
+ #endregion
- public TerminalWindow()
- {
- InitializeComponent();
+ public TerminalWindow()
+ {
+ InitializeComponent();
#if DEBUG
- this.AttachDevTools();
+ this.AttachDevTools();
#endif
- _terminalTextBox = this.FindControl<TextBox>("TerminalTextBox");
- AddHandler(DragDrop.DropEvent, (sender, e) =>
+ _terminalTextBox = this.FindControl<TextBox>("TerminalTextBox");
+ AddHandler(DragDrop.DropEvent, (sender, e) =>
+ {
+ var text = e.Data.GetText();
+ if (text is not null)
{
- var text = e.Data.GetText();
- if (text is not null)
- {
- CommandText = text;
- return;
- }
+ CommandText = text;
+ return;
+ }
- var fileNames = e.Data.GetFileNames();
- if (fileNames is not null)
+ var fileNames = e.Data.GetFileNames();
+ if (fileNames is not null)
+ {
+ var sb = new StringBuilder();
+ foreach (var fileName in fileNames)
{
- var sb = new StringBuilder();
- foreach (var fileName in fileNames)
+ try
{
- try
- {
- if (!File.Exists(fileName)) continue;
- var fi = new FileInfo(fileName);
- if(fi.Length > 5000000) continue; // 5Mb only!
- sb.AppendLine(File.ReadAllText(fi.FullName));
- }
- catch (Exception exception)
- {
- Console.WriteLine(exception);
- }
-
+ if (!File.Exists(fileName)) continue;
+ var fi = new FileInfo(fileName);
+ if(fi.Length > 5000000) continue; // 5Mb only!
+ sb.AppendLine(File.ReadAllText(fi.FullName));
}
-
- CommandText = sb.ToString();
+ catch (Exception exception)
+ {
+ Console.WriteLine(exception);
+ }
+
}
- });
+
+ CommandText = sb.ToString();
+ }
+ });
- DataContext = this;
- }
+ DataContext = this;
+ }
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
- public void Clear()
+ public void Clear()
+ {
+ TerminalText = DefaultTerminalText;
+ }
+
+ public async void SendCommand()
+ {
+ if (string.IsNullOrWhiteSpace(_commandText))
{
- TerminalText = DefaultTerminalText;
+ TerminalText += '\n';
+ return;
}
- public async void SendCommand()
+ var output = new StringBuilder(_terminalText);
+ if (_verbose) output.AppendLine(_commandText);
+
+ try
{
- if (string.IsNullOrWhiteSpace(_commandText))
+ if (_scriptState is null)
{
- TerminalText += '\n';
- return;
+ _scriptState = CSharpScript.RunAsync(_commandText,
+ ScriptOptions.Default
+ .AddReferences(typeof(About).Assembly)
+ .AddImports(
+ "System",
+ "System.Collections.Generic",
+ "System.Math",
+ "System.IO",
+ "System.Linq",
+ "System.Threading",
+ "System.Threading.Tasks",
+ "UVtools.Core",
+ "UVtools.Core.EmguCV",
+ "UVtools.Core.Extensions",
+ "UVtools.Core.FileFormats",
+ "UVtools.Core.GCode",
+ "UVtools.Core.Layers",
+ "UVtools.Core.Managers",
+ "UVtools.Core.MeshFormats",
+ "UVtools.Core.Objects",
+ "UVtools.Core.Operations",
+ "UVtools.Core.PixelEditor",
+ "UVtools.Core.Suggestions"
+ )
+ .WithAllowUnsafe(true), App.MainWindow).Result;
}
-
- var output = new StringBuilder(_terminalText);
- if (_verbose) output.AppendLine(_commandText);
-
- try
+ else
{
- if (_scriptState is null)
- {
- _scriptState = CSharpScript.RunAsync(_commandText,
- ScriptOptions.Default
- .AddReferences(typeof(About).Assembly)
- .AddImports(
- "System",
- "System.Collections.Generic",
- "System.Math",
- "System.IO",
- "System.Linq",
- "System.Threading",
- "System.Threading.Tasks",
- "UVtools.Core",
- "UVtools.Core.Extensions",
- "UVtools.Core.FileFormats",
- "UVtools.Core.Layers",
- "UVtools.Core.Objects",
- "UVtools.Core.Operations")
- .WithAllowUnsafe(true), this).Result;
- }
- else
- {
- _scriptState = await _scriptState.ContinueWithAsync(_commandText);
- }
+ _scriptState = await _scriptState.ContinueWithAsync(_commandText);
+ }
- if (_scriptState.ReturnValue is not null)
- {
- output.AppendLine(_scriptState.ReturnValue.ToString());
- }
- else if (_scriptState.Exception is not null)
- {
- output.AppendLine(_scriptState.Exception.ToString());
- }
- else if(!_verbose)
- {
- output.AppendLine(_commandText);
- }
+ if (_scriptState.ReturnValue is not null)
+ {
+ output.AppendLine(_scriptState.ReturnValue.ToString());
+ }
+ else if (_scriptState.Exception is not null)
+ {
+ output.AppendLine(_scriptState.Exception.ToString());
}
- catch (Exception e)
+ else if(!_verbose)
{
- output.AppendLine(e.Message);
+ output.AppendLine(_commandText);
}
+ }
+ catch (Exception e)
+ {
+ output.AppendLine(e.Message);
+ }
- TerminalText = output.ToString();
+ TerminalText = output.ToString();
- if (_clearCommandAfterSend) CommandText = string.Empty;
- }
+ if (_clearCommandAfterSend) CommandText = string.Empty;
}
-}
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml
index 28bda11..504f6c4 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml
+++ b/UVtools.WPF/Windows/ToolWindow.axaml
@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:UVtools.WPF.Controls"
+ xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="UVtools.WPF.Windows.ToolWindow"
CanResize="False"
@@ -15,7 +16,7 @@
<!-- Description -->
<Border Grid.Row="0"
- Background="WhiteSmoke"
+ Classes="BoxBackground"
Padding="10"
IsVisible="{Binding Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<Expander
@@ -34,14 +35,12 @@
<!-- Layer Range -->
<Border Grid.Row="1"
- Background="WhiteSmoke"
Classes="GroupBox"
Margin="5"
IsVisible="{Binding LayerRangeVisible}">
<StackPanel Orientation="Vertical">
- <Grid
- ColumnDefinitions="Auto,*" Background="LightBlue">
+ <Grid ColumnDefinitions="Auto,*" Classes="GroupBoxHeader">
<TextBlock
Padding="10" FontWeight="Bold"
VerticalAlignment="Center"
@@ -188,16 +187,13 @@
</Border>
<!-- ROI -->
- <Border
- Grid.Row="2" Classes="GroupBox"
- Background="WhiteSmoke"
- Margin="5"
- IsVisible="{Binding IsROIOrMasksVisible}">
+ <Border Grid.Row="2" Classes="GroupBox"
+ Margin="5"
+ IsVisible="{Binding IsROIOrMasksVisible}">
<StackPanel Orientation="Vertical">
- <Grid
- ColumnDefinitions="Auto,*" Background="LightBlue">
+ <Grid ColumnDefinitions="Auto,*" Classes="GroupBoxHeader">
<TextBlock
Padding="10" FontWeight="Bold"
VerticalAlignment="Center"
@@ -240,41 +236,31 @@
<!-- Profiles -->
<Border
Grid.Row="3" Classes="GroupBox"
- Background="WhiteSmoke"
Margin="5"
- IsVisible="{Binding IsProfilesVisible}"
- >
+ IsVisible="{Binding IsProfilesVisible}">
<StackPanel Orientation="Vertical">
- <Grid
- ColumnDefinitions="Auto,*" Background="LightBlue">
- <TextBlock
- Padding="10" FontWeight="Bold"
- VerticalAlignment="Center"
- Text="{Binding Profiles.Count, StringFormat=Profiles: \{0\}}" />
-
- <StackPanel Orientation="Horizontal"
- Grid.Column="1"
- HorizontalAlignment="Right"
- Spacing="2">
- <Button
- VerticalAlignment="Center"
- ToolTip.Tip="Clear all profiles"
- IsEnabled="{Binding Profiles.Count}"
- Command="{Binding ClearProfiles}"
- >
- <Image Width="16" Height="16" Source="/Assets/Icons/delete-16x16.png" />
+ <Grid ColumnDefinitions="Auto,*" Classes="GroupBoxHeader">
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ Padding="10" FontWeight="Bold"
+ VerticalAlignment="Center"
+ Text="{Binding Profiles.Count, StringFormat=Profiles: \{0\}}" />
+
+ <Button Grid.Row="0" Grid.Column="1"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Right"
+ ToolTip.Tip="Clear all profiles"
+ IsEnabled="{Binding Profiles.Count}"
+ Command="{Binding ClearProfiles}"
+ i:Attached.Icon="fas fa-times">
</Button>
- </StackPanel>
</Grid>
- <Grid
- RowDefinitions="Auto,Auto"
+ <Grid RowDefinitions="Auto,Auto"
ColumnDefinitions="*,5,Auto,5,Auto,5,Auto"
- Margin="15"
- >
+ Margin="15">
<ComboBox
Name="ProfileComboBox"
@@ -294,9 +280,8 @@
ToolTip.Tip="Deselect the current profile"
IsEnabled="{Binding SelectedProfileItem, Converter={x:Static ObjectConverters.IsNotNull}}"
IsVisible="{Binding Profiles.Count}"
- Command="{Binding DeselectProfile}">
- <Image Source="/Assets/Icons/checkbox-unmarked-16x16.png" />
- </Button>
+ Command="{Binding DeselectProfile}"
+ i:Attached.Icon="far fa-square"/>
<Button
Grid.Row="0"
@@ -320,9 +305,8 @@
IsEnabled="{Binding SelectedProfileItem, Converter={x:Static ObjectConverters.IsNotNull}}"
IsVisible="{Binding Profiles.Count}"
Command="{Binding RemoveSelectedProfile}"
- >
- <Image Source="/Assets/Icons/trash-16x16.png" />
- </Button>
+ i:Attached.Icon="fas fa-trash-alt"/>
+
<TextBox
Name="ProfileName"
@@ -331,19 +315,14 @@
IsEnabled="{Binding ButtonOkEnabled}"
Text="{Binding ProfileText}"
Watermark="Profile name or leave empty for auto name"
- Width="{Binding ProfileBoxMaxWidth}"
- />
-
- <Button
- Grid.Row="1"
- Grid.Column="2"
- VerticalAlignment="Stretch"
- ToolTip.Tip="Add a new profile with the current set values"
- IsEnabled="{Binding ButtonOkEnabled}"
- Command="{Binding AddProfile}"
- >
- <Image Source="/Assets/Icons/plus-16x16.png" />
- </Button>
+ Width="{Binding ProfileBoxMaxWidth}"/>
+
+ <Button Grid.Row="1" Grid.Column="2"
+ VerticalAlignment="Stretch"
+ ToolTip.Tip="Add a new profile with the current set values"
+ IsEnabled="{Binding ButtonOkEnabled}"
+ Command="{Binding AddProfile}"
+ i:Attached.Icon="fas fa-plus"/>
</Grid>
@@ -355,7 +334,6 @@
<Border Grid.Row="4"
Classes="GroupBox"
IsVisible="{Binding IsContentVisible}"
- Background="WhiteSmoke"
Margin="5">
<Grid RowDefinitions="Auto,*">
@@ -379,60 +357,40 @@
Content="☰">
<Button.ContextMenu>
<ContextMenu Name="OptionsContextMenu" PlacementMode="Top">
- <MenuItem
- IsVisible="{Binding CanROI}"
- Command="{Binding SelectVolumeBoundingRectangle}"
- Header="Select print volume ROI">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/expand-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem IsVisible="{Binding CanROI}"
+ Command="{Binding SelectVolumeBoundingRectangle}"
+ Header="Select print volume ROI"
+ i:MenuItem.Icon="fas fa-expand"/>
<Separator IsVisible="{Binding CanROI}"/>
- <MenuItem
- IsVisible="{Binding CanHaveProfiles}"
- Command="{Binding ImportSettings}"
- Header="Import settings">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/file-import-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
-
- <MenuItem
- IsVisible="{Binding CanHaveProfiles}"
- Command="{Binding ExportSettings}"
- Header="Export settings">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/file-export-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem IsVisible="{Binding CanHaveProfiles}"
+ Command="{Binding ImportSettings}"
+ Header="Import settings"
+ i:MenuItem.Icon="fas fa-file-import"/>
+
+
+ <MenuItem IsVisible="{Binding CanHaveProfiles}"
+ Command="{Binding ExportSettings}"
+ Header="Export settings"
+ i:MenuItem.Icon="fas fa-file-export"/>
<Separator IsVisible="{Binding CanHaveProfiles}"/>
- <MenuItem
- Command="{Binding ResetToDefaults}"
- Header="Reset to defaults">
- <MenuItem.Icon>
- <Image Source="/Assets/Icons/undo-alt-16x16.png"/>
- </MenuItem.Icon>
- </MenuItem>
+ <MenuItem Command="{Binding ResetToDefaults}"
+ Header="Reset to defaults"
+ i:MenuItem.Icon="fas fa-undo-alt"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
- <Button
- Padding="10"
- IsDefault="True"
- IsVisible="{Binding IsButton1Visible}"
- Command="{Binding OnButton1Click}">
- <StackPanel
- Orientation="Horizontal"
- Spacing="10">
- <Image Source="/Assets/Icons/undo-alt-16x16.png"/>
- <TextBlock Text="{Binding Button1Text}"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Padding="10"
+ IsDefault="True"
+ IsVisible="{Binding IsButton1Visible}"
+ Icon="fas fa-undo-alt"
+ Text="{Binding Button1Text}"
+ Command="{Binding OnButton1Click}">
+ </controls:ButtonWithIcon>
<CheckBox
IsChecked="{Binding IsCheckBox1Checked}"
@@ -445,31 +403,20 @@
HorizontalAlignment="Right"
Orientation="Horizontal">
- <Button
- Padding="10"
- IsDefault="True"
- IsVisible="{Binding ButtonOkVisible}"
- IsEnabled="{Binding ButtonOkEnabled}"
- Command="{Binding Process}">
- <StackPanel
- Orientation="Horizontal"
- Spacing="10">
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- <TextBlock Text="{Binding ButtonOkText}"/>
- </StackPanel>
- </Button>
-
- <Button
- Padding="10"
- IsCancel="True"
- Command="{Binding Close}">
- <StackPanel
- Orientation="Horizontal"
- Spacing="10">
- <Image Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Text="Cancel"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Padding="10"
+ IsDefault="True"
+ IsVisible="{Binding ButtonOkVisible}"
+ IsEnabled="{Binding ButtonOkEnabled}"
+ Icon="fas fa-check"
+ Text="{Binding ButtonOkText}"
+ Command="{Binding Process}">
+ </controls:ButtonWithIcon>
+
+ <controls:ButtonWithIcon Padding="10"
+ IsCancel="True"
+ Icon="fas fa-sign-out-alt"
+ Text="Cancel"
+ Command="{Binding Close}"/>
</StackPanel>
</Grid>
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs
index a3543d6..9f51cc5 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs
@@ -25,700 +25,665 @@ using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
using Helpers = UVtools.WPF.Controls.Helpers;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public class ToolWindow : WindowEx
{
- public class ToolWindow : WindowEx
+ public enum Callbacks : byte
{
- public enum Callbacks : byte
- {
- Init,
- ClearROI,
- Loaded,
- Button1, // Reset to defaults
- Checkbox1, // Show Advanced
- }
- private KeyModifiers _globalModifiers;
- public ToolControl ToolControl;
- private string _description;
- private double _descriptionMaxWidth;
- private double _profileBoxMaxWidth = double.NaN;
- private bool _layerRangeVisible = true;
- private bool _layerRangeSync;
- private uint _layerIndexStart;
- private uint _layerIndexEnd;
- private bool _layerIndexEndEnabled = true;
- private bool _isROIVisible;
- private bool _isMasksVisible;
-
- private bool _clearRoiAndMaskAfterOperation;
-
- private bool _isProfilesVisible;
- private RangeObservableCollection<Operation> _profiles = new();
- private Operation _selectedProfileItem;
- private string _profileText;
-
- private IControl _contentControl;
- private readonly ScrollViewer _contentScrollViewer;
-
- private bool _isButton1Visible;
- private string _button1Text = "Reset to defaults";
- private string _checkBox1Text = "Show advanced options";
- private bool _isCheckBox1Checked;
- private bool _isCheckBox1Visible;
-
- private bool _buttonOkEnabled = true;
- private string _buttonOkText = "Ok";
- private bool _buttonOkVisible = true;
+ Init,
+ ClearROI,
+ Loaded,
+ Button1, // Reset to defaults
+ Checkbox1, // Show Advanced
+ }
+ private KeyModifiers _globalModifiers;
+ public ToolControl ToolControl;
+ private string _description;
+ private double _descriptionMaxWidth;
+ private double _profileBoxMaxWidth = double.NaN;
+ private bool _layerRangeVisible = true;
+ private bool _layerRangeSync;
+ private uint _layerIndexStart;
+ private uint _layerIndexEnd;
+ private bool _layerIndexEndEnabled = true;
+ private bool _isROIVisible;
+ private bool _isMasksVisible;
+
+ private bool _clearRoiAndMaskAfterOperation;
+
+ private bool _isProfilesVisible;
+ private RangeObservableCollection<Operation> _profiles = new();
+ private Operation _selectedProfileItem;
+ private string _profileText;
+
+ private IControl _contentControl;
+ private readonly ScrollViewer _contentScrollViewer;
+
+ private bool _isButton1Visible;
+ private string _button1Text = "Reset to defaults";
+ private string _checkBox1Text = "Show advanced options";
+ private bool _isCheckBox1Checked;
+ private bool _isCheckBox1Visible;
+
+ private bool _buttonOkEnabled = true;
+ private string _buttonOkText = "Ok";
+ private bool _buttonOkVisible = true;
- #region Description
+ #region Description
- public string Description
- {
- get => _description;
- set => RaiseAndSetIfChanged(ref _description, value);
- }
+ public string Description
+ {
+ get => _description;
+ set => RaiseAndSetIfChanged(ref _description, value);
+ }
- public double DescriptionMaxWidth
- {
- get => _descriptionMaxWidth;
- set => RaiseAndSetIfChanged(ref _descriptionMaxWidth, value);
- }
+ public double DescriptionMaxWidth
+ {
+ get => _descriptionMaxWidth;
+ set => RaiseAndSetIfChanged(ref _descriptionMaxWidth, value);
+ }
- public double ProfileBoxMaxWidth
- {
- get => _profileBoxMaxWidth;
- set => RaiseAndSetIfChanged(ref _profileBoxMaxWidth, value);
- }
+ public double ProfileBoxMaxWidth
+ {
+ get => _profileBoxMaxWidth;
+ set => RaiseAndSetIfChanged(ref _profileBoxMaxWidth, value);
+ }
- #endregion
+ #endregion
- #region Layer Selector
+ #region Layer Selector
- public bool LayerRangeVisible
- {
- get => _layerRangeVisible;
- set => RaiseAndSetIfChanged(ref _layerRangeVisible, value);
- }
+ public bool LayerRangeVisible
+ {
+ get => _layerRangeVisible;
+ set => RaiseAndSetIfChanged(ref _layerRangeVisible, value);
+ }
- public bool LayerRangeSync
+ public bool LayerRangeSync
+ {
+ get => _layerRangeSync;
+ set
{
- get => _layerRangeSync;
- set
+ if(!RaiseAndSetIfChanged(ref _layerRangeSync, value)) return;
+ if (_layerRangeSync)
{
- if(!RaiseAndSetIfChanged(ref _layerRangeSync, value)) return;
- if (_layerRangeSync)
- {
- LayerIndexEnd = _layerIndexStart;
- }
+ LayerIndexEnd = _layerIndexStart;
}
}
+ }
- public uint LayerIndexStart
+ public uint LayerIndexStart
+ {
+ get => _layerIndexStart;
+ set
{
- get => _layerIndexStart;
- set
- {
- value = Math.Min(value, SlicerFile.LastLayerIndex);
-
- if (ToolControl?.BaseOperation is not null)
- {
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
- ToolControl.BaseOperation.LayerIndexStart = value;
- }
+ value = Math.Min(value, SlicerFile.LastLayerIndex);
- if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return;
- RaisePropertyChanged(nameof(LayerStartMM));
- RaisePropertyChanged(nameof(LayerRangeCountStr));
+ if (ToolControl?.BaseOperation is not null)
+ {
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
+ ToolControl.BaseOperation.LayerIndexStart = value;
+ }
- if (_layerRangeSync)
- {
- LayerIndexEnd = _layerIndexStart;
- }
+ if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return;
+ RaisePropertyChanged(nameof(LayerStartMM));
+ RaisePropertyChanged(nameof(LayerRangeCountStr));
- App.MainWindow.ActualLayer = _layerIndexStart;
+ if (_layerRangeSync)
+ {
+ LayerIndexEnd = _layerIndexStart;
}
+
+ App.MainWindow.ActualLayer = _layerIndexStart;
}
+ }
- public float LayerStartMM => SlicerFile[_layerIndexStart].PositionZ;
+ public float LayerStartMM => SlicerFile[_layerIndexStart].PositionZ;
- public uint LayerIndexEnd
+ public uint LayerIndexEnd
+ {
+ get => _layerIndexEnd;
+ set
{
- get => _layerIndexEnd;
- set
- {
- value = Math.Min(value, SlicerFile.LastLayerIndex);
+ value = Math.Min(value, SlicerFile.LastLayerIndex);
- if (ToolControl?.BaseOperation is not null)
- {
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
- ToolControl.BaseOperation.LayerIndexEnd = value;
- }
+ if (ToolControl?.BaseOperation is not null)
+ {
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
+ ToolControl.BaseOperation.LayerIndexEnd = value;
+ }
- if (!RaiseAndSetIfChanged(ref _layerIndexEnd, value)) return;
- RaisePropertyChanged(nameof(LayerEndMM));
- RaisePropertyChanged(nameof(LayerRangeCountStr));
+ if (!RaiseAndSetIfChanged(ref _layerIndexEnd, value)) return;
+ RaisePropertyChanged(nameof(LayerEndMM));
+ RaisePropertyChanged(nameof(LayerRangeCountStr));
- //App.MainWindow.ActualLayer = _layerIndexEnd;
- }
+ //App.MainWindow.ActualLayer = _layerIndexEnd;
}
+ }
- public float LayerEndMM => SlicerFile[_layerIndexEnd].PositionZ;
+ public float LayerEndMM => SlicerFile[_layerIndexEnd].PositionZ;
- public bool LayerIndexEndEnabled
- {
- get => _layerIndexEndEnabled;
- set => RaiseAndSetIfChanged(ref _layerIndexEndEnabled, value);
- }
+ public bool LayerIndexEndEnabled
+ {
+ get => _layerIndexEndEnabled;
+ set => RaiseAndSetIfChanged(ref _layerIndexEndEnabled, value);
+ }
- public string LayerRangeCountStr
+ public string LayerRangeCountStr
+ {
+ get
{
- get
- {
- uint layerCount = (uint) Math.Max(0, (int)LayerIndexEnd - LayerIndexStart + 1);
- return $"({layerCount} layers / {Layer.ShowHeight(SlicerFile.LayerHeight * layerCount)}mm)";
- }
-
+ uint layerCount = (uint) Math.Max(0, (int)LayerIndexEnd - LayerIndexStart + 1);
+ return $"({layerCount} layers / {Layer.ShowHeight(SlicerFile.LayerHeight * layerCount)}mm)";
}
+
+ }
- public uint MaximumLayerIndex => App.MainWindow?.SliderMaximumValue ?? 0;
+ public uint MaximumLayerIndex => App.MainWindow?.SliderMaximumValue ?? 0;
- public void SelectAllLayers()
- {
- LayerIndexStart = 0;
- LayerIndexEnd = MaximumLayerIndex;
- if(ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.All;
- }
+ public void SelectAllLayers()
+ {
+ LayerIndexStart = 0;
+ LayerIndexEnd = MaximumLayerIndex;
+ if(ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.All;
+ }
- public void SelectCurrentLayer()
- {
- LayerIndexStart = LayerIndexEnd = App.MainWindow.ActualLayer;
- if (ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Current;
- }
+ public void SelectCurrentLayer()
+ {
+ LayerIndexStart = LayerIndexEnd = App.MainWindow.ActualLayer;
+ if (ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Current;
+ }
- public void SelectFirstToCurrentLayer()
- {
- LayerIndexEnd = App.MainWindow.ActualLayer;
- LayerIndexStart = 0;
- if (ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
- }
+ public void SelectFirstToCurrentLayer()
+ {
+ LayerIndexEnd = App.MainWindow.ActualLayer;
+ LayerIndexStart = 0;
+ if (ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
+ }
- public void SelectCurrentToLastLayer()
- {
- LayerIndexStart = App.MainWindow.ActualLayer;
- LayerIndexEnd = SlicerFile.LastLayerIndex;
- if (ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
- }
+ public void SelectCurrentToLastLayer()
+ {
+ LayerIndexStart = App.MainWindow.ActualLayer;
+ LayerIndexEnd = SlicerFile.LastLayerIndex;
+ if (ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
+ }
- public void SelectBottomLayers()
- {
- LayerIndexStart = 0;
- LayerIndexEnd = Math.Max(1, SlicerFile.FirstNormalLayer?.Index ?? 1) - 1u;
- if (ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Bottom;
- }
+ public void SelectBottomLayers()
+ {
+ LayerIndexStart = 0;
+ LayerIndexEnd = Math.Max(1, SlicerFile.FirstNormalLayer?.Index ?? 1) - 1u;
+ if (ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Bottom;
+ }
- public void SelectNormalLayers()
- {
- LayerIndexStart = SlicerFile.FirstNormalLayer?.Index ?? 0;
- LayerIndexEnd = MaximumLayerIndex;
- if (ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Normal;
- }
+ public void SelectNormalLayers()
+ {
+ LayerIndexStart = SlicerFile.FirstNormalLayer?.Index ?? 0;
+ LayerIndexEnd = MaximumLayerIndex;
+ if (ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Normal;
+ }
- public void SelectFirstLayer()
- {
- LayerIndexStart = LayerIndexEnd = 0;
- if (ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.First;
- }
+ public void SelectFirstLayer()
+ {
+ LayerIndexStart = LayerIndexEnd = 0;
+ if (ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.First;
+ }
- public void SelectLastLayer()
- {
- LayerIndexStart = LayerIndexEnd = MaximumLayerIndex;
- if (ToolControl is not null)
- ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Last;
- }
+ public void SelectLastLayer()
+ {
+ LayerIndexStart = LayerIndexEnd = MaximumLayerIndex;
+ if (ToolControl is not null)
+ ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Last;
+ }
- public void SelectLayers(Enumerations.LayerRangeSelection range)
- {
- switch (range)
- {
- case Enumerations.LayerRangeSelection.None:
- break;
- case Enumerations.LayerRangeSelection.All:
- SelectAllLayers();
- break;
- case Enumerations.LayerRangeSelection.Current:
- SelectCurrentLayer();
- break;
- case Enumerations.LayerRangeSelection.Bottom:
- SelectBottomLayers();
- break;
- case Enumerations.LayerRangeSelection.Normal:
- SelectNormalLayers();
- break;
- case Enumerations.LayerRangeSelection.First:
- SelectFirstLayer();
- break;
- case Enumerations.LayerRangeSelection.Last:
- SelectLastLayer();
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
+ public void SelectLayers(Enumerations.LayerRangeSelection range)
+ {
+ switch (range)
+ {
+ case Enumerations.LayerRangeSelection.None:
+ break;
+ case Enumerations.LayerRangeSelection.All:
+ SelectAllLayers();
+ break;
+ case Enumerations.LayerRangeSelection.Current:
+ SelectCurrentLayer();
+ break;
+ case Enumerations.LayerRangeSelection.Bottom:
+ SelectBottomLayers();
+ break;
+ case Enumerations.LayerRangeSelection.Normal:
+ SelectNormalLayers();
+ break;
+ case Enumerations.LayerRangeSelection.First:
+ SelectFirstLayer();
+ break;
+ case Enumerations.LayerRangeSelection.Last:
+ SelectLastLayer();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
}
- #endregion
+ }
+ #endregion
- #region ROI & Masks
+ #region ROI & Masks
- public bool IsROIOrMasksVisible => IsROIVisible || IsMasksVisible;
+ public bool IsROIOrMasksVisible => IsROIVisible || IsMasksVisible;
- public bool IsROIVisible
+ public bool IsROIVisible
+ {
+ get
{
- get
- {
- if (ToolControl is null) return _isROIVisible;
- return ToolControl.BaseOperation.CanROI && _isROIVisible;
- }
- set
- {
- if(!RaiseAndSetIfChanged(ref _isROIVisible, value)) return;
- RaisePropertyChanged(nameof(IsROIOrMasksVisible));
- }
+ if (ToolControl is null) return _isROIVisible;
+ return ToolControl.BaseOperation.CanROI && _isROIVisible;
}
-
- public bool IsMasksVisible
+ set
{
- get
- {
- if (ToolControl is null) return _isMasksVisible;
- return ToolControl.BaseOperation.CanMask && _isMasksVisible;
- }
- set
- {
- if(!RaiseAndSetIfChanged(ref _isMasksVisible, value)) return;
- RaisePropertyChanged(nameof(IsROIOrMasksVisible));
- }
+ if(!RaiseAndSetIfChanged(ref _isROIVisible, value)) return;
+ RaisePropertyChanged(nameof(IsROIOrMasksVisible));
}
+ }
- public bool CanROI => ToolControl.BaseOperation.CanROI;
-
- public Rectangle ROI
+ public bool IsMasksVisible
+ {
+ get
{
- get => App.MainWindow.ROI;
- set
- {
- App.MainWindow.ROI = value;
- ToolControl.BaseOperation.ROI = value;
- IsROIVisible = !value.IsEmpty;
- RaisePropertyChanged();
- }
+ if (ToolControl is null) return _isMasksVisible;
+ return ToolControl.BaseOperation.CanMask && _isMasksVisible;
+ }
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _isMasksVisible, value)) return;
+ RaisePropertyChanged(nameof(IsROIOrMasksVisible));
}
+ }
- public System.Drawing.Point[][] Masks => App.MainWindow.MaskPoints?.ToArray();
+ public bool CanROI => ToolControl.BaseOperation.CanROI;
- public bool ClearROIAndMaskAfterOperation
+ public Rectangle ROI
+ {
+ get => App.MainWindow.ROI;
+ set
{
- get => _clearRoiAndMaskAfterOperation;
- set => RaiseAndSetIfChanged(ref _clearRoiAndMaskAfterOperation, value);
+ App.MainWindow.ROI = value;
+ ToolControl.BaseOperation.ROI = value;
+ IsROIVisible = !value.IsEmpty;
+ RaisePropertyChanged();
}
+ }
- public async void ClearROI()
- {
- if (await this.MessageBoxQuestion("Are you sure you want to clear the current ROI?\n" +
- "This action can not be reverted, to select another ROI you must quit this window and select it on layer preview.",
+ public System.Drawing.Point[][] Masks => App.MainWindow.MaskPoints?.ToArray();
+
+ public bool ClearROIAndMaskAfterOperation
+ {
+ get => _clearRoiAndMaskAfterOperation;
+ set => RaiseAndSetIfChanged(ref _clearRoiAndMaskAfterOperation, value);
+ }
+
+ public async void ClearROI()
+ {
+ if (await this.MessageBoxQuestion("Are you sure you want to clear the current ROI?\n" +
+ "This action can not be reverted, to select another ROI you must quit this window and select it on layer preview.",
"Clear the current ROI?") != ButtonResult.Yes) return;
- ROI = Rectangle.Empty;
- ToolControl?.Callback(Callbacks.ClearROI);
- }
+ ROI = Rectangle.Empty;
+ ToolControl?.Callback(Callbacks.ClearROI);
+ }
- public async void ClearMasks()
- {
- var layer = SlicerFile.LayerManager.LargestNormalLayer;
- if (await this.MessageBoxQuestion("Are you sure you want to clear all masks?\n" +
- "This action can not be reverted, to select another mask(s) you must quit this window and select it on layer preview.",
+ public async void ClearMasks()
+ {
+ var layer = SlicerFile.LargestNormalLayer;
+ if (await this.MessageBoxQuestion("Are you sure you want to clear all masks?\n" +
+ "This action can not be reverted, to select another mask(s) you must quit this window and select it on layer preview.",
"Clear the all masks?") != ButtonResult.Yes) return;
- IsMasksVisible = false;
- App.MainWindow.ClearMask();
- ToolControl.BaseOperation.ClearMasks();
- ToolControl?.Callback(Callbacks.ClearROI);
- }
+ IsMasksVisible = false;
+ App.MainWindow.ClearMask();
+ ToolControl.BaseOperation.ClearMasks();
+ ToolControl?.Callback(Callbacks.ClearROI);
+ }
- public void SelectVolumeBoundingRectangle()
- {
- ROI = SlicerFile.BoundingRectangle;
- }
+ public void SelectVolumeBoundingRectangle()
+ {
+ ROI = SlicerFile.BoundingRectangle;
+ }
- #endregion
+ #endregion
- #region Profiles
+ #region Profiles
- public bool CanHaveProfiles => ToolControl.BaseOperation.CanHaveProfiles;
+ public bool CanHaveProfiles => ToolControl.BaseOperation.CanHaveProfiles;
- public bool IsProfilesVisible
- {
- get => _isProfilesVisible;
- set => RaiseAndSetIfChanged(ref _isProfilesVisible, value);
- }
+ public bool IsProfilesVisible
+ {
+ get => _isProfilesVisible;
+ set => RaiseAndSetIfChanged(ref _isProfilesVisible, value);
+ }
- public RangeObservableCollection<Operation> Profiles
- {
- get => _profiles;
- set => RaiseAndSetIfChanged(ref _profiles, value);
- }
+ public RangeObservableCollection<Operation> Profiles
+ {
+ get => _profiles;
+ set => RaiseAndSetIfChanged(ref _profiles, value);
+ }
- public Operation SelectedProfileItem
- {
- get => _selectedProfileItem;
- set
+ public Operation SelectedProfileItem
+ {
+ get => _selectedProfileItem;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _selectedProfileItem, value) || value is null) return;
+ if (ToolControl is null) return;
+ var operation = _selectedProfileItem.Clone();
+ operation.ProfileName = null;
+ operation.ImportedFrom = Operation.OperationImportFrom.Profile;
+ ToolControl.BaseOperation = operation;
+ switch (operation.LayerRangeSelection)
{
- if(!RaiseAndSetIfChanged(ref _selectedProfileItem, value) || value is null) return;
- if (ToolControl is null) return;
- var operation = _selectedProfileItem.Clone();
- operation.ProfileName = null;
- operation.ImportedFrom = Operation.OperationImportFrom.Profile;
- ToolControl.BaseOperation = operation;
- switch (operation.LayerRangeSelection)
- {
- case Enumerations.LayerRangeSelection.None:
- LayerIndexStart = operation.LayerIndexStart;
- LayerIndexEnd = operation.LayerIndexEnd;
- break;
- default:
- SelectLayers(operation.LayerRangeSelection);
- break;
- }
-
- //ToolControl.Callback(Callbacks.Loaded);
- //ToolControl.ResetDataContext();
+ case Enumerations.LayerRangeSelection.None:
+ LayerIndexStart = operation.LayerIndexStart;
+ LayerIndexEnd = operation.LayerIndexEnd;
+ break;
+ default:
+ SelectLayers(operation.LayerRangeSelection);
+ break;
}
- }
- public string ProfileText
- {
- get => _profileText;
- set => RaiseAndSetIfChanged(ref _profileText, value);
+ //ToolControl.Callback(Callbacks.Loaded);
+ //ToolControl.ResetDataContext();
}
+ }
+
+ public string ProfileText
+ {
+ get => _profileText;
+ set => RaiseAndSetIfChanged(ref _profileText, value);
+ }
- public async void AddProfile()
+ public async void AddProfile()
+ {
+ var name = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
+ var operation = OperationProfiles.FindByName(ToolControl.BaseOperation, name);
+ if (operation is not null)
{
- var name = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
- var operation = OperationProfiles.FindByName(ToolControl.BaseOperation, name);
- if (operation is not null)
- {
- if (await this.MessageBoxQuestion(
+ if (await this.MessageBoxQuestion(
$"A profile with same name or settings already exists.\nDo you want to overwrite:\n{operation}",
"Overwrite profile?") != ButtonResult.Yes) return;
- /*var index = OperationProfiles.Instance.IndexOf(operation);
- OperationProfiles.Profiles[index] = ToolControl.BaseOperation;
- index = Profiles.IndexOf(operation);
- Profiles[index] = ToolControl.BaseOperation;*/
+ /*var index = OperationProfiles.Instance.IndexOf(operation);
+ OperationProfiles.Profiles[index] = ToolControl.BaseOperation;
+ index = Profiles.IndexOf(operation);
+ Profiles[index] = ToolControl.BaseOperation;*/
- OperationProfiles.RemoveProfile(operation, false);
- Profiles.Remove(operation);
- }
+ OperationProfiles.RemoveProfile(operation, false);
+ Profiles.Remove(operation);
+ }
- var toAdd = ToolControl.BaseOperation.Clone();
- toAdd.ProfileName = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
- OperationProfiles.AddProfile(toAdd);
- Profiles.Insert(0, toAdd);
+ var toAdd = ToolControl.BaseOperation.Clone();
+ toAdd.ProfileName = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
+ OperationProfiles.AddProfile(toAdd);
+ Profiles.Insert(0, toAdd);
- ProfileText = null;
- }
+ ProfileText = null;
+ }
- public async void RemoveSelectedProfile()
- {
- if (_selectedProfileItem is null) return;
+ public async void RemoveSelectedProfile()
+ {
+ if (_selectedProfileItem is null) return;
- if (await this.MessageBoxQuestion(
+ if (await this.MessageBoxQuestion(
$"Are you sure you want to remove the selected profile?\n{_selectedProfileItem}",
"Remove selected profile?") != ButtonResult.Yes) return;
- OperationProfiles.RemoveProfile(_selectedProfileItem);
- Profiles.Remove(_selectedProfileItem);
- SelectedProfileItem = null;
+ OperationProfiles.RemoveProfile(_selectedProfileItem);
+ Profiles.Remove(_selectedProfileItem);
+ SelectedProfileItem = null;
- }
+ }
- public async void ClearProfiles()
- {
- if (Profiles.Count == 0) return;
- if (await this.MessageBoxQuestion(
+ public async void ClearProfiles()
+ {
+ if (Profiles.Count == 0) return;
+ if (await this.MessageBoxQuestion(
$"Are you sure you want to clear all the {Profiles.Count} profiles?",
"Clear all profiles?") != ButtonResult.Yes) return;
- OperationProfiles.ClearProfiles(Profiles[0].GetType());
- Profiles.Clear();
- }
+ OperationProfiles.ClearProfiles(Profiles[0].GetType());
+ Profiles.Clear();
+ }
- public void DeselectProfile()
- {
- SelectedProfileItem = null;
- }
+ public void DeselectProfile()
+ {
+ SelectedProfileItem = null;
+ }
- public async void SetDefaultProfile()
- {
- if (_selectedProfileItem is null) return;
+ public async void SetDefaultProfile()
+ {
+ if (_selectedProfileItem is null) return;
- if ((_globalModifiers & KeyModifiers.Shift) != 0)
- {
- if (await this.MessageBoxQuestion(
+ if ((_globalModifiers & KeyModifiers.Shift) != 0)
+ {
+ if (await this.MessageBoxQuestion(
$"Are you sure you want to clear the selected profile as default settings for this dialog?",
"Clear the default profile?") != ButtonResult.Yes) return;
- foreach (var operation in Profiles)
- {
- operation.ProfileIsDefault = false;
- }
- }
- else
+ foreach (var operation in Profiles)
{
- if (await this.MessageBoxQuestion(
+ operation.ProfileIsDefault = false;
+ }
+ }
+ else
+ {
+ if (await this.MessageBoxQuestion(
$"Are you sure you want to set the selected profile as default settings for this dialog?\n{_selectedProfileItem}",
"Set as default profile?") != ButtonResult.Yes) return;
- foreach (var operation in Profiles)
- {
- operation.ProfileIsDefault = false;
- }
-
- _selectedProfileItem.ProfileIsDefault = true;
+ foreach (var operation in Profiles)
+ {
+ operation.ProfileIsDefault = false;
}
+
+ _selectedProfileItem.ProfileIsDefault = true;
+ }
- OperationProfiles.Save();
+ OperationProfiles.Save();
- }
-
- #endregion
+ }
- #region Content
+ #endregion
- public bool IsContentVisible => ContentControl is null || ContentControl.IsVisible;
+ #region Content
- public IControl ContentControl
- {
- get => _contentControl;
- set => RaiseAndSetIfChanged(ref _contentControl, value);
- }
+ public bool IsContentVisible => ContentControl is null || ContentControl.IsVisible;
- public ScrollViewer ContentScrollViewer => _contentScrollViewer;
+ public IControl ContentControl
+ {
+ get => _contentControl;
+ set => RaiseAndSetIfChanged(ref _contentControl, value);
+ }
- #endregion
+ public ScrollViewer ContentScrollViewer => _contentScrollViewer;
- #region Actions
+ #endregion
- public bool IsButton1Visible
- {
- get => _isButton1Visible;
- set => RaiseAndSetIfChanged(ref _isButton1Visible, value);
- }
+ #region Actions
- public string Button1Text
- {
- get => _button1Text;
- set => RaiseAndSetIfChanged(ref _button1Text, value);
- }
+ public bool IsButton1Visible
+ {
+ get => _isButton1Visible;
+ set => RaiseAndSetIfChanged(ref _isButton1Visible, value);
+ }
- public void OnButton1Click() => ToolControl?.Callback(Callbacks.Button1);
+ public string Button1Text
+ {
+ get => _button1Text;
+ set => RaiseAndSetIfChanged(ref _button1Text, value);
+ }
- public string CheckBox1Text
- {
- get => _checkBox1Text;
- set => RaiseAndSetIfChanged(ref _checkBox1Text, value);
- }
+ public void OnButton1Click() => ToolControl?.Callback(Callbacks.Button1);
- public bool IsCheckBox1Checked
- {
- get => _isCheckBox1Checked;
- set
- {
- if(!RaiseAndSetIfChanged(ref _isCheckBox1Checked, value)) return;
- ToolControl?.Callback(Callbacks.Checkbox1);
- }
- }
+ public string CheckBox1Text
+ {
+ get => _checkBox1Text;
+ set => RaiseAndSetIfChanged(ref _checkBox1Text, value);
+ }
- public bool IsCheckBox1Visible
+ public bool IsCheckBox1Checked
+ {
+ get => _isCheckBox1Checked;
+ set
{
- get => _isCheckBox1Visible;
- set => RaiseAndSetIfChanged(ref _isCheckBox1Visible, value);
+ if(!RaiseAndSetIfChanged(ref _isCheckBox1Checked, value)) return;
+ ToolControl?.Callback(Callbacks.Checkbox1);
}
+ }
+ public bool IsCheckBox1Visible
+ {
+ get => _isCheckBox1Visible;
+ set => RaiseAndSetIfChanged(ref _isCheckBox1Visible, value);
+ }
- public bool ButtonOkEnabled
- {
- get => _buttonOkEnabled;
- set => RaiseAndSetIfChanged(ref _buttonOkEnabled, value);
- }
-
- public bool ButtonOkVisible
- {
- get => _buttonOkVisible;
- set => RaiseAndSetIfChanged(ref _buttonOkVisible, value);
- }
- public string ButtonOkText
- {
- get => _buttonOkText;
- set => RaiseAndSetIfChanged(ref _buttonOkText, value);
- }
+ public bool ButtonOkEnabled
+ {
+ get => _buttonOkEnabled;
+ set => RaiseAndSetIfChanged(ref _buttonOkEnabled, value);
+ }
+ public bool ButtonOkVisible
+ {
+ get => _buttonOkVisible;
+ set => RaiseAndSetIfChanged(ref _buttonOkVisible, value);
+ }
+ public string ButtonOkText
+ {
+ get => _buttonOkText;
+ set => RaiseAndSetIfChanged(ref _buttonOkText, value);
+ }
- #endregion
- #region Constructors
- public ToolWindow()
- {
- InitializeComponent();
- _contentScrollViewer = this.FindControl<ScrollViewer>("ContentScrollViewer");
- SelectAllLayers();
- if (ROI != Rectangle.Empty)
- {
- IsROIVisible = true;
- }
+ #endregion
- if (Masks?.Length > 0)
- {
- IsMasksVisible = true;
- }
- }
+ #region Constructors
+ public ToolWindow()
+ {
+ InitializeComponent();
+ _contentScrollViewer = this.FindControl<ScrollViewer>("ContentScrollViewer");
+ SelectAllLayers();
- public ToolWindow(string description = null, bool layerRangeVisible = true, bool layerEndIndexEnabled = true) : this()
+ if (ROI != Rectangle.Empty)
{
- _description = description;
- _layerRangeVisible = layerRangeVisible;
- _layerIndexEndEnabled = layerEndIndexEnabled;
+ IsROIVisible = true;
}
- public ToolWindow(ToolControl toolControl) : this(toolControl.BaseOperation.Description, toolControl.BaseOperation.StartLayerRangeSelection != Enumerations.LayerRangeSelection.None, toolControl.BaseOperation.LayerIndexEndEnabled)
+ if (Masks?.Length > 0)
{
- ToolControl = toolControl;
- toolControl.ParentWindow = this;
- toolControl.Margin = new Thickness(15);
+ IsMasksVisible = true;
+ }
+ }
- Title = toolControl.BaseOperation.Title;
- //LayerRangeVisible = toolControl.BaseOperation.StartLayerRangeSelection != Enumerations.LayerRangeSelection.None;
- //IsROIVisible = toolControl.BaseOperation.CanROI;
- _contentControl = toolControl;
- _buttonOkText = toolControl.BaseOperation.ButtonOkText;
- _buttonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction;
+ public ToolWindow(string description = null, bool layerRangeVisible = true, bool layerEndIndexEnabled = true) : this()
+ {
+ _description = description;
+ _layerRangeVisible = layerRangeVisible;
+ _layerIndexEndEnabled = layerEndIndexEnabled;
+ }
- bool loadedFromSession = false;
- if (!toolControl.BaseOperation.HaveExecuted
- && toolControl.BaseOperation.ImportedFrom is Operation.OperationImportFrom.None
- && Settings.Tools.RestoreLastUsedSettings)
+ public ToolWindow(ToolControl toolControl) : this(toolControl.BaseOperation.Description, toolControl.BaseOperation.StartLayerRangeSelection != Enumerations.LayerRangeSelection.None, toolControl.BaseOperation.LayerIndexEndEnabled)
+ {
+ ToolControl = toolControl;
+ toolControl.ParentWindow = this;
+ toolControl.Margin = new Thickness(15);
+
+ Title = toolControl.BaseOperation.Title;
+ //LayerRangeVisible = toolControl.BaseOperation.StartLayerRangeSelection != Enumerations.LayerRangeSelection.None;
+ //IsROIVisible = toolControl.BaseOperation.CanROI;
+ _contentControl = toolControl;
+ _buttonOkText = toolControl.BaseOperation.ButtonOkText;
+ _buttonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction;
+
+ bool loadedFromSession = false;
+ if (!toolControl.BaseOperation.HaveExecuted
+ && toolControl.BaseOperation.ImportedFrom is Operation.OperationImportFrom.None
+ && Settings.Tools.RestoreLastUsedSettings)
+ {
+ var operation = OperationSessionManager.Instance.Find(toolControl.BaseOperation.GetType());
+ if (operation is not null)
{
- var operation = OperationSessionManager.Instance.Find(toolControl.BaseOperation.GetType());
- if (operation is not null)
- {
- toolControl.BaseOperation = operation.Clone();
- loadedFromSession = true;
- }
+ toolControl.BaseOperation = operation.Clone();
+ loadedFromSession = true;
}
+ }
- if (toolControl.BaseOperation.HaveExecuted
- || toolControl.BaseOperation.ImportedFrom != Operation.OperationImportFrom.None) // Loaded from something
+ if (toolControl.BaseOperation.HaveExecuted
+ || toolControl.BaseOperation.ImportedFrom != Operation.OperationImportFrom.None) // Loaded from something
+ {
+ if (toolControl.BaseOperation.HaveROI)
{
- if (toolControl.BaseOperation.HaveROI)
- {
- ROI = toolControl.BaseOperation.ROI;
- }
+ ROI = toolControl.BaseOperation.ROI;
+ }
- if (toolControl.BaseOperation.HaveMask)
- {
- App.MainWindow.AddMaskPoints(toolControl.BaseOperation.MaskPoints);
- }
+ if (toolControl.BaseOperation.HaveMask)
+ {
+ App.MainWindow.AddMaskPoints(toolControl.BaseOperation.MaskPoints);
+ }
- if (toolControl.BaseOperation.LayerRangeSelection == Enumerations.LayerRangeSelection.None)
- {
- LayerIndexStart = toolControl.BaseOperation.LayerIndexStart;
- LayerIndexEnd = toolControl.BaseOperation.LayerIndexEnd;
- }
- else
- {
- SelectLayers(toolControl.BaseOperation.LayerRangeSelection);
- }
+ if (toolControl.BaseOperation.LayerRangeSelection == Enumerations.LayerRangeSelection.None)
+ {
+ LayerIndexStart = toolControl.BaseOperation.LayerIndexStart;
+ LayerIndexEnd = toolControl.BaseOperation.LayerIndexEnd;
}
else
{
- SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection);
+ SelectLayers(toolControl.BaseOperation.LayerRangeSelection);
}
+ }
+ else
+ {
+ SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection);
+ }
- if (ToolControl.BaseOperation.CanHaveProfiles)
- {
- _isProfilesVisible = true;
- var profiles = OperationProfiles.GetOperations(ToolControl.BaseOperation.GetType());
- _profiles.AddRange(profiles);
+ if (ToolControl.BaseOperation.CanHaveProfiles)
+ {
+ _isProfilesVisible = true;
+ var profiles = OperationProfiles.GetOperations(ToolControl.BaseOperation.GetType());
+ _profiles.AddRange(profiles);
- if (toolControl.BaseOperation.ImportedFrom == Operation.OperationImportFrom.None ||
- (loadedFromSession && !Settings.Tools.LastUsedSettingsPriorityOverDefaultProfile))
+ if (toolControl.BaseOperation.ImportedFrom == Operation.OperationImportFrom.None ||
+ (loadedFromSession && !Settings.Tools.LastUsedSettingsPriorityOverDefaultProfile))
+ {
+ //Operation profile = _profiles.FirstOrDefault(operation => operation.ProfileIsDefault);
+ foreach (var operation in Profiles)
{
- //Operation profile = _profiles.FirstOrDefault(operation => operation.ProfileIsDefault);
- foreach (var operation in Profiles)
+ if (operation.ProfileIsDefault)
{
- if (operation.ProfileIsDefault)
- {
- SelectedProfileItem = operation;
- break;
- }
+ SelectedProfileItem = operation;
+ break;
}
}
}
-
- //RaisePropertyChanged(nameof(IsContentVisible));
- //RaisePropertyChanged(nameof(IsROIVisible));
-
- // Ensure the description don't stretch window
- /*DispatcherTimer.Run(() =>
- {
- if (Bounds.Width == 0) return true;
- //ScrollViewerMaxHeight = this.GetScreenWorkingArea().Height - Bounds.Height + ToolControl.Bounds.Height - UserSettings.Instance.General.WindowsVerticalMargin;
- DescriptionMaxWidth = Math.Max(Bounds.Width, ToolControl.Bounds.Width) - 20;
- ExpanderHeaderMaxWidth = DescriptionMaxWidth - 40;
- Height = MaxHeight;
-
- DispatcherTimer.Run(() =>
- {
- if (Math.Max((int)_contentScrollViewer.Extent.Height - (int)_contentScrollViewer.Viewport.Height, 0) == 0)
- {
- Height = 10;
- SizeToContent = SizeToContent.WidthAndHeight;
- }
- Position = new PixelPoint(
- (int)(App.MainWindow.Position.X + App.MainWindow.Width / 2 - Width / 2),
- App.MainWindow.Position.Y + 20
- );
- return false;
- }, TimeSpan.FromMilliseconds(2));
-
- return false;
- }, TimeSpan.FromMilliseconds(1));*/
-
- toolControl.Callback(Callbacks.Init);
- toolControl.DataContext = toolControl;
- DataContext = this;
}
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
+ //RaisePropertyChanged(nameof(IsContentVisible));
+ //RaisePropertyChanged(nameof(IsROIVisible));
- protected override void OnOpened(EventArgs e)
+ // Ensure the description don't stretch window
+ /*DispatcherTimer.Run(() =>
{
- base.OnOpened(e);
- var profileTextBox = this.FindControl<TextBox>("ProfileName");
+ if (Bounds.Width == 0) return true;
+ //ScrollViewerMaxHeight = this.GetScreenWorkingArea().Height - Bounds.Height + ToolControl.Bounds.Height - UserSettings.Instance.General.WindowsVerticalMargin;
DescriptionMaxWidth = Math.Max(Bounds.Width, ToolControl.Bounds.Width) - 20;
- ProfileBoxMaxWidth = profileTextBox.Bounds.Width;
+ ExpanderHeaderMaxWidth = DescriptionMaxWidth - 40;
Height = MaxHeight;
DispatcherTimer.Run(() =>
@@ -732,183 +697,216 @@ namespace UVtools.WPF.Windows
(int)(App.MainWindow.Position.X + App.MainWindow.Width / 2 - Width / 2),
App.MainWindow.Position.Y + 20
);
+ return false;
+ }, TimeSpan.FromMilliseconds(2));
- CanResize = Settings.General.WindowsCanResize;
+ return false;
+ }, TimeSpan.FromMilliseconds(1));*/
- return false;
- }, TimeSpan.FromMilliseconds(1));
- }
+ toolControl.Callback(Callbacks.Init);
+ toolControl.DataContext = toolControl;
+ DataContext = this;
+ }
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ protected override void OnOpened(EventArgs e)
+ {
+ base.OnOpened(e);
+ var profileTextBox = this.FindControl<TextBox>("ProfileName");
+ DescriptionMaxWidth = Math.Max(Bounds.Width, ToolControl.Bounds.Width) - 20;
+ ProfileBoxMaxWidth = profileTextBox.Bounds.Width;
+ Height = MaxHeight;
- public void FitToSize()
+ Dispatcher.UIThread.Post(() =>
{
- SizeToContent = SizeToContent.Manual;
- Height = MaxHeight;
- DispatcherTimer.Run(() =>
+ if (Math.Max((int)_contentScrollViewer.Extent.Height - (int)_contentScrollViewer.Viewport.Height, 0) == 0)
{
- if (Math.Max((int)_contentScrollViewer.Extent.Height - (int)_contentScrollViewer.Viewport.Height, 0) == 0)
- {
- Height = 10;
- SizeToContent = SizeToContent.WidthAndHeight;
- }
- Position = new PixelPoint(
- (int)(App.MainWindow.Position.X + App.MainWindow.Width / 2 - Width / 2),
- App.MainWindow.Position.Y + 20
- );
- return false;
- }, TimeSpan.FromMilliseconds(1));
- }
- #endregion
+ Height = 10;
+ SizeToContent = SizeToContent.WidthAndHeight;
+ }
+
+ Position = new PixelPoint(
+ Math.Max(0, (int)(Math.Max(0, App.MainWindow.Position.X) + App.MainWindow.Width / 2 - Width / 2)),
+ Math.Max(0, App.MainWindow.Position.Y) + 20
+ );
+
+ CanResize = Settings.General.WindowsCanResize;
+ }, DispatcherPriority.Loaded);
+ }
- /*protected override void OnOpened(EventArgs e)
+ public void FitToSize()
+ {
+ SizeToContent = SizeToContent.Manual;
+ Height = MaxHeight;
+ Dispatcher.UIThread.Post(() =>
{
- base.OnOpened(e);
-
- if (!(ToolControl is null))
+ if (Math.Max((int)_contentScrollViewer.Extent.Height - (int)_contentScrollViewer.Viewport.Height, 0) == 0)
{
- DescriptionMaxWidth = Math.Max(Bounds.Width, ToolControl?.Bounds.Width ?? 0) - 40;
- Description = ToolControl.BaseOperation.Description;
- }
- DataContext = this;
- }*/
+ Height = 10;
+ SizeToContent = SizeToContent.WidthAndHeight;
+ }
- #region Overrides
+ Position = new PixelPoint(
+ Math.Max(0, (int)(Math.Max(0, App.MainWindow.Position.X) + App.MainWindow.Width / 2 - Width / 2)),
+ Math.Max(0, App.MainWindow.Position.Y) + 20
+ );
- protected override void OnPointerMoved(PointerEventArgs e)
- {
- base.OnPointerMoved(e);
- _globalModifiers = e.KeyModifiers;
- }
+ }, DispatcherPriority.Loaded);
+ }
- protected override void OnKeyDown(KeyEventArgs e)
- {
- base.OnKeyDown(e);
- _globalModifiers = e.KeyModifiers;
- }
+ #endregion
- protected override void OnKeyUp(KeyEventArgs e)
+ /*protected override void OnOpened(EventArgs e)
+ {
+ base.OnOpened(e);
+
+ if (!(ToolControl is null))
{
- base.OnKeyUp(e);
- _globalModifiers = e.KeyModifiers;
- }
+ DescriptionMaxWidth = Math.Max(Bounds.Width, ToolControl?.Bounds.Width ?? 0) - 40;
+ Description = ToolControl.BaseOperation.Description;
+ }
+ DataContext = this;
+ }*/
+
+ #region Overrides
+
+ protected override void OnPointerMoved(PointerEventArgs e)
+ {
+ base.OnPointerMoved(e);
+ _globalModifiers = e.KeyModifiers;
+ }
+
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ _globalModifiers = e.KeyModifiers;
+ }
+
+ protected override void OnKeyUp(KeyEventArgs e)
+ {
+ base.OnKeyUp(e);
+ _globalModifiers = e.KeyModifiers;
+ }
- #endregion
+ #endregion
- public async void Process()
+ public async void Process()
+ {
+ if (LayerIndexStart > LayerIndexEnd)
{
- if (LayerIndexStart > LayerIndexEnd)
- {
- await this.MessageBoxError("Layer range start can't be higher than layer end.\nPlease fix and try again.");
- return;
- }
+ await this.MessageBoxError("Layer range start can't be higher than layer end.\nPlease fix and try again.");
+ return;
+ }
- if (ToolControl is not null)
- {
- ToolControl.BaseOperation.LayerIndexStart = LayerIndexStart;
- ToolControl.BaseOperation.LayerIndexEnd = LayerIndexEnd;
- /*if (IsROIVisible && ToolControl.BaseOperation.ROI.IsEmpty)
- {
- }*/
- ToolControl.BaseOperation.SetROIIfEmpty(ROI);
- ToolControl.BaseOperation.SetMasksIfEmpty(Masks);
-
- if (!await ToolControl.ValidateForm()) return;
- if (!string.IsNullOrEmpty(ToolControl.BaseOperation.ConfirmationText))
- {
- var result =
- await this.MessageBoxQuestion(
- $"Are you sure you want to {ToolControl.BaseOperation.ConfirmationText}");
- if (result != ButtonResult.Yes) return;
- }
- }
+ if (ToolControl is not null)
+ {
+ ToolControl.BaseOperation.LayerIndexStart = LayerIndexStart;
+ ToolControl.BaseOperation.LayerIndexEnd = LayerIndexEnd;
+ /*if (IsROIVisible && ToolControl.BaseOperation.ROI.IsEmpty)
+ {
+ }*/
+ ToolControl.BaseOperation.SetROIIfEmpty(ROI);
+ ToolControl.BaseOperation.SetMasksIfEmpty(Masks);
- if (_clearRoiAndMaskAfterOperation)
+ if (!await ToolControl.ValidateForm()) return;
+ if (!string.IsNullOrEmpty(ToolControl.BaseOperation.ConfirmationText))
{
- App.MainWindow.ClearROIAndMask();
+ var result =
+ await this.MessageBoxQuestion(
+ $"Are you sure you want to {ToolControl.BaseOperation.ConfirmationText}");
+ if (result != ButtonResult.Yes) return;
}
-
- DialogResult = DialogResults.OK;
- Close(DialogResult);
}
- public void OpenContextMenu(string name)
+ if (_clearRoiAndMaskAfterOperation)
{
- var menu = this.FindControl<ContextMenu>($"{name}ContextMenu");
- if (menu is null) return;
- var parent = this.FindControl<Button>($"{name}Button");
- if (parent is null) return;
- menu.Open(parent);
+ App.MainWindow.ClearROIAndMask();
}
- public async void ExportSettings()
+ DialogResult = DialogResults.OK;
+ Close(DialogResult);
+ }
+
+ public void OpenContextMenu(string name)
+ {
+ var menu = this.FindControl<ContextMenu>($"{name}ContextMenu");
+ if (menu is null) return;
+ var parent = this.FindControl<Button>($"{name}Button");
+ if (parent is null) return;
+ menu.Open(parent);
+ }
+
+ public async void ExportSettings()
+ {
+ if (ToolControl.BaseOperation is null) return;
+ var dialog = new SaveFileDialog
{
- if (ToolControl.BaseOperation is null) return;
- var dialog = new SaveFileDialog
- {
- Filters = Helpers.OperationSettingFileFilter,
- InitialFileName = ToolControl.BaseOperation.Id
- };
+ Filters = Helpers.OperationSettingFileFilter,
+ InitialFileName = ToolControl.BaseOperation.Id
+ };
- var file = await dialog.ShowAsync(this);
+ var file = await dialog.ShowAsync(this);
- if (string.IsNullOrWhiteSpace(file)) return;
+ if (string.IsNullOrWhiteSpace(file)) return;
- try
- {
- ToolControl.BaseOperation.Serialize(file);
- }
- catch (Exception e)
- {
- await this.MessageBoxError(e.ToString(), "Error while trying to export the settings");
- }
+ try
+ {
+ ToolControl.BaseOperation.Serialize(file);
}
-
- public async void ImportSettings()
+ catch (Exception e)
{
- var dialog = new OpenFileDialog
- {
- AllowMultiple = false,
- Filters = Helpers.OperationSettingFileFilter
- };
+ await this.MessageBoxError(e.ToString(), "Error while trying to export the settings");
+ }
+ }
- var files = await dialog.ShowAsync(this);
+ public async void ImportSettings()
+ {
+ var dialog = new OpenFileDialog
+ {
+ AllowMultiple = false,
+ Filters = Helpers.OperationSettingFileFilter
+ };
- if (files is null || files.Length == 0) return;
+ var files = await dialog.ShowAsync(this);
- try
- {
- var operation = Operation.Deserialize(files[0], ToolControl.BaseOperation);
+ if (files is null || files.Length == 0) return;
- ToolControl.BaseOperation = operation;
- switch (operation.LayerRangeSelection)
- {
- case Enumerations.LayerRangeSelection.None:
- LayerIndexStart = operation.LayerIndexStart;
- LayerIndexEnd = operation.LayerIndexEnd;
- break;
- default:
- SelectLayers(operation.LayerRangeSelection);
- break;
- }
+ try
+ {
+ var operation = Operation.Deserialize(files[0], ToolControl.BaseOperation);
-
- }
- catch (Exception e)
+ ToolControl.BaseOperation = operation;
+ switch (operation.LayerRangeSelection)
{
- await this.MessageBoxError(e.ToString(), "Error while trying to import the settings");
+ case Enumerations.LayerRangeSelection.None:
+ LayerIndexStart = operation.LayerIndexStart;
+ LayerIndexEnd = operation.LayerIndexEnd;
+ break;
+ default:
+ SelectLayers(operation.LayerRangeSelection);
+ break;
}
- }
- public void ResetToDefaults()
+
+ }
+ catch (Exception e)
{
- var operation = ToolControl.BaseOperation.GetType().CreateInstance<Operation>(SlicerFile);
- operation.LayerIndexStart = ToolControl.BaseOperation.LayerIndexStart;
- operation.LayerIndexEnd = ToolControl.BaseOperation.LayerIndexEnd;
- operation.LayerRangeSelection = ToolControl.BaseOperation.LayerRangeSelection;
- operation.ROI = ToolControl.BaseOperation.ROI;
- operation.MaskPoints = ToolControl.BaseOperation.MaskPoints;
- ToolControl.BaseOperation = operation;
+ await this.MessageBoxError(e.ToString(), "Error while trying to import the settings");
}
}
-}
+
+ public void ResetToDefaults()
+ {
+ var operation = ToolControl.BaseOperation.GetType().CreateInstance<Operation>(SlicerFile);
+ operation.LayerIndexStart = ToolControl.BaseOperation.LayerIndexStart;
+ operation.LayerIndexEnd = ToolControl.BaseOperation.LayerIndexEnd;
+ operation.LayerRangeSelection = ToolControl.BaseOperation.LayerRangeSelection;
+ operation.ROI = ToolControl.BaseOperation.ROI;
+ operation.MaskPoints = ToolControl.BaseOperation.MaskPoints;
+ ToolControl.BaseOperation = operation;
+ }
+} \ No newline at end of file
diff --git a/UVtools.WPF/Windows/VersionSelectorWindow.axaml b/UVtools.WPF/Windows/VersionSelectorWindow.axaml
index eadbd84..82eb2ed 100644
--- a/UVtools.WPF/Windows/VersionSelectorWindow.axaml
+++ b/UVtools.WPF/Windows/VersionSelectorWindow.axaml
@@ -2,6 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:controls="clr-namespace:UVtools.WPF.Controls"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="450"
x:Class="UVtools.WPF.Windows.VersionSelectorWindow"
CanResize="False"
@@ -25,19 +26,16 @@
<Border DockPanel.Dock="Bottom" Classes="FooterActions" Margin="-10" Padding="10,15">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="5">
- <Button Command="{Binding SelectVersion}" IsDefault="True">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/accept-16x16.png"/>
- <TextBlock Text="{Binding Version, StringFormat=Select v{0}}" VerticalAlignment="Center"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon Command="{Binding SelectVersion}" IsDefault="True"
+ Icon="fas fa-check"
+ Text="{Binding Version, StringFormat=Select v{0}}">
+ </controls:ButtonWithIcon>
<Button Content="Use the default" Command="{Binding SelectDefault}"/>
- <Button Command="{Binding Close}" IsCancel="True">
- <StackPanel Orientation="Horizontal" Spacing="5">
- <Image Source="/Assets/Icons/exit-16x16.png"/>
- <TextBlock Text="Cancel" VerticalAlignment="Center"/>
- </StackPanel>
- </Button>
+ <controls:ButtonWithIcon IsCancel="True"
+ Icon="fas fa-sign-out-alt"
+ Text="Cancel"
+ Command="{Binding Close}">
+ </controls:ButtonWithIcon>
</StackPanel>
</Border>
diff --git a/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs b/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs
index 1fb4ed9..d9bf6ce 100644
--- a/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs
+++ b/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs
@@ -2,57 +2,56 @@ using Avalonia.Markup.Xaml;
using UVtools.Core.FileFormats;
using UVtools.WPF.Controls;
-namespace UVtools.WPF.Windows
+namespace UVtools.WPF.Windows;
+
+public partial class VersionSelectorWindow : WindowEx
{
- public partial class VersionSelectorWindow : WindowEx
+ private uint _version;
+
+ public string DescriptionText =>
+ $"This file format \"{FileExtension.Description}\" contains multiple available versions. Some versions may require a specific firmware version in order to run.\n" +
+ "Select the version you wish to use on the output file.\n" +
+ $"If unsure, use the default version {SlicerFile.DefaultVersion}.";
+
+ public sealed override FileFormat SlicerFile { get; set; }
+
+ public FileExtension FileExtension { get; set; }
+
+ public uint Version
+ {
+ get => _version;
+ set => RaiseAndSetIfChanged(ref _version, value);
+ }
+
+ public VersionSelectorWindow()
+ {
+ InitializeComponent();
+ DialogResult = DialogResults.Cancel;
+ }
+
+ public VersionSelectorWindow(FileFormat slicerFile, FileExtension fileExtension) : this()
+ {
+ SlicerFile = slicerFile;
+ FileExtension = fileExtension;
+ Version = slicerFile.DefaultVersion;
+ Title += $" - {FileExtension.Description}";
+ DataContext = this;
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public void SelectVersion()
+ {
+ DialogResult = Version == SlicerFile.DefaultVersion ? DialogResults.Unknown : DialogResults.OK;
+ Close();
+ }
+
+ public void SelectDefault()
{
- private uint _version;
-
- public string DescriptionText =>
- $"This file format \"{FileExtension.Description}\" contains multiple available versions. Some versions may require a specific firmware version in order to run.\n" +
- "Select the version you wish to use on the output file.\n" +
- $"If unsure, use the default version {SlicerFile.DefaultVersion}.";
-
- public sealed override FileFormat SlicerFile { get; set; }
-
- public FileExtension FileExtension { get; set; }
-
- public uint Version
- {
- get => _version;
- set => RaiseAndSetIfChanged(ref _version, value);
- }
-
- public VersionSelectorWindow()
- {
- InitializeComponent();
- DialogResult = DialogResults.Cancel;
- }
-
- public VersionSelectorWindow(FileFormat slicerFile, FileExtension fileExtension) : this()
- {
- SlicerFile = slicerFile;
- FileExtension = fileExtension;
- Version = slicerFile.DefaultVersion;
- Title += $" - {FileExtension.Description}";
- DataContext = this;
- }
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
-
- public void SelectVersion()
- {
- DialogResult = Version == SlicerFile.DefaultVersion ? DialogResults.Unknown : DialogResults.OK;
- Close();
- }
-
- public void SelectDefault()
- {
- DialogResult = DialogResults.Unknown;
- Close();
- }
+ DialogResult = DialogResults.Unknown;
+ Close();
}
-}
+} \ No newline at end of file
diff --git a/build/CreateRelease.WPF.ps1 b/build/CreateRelease.WPF.ps1
index 9cd4e88..a7a1eb2 100644
--- a/build/CreateRelease.WPF.ps1
+++ b/build/CreateRelease.WPF.ps1
@@ -26,66 +26,6 @@ class FixedEncoder : System.Text.UTF8Encoding {
}
}
-function WriteXmlToScreen([xml]$xml)
-{
- $StringWriter = New-Object System.IO.StringWriter;
- $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter;
- $XmlWriter.Formatting = "indented";
- $xml.WriteTo($XmlWriter);
- $XmlWriter.Flush();
- $StringWriter.Flush();
- Write-Output $StringWriter.ToString();
-}
-
-# Script working directory
-Set-Location $PSScriptRoot\..
-
-####################################
-### Configuration ###
-####################################
-$enableMSI = $true
-#$buildOnly = 'win-x64'
-#$buildOnly = 'linux-x64'
-#$buildOnly = 'osx-x64'
-$enableNugetPublish = $true
-# Profilling
-$stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
-$deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
-$stopWatch.Start()
-
-
-# Variables
-$software = "UVtools"
-$project = "UVtools.WPF"
-$buildWith = "Release"
-$netFolder = "net5.0"
-$rootFolder = $(Get-Location)
-$releaseFolder = "$project\bin\$buildWith\$netFolder"
-$objFolder = "$project\obj\$buildWith\$netFolder"
-$publishFolder = "publish"
-$platformsFolder = "UVtools.Platforms"
-
-# Not supported yet! No fuse on WSL
-$appImageFile = 'appimagetool-x86_64.AppImage'
-$appImageFilePath = "$platformsFolder/AppImage/$appImageFile"
-$appImageUrl = "https://github.com/AppImage/AppImageKit/releases/download/continuous/$appImageFile"
-
-$macIcns = "UVtools.CAD/UVtools.icns"
-
-#$version = (Get-Command "$releaseFolder\UVtools.dll").FileVersionInfo.ProductVersion
-$projectXml = [Xml] (Get-Content "$project\$project.csproj")
-$version = "$($projectXml.Project.PropertyGroup.Version)".Trim();
-if([string]::IsNullOrWhiteSpace($version)){
- Write-Error "Can not detect the UVtools version, does $project\$project.csproj exists?"
- exit
-}
-
-# MSI Variables
-$installers = @("UVtools.InstallerMM", "UVtools.Installer")
-$msiOutputFile = "$rootFolder\UVtools.Installer\bin\x64\Release\UVtools.msi"
-$msiComponentsFile = "$rootFolder\UVtools.InstallerMM\UVtools.InstallerMM.wxs"
-$msiSourceFiles = "$rootFolder\$publishFolder\win-x64"
-$msbuild = "`"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe`" /t:Build /p:Configuration=$buildWith /p:MSIProductVersion=$version"
function wixCleanUpElement([System.Xml.XmlElement]$element, [string]$rootPath)
{
@@ -220,6 +160,70 @@ foreach($element in $msiComponentsXml.Wix.Module.Directory.Directory)
}
#>
+function WriteXmlToScreen([xml]$xml)
+{
+ $StringWriter = New-Object System.IO.StringWriter;
+ $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter;
+ $XmlWriter.Formatting = "indented";
+ $xml.WriteTo($XmlWriter);
+ $XmlWriter.Flush();
+ $StringWriter.Flush();
+ Write-Output $StringWriter.ToString();
+}
+
+# Script working directory
+Set-Location $PSScriptRoot\..
+
+####################################
+### Configuration ###
+####################################
+$enableMSI = $true
+#$buildOnly = 'win-x64'
+#$buildOnly = 'linux-x64'
+#$buildOnly = 'osx-x64'
+#$buildOnly = 'osx-arm64'
+$zipPackages = $true
+#$enableNugetPublish = $true
+
+# Profilling
+$stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
+$deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
+$stopWatch.Start()
+
+
+# Variables
+$software = "UVtools"
+$project = "UVtools.WPF"
+$buildWith = "Release"
+$netFolder = "net6.0"
+$rootFolder = $(Get-Location)
+$releaseFolder = "$project\bin\$buildWith\$netFolder"
+$objFolder = "$project\obj\$buildWith\$netFolder"
+$publishFolder = "publish"
+$platformsFolder = "UVtools.Platforms"
+
+# Not supported yet! No fuse on WSL
+$appImageFile = 'appimagetool-x86_64.AppImage'
+$appImageFilePath = "$platformsFolder/AppImage/$appImageFile"
+$appImageUrl = "https://github.com/AppImage/AppImageKit/releases/download/continuous/$appImageFile"
+
+$macIcns = "UVtools.CAD/UVtools.icns"
+
+#$version = (Get-Command "$releaseFolder\UVtools.dll").FileVersionInfo.ProductVersion
+$projectXml = [Xml] (Get-Content "$project\$project.csproj")
+$version = "$($projectXml.Project.PropertyGroup.Version)".Trim();
+if([string]::IsNullOrWhiteSpace($version)){
+ Write-Error "Can not detect the UVtools version, does $project\$project.csproj exists?"
+ exit
+}
+
+# MSI Variables
+$installers = @("UVtools.InstallerMM", "UVtools.Installer")
+$msiOutputFile = "$rootFolder\UVtools.Installer\bin\x64\Release\UVtools.msi"
+$msiComponentsFile = "$rootFolder\UVtools.InstallerMM\UVtools.InstallerMM.wxs"
+$msiSourceFiles = "$rootFolder\$publishFolder\win-x64"
+$msbuild = "`"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe`" /t:Build /p:Configuration=$buildWith /p:MSIProductVersion=$version"
+
Write-Output "
####################################
### UVtools builder & deployer ###
@@ -239,36 +243,41 @@ Remove-Item $publishFolder -Recurse -ErrorAction Ignore # Clean
$runtimes =
@{
"win-x64" = @{
- "extraCmd" = "-p:PublishReadyToRun=true"
+ "extraCmd" = ""
"exclude" = @("UVtools.sh")
"include" = @()
}
"linux-x64" = @{
- "extraCmd" = "-p:PublishReadyToRun=true"
+ "extraCmd" = ""
"exclude" = @()
"include" = @("libcvextern.so")
}
"arch-x64" = @{
- "extraCmd" = "-p:PublishReadyToRun=true"
+ "extraCmd" = ""
"exclude" = @()
"include" = @("libcvextern.so")
}
"rhel-x64" = @{
- "extraCmd" = "-p:PublishReadyToRun=true"
+ "extraCmd" = ""
"exclude" = @()
"include" = @("libcvextern.so")
}
#"linux-arm64" = @{
- # "extraCmd" = "-p:PublishReadyToRun=true"
+ # "extraCmd" = ""
# "exclude" = @()
# "include" = @("libcvextern.so")
#}
#"unix-x64" = @{
- # "extraCmd" = "-p:PublishReadyToRun=true"
+ # "extraCmd" = ""
# "exclude" = @("x86", "x64", "libcvextern.dylib")
#}
"osx-x64" = @{
- "extraCmd" = "-p:PublishReadyToRun=true"
+ "extraCmd" = ""
+ "exclude" = @()
+ "include" = @("libcvextern.dylib")
+ }
+ "osx-arm64" = @{
+ "extraCmd" = ""
"exclude" = @()
"include" = @("libcvextern.dylib")
}
@@ -312,7 +321,7 @@ foreach ($obj in $runtimes.GetEnumerator()) {
# Deploy
Write-Output "################################
Building: $runtime"
- dotnet publish $project -o "$publishFolder/$runtime" -c $buildWith -r $runtime $extraCmd
+ dotnet publish $project -o "$publishFolder/$runtime" -c $buildWith -r $runtime -p:PublishReadyToRun=true --self-contained $extraCmd
New-Item "$publishFolder/$runtime/runtime_package.dat" -ItemType File -Value $runtime
if(!$runtime.Equals('win-x64'))
@@ -344,7 +353,7 @@ Building: $runtime"
# wsl cp -a "$publishFolder/$runtime/." "$appDirDest/usr/bin"
# wsl $appImageFilePath $appDirDest
#}
- if($runtime.Equals('osx-x64')){
+ if($runtime.StartsWith('osx-')){
$macAppFolder = "${software}.app"
$macPublishFolder = "$publishFolder/${macAppFolder}"
$macInfoplist = "$platformsFolder/$runtime/Info.plist"
@@ -360,15 +369,24 @@ Building: $runtime"
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 chmod +x "$macPublishFolder/Contents/MacOS/UVtools"
+
+ if($null -ne $zipPackages -and $zipPackages)
+ {
+ wsl cd "$publishFolder/" `&`& pwd `&`& zip -r "../$targetZip" "$macAppFolder/*"
+ #wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$macTargetZipLegacy" .
+ }
- wsl cd "$publishFolder/" `&`& pwd `&`& zip -r "../$targetZip" "$macAppFolder/*"
- #wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$macTargetZipLegacy" .
+ Rename-Item -Path $macPublishFolder -NewName "${macAppFolder}_${runtime}"
}
else {
- Write-Output "Compressing $runtime to: $targetZip"
- Write-Output $targetZip "$publishFolder/$runtime"
- wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$targetZip" .
+ if($null -ne $zipPackages -and $zipPackages)
+ {
+ Write-Output "Compressing $runtime to: $targetZip"
+ Write-Output $targetZip "$publishFolder/$runtime"
+ wsl cd "$publishFolder/$runtime" `&`& pwd `&`& zip -r "../../$targetZip" .
+ }
}
# Zip
@@ -448,4 +466,6 @@ Write-Output "
####################################
### Completed ###
####################################
-In: $($stopWatch.Elapsed)" \ No newline at end of file
+In: $($stopWatch.Elapsed)"
+
+