mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-25 18:06:12 +02:00
- **Core:**
- (Add) Machine presets and able to load machine collection from PrusaSlicer
- (Improvement) Core: Reference EmguCV runtimes into core instead of the UI project
- **File formats:**
- **CXDLP:**
- (Add) Detection support for Halot One Pro
- (Add) Detection support for Halot One Plus
- (Add) Detection support for Halot Sky Plus
- (Add) Detection support for Halot Lite
- (Improvement) Better handling and detection of printer model when converting
- (Improvement) Discovered more fields meanings on format
- (Fix) Exposure time in format is `round(time * 10, 1)`
- (Fix) Speeds in format are in mm/s, was using mm/min before
- (Add) JXS format for Uniformation GKone [Zip+GCode]
- (Improvement) Saving and converting files now handle the file backup on Core instead on the UI, which prevents scripts and other projects lose the original file in case of error while saving
- (Fix) After load files they was flagged as requiring a full encode, preventing fast save a fresh file
- **UVtoolsCmd:**
- Bring back the commandline project
- Consult README to see the available commands and syntax
- Old terminal commands on UVtools still works for now, but consider switch to UVtoolsCmd or redirect the command using `UVtools --cmd "commands"`
- **Tools:**
- **Change print resolution:**
- (Add) Allow to change the display size to match the new printer
- (Add) Machine presets to help set both resolution and display size to a correct printer and auto set fix pixel ratio
- (Improvement) Real pixel pitch fixer due new display size information, this allow full transfers between different printers "without" invalidating the model size
- (Improvement) Better arrangement of the layout
- (Add) Infill: Option "Reinforce infill if possible", it was always on before, now default is off and configurable
- (Improvement) Always allow to export settings from tools
- **GCode:**
- (Improvement) After print the last layer, do one lift with the same layer settings before attempt a fast move to top
- (Improvement) Use the highest defined speed to send the build plate to top after finish print
- (Improvement) Append a wait sync command in the end of gcode if needed
- (Fix) When lift without a retract it still output the motor sync delay for the retract time and the wait time after retract
- **PrusaSlicer:**
- (Add) Printer: Creality Halot One Pro CL-70
- (Add) Printer: Creality Halot One Plus CL-79
- (Add) Printer: Creality Halot Sky Plus CL-92
- (Add) Printer: Creality Halot Lite CL-89L
- (Add) Printer: Creality Halot Lite CL-89L
- (Add) Printer: Creality CT133 Pro
- (Add) Printer: Creality CT-005 Pro
- (Add) Printer: Uniformation GKone
- (Add) Printer: FlashForge Foto 8.9S
- (Add) Printer: Elegoo Mars 2
- (Improvement) Rename all Creality printers
- (Fix) Creality model in print notes
830 lines
34 KiB
C#
830 lines
34 KiB
C#
/*
|
|
* 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 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 UVtools.Core.Extensions;
|
|
using UVtools.Core.Layers;
|
|
using UVtools.Core.Operations;
|
|
|
|
namespace UVtools.Core.FileFormats;
|
|
|
|
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 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 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 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
|
|
}
|
|
#endregion
|
|
|
|
#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
|
|
|
|
#region Layers
|
|
|
|
public float InitialLayerHeight { get; set; }
|
|
#endregion
|
|
|
|
#region Exposure
|
|
|
|
public float ExposureTime { get; set; }
|
|
public float InitialExposureTime { get; set; }
|
|
#endregion
|
|
|
|
#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;
|
|
|
|
#endregion
|
|
|
|
#region Material print profile
|
|
|
|
public string MaterialPrintSpeed { get; set; } = "fast";
|
|
|
|
#endregion
|
|
|
|
#region Dependencies
|
|
|
|
public string CompatiblePrintersConditionCummulative { get; set; } = string.Empty;
|
|
public string CompatiblePrintsConditionCummulative { get; set; } = string.Empty;
|
|
|
|
#endregion
|
|
|
|
#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
|
|
}
|
|
#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 Supports
|
|
|
|
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; } = 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 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; } = string.Empty;
|
|
#endregion
|
|
|
|
#region Dependencies
|
|
public string CompatiblePrintsCondition { set; get; } = string.Empty;
|
|
#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
|
|
}
|
|
|
|
#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
|
|
{
|
|
//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}";
|
|
}
|
|
}
|
|
|
|
#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 Size[]? ThumbnailsOriginalSize { get; } =
|
|
{
|
|
new(400, 400),
|
|
new(800, 480)
|
|
};
|
|
|
|
public override uint ResolutionX
|
|
{
|
|
get => PrinterSettings.DisplayPixelsX;
|
|
set
|
|
{
|
|
PrinterSettings.DisplayPixelsX = value;
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override uint ResolutionY
|
|
{
|
|
get => PrinterSettings.DisplayPixelsY;
|
|
set
|
|
{
|
|
PrinterSettings.DisplayPixelsY = value;
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override float DisplayWidth
|
|
{
|
|
get => PrinterSettings.DisplayWidth;
|
|
set
|
|
{
|
|
PrinterSettings.DisplayWidth = (float)Math.Round(value, 2);
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override float DisplayHeight
|
|
{
|
|
get => PrinterSettings.DisplayHeight;
|
|
set
|
|
{
|
|
PrinterSettings.DisplayHeight = (float)Math.Round(value, 2);
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override float MachineZ
|
|
{
|
|
get => PrinterSettings.MaxPrintHeight;
|
|
set
|
|
{
|
|
PrinterSettings.MaxPrintHeight = (float)Math.Round(value, 2);
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override FlipDirection DisplayMirror
|
|
{
|
|
get
|
|
{
|
|
if (PrinterSettings.DisplayMirrorX > 0 && PrinterSettings.DisplayMirrorY > 0) return FlipDirection.Both;
|
|
if (PrinterSettings.DisplayMirrorX > 0) return FlipDirection.Horizontally;
|
|
if (PrinterSettings.DisplayMirrorY > 0) return FlipDirection.Vertically;
|
|
return FlipDirection.None;
|
|
}
|
|
set
|
|
{
|
|
PrinterSettings.DisplayMirrorX = (byte)(value is FlipDirection.Horizontally or FlipDirection.Both ? 1 : 0);
|
|
PrinterSettings.DisplayMirrorY = (byte)(value is FlipDirection.Vertically or 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 LayerHeight
|
|
{
|
|
get => OutputConfigSettings.LayerHeight;
|
|
set
|
|
{
|
|
OutputConfigSettings.LayerHeight = Layer.RoundHeight(value);
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override uint LayerCount
|
|
{
|
|
get => base.LayerCount;
|
|
set
|
|
{
|
|
OutputConfigSettings.NumSlow = 0;
|
|
base.LayerCount = OutputConfigSettings.NumFast = base.LayerCount;
|
|
}
|
|
}
|
|
|
|
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 ExposureTime
|
|
{
|
|
get => OutputConfigSettings.ExpTime;
|
|
set => base.ExposureTime = OutputConfigSettings.ExpTime = (float)Math.Round(value, 2);
|
|
}
|
|
|
|
public override float PrintTime
|
|
{
|
|
get => base.PrintTime;
|
|
set
|
|
{
|
|
base.PrintTime = value;
|
|
OutputConfigSettings.PrintTime = base.PrintTime;
|
|
}
|
|
}
|
|
|
|
public override float MaterialMilliliters
|
|
{
|
|
get => base.MaterialMilliliters;
|
|
set
|
|
{
|
|
base.MaterialMilliliters = value;
|
|
OutputConfigSettings.UsedMaterial = base.MaterialMilliliters;
|
|
}
|
|
}
|
|
|
|
public override float MaterialGrams => (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.MaterialDensity, 3);
|
|
|
|
public override float MaterialCost => MaterialSettings.BottleVolume > 0 ? (float) Math.Round(OutputConfigSettings.UsedMaterial * MaterialSettings.BottleCost / MaterialSettings.BottleVolume, 3) : 0;
|
|
|
|
public override string? MaterialName
|
|
{
|
|
get => OutputConfigSettings.MaterialName;
|
|
set => base.MaterialName = OutputConfigSettings.MaterialName = value;
|
|
}
|
|
|
|
public override string MachineName
|
|
{
|
|
get => PrinterSettings.PrinterSettingsId;
|
|
set => base.MachineName = PrinterSettings.PrinterSettingsId = value;
|
|
}
|
|
|
|
public override object[] Configs => new object[] { PrinterSettings, MaterialSettings, PrintSettings, OutputConfigSettings };
|
|
#endregion
|
|
|
|
#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
|
|
|
|
#region Contructors
|
|
public SL1File() { }
|
|
#endregion
|
|
|
|
#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
|
|
|
|
#region Methods
|
|
public override void Clear()
|
|
{
|
|
base.Clear();
|
|
Statistics.Clear();
|
|
}
|
|
|
|
protected override bool OnBeforeConvertTo(FileFormat output)
|
|
{
|
|
int fileVersion = LookupCustomValue(Keyword_FileVersion, int.MinValue);
|
|
if (fileVersion > 0)
|
|
{
|
|
output.Version = (uint)fileVersion;
|
|
}
|
|
|
|
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 var outputFile = ZipFile.Open(TemporaryOutputFileFullPath, ZipArchiveMode.Create);
|
|
var entry = outputFile.CreateEntry("config.ini");
|
|
using (TextWriter tw = new StreamWriter(entry.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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
EncodeLayersInZip(outputFile, filename, 5, IndexStartNumber.Zero, progress);
|
|
}
|
|
|
|
|
|
protected override void DecodeInternally(OperationProgress progress)
|
|
{
|
|
PrinterSettings = new Printer();
|
|
MaterialSettings = new Material();
|
|
PrintSettings = new Print();
|
|
OutputConfigSettings = new OutputConfig();
|
|
|
|
Statistics.ExecutionTime.Restart();
|
|
|
|
using var inputFile = ZipFile.OpenRead(FileFullPath!);
|
|
List<string> iniFiles = new();
|
|
foreach (var 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)
|
|
{
|
|
var keyValue = line.Split('=', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
if (keyValue.Length < 2) continue;
|
|
|
|
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]);
|
|
|
|
Statistics.ImplementedKeys.Add(keyValue[0]);
|
|
foundMember = true;
|
|
}
|
|
|
|
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.");
|
|
}
|
|
|
|
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);
|
|
|
|
BottomWaitTimeAfterCure = LookupCustomValue(Keyword_BottomWaitTimeAfterCure, 0f);
|
|
WaitTimeAfterCure = LookupCustomValue(Keyword_WaitTimeAfterCure, 0f);
|
|
|
|
BottomLiftHeight = LookupCustomValue(Keyword_BottomLiftHeight, DefaultBottomLiftHeight);
|
|
BottomLiftSpeed = LookupCustomValue(Keyword_BottomLiftSpeed, DefaultBottomLiftSpeed);
|
|
|
|
LiftHeight = LookupCustomValue(Keyword_LiftHeight, DefaultLiftHeight);
|
|
LiftSpeed = LookupCustomValue(Keyword_LiftSpeed, DefaultLiftSpeed);
|
|
|
|
BottomLiftHeight2 = LookupCustomValue(Keyword_BottomLiftHeight2, DefaultBottomLiftHeight2);
|
|
BottomLiftSpeed2 = LookupCustomValue(Keyword_BottomLiftSpeed2, DefaultBottomLiftSpeed2);
|
|
|
|
LiftHeight2 = LookupCustomValue(Keyword_LiftHeight2, DefaultLiftHeight2);
|
|
LiftSpeed2 = LookupCustomValue(Keyword_LiftSpeed2, DefaultLiftSpeed2);
|
|
|
|
BottomWaitTimeAfterLift = LookupCustomValue(Keyword_BottomWaitTimeAfterLift, 0f);
|
|
WaitTimeAfterLift = LookupCustomValue(Keyword_WaitTimeAfterLift, 0f);
|
|
|
|
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);
|
|
});
|
|
|
|
Init(OutputConfigSettings.NumSlow + OutputConfigSettings.NumFast, DecodeType == FileDecodeType.Partial);
|
|
|
|
progress.ItemCount = LayerCount;
|
|
|
|
foreach (var entity in inputFile.Entries)
|
|
{
|
|
if (!entity.Name.EndsWith(".png")) continue;
|
|
if (!entity.Name.StartsWith("thumbnail")) continue;
|
|
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++;
|
|
}
|
|
|
|
DecodeLayersFromZip(inputFile, 5, IndexStartNumber.Zero, progress);
|
|
|
|
if (TransitionLayerCount > 0)
|
|
{
|
|
SetTransitionLayers(TransitionLayerCount, false);
|
|
}
|
|
|
|
Statistics.ExecutionTime.Stop();
|
|
|
|
Debug.WriteLine(Statistics);
|
|
}
|
|
|
|
protected override void PartialSaveInternally(OperationProgress progress)
|
|
{
|
|
using var outputFile = ZipFile.Open(TemporaryOutputFileFullPath, 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();
|
|
}
|
|
|
|
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);
|
|
|
|
foreach (var property in properties)
|
|
{
|
|
if (property.Name.Equals("Item")) continue;
|
|
tw.WriteLine($"{MemberNameToIniKey(property.Name)} = {property.GetValue(config)}");
|
|
}
|
|
}
|
|
|
|
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 += '_';
|
|
|
|
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);
|
|
|
|
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 (typeof(T) == typeof(string))
|
|
{
|
|
if (char.IsWhiteSpace(c)) break;
|
|
}
|
|
else
|
|
{
|
|
if (!char.IsLetterOrDigit(c) && c != '.')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
result += c;
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(result)) break; // Found a candidate
|
|
}
|
|
|
|
return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>();
|
|
}
|
|
|
|
#endregion
|
|
} |