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

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'UVtools.Core/FileFormats/FileFormat.cs')
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs669
1 files changed, 669 insertions, 0 deletions
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
new file mode 100644
index 0000000..89235cc
--- /dev/null
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -0,0 +1,669 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Emgu.CV;
+using UVtools.Core.Extensions;
+using UVtools.Core.Operations;
+
+namespace UVtools.Core.FileFormats
+{
+ /// <summary>
+ /// Slicer <see cref="FileFormat"/> representation
+ /// </summary>
+ public abstract class FileFormat : IFileFormat, IDisposable, IEquatable<FileFormat>, IEnumerable<Layer>
+ {
+ #region Enums
+
+ /// <summary>
+ /// Enumeration of file format types
+ /// </summary>
+ public enum FileFormatType : byte
+ {
+ Archive,
+ Binary
+ }
+
+ /// <summary>
+ /// Enumeration of file thumbnail size types
+ /// </summary>
+ public enum FileThumbnailSize : byte
+ {
+ Small = 0,
+ Large
+ }
+ #endregion
+
+ #region Sub Classes
+ /// <summary>
+ /// Available Print Parameters to modify
+ /// </summary>
+ public class PrintParameterModifier
+ {
+
+ #region Instances
+ public static PrintParameterModifier InitialLayerCount { get; } = new PrintParameterModifier("Initial Layer Count", @"Modify 'Initial Layer Count' value", null,0, ushort.MaxValue);
+ public static PrintParameterModifier InitialExposureSeconds { get; } = new PrintParameterModifier("Initial Exposure Time", @"Modify 'Initial Exposure Time' seconds", "s", 0.1M, byte.MaxValue);
+ public static PrintParameterModifier ExposureSeconds { get; } = new PrintParameterModifier("Exposure Time", @"Modify 'Exposure Time' seconds", "s", 0.1M, byte.MaxValue);
+
+ public static PrintParameterModifier BottomLayerOffTime { get; } = new PrintParameterModifier("Bottom Layer Off Time", @"Modify 'Bottom Layer Off Time' seconds", "s");
+ public static PrintParameterModifier LayerOffTime { get; } = new PrintParameterModifier("Layer Off Time", @"Modify 'Layer Off Time' seconds", "s");
+ public static PrintParameterModifier BottomLiftHeight { get; } = new PrintParameterModifier("Bottom Lift Height", @"Modify 'Bottom Lift Height' millimeters between bottom layers", "mm");
+ public static PrintParameterModifier BottomLiftSpeed { get; } = new PrintParameterModifier("Bottom Lift Speed", @"Modify 'Bottom Lift Speed' mm/min between bottom layers", "mm/min");
+ public static PrintParameterModifier LiftHeight { get; } = new PrintParameterModifier("Lift Height", @"Modify 'Lift Height' millimeters between layers", "mm");
+ public static PrintParameterModifier LiftSpeed { get; } = new PrintParameterModifier("Lift Speed", @"Modify 'Lift Speed' mm/min between layers", "mm/min", 10, 5000);
+ public static PrintParameterModifier RetractSpeed { get; } = new PrintParameterModifier("Retract Speed", @"Modify 'Retract Speed' mm/min between layers", "mm/min", 10, 5000);
+
+ public static PrintParameterModifier BottomLightPWM { get; } = new PrintParameterModifier("Bottom Light PWM", @"Modify 'Bottom Light PWM' value", null, 50, byte.MaxValue);
+ public static PrintParameterModifier LightPWM { get; } = new PrintParameterModifier("Light PWM", @"Modify 'Light PWM' value", null, 50, byte.MaxValue);
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets the name
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets the description
+ /// </summary>
+ public string Description { get; }
+
+ /// <summary>
+ /// Gets the value unit
+ /// </summary>
+ public string ValueUnit { get; }
+
+ /// <summary>
+ /// Gets the minimum value
+ /// </summary>
+ public decimal Minimum { get; }
+
+ /// <summary>
+ /// Gets the maximum value
+ /// </summary>
+ public decimal Maximum { get; }
+ #endregion
+
+ #region Constructor
+ public PrintParameterModifier(string name, string description, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000)
+ {
+ Name = name;
+ Description = description;
+ ValueUnit = valueUnit ?? string.Empty;
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+ #endregion
+
+ #region Overrides
+ public override string ToString()
+ {
+ return $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ValueUnit)}: {ValueUnit}, {nameof(Minimum)}: {Minimum}, {nameof(Maximum)}: {Maximum}";
+ }
+ #endregion
+ }
+ #endregion
+
+ #region Constants
+ private const string ExtractConfigFileName = "Configuration";
+ private const string ExtractConfigFileExtension = "ini";
+ #endregion
+
+ #region Static Methods
+ /// <summary>
+ /// Gets the available formats to process
+ /// </summary>
+ public static FileFormat[] AvaliableFormats { get; } =
+ {
+ new SL1File(), // Prusa SL1
+ new ChituboxZipFile(), // Zip
+ new ChituboxFile(), // cbddlp, cbt, photon
+ new PHZFile(), // phz
+ new PWSFile(), // PSW
+ new ZCodexFile(), // zcodex
+ new CWSFile(), // CWS
+ new ImageFile(), // images
+ };
+
+ public static string AllSlicerFiles => AvaliableFormats.Aggregate("All slicer files|",
+ (current, fileFormat) => current.EndsWith("|")
+ ? $"{current}{fileFormat.FileFilterExtensionsOnly}"
+ : $"{current}; {fileFormat.FileFilterExtensionsOnly}");
+
+ /// <summary>
+ /// Gets all filters for open and save file dialogs
+ /// </summary>
+ public static string AllFileFilters =>
+ AllSlicerFiles
+ +
+ AvaliableFormats.Aggregate(string.Empty,
+ (current, fileFormat) => $"{current}|" + fileFormat.FileFilter);
+
+
+
+ /// <summary>
+ /// Gets the count of available file extensions
+ /// </summary>
+ public static byte FileExtensionsCount
+ {
+ get
+ {
+ return AvaliableFormats.Aggregate<FileFormat, byte>(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length));
+ }
+ }
+
+ /// <summary>
+ /// Find <see cref="FileFormat"/> by an extension
+ /// </summary>
+ /// <param name="extension">Extension name to find</param>
+ /// <param name="isFilePath">True if <see cref="extension"/> is a file path rather than only a extension name</param>
+ /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
+ /// <returns><see cref="FileFormat"/> object or null if not found</returns>
+ public static FileFormat FindByExtension(string extension, bool isFilePath = false, bool createNewInstance = false)
+ {
+ return (from fileFormat in AvaliableFormats where fileFormat.IsExtensionValid(extension, isFilePath) select createNewInstance ? (FileFormat) Activator.CreateInstance(fileFormat.GetType()) : fileFormat).FirstOrDefault();
+ }
+
+ /// <summary>
+ /// Find <see cref="FileFormat"/> by an type
+ /// </summary>
+ /// <param name="type">Type to find</param>
+ /// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
+ /// <returns><see cref="FileFormat"/> object or null if not found</returns>
+ public static FileFormat FindByType(Type type, bool createNewInstance = false)
+ {
+ return (from t in AvaliableFormats where type == t.GetType() select createNewInstance ? (FileFormat) Activator.CreateInstance(type) : t).FirstOrDefault();
+ }
+ #endregion
+
+ #region Properties
+
+ public abstract FileFormatType FileType { get; }
+
+ public abstract FileExtension[] FileExtensions { get; }
+ public abstract Type[] ConvertToFormats { get; }
+
+ public abstract PrintParameterModifier[] PrintParameterModifiers { get; }
+
+ public string FileFilter {
+ get
+ {
+ var result = string.Empty;
+
+ foreach (var fileExt in FileExtensions)
+ {
+ if (!ReferenceEquals(result, string.Empty))
+ {
+ result += '|';
+ }
+ result += fileExt.Filter;
+ }
+
+ return result;
+ }
+ }
+
+ public string FileFilterExtensionsOnly
+ {
+ get
+ {
+ var result = string.Empty;
+
+ foreach (var fileExt in FileExtensions)
+ {
+ if (!ReferenceEquals(result, string.Empty))
+ {
+ result += "; ";
+ }
+ result += $"*.{fileExt.Extension}";
+ }
+
+ return result;
+ }
+ }
+
+ public string FileFullPath { get; set; }
+
+ public abstract byte ThumbnailsCount { get; }
+
+ public byte CreatedThumbnailsCount {
+ get
+ {
+ if (ReferenceEquals(Thumbnails, null)) return 0;
+ byte count = 0;
+
+ foreach (var thumbnail in Thumbnails)
+ {
+ if (ReferenceEquals(thumbnail, null)) continue;
+ count++;
+ }
+
+ return count;
+ }
+ }
+
+ public abstract Size[] ThumbnailsOriginalSize { get; }
+
+ public Mat[] Thumbnails { get; set; }
+ public LayerManager LayerManager { get; set; }
+
+ /// <summary>
+ /// Gets if any layer got modified
+ /// </summary>
+ public bool ModifiedLayers => LayerManager.IsModified;
+
+ public abstract uint ResolutionX { get; }
+
+ public abstract uint ResolutionY { get; }
+ public bool HaveAntiAliasing => AntiAliasing > 1;
+ public abstract byte AntiAliasing { get; }
+
+ public abstract float LayerHeight { get; }
+
+ public float TotalHeight => LayerCount == 0 ? 0 : this[LayerCount - 1].PositionZ; //(float)Math.Round(LayerCount * LayerHeight, 2);
+
+ public uint LayerCount => LayerManager?.Count ?? 0;
+
+ public abstract ushort InitialLayerCount { get; }
+
+ public abstract float InitialExposureTime { get; }
+
+ public abstract float LayerExposureTime { get; }
+
+ public abstract float LiftHeight { get; }
+
+ public abstract float RetractSpeed { get; }
+
+ public abstract float LiftSpeed { get; }
+
+ public abstract float PrintTime { get; }
+
+ public abstract float UsedMaterial { get; }
+
+ public abstract float MaterialCost { get; }
+
+ public abstract string MaterialName { get; }
+
+ public abstract string MachineName { get; }
+
+ public StringBuilder GCode { get; set; }
+
+ public abstract object[] Configs { get; }
+
+ public bool IsValid => !ReferenceEquals(FileFullPath, null);
+ #endregion
+
+ #region Constructor
+ protected FileFormat()
+ {
+ Thumbnails = new Mat[ThumbnailsCount];
+ }
+ #endregion
+
+ #region Indexers
+ public Layer this[int index]
+ {
+ get => LayerManager[index];
+ set => LayerManager[index] = value;
+ }
+
+ public Layer this[uint index]
+ {
+ get => LayerManager[index];
+ set => LayerManager[index] = value;
+ }
+
+ public Layer this[long index]
+ {
+ get => LayerManager[index];
+ set => LayerManager[index] = value;
+ }
+ #endregion
+
+ #region Numerators
+ public IEnumerator<Layer> GetEnumerator()
+ {
+ return ((IEnumerable<Layer>)LayerManager.Layers).GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ #endregion
+
+ #region Overrides
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as FileFormat);
+ }
+
+ public bool Equals(FileFormat other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return FileFullPath.Equals(other.FileFullPath);
+ }
+
+ public override int GetHashCode()
+ {
+ return (FileFullPath != null ? FileFullPath.GetHashCode() : 0);
+ }
+
+ public void Dispose()
+ {
+ Clear();
+ }
+
+ #endregion
+
+ #region Methods
+ public virtual void Clear()
+ {
+ FileFullPath = null;
+ LayerManager = null;
+ GCode = null;
+
+ if (!ReferenceEquals(Thumbnails, null))
+ {
+ for (int i = 0; i < ThumbnailsCount; i++)
+ {
+ Thumbnails[i]?.Dispose();
+ }
+ }
+
+
+ }
+
+ public void FileValidation(string fileFullPath)
+ {
+ if (ReferenceEquals(fileFullPath, null)) throw new ArgumentNullException(nameof(FileFullPath), "fullFilePath can't be null.");
+ if (!File.Exists(fileFullPath)) throw new FileNotFoundException("The specified file does not exists.", fileFullPath);
+
+ if (IsExtensionValid(fileFullPath, true))
+ {
+ return;
+ }
+
+ throw new FileLoadException($"The specified file is not valid.", fileFullPath);
+ }
+
+ public bool IsExtensionValid(string extension, bool isFilePath = false)
+ {
+ extension = isFilePath ? Path.GetExtension(extension)?.Remove(0, 1) : extension;
+ return FileExtensions.Any(fileExtension => fileExtension.Extension.Equals(extension, StringComparison.InvariantCultureIgnoreCase));
+ }
+
+ public string GetFileExtensions(string prepend = ".", string separator = ", ")
+ {
+ var result = string.Empty;
+
+ foreach (var fileExt in FileExtensions)
+ {
+ if (!ReferenceEquals(result, string.Empty))
+ {
+ result += separator;
+ }
+ result += $"{prepend}{fileExt.Extension}";
+ }
+
+ return result;
+ }
+
+ public Mat GetThumbnail(uint maxHeight = 400)
+ {
+ for (int i = 0; i < ThumbnailsCount; i++)
+ {
+ if(ReferenceEquals(Thumbnails[i], null)) continue;
+ if (Thumbnails[i].Height <= maxHeight) return Thumbnails[i];
+ }
+
+ return null;
+ }
+
+ public void SetThumbnails(Mat[] images)
+ {
+ for (var i = 0; i < ThumbnailsCount; i++)
+ {
+ Thumbnails[i] = images[Math.Min(i, images.Length - 1)].Clone();
+ }
+ }
+
+ public void SetThumbnails(Mat image)
+ {
+ for (var i = 0; i < ThumbnailsCount; i++)
+ {
+ Thumbnails[i] = image.Clone();
+ }
+ }
+
+ public virtual void Encode(string fileFullPath, OperationProgress progress = null)
+ {
+ FileFullPath = fileFullPath;
+
+ if (File.Exists(fileFullPath))
+ {
+ File.Delete(fileFullPath);
+ }
+
+ for (var i = 0; i < Thumbnails.Length; i++)
+ {
+ if (ReferenceEquals(Thumbnails[i], null)) continue;
+ CvInvoke.Resize(Thumbnails[i], Thumbnails[i], new Size(ThumbnailsOriginalSize[i].Width, ThumbnailsOriginalSize[i].Height));
+ }
+
+ if(ReferenceEquals(progress, null)) progress = new OperationProgress();
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ }
+
+ /*public virtual void BeginEncode(string fileFullPath)
+ {
+ }
+
+
+ public abstract void InsertLayerImageEncode(Image<L8> image, uint layerIndex);
+
+ public abstract void EndEncode();*/
+
+ public virtual void Decode(string fileFullPath, OperationProgress progress = null)
+ {
+ Clear();
+ FileValidation(fileFullPath);
+ FileFullPath = fileFullPath;
+ if(ReferenceEquals(progress, null)) progress = new OperationProgress();
+ progress.ItemName = OperationProgress.StatusGatherLayers;
+ }
+
+ public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true,
+ OperationProgress progress = null)
+ {
+ if (ReferenceEquals(progress, null)) progress = new OperationProgress();
+ progress.ItemName = OperationProgress.StatusExtracting;
+ /*if (emptyFirst)
+ {
+ if (Directory.Exists(path))
+ {
+ DirectoryInfo di = new DirectoryInfo(path);
+
+ foreach (FileInfo file in di.GetFiles())
+ {
+ file.Delete();
+ }
+ foreach (DirectoryInfo dir in di.GetDirectories())
+ {
+ dir.Delete(true);
+ }
+ }
+ }*/
+
+ //if (!Directory.Exists(path))
+ //{
+ Directory.CreateDirectory(path);
+ //}
+
+
+ if (FileType == FileFormatType.Archive)
+ {
+
+ progress.CanCancel = false;
+ //ZipFile.ExtractToDirectory(FileFullPath, path);
+ ZipArchiveExtensions.ImprovedExtractToDirectory(FileFullPath, path, ZipArchiveExtensions.Overwrite.Always);
+ return;
+ }
+
+ progress.ItemCount = LayerCount;
+
+ if (genericConfigExtract)
+ {
+ if (!ReferenceEquals(Configs, null))
+ {
+ using (TextWriter tw = new StreamWriter(Path.Combine(path, $"{ExtractConfigFileName}.{ExtractConfigFileExtension}"), false))
+ {
+ foreach (var config in Configs)
+ {
+ var type = config.GetType();
+ tw.WriteLine($"[{type.Name}]");
+ foreach (var property in type.GetProperties())
+ {
+ tw.WriteLine($"{property.Name} = {property.GetValue(config)}");
+ }
+
+ tw.WriteLine();
+ }
+
+ tw.Close();
+ }
+ }
+ }
+
+ if (genericLayersExtract)
+ {
+ uint i = 0;
+ if (!ReferenceEquals(Thumbnails, null))
+ {
+ foreach (var thumbnail in Thumbnails)
+ {
+ if (ReferenceEquals(thumbnail, null))
+ {
+ continue;
+ }
+
+ thumbnail.Save(Path.Combine(path, $"Thumbnail{i}.png"));
+ i++;
+ }
+ }
+
+ if (LayerCount > 0)
+ {
+ Parallel.ForEach(this, (layer) =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ var byteArr = layer.CompressedBytes;
+ using (FileStream stream = File.Create(Path.Combine(path, $"Layer{layer.Index}.png"),
+ byteArr.Length))
+ {
+ stream.Write(byteArr, 0, byteArr.Length);
+ stream.Close();
+ lock (progress.Mutex)
+ {
+ progress++;
+ }
+ }
+ });
+ }
+
+ /* Parallel.For(0, LayerCount, layerIndex => {
+ var byteArr = this[layerIndex].RawData;
+ using (FileStream stream = File.Create(Path.Combine(path, $"Layer{layerIndex}.png"), byteArr.Length))
+ {
+ stream.Write(byteArr, 0, byteArr.Length);
+ stream.Close();
+ }
+ });*/
+ /*for (i = 0; i < LayerCount; i++)
+ {
+ var byteArr = GetLayer(i);
+ using (FileStream stream = File.Create(Path.Combine(path, $"Layer{i}.png"), byteArr.Length))
+ {
+ stream.Write(byteArr, 0, byteArr.Length);
+ stream.Close();
+ }
+ }*/
+ }
+ }
+
+ public virtual float GetHeightFromLayer(uint layerIndex, bool realHeight = true)
+ {
+ return (float)Math.Round((layerIndex+(realHeight ? 1 : 0)) * LayerHeight, 2);
+ }
+
+ public T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue)
+ {
+ return layerIndex < InitialLayerCount ? initialLayerValue : normalLayerValue;
+ }
+
+ public virtual object GetValueFromPrintParameterModifier(PrintParameterModifier modifier)
+ {
+ if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount))
+ return InitialLayerCount;
+ if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds))
+ return InitialExposureTime;
+ if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds))
+ return LayerExposureTime;
+
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight))
+ return LiftHeight;
+ if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed))
+ return LiftSpeed;
+ if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed))
+ return RetractSpeed;
+
+
+
+ return null;
+ }
+
+ public virtual bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, object value)
+ {
+ return SetValueFromPrintParameterModifier(modifier, value.ToString());
+ }
+
+ public abstract bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value);
+
+ public void Save(OperationProgress progress = null)
+ {
+ SaveAs(null, progress);
+ }
+
+ public abstract void SaveAs(string filePath = null, OperationProgress progress = null);
+
+ public abstract bool Convert(Type to, string fileFullPath, OperationProgress progress = null);
+ public bool Convert(FileFormat to, string fileFullPath, OperationProgress progress = null)
+ {
+ return Convert(to.GetType(), fileFullPath, progress);
+ }
+
+ public byte ValidateAntiAliasingLevel()
+ {
+ if (AntiAliasing < 2) return 1;
+ if(AntiAliasing % 2 != 0) throw new ArgumentException("AntiAliasing must be multiples of 2, otherwise use 0 or 1 to disable it", nameof(AntiAliasing));
+ return AntiAliasing;
+ }
+
+ #endregion
+ }
+}