Files
UVtools/UVtools.Core/FileFormats/SL1File.cs
T
Tiago Conceição 22897ea389 v3.1.0
- **Benchmark:**
   - (Add) PNG, GZip, Deflate and LZ4 compress tests
   - (Change) Test against a known image instead of random noise
   - (Change) Single-thread tests from 100 to 200 and multi-thread tests from 1000 to 5000
   - (Improvement) Same image instance is shared between tests instead of create new per test
   - (Fix) Encode typo
- **Core:**
   - (Add) Layer compression method: Allow to choose the compression method for layer image
      - **PNG:** Compression=High Speed=Slow (Use with low RAM)
      - **GZip:** Compression=Medium Speed=Medium (Optimal)
      - **Deflate:** Compression=Medium Speed=Medium (Optimal)
      - **LZ4:** Compression=Low Speed=Fast (Use with high RAM)
   - (Improvement) Better handling on cancel operations and more immediate response
   - (Fix) Extract: Zip Slip Vulnerability (CWE-22)
- **File formats:**
   - (Improvement) Better handling of encode/decoding layers from zip files
   - (Fix) ZCode: Canceling the file load can trigger an error
   - (Fix) VDA: Unable to open vda zip files
- **Tools:**
   - (Improvement) Allow operations to be aware of ROI and Masks before execution (#436)
   - (Improvement) Scripting: Allow save and load profiles (#436)
   - (Fix) Adjust layer height: When using the Offset type the last layer in the range was not taken in account (#435)
- **UI:**
   - (Improvement) Allow layer zoom levels of 0.1x and 64x but constrain minimum zoom to the level of image fit
   - (Improvement) Update change log now shows with markdown style and more readable
   - (Fix) Windows MSI upgrade to this version (#432)
   - (Fix) Auto-updater for Mac ARM, was downloading x64 instead
2022-03-17 23:50:47 +00:00

832 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 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();
}
}
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 ZipArchive outputFile = ZipFile.Open(FileFullPath!, 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, Enumerations.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)
{
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;
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, Enumerations.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(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();
}
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
}