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:
authorTiago Conceição <Tiago_caza@hotmail.com>2022-10-12 03:16:34 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-10-12 03:16:34 +0300
commit9f7722d369eefcfe610f216ea8da2107bc17863a (patch)
tree1e245f8bc8c5e5cb219eba7b2e4caeff0ccc5560
parentcde8c51456b35a434e6028acf1b1ad1053a28456 (diff)
v3.7.0v3.7.0
- **File formats:** - (Add) `TransitionLayerCount` modifier to: Chitubox Zip, CWS, JXS, OSLA, PW*, UVJ, ZCodex, ZCode - (Add) Utility methods for transition layers calculation/parse - (Improvement) Calculate and set `TransitionLayerCount` property in file decode based on layer exposure time configuration - **GCode:** - (Improvement) GCode: Able to parse layer image file with appended numbers on the filename (Afecting CWS) (#577) - (Fix) Bad parsing of the file when it comes from Lychee or NovaMaker slicer (Afecting CWS) - (Fix) Incorrect parse of "Wait time before cure" from layers when printer require wait sync moves (Afecting CWS) - **Tools:** - (Add) External tests: The Complete Resin 3D Printing Settings Guide for Beginners - (Add) External tests: 9 settings for faster printing - (Improvement) Fade exposure time: Set `TransitionLayerCount` property with the affected layer count - **Suggestions:** - (Add) Transition layers: If you are printing flat on the build plate your model will print better when using a smooth transition exposure time instead of a harsh variation, resulting in reduced layer line effect and avoid possible problems due the large exposure difference. This is not so important when your model print raised under a raft/supports unaffected by the bottom exposure, in that case, it's fine to ignore this. - (Add) Model position: Printing on a corner will reduce the FEP stretch forces when detaching from the model during a lift sequence, benefits are: Reduced lift height and faster printing, less stretch, less FEP marks, better FEP lifespan, easier to peel, less prone to failure and use the screen pixels more evenly. If the model is too large to fit within the margin(s) on the screen, it will attempt to center it on that same axis to avoid touching on screen edge(s) and to give a sane margin from it. - **Status bar:** - (Add) Transition layers: 0/-0.00s - (Improvement) Change "Layer Height: 0.000mm" to "Layers: count @ 0.000mm" - (Improvement) Change "Bottom layers: 0" to "Bottom layers: 0/0.000mm" - (Change) Show user informative message about CTB Encrypted file format once per ten file loads - (Upgrade) .NET from 6.0.9 to 6.0.10 - (Fix) Windows MSI installation not upgrading well when downgrade libraries
-rw-r--r--CHANGELOG.md27
-rw-r--r--README.md1
-rw-r--r--RELEASE_NOTES.md25
-rw-r--r--UVtools.Core/Extensions/NumberExtensions.cs252
-rw-r--r--UVtools.Core/Extensions/StringExtensions.cs10
-rw-r--r--UVtools.Core/Extensions/TypeExtensions.cs2
-rw-r--r--UVtools.Core/FileFormats/CWSFile.cs3
-rw-r--r--UVtools.Core/FileFormats/ChituboxFile.cs3
-rw-r--r--UVtools.Core/FileFormats/ChituboxZipFile.cs1
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs168
-rw-r--r--UVtools.Core/FileFormats/JXSFile.cs1
-rw-r--r--UVtools.Core/FileFormats/OSLAFile.cs1
-rw-r--r--UVtools.Core/FileFormats/PhotonWorkshopFile.cs3
-rw-r--r--UVtools.Core/FileFormats/SL1File.cs2
-rw-r--r--UVtools.Core/FileFormats/UVJFile.cs1
-rw-r--r--UVtools.Core/FileFormats/ZCodeFile.cs1
-rw-r--r--UVtools.Core/FileFormats/ZCodexFile.cs3
-rw-r--r--UVtools.Core/GCode/GCodeBuilder.cs41
-rw-r--r--UVtools.Core/GCode/GCodeLayer.cs5
-rw-r--r--UVtools.Core/Managers/SuggestionManager.cs12
-rw-r--r--UVtools.Core/Network/MappedDevice.cs2
-rw-r--r--UVtools.Core/Network/RemotePrinterRequest.cs2
-rw-r--r--UVtools.Core/Objects/MappedProcess.cs2
-rw-r--r--UVtools.Core/Operations/OperationFadeExposureTime.cs13
-rw-r--r--UVtools.Core/Suggestions/Suggestion.cs13
-rw-r--r--UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs30
-rw-r--r--UVtools.Core/Suggestions/SuggestionLayerHeight.cs7
-rw-r--r--UVtools.Core/Suggestions/SuggestionModelPosition.cs290
-rw-r--r--UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs193
-rw-r--r--UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs5
-rw-r--r--UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs5
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.Installer/Code/HeatGeneratedFileList.wxs6
-rw-r--r--UVtools.Installer/Code/Product.wxs25
-rw-r--r--UVtools.Installer/UVtools.Installer.wixproj3
-rw-r--r--UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml69
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml111
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml.cs23
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml63
-rw-r--r--UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml.cs23
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMorphControl.axaml58
-rw-r--r--UVtools.WPF/MainWindow.PixelEditor.cs1
-rw-r--r--UVtools.WPF/MainWindow.Suggestions.cs62
-rw-r--r--UVtools.WPF/MainWindow.axaml25
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs10
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj2
-rw-r--r--UVtools.WPF/UserSettings.cs9
-rw-r--r--build/createRelease.ps12
-rw-r--r--documentation/UVtools.Core.xml128
49 files changed, 1580 insertions, 166 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1042e5a..2381245 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,32 @@
# Changelog
+## 12/10/2022 - v3.7.0
+
+- **File formats:**
+ - (Add) `TransitionLayerCount` modifier to: Chitubox Zip, CWS, JXS, OSLA, PW*, UVJ, ZCodex, ZCode
+ - (Add) Utility methods for transition layers calculation/parse
+ - (Improvement) Calculate and set `TransitionLayerCount` property in file decode based on layer exposure time configuration
+- **GCode:**
+ - (Improvement) GCode: Able to parse layer image file with appended numbers on the filename (Afecting CWS) (#577)
+ - (Fix) Bad parsing of the file when it comes from Lychee or NovaMaker slicer (Afecting CWS)
+ - (Fix) Incorrect parse of "Wait time before cure" from layers when printer require wait sync moves (Afecting CWS)
+- **Tools:**
+ - (Add) External tests: The Complete Resin 3D Printing Settings Guide for Beginners
+ - (Add) External tests: 9 settings for faster printing
+ - (Improvement) Fade exposure time: Set `TransitionLayerCount` property with the affected layer count
+- **Suggestions:**
+ - (Add) Transition layers: If you are printing flat on the build plate your model will print better when using a smooth transition exposure time instead of a harsh variation, resulting in reduced layer line effect and avoid possible problems due the large exposure difference.
+ This is not so important when your model print raised under a raft/supports unaffected by the bottom exposure, in that case, it's fine to ignore this.
+ - (Add) Model position: Printing on a corner will reduce the FEP stretch forces when detaching from the model during a lift sequence, benefits are: Reduced lift height and faster printing, less stretch, less FEP marks, better FEP lifespan, easier to peel, less prone to failure and use the screen pixels more evenly.
+ If the model is too large to fit within the margin(s) on the screen, it will attempt to center it on that same axis to avoid touching on screen edge(s) and to give a sane margin from it.
+- **Status bar:**
+ - (Add) Transition layers: 0/-0.00s
+ - (Improvement) Change "Layer Height: 0.000mm" to "Layers: count @ 0.000mm"
+ - (Improvement) Change "Bottom layers: 0" to "Bottom layers: 0/0.000mm"
+- (Change) Show user informative message about CTB Encrypted file format once per ten file loads
+- (Upgrade) .NET from 6.0.9 to 6.0.10
+- (Fix) Windows MSI installation not upgrading well when downgrade libraries
+
## 04/10/2022 - v3.6.8
- (Fix) Occasional crash when detecting islands and/or repair issues (#568, #569)
diff --git a/README.md b/README.md
index fa2b747..64b334c 100644
--- a/README.md
+++ b/README.md
@@ -256,7 +256,6 @@ The following commands are the old way and commands under the UI executable, the
**Copy the following script, paste and run on a terminal:**
-
```bash
[[ "$(command -v apt-get)" ]] && sudo apt-get install -y wget
[[ "$(command -v pacman)" ]] && sudo pacman -S wget
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index f07d248..3e8d757 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,2 +1,25 @@
-- (Fix) Occasional crash when detecting islands and/or repair issues (#568, #569)
+- **File formats:**
+ - (Add) `TransitionLayerCount` modifier to: Chitubox Zip, CWS, JXS, OSLA, PW*, UVJ, ZCodex, ZCode
+ - (Add) Utility methods for transition layers calculation/parse
+ - (Improvement) Calculate and set `TransitionLayerCount` property in file decode based on layer exposure time configuration
+- **GCode:**
+ - (Improvement) GCode: Able to parse layer image file with appended numbers on the filename (Afecting CWS) (#577)
+ - (Fix) Bad parsing of the file when it comes from Lychee or NovaMaker slicer (Afecting CWS)
+ - (Fix) Incorrect parse of "Wait time before cure" from layers when printer require wait sync moves (Afecting CWS)
+- **Tools:**
+ - (Add) External tests: The Complete Resin 3D Printing Settings Guide for Beginners
+ - (Add) External tests: 9 settings for faster printing
+ - (Improvement) Fade exposure time: Set `TransitionLayerCount` property with the affected layer count
+- **Suggestions:**
+ - (Add) Transition layers: If you are printing flat on the build plate your model will print better when using a smooth transition exposure time instead of a harsh variation, resulting in reduced layer line effect and avoid possible problems due the large exposure difference.
+ This is not so important when your model print raised under a raft/supports unaffected by the bottom exposure, in that case, it's fine to ignore this.
+ - (Add) Model position: Printing on a corner will reduce the FEP stretch forces when detaching from the model during a lift sequence, benefits are: Reduced lift height and faster printing, less stretch, less FEP marks, better FEP lifespan, easier to peel, less prone to failure and use the screen pixels more evenly.
+ If the model is too large to fit within the margin(s) on the screen, it will attempt to center it on that same axis to avoid touching on screen edge(s) and to give a sane margin from it.
+- **Status bar:**
+ - (Add) Transition layers: 0/-0.00s
+ - (Improvement) Change "Layer Height: 0.000mm" to "Layers: count @ 0.000mm"
+ - (Improvement) Change "Bottom layers: 0" to "Bottom layers: 0/0.000mm"
+- (Change) Show user informative message about CTB Encrypted file format once per ten file loads
+- (Upgrade) .NET from 6.0.9 to 6.0.10
+- (Fix) Windows MSI installation not upgrading well when downgrade libraries
diff --git a/UVtools.Core/Extensions/NumberExtensions.cs b/UVtools.Core/Extensions/NumberExtensions.cs
new file mode 100644
index 0000000..dd13e86
--- /dev/null
+++ b/UVtools.Core/Extensions/NumberExtensions.cs
@@ -0,0 +1,252 @@
+/*
+ * 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.
+ */
+
+namespace UVtools.Core.Extensions;
+
+public static class NumberExtensions
+{
+ /// <summary>
+ /// Convert bool to byte (0/1)
+ /// </summary>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ public static byte ToByte(this bool value) => (byte)(value ? 1 : 0);
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this sbyte n)
+ {
+ if (n >= 0)
+ {
+ return n switch
+ {
+ < 10 => 1,
+ < 100 => 2,
+ _ => 3
+ };
+ }
+
+ return n switch
+ {
+ > -10 => 1,
+ > -100 => 2,
+ _ => 3
+ };
+ }
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this byte n)
+ {
+ return n switch
+ {
+ < 10 => 1,
+ < 100 => 2,
+ _ => 3
+ };
+ }
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this short n)
+ {
+ if (n >= 0)
+ {
+ return n switch
+ {
+ < 10 => 1,
+ < 100 => 2,
+ < 1000 => 3,
+ _ => 4
+ };
+ }
+
+ return n switch
+ {
+ > -10 => 1,
+ > -100 => 2,
+ > -1000 => 3,
+ _ => 4
+ };
+ }
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this ushort n)
+ {
+ return n switch
+ {
+ < 10 => 1,
+ < 100 => 2,
+ < 1000 => 3,
+ _ => 4
+ };
+ }
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this int n)
+ {
+ if (n >= 0)
+ {
+ return n switch
+ {
+ < 10 => 1,
+ < 100 => 2,
+ < 1000 => 3,
+ < 10000 => 4,
+ < 100000 => 5,
+ < 1000000 => 6,
+ < 10000000 => 7,
+ < 100000000 => 8,
+ < 1000000000 => 9,
+ _ => 10
+ };
+ }
+
+ return n switch
+ {
+ > -10 => 1,
+ > -100 => 2,
+ > -1000 => 3,
+ > -10000 => 4,
+ > -100000 => 5,
+ > -1000000 => 6,
+ > -10000000 => 7,
+ > -100000000 => 8,
+ > -1000000000 => 9,
+ _ => 10
+ };
+ }
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this uint n)
+ {
+ return n switch
+ {
+ < 10U => 1,
+ < 100U => 2,
+ < 1000U => 3,
+ < 10000U => 4,
+ < 100000U => 5,
+ < 1000000U => 6,
+ < 10000000U => 7,
+ < 100000000U => 8,
+ < 1000000000U => 9,
+ _ => 10
+ };
+ }
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this long n)
+ {
+ if (n >= 0)
+ {
+ return n switch
+ {
+ < 10L => 1,
+ < 100L => 2,
+ < 1000L => 3,
+ < 10000L => 4,
+ < 100000L => 5,
+ < 1000000L => 6,
+ < 10000000L => 7,
+ < 100000000L => 8,
+ < 1000000000L => 9,
+ < 10000000000L => 10,
+ < 100000000000L => 11,
+ < 1000000000000L => 12,
+ < 10000000000000L => 13,
+ < 100000000000000L => 14,
+ < 1000000000000000L => 15,
+ < 10000000000000000L => 16,
+ < 100000000000000000L => 17,
+ < 1000000000000000000L => 18,
+ _ => 19
+ };
+ }
+
+ return n switch
+ {
+ > -10L => 1,
+ > -100L => 2,
+ > -1000L => 3,
+ > -10000L => 4,
+ > -100000L => 5,
+ > -1000000L => 6,
+ > -10000000L => 7,
+ > -100000000L => 8,
+ > -1000000000L => 9,
+ > -10000000000L => 10,
+ > -100000000000L => 11,
+ > -1000000000000L => 12,
+ > -10000000000000L => 13,
+ > -100000000000000L => 14,
+ > -1000000000000000L => 15,
+ > -10000000000000000L => 16,
+ > -100000000000000000L => 17,
+ > -1000000000000000000L => 18,
+ _ => 19
+ };
+ }
+
+ /// <summary>
+ /// Gets the number of digits this number haves
+ /// </summary>
+ /// <param name="n"></param>
+ /// <returns>Digit count</returns>
+ public static int DigitCount(this ulong n)
+ {
+ return n switch
+ {
+ < 10UL => 1,
+ < 100UL => 2,
+ < 1000UL => 3,
+ < 10000UL => 4,
+ < 100000UL => 5,
+ < 1000000UL => 6,
+ < 10000000UL => 7,
+ < 100000000UL => 8,
+ < 1000000000UL => 9,
+ < 10000000000UL => 10,
+ < 100000000000UL => 11,
+ < 1000000000000UL => 12,
+ < 10000000000000UL => 13,
+ < 100000000000000UL => 14,
+ < 1000000000000000UL => 15,
+ < 10000000000000000UL => 16,
+ < 100000000000000000UL => 17,
+ < 1000000000000000000UL => 18,
+ < 10000000000000000000UL => 19,
+ _ => 20
+ };
+ }
+} \ No newline at end of file
diff --git a/UVtools.Core/Extensions/StringExtensions.cs b/UVtools.Core/Extensions/StringExtensions.cs
index 23943d4..d30b2cb 100644
--- a/UVtools.Core/Extensions/StringExtensions.cs
+++ b/UVtools.Core/Extensions/StringExtensions.cs
@@ -34,8 +34,14 @@ public static class StringExtensions
};
}
- public static string Repeat(this string s, int n)
- => n <= 0 ? string.Empty : new StringBuilder(s.Length * n).Insert(0, s, n).ToString();
+ /// <summary>
+ /// Repeat this string <paramref name="count"/> times
+ /// </summary>
+ /// <param name="str">String to repeat</param>
+ /// <param name="count">Number of times to repeat</param>
+ /// <returns><paramref name="str"/> repeated <paramref name="count"/> times</returns>
+ public static string Repeat(this string str, int count)
+ => count <= 0 ? string.Empty : new StringBuilder(str.Length * count).Insert(0, str, count).ToString();
/// <summary>
/// Converts a string into a target type
diff --git a/UVtools.Core/Extensions/TypeExtensions.cs b/UVtools.Core/Extensions/TypeExtensions.cs
index 39be38e..1a8b331 100644
--- a/UVtools.Core/Extensions/TypeExtensions.cs
+++ b/UVtools.Core/Extensions/TypeExtensions.cs
@@ -39,8 +39,6 @@ public static class TypeExtensions
return (T)instance;
}
- public static byte ToByte(this bool value) => (byte)(value ? 1 : 0);
-
public static IEnumerable<Type> GetTypesInNamespace(Assembly assembly, string nameSpace)
{
return assembly.GetTypes()
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs
index 0414165..b4b5e0e 100644
--- a/UVtools.Core/FileFormats/CWSFile.cs
+++ b/UVtools.Core/FileFormats/CWSFile.cs
@@ -321,6 +321,7 @@ public class CWSFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomWaitTimeBeforeCure,
PrintParameterModifier.WaitTimeBeforeCure,
@@ -840,7 +841,7 @@ public class CWSFile : FileFormat
}
}
- DecodeLayersFromZipRegex(inputFile, @"(\d+).png", IndexStartNumber.Zero, progress);
+ DecodeLayersFromZipIgnoreFilename(inputFile, progress);
GCode.ParseLayersFromGCode(this);
}
diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs
index 2b0b86c..eb7c6e9 100644
--- a/UVtools.Core/FileFormats/ChituboxFile.cs
+++ b/UVtools.Core/FileFormats/ChituboxFile.cs
@@ -1826,8 +1826,7 @@ public class ChituboxFile : FileFormat
{
if (HeaderSettings.EncryptionKey == 0)
{
- var rnd = new Random();
- HeaderSettings.EncryptionKey = (uint)rnd.Next(byte.MaxValue, int.MaxValue);
+ HeaderSettings.EncryptionKey = (uint)new Random().Next(byte.MaxValue, int.MaxValue);
}
}
else
diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs
index 847f2ec..c6d0448 100644
--- a/UVtools.Core/FileFormats/ChituboxZipFile.cs
+++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs
@@ -85,6 +85,7 @@ public class ChituboxZipFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomWaitTimeBeforeCure,
PrintParameterModifier.WaitTimeBeforeCure,
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index f60faf6..a252117 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -1796,10 +1796,14 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
set {
RaisePropertyChanged();
RaisePropertyChanged(nameof(NormalLayerCount));
+ RaisePropertyChanged(nameof(TransitionLayersRepresentation));
}
}
- public byte LayerDigits => (byte)LayerCount.ToString().Length;
+ /// <summary>
+ /// Return the number of digits on the layer count number, eg: 123 layers = 3 digits
+ /// </summary>
+ public byte LayerDigits => (byte)LayerCount.DigitCount();
/// <summary>
/// Gets or sets the total height for the bottom layers in millimeters
@@ -1822,13 +1826,14 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
{
RaiseAndSet(ref _bottomLayerCount, value);
RaisePropertyChanged(nameof(NormalLayerCount));
+ RaisePropertyChanged(nameof(TransitionLayersRepresentation));
}
}
/// <summary>
/// Gets the transition layer type
/// </summary>
- public virtual TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Firmware;
+ public virtual TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software;
/// <summary>
/// Gets or sets the number of transition layers
@@ -1840,6 +1845,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
{
RaiseAndSet(ref _transitionLayerCount, (ushort)Math.Min(value, MaximumPossibleTransitionLayerCount));
RaisePropertyChanged(nameof(HaveTransitionLayers));
+ RaisePropertyChanged(nameof(TransitionLayersRepresentation));
}
}
@@ -1931,6 +1937,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
{
RaiseAndSet(ref _bottomExposureTime, (float)Math.Round(value, 2));
RaisePropertyChanged(nameof(ExposureRepresentation));
+ RaisePropertyChanged(nameof(TransitionLayersRepresentation));
}
}
@@ -1944,6 +1951,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
{
RaiseAndSet(ref _exposureTime, (float)Math.Round(value, 2));
RaisePropertyChanged(nameof(ExposureRepresentation));
+ RaisePropertyChanged(nameof(TransitionLayersRepresentation));
}
}
@@ -2426,6 +2434,30 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
public bool CanUseLayerAnyWaitTimeBeforeCure => CanUseLayerWaitTimeBeforeCure || CanUseLayerLightOffDelay;
public bool CanUseLayerLightPWM => HaveLayerParameterModifier(PrintParameterModifier.LightPWM);
+ public string TransitionLayersRepresentation
+ {
+ get
+ {
+ var str = TransitionLayerCount.ToString(CultureInfo.InvariantCulture);
+
+ if (!CanUseTransitionLayerCount)
+ {
+ return str;
+ }
+
+ if (TransitionLayerCount > 0)
+ {
+ var decrement = ParseTransitionStepTimeFromLayers();
+ if (decrement != 0)
+ {
+ str += $"/{-decrement}s";
+ }
+ }
+
+ return str;
+ }
+ }
+
public string ExposureRepresentation
{
get
@@ -3420,6 +3452,15 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
progress.Reset(OperationProgress.StatusGatherLayers, LayerCount);
DecodeInternally(progress);
+ progress.ThrowIfCancellationRequested();
+
+ var layerHeightDigits = LayerHeight.DecimalDigits();
+ if (layerHeightDigits > Layer.HeightPrecision)
+ {
+ throw new FileLoadException($"The layer height ({LayerHeight}mm) have more decimal digits than the supported ({Layer.HeightPrecision}) digits.\n" +
+ "Lower and fix your layer height on slicer to avoid precision errors.", fileFullPath);
+ }
+
IsModified = false;
StartingMaterialMilliliters = MaterialMilliliters;
StartingMaterialCost = MaterialCost;
@@ -3429,13 +3470,9 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
MaterialCost = StartingMaterialCost;
}
- progress.ThrowIfCancellationRequested();
-
- var layerHeightDigits = LayerHeight.DecimalDigits();
- if (layerHeightDigits > Layer.HeightPrecision)
+ if (CanUseTransitionLayerCount && TransitionLayerType == TransitionLayerTypes.Software)
{
- throw new FileLoadException($"The layer height ({LayerHeight}mm) have more decimal digits than the supported ({Layer.HeightPrecision}) digits.\n" +
- "Lower and fix your layer height on slicer to avoid precision errors.", fileFullPath);
+ SuppressRebuildPropertiesWork(() => TransitionLayerCount = ParseTransitionLayerCountFromLayers());
}
bool reSaveFile = Sanitize();
@@ -3632,14 +3669,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
foreach (var entry in zipArchive.Entries)
{
var match = Regex.Match(entry.Name, regex);
- if (!match.Success || match.Groups.Count < 2) continue;
- if (!uint.TryParse(match.Groups[1].Value, out var layerIndex)) continue;
+ if (!match.Success || match.Groups.Count < 2 || match.Groups[1].Value.Length == 0 || !uint.TryParse(match.Groups[1].Value, out var layerIndex)) continue;
+
if (layerIndexStartNumber == IndexStartNumber.One && layerIndex > 0) layerIndex--;
- if (layerIndex >= LayerCount)
- {
- continue;
- }
+ if (layerIndex >= LayerCount) continue;
layerEntries[layerIndex] = entry;
}
@@ -3656,16 +3690,24 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
}
public void DecodeLayersFromZip(ZipArchive zipArchive, byte padDigits, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func<uint, byte[], Mat>? matGenFunc = null)
- => DecodeLayersFromZipRegex(zipArchive, $@"(\d{{{padDigits}}}).png", layerIndexStartNumber, progress, matGenFunc);
-
- public void DecodeLayersFromZip(ZipArchive zipArchive, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func<uint, byte[], Mat>? matGenFunc = null)
- => DecodeLayersFromZipRegex(zipArchive, @"^(\d+).png", layerIndexStartNumber, progress, matGenFunc);
+ => DecodeLayersFromZipRegex(zipArchive, $@"(\d{{{padDigits}}}).png$", layerIndexStartNumber, progress, matGenFunc);
public void DecodeLayersFromZip(ZipArchive zipArchive, string prepend, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func<uint, byte[], Mat>? matGenFunc = null)
- => DecodeLayersFromZipRegex(zipArchive, $@"^{Regex.Escape(prepend)}(\d+).png", layerIndexStartNumber, progress, matGenFunc);
+ => DecodeLayersFromZipRegex(zipArchive, $@"^{Regex.Escape(prepend)}(\d+).png$", layerIndexStartNumber, progress, matGenFunc);
+
+ public void DecodeLayersFromZip(ZipArchive zipArchive, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func<uint, byte[], Mat>? matGenFunc = null)
+ => DecodeLayersFromZipRegex(zipArchive, @"^(\d+).png$", layerIndexStartNumber, progress, matGenFunc);
public void DecodeLayersFromZip(ZipArchive zipArchive, OperationProgress progress, Func<uint, byte[], Mat>? matGenFunc = null)
- => DecodeLayersFromZipRegex(zipArchive, @"^(\d+).png", IndexStartNumber.Zero, progress, matGenFunc);
+ => DecodeLayersFromZipRegex(zipArchive, @"^(\d+).png$", IndexStartNumber.Zero, progress, matGenFunc);
+
+ public void DecodeLayersFromZipIgnoreFilename(ZipArchive zipArchive, IndexStartNumber layerIndexStartNumber = IndexStartNumber.Zero, OperationProgress? progress = null, Func<uint, byte[], Mat>? matGenFunc = null)
+ {
+ DecodeLayersFromZipRegex(zipArchive, @$"({@"\d?".Repeat(LayerDigits - 1)}\d).png$", layerIndexStartNumber, progress, matGenFunc);
+ }
+
+ public void DecodeLayersFromZipIgnoreFilename(ZipArchive zipArchive, OperationProgress progress, Func<uint, byte[], Mat>? matGenFunc = null)
+ => DecodeLayersFromZipIgnoreFilename(zipArchive, IndexStartNumber.Zero, progress, matGenFunc);
/// <summary>
/// Extract contents to a folder
@@ -3824,6 +3866,84 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
}
/// <summary>
+ /// Gets the transition layer count calculated from layer exposure time configuration
+ /// </summary>
+ /// <returns>Transition layer count</returns>
+ public ushort ParseTransitionLayerCountFromLayers()
+ {
+ ushort count = 0;
+ for (uint layerIndex = BottomLayerCount + 1u; layerIndex < LayerCount; layerIndex++)
+ {
+ if (Math.Abs(this[layerIndex - 1].ExposureTime - this[layerIndex].ExposureTime) < 0.009f) break; // First equal layer, transition ended
+ count++;
+ }
+
+ return count;
+ }
+
+ /// <summary>
+ /// Parse the transition step time from layers, value is returned as positive from normal perspective and logic (Longer - shorter)
+ /// </summary>
+ /// <returns>Seconds</returns>
+ public float ParseTransitionStepTimeFromLayers()
+ {
+ var transitionLayerCount = ParseTransitionLayerCountFromLayers();
+ return transitionLayerCount == 0
+ ? 0
+ : (float)Math.Round(this[BottomLayerCount].ExposureTime - this[BottomLayerCount + 1].ExposureTime, 2);
+ }
+
+ /// <summary>
+ /// Gets the transition step time from a long and short exposure time, value is returned as positive from normal perspective and logic (Longer - shorter)
+ /// </summary>
+ /// <param name="longExposureTime">The long exposure time</param>
+ /// <param name="shortExposureTime">The small exposure time</param>
+ /// <param name="transitionLayerCount">Number of transition layers</param>
+ /// <returns>Seconds</returns>
+ public static float GetTransitionStepTime(float longExposureTime, float shortExposureTime, ushort transitionLayerCount)
+ {
+ return transitionLayerCount == 0 ? 0 : (float)Math.Round((longExposureTime - shortExposureTime) / (transitionLayerCount + 1), 2, MidpointRounding.AwayFromZero);
+ }
+
+ /// <summary>
+ /// Gets the transition step time from <see cref="BottomExposureTime"/> and <see cref="ExposureTime"/>, value is returned as positive from normal perspective and logic (Longer - shorter)
+ /// </summary>
+ /// <param name="transitionLayerCount">Number of transition layers</param>
+ /// <returns>Seconds</returns>
+ public float GetTransitionStepTime(ushort transitionLayerCount)
+ {
+ return GetTransitionStepTime(BottomExposureTime, ExposureTime, transitionLayerCount);
+ }
+
+ /// <summary>
+ /// Gets the transition layer count based on long and short exposure time
+ /// </summary>
+ /// <param name="longExposureTime">The long exposure time</param>
+ /// <param name="shortExposureTime">The small exposure time</param>
+ /// <param name="decrementTime">Decrement time</param>
+ /// <param name="rounding">Midpoint rounding method</param>
+ /// <returns></returns>
+ public static ushort GetTransitionLayerCount(float longExposureTime, float shortExposureTime, float decrementTime, MidpointRounding rounding = MidpointRounding.AwayFromZero)
+ {
+ return decrementTime == 0 ? (ushort)0 : (ushort)Math.Round((longExposureTime - shortExposureTime) / decrementTime - 1, rounding);
+ }
+
+ /// <summary>
+ /// Gets the transition layer count based on <see cref="BottomExposureTime"/> and <see cref="ExposureTime"/>
+ /// </summary>
+ /// <param name="stepDecrementTime">Step decrement time in seconds</param>
+ /// <param name="constrainToLayerCount">True if transition layer count can't be higher than supported by the file, otherwise set to false to not look at possible file layers</param>
+ /// <param name="rounding">Midpoint rounding method</param>
+ /// <returns>Transition layer count</returns>
+ public ushort GetTransitionLayerCount(float stepDecrementTime, bool constrainToLayerCount = true, MidpointRounding rounding = MidpointRounding.AwayFromZero)
+ {
+ var count = GetTransitionLayerCount(BottomExposureTime, ExposureTime, stepDecrementTime, rounding);
+ if (constrainToLayerCount) count = (ushort)Math.Min(count, MaximumPossibleTransitionLayerCount);
+ return count;
+ }
+
+
+ /// <summary>
/// Re-set exposure time to the transition layers
/// </summary>
/// <param name="resetExposureTimes">True to default all the previous transition layers exposure time, otherwise false</param>
@@ -3845,7 +3965,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
for (uint layerIndex = BottomLayerCount; layerIndex < LayerCount; layerIndex++)
{
var layer = this[layerIndex];
- if (layer.ExposureTime == ExposureTime) break; // First equal layer, transition ended
+ if (Math.Abs(layer.ExposureTime - ExposureTime) < 0.009) break; // First equal layer, transition ended
layer.ExposureTime = ExposureTime;
}
@@ -3853,14 +3973,14 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
if (transitionLayerCount == 0) return;
- float increment = Math.Max((BottomExposureTime - ExposureTime) / (transitionLayerCount + 1), 0f);
- if (increment <= 0) return;
+ float decrement = GetTransitionStepTime(transitionLayerCount);
+ if (decrement <= 0) return;
uint appliedLayers = 0;
for (uint layerIndex = BottomLayerCount; appliedLayers < transitionLayerCount && layerIndex < LayerCount; layerIndex++)
{
appliedLayers++;
- this[layerIndex].ExposureTime = Math.Clamp(BottomExposureTime - (increment * appliedLayers), ExposureTime, BottomExposureTime);
+ this[layerIndex].ExposureTime = Math.Clamp(BottomExposureTime - (decrement * appliedLayers), ExposureTime, BottomExposureTime);
}
}
diff --git a/UVtools.Core/FileFormats/JXSFile.cs b/UVtools.Core/FileFormats/JXSFile.cs
index a9a9557..d3559e7 100644
--- a/UVtools.Core/FileFormats/JXSFile.cs
+++ b/UVtools.Core/FileFormats/JXSFile.cs
@@ -93,6 +93,7 @@ public class JXSFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomWaitTimeBeforeCure,
PrintParameterModifier.WaitTimeBeforeCure,
diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs
index 67a9a14..7569d02 100644
--- a/UVtools.Core/FileFormats/OSLAFile.cs
+++ b/UVtools.Core/FileFormats/OSLAFile.cs
@@ -255,6 +255,7 @@ public class OSLAFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomWaitTimeBeforeCure,
PrintParameterModifier.WaitTimeBeforeCure,
diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
index 815d361..5a50448 100644
--- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
+++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
@@ -1124,7 +1124,7 @@ public class PhotonWorkshopFile : FileFormat
return new[]
{
PrintParameterModifier.BottomLayerCount,
- PrintParameterModifier.TransitionLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.WaitTimeBeforeCure,
@@ -1152,6 +1152,7 @@ public class PhotonWorkshopFile : FileFormat
return new[]
{
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.WaitTimeBeforeCure,
diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs
index 04fe5c2..417e71d 100644
--- a/UVtools.Core/FileFormats/SL1File.cs
+++ b/UVtools.Core/FileFormats/SL1File.cs
@@ -518,7 +518,7 @@ public class SL1File : FileFormat
#endregion
- #region Contructors
+ #region Constructors
public SL1File() { }
#endregion
diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs
index 650a141..786f3ae 100644
--- a/UVtools.Core/FileFormats/UVJFile.cs
+++ b/UVtools.Core/FileFormats/UVJFile.cs
@@ -182,6 +182,7 @@ public class UVJFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomLightOffDelay,
PrintParameterModifier.LightOffDelay,
diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs
index 4c6ee4d..bdaaf1b 100644
--- a/UVtools.Core/FileFormats/ZCodeFile.cs
+++ b/UVtools.Core/FileFormats/ZCodeFile.cs
@@ -183,6 +183,7 @@ public class ZCodeFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomWaitTimeBeforeCure,
PrintParameterModifier.WaitTimeBeforeCure,
diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs
index 6ecbe4b..ed49591 100644
--- a/UVtools.Core/FileFormats/ZCodexFile.cs
+++ b/UVtools.Core/FileFormats/ZCodexFile.cs
@@ -166,7 +166,8 @@ public class ZCodexFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
-
+ PrintParameterModifier.TransitionLayerCount,
+
PrintParameterModifier.WaitTimeBeforeCure,
PrintParameterModifier.BottomExposureTime,
diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs
index cb2791d..d35fb54 100644
--- a/UVtools.Core/GCode/GCodeBuilder.cs
+++ b/UVtools.Core/GCode/GCodeBuilder.cs
@@ -907,6 +907,8 @@ public class GCodeBuilder : BindableBase
var layerBlock = new GCodeLayer(slicerFile);
+ bool parseLayerIndexFromComments = false;
+
using var reader = new StringReader(gcode);
string? line;
while ((line = reader.ReadLine()) != null)
@@ -943,6 +945,7 @@ public class GCodeBuilder : BindableBase
}
layerBlock.SetLayer(true);
layerBlock.LayerIndex = layerIndex;
+ parseLayerIndexFromComments = true;
continue;
}
}
@@ -950,9 +953,7 @@ public class GCodeBuilder : BindableBase
// Display image
if (line.StartsWith(CommandShowImageM6054.Command))
{
- match = Regex.Match(line,
- CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")),
- RegexOptions.IgnoreCase);
+ match = Regex.Match(line, CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")), RegexOptions.IgnoreCase);
if (match.Success && match.Groups.Count >= 2) // Begin new layer
{
var layerIndex = uint.Parse(match.Groups[1].Value);
@@ -965,12 +966,12 @@ public class GCodeBuilder : BindableBase
}
// Propagate values before switch to the new layer
- if (_gCodeShowImagePosition == GCodeShowImagePositions.FirstLine && layerBlock.LayerIndex != layerIndex)
+ if (/*_gCodeShowImagePosition == GCodeShowImagePositions.FirstLine && */layerBlock.LayerIndex != layerIndex)
{
layerBlock.SetLayer(true);
+ layerBlock.LayerIndex = layerIndex;
}
- layerBlock.LayerIndex = layerIndex;
continue;
}
@@ -981,18 +982,13 @@ public class GCodeBuilder : BindableBase
// Check moves
if (line.StartsWith(CommandMoveG0.Command) || line.StartsWith(CommandMoveG1.Command))
{
- var moveG0Regex = Regex.Match(line,
- CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"),
- RegexOptions.IgnoreCase);
- var moveG1Regex = Regex.Match(line,
- CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"),
- RegexOptions.IgnoreCase);
- match = moveG0Regex.Success && moveG0Regex.Groups.Count >= 2 ? moveG0Regex : moveG1Regex;
-
- if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed2.HasValue && layerBlock.LightOffCount < 2)
+ match = Regex.Match(line, $"{CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)")}|{CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)")}", RegexOptions.IgnoreCase);
+
+ if (match.Success && match.Groups.Count >= 9 && !layerBlock.RetractSpeed2.HasValue && layerBlock.LightOffCount < 2)
{
- float pos = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
- float speed = ConvertToMillimetersPerMinute(float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture));
+ var startIndex = match.Groups[1].Value.Length > 0 ? 0 : 4;
+ float pos = float.Parse(match.Groups[startIndex+1].Value, CultureInfo.InvariantCulture);
+ float speed = ConvertToMillimetersPerMinute(float.Parse(match.Groups[startIndex+3].Value, CultureInfo.InvariantCulture));
layerBlock.Movements.Add(new(pos, speed));
@@ -1037,12 +1033,14 @@ public class GCodeBuilder : BindableBase
// Check for waits
if (line.StartsWith(CommandWaitG4.Command))
{
- match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"),
- RegexOptions.IgnoreCase);
+ match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase);
if (match.Success && match.Groups.Count >= 2)
{
- if (/*CommandWaitSyncDelay.Enabled && */match.Groups[1].Value.StartsWith('0')) continue; // Sync movement delay, skip
-
+ if (/*CommandWaitSyncDelay.Enabled && */match.Groups[1].Value.StartsWith('0'))
+ {
+ layerBlock.WaitSyncDelayDetected = true;
+ continue; // Sync movement delay, skip
+ }
var waitTime = float.Parse(match.Groups[1].Value);
if (layerBlock.PositionZ.HasValue &&
@@ -1091,8 +1089,7 @@ public class GCodeBuilder : BindableBase
// Check LightPWM
if (line.StartsWith(CommandTurnLEDM106.Command))
{
- match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"),
- RegexOptions.IgnoreCase);
+ match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase);
if (match.Success && match.Groups.Count >= 2)
{
byte pwm;
diff --git a/UVtools.Core/GCode/GCodeLayer.cs b/UVtools.Core/GCode/GCodeLayer.cs
index 400b562..29181e3 100644
--- a/UVtools.Core/GCode/GCodeLayer.cs
+++ b/UVtools.Core/GCode/GCodeLayer.cs
@@ -43,6 +43,8 @@ public class GCodeLayer
public float PreviousPositionZ { get; set; }
+ public bool WaitSyncDelayDetected { get; set; }
+
public float? WaitTimeBeforeCure
{
get => _waitTimeBeforeCure;
@@ -118,6 +120,7 @@ public class GCodeLayer
public byte LightOffCount { get; set; }
public bool IsAfterLightOff => LightOffCount > 0;
+
public GCodeLayer(FileFormat slicerFile)
{
@@ -299,7 +302,7 @@ public class GCodeLayer
layer.RetractSpeed2 = RetractSpeed2 ?? SlicerFile.GetBottomOrNormalValue(layer, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2);
layer.LightPWM = LightPWM ?? 0;//SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM);
- if (SlicerFile.GCode!.CommandWaitSyncDelay.Enabled) // Dirty fix of the value
+ if (SlicerFile.GCode!.CommandWaitSyncDelay.Enabled && !WaitSyncDelayDetected) // Dirty fix of the value
{
var syncTime = OperationCalculator.LightOffDelayC.CalculateSeconds(layer, 1.5f);
if (syncTime < layer.WaitTimeBeforeCure)
diff --git a/UVtools.Core/Managers/SuggestionManager.cs b/UVtools.Core/Managers/SuggestionManager.cs
index af7c7c7..cd6b272 100644
--- a/UVtools.Core/Managers/SuggestionManager.cs
+++ b/UVtools.Core/Managers/SuggestionManager.cs
@@ -26,9 +26,11 @@ public class SuggestionManager
public static string FilePath => Path.Combine(CoreSettings.DefaultSettingsFolderAndEnsureCreation, "suggestions.xml");
public SuggestionBottomLayerCount BottomLayerCount { get; set; } = new();
+ public SuggestionTransitionLayerCount TransitionLayerCount { get; set; } = new();
public SuggestionWaitTimeBeforeCure WaitTimeBeforeCure { get; set; } = new();
public SuggestionWaitTimeAfterCure WaitTimeAfterCure { get; set; } = new();
public SuggestionLayerHeight LayerHeight { get; set; } = new();
+ public SuggestionModelPosition ModelPosition { get; set; } = new();
/// <summary>
/// Gets all suggestions
@@ -41,9 +43,11 @@ public class SuggestionManager
return new Suggestion[]
{
BottomLayerCount,
+ TransitionLayerCount,
WaitTimeBeforeCure,
WaitTimeAfterCure,
- LayerHeight
+ LayerHeight,
+ ModelPosition,
};
}
set
@@ -64,6 +68,9 @@ public class SuggestionManager
case SuggestionBottomLayerCount bottomLayerCount:
BottomLayerCount = bottomLayerCount;
break;
+ case SuggestionTransitionLayerCount transitionLayerCount:
+ TransitionLayerCount = transitionLayerCount;
+ break;
case SuggestionWaitTimeBeforeCure waitTimeBeforeCure:
WaitTimeBeforeCure = waitTimeBeforeCure;
break;
@@ -73,6 +80,9 @@ public class SuggestionManager
case SuggestionLayerHeight layerHeight:
LayerHeight = layerHeight;
break;
+ case SuggestionModelPosition modelPosition:
+ ModelPosition = modelPosition;
+ break;
default: throw new ArgumentOutOfRangeException(nameof(suggestion));
}
}
diff --git a/UVtools.Core/Network/MappedDevice.cs b/UVtools.Core/Network/MappedDevice.cs
index 87c60cb..582a23e 100644
--- a/UVtools.Core/Network/MappedDevice.cs
+++ b/UVtools.Core/Network/MappedDevice.cs
@@ -62,7 +62,7 @@ public class MappedDevice : BindableBase
#endregion
- #region Contructors
+ #region Constructors
public MappedDevice() { }
diff --git a/UVtools.Core/Network/RemotePrinterRequest.cs b/UVtools.Core/Network/RemotePrinterRequest.cs
index 2894885..1936944 100644
--- a/UVtools.Core/Network/RemotePrinterRequest.cs
+++ b/UVtools.Core/Network/RemotePrinterRequest.cs
@@ -97,7 +97,7 @@ public class RemotePrinterRequest : BindableBase
#endregion
- #region Contructors
+ #region Constructors
public RemotePrinterRequest() { }
diff --git a/UVtools.Core/Objects/MappedProcess.cs b/UVtools.Core/Objects/MappedProcess.cs
index addfe3f..9abe4d1 100644
--- a/UVtools.Core/Objects/MappedProcess.cs
+++ b/UVtools.Core/Objects/MappedProcess.cs
@@ -89,7 +89,7 @@ public class MappedProcess : BindableBase
#endregion
- #region Contructors
+ #region Constructors
public MappedProcess() { }
diff --git a/UVtools.Core/Operations/OperationFadeExposureTime.cs b/UVtools.Core/Operations/OperationFadeExposureTime.cs
index 8ea6b08..e0715e3 100644
--- a/UVtools.Core/Operations/OperationFadeExposureTime.cs
+++ b/UVtools.Core/Operations/OperationFadeExposureTime.cs
@@ -133,7 +133,7 @@ public class OperationFadeExposureTime : Operation
}
public decimal IncrementValue => Math.Round(IncrementValueRaw, 2);
- public decimal IncrementValueRaw => (_toExposureTime - _fromExposureTime) / (LayerRangeCount + 1);
+ public decimal IncrementValueRaw => (decimal)FileFormat.GetTransitionStepTime((float) _toExposureTime, (float)_fromExposureTime, (ushort) LayerRangeCount);
#endregion
@@ -182,9 +182,16 @@ public class OperationFadeExposureTime : Operation
{
LayerIndexEnd = LayerIndexStart + _layerCount - 1; // Sanitize
- if (_disableFirmwareTransitionLayers)
+ if (SlicerFile.TransitionLayerType == FileFormat.TransitionLayerTypes.Firmware && _disableFirmwareTransitionLayers)
{
- SlicerFile.TransitionLayerCount = 0;
+ SlicerFile.TransitionLayerCount = 0;
+ }
+ else
+ {
+ if (SlicerFile.BottomLayerCount == LayerIndexStart)
+ {
+ SlicerFile.TransitionLayerCount = (ushort) LayerRangeCount;
+ }
}
var increment = IncrementValueRaw;
diff --git a/UVtools.Core/Suggestions/Suggestion.cs b/UVtools.Core/Suggestions/Suggestion.cs
index 613a3ee..ae44626 100644
--- a/UVtools.Core/Suggestions/Suggestion.cs
+++ b/UVtools.Core/Suggestions/Suggestion.cs
@@ -11,6 +11,7 @@ using System.ComponentModel;
using System.Xml.Serialization;
using UVtools.Core.FileFormats;
using UVtools.Core.Objects;
+using UVtools.Core.Operations;
namespace UVtools.Core.Suggestions;
@@ -151,9 +152,10 @@ public abstract class Suggestion : BindableBase
/// <summary>
/// Executes and applies the suggestion
/// </summary>
+ /// <param name="progress"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
- protected virtual bool ExecuteInternally()
+ protected virtual bool ExecuteInternally(OperationProgress progress)
{
throw new NotImplementedException();
}
@@ -163,12 +165,15 @@ public abstract class Suggestion : BindableBase
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
- public bool Execute()
+ public bool Execute(OperationProgress? progress = null)
{
if (_slicerFile is null) throw new InvalidOperationException($"The suggestion '{Title}' can't execute due the lacking of a file parent.");
if (!Enabled || !IsAvailable || IsApplied || IsInformativeOnly || SlicerFile.LayerCount == 0) return false;
-
- var result = ExecuteInternally();
+
+ progress ??= new OperationProgress();
+ progress.Title = $"Applying suggestion: {Title}";
+
+ var result = ExecuteInternally(progress);
RaisePropertyChanged(nameof(IsApplied));
RefreshNotifyMessage();
diff --git a/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs b/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs
index 28c3c77..2a244f4 100644
--- a/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs
+++ b/UVtools.Core/Suggestions/SuggestionBottomLayerCount.cs
@@ -9,6 +9,7 @@
using System;
using System.Text;
using UVtools.Core.Layers;
+using UVtools.Core.Operations;
namespace UVtools.Core.Suggestions;
@@ -48,7 +49,7 @@ public sealed class SuggestionBottomLayerCount : Suggestion
}
}
- public override string Title => "Bottom layer count";
+ public override string Title => "Bottom layers";
public override string Description => "Bottom layers should be kept to a minimum, usually from 2 to 3, it function is to provide a good adhesion to the first layer on the build plate, using a high count have disadvantages.";
@@ -56,7 +57,7 @@ public sealed class SuggestionBottomLayerCount : Suggestion
? $"{GlobalAppliedMessage}: {SlicerFile.BottomLayerCount} / {SlicerFile.BottomLayersHeight}mm"
: $"{GlobalNotAppliedMessage} ({SlicerFile.BottomLayerCount}) is out of the recommended {BottomLayerCountValue} layers";
- public override string ToolTip => $"The recommended total height for the bottom layers must be between {_minimumBottomHeight}mm and {_maximumBottomHeight}mm constrained from {_minimumBottomLayerCount} to {_maximumBottomLayerCount} layers.\n" +
+ public override string ToolTip => $"The recommended total height for the bottom layers must be between [{_minimumBottomHeight}mm to {_maximumBottomHeight}mm] constrained from [{_minimumBottomLayerCount} to {_maximumBottomLayerCount}] layers.\n" +
$"Explanation: {Description}";
public override string? InformationUrl => "https://ameralabs.com/blog/default-3d-printing-raft-settings";
@@ -66,19 +67,19 @@ public sealed class SuggestionBottomLayerCount : Suggestion
public decimal TargetBottomHeight
{
get => _targetBottomHeight;
- set => RaiseAndSetIfChanged(ref _targetBottomHeight, Layer.RoundHeight(value));
+ set => RaiseAndSetIfChanged(ref _targetBottomHeight, Layer.RoundHeight(Math.Max(0, value)));
}
public decimal MinimumBottomHeight
{
get => _minimumBottomHeight;
- set => RaiseAndSetIfChanged(ref _minimumBottomHeight, Layer.RoundHeight(value));
+ set => RaiseAndSetIfChanged(ref _minimumBottomHeight, Layer.RoundHeight(Math.Max(0, value)));
}
public decimal MaximumBottomHeight
{
get => _maximumBottomHeight;
- set => RaiseAndSetIfChanged(ref _maximumBottomHeight, Layer.RoundHeight(value));
+ set => RaiseAndSetIfChanged(ref _maximumBottomHeight, Layer.RoundHeight(Math.Max(0, value)));
}
public byte MinimumBottomLayerCount
@@ -103,6 +104,21 @@ public sealed class SuggestionBottomLayerCount : Suggestion
{
var sb = new StringBuilder();
+ if (_targetBottomHeight < 0)
+ {
+ sb.AppendLine("Bottom height must be a positive value");
+ }
+
+ if (_minimumBottomHeight < 0)
+ {
+ sb.AppendLine("Minimum limit (mm) must be a positive value");
+ }
+
+ if (_maximumBottomHeight < 0)
+ {
+ sb.AppendLine("maximum limit (mm) must be a positive value");
+ }
+
if (_minimumBottomHeight > _maximumBottomHeight)
{
sb.AppendLine("Minimum limit (mm) can't be higher than maximum limit (mm)");
@@ -118,7 +134,7 @@ public sealed class SuggestionBottomLayerCount : Suggestion
#endregion
- #region Contructor
+ #region Constructor
public SuggestionBottomLayerCount()
{
_applyWhen = SuggestionApplyWhen.OutsideLimits;
@@ -127,7 +143,7 @@ public sealed class SuggestionBottomLayerCount : Suggestion
#region Methods
- protected override bool ExecuteInternally()
+ protected override bool ExecuteInternally(OperationProgress progress)
{
SlicerFile.BottomLayerCount = BottomLayerCountValue;
return true;
diff --git a/UVtools.Core/Suggestions/SuggestionLayerHeight.cs b/UVtools.Core/Suggestions/SuggestionLayerHeight.cs
index 884d32c..f5fda81 100644
--- a/UVtools.Core/Suggestions/SuggestionLayerHeight.cs
+++ b/UVtools.Core/Suggestions/SuggestionLayerHeight.cs
@@ -56,7 +56,7 @@ public sealed class SuggestionLayerHeight : Suggestion
? $"{GlobalAppliedMessage}: {SlicerFile.LayerHeight}mm"
: $"{GlobalNotAppliedMessage} is out of the recommended {_minimumLayerHeight}mm » {_maximumLayerHeight}mm, up to {_maximumLayerHeightDecimalPlates} decimal digit(s)";
- public override string ToolTip => $"The recommended layer height is between {_minimumLayerHeight}mm and {_maximumLayerHeight}mm up to {_maximumLayerHeightDecimalPlates} digit(s) precision.\n" +
+ public override string ToolTip => $"The recommended layer height is between [{_minimumLayerHeight}mm to {_maximumLayerHeight}mm] up to {_maximumLayerHeightDecimalPlates} digit(s) precision.\n" +
$"Explanation: {Description}";
public override string? ConfirmationMessage => $"{Title}: Re-slice the model with proper layer height";
@@ -97,6 +97,11 @@ public sealed class SuggestionLayerHeight : Suggestion
sb.AppendLine("Minimum layer height must be higher than 0mm");
}
+ if (_maximumLayerHeight <= 0)
+ {
+ sb.AppendLine("Maximum layer height must be higher than 0mm");
+ }
+
if (_minimumLayerHeight > _maximumLayerHeight)
{
sb.AppendLine("Minimum layer height can't be higher than maximum layer height");
diff --git a/UVtools.Core/Suggestions/SuggestionModelPosition.cs b/UVtools.Core/Suggestions/SuggestionModelPosition.cs
new file mode 100644
index 0000000..a9250ed
--- /dev/null
+++ b/UVtools.Core/Suggestions/SuggestionModelPosition.cs
@@ -0,0 +1,290 @@
+/*
+ * 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.ComponentModel;
+using System.Drawing;
+using System.Text;
+using UVtools.Core.Operations;
+
+namespace UVtools.Core.Suggestions;
+
+public sealed class SuggestionModelPosition : Suggestion
+{
+ #region Enums
+ public enum SuggestionModelAnchor : byte
+ {
+ [Description("⬚ Random")]
+ Random,
+ [Description("⌜ Top left")]
+ TopLeft,
+ [Description("⌝ Top right")]
+ TopRight,
+ [Description("⌟ Bottom right")]
+ BottomRight,
+ [Description("⌞ Bottom left")]
+ BottomLeft
+ }
+ #endregion
+
+ #region Members
+
+ private ushort _targetTopBottomMargin = 100;
+ private ushort _targetLeftRightMargin = 100;
+ private ushort _minimumTopBottomMargin = 50;
+ private ushort _maximumTopBottomMargin = 300;
+ private ushort _minimumLeftRightMargin = 50;
+ private ushort _maximumLeftRightMargin = 300;
+ private SuggestionModelAnchor _anchorType = SuggestionModelAnchor.Random;
+
+ #endregion
+
+ #region Properties
+
+ public override bool IsApplied
+ {
+ get
+ {
+ if (SlicerFile is null) return false;
+
+ if (SlicerFile.BoundingRectangle.Size == SlicerFile.Resolution) return true;
+
+ var topLeft = new Point(LeftRightMargin, TopBottomMargin);
+ var topRight = new Point((int)SlicerFile.ResolutionX - LeftRightMargin, TopBottomMargin);
+ var bottomRight = new Point((int)SlicerFile.ResolutionX - LeftRightMargin, (int)SlicerFile.ResolutionY - TopBottomMargin);
+ var bottomLeft = new Point(LeftRightMargin, (int)SlicerFile.ResolutionY - TopBottomMargin);
+
+ var applyWhen = _applyWhen;
+ if (SlicerFile.ResolutionX - SlicerFile.BoundingRectangle.Size.Width < _minimumLeftRightMargin * 2
+ || SlicerFile.ResolutionY - SlicerFile.BoundingRectangle.Size.Height < _minimumTopBottomMargin * 2)
+ {
+ applyWhen = SuggestionApplyWhen.Different;
+ }
+
+ return applyWhen switch
+ {
+ SuggestionApplyWhen.OutsideLimits =>
+ /*TL*/ (SlicerFile.BoundingRectangle.X >= _minimumLeftRightMargin && SlicerFile.BoundingRectangle.X <= _maximumLeftRightMargin && SlicerFile.BoundingRectangle.Y >= _minimumTopBottomMargin && SlicerFile.BoundingRectangle.Y <= _maximumTopBottomMargin)
+ /*TR*/ || (SlicerFile.BoundingRectangle.Right <= (int)SlicerFile.ResolutionX - _minimumLeftRightMargin && SlicerFile.BoundingRectangle.Right >= (int)SlicerFile.ResolutionX - _maximumLeftRightMargin && SlicerFile.BoundingRectangle.Y >= _minimumTopBottomMargin && SlicerFile.BoundingRectangle.Y <= _maximumTopBottomMargin)
+ /*BR*/ || (SlicerFile.BoundingRectangle.Right <= (int)SlicerFile.ResolutionX - _minimumLeftRightMargin && SlicerFile.BoundingRectangle.Right >= (int)SlicerFile.ResolutionX - _maximumLeftRightMargin && SlicerFile.BoundingRectangle.Bottom <= (int)SlicerFile.ResolutionY - _minimumTopBottomMargin && SlicerFile.BoundingRectangle.Bottom >= (int)SlicerFile.ResolutionY - _maximumTopBottomMargin)
+ /*BL*/ || (SlicerFile.BoundingRectangle.X >= _minimumLeftRightMargin && SlicerFile.BoundingRectangle.X <= _maximumLeftRightMargin && SlicerFile.BoundingRectangle.Bottom <= (int)SlicerFile.ResolutionY - _minimumTopBottomMargin && SlicerFile.BoundingRectangle.Bottom >= (int)SlicerFile.ResolutionY - _maximumTopBottomMargin)
+ ,
+ SuggestionApplyWhen.Different =>
+ /*TL*/ (SlicerFile.BoundingRectangle.Location == topLeft)
+ /*TR*/ || (SlicerFile.BoundingRectangle.Right == topRight.X && SlicerFile.BoundingRectangle.Y == topRight.Y)
+ /*BR*/ || (SlicerFile.BoundingRectangle.Right == bottomRight.X && SlicerFile.BoundingRectangle.Bottom == bottomRight.Y)
+ /*BL*/ || (SlicerFile.BoundingRectangle.X == bottomLeft.X && SlicerFile.BoundingRectangle.Bottom == bottomLeft.Y)
+ ,
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ }
+ }
+
+ public override string Title => "Model position";
+
+ public override string Description =>
+ "Printing on a corner will reduce the FEP stretch forces when detaching from the model during a lift sequence, benefits are: Reduced lift height and faster printing, less stretch, less FEP marks, better FEP lifespan, easier to peel, less prone to failure and use the screen pixels more evenly.\n" +
+ "If the model is too large to fit within the margin(s) on the screen, it will attempt to center it on that same axis to avoid touching on screen edge(s) and to give a sane margin from it.";
+
+ public override string Message => IsApplied
+ ? $"{GlobalAppliedMessage}: {SlicerFile.BoundingRectangle.ToString().Replace(",", ", ")}"
+ : $"{GlobalNotAppliedMessage}: {SlicerFile.BoundingRectangle.Location} is out of the recommended {AnchorPosition}";
+
+ public override string ToolTip => $"The recommended model position must be at a corner and between a top/bottom margin of [{_minimumTopBottomMargin}px to {_maximumTopBottomMargin}px] and left/right margin of [{_minimumLeftRightMargin}px to {_maximumLeftRightMargin}px].\n" +
+ $"Explanation: {Description}";
+
+ public override string? InformationUrl => "https://ameralabs.com/blog/9-settings-to-change-for-faster-resin-3d-printing";
+
+ public override string? ConfirmationMessage => $"{Title}: {SlicerFile.BoundingRectangle.Location} » {AnchorPosition} ({AnchorType})";
+
+ public SuggestionModelAnchor AnchorType
+ {
+ get => _anchorType;
+ set => RaiseAndSetIfChanged(ref _anchorType, value);
+ }
+
+ public ushort TargetTopBottomMargin
+ {
+ get => _targetTopBottomMargin;
+ set => RaiseAndSetIfChanged(ref _targetTopBottomMargin, value);
+ }
+
+ public ushort TopBottomMargin
+ {
+ get
+ {
+ var margin = Math.Clamp(_targetTopBottomMargin, _minimumTopBottomMargin, _maximumTopBottomMargin);
+ return SlicerFile.ResolutionY - SlicerFile.BoundingRectangle.Size.Height < margin * 2
+ ? (ushort) Math.Round((SlicerFile.ResolutionY - SlicerFile.BoundingRectangle.Size.Height) / 2f,
+ MidpointRounding.AwayFromZero)
+ : margin;
+ }
+ }
+
+
+ public ushort TargetLeftRightMargin
+ {
+ get => _targetLeftRightMargin;
+ set => RaiseAndSetIfChanged(ref _targetLeftRightMargin, value);
+ }
+
+ public ushort LeftRightMargin
+ {
+ get
+ {
+ var margin = Math.Clamp(_targetLeftRightMargin, _minimumLeftRightMargin, _maximumLeftRightMargin);
+ return SlicerFile.ResolutionX - SlicerFile.BoundingRectangle.Size.Width < margin * 2
+ ? (ushort) Math.Round((SlicerFile.ResolutionX - SlicerFile.BoundingRectangle.Size.Width) / 2f,
+ MidpointRounding.AwayFromZero)
+ : margin;
+ }
+ }
+
+ public ushort MinimumTopBottomMargin
+ {
+ get => _minimumTopBottomMargin;
+ set => RaiseAndSetIfChanged(ref _minimumTopBottomMargin, value);
+ }
+
+ public ushort MaximumTopBottomMargin
+ {
+ get => _maximumTopBottomMargin;
+ set => RaiseAndSetIfChanged(ref _maximumTopBottomMargin, value);
+ }
+
+ public ushort MinimumLeftRightMargin
+ {
+ get => _minimumLeftRightMargin;
+ set => RaiseAndSetIfChanged(ref _minimumLeftRightMargin, value);
+ }
+
+ public ushort MaximumLeftRightMargin
+ {
+ get => _maximumLeftRightMargin;
+ set => RaiseAndSetIfChanged(ref _maximumLeftRightMargin, value);
+ }
+
+ public Anchor ToolMoveAnchor
+ {
+ get
+ {
+ switch (_anchorType)
+ {
+ case SuggestionModelAnchor.TopLeft:
+ return Anchor.TopLeft;
+ case SuggestionModelAnchor.TopRight:
+ return Anchor.TopRight;
+ case SuggestionModelAnchor.BottomRight:
+ return Anchor.BottomRight;
+ case SuggestionModelAnchor.BottomLeft:
+ return Anchor.BottomLeft;
+ case SuggestionModelAnchor.Random:
+ default:
+ var anchors = new[]
+ {
+ Anchor.TopLeft,
+ Anchor.TopRight,
+ Anchor.BottomRight,
+ Anchor.BottomLeft
+ };
+
+ return anchors[new Random().Next(anchors.Length)];
+ }
+ }
+ }
+
+ public Point AnchorPosition =>
+ ToolMoveAnchor switch
+ {
+ Anchor.TopLeft => new Point(LeftRightMargin, TopBottomMargin),
+ Anchor.TopRight => new Point((int) SlicerFile.ResolutionX - LeftRightMargin, TopBottomMargin),
+ Anchor.BottomRight => new Point((int) SlicerFile.ResolutionX - LeftRightMargin, (int) SlicerFile.ResolutionY - TopBottomMargin),
+ Anchor.BottomLeft => new Point(LeftRightMargin, (int) SlicerFile.ResolutionY - TopBottomMargin),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ /*private static Anchor ToolMoveRandomAnchor
+ {
+ get
+ {
+ var anchors = new[]
+ {
+ Anchor.TopLeft,
+ Anchor.TopRight,
+ Anchor.BottomRight,
+ Anchor.BottomLeft
+ };
+
+ return anchors[new Random().Next(0, anchors.Length)];
+ }
+ }*/
+
+ #endregion
+
+ #region Override
+
+ public override string? Validate()
+ {
+ var sb = new StringBuilder();
+
+ if (_minimumTopBottomMargin > _maximumTopBottomMargin)
+ {
+ sb.AppendLine("Minimum top/bottom margin limit (px) can't be higher than maximum limit (px)");
+ }
+
+ if (_minimumLeftRightMargin > _maximumLeftRightMargin)
+ {
+ sb.AppendLine("Minimum left/right margin limit (px) can't be higher than maximum limit (px)");
+ }
+
+ return sb.ToString();
+ }
+
+ #endregion
+
+ #region Constructor
+ public SuggestionModelPosition()
+ {
+ _applyWhen = SuggestionApplyWhen.OutsideLimits;
+ }
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ var anchor = ToolMoveAnchor;
+ var operation = new OperationMove(SlicerFile, anchor);
+
+ switch (anchor)
+ {
+ case Anchor.TopLeft:
+ operation.MarginLeft = LeftRightMargin;
+ operation.MarginTop = TopBottomMargin;
+ break;
+ case Anchor.TopRight:
+ operation.MarginRight = LeftRightMargin;
+ operation.MarginTop = TopBottomMargin;
+ break;
+ case Anchor.BottomLeft:
+ operation.MarginLeft = LeftRightMargin;
+ operation.MarginBottom = TopBottomMargin;
+ break;
+ case Anchor.BottomRight:
+ operation.MarginRight = LeftRightMargin;
+ operation.MarginBottom = TopBottomMargin;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return operation.IsWithinBoundary && operation.Execute(progress);
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs b/UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs
new file mode 100644
index 0000000..7cc84b5
--- /dev/null
+++ b/UVtools.Core/Suggestions/SuggestionTransitionLayerCount.cs
@@ -0,0 +1,193 @@
+/*
+ * 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.Text;
+using UVtools.Core.Operations;
+
+namespace UVtools.Core.Suggestions;
+
+public sealed class SuggestionTransitionLayerCount : Suggestion
+{
+ #region Members
+
+ private decimal _transitionStepTime = 2;
+ private ushort _minimumTransitionLayerCount = 3;
+ private ushort _maximumTransitionLayerCount = 10;
+
+ #endregion
+
+ #region Properties
+
+ public override bool IsAvailable
+ {
+ get
+ {
+ if (SlicerFile is null) return false;
+ return SlicerFile.CanUseLayerExposureTime && SlicerFile.LayerCount != 0;
+ }
+ }
+
+ public override bool IsApplied
+ {
+ get
+ {
+ if (SlicerFile is null) return false;
+
+ // Can't apply
+ if (SlicerFile.BottomLayerCount == 0
+ || SlicerFile.BottomExposureTime <= 0
+ || SlicerFile.ExposureTime <= 0
+ || SlicerFile.BottomExposureTime < SlicerFile.ExposureTime
+ || SlicerFile.BottomLayerCount + _minimumTransitionLayerCount > SlicerFile.LayerCount
+ || SlicerFile.MaximumPossibleTransitionLayerCount < _minimumTransitionLayerCount) return true;
+
+ var actualTransitionLayerCount = SlicerFile.ParseTransitionLayerCountFromLayers();
+ var suggestedTransitionLayerCount = TransitionLayerCount;
+
+ if (actualTransitionLayerCount == suggestedTransitionLayerCount) return true;
+
+ return _applyWhen switch
+ {
+ SuggestionApplyWhen.OutsideLimits => actualTransitionLayerCount >= _minimumTransitionLayerCount &&
+ actualTransitionLayerCount <= _maximumTransitionLayerCount,
+ SuggestionApplyWhen.Different => actualTransitionLayerCount == suggestedTransitionLayerCount,
+
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ }
+ }
+
+ public override string Title => "Transition layers";
+
+ public override string Description => "If you are printing flat on the build plate your model will print better when using a smooth transition exposure time instead of a harsh variation, resulting in reduced layer line effect and avoid possible problems due the large exposure difference.\n" +
+ "This is not so important when your model print raised under a raft/supports unaffected by the bottom exposure, in that case, it's fine to ignore this.";
+
+ public override string Message
+ {
+ get
+ {
+ var actualTransitionDecrementTime = SlicerFile.ParseTransitionStepTimeFromLayers();
+ var actualTransitionLayerCount = SlicerFile.ParseTransitionLayerCountFromLayers();
+
+ var suggestedTransitionLayerCount = TransitionLayerCount;
+ var suggestedTransitionDecrementTime = SlicerFile.GetTransitionStepTime(suggestedTransitionLayerCount);
+
+ return IsApplied
+ ? $"{GlobalAppliedMessage}: {SlicerFile.BottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s"
+ : $"{GlobalNotAppliedMessage} {SlicerFile.BottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s is out of the recommended {SlicerFile.BottomExposureTime}s » {(suggestedTransitionDecrementTime <= 0 || suggestedTransitionLayerCount == 0 ? string.Empty : $"[-{suggestedTransitionDecrementTime}s/{suggestedTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s";
+ }
+ }
+
+ public override string ToolTip => $"The recommended transition time is ±{_transitionStepTime}s constrained over [{_minimumTransitionLayerCount} to {_maximumTransitionLayerCount}] layers.\n" +
+ $"Explanation: {Description}";
+
+ public override string? InformationUrl => "https://ameralabs.com/blog/9-settings-to-change-for-faster-resin-3d-printing";
+
+ public override string? ConfirmationMessage
+ {
+ get
+ {
+ var actualTransitionDecrementTime = SlicerFile.ParseTransitionStepTimeFromLayers();
+ var actualTransitionLayerCount = SlicerFile.ParseTransitionLayerCountFromLayers();
+
+ var suggestedTransitionLayerCount = TransitionLayerCount;
+ var suggestedTransitionDecrementTime = SlicerFile.GetTransitionStepTime(suggestedTransitionLayerCount);
+
+ return
+ $"{Title}: ({SlicerFile.BottomExposureTime}s » {(actualTransitionDecrementTime <= 0 || actualTransitionLayerCount == 0 ? string.Empty : $"[-{actualTransitionDecrementTime}s/{actualTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s) » ({SlicerFile.BottomExposureTime}s » {(suggestedTransitionDecrementTime <= 0 || suggestedTransitionLayerCount == 0 ? string.Empty : $"[-{suggestedTransitionDecrementTime}s/{suggestedTransitionLayerCount} layers] » ")}{SlicerFile.ExposureTime}s)";
+ }
+ }
+
+ public ushort TransitionLayerCount =>
+ (ushort)Math.Min(
+ Math.Clamp(
+ SlicerFile.GetTransitionLayerCount((float)_transitionStepTime, false),
+ _minimumTransitionLayerCount,
+ _maximumTransitionLayerCount)
+ , SlicerFile.MaximumPossibleTransitionLayerCount);
+
+ public decimal TransitionStepTime
+ {
+ get => _transitionStepTime;
+ set => RaiseAndSetIfChanged(ref _transitionStepTime, Math.Max(0, Math.Round(value, 2)));
+ }
+
+ public ushort MinimumTransitionLayerCount
+ {
+ get => _minimumTransitionLayerCount;
+ set => RaiseAndSetIfChanged(ref _minimumTransitionLayerCount, value);
+ }
+
+ public ushort MaximumTransitionLayerCount
+ {
+ get => _maximumTransitionLayerCount;
+ set => RaiseAndSetIfChanged(ref _maximumTransitionLayerCount, value);
+ }
+
+ #endregion
+
+ #region Override
+
+ public override string? Validate()
+ {
+ var sb = new StringBuilder();
+
+
+ if (_transitionStepTime < 0)
+ {
+ sb.AppendLine("Decrement time (s) must be zero or a positive value");
+ }
+
+ /*if (_minimumTransitionTimeDecrement < 0)
+ {
+ sb.AppendLine("Minimum limit (s) can't be a negative value");
+ }
+
+ if (_maximumTransitionTimeDecrement < 0)
+ {
+ sb.AppendLine("Maximum limit (s) can't be a negative value");
+ }
+
+ if (_minimumTransitionTimeDecrement > _maximumTransitionTimeDecrement)
+ {
+ sb.AppendLine("Minimum limit (s) can't be higher than maximum limit (s)");
+ }*/
+
+ /*if (_minimumTransitionLayerCount == 0)
+ {
+ sb.AppendLine("Minimum limit (layers) must be a positive value");
+ }*/
+
+ if (_minimumTransitionLayerCount > _maximumTransitionLayerCount)
+ {
+ sb.AppendLine("Minimum limit (layers) can't be higher than maximum limit (layers)");
+ }
+
+ return sb.ToString();
+ }
+
+ #endregion
+
+ #region Constructor
+ public SuggestionTransitionLayerCount()
+ {
+ _applyWhen = SuggestionApplyWhen.Different;
+ }
+ #endregion
+
+ #region Methods
+
+ protected override bool ExecuteInternally(OperationProgress progress)
+ {
+ SlicerFile.TransitionLayerCount = TransitionLayerCount;
+ return true;
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs b/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs
index b34f689..3ba2815 100644
--- a/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs
+++ b/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs
@@ -10,6 +10,7 @@ using System;
using System.ComponentModel;
using System.Text;
using UVtools.Core.Extensions;
+using UVtools.Core.Operations;
namespace UVtools.Core.Suggestions;
@@ -110,7 +111,7 @@ public sealed class SuggestionWaitTimeAfterCure : Suggestion
public override string ToolTip => (_setType == SuggestionWaitTimeAfterCureSetType.Fixed
? $"The recommended wait time must be {_fixedBottomWaitTimeAfterCure}/{_fixedWaitTimeAfterCure}s"
: $"The recommended wait time is a ratio of (wait time){_proportionalWaitTimeAfterCure}s to (exposure time){_proportionalExposureTime}s") +
- $" constrained from [Bottoms={_minimumBottomWaitTimeAfterCure}/{_maximumBottomWaitTimeAfterCure}s] and [Normals={_minimumWaitTimeAfterCure}/{_maximumWaitTimeAfterCure}s].\n" +
+ $" constrained from [Bottoms={_minimumBottomWaitTimeAfterCure}s to {_maximumBottomWaitTimeAfterCure}s] and [Normals={_minimumWaitTimeAfterCure}s to {_maximumWaitTimeAfterCure}s].\n" +
$"Explanation: {Description}";
public override string? ConfirmationMessage => $"{Title}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s » {CalculateWaitTime(true, (decimal)SlicerFile.BottomWaitTimeAfterCure)}/{CalculateWaitTime(false, (decimal)SlicerFile.WaitTimeAfterCure)}s";
@@ -224,7 +225,7 @@ public sealed class SuggestionWaitTimeAfterCure : Suggestion
#region Methods
- protected override bool ExecuteInternally()
+ protected override bool ExecuteInternally(OperationProgress progress)
{
if (SlicerFile.CanUseBottomWaitTimeAfterCure)
{
diff --git a/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
index 5c84694..b086481 100644
--- a/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
+++ b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
@@ -12,6 +12,7 @@ using System.Linq;
using System.Text;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
+using UVtools.Core.Operations;
namespace UVtools.Core.Suggestions;
@@ -147,7 +148,7 @@ public sealed class SuggestionWaitTimeBeforeCure : Suggestion
public override string ToolTip => (_setType == SuggestionWaitTimeBeforeCureSetType.Fixed
? $"The recommended wait time must be {_fixedBottomWaitTimeBeforeCure}/{_fixedWaitTimeBeforeCure}s"
: $"The recommended wait time is a ratio of (wait time){_proportionalWaitTimeBeforeCure}s to (exposure time){_proportionalLayerArea}s") +
- $" constrained from [Bottoms={_minimumBottomWaitTimeBeforeCure}/{_maximumBottomWaitTimeBeforeCure}s] and [Normals={_minimumWaitTimeBeforeCure}/{_maximumWaitTimeBeforeCure}s].\n" +
+ $" constrained from [Bottoms={_minimumBottomWaitTimeBeforeCure}s to {_maximumBottomWaitTimeBeforeCure}s] and [Normals={_minimumWaitTimeBeforeCure}s to {_maximumWaitTimeBeforeCure}s].\n" +
$"Explanation: {Description}";
public override string? ConfirmationMessage => $"{Title}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s » {CalculateWaitTime(true)}/{CalculateWaitTime(false)}s";
@@ -380,7 +381,7 @@ public sealed class SuggestionWaitTimeBeforeCure : Suggestion
#region Methods
- protected override bool ExecuteInternally()
+ protected override bool ExecuteInternally(OperationProgress progress)
{
if (SlicerFile.CanUseBottomWaitTimeAfterCure || SlicerFile.CanUseBottomLightOffDelay)
{
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 7e4e895..f7e84d7 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description>
- <Version>3.6.8</Version>
+ <Version>3.7.0</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
diff --git a/UVtools.Installer/Code/HeatGeneratedFileList.wxs b/UVtools.Installer/Code/HeatGeneratedFileList.wxs
index 460b04c..76232c5 100644
--- a/UVtools.Installer/Code/HeatGeneratedFileList.wxs
+++ b/UVtools.Installer/Code/HeatGeneratedFileList.wxs
@@ -323,8 +323,8 @@
<Component Id="cmpE35DDB3C925C83E74C7DAAE6FEA6C156" Guid="*">
<File Id="filEAEAE1CB9E8AC1FF00B05453ABEE2ADF" KeyPath="yes" Source="$(var.HarvestPath)\mscordaccore.dll" />
</Component>
- <Component Id="cmpDF0539C05F3B669297316A4721F7F597" Guid="*">
- <File Id="fil801BA39FE5EF9977D6A6F557603017B8" KeyPath="yes" Source="$(var.HarvestPath)\mscordaccore_amd64_amd64_6.0.922.41905.dll" />
+ <Component Id="cmp512821EEEEE08D5AC2E2CEA01FDB1ABE" Guid="*">
+ <File Id="fil44B372C655F54FBD2A2C09F0E183A476" KeyPath="yes" Source="$(var.HarvestPath)\mscordaccore_amd64_amd64_6.0.1022.47605.dll" />
</Component>
<Component Id="cmpA5ABEAD31F33B1E5825EC6F159ABFB0F" Guid="*">
<File Id="fil1A6B0EBD5A5BF8DD0582A2665D3492F1" KeyPath="yes" Source="$(var.HarvestPath)\mscordbi.dll" />
@@ -1685,7 +1685,7 @@
<ComponentRef Id="cmp3E4F3E4686A9B425B5812E5171CCDEEF" />
<ComponentRef Id="cmp16861E24C897764CA922EC062BA74EC9" />
<ComponentRef Id="cmpE35DDB3C925C83E74C7DAAE6FEA6C156" />
- <ComponentRef Id="cmpDF0539C05F3B669297316A4721F7F597" />
+ <ComponentRef Id="cmp512821EEEEE08D5AC2E2CEA01FDB1ABE" />
<ComponentRef Id="cmpA5ABEAD31F33B1E5825EC6F159ABFB0F" />
<ComponentRef Id="cmp5D6845991403E371A015DF7EBDF71F13" />
<ComponentRef Id="cmp57B8361D25AD503DC733F4039E5A38B0" />
diff --git a/UVtools.Installer/Code/Product.wxs b/UVtools.Installer/Code/Product.wxs
index 1e90877..b094293 100644
--- a/UVtools.Installer/Code/Product.wxs
+++ b/UVtools.Installer/Code/Product.wxs
@@ -9,16 +9,35 @@
is a seamless uninstall/reinstall.
Version="$(var.MSIProductVersion)"
-->
- <Product Id="*" Name="UVtools" Language="1033" Version="$(var.MSIProductVersion)" Manufacturer="PTRTECH" UpgradeCode="1ea6d212-15c0-425e-b2ec-4b6c60817552">
+
+ <?define UpgradeCode = "1ea6d212-15c0-425e-b2ec-4b6c60817552"?>
+
+ <Product Id="*" Name="UVtools" Language="1033" Version="$(var.MSIProductVersion)" Manufacturer="PTRTECH" UpgradeCode="$(var.UpgradeCode)">
<Package InstallerVersion="301" Compressed="yes" Keywords="MSLA, DLP" Description="MSLA/DLP, file analysis, repair, conversion and manipulation" InstallScope="perMachine" Platform="x64" />
<MediaTemplate EmbedCab="yes" />
- <!-- Major Upgrade Rule to disallow downgrades -->
+ <!-- Major Upgrade Rule to disallow downgrades
<MajorUpgrade AllowDowngrades="no"
AllowSameVersionUpgrades="yes"
IgnoreRemoveFailure="no"
- DowngradeErrorMessage="A newer version of [ProductName] is already installed."
+ DowngradeErrorMessage="A newer version of [ProductName] is already installed. Setup will now exit."
Schedule="afterInstallInitialize" />
+ -->
+
+ <!-- Product upgrade -->
+ <Upgrade Id="$(var.UpgradeCode)">
+ <UpgradeVersion OnlyDetect="no" Property="WIX_UPGRADE_DETECTED"
+ Maximum="$(var.MSIProductVersion)" IncludeMaximum="no" IncludeMinimum="no"
+ MigrateFeatures="yes" />
+ <UpgradeVersion OnlyDetect="yes" Property="WIX_DOWNGRADE_DETECTED"
+ Minimum="$(var.MSIProductVersion)" IncludeMinimum="no" />
+ </Upgrade>
+ <InstallExecuteSequence>
+ <RemoveExistingProducts Before="CostInitialize" />
+ </InstallExecuteSequence>
+ <Condition Message="A newer version of [ProductName] is already installed. Setup will now exit.">NOT WIX_DOWNGRADE_DETECTED</Condition>
+
+
<!--Common Launch Condition-->
<!-- Examples at http://wixtoolset.org/documentation/manual/v3/customactions/wixnetfxextension.html -->
<!--
diff --git a/UVtools.Installer/UVtools.Installer.wixproj b/UVtools.Installer/UVtools.Installer.wixproj
index e14820e..89cf6a7 100644
--- a/UVtools.Installer/UVtools.Installer.wixproj
+++ b/UVtools.Installer/UVtools.Installer.wixproj
@@ -26,6 +26,9 @@
<MsixTimestampUrl Condition=" '$(MsixTimestampUrl)' == '' ">
</MsixTimestampUrl>
</PropertyGroup>
+ <PropertyGroup>
+ <SuppressIces>ICE27</SuppressIces>
+ </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml
index ef5ecdd..fffc08d 100644
--- a/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml
+++ b/UVtools.WPF/Controls/Calibrators/CalibrateExternalTestsControl.axaml
@@ -5,37 +5,50 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" Width="500"
x:Class="UVtools.WPF.Controls.Calibrators.CalibrateExternalTestsControl">
<Grid
- RowDefinitions="Auto,Auto,Auto"
+ RowDefinitions="Auto"
ColumnDefinitions="*">
- <Button Grid.Row="0"
- Padding="5"
- Content="Photonsters Validation Matrix / Exposure finder"
- Command="{Binding ButtonClicked}"
- VerticalAlignment="Center"
- HorizontalAlignment="Stretch"
- HorizontalContentAlignment="Center"
- CommandParameter="https://www.thingiverse.com/thing:4707289"/>
+ <StackPanel Orientation="Vertical" Spacing="5">
+ <Button Padding="5"
+ Content="The Complete Resin 3D Printing Settings Guide for Beginners"
+ Command="{Binding ButtonClicked}"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Center"
+ CommandParameter="https://ameralabs.com/blog/the-complete-resin-3d-printing-settings-guide-for-beginners"/>
- <Button Grid.Row="1"
- Margin="0,5,0,0"
- Padding="5"
- Content="Support pull test"
- Command="{Binding ButtonClicked}"
- VerticalAlignment="Center"
- HorizontalAlignment="Stretch"
- HorizontalContentAlignment="Center"
- CommandParameter="https://www.thingiverse.com/thing:4308175"/>
+ <Button Content="Photonsters Validation Matrix / Exposure finder"
+ Command="{Binding ButtonClicked}"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Center"
+ CommandParameter="https://www.thingiverse.com/thing:4707289"/>
- <Button Grid.Row="2"
- Margin="0,5,0,0"
- Padding="5"
- Content="AmeraLabs town calibration part"
- Command="{Binding ButtonClicked}"
- VerticalAlignment="Center"
- HorizontalAlignment="Stretch"
- HorizontalContentAlignment="Center"
- CommandParameter="https://www.thingiverse.com/thing:2810666"/>
+ <Button Padding="5"
+ Content="Support pull test"
+ Command="{Binding ButtonClicked}"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Center"
+ CommandParameter="https://www.thingiverse.com/thing:4308175"/>
- </Grid>
+ <Button Padding="5"
+ Content="AmeraLabs town calibration part"
+ Command="{Binding ButtonClicked}"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Center"
+ CommandParameter="https://www.thingiverse.com/thing:2810666"/>
+
+ <Button Padding="5"
+ Content="9 settings for faster printing"
+ Command="{Binding ButtonClicked}"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Stretch"
+ HorizontalContentAlignment="Center"
+ CommandParameter="https://ameralabs.com/blog/9-settings-to-change-for-faster-resin-3d-printing"/>
+
+ </StackPanel>
+
+ </Grid>
</UserControl>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml b/UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml
new file mode 100644
index 0000000..0ec102c
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml
@@ -0,0 +1,111 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="UVtools.WPF.Controls.Suggestions.SuggestionModelPositionControl">
+ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,2,Auto,10,Auto"
+ ColumnDefinitions="Auto,10,180,5,Auto,5,180">
+
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Desired model placement on the screen regarding an anchor position.
+&#x0a;Random is recommended as it will use a random placement per apply, resulting in using the screen pixels more evenly than print on a same position."
+ Text="Placement anchor:"/>
+
+ <ComboBox Grid.Row="0" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Stretch"
+ Items="{Binding Suggestion.AnchorType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Suggestion.AnchorType, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Desired margin from top/bottom to place the model"
+ Text="Top/bottom margin:"/>
+
+ <NumericUpDown Grid.Row="2" Grid.Column="2"
+ Classes="ValueLabel ValueLabel_px"
+ VerticalAlignment="Center"
+ Minimum="0"
+ Maximum="1000"
+ Increment="10"
+ Value="{Binding Suggestion.TargetTopBottomMargin}"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Desired margin from left/right to place the model"
+ Text="Left/right margin:"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ Classes="ValueLabel ValueLabel_px"
+ VerticalAlignment="Center"
+ Minimum="0"
+ Maximum="1000"
+ Increment="10"
+ Value="{Binding Suggestion.TargetLeftRightMargin}"/>
+
+ <TextBlock Grid.Row="6" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Minimum:"/>
+
+ <TextBlock Grid.Row="6" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Maximum:"/>
+
+ <TextBlock Grid.Row="8" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits for top/bottom margin in pixels for the trigger detection (Min-Max)"
+ Text="Top/bottom limits:"/>
+
+ <NumericUpDown Grid.Row="8" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_px"
+ Minimum="0"
+ Maximum="1000"
+ Increment="10"
+ Value="{Binding Suggestion.MinimumTopBottomMargin}"/>
+
+ <TextBlock Grid.Row="10" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="8" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_px"
+ Minimum="0"
+ Maximum="1000"
+ Increment="10"
+ Value="{Binding Suggestion.MaximumTopBottomMargin}"/>
+
+ <TextBlock Grid.Row="10" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits for left/right margin in pixels for the trigger detection (Min-Max)"
+ Text="Left/right limits:"/>
+
+ <NumericUpDown Grid.Row="10" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_px"
+ Minimum="0"
+ Maximum="1000"
+ Increment="10"
+ Value="{Binding Suggestion.MinimumLeftRightMargin}"/>
+
+ <TextBlock Grid.Row="10" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="10" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_px"
+ Minimum="0"
+ Maximum="1000"
+ Increment="10"
+ Value="{Binding Suggestion.MaximumLeftRightMargin}"/>
+ </Grid>
+</UserControl>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml.cs b/UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml.cs
new file mode 100644
index 0000000..d44051f
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionModelPositionControl.axaml.cs
@@ -0,0 +1,23 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Controls.Suggestions
+{
+ public partial class SuggestionModelPositionControl : SuggestionControl
+ {
+ public SuggestionModelPositionControl() : this(new SuggestionModelPosition())
+ { }
+
+ public SuggestionModelPositionControl(Suggestion suggestion)
+ {
+ Suggestion = suggestion;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml b/UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml
new file mode 100644
index 0000000..c465ec4
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml
@@ -0,0 +1,63 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="UVtools.WPF.Controls.Suggestions.SuggestionTransitionLayerCountControl">
+ <Grid RowDefinitions="Auto,10,Auto,2,Auto"
+ ColumnDefinitions="Auto,10,180,5,Auto,5,180">
+
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Desired transition step time from bottom exposure to normal exposure time. Each layer will sequential subtract this amount of exposure time down to the normal exposure.
+&#x0a;However this step time is not always guaranteed to be exact. It will be constrained over a minimum/maximum layer count and sanitized as required."
+ Text="Transition step:"/>
+
+ <NumericUpDown Grid.Row="0" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_s"
+ Minimum="0"
+ Maximum="100"
+ FormatString="F2"
+ Increment="0.5"
+ Value="{Binding Suggestion.TransitionStepTime}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="2"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Minimum:"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="6"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ FontWeight="Bold"
+ Text="Maximum:"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Sets the limits in layer count for the trigger detection and
+constrains the set value of the applied value to this limit (Min-Max)"
+ Text="Limits (layers):"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="2"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_layers"
+ Minimum="0"
+ Maximum="1000"
+ Increment="1"
+ Value="{Binding Suggestion.MinimumTransitionLayerCount}"/>
+
+ <TextBlock Grid.Row="4" Grid.Column="4"
+ VerticalAlignment="Center"
+ Text="-"/>
+
+ <NumericUpDown Grid.Row="4" Grid.Column="6"
+ VerticalAlignment="Center"
+ Classes="ValueLabel ValueLabel_layers"
+ Minimum="0"
+ Maximum="1000"
+ Increment="1"
+ Value="{Binding Suggestion.MaximumTransitionLayerCount}"/>
+ </Grid>
+</UserControl>
diff --git a/UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml.cs b/UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml.cs
new file mode 100644
index 0000000..2c64cb8
--- /dev/null
+++ b/UVtools.WPF/Controls/Suggestions/SuggestionTransitionLayerCountControl.axaml.cs
@@ -0,0 +1,23 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Suggestions;
+
+namespace UVtools.WPF.Controls.Suggestions
+{
+ public partial class SuggestionTransitionLayerCountControl : SuggestionControl
+ {
+ public SuggestionTransitionLayerCountControl() : this(new SuggestionTransitionLayerCount())
+ { }
+
+ public SuggestionTransitionLayerCountControl(Suggestion suggestion)
+ {
+ Suggestion = suggestion;
+ DataContext = this;
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml
index 5c45144..339818f 100644
--- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml
@@ -16,47 +16,37 @@
Text="Iterations:"/>
- <StackPanel
- Grid.Row="0"
- Grid.Column="2"
- Orientation="Horizontal" Spacing="10">
+ <StackPanel Grid.Row="0" Grid.Column="2"
+ Orientation="Horizontal" Spacing="10">
- <NumericUpDown
- Classes="ValueLabel ValueLabel_px"
- Minimum="1"
- Width="180"
- Value="{Binding Operation.IterationsStart}"/>
+ <NumericUpDown Classes="ValueLabel ValueLabel_px"
+ Minimum="1"
+ Width="180"
+ Value="{Binding Operation.IterationsStart}"/>
- <TextBlock
- VerticalAlignment="Center"
- Text="To:"
- IsEnabled="{Binding Operation.Chamfer}"/>
+ <TextBlock VerticalAlignment="Center"
+ Text="To:"
+ IsEnabled="{Binding Operation.Chamfer}"/>
- <NumericUpDown
- Classes="ValueLabel ValueLabel_px"
- Minimum="1"
- Width="180"
- Value="{Binding Operation.IterationsEnd}"
- IsEnabled="{Binding Operation.Chamfer}"/>
+ <NumericUpDown Classes="ValueLabel ValueLabel_px"
+ Minimum="1"
+ Width="180"
+ Value="{Binding Operation.IterationsEnd}"
+ IsEnabled="{Binding Operation.Chamfer}"/>
- <CheckBox
- ToolTip.Tip="Allow the number of iterations to be gradually varied as the operation progresses from the starting layer to the ending layer."
- Content="Chamfer"
- IsChecked="{Binding Operation.Chamfer}"/>
+ <CheckBox Content="Chamfer"
+ ToolTip.Tip="Allow the number of iterations to be gradually varied as the operation progresses from the starting layer to the ending layer."
+ IsChecked="{Binding Operation.Chamfer}"/>
</StackPanel>
- <TextBlock
- Grid.Row="2"
- Grid.Column="0"
- VerticalAlignment="Center"
- Text="Operation:"/>
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Operation:"/>
- <ComboBox
- Grid.Row="2"
- Grid.Column="2"
- HorizontalAlignment="Stretch"
- Items="{Binding Operation.MorphOperation, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
- SelectedItem="{Binding Operation.MorphOperation, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+ <ComboBox Grid.Row="2" Grid.Column="2"
+ HorizontalAlignment="Stretch"
+ Items="{Binding Operation.MorphOperation, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Operation.MorphOperation, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
</Grid>
diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs
index 9da4734..cb15bf4 100644
--- a/UVtools.WPF/MainWindow.PixelEditor.cs
+++ b/UVtools.WPF/MainWindow.PixelEditor.cs
@@ -568,6 +568,7 @@ public partial class MainWindow
}
Clipboard.Clip($"Draw {Drawings.Count} modifications");
+ PopulateSuggestions();
if (Settings.PixelEditor.PartialUpdateIslandsOnEditing)
{
diff --git a/UVtools.WPF/MainWindow.Suggestions.cs b/UVtools.WPF/MainWindow.Suggestions.cs
index a61efae..497faa2 100644
--- a/UVtools.WPF/MainWindow.Suggestions.cs
+++ b/UVtools.WPF/MainWindow.Suggestions.cs
@@ -6,12 +6,16 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using Avalonia.Controls;
+using Avalonia.Threading;
using MessageBox.Avalonia.Enums;
+using UVtools.Core.Managers;
using UVtools.Core.Suggestions;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
@@ -82,14 +86,36 @@ public partial class MainWindow
}
if (await this.MessageBoxQuestion(sb.ToString(), "Apply suggestions?") != ButtonResult.Yes) return;
- uint executed = 0;
- foreach (var suggestion in suggestions)
+ IsGUIEnabled = false;
+ ShowProgressWindow($"Applying {suggestions.Length} suggestions", false);
+
+ var executed = await Task.Factory.StartNew(() =>
{
- if (suggestion.Execute())
+ uint executed = 0;
+
+ try
{
- executed++;
+ foreach (var suggestion in suggestions)
+ {
+ if (suggestion.Execute(Progress))
+ {
+ executed++;
+ }
+ }
}
- }
+ catch (OperationCanceledException)
+ { }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () => await this.MessageBoxError(ex.ToString(), "Error while applying a suggestion"));
+ }
+
+ return executed;
+ });
+
+ IsGUIEnabled = true;
+
+
if (executed > 0)
{
@@ -107,12 +133,36 @@ public partial class MainWindow
if (await this.MessageBoxQuestion($"Are you sure you want to apply the following suggestion?:\n\n{suggestion.ConfirmationMessage}", "Apply the suggestion?") != ButtonResult.Yes) return;
- if (suggestion.Execute())
+
+ IsGUIEnabled = false;
+ ShowProgressWindow(suggestion.Title, false);
+
+ var result = await Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ return suggestion.Execute(Progress);
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () => await this.MessageBoxError(ex.ToString(), $"{suggestion.Title} Error"));
+ }
+
+ return false;
+ });
+
+ IsGUIEnabled = true;
+
+ if (result)
{
CanSave = true;
ResetDataContext();
ForceUpdateActualLayer();
}
+
PopulateSuggestions(false);
}
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 8a08362..5adbec4 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -262,11 +262,30 @@
<WrapPanel
Orientation="Horizontal"
VerticalAlignment="Center">
- <TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: {0}mm}"/>
+ <TextBlock>
+ <TextBlock.Text>
+ <MultiBinding StringFormat="Layers: {0} @ {1}mm">
+ <Binding Path="SlicerFile.LayerCount"/>
+ <Binding Path="SlicerFile.LayerHeight"/>
+ </MultiBinding>
+ </TextBlock.Text>
+ </TextBlock>
+
<TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" Text=" | "/>
- <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}"
- Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}">
+ <TextBlock.Text>
+ <MultiBinding StringFormat="Bottom layers: {0}/{1}mm">
+ <Binding Path="SlicerFile.BottomLayerCount"/>
+ <Binding Path="SlicerFile.BottomLayersHeight"/>
+ </MultiBinding>
+ </TextBlock.Text>
+ </TextBlock>
+
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseTransitionLayerCount}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseTransitionLayerCount}"
+ Text="{Binding SlicerFile.TransitionLayersRepresentation, StringFormat=Transition layers: {0}}"/>
+
<TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}"
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index 50daf79..c8b727a 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -1178,7 +1178,7 @@ public partial class MainWindow : WindowEx
public void MenuHelpDebugThrowExceptionClicked()
{
- var i = 1 / new Random().Next(0, 0);
+ var i = 1 / new Random().Next(0);
}
public async void MenuHelpDebugLongMessageBoxClicked()
@@ -1759,7 +1759,13 @@ public partial class MainWindow : WindowEx
if (SlicerFile is CTBEncryptedFile)
{
- await this.MessageBoxInfo(CTBEncryptedFile.Preamble, "Information");
+ Settings.General.LockedFilesOpenCounter++;
+ if (Settings.General.LockedFilesOpenCounter >= UserSettings.GeneralUserSettings.LockedFilesMaxOpenCounter)
+ {
+ await this.MessageBoxInfo(CTBEncryptedFile.Preamble, "Information");
+ Settings.General.LockedFilesOpenCounter = 0;
+ }
+ UserSettings.Save();
}
}
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 172952c..8bb9917 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>3.6.8</Version>
+ <Version>3.7.0</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index aac18de..fdbb0a2 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -65,6 +65,9 @@ public sealed class UserSettings : BindableBase
private bool _sendToPromptForRemovableDeviceEject = true;
private RangeObservableCollection<MappedDevice> _sendToCustomLocations = new();
private RangeObservableCollection<MappedProcess> _sendToProcess = new();
+ private ushort _lockedFilesOpenCounter;
+
+ public const byte LockedFilesMaxOpenCounter = 10;
public App.ApplicationTheme Theme
{
@@ -232,6 +235,12 @@ public sealed class UserSettings : BindableBase
set => RaiseAndSetIfChanged(ref _sendToProcess, value);
}
+ public ushort LockedFilesOpenCounter
+ {
+ get => _lockedFilesOpenCounter;
+ set => RaiseAndSetIfChanged(ref _lockedFilesOpenCounter, value);
+ }
+
public GeneralUserSettings() { }
public GeneralUserSettings Clone()
diff --git a/build/createRelease.ps1 b/build/createRelease.ps1
index 92549b3..d36fbc1 100644
--- a/build/createRelease.ps1
+++ b/build/createRelease.ps1
@@ -341,7 +341,7 @@ if($null -ne $enableNugetPublish -and $enableNugetPublish)
}
#>
-foreach ($obj in $runtimes.GetEnumerator()) {
+foreach ($obj in $runtimes.GetEnumerator()) {
if(![string]::IsNullOrWhiteSpace($buildOnly) -and !$buildOnly.Equals($obj.Name)) {continue}
# Configuration
$deployStopWatch.Restart()
diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml
index c670815..ee89b81 100644
--- a/documentation/UVtools.Core.xml
+++ b/documentation/UVtools.Core.xml
@@ -1055,6 +1055,69 @@
<param name="action"></param>
<param name="offset"></param>
</member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.ToByte(System.Boolean)">
+ <summary>
+ Convert bool to byte (0/1)
+ </summary>
+ <param name="value"></param>
+ <returns></returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.SByte)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.Byte)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.Int16)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.UInt16)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.Int32)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.UInt32)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.Int64)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
+ <member name="M:UVtools.Core.Extensions.NumberExtensions.DigitCount(System.UInt64)">
+ <summary>
+ Gets the number of digits this number haves
+ </summary>
+ <param name="n"></param>
+ <returns>Digit count</returns>
+ </member>
<member name="M:UVtools.Core.Extensions.PathExtensions.GetTemporaryDirectory(System.String,System.Boolean)">
<summary>
Gets a temporary directory path
@@ -1113,6 +1176,14 @@
<param name="input">Input string</param>
<returns>Modified string with fist character upper</returns>
</member>
+ <member name="M:UVtools.Core.Extensions.StringExtensions.Repeat(System.String,System.Int32)">
+ <summary>
+ Repeat this string <paramref name="count"/> times
+ </summary>
+ <param name="str">String to repeat</param>
+ <param name="count">Number of times to repeat</param>
+ <returns><paramref name="str"/> repeated <paramref name="count"/> times</returns>
+ </member>
<member name="M:UVtools.Core.Extensions.StringExtensions.Convert``1(System.String)">
<summary>
Converts a string into a target type
@@ -2595,6 +2666,11 @@
Gets or sets the layer count
</summary>
</member>
+ <member name="P:UVtools.Core.FileFormats.FileFormat.LayerDigits">
+ <summary>
+ Return the number of digits on the layer count number, eg: 123 layers = 3 digits
+ </summary>
+ </member>
<member name="P:UVtools.Core.FileFormats.FileFormat.BottomLayersHeight">
<summary>
Gets or sets the total height for the bottom layers in millimeters
@@ -3082,6 +3158,53 @@
<param name="genericLayersExtract"></param>
<param name="progress"></param>
</member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.ParseTransitionLayerCountFromLayers">
+ <summary>
+ Gets the transition layer count calculated from layer exposure time configuration
+ </summary>
+ <returns>Transition layer count</returns>
+ </member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.ParseTransitionStepTimeFromLayers">
+ <summary>
+ Parse the transition step time from layers, value is returned as positive from normal perspective and logic (Longer - shorter)
+ </summary>
+ <returns>Seconds</returns>
+ </member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.GetTransitionStepTime(System.Single,System.Single,System.UInt16)">
+ <summary>
+ Gets the transition step time from a long and short exposure time, value is returned as positive from normal perspective and logic (Longer - shorter)
+ </summary>
+ <param name="longExposureTime">The long exposure time</param>
+ <param name="shortExposureTime">The small exposure time</param>
+ <param name="transitionLayerCount">Number of transition layers</param>
+ <returns>Seconds</returns>
+ </member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.GetTransitionStepTime(System.UInt16)">
+ <summary>
+ Gets the transition step time from <see cref="P:UVtools.Core.FileFormats.FileFormat.BottomExposureTime"/> and <see cref="P:UVtools.Core.FileFormats.FileFormat.ExposureTime"/>, value is returned as positive from normal perspective and logic (Longer - shorter)
+ </summary>
+ <param name="transitionLayerCount">Number of transition layers</param>
+ <returns>Seconds</returns>
+ </member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.GetTransitionLayerCount(System.Single,System.Single,System.Single,System.MidpointRounding)">
+ <summary>
+ Gets the transition layer count based on long and short exposure time
+ </summary>
+ <param name="longExposureTime">The long exposure time</param>
+ <param name="shortExposureTime">The small exposure time</param>
+ <param name="decrementTime">Decrement time</param>
+ <param name="rounding">Midpoint rounding method</param>
+ <returns></returns>
+ </member>
+ <member name="M:UVtools.Core.FileFormats.FileFormat.GetTransitionLayerCount(System.Single,System.Boolean,System.MidpointRounding)">
+ <summary>
+ Gets the transition layer count based on <see cref="P:UVtools.Core.FileFormats.FileFormat.BottomExposureTime"/> and <see cref="P:UVtools.Core.FileFormats.FileFormat.ExposureTime"/>
+ </summary>
+ <param name="stepDecrementTime">Step decrement time in seconds</param>
+ <param name="constrainToLayerCount">True if transition layer count can't be higher than supported by the file, otherwise set to false to not look at possible file layers</param>
+ <param name="rounding">Midpoint rounding method</param>
+ <returns>Transition layer count</returns>
+ </member>
<member name="M:UVtools.Core.FileFormats.FileFormat.ResetCurrentTransitionLayers(System.Boolean)">
<summary>
Re-set exposure time to the transition layers
@@ -6596,14 +6719,15 @@
</summary>
<returns></returns>
</member>
- <member name="M:UVtools.Core.Suggestions.Suggestion.ExecuteInternally">
+ <member name="M:UVtools.Core.Suggestions.Suggestion.ExecuteInternally(UVtools.Core.Operations.OperationProgress)">
<summary>
Executes and applies the suggestion
</summary>
+ <param name="progress"></param>
<returns></returns>
<exception cref="T:System.NotImplementedException"></exception>
</member>
- <member name="M:UVtools.Core.Suggestions.Suggestion.Execute">
+ <member name="M:UVtools.Core.Suggestions.Suggestion.Execute(UVtools.Core.Operations.OperationProgress)">
<summary>
Executes and applies the suggestion
</summary>