diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3dcdaf..f44504cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 11/06/2023 - v3.14.3 + +- **Settings:** + - (Add) After save the file replace on it name with the updated print time and material if possible + - (Add) File 'Save as' default name with extended variables (#725) + - (Add) File 'Save as' name cleanup regex + - (Add) When save as a file and if the file name already exists on that directory it will append a number up to one available + - (Remove) File 'Save as' suffix and prefix +- (Add) About: Graphic card name +- (Fix) CTB Encrypted: Check the checksum as last step to be compatible with new Chitubox 1.4.5 CTB files (#696, #726) + ## 07/06/2023 - v3.14.2 - **PrusaSlicer printers:** @@ -232,7 +243,7 @@ - First parameter is the first request to get response content from - Second parameter is the regex pattern to match content with - Third parameter is the final request that supports a parameter from regex matching group, eg: **{#1}** is match Group[1] value - - **Example:** <\? getfiles > {0}\/(\d+\.[\da-zA-Z]+), > printfile,{#1} ?> + - **Example:** <\? getfiles > {0}\/([0-9]+[.][0-9a-zA-Z]+), > printfile,{#1} ?> - (Change) Allow to print a filename without send it when upload request path is empty - (Fix) Do not show printers with empty requests - (Change) Default layer compression to Lz4 instead of Png diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 64e5104f..091a18e9 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,9 @@ -- **PrusaSlicer printers:** - - (Add) Elegoo Mars 4 Ultra - - (Add) Elegoo Saturn 3 - - (Add) Elegoo Saturn 3 Ultra -- (Fix) AnyCubic file format: Retract speed (slow/fast) variables was swapped (#722) +- **Settings:** + - (Add) After save the file replace on it name with the updated print time and material if possible + - (Add) File 'Save as' default name with extended variables (#725) + - (Add) File 'Save as' name cleanup regex + - (Add) When save as a file and if the file name already exists on that directory it will append a number up to one available + - (Remove) File 'Save as' suffix and prefix +- (Add) About: Graphic card name +- (Fix) CTB Encrypted: Check the checksum as last step to be compatible with new Chitubox 1.4.5 CTB files (#696, #726) diff --git a/Scripts/010 Editor/ctb_encrypted.bt b/Scripts/010 Editor/ctb_encrypted.bt index 84562fcf..3fefb63e 100644 --- a/Scripts/010 Editor/ctb_encrypted.bt +++ b/Scripts/010 Editor/ctb_encrypted.bt @@ -39,13 +39,15 @@ struct PREVIEW { PREVIEW SmallPreview; -local ushort machineNameLength = 1; +local ushort machineNameLength = 0; while (ReadString(FTell()+machineNameLength, 17 ) != "Layout and record") { machineNameLength++; if(machineNameLength > 255) return; } -char MachineName[machineNameLength] ; +if(machineNameLength > 0){ + char MachineName[machineNameLength] ; +} char Disclaimer[320] ; diff --git a/Scripts/install-uvtools.sh b/Scripts/install-uvtools.sh index 0269e22a..4b56c1d1 100644 --- a/Scripts/install-uvtools.sh +++ b/Scripts/install-uvtools.sh @@ -22,9 +22,9 @@ if [ "$arch" != "x86_64" -a "$arch" != "arm64" ]; then fi # Tag validation -if [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then +if [[ "$tag" =~ ^v[0-9]+[.][0-9]+[.][0-9]+$ ]]; then api_url="https://api.github.com/repos/sn4k3/UVtools/releases/tags/$tag" -elif [[ "$tag" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then +elif [[ "$tag" =~ ^[0-9]+[.][0-9]+[.][0-9]+$ ]]; then api_url="https://api.github.com/repos/sn4k3/UVtools/releases/tags/v$tag" elif [ "$tag" != "latest" -a -n "$tag" ]; then echo "Error: Invalid '$tag' tag/version was provided." @@ -132,7 +132,7 @@ if [ "$osVariant" == "osx" ]; then echo '- Detecting download' download_url="$(curl -s "$api_url" \ - | grep "browser_download_url.*_${osVariant}-${archCode}_.*\.zip" \ + | grep "browser_download_url.*_${osVariant}-${archCode}_.*[.]zip" \ | head -1 \ | cut -d : -f 2,3 \ | tr -d \")" @@ -210,14 +210,14 @@ else response="$(curl -s "$api_url")" download_url="$(echo "$response" \ - | grep "browser_download_url.*_${osVariant}-x64_.*\.AppImage" \ + | grep "browser_download_url.*_${osVariant}-x64_.*[.]AppImage" \ | head -1 \ | cut -d : -f 2,3 \ | tr -d \")" if [ -z "$download_url" ]; then download_url="$(echo "$response" \ - | grep "browser_download_url.*_linux-x64_.*\.AppImage" \ + | grep "browser_download_url.*_linux-x64_.*[.]AppImage" \ | head -1 \ | cut -d : -f 2,3 \ | tr -d \")" diff --git a/UVtools.Core/Excellon/ExcellonDrillFormat.cs b/UVtools.Core/Excellon/ExcellonDrillFormat.cs index 2a2bd0ff..8ccb11c1 100644 --- a/UVtools.Core/Excellon/ExcellonDrillFormat.cs +++ b/UVtools.Core/Excellon/ExcellonDrillFormat.cs @@ -246,7 +246,7 @@ private void Load(string filePath) { if (!endOfHeader) { - var match = Regex.Match(line, @"^T(\d+)C(([0-9]*[.])?[0-9]+)"); + var match = Regex.Match(line, @"^T([0-9]+)C(([0-9]*[.])?[0-9]+)"); if (match is { Success: true, diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index 73832d15..87c5ede4 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -15,7 +15,7 @@ public static class SizeExtensions public static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; - public static string SizeSuffix(long value, byte decimalPlaces = 2) + public static string SizeSuffix(long value, byte decimalPlaces = 2, bool suffixSpaced = true) { //if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); } if (value < 0) { return "-" + SizeSuffix(-value); } @@ -30,17 +30,17 @@ public static string SizeSuffix(long value, byte decimalPlaces = 2) // make adjustment when the value is large enough that // it would round up to 1000 or more - if (Math.Round(adjustedSize, decimalPlaces) >= 1000) + if (Math.Round(adjustedSize, decimalPlaces, MidpointRounding.AwayFromZero) >= 1000) { mag += 1; adjustedSize /= 1024; } - return string.Format("{0:n" + decimalPlaces + "} {1}", - adjustedSize, - SizeSuffixes[mag]); + return string.Format($"{{0:n{decimalPlaces}}}{(suffixSpaced ? ' ' : string.Empty)}{SizeSuffixes[mag]}", adjustedSize); } + public static string SizeSuffix(long value, bool suffixSpaced) => SizeSuffix(value, 2, suffixSpaced); + public static Size Add(this Size size, Size otherSize) => new (size.Width + otherSize.Width, size.Height + otherSize.Height); public static Size Add(this Size size) => size.Add(size); public static Size Add(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels); diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index f5d4d34e..5f432e19 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -1110,12 +1110,13 @@ protected override void DecodeInternally(OperationProgress progress) var checksumHash = CryptExtensions.ComputeSHA256Hash(checksumBytes); var encryptedHash = CryptExtensions.AesCryptBytes(checksumHash, Bigfoot, CipherMode.CBC, PaddingMode.None, true, CookieMonster); - inputFile.Seek(-HASH_LENGTH, SeekOrigin.End); + // Does not work with recent Chitubox 1.4.5 + /*inputFile.Seek(-HASH_LENGTH, SeekOrigin.End); var hash = inputFile.ReadBytes(HASH_LENGTH); - if (!hash.SequenceEqual(encryptedHash)) + if (!hash.SequenceEqual(encryptedHash)) { throw new FileLoadException("The file checksum does not match, malformed file.", FileFullPath); - } + }*/ progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); @@ -1264,7 +1265,12 @@ protected override void DecodeInternally(OperationProgress progress) "Please increase the portion of the plate in use and re-slice the file."); } } - //inputFile.ReadBytes(HashLength); + + var hash = inputFile.ReadBytes(HASH_LENGTH); + if (!hash.SequenceEqual(encryptedHash)) + { + throw new FileLoadException("The file checksum does not match, malformed file.", FileFullPath); + } } protected override void OnBeforeEncode(bool isPartialEncode) diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 03774416..17c27a1f 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -809,7 +809,7 @@ protected override void DecodeInternally(OperationProgress progress) foreach (var pngEntry in inputFile.Entries) { if (!pngEntry.Name.EndsWith(".png")) continue; - var match = Regex.Match(pngEntry.Name, @"(\d+).png"); + var match = Regex.Match(pngEntry.Name, @"([0-9]+)[.]png$"); if (!match.Success || match.Groups.Count < 2) continue; if (!uint.TryParse(match.Groups[1].Value, out var layerIndex)) continue; diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index 369f451f..0756d280 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -614,7 +614,7 @@ public override string MachineName if (!string.IsNullOrWhiteSpace(value) && !value.StartsWith("CL-") && !value.StartsWith("CT-")) { // Parse from machine name, if coming from PrusaSlicer this will help - var match = Regex.Match(value, @"(CL|CT)-?\d+[a-zA-Z]?"); + var match = Regex.Match(value, @"(CL|CT)-?[0-9]+[a-zA-Z]?"); if (match is {Success: true, Groups.Count: > 1}) { value = match.Value; diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 6e92ff5e..20032354 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -32,7 +32,6 @@ using UVtools.Core.Objects; using UVtools.Core.Operations; using UVtools.Core.PixelEditor; -using Range = System.Range; namespace UVtools.Core.FileFormats; @@ -984,6 +983,9 @@ public static int MutateGetIterationChamfer(uint layerIndex, uint startLayerInde #region Members public object Mutex = new(); + private string? _fileFullPath; + + protected Layer[] _layers = Array.Empty(); private bool _haveModifiedLayers; @@ -1166,11 +1168,34 @@ public string FileFilterExtensionsOnly /// /// Gets the input file path loaded into this /// - public string? FileFullPath { get; set; } + public string? FileFullPath + { + get => _fileFullPath; + set + { + if(!RaiseAndSetIfChanged(ref _fileFullPath, value)) return; + RaisePropertyChanged(DirectoryPath); + RaisePropertyChanged(Filename); + RaisePropertyChanged(FilenameNoExt); + RaisePropertyChanged(FilenameStripExtensions); + RaisePropertyChanged(FileExtension); + RaisePropertyChanged(FileAbsoluteExtension); + } + } public string? DirectoryPath => Path.GetDirectoryName(FileFullPath); public string? Filename => Path.GetFileName(FileFullPath); + /// + /// Returns the file name without the extension + /// + public string? FilenameNoExt => GetFileNameStripExtensions(FileFullPath); + + /// + /// Returns the file name without the extension(s) + /// + public string? FilenameStripExtensions => FileFullPath is null ? null : GetFileNameStripExtensions(FileFullPath, out _); + /// /// Returns the file extension. The returned value includes the period (".") character of the /// extension except when you have a terminal period when you get string.Empty, such as ".exe" or ".cpp". @@ -1180,9 +1205,20 @@ public string FileFilterExtensionsOnly public string? FileExtension => Path.GetExtension(FileFullPath); /// - /// Returns the file name without the extension + /// Returns the file extension as safe method where it can include more than one extension. The returned value includes the period (".") character of the + /// extension except when you have a terminal period when you get string.Empty, such as ".exe" or ".cpp". + /// The returned value is null if the given path is null or empty if the given path does not include an + /// extension. /// - public string? FilenameNoExt => GetFileNameStripExtensions(FileFullPath); + public string? FileAbsoluteExtension + { + get + { + if(FileFullPath is null) return null; + GetFileNameStripExtensions(FileFullPath, out var ext); + return ext; + } + } /// /// Gets the available versions to set in this file format @@ -1851,7 +1887,6 @@ public virtual byte AntiAliasing /// public ushort LayerHeightUm => (ushort) (LayerHeight * 1000); - /// /// Gets or sets the print height in mm /// @@ -2955,6 +2990,7 @@ public virtual float MaterialMilliliters { } if(!RaiseAndSetIfChanged(ref _materialMilliliters, value)) return; + RaisePropertyChanged(nameof(MaterialMillilitersInteger)); if (StartingMaterialMilliliters > 0 && StartingMaterialCost > 0) { @@ -2964,12 +3000,17 @@ public virtual float MaterialMilliliters { } } + /// + /// Gets the estimate used material in ml and rounded to next integer + /// + public uint MaterialMillilitersInteger => (uint)Math.Ceiling(MaterialMilliliters); + //public float MaterialMillilitersComputed => - /// - /// Gets the estimate material in grams - /// + /// + /// Gets the estimate material in grams + /// public virtual float MaterialGrams { get => _materialGrams; @@ -3160,7 +3201,7 @@ public Layer this[long index] set => SetLayer((uint)index, value); } - public Layer[] this[Range range] => _layers[range]; + public Layer[] this[System.Range range] => _layers[range]; /// /// Sets a layer @@ -3343,6 +3384,28 @@ public bool FileEndsWith(string extension) FileFullPath.EndsWith($"{extension}{TemporaryFileAppend}", StringComparison.OrdinalIgnoreCase); } + /// + /// Renames the current file with a new name in the same directory. + /// + /// New filename without the extension + /// True to overwrite file if exists, otherwise false + /// True if renamed, otherwise false. + public bool RenameFile(string newFileName, bool overwrite = false) + { + if(string.IsNullOrWhiteSpace(newFileName)) return false; + if (!File.Exists(FileFullPath)) return false; + + var filename = GetFileNameStripExtensions(FileFullPath, out var ext); + + if (string.Equals(filename, newFileName, StringComparison.Ordinal)) return false; + + var newFileFullPath = Path.Combine(DirectoryPath!, $"{newFileName}.{ext}"); + File.Move(FileFullPath, newFileFullPath, overwrite); + FileFullPath = newFileFullPath; + + return true; + } + /// /// Gets a thumbnail by it height or lower /// @@ -3910,20 +3973,20 @@ public void DecodeLayersFromZipRegex(ZipArchive zipArchive, string regex, IndexS } public void DecodeLayersFromZip(ZipArchive zipArchive, byte padDigits, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func? matGenFunc = null) - => DecodeLayersFromZipRegex(zipArchive, $@"(\d{{{padDigits}}}).png$", layerIndexStartNumber, progress, matGenFunc); + => DecodeLayersFromZipRegex(zipArchive, @$"([0-9]{{{padDigits}}})[.]png$", layerIndexStartNumber, progress, matGenFunc); public void DecodeLayersFromZip(ZipArchive zipArchive, string prepend, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func? matGenFunc = null) - => DecodeLayersFromZipRegex(zipArchive, $@"^{Regex.Escape(prepend)}(\d+).png$", layerIndexStartNumber, progress, matGenFunc); + => DecodeLayersFromZipRegex(zipArchive, @$"^{Regex.Escape(prepend)}([0-9]+)[.]png$", layerIndexStartNumber, progress, matGenFunc); public void DecodeLayersFromZip(ZipArchive zipArchive, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func? matGenFunc = null) - => DecodeLayersFromZipRegex(zipArchive, @"^(\d+).png$", layerIndexStartNumber, progress, matGenFunc); + => DecodeLayersFromZipRegex(zipArchive, @"^([0-9]+)[.]png$", layerIndexStartNumber, progress, matGenFunc); public void DecodeLayersFromZip(ZipArchive zipArchive, OperationProgress progress, Func? matGenFunc = null) - => DecodeLayersFromZipRegex(zipArchive, @"^(\d+).png$", IndexStartNumber.Zero, progress, matGenFunc); + => DecodeLayersFromZipRegex(zipArchive, @"^([0-9]+)[.]png$", IndexStartNumber.Zero, progress, matGenFunc); public void DecodeLayersFromZipIgnoreFilename(ZipArchive zipArchive, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func? matGenFunc = null) { - DecodeLayersFromZipRegex(zipArchive, @$"({@"\d?".Repeat(LayerDigits - 1)}\d).png$", layerIndexStartNumber, progress, matGenFunc); + DecodeLayersFromZipRegex(zipArchive, @$"([0-9]{{1,{LayerDigits}}})[.]png$", layerIndexStartNumber, progress, matGenFunc); } public void DecodeLayersFromZipIgnoreFilename(ZipArchive zipArchive, OperationProgress progress, Func? matGenFunc = null) diff --git a/UVtools.Core/FileFormats/JXSFile.cs b/UVtools.Core/FileFormats/JXSFile.cs index 14048333..056d7468 100644 --- a/UVtools.Core/FileFormats/JXSFile.cs +++ b/UVtools.Core/FileFormats/JXSFile.cs @@ -356,7 +356,7 @@ private void RebuildFileProperties() if (line.StartsWith(GCode.CommandShowImageM6054.Command)) { - var match = Regex.Match(line, GCode.GetShowImageString(@"(\d+)")); + var match = Regex.Match(line, GCode.GetShowImageString(@"([0-9]+)")); if (!match.Success || match.Groups.Count <= 1) { @@ -383,7 +383,7 @@ private void RebuildFileProperties() if (line.StartsWith(GCode.CommandWaitG4.Command)) { - var match = Regex.Match(line, GCode.CommandWaitG4.ToStringWithoutComments(@"(\d+)")); + var match = Regex.Match(line, GCode.CommandWaitG4.ToStringWithoutComments(@"([0-9]+)")); if (!match.Success || match.Groups.Count <= 1) { diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 7ec5ccbf..da6bc78d 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -519,8 +519,8 @@ M106 S0 byte pwm = GetBottomOrNormalValue((uint)layerIndex, BottomLightPWM, LightPWM); //var currPos = Regex.Match(stripGcode, "G1 Z([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase); - var moveG1Regex = Regex.Match(stripGcode, @"G1 Z([+-]?([0-9]*[.])?[0-9]+) F(\d+)", RegexOptions.IgnoreCase); - var pwmM106Regex = Regex.Match(stripGcode, @"M106 S(\d+)", RegexOptions.IgnoreCase); + var moveG1Regex = Regex.Match(stripGcode, @"G1 Z([+-]?([0-9]*[.])?[0-9]+) F([0-9]+)", RegexOptions.IgnoreCase); + var pwmM106Regex = Regex.Match(stripGcode, @"M106 S([0-9]+)", RegexOptions.IgnoreCase); if (moveG1Regex.Success) { @@ -582,10 +582,10 @@ M106 S0 public override void RebuildGCode() { var gcode = GCodeStr!; - gcode = Regex.Replace(gcode, @"Z[+]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)", + gcode = Regex.Replace(gcode, @"Z[+]?([0-9]*[.][0-9]+|[0-9]+) F[+]?([0-9]*[.][0-9]+|[0-9]+)", $"Z{UserSettings.ZLiftDistance} F{UserSettings.ZLiftFeedRate}"); - gcode = Regex.Replace(gcode, @"Z-[-]?([0-9]*\.[0-9]+|[0-9]+) F[+]?([0-9]*\.[0-9]+|[0-9]+)", + gcode = Regex.Replace(gcode, @"Z-[-]?([0-9]*[.][0-9]+|[0-9]+) F[+]?([0-9]*[.][0-9]+|[0-9]+)", $"Z-{UserSettings.ZLiftDistance - LayerHeight} F{UserSettings.ZLiftRetractRate}"); GCode!.Clear(); diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 340e021c..dd8285ce 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -931,7 +931,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu if (line[0] == ';') { // Can be dangerous! - match = Regex.Match(line, @"^;\W*(layer\W*|LAYER_START:\s*)(\d+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + match = Regex.Match(line, @"^;\W*(layer\W*|LAYER_START:\s*)([0-9]+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); if (match is {Success: true, Groups.Count: > 2} && uint.TryParse(match.Groups[2].Value, out var layerIndex)) { if (layerIndex > slicerFile.LayerCount) @@ -950,7 +950,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu // Display image if (line.StartsWith(CommandShowImageM6054.Command)) { - match = Regex.Match(line, CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")), RegexOptions.IgnoreCase); + match = Regex.Match(line, CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"([0-9]+)")), RegexOptions.IgnoreCase); if (match is {Success: true, Groups.Count: >= 2}) // Begin new layer { var layerIndex = uint.Parse(match.Groups[1].Value); @@ -1086,7 +1086,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu // Check LightPWM if (line.StartsWith(CommandTurnLEDM106.Command)) { - match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase); + match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"([0-9]+)"), RegexOptions.IgnoreCase); if (match is {Success: true, Groups.Count: >= 2}) { byte pwm; diff --git a/UVtools.Core/Gerber/Apertures/Aperture.cs b/UVtools.Core/Gerber/Apertures/Aperture.cs index 0ded93e5..d1ec004b 100644 --- a/UVtools.Core/Gerber/Apertures/Aperture.cs +++ b/UVtools.Core/Gerber/Apertures/Aperture.cs @@ -54,7 +54,7 @@ protected Aperture(GerberFormat document, string name) public static Aperture? Parse(string line, GerberFormat document) { - var match = Regex.Match(line, @"\%ADD(\d+)(\w+),?(\S+)?\*\%"); + var match = Regex.Match(line, @"\%ADD([0-9]+)(\w+),?(\S+)?\*\%"); if (!match.Success || match.Groups.Count < 3) return null; if (!int.TryParse(match.Groups[1].Value, out var index)) return null; diff --git a/UVtools.Core/Gerber/GerberFormat.cs b/UVtools.Core/Gerber/GerberFormat.cs index bf76b7c8..8ca60d46 100644 --- a/UVtools.Core/Gerber/GerberFormat.cs +++ b/UVtools.Core/Gerber/GerberFormat.cs @@ -283,7 +283,7 @@ public static void ParseAndDraw(GerberFormat document, string filePath, Mat mat, // Aperture selector if (line[0] == 'D' || line.StartsWith("G54")) { - var matchD = Regex.Match(line, @"D(\d+)"); + var matchD = Regex.Match(line, @"D([0-9]+)"); if (!matchD.Success || matchD.Groups.Count < 2) continue; if (!int.TryParse(matchD.Groups[1].Value, out var d)) continue; @@ -298,9 +298,9 @@ public static void ParseAndDraw(GerberFormat document, string filePath, Mat mat, if (line[0] == 'X' || line[0] == 'Y' || line[0] == 'D') { - var matchX = Regex.Match(line, @"X-?(\d+)?"); - var matchY = Regex.Match(line, @"Y-?(\d+)?"); - var matchD = Regex.Match(line, @"D(\d+)"); + var matchX = Regex.Match(line, @"X-?([0-9]+)?"); + var matchY = Regex.Match(line, @"Y-?([0-9]+)?"); + var matchD = Regex.Match(line, @"D([0-9]+)"); double nowX = 0; double nowY = 0; @@ -376,8 +376,8 @@ public static void ParseAndDraw(GerberFormat document, string filePath, Mat mat, { double xOffset = 0; double yOffset = 0; - var matchI = Regex.Match(line, @"I(-?\d+)"); - var matchJ = Regex.Match(line, @"J(-?\d+)"); + var matchI = Regex.Match(line, @"I(-?[0-9]+)"); + var matchJ = Regex.Match(line, @"J(-?[0-9]+)"); if (!matchI.Success || !matchJ.Success || matchI.Groups.Count < 2 || matchJ.Groups.Count < 2) continue; // xOffset diff --git a/UVtools.Core/Gerber/Macro.cs b/UVtools.Core/Gerber/Macro.cs index 8b9f8783..bc331fb5 100644 --- a/UVtools.Core/Gerber/Macro.cs +++ b/UVtools.Core/Gerber/Macro.cs @@ -110,7 +110,7 @@ public Macro Clone() public static Macro? Parse(GerberFormat document, string line) { - var match = Regex.Match(line, @"%?AM([a-zA-Z\d]+)\*?"); + var match = Regex.Match(line, @"%?AM([a-zA-Z0-9]+)\*?"); if (!match.Success || match.Groups.Count < 2) return null; return new Macro(document, match.Groups[1].Value); diff --git a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs index 868d8640..f7bcaa7c 100644 --- a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs @@ -105,7 +105,7 @@ public override void ParseExpressions(params string[] args) if (byte.TryParse(ExposureExpression, out var exposure)) Exposure = exposure; else { - csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$(\d+)", "{$1}"), args); + csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$([0-9]+)", "{$1}"), args); var temp = exp.Compute(csharpExp, null); if(temp is not DBNull) Exposure = Convert.ToByte(temp); } @@ -113,7 +113,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(WidthExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Width = num; else { - csharpExp = Regex.Replace(WidthExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(WidthExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Width = Convert.ToSingle(temp); @@ -123,7 +123,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(HeightExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Height = num; else { - csharpExp = Regex.Replace(HeightExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(HeightExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Height = Convert.ToSingle(temp); @@ -133,7 +133,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(CenterXExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) CenterX = num; else { - csharpExp = Regex.Replace(CenterXExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(CenterXExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) CenterX = Convert.ToSingle(temp); @@ -143,7 +143,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(CenterYExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) CenterY = num; else { - csharpExp = Regex.Replace(CenterYExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(CenterYExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) CenterY = Convert.ToSingle(temp); @@ -153,7 +153,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(RotationExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Rotation = (short)num; else { - csharpExp = Regex.Replace(RotationExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(RotationExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Rotation = Convert.ToSingle(temp); diff --git a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs index 8609c3c8..ee4e1e52 100644 --- a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs @@ -97,7 +97,7 @@ public override void ParseExpressions(params string[] args) if (byte.TryParse(ExposureExpression, out var exposure)) Exposure = exposure; else { - csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$(\d+)", "{$1}"), args); + csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$([0-9]+)", "{$1}"), args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Exposure = Convert.ToByte(temp); } @@ -105,7 +105,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(DiameterExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Diameter = num; else { - csharpExp = Regex.Replace(DiameterExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(DiameterExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Diameter = Convert.ToSingle(temp); @@ -115,7 +115,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(CenterXExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) CenterX = num; else { - csharpExp = Regex.Replace(CenterXExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(CenterXExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) CenterX = Convert.ToSingle(temp); @@ -125,7 +125,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(CenterYExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) CenterY = num; else { - csharpExp = Regex.Replace(CenterYExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(CenterYExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) CenterY = Convert.ToSingle(temp); @@ -136,7 +136,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(RotationExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Rotation = (short)num; else { - csharpExp = Regex.Replace(RotationExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(RotationExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Rotation = Convert.ToSingle(temp); diff --git a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs index f25ad25f..1f99b00d 100644 --- a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs @@ -107,7 +107,7 @@ public override void ParseExpressions(params string[] args) if (byte.TryParse(ExposureExpression, out var exposure)) Exposure = exposure; else { - csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$(\d+)", "{$1}"), args); + csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$([0-9]+)", "{$1}"), args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Exposure = Convert.ToByte(temp); } @@ -118,7 +118,7 @@ public override void ParseExpressions(params string[] args) { if (!float.TryParse(coordinate, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) { - csharpExp = string.Format(Regex.Replace(coordinate, @"\$(\d+)", "{$1}"), args); + csharpExp = string.Format(Regex.Replace(coordinate, @"\$([0-9]+)", "{$1}"), args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) num = Convert.ToSingle(temp); } @@ -139,7 +139,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(RotationExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Rotation = (short)num; else { - csharpExp = Regex.Replace(RotationExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(RotationExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Rotation = Convert.ToSingle(temp); diff --git a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs index c7df0be0..cebf8452 100644 --- a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs @@ -105,7 +105,7 @@ public override void ParseExpressions(params string[] args) if (byte.TryParse(ExposureExpression, out var exposure)) Exposure = exposure; else { - csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$(\d+)", "{$1}"), args); + csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$([0-9]+)", "{$1}"), args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Exposure = Convert.ToByte(temp); } @@ -113,7 +113,7 @@ public override void ParseExpressions(params string[] args) if (byte.TryParse(DiameterExpression, out var vertices)) VerticesCount = vertices; else { - csharpExp = Regex.Replace(DiameterExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(DiameterExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) VerticesCount = Convert.ToByte(temp); @@ -122,7 +122,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(CenterXExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) CenterX = num; else { - csharpExp = Regex.Replace(CenterXExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(CenterXExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) CenterX = Convert.ToSingle(temp); @@ -132,7 +132,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(CenterYExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) CenterY = num; else { - csharpExp = Regex.Replace(CenterYExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(CenterYExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) CenterY = Convert.ToSingle(temp); @@ -142,7 +142,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(DiameterExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Diameter = num; else { - csharpExp = Regex.Replace(DiameterExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(DiameterExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Diameter = Convert.ToSingle(temp); @@ -152,7 +152,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(RotationExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Rotation = (short)num; else { - csharpExp = Regex.Replace(RotationExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(RotationExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Rotation = Convert.ToSingle(temp); diff --git a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs index 12f33a09..e80141f6 100644 --- a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs @@ -122,7 +122,7 @@ public override void ParseExpressions(params string[] args) if (byte.TryParse(ExposureExpression, out var exposure)) Exposure = exposure; else { - csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$(\d+)", "{$1}"), args); + csharpExp = string.Format(Regex.Replace(ExposureExpression, @"\$([0-9]+)", "{$1}"), args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Exposure = Convert.ToByte(temp); } @@ -130,7 +130,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(LineWidthExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) LineWidth = num; else { - csharpExp = Regex.Replace(LineWidthExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(LineWidthExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) LineWidth = Convert.ToSingle(temp); @@ -140,7 +140,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(StartXExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) StartX = num; else { - csharpExp = Regex.Replace(StartXExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(StartXExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) StartX = Convert.ToSingle(temp); @@ -150,7 +150,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(EndXExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) EndX = num; else { - csharpExp = Regex.Replace(EndXExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(EndXExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) EndX = Convert.ToSingle(temp); @@ -160,7 +160,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(StartYExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) StartY = num; else { - csharpExp = Regex.Replace(StartYExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(StartYExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) StartY = Convert.ToSingle(temp); @@ -170,7 +170,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(EndYExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) EndY = num; else { - csharpExp = Regex.Replace(EndYExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(EndYExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) EndY = Convert.ToSingle(temp); @@ -180,7 +180,7 @@ public override void ParseExpressions(params string[] args) if (float.TryParse(RotationExpression, NumberStyles.Float, CultureInfo.InvariantCulture, out num)) Rotation = (short)num; else { - csharpExp = Regex.Replace(RotationExpression, @"\$(\d+)", "{$1}"); + csharpExp = Regex.Replace(RotationExpression, @"\$([0-9]+)", "{$1}"); csharpExp = string.Format(csharpExp, args); var temp = exp.Compute(csharpExp, null); if (temp is not DBNull) Rotation = Convert.ToSingle(temp); diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index c9a207fd..8c413903 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -908,12 +908,12 @@ public Mat LayerMat CvInvoke.Imdecode(_compressedBytes, ImreadModes.Grayscale, mat); break; case LayerCompressionCodec.Lz4: - mat = new Mat(Resolution, DepthType.Cv8U, 1); + mat = CreateMat(); LZ4Codec.Decode(_compressedBytes.AsSpan(), mat.GetDataByteSpan()); break; case LayerCompressionCodec.GZip: { - mat = new Mat(Resolution, DepthType.Cv8U, 1); + mat = CreateMat(); unsafe { fixed (byte* pBuffer = _compressedBytes) @@ -929,7 +929,7 @@ public Mat LayerMat } case LayerCompressionCodec.Deflate: { - mat = new Mat(Resolution, DepthType.Cv8U, 1); + mat = CreateMat(); unsafe { fixed (byte* pBuffer = _compressedBytes) @@ -1301,6 +1301,15 @@ public override string ToString() #region Methods + /// + /// Creates an using this layer information + /// + /// + public Mat CreateMat() + { + return new Mat(Resolution, DepthType.Cv8U, 1); + } + /// /// Reset all parameters to the default values from the global parameters /// diff --git a/UVtools.Core/SystemOS/SystemAware.cs b/UVtools.Core/SystemOS/SystemAware.cs index c73c0aa0..6be335e5 100644 --- a/UVtools.Core/SystemOS/SystemAware.cs +++ b/UVtools.Core/SystemOS/SystemAware.cs @@ -14,6 +14,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; +using UVtools.Core.Extensions; namespace UVtools.Core.SystemOS; @@ -67,7 +68,7 @@ public static MEMORYSTATUSEX GetMemoryStatus() const ushort factor = 1024; var result = File.ReadAllText("/proc/meminfo"); //var result = "MemTotal: 8288440 kB\nMemFree: 5616380 kB\nMemAvailable: 6885408 kB\nBuffers: 63240 kB\nCached: 1390996 kB\nSwapCached: 0 kB\nActive: 272516 kB\nInactive: 1773312 kB\nActive(anon): 1888 kB\nInactive(anon): 596168 kB\nActive(file): 270628 kB\nInactive(file): 1177144 kB\nUnevictable: 0 kB\nMlocked: 0 kB\nSwapTotal: 2097148 kB\nSwapFree: 2097148 kB\nDirty: 172 kB\nWriteback: 0 kB\nAnonPages: 591608 kB\nMapped: 292288 kB\nShmem: 6464 kB\nKReclaimable: 86228 kB\nSlab: 170544 kB\nSReclaimable: 86228 kB\nSUnreclaim: 84316 kB\nKernelStack: 12160 kB\nPageTables: 13992 kB\nNFS_Unstable: 0 kB\nBounce: 0 kB\nWritebackTmp: 0 kB\nCommitLimit: 6241368 kB\nCommitted_AS: 3588500 kB\nVmallocTotal: 34359738367 kB\nVmallocUsed: 61060 kB\nVmallocChunk: 0 kB\nPercpu: 91136 kB\nHardwareCorrupted: 0 kB\nAnonHugePages: 0 kB\nShmemHugePages: 0 kB\nShmemPmdMapped: 0 kB\nFileHugePages: 0 kB\nFilePmdMapped: 0 kB\nHugePages_Total: 0\nHugePages_Free: 0\nHugePages_Rsvd: 0\nHugePages_Surp: 0\nHugepagesize: 2048 kB\nHugetlb: 0 kB\nDirectMap4k: 216464 kB\nDirectMap2M: 4169728 kB\nDirectMap1G: 5242880 kB"; - var matches = Regex.Matches(result, @"(\S+):\s*(\d+).(\S+)"); + var matches = Regex.Matches(result, @"(\S+):\s*([0-9]+).(\S+)"); foreach (Match match in matches) { if (!match.Success || match.Groups.Count < 2) continue; @@ -107,12 +108,12 @@ public static MEMORYSTATUSEX GetMemoryStatus() if (string.IsNullOrWhiteSpace(result)) return statEX; //var result = "Mach Virtual Memory Statistics: (page size of 4096 bytes)\nPages free: 3044485.\nPages active: 400375.\nPages inactive: 235679.\nPages speculative: 189311.\nPages throttled: 0.\nPages wired down: 324269.\nPages purgeable: 27417.\n\"Translation faults\": 5500903.\nPages copy-on-write: 388354.\nPages zero filled: 2724856.\nPages reactivated: 410.\nPages purged: 972.\nFile-backed pages: 400847.\nAnonymous pages: 424518.\nPages stored in compressor: 0.\nPages occupied by compressor: 0.\nDecompressions: 0.\nCompressions: 0.\nPageins: 354428.\nPageouts: 0.\nSwapins: 0.\nSwapouts: 0."; - var matchPageSize = Regex.Match(result, @"page size of (\d+) bytes"); + var matchPageSize = Regex.Match(result, @"page size of ([0-9]+) bytes"); if (!matchPageSize.Success || matchPageSize.Groups.Count < 2) return statEX; ushort pageSize = ushort.Parse(matchPageSize.Groups[1].Value); - var matches = Regex.Matches(result, @"(.*):\s*(\d+)"); + var matches = Regex.Matches(result, @"(.*):\s*([0-9]+)"); foreach (Match match in matches) { if (!match.Success || match.Groups.Count < 2) continue; @@ -192,6 +193,62 @@ public static MEMORYSTATUSEX GetMemoryStatus() return null; } + public static string? GetGraphicCardName() + { + try + { + if (OperatingSystem.IsWindows()) + { + //HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000 + //HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000 + using var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0001") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0002") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0003") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0004") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0005") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0006") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0007") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0008") ?? + Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0009"); + + var gpu = key?.GetValue("DriverDesc")?.ToString(); + if (gpu is null) return null; + + try + { + var gpuMemBytes = key!.GetValue("HardwareInformation.qwMemorySize"); + if (gpuMemBytes is not null) + { + gpu += $" {SizeExtensions.SizeSuffix((long)gpuMemBytes, 1, false)}"; + } + } + catch (Exception e) + { + Debug.WriteLine(e); + } + + return gpu; + } + + if (OperatingSystem.IsLinux()) + { + return GetProcessOutput("bash", "-c \"lspci | grep ' VGA ' | cut -d' ' -f 1 | xargs -i lspci -v -s {} | grep 'Subsystem:' | cut -d':' -f 2\"").Trim(); + } + + if (OperatingSystem.IsMacOS()) + { + return GetProcessOutput("bash", "-c \"system_profiler SPDisplaysDataType | grep ' Chipset Model: ' | cut -d':' -f 2\"").Trim(); + } + } + catch (Exception e) + { + Debug.WriteLine(e); + } + + return null; + } + public static bool SelectFileOnExplorer(string filePath) { if (!File.Exists(filePath)) diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 94c2b1af..f20eee3f 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 3.14.2 + 3.14.3 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 diff --git a/UVtools.Installer/UVtools.Installer.wixproj b/UVtools.Installer/UVtools.Installer.wixproj index 0149720d..b85a0740 100644 --- a/UVtools.Installer/UVtools.Installer.wixproj +++ b/UVtools.Installer/UVtools.Installer.wixproj @@ -4,9 +4,9 @@ 3.7 UVtools - $([System.Text.RegularExpressions.Regex]::Match($(TF_BUILD_BUILDNUMBER), "\d+.\d+.\d+.\d+")) + $([System.Text.RegularExpressions.Regex]::Match($(TF_BUILD_BUILDNUMBER), "[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+")) - $([System.Text.RegularExpressions.Regex]::Match($(BUILD_BUILDNUMBER), "\d+.\d+.\d+.\d+")) + $([System.Text.RegularExpressions.Regex]::Match($(BUILD_BUILDNUMBER), "[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+")) 3.0.0 diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index c5244268..3d2da011 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -20,7 +20,9 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using UVtools.AvaloniaControls; @@ -924,6 +926,61 @@ public async void MenuFileSaveAsClicked() await this.MessageBoxError("Unable to find the target extension.", "Invalid extension"); return; } + + var defaultDirectory = string.IsNullOrWhiteSpace(Settings.General.DefaultDirectorySaveFile) + ? Path.GetDirectoryName(SlicerFile.FileFullPath) + : Settings.General.DefaultDirectorySaveFile; + var defaultFilename = $"{filename}_copy"; + + try + { + if (!string.IsNullOrWhiteSpace(Settings.General.FileSaveAsDefaultName)) + { + defaultFilename = Settings.General.FileSaveAsDefaultName; + var matches = Regex.Matches(defaultFilename, @"{([a-zA-z]+)}"); + if (matches.Count > 0) + { + foreach (Match match in matches) + { + if (!match.Success) continue; + var property = SlicerFile.GetType().GetProperty(match.Groups[1].Value, BindingFlags.Public | BindingFlags.Instance); + if (property is null || !property.CanRead || property.GetMethod is null) + { + defaultFilename = defaultFilename.Replace(match.Value, null); + continue; + } + + defaultFilename = defaultFilename.Replace(match.Value, property.GetValue(SlicerFile)?.ToString()); + } + } + + if (!string.IsNullOrWhiteSpace(Settings.General.FileSaveAsDefaultNameCleanUpRegex)) + { + filename = Regex.Replace(filename, Settings.General.FileSaveAsDefaultNameCleanUpRegex, string.Empty); + } + + defaultFilename = string.Format(defaultFilename, filename); + } + } + catch + { + defaultFilename = $"{filename}_copy"; + } + + var i = 0; + var searchFile = Path.Combine(defaultDirectory, $"{defaultFilename}.{ext}"); + while (File.Exists(searchFile)) + { + i++; + searchFile = Path.Combine(defaultDirectory, $"{defaultFilename}{i}.{ext}"); + } + + if (i > 0) + { + defaultFilename = $"{defaultFilename}{i}"; + } + + SaveFileDialog dialog = new() { DefaultExtension = extension.Extension, @@ -938,10 +995,9 @@ public async void MenuFileSaveAsClicked() } } }, - Directory = string.IsNullOrEmpty(Settings.General.DefaultDirectorySaveFile) - ? Path.GetDirectoryName(SlicerFile.FileFullPath) - : Settings.General.DefaultDirectorySaveFile, - InitialFileName = $"{Settings.General.FileSaveNamePrefix}{filename}{Settings.General.FileSaveNameSuffix}" + + Directory = defaultDirectory, + InitialFileName = defaultFilename }; var file = await dialog.ShowAsync(this); if (string.IsNullOrEmpty(file)) return; @@ -1924,9 +1980,9 @@ private async void ConvertToOnTapped(object? sender, RoutedEventArgs e) public async Task SaveFile(string filepath = null, bool ignoreOverwriteWarning = false) { - if (filepath is null) + if (filepath is null) // Not save as { - if (!ignoreOverwriteWarning && SavesCount == 0 && Settings.General.PromptOverwriteFileSave) + if (!ignoreOverwriteWarning && SavesCount == 0 && Settings.General.FileSavePromptOverwrite) { var result = await this.MessageBoxQuestion( "Original input file will be overwritten. Do you wish to proceed?", "Overwrite file?"); @@ -1954,18 +2010,35 @@ public async Task SaveFile(string filepath = null, bool ignoreOverwriteWar return false; }, Progress.Token); - - IsGUIEnabled = true; - + if (task) { SavesCount++; CanSave = false; UpdateTitle(); RefreshProperties(); // Some fields can change after encoding + + if (Settings.General.FileSaveUpdateNameWithNewInformation && oldFile == SlicerFile.FileFullPath) + { + try + { + var newFilename = SlicerFile.FilenameStripExtensions!; + newFilename = Regex.Replace(newFilename, @"[0-9]+h[0-9]+m([0-9]+s)?", SlicerFile.PrintTimeString); + newFilename = Regex.Replace(newFilename, @"(([0-9]*[.])?[0-9]+)ml", $"{SlicerFile.MaterialMillilitersInteger}ml"); + if (SlicerFile.RenameFile(newFilename)) RemoveRecentFile(oldFile); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + + } + if (oldFile != SlicerFile.FileFullPath) AddRecentFile(SlicerFile.FileFullPath); } + IsGUIEnabled = true; + return task; } diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs index 213c0818..8f666a0c 100644 --- a/UVtools.WPF/Structures/AppVersionChecker.cs +++ b/UVtools.WPF/Structures/AppVersionChecker.cs @@ -220,7 +220,7 @@ public async Task AutoUpgrade(OperationProgress progress) const string newFilename = $"{About.Software}.AppImage"; //var oldFileName = Path.GetFileName(appImagePath); // Try to keep same filename logic if user renamed the file, like UVtools.AppImage would keep same same - //var newFilename = Regex.Replace(oldFileName, @"v\d+.\d+.\d+", $"v{_version}"); + //var newFilename = Regex.Replace(oldFileName, @"v[0-9]+.[0-9]+.[0-9]+", $"v{_version}"); var newFullPath = Path.Combine(directory, newFilename); if (File.Exists(appImagePath)) File.Delete(appImagePath); @@ -290,7 +290,7 @@ public async Task AutoUpgrade(OperationProgress progress) if (About.VersionStr != _version) { var di = new DirectoryInfo(App.ApplicationPath); - var newDirectoryName = Regex.Replace(di.Name, $@"({About.Software}.*)(v\d+.\d+.\d+)", $@"$1v{_version}", RegexOptions.IgnoreCase); + var newDirectoryName = Regex.Replace(di.Name, $@"({About.Software}.*)(v[0-9]+[.][0-9]+[.][0-9]+)", $@"$1v{_version}", RegexOptions.IgnoreCase); if (di.Name != newDirectoryName) { await stream.WriteLineAsync(); diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 006fb37a..f28ff88f 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ LICENSE https://github.com/sn4k3/UVtools Git - 3.14.2 + 3.14.3 AnyCPU;x64 UVtools.png README.md diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs index a2692e50..9ea70e2d 100644 --- a/UVtools.WPF/UserSettings.cs +++ b/UVtools.WPF/UserSettings.cs @@ -59,13 +59,14 @@ public sealed class GeneralUserSettings : BindableBase private string _defaultDirectoryExtractFile; private string _defaultDirectoryConvertFile; private string _defaultDirectoryScripts; - private bool _promptOverwriteFileSave = true; - private string _fileSaveNamePrefix; - private string _fileSaveNameSuffix = "_copy"; + private bool _fileSavePromptOverwrite = true; + private string _fileSaveAsDefaultName = "{0}_{PrintTimeString}_{MaterialMillilitersInteger}ml_copy"; + private string _fileSaveAsDefaultNameCleanUpRegex = @"_?[0-9]+h[0-9]+m([0-9]+s)?|_?(([0-9]*[.])?[0-9]+)ml|_copy([0-9]*)?"; private bool _sendToPromptForRemovableDeviceEject = true; private RangeObservableCollection _sendToCustomLocations = new(); private RangeObservableCollection _sendToProcess = new(); private ushort _lockedFilesOpenCounter; + private bool _fileSaveUpdateNameWithNewInformation = true; public const byte LockedFilesMaxOpenCounter = 10; @@ -199,22 +200,28 @@ public string DefaultDirectoryScripts } - public bool PromptOverwriteFileSave + public bool FileSavePromptOverwrite { - get => _promptOverwriteFileSave; - set => RaiseAndSetIfChanged(ref _promptOverwriteFileSave, value); + get => _fileSavePromptOverwrite; + set => RaiseAndSetIfChanged(ref _fileSavePromptOverwrite, value); } - public string FileSaveNamePrefix + public bool FileSaveUpdateNameWithNewInformation { - get => _fileSaveNamePrefix; - set => RaiseAndSetIfChanged(ref _fileSaveNamePrefix, value); + get => _fileSaveUpdateNameWithNewInformation; + set => RaiseAndSetIfChanged(ref _fileSaveUpdateNameWithNewInformation, value); } - public string FileSaveNameSuffix + public string FileSaveAsDefaultName { - get => _fileSaveNameSuffix; - set => RaiseAndSetIfChanged(ref _fileSaveNameSuffix, value); + get => _fileSaveAsDefaultName; + set => RaiseAndSetIfChanged(ref _fileSaveAsDefaultName, value); + } + + public string FileSaveAsDefaultNameCleanUpRegex + { + get => _fileSaveAsDefaultNameCleanUpRegex; + set => RaiseAndSetIfChanged(ref _fileSaveAsDefaultNameCleanUpRegex, value); } public bool SendToPromptForRemovableDeviceEject @@ -1767,8 +1774,8 @@ public static void Load() // https://github.com/adamoutler/Pi-Zero-W-Smart-USB-Flash-Drive/tree/main/src/home/pi/usb_share/scripts CompatibleExtensions = "pws;pw0;pwx;dlp;dl2p;pwmo;pwma;pwms;pwmx;pmx2;pwmb;pwsq;pm3;pm3m;pm3r;pwc", RequestUploadFile = new (RemotePrinterRequest.RequestType.UploadFile, RemotePrinterRequest.RequestMethod.TCP), - RequestPrintFile = new (RemotePrinterRequest.RequestType.PrintFile, RemotePrinterRequest.RequestMethod.TCP, @"<$getfile>{0}\/(\d+\.[\da-zA-Z]+),>goprint,{#1}$>"), - RequestDeleteFile = new (RemotePrinterRequest.RequestType.DeleteFile, RemotePrinterRequest.RequestMethod.TCP, @"<$getfile>{0}\/(\d+\.[\da-zA-Z]+),>delfile,{#1}$>"), + RequestPrintFile = new (RemotePrinterRequest.RequestType.PrintFile, RemotePrinterRequest.RequestMethod.TCP, @"<$getfile>{0}\/([0-9]+[.][0-9a-zA-Z]+),>goprint,{#1}$>"), + RequestDeleteFile = new (RemotePrinterRequest.RequestType.DeleteFile, RemotePrinterRequest.RequestMethod.TCP, @"<$getfile>{0}\/([0-9]+[.][0-9a-zA-Z]+),>delfile,{#1}$>"), RequestPausePrint = new (RemotePrinterRequest.RequestType.PausePrint, RemotePrinterRequest.RequestMethod.TCP, "gopause"), RequestResumePrint = new (RemotePrinterRequest.RequestType.ResumePrint, RemotePrinterRequest.RequestMethod.TCP, "goresume"), RequestStopPrint = new (RemotePrinterRequest.RequestType.StopPrint, RemotePrinterRequest.RequestMethod.TCP, "gostop"), diff --git a/UVtools.WPF/Windows/AboutWindow.axaml b/UVtools.WPF/Windows/AboutWindow.axaml index 8b8f6a48..3511ce17 100644 --- a/UVtools.WPF/Windows/AboutWindow.axaml +++ b/UVtools.WPF/Windows/AboutWindow.axaml @@ -15,8 +15,8 @@ Icon="/Assets/Icons/UVtools.ico"> - - + + - - + + - + - - - - - - + + + + + + + + Watermark="Graphic Card:" + UseFloatingWatermark="True" + IsVisible="{Binding GraphicCardName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/> + + - - - + + + - - - + + + - - - + + + - - - + + + @@ -133,40 +132,37 @@ - + + Icon="fa-solid fa-balance-scale"/> - - + - + - + - - - + + + diff --git a/UVtools.WPF/Windows/AboutWindow.axaml.cs b/UVtools.WPF/Windows/AboutWindow.axaml.cs index 987278ed..2da5e2f1 100644 --- a/UVtools.WPF/Windows/AboutWindow.axaml.cs +++ b/UVtools.WPF/Windows/AboutWindow.axaml.cs @@ -71,6 +71,8 @@ public static string OpenCVVersion } } + public static string GraphicCardName => SystemAware.GetGraphicCardName(); + public static string ProcessorName => SystemAware.GetProcessorName(); public static int ProcessorCount => Environment.ProcessorCount; @@ -111,7 +113,7 @@ public string ScreensDescription ); result.AppendLine($" WA: {screen.WorkingArea.Width} x {screen.WorkingArea.Height} UA: {Math.Round(screen.WorkingArea.Width / screen.PixelDensity)} x {Math.Round(screen.WorkingArea.Height / screen.PixelDensity)}"); } - return result.ToString(); + return result.ToString().TrimEnd(); } } @@ -129,9 +131,11 @@ private void InitializeComponent() public static string GetEssentialInformationStatic() { + var gpu = GraphicCardName; var message = new StringBuilder(); message.AppendLine($"{About.SoftwareWithVersionArch}"); message.AppendLine($"Operative system: {OSDescription}"); + if (string.IsNullOrWhiteSpace(gpu)) message.AppendLine($"Graphic card: {gpu}"); message.AppendLine($"Processor: {ProcessorName}"); message.AppendLine($"Processor cores: {ProcessorCount}"); message.AppendLine($"Memory RAM: {MemoryRAMDescription}"); @@ -152,9 +156,11 @@ public static string GetEssentialInformationStatic() private string GetEssentialInformation() { + var gpu = GraphicCardName; var message = new StringBuilder(); message.AppendLine($"{About.SoftwareWithVersionArch}"); message.AppendLine($"Operative system: {OSDescription}"); + if(string.IsNullOrWhiteSpace(gpu)) message.AppendLine($"Graphic card: {gpu}"); message.AppendLine($"Processor: {ProcessorName}"); message.AppendLine($"Processor cores: {ProcessorCount}"); message.AppendLine($"Memory RAM: {MemoryRAMDescription}"); diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml index 82722c76..306ad3eb 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml +++ b/UVtools.WPF/Windows/SettingsWindow.axaml @@ -351,15 +351,30 @@ i:Attached.Icon="fa-solid fa-xmark"/> - - - - - - - + + + + + + + + + + diff --git a/build/createRelease.sh b/build/createRelease.sh index 64d09db3..98ff8977 100644 --- a/build/createRelease.sh +++ b/build/createRelease.sh @@ -18,8 +18,8 @@ for runtime in $@; do :; done # Get last argument rootDir="$PWD" buildDir="$rootDir/build" coreDir="$rootDir/UVtools.Core" -#version="$(grep -oP '\K(\d\.\d\.\d)(?=<\/Version>)' "$coreDir/UVtools.Core.csproj")" # Not supported on recent macos! -version="$(perl -nle'print $& while m{\K(\d+\.\d+\.\d+)(?=<\/Version>)}g' "$coreDir/UVtools.Core.csproj")" +#version="$(grep -oP '\K([0-9][.][0-9][.][0-9])(?=<\/Version>)' "$coreDir/UVtools.Core.csproj")" # Not supported on recent macos! +version="$(perl -nle'print $& while m{\K([0-9]+[.][0-9]+[.][0-9]+)(?=<\/Version>)}g' "$coreDir/UVtools.Core.csproj")" platformsDir="$buildDir/platforms" runtimePlatformDir="$platformsDir/$runtime" publishName="UVtools_${runtime}_v$version" diff --git a/build/cvextern.ps1 b/build/cvextern.ps1 index 6a840edf..5d83f7bb 100644 --- a/build/cvextern.ps1 +++ b/build/cvextern.ps1 @@ -33,7 +33,7 @@ Option" if ($confirmation -eq 'y' -or $confirmation -eq 'yes') { Write-Output "Clone master" git clone --recurse-submodules --depth 1 "https://github.com/emgucv/emgucv" "$libFolder" - }elseif($confirmation -match '^\d+\.\d+\.\d+$') { + }elseif($confirmation -match '^[0-9]+[.][0-9]+[.][0-9]+$') { Write-Output "$confirmation" git clone --recurse-submodules --depth 1 --branch "$confirmation" "https://github.com/emgucv/emgucv" "$libFolder" } diff --git a/build/libcvextern.sh b/build/libcvextern.sh index 5d3750a5..3728e9b3 100644 --- a/build/libcvextern.sh +++ b/build/libcvextern.sh @@ -46,7 +46,7 @@ Anwser: " confirmation echo "Continuing with master..." elif [ "$confirmation" == "n" -o "$confirmation" == "no" -o "$confirmation" == "cancel" ]; then exit 1 - elif [[ "$confirmation" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + elif [[ "$confirmation" =~ ^[0-9]+[.][0-9]+[.][0-9]+$ ]]; then echo "Changing from master to $confirmation branch" lastArg="$confirmation" else diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml index 90b93700..a144280d 100644 --- a/documentation/UVtools.Core.xml +++ b/documentation/UVtools.Core.xml @@ -2543,6 +2543,16 @@ Gets the input file path loaded into this + + + Returns the file name without the extension + + + + + Returns the file name without the extension(s) + + Returns the file extension. The returned value includes the period (".") character of the @@ -2551,9 +2561,12 @@ extension. - + - Returns the file name without the extension + Returns the file extension as safe method where it can include more than one extension. The returned value includes the period (".") character of the + extension except when you have a terminal period when you get string.Empty, such as ".exe" or ".cpp". + The returned value is null if the given path is null or empty if the given path does not include an + extension. @@ -3246,6 +3259,11 @@ Gets the estimate used material in ml + + + Gets the estimate used material in ml and rounded to next integer + + Gets the estimate material in grams @@ -3369,6 +3387,14 @@ Gets all valid file extensions in a specified format + + + Renames the current file with a new name in the same directory. + + New filename without the extension + True to overwrite file if exists, otherwise false + True if renamed, otherwise false. + Gets a thumbnail by it height or lower @@ -5522,6 +5548,12 @@ Gets tree contours cache for this layer. If not set it will calculate contours first + + + Creates an using this layer information + + + Reset all parameters to the default values from the global parameters