diff --git a/CHANGELOG.md b/CHANGELOG.md index 952b580..263e3c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ # Changelog -## ?/08/2020 - v0.7.1.0 +## ?/08/2020 - v0.8.0.0 +* (Add) LGS and LGS30 file format for Longer Orange 10 and 30 (ezrec/uv3dp#105) +* (Add) CWS: Support the GRAY2RGB and RBG2GRAY encoding for Bene Mono +* (Add) PrusaSlicer Printer: Nova Bene4 Mono * (Add) Layer importer tool (#37) * (Add) Settings & Issues: Enable or disable Empty Layers -* (Add) PrusaSlicer Printer: Nova Bene4 Mono -* (Add) CWS: Support the GRAY2RGB and RBG2GRAY encoding for Bene Mono * (Add) Layer issue Z map paired with layer navigation tracker bar * (Add) Setting: Pixel editor can be configured to exit after each apply operation (#45) * (Add) More abstraction on GUI and operations diff --git a/UVtools.Core/Extensions/FileStreamExtensions.cs b/UVtools.Core/Extensions/FileStreamExtensions.cs index d772033..4e13028 100644 --- a/UVtools.Core/Extensions/FileStreamExtensions.cs +++ b/UVtools.Core/Extensions/FileStreamExtensions.cs @@ -33,5 +33,10 @@ namespace UVtools.Core.Extensions stream.Write(bytes, offset, bytes.Length); return (uint)bytes.Length; } + + public static uint WriteSerialize(this FileStream fs, object data, int offset = 0) + { + return Helpers.SerializeWriteFileStream(fs, data, offset); + } } } diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index a99df69..cb30a88 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -144,6 +144,7 @@ namespace UVtools.Core.FileFormats new ZCodexFile(), // zcodex new CWSFile(), // CWS //new MakerbaseFile(), // MKS + new LGSFile(), // LGS, LGS30 new UVJFile(), // UVJ new ImageFile(), // images }; diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs new file mode 100644 index 0000000..b359e02 --- /dev/null +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -0,0 +1,889 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Threading.Tasks; +using BinarySerialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.FileFormats +{ + public class LGSFile : FileFormat + { + #region Sub Classes + + #region Header + + public class Header + { + public const string NameValue = "Longer3D"; + //[FieldOrder(0)] public uint Offset1 { get; set; } + + /// + /// Gets the file tag = MKSDLP + /// + [FieldOrder(0)] [FieldLength(8)] public string Name { get; set; } = NameValue; // 0x00: + [FieldOrder(1)] public uint Uint_08 { get; set; } = 4278190081; // 0x08: 0xff000001 ? + [FieldOrder(2)] public uint Uint_0c { get; set; } = 1; // 0x0c: 1 ? + [FieldOrder(3)] public uint Uint_10 { get; set; } = 10; // 0x10: 10 ? + [FieldOrder(4)] public uint Uint_14 { get; set; } = 0; // 0x14: 0 ? + [FieldOrder(5)] public uint Uint_18 { get; set; } = 34; // 0x18: 34 ? + [FieldOrder(6)] public float PixelPerMmY { get; set; } + [FieldOrder(7)] public float PixelPerMmX { get; set; } + [FieldOrder(8)] public float ResolutionY { get; set; } + [FieldOrder(9)] public float ResolutionX { 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 Float_94 { get; set; } = 140;// 0x94: 140 for Longer 10, 170 for Longer 30? + [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 + + #region LayerData + + public class LayerData + { + [Ignore] public LGSFile Parent { get; set; } + + [FieldOrder(0)] + public uint DataSize { get; set; } + + [FieldOrder(1)] + [FieldLength(nameof(DataSize))] + public byte[] EncodedRle { get; set; } + + public byte[] Encode(Mat mat) + { + List rawData = new List(); + + var spanMat = mat.GetPixelSpan(); + + uint span = 0; + byte lc = 0; + + void addSpan(){ + for(; span > 0; span >>= 4) { + rawData.Add((byte) ((byte)(span & 0xf) | (lc & 0xf0))); + } + } + + for (int i = 0; i < spanMat.Length; i++) + { + byte c = (byte) (spanMat[i] & 0xf0); + + if (c == lc) + { + span++; + } + else + { + addSpan(); + span = 1; + + } + + lc = c; + } + + addSpan(); + + EncodedRle = rawData.ToArray(); + DataSize = (uint) EncodedRle.Length; + return EncodedRle; + } + + public Mat Decode(bool consumeRle = true) + { + Mat mat = new Mat(new Size((int)Parent.HeaderSettings.ResolutionX, (int) Parent.HeaderSettings.ResolutionY), DepthType.Cv8U, 1); + var matSpan = mat.GetPixelSpan(); + + byte last = 0; + int span = 0; + int index = 0; + + foreach (var b in EncodedRle) + { + byte color = (byte) ((b & 0xf0) | (b >> 4)); + + if (color == last) + { + span = (span << 4) | (b & 0xf); + } + else + { + for(; span > 0; span--) + { + if (index >= matSpan.Length) + { + throw new FileLoadException($"'{span}' bytes to many"); + } + + matSpan[index++] = last; + } + + span = b & 0xf; + + } + + last = color; + } + + for (; span > 0; span--) + { + if (index >= matSpan.Length) + { + throw new FileLoadException($"'{span}' bytes to many"); + } + + matSpan[index++] = last; + } + + if (index != matSpan.Length) + { + throw new FileLoadException($"Incomplete buffer, expected: {matSpan.Length}, got: {index}"); + } + + + if (consumeRle) + EncodedRle = null; + + return mat; + } + } + #endregion + + #endregion + + #region Properties + + public Header HeaderSettings { get; protected internal set; } = new Header(); + public override FileFormatType FileType => FileFormatType.Binary; + + public override FileExtension[] FileExtensions { get; } = { + new FileExtension("lgs", "Longer Orange 10 Files"), + new FileExtension("lgs30", "Longer Orange 30 Files"), + }; + + public override Type[] ConvertToFormats { get; } = + { + typeof(UVJFile), + }; + + public override PrintParameterModifier[] PrintParameterModifiers { get; } = + { + PrintParameterModifier.InitialLayerCount, + PrintParameterModifier.InitialExposureSeconds, + PrintParameterModifier.ExposureSeconds, + + PrintParameterModifier.BottomLayerOffTime, + PrintParameterModifier.LayerOffTime, + PrintParameterModifier.BottomLiftHeight, + PrintParameterModifier.BottomLiftSpeed, + PrintParameterModifier.LiftHeight, + PrintParameterModifier.LiftSpeed, + //PrintParameterModifier.RetractSpeed, + + //PrintParameterModifier.BottomLightPWM, + //PrintParameterModifier.LightPWM, + }; + + public override byte ThumbnailsCount { get; } = 1; + + public override Size[] ThumbnailsOriginalSize { get; } = {new Size(120, 150)}; + + public override uint ResolutionX + { + get => (uint) HeaderSettings.ResolutionX; + set => HeaderSettings.ResolutionX = value; + } + + public override uint ResolutionY + { + get => (uint)HeaderSettings.ResolutionY; + set => HeaderSettings.ResolutionX = value; + } + + public override byte AntiAliasing => 4; + + public override float LayerHeight + { + get => HeaderSettings.LayerHeight; + set => HeaderSettings.LayerHeight = value; + } + + public override uint LayerCount + { + set => HeaderSettings.LayerCount = LayerCount; + } + + public override ushort InitialLayerCount => (ushort) (HeaderSettings.BottomHeight / LayerHeight); + + public override float InitialExposureTime => (float) Math.Round(HeaderSettings.BottomExposureTimeMs/1000, 2); + + public override float LayerExposureTime => (float) Math.Round(HeaderSettings.ExposureTimeMs/1000, 2); + public override float LiftHeight => HeaderSettings.LiftHeight; + public override float LiftSpeed => HeaderSettings.LiftSpeed; + public override float RetractSpeed => 0; + + public override float PrintTime => 0; + + public override float UsedMaterial => 0; + + public override float MaterialCost => 0; + + public override string MaterialName => "Unknown"; + public override string MachineName => null; + + public override object[] Configs => new object[] { HeaderSettings }; + + #endregion + + #region Constructors + public LGSFile() + { + } + #endregion + + #region Methods + + public byte[] PreviewEncode(Mat mat) + { + var span = mat.GetPixelSpan(); + byte[] bytes = new byte[mat.Width*mat.Height*2]; + + int index = 0; + for (int i = 0; i < span.Length; i+=3) + { + byte b = span[i]; + byte g = span[i+1]; + byte r = span[i+2]; + + ushort rgb15 = (ushort) (((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 0)); + + bytes[index++] = (byte) (rgb15 >> 8); + bytes[index++] = (byte) (rgb15 & 0xff); + } + + return bytes; + } + public override void Encode(string fileFullPath, OperationProgress progress = null) + { + base.Encode(fileFullPath, progress); + + if (ResolutionY >= 2560) // Longer Orange 30 + { + HeaderSettings.Float_94 = 170; + } + + //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); + using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) + { + outputFile.WriteSerialize(HeaderSettings); + outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); + + LayerData[] layerData = new LayerData[LayerCount]; + + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) + { + layerData[layerIndex] = new LayerData(); + layerData[layerIndex].Encode(mat); + } + + lock (progress.Mutex) + { + progress++; + } + }); + + progress.ItemName = "Saving layers"; + progress.ProcessedItems = 0; + + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + outputFile.WriteSerialize(layerData[layerIndex]); + progress++; + } + } + + AfterEncode(); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine("-End-"); + } + + public Mat PreviewDecode(byte []data) + { + Mat mat = new Mat((int) HeaderSettings.PreviewSizeY, (int)HeaderSettings.PreviewSizeX, DepthType.Cv8U, 3); + var span = mat.GetPixelSpan(); + int spanIndex = 0; + for (int i = 0; i < data.Length; i+=2) + { + ushort rgb15 = (ushort) ((ushort)(data[i + 0] << 8) | data[i + 1]); + byte r = (byte) ((((rgb15 >> 11) & 0x1f) << 3) | 0x7); + byte g = (byte) ((((rgb15 >> 6) & 0x1f) << 3) | 0x7); + byte b = (byte) ((((rgb15 >> 0) & 0x1f) << 3) | 0x7); + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + return mat; + } + + public override void Decode(string fileFullPath, OperationProgress progress = null) + { + base.Decode(fileFullPath, progress); + + using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) + { + //HeaderSettings = Helpers.ByteToType(InputFile); + //HeaderSettings = Helpers.Serializer.Deserialize
(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); + HeaderSettings = Helpers.Deserialize
(inputFile); + if (HeaderSettings.Name != Header.NameValue) + { + throw new FileLoadException("Not a valid LGS file!", fileFullPath); + } + + int previewSize = (int) (HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2); + byte[] previewData = new byte[previewSize]; + + + uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings); + currentOffset += inputFile.ReadBytes(previewData); + Thumbnails[0] = PreviewDecode(previewData); + + + LayerData[] layerData = new LayerData[HeaderSettings.LayerCount]; + progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount); + + for (int layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerData[layerIndex] = Helpers.Deserialize(inputFile); + layerData[layerIndex].Parent = this; + } + + LayerManager = new LayerManager(HeaderSettings.LayerCount, this); + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + + using (var image = layerData[layerIndex].Decode()) + { + this[layerIndex] = new Layer((uint) layerIndex, image); + + lock (progress.Mutex) + { + progress++; + } + } + }); + + LayerManager.RebuildLayersProperties(); + + + FileFullPath = fileFullPath; + + } + + progress.Token.ThrowIfCancellationRequested(); + } + + public override object GetValueFromPrintParameterModifier(PrintParameterModifier modifier) + { + var baseValue = base.GetValueFromPrintParameterModifier(modifier); + if (!ReferenceEquals(baseValue, null)) return baseValue; + /*if (ReferenceEquals(modifier, PrintParameterModifier.BottomLayerOffTime)) return PrintParametersSettings.BottomLightOffDelay; + if (ReferenceEquals(modifier, PrintParameterModifier.LayerOffTime)) return PrintParametersSettings.LightOffDelay; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight)) return PrintParametersSettings.BottomLiftHeight; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed)) return PrintParametersSettings.BottomLiftSpeed;*/ + /*if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight)) return PrintParametersSettings.LiftHeight; + if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) return PrintParametersSettings.LiftingSpeed; + if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) return PrintParametersSettings.RetractSpeed;*/ + + /*if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) return HeaderSettings.BottomLightPWM; + if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) return HeaderSettings.LightPWM;*/ + + + + return null; + } + + public override bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value) + { + return false; + } + + public override void SaveAs(string filePath = null, OperationProgress progress = null) + { + if (RequireFullEncode) + { + if (!string.IsNullOrEmpty(filePath)) + { + FileFullPath = filePath; + } + Encode(FileFullPath, progress); + return; + } + + + if (!string.IsNullOrEmpty(filePath)) + { + File.Copy(FileFullPath, filePath, true); + FileFullPath = filePath; + } + + /*using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) + { + + outputFile.Seek(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 < HeaderSettings.LayerCount; layerIndex++) + { + outputFile.Seek(layerOffset, SeekOrigin.Begin); + layerOffset += Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[aaIndex, layerIndex]); + } + } + }*/ + + //Decode(FileFullPath, progress); + } + + public override bool Convert(Type to, string fileFullPath, OperationProgress progress = null) + { + /*if (to == typeof(ChituboxFile)) + { + if (Path.GetExtension(FileFullPath).Equals(Path.GetExtension(fileFullPath))) + { + return false; + } + ChituboxFile file = new ChituboxFile + { + LayerManager = LayerManager, + HeaderSettings = + { + ResolutionX = ResolutionX, + ResolutionY = ResolutionY, + BedSizeX = HeaderSettings.BedSizeX, + BedSizeY = HeaderSettings.BedSizeY, + BedSizeZ = HeaderSettings.BedSizeZ, + ProjectorType = HeaderSettings.ProjectorType, + LayerCount = LayerCount, + AntiAliasLevel = ValidateAntiAliasingLevel(), + BottomLightPWM = (byte) HeaderSettings.BottomLightPWM, + LightPWM = (byte) HeaderSettings.LightPWM, + LayerOffTime = HeaderSettings.LayerOffTime, + PrintTime = HeaderSettings.PrintTime, + BottomExposureSeconds = HeaderSettings.BottomExposureSeconds, + BottomLayersCount = HeaderSettings.BottomLayersCount, + //EncryptionKey = HeaderSettings.EncryptionKey, + LayerExposureSeconds = HeaderSettings.LayerExposureSeconds, + LayerHeightMilimeter = HeaderSettings.LayerHeightMilimeter, + OverallHeightMilimeter = HeaderSettings.OverallHeightMilimeter, + }, + PrintParametersSettings = + { + LiftSpeed = PrintParametersSettings.LiftSpeed, + LiftHeight = PrintParametersSettings.LiftHeight, + BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed, + RetractSpeed = PrintParametersSettings.RetractSpeed, + BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay, + LightOffDelay = PrintParametersSettings.BottomLightOffDelay, + BottomLayerCount = PrintParametersSettings.BottomLayerCount, + VolumeMl = PrintParametersSettings.VolumeMl, + BottomLiftHeight = PrintParametersSettings.BottomLiftHeight, + CostDollars = PrintParametersSettings.CostDollars, + WeightG = PrintParametersSettings.WeightG + }, + SlicerInfoSettings = + { + AntiAliasLevel = SlicerInfoSettings.AntiAliasLevel, + MachineName = SlicerInfoSettings.MachineName, + //EncryptionMode = SlicerInfoSettings.EncryptionMode, + MachineNameSize = SlicerInfoSettings.MachineNameSize, + }, + Thumbnails = Thumbnails, + }; + + //file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + } + + if (to == typeof(ChituboxZipFile)) + { + ChituboxZipFile file = new ChituboxZipFile + { + LayerManager = LayerManager, + HeaderSettings = + { + Filename = Path.GetFileName(FileFullPath), + + ResolutionX = ResolutionX, + ResolutionY = ResolutionY, + MachineX = HeaderSettings.BedSizeX, + MachineY = HeaderSettings.BedSizeY, + MachineZ = HeaderSettings.BedSizeZ, + MachineType = MachineName, + ProjectType = HeaderSettings.ProjectorType == 0 ? "Normal" : "LCD_mirror", + + Resin = MaterialName, + Price = MaterialCost, + Weight = PrintParametersSettings.WeightG, + Volume = UsedMaterial, + Mirror = (byte) (HeaderSettings.ProjectorType == 0 ? 0 : 1), + + + BottomLiftHeight = PrintParametersSettings.BottomLiftHeight, + LiftHeight = PrintParametersSettings.LiftHeight, + BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed, + LiftSpeed = PrintParametersSettings.LiftSpeed, + RetractSpeed = PrintParametersSettings.RetractSpeed, + BottomLayCount = InitialLayerCount, + BottomLayerCount = InitialLayerCount, + BottomLightOffTime = PrintParametersSettings.BottomLightOffDelay, + LightOffTime = PrintParametersSettings.LightOffDelay, + BottomLayExposureTime = InitialExposureTime, + BottomLayerExposureTime = InitialExposureTime, + LayerExposureTime = LayerExposureTime, + LayerHeight = LayerHeight, + LayerCount = LayerCount, + AntiAliasing = ValidateAntiAliasingLevel(), + BottomLightPWM = (byte) HeaderSettings.BottomLightPWM, + LayerLightPWM = (byte) HeaderSettings.LightPWM, + + EstimatedPrintTime = PrintTime + }, + }; + + file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + } + + if (to == typeof(PWSFile)) + { + PWSFile file = new PWSFile + { + LayerManager = LayerManager, + HeaderSettings = + { + ResolutionX = ResolutionX, + ResolutionY = ResolutionY, + LayerHeight = LayerHeight, + LayerExposureTime = LayerExposureTime, + LiftHeight = LiftHeight, + LiftSpeed = LiftSpeed / 60, + RetractSpeed = RetractSpeed / 60, + LayerOffTime = HeaderSettings.LayerOffTime, + BottomLayersCount = InitialLayerCount, + BottomExposureSeconds = InitialExposureTime, + Price = MaterialCost, + Volume = UsedMaterial, + Weight = PrintParametersSettings.WeightG, + AntiAliasing = ValidateAntiAliasingLevel() + } + }; + + file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + } + + if (to == typeof(PHZFile)) + { + PHZFile file = new PHZFile + { + LayerManager = LayerManager, + HeaderSettings = + { + Version = 2, + BedSizeX = HeaderSettings.BedSizeX, + BedSizeY = HeaderSettings.BedSizeY, + BedSizeZ = HeaderSettings.BedSizeZ, + OverallHeightMilimeter = TotalHeight, + BottomExposureSeconds = InitialExposureTime, + BottomLayersCount = InitialLayerCount, + BottomLightPWM = HeaderSettings.BottomLightPWM, + LayerCount = LayerCount, + LayerExposureSeconds = LayerExposureTime, + LayerHeightMilimeter = LayerHeight, + LayerOffTime = HeaderSettings.LayerOffTime, + LightPWM = HeaderSettings.LightPWM, + PrintTime = HeaderSettings.PrintTime, + ProjectorType = HeaderSettings.ProjectorType, + ResolutionX = ResolutionX, + ResolutionY = ResolutionY, + BottomLayerCount = InitialLayerCount, + BottomLiftHeight = PrintParametersSettings.BottomLiftHeight, + BottomLiftSpeed = PrintParametersSettings.BottomLiftSpeed, + BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay, + CostDollars = MaterialCost, + LiftHeight = PrintParametersSettings.LiftHeight, + LiftSpeed = PrintParametersSettings.LiftSpeed, + RetractSpeed = PrintParametersSettings.RetractSpeed, + VolumeMl = UsedMaterial, + AntiAliasLevelInfo = ValidateAntiAliasingLevel(), + WeightG = PrintParametersSettings.WeightG, + MachineName = MachineName, + MachineNameSize = (uint)MachineName.Length + } + }; + + + + file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + } + + if (to == typeof(ZCodexFile)) + { + TimeSpan ts = new TimeSpan(0, 0, (int)PrintTime); + ZCodexFile file = new ZCodexFile + { + ResinMetadataSettings = new ZCodexFile.ResinMetadata + { + MaterialId = 2, + Material = MaterialName, + AdditionalSupportLayerTime = 0, + BottomLayersNumber = InitialLayerCount, + BottomLayersTime = (uint)(InitialExposureTime * 1000), + LayerTime = (uint)(LayerExposureTime * 1000), + DisableSettingsChanges = false, + LayerThickness = LayerHeight, + PrintTime = (uint)PrintTime, + TotalLayersCount = LayerCount, + TotalMaterialVolumeUsed = UsedMaterial, + TotalMaterialWeightUsed = UsedMaterial, + }, + UserSettings = new ZCodexFile.UserSettingsdata + { + Printer = MachineName, + BottomLayersCount = InitialLayerCount, + PrintTime = $"{ts.Hours}h {ts.Minutes}m", + LayerExposureTime = (uint)(LayerExposureTime * 1000), + BottomLayerExposureTime = (uint)(InitialExposureTime * 1000), + MaterialId = 2, + LayerThickness = $"{LayerHeight} mm", + AntiAliasing = (byte)(ValidateAntiAliasingLevel() > 1 ? 1 : 0), + CrossSupportEnabled = 1, + ExposureOffTime = (uint) HeaderSettings.LayerOffTime, + HollowEnabled = 0, + HollowThickness = 0, + InfillDensity = 0, + IsAdvanced = 0, + MaterialType = MaterialName, + MaterialVolume = UsedMaterial, + MaxLayer = LayerCount - 1, + ModelLiftEnabled = 0, + ModelLiftHeight = 0, + RaftEnabled = 0, + RaftHeight = 0, + RaftOffset = 0, + SupportAdditionalExposureEnabled = 0, + SupportAdditionalExposureTime = 0, + XCorrection = 0, + YCorrection = 0, + ZLiftDistance = PrintParametersSettings.LiftHeight, + ZLiftFeedRate = PrintParametersSettings.LiftSpeed, + ZLiftRetractRate = PrintParametersSettings.RetractSpeed, + }, + ZCodeMetadataSettings = new ZCodexFile.ZCodeMetadata + { + PrintTime = (uint)PrintTime, + PrinterName = MachineName, + Materials = new List + { + new ZCodexFile.ZCodeMetadata.MaterialsData + { + Name = MaterialName, + ExtruderType = "MAIN", + Id = 0, + Usage = 0, + Temperature = 0 + } + }, + }, + LayerManager = LayerManager + }; + + float usedMaterial = UsedMaterial / LayerCount; + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + file.ResinMetadataSettings.Layers.Add(new ZCodexFile.ResinMetadata.LayerData + { + Layer = layerIndex, + UsedMaterialVolume = usedMaterial + }); + } + + file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + return true; + } + + if (to == typeof(CWSFile)) + { + CWSFile defaultFormat = (CWSFile)FindByType(typeof(CWSFile)); + CWSFile file = new CWSFile { LayerManager = LayerManager }; + + file.SliceSettings.Xppm = file.OutputSettings.PixPermmX = (float)Math.Round(ResolutionX / HeaderSettings.BedSizeX, 3); + file.SliceSettings.Yppm = file.OutputSettings.PixPermmY = (float)Math.Round(ResolutionY / HeaderSettings.BedSizeY, 3); + file.SliceSettings.Xres = file.OutputSettings.XResolution = (ushort)ResolutionX; + file.SliceSettings.Yres = file.OutputSettings.YResolution = (ushort)ResolutionY; + file.SliceSettings.Thickness = file.OutputSettings.LayerThickness = LayerHeight; + file.SliceSettings.LayersNum = file.OutputSettings.LayersNum = LayerCount; + file.SliceSettings.HeadLayersNum = file.OutputSettings.NumberBottomLayers = InitialLayerCount; + file.SliceSettings.LayersExpoMs = file.OutputSettings.LayerTime = (uint)LayerExposureTime * 1000; + file.SliceSettings.HeadLayersExpoMs = file.OutputSettings.BottomLayersTime = (uint)InitialExposureTime * 1000; + file.SliceSettings.WaitBeforeExpoMs = (uint)(PrintParametersSettings.LightOffDelay * 1000); + file.SliceSettings.LiftDistance = file.OutputSettings.LiftDistance = LiftHeight; + file.SliceSettings.LiftUpSpeed = file.OutputSettings.ZLiftFeedRate = LiftSpeed; + file.SliceSettings.LiftDownSpeed = file.OutputSettings.ZLiftRetractRate = RetractSpeed; + file.SliceSettings.LiftWhenFinished = defaultFormat.SliceSettings.LiftWhenFinished; + + file.OutputSettings.BlankingLayerTime = (uint) (PrintParametersSettings.LightOffDelay * 1000); + //file.OutputSettings.RenderOutlines = false; + //file.OutputSettings.OutlineWidthInset = 0; + //file.OutputSettings.OutlineWidthOutset = 0; + file.OutputSettings.RenderOutlines = false; + //file.OutputSettings.TiltValue = 0; + //file.OutputSettings.UseMainliftGCodeTab = false; + //file.OutputSettings.AntiAliasing = 0; + //file.OutputSettings.AntiAliasingValue = 0; + file.OutputSettings.FlipX = HeaderSettings.ProjectorType != 0; + file.OutputSettings.FlipY = file.OutputSettings.FlipX; + file.OutputSettings.AntiAliasingValue = ValidateAntiAliasingLevel(); + file.OutputSettings.AntiAliasing = file.OutputSettings.AntiAliasingValue > 1; + + file.Encode(fileFullPath, progress); + + return true; + } + + /*if (to == typeof(UVJFile)) + { + UVJFile file = new UVJFile + { + LayerManager = LayerManager, + JsonSettings = new UVJFile.Settings + { + Properties = new UVJFile.Properties + { + Size = new UVJFile.Size + { + X = (ushort)ResolutionX, + Y = (ushort)ResolutionY, + Millimeter = new UVJFile.Millimeter + { + X = HeaderSettings.BedSizeX, + Y = HeaderSettings.BedSizeY, + }, + LayerHeight = LayerHeight, + Layers = LayerCount + }, + Bottom = new UVJFile.Bottom + { + LiftHeight = PrintParametersSettings.BottomLiftHeight, + LiftSpeed = PrintParametersSettings.BottomLiftSpeed, + LightOnTime = InitialExposureTime, + LightOffTime = PrintParametersSettings.BottomLightOffDelay, + LightPWM = (byte) HeaderSettings.BottomLightPWM, + RetractSpeed = PrintParametersSettings.RetractSpeed, + Count = InitialLayerCount + //RetractHeight = LookupCustomValue(Keyword_LiftHeight, defaultFormat.JsonSettings.Properties.Bottom.RetractHeight), + }, + Exposure = new UVJFile.Exposure + { + LiftHeight = PrintParametersSettings.LiftHeight, + LiftSpeed = PrintParametersSettings.LiftSpeed, + LightOnTime = LayerExposureTime, + LightOffTime = PrintParametersSettings.LightOffDelay, + LightPWM = (byte) HeaderSettings.LightPWM, + RetractSpeed = PrintParametersSettings.RetractSpeed, + }, + AntiAliasLevel = ValidateAntiAliasingLevel() + } + } + }; + + file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + }*/ + + return false; + } + #endregion + } +} diff --git a/UVtools.Core/FileFormats/MakerbaseFile.cs b/UVtools.Core/FileFormats/MakerbaseFile.cs index 9f86a6b..84fcb65 100644 --- a/UVtools.Core/FileFormats/MakerbaseFile.cs +++ b/UVtools.Core/FileFormats/MakerbaseFile.cs @@ -9,17 +9,10 @@ // https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc using System; -using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; -using System.Linq; -using System.Net.Http.Headers; -using System.Threading.Tasks; using BinarySerialization; -using Emgu.CV; -using Emgu.CV.CvEnum; -using UVtools.Core.Extensions; using UVtools.Core.Operations; namespace UVtools.Core.FileFormats diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 1e11ef1..cbc019e 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -300,6 +300,7 @@ namespace UVtools.Core.FileFormats typeof(PHZFile), typeof(ZCodexFile), typeof(CWSFile), + typeof(LGSFile), typeof(UVJFile), }; @@ -1049,6 +1050,47 @@ namespace UVtools.Core.FileFormats return true; } + if (to == typeof(LGSFile)) + { + LGSFile defaultFormat = (LGSFile)FindByType(typeof(LGSFile)); + LGSFile file = new LGSFile + { + LayerManager = LayerManager, + HeaderSettings = + { + ResolutionX = ResolutionX, + ResolutionY = ResolutionY, + LayerHeight = LayerHeight, + ExposureTimeMs = LayerExposureTime * 1000, + LiftHeight = LookupCustomValue(Keyword_LiftHeight, defaultFormat.HeaderSettings.LiftHeight), + LiftSpeed = LookupCustomValue(Keyword_LiftSpeed, defaultFormat.HeaderSettings.LiftSpeed) / 60, + LiftSpeed_ = LookupCustomValue(Keyword_LiftSpeed, defaultFormat.HeaderSettings.LiftSpeed) / 60, + LightOffDelayMs = LookupCustomValue(Keyword_LayerOffTime, defaultFormat.HeaderSettings.LightOffDelayMs), + BottomHeight = InitialLayerCount * LayerHeight, + BottomExposureTimeMs = InitialExposureTime * 1000, + LayerCount = LayerCount, + BottomLiftSpeed = LookupCustomValue(Keyword_BottomLiftSpeed, defaultFormat.HeaderSettings.BottomLiftSpeed), + BottomLiftSpeed_ = LookupCustomValue(Keyword_BottomLiftSpeed, defaultFormat.HeaderSettings.BottomLiftSpeed_), + BottomLiftHeight = LookupCustomValue(Keyword_BottomLiftHeight, defaultFormat.HeaderSettings.BottomLiftHeight), + BottomLightOffDelayMs = LookupCustomValue(Keyword_BottomLightOffDelay, defaultFormat.HeaderSettings.BottomLightOffDelayMs), + PixelPerMmX = (float) Math.Round(ResolutionY / PrinterSettings.DisplayWidth, 3), + PixelPerMmY = (float) Math.Round(ResolutionX / PrinterSettings.DisplayHeight, 3), + } + }; + + + if (LookupCustomValue("FLIP_XY", false, true)) + { + file.HeaderSettings.ResolutionX = PrinterSettings.DisplayPixelsY; + file.HeaderSettings.ResolutionY = PrinterSettings.DisplayPixelsX; + } + + file.SetThumbnails(Thumbnails); + file.Encode(fileFullPath, progress); + + return true; + } + if (to == typeof(UVJFile)) { UVJFile defaultFormat = (UVJFile)FindByType(typeof(UVJFile)); diff --git a/UVtools.Core/Helpers.cs b/UVtools.Core/Helpers.cs index b35a197..42d1f35 100644 --- a/UVtools.Core/Helpers.cs +++ b/UVtools.Core/Helpers.cs @@ -41,7 +41,7 @@ namespace UVtools.Core public static uint SerializeWriteFileStream(FileStream fs, object value, int offset = 0) { - using (MemoryStream stream = Helpers.Serialize(value)) + using (MemoryStream stream = Serialize(value)) { return fs.WriteStream(stream, offset); } diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index d4cd898..14ce9d4 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -167,12 +167,12 @@ namespace UVtools.Core /// public void RebuildLayersProperties() { - var layerHeight = SlicerFile.LayerHeight; + //var layerHeight = SlicerFile.LayerHeight; for (uint layerIndex = 0; layerIndex < Count; layerIndex++) { var layer = this[layerIndex]; layer.Index = layerIndex; - layer.PositionZ = (float) Math.Round(layerHeight * (layerIndex + 1), 2); + layer.PositionZ = SlicerFile.GetHeightFromLayer(layerIndex); layer.ExposureTime = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.InitialExposureTime, SlicerFile.LayerExposureTime); } }