diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1797a493..06bbe51c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,39 @@
# Changelog
+## 16/05/2022 - v3.4.2
+
+- **Core:**
+ - (Add) Getter `FileFormat.DisplayPixelCount` Gets the display total number of pixels (ResolutionX * ResolutionY)
+ - (Add) Getter `Layer.NonZeroPixelRatio` Gets the ratio between non zero pixels and display number of pixels
+ - (Add) Getter `Layer.NonZeroPixelPercentage` Gets the percentage of non zero pixels relative to the display number of pixels
+ - (Add) Getter `Layer.PreviousHeightLayer()` Gets the previous layer with a different height from the current, returns null if no previous layer
+ - (Add) Getter `Layer.NextHeightLayer()` Gets the next layer with a different height from the current, returns null if no next layer
+ - (Add) Method `Layer.GetPreviousLayerWithAtLeastPixelCountOf()` Gets the previous layer matching at least a number of pixels, returns null if no previous layer
+ - (Add) Method `Layer.GetNextLayerWithAtLeastPixelCountOf()` Gets the next layer matching at least a number of pixels, returns null if no next layer
+ - (Add) Method `Operation.GetRoiOrVolumeBounds()` returns the selected ROI rectangle or model volume bounds rectangle
+ - (Add) Documentation around `Operation` methods
+ - (Fix) Open files in partial mode when the resolution is not defined would cause a `NullPointerException` (#474)
+- **Suggestion: Wait time before cure**
+ - (Add) Proportional maximum time change: Sets the maximum allowed time difference relative to the previous layer (#471)
+ - (Add) Proportional mass get modes: Previous, Average and Maximum relative to a defined height (#471)
+ - (Change) Proportional set type sets fallback time to the first layer
+ - (Fix) Proportional set type was taking current layer mass instead of looking to the previous cured layer (#471)
+- **Tools:**
+ - **Edit print parameters:**
+ - (Change) Incorporate the unit label into the numeric input box
+ - (Change) Allow TSMC speeds to be 0 as minimum value (#472)
+ - (Fix) PCB Exposure: The thumbnail has random noise around the image
+- **Settings:**
+ - (Add) Tools: "Always prompt for confirmation before execute the operation"
+ - (Fix) Changing layer compression method when no file is loaded would cause a error
+- **UI:**
+ - (Add) Holding Shift key while drag and drop a .uvtop file will try to execute the operation without showing the window or prompt
+ - (Add) Drag and drop a .cs or .csx file into UVtools will load and show the scripting dialog with the file selected
+- (Add) Errors that crash application will now show an report window with the crash information and able to fast report them
+- (Add) "Version" key and value on registry to tell the current installed version (Windows MSI only)
+- (Upgrade) AvaloniaUI from 0.10.13 to 0.10.14
+- (Upgrade) .NET from 6.0.4 to 6.0.5
+-
## 02/05/2022 - v3.4.1
- (Add) Suggestion - Wait time before cure: Allow to set the number of layers to smooth transition from bottom to normal wait time (Defaults to 8)
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 516ea107..d889774a 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,4 +1,32 @@
-- (Add) Suggestion - Wait time before cure: Allow to set the number of layers to smooth transition from bottom to normal wait time (Defaults to 8)
-- (Fix) Tool - PCB Exposure: Pixels per millimeter was been set to fixed value instead of use printer lcd pitch, causing wrong dimentions on different from 50µm pitch
-- (Fix) Tool - PCB Exposure: Unable to run the tool when the display size information isn't available
+- **Core:**
+ - (Add) Getter `FileFormat.DisplayPixelCount` Gets the display total number of pixels (ResolutionX * ResolutionY)
+ - (Add) Getter `Layer.NonZeroPixelRatio` Gets the ratio between non zero pixels and display number of pixels
+ - (Add) Getter `Layer.NonZeroPixelPercentage` Gets the percentage of non zero pixels relative to the display number of pixels
+ - (Add) Getter `Layer.PreviousHeightLayer()` Gets the previous layer with a different height from the current, returns null if no previous layer
+ - (Add) Getter `Layer.NextHeightLayer()` Gets the next layer with a different height from the current, returns null if no next layer
+ - (Add) Method `Layer.GetPreviousLayerWithAtLeastPixelCountOf()` Gets the previous layer matching at least a number of pixels, returns null if no previous layer
+ - (Add) Method `Layer.GetNextLayerWithAtLeastPixelCountOf()` Gets the next layer matching at least a number of pixels, returns null if no next layer
+ - (Add) Method `Operation.GetRoiOrVolumeBounds()` returns the selected ROI rectangle or model volume bounds rectangle
+ - (Add) Documentation around `Operation` methods
+ - (Fix) Open files in partial mode when the resolution is not defined would cause a `NullPointerException` (#474)
+- **Suggestion: Wait time before cure**
+ - (Add) Proportional maximum time change: Sets the maximum allowed time difference relative to the previous layer (#471)
+ - (Add) Proportional mass get modes: Previous, Average and Maximum relative to a defined height (#471)
+ - (Change) Proportional set type sets fallback time to the first layer
+ - (Fix) Proportional set type was taking current layer mass instead of looking to the previous cured layer (#471)
+- **Tools:**
+ - **Edit print parameters:**
+ - (Change) Incorporate the unit label into the numeric input box
+ - (Change) Allow TSMC speeds to be 0 as minimum value (#472)
+ - (Fix) PCB Exposure: The thumbnail has random noise around the image
+- **Settings:**
+ - (Add) Tools: "Always prompt for confirmation before execute the operation"
+ - (Fix) Changing layer compression method when no file is loaded would cause a error
+- **UI:**
+ - (Add) Holding Shift key while drag and drop a .uvtop file will try to execute the operation without showing the window or prompt
+ - (Add) Drag and drop a .cs or .csx file into UVtools will load and show the scripting dialog with the file selected
+- (Add) Errors that crash application will now show an report window with the crash information and able to fast report them
+- (Upgrade) AvaloniaUI from 0.10.13 to 0.10.14
+- (Upgrade) .NET from 6.0.4 to 6.0.5
+-
diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
index a3f87224..45106a35 100644
--- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
+++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
@@ -38,7 +38,7 @@
-
+
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 5c506ae9..833fd8de 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -489,7 +489,7 @@ public static Mat CropByBounds(this Mat src, Size margin)
using var roi = src.Size == rect.Size ? src.Roi(src.Size) : src.Roi(rect);
var numberOfChannels = roi.NumberOfChannels;
- var cropped = new Mat(roi.Rows + margin.Height * 2, roi.Cols + margin.Width * 2, roi.Depth, numberOfChannels);
+ var cropped = InitMat(new Size(roi.Width + margin.Width * 2, roi.Height + margin.Height * 2), numberOfChannels, roi.Depth);
using var dest = new Mat(cropped, new Rectangle(margin.Width, margin.Height, roi.Width, roi.Height));
roi.CopyTo(dest);
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs
index 8fb5f6af..b32d673c 100644
--- a/UVtools.Core/FileFormats/CWSFile.cs
+++ b/UVtools.Core/FileFormats/CWSFile.cs
@@ -18,7 +18,6 @@
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
-using System.Threading.Tasks;
using System.Xml.Serialization;
using UVtools.Core.Converters;
using UVtools.Core.Extensions;
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 69ec05a6..b62099d6 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -172,7 +172,7 @@ public class PrintParameterModifier
#region Instances
public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Absolute Z position", "mm", 0, 100000, 0.01, Layer.HeightPrecision);
public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layer count", "Number of bottom/burn-in layers", "layers", 0, ushort.MaxValue, 1, 0);
- public static PrintParameterModifier TransitionLayerCount { get; } = new ("Transition layer count", "Number of transition layers", "layers",0, ushort.MaxValue, 1, 0);
+ public static PrintParameterModifier TransitionLayerCount { get; } = new ("Transition layer count", "Number of fade/transition layers", "layers",0, ushort.MaxValue, 1, 0);
public static PrintParameterModifier BottomLightOffDelay { get; } = new("Bottom light-off seconds", "Total motor movement time + rest time to wait before cure a new bottom layer", "s");
public static PrintParameterModifier LightOffDelay { get; } = new("Light-off seconds", "Total motor movement time + rest time to wait before cure a new layer", "s");
@@ -180,8 +180,8 @@ public class PrintParameterModifier
public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
- public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers cure time", "s", 0.1M);
- public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Layers cure time", "s", 0.1M);
+ public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers exposure time", "s", 0.1M);
+ public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Normal layers exposure time", "s", 0.1M);
public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
@@ -189,14 +189,14 @@ public class PrintParameterModifier
public static PrintParameterModifier BottomLiftHeight { get; } = new ("Bottom lift height", "Bottom lift/peel height between layers", "mm");
public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm");
- public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", "Lift speed of bottom layers", "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", "Lift speed of normal layers", "mm/min", 10, 5000, 5);
public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height", "Bottom second lift/peel height between layers", "mm");
public static PrintParameterModifier LiftHeight2 { get; } = new("2) Lift height", @"Second lift/peel height between layers", "mm");
- public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", "Lift speed of bottom layers for the second lift sequence (TSMC)", "mm/min", 0, 5000, 5);
+ public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", "Lift speed of normal layers for the second lift sequence (TSMC)", "mm/min", 0, 5000, 5);
public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
@@ -204,10 +204,10 @@ public class PrintParameterModifier
public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
- public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", null, "mm");
- public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", null, "mm");
- public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", null, "mm/min", 10, 5000, 5);
- public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", null, "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", "Slow retract height of bottom layers (TSMC)", "mm");
+ public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", "Slow retract height of normal layers (TSMC)", "mm");
+ public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", "Slow retract speed of bottom layers (TSMC)", "mm/min", 0, 5000, 5);
+ public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", "Slow retract speed of normal layers (TSMC)", "mm/min", 0, 5000, 5);
public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 5, 0);
public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 5, 0);
@@ -1428,7 +1428,7 @@ public bool IsModified
}
///
- /// Gets the bounding rectangle of the object
+ /// Gets the bounding rectangle of the model
///
public Rectangle BoundingRectangle
{
@@ -1492,6 +1492,11 @@ public Size Resolution
///
public abstract uint ResolutionY { get; set; }
+ ///
+ /// Gets the display total number of pixels ( * )
+ ///
+ public uint DisplayPixelCount => ResolutionX * ResolutionY;
+
///
/// Gets the size of display in millimeters
///
@@ -5320,7 +5325,7 @@ public bool Sanitize()
throw new InvalidDataException($"Layer {layerIndex - 1} ({this[layerIndex - 1].PositionZ}mm) have a higher Z position than the successor layer {layerIndex} ({this[layerIndex].PositionZ}mm).\n");
}
- if (ResolutionX == 0 || ResolutionY == 0)
+ if ((ResolutionX == 0 || ResolutionY == 0) && DecodeType == FileDecodeType.Full)
{
var layer = FirstLayer;
if (layer is not null)
diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
index 43ab3a4e..8ad7e920 100644
--- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
+++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
@@ -623,7 +623,7 @@ private unsafe Mat DecodePWS()
if (pixel > imageLength)
{
image.Dispose();
- throw new FileLoadException("Error image ran off the end");
+ throw new FileLoadException("Image ran off the end.");
}
}
}
@@ -763,6 +763,12 @@ private Mat DecodePW0()
//color &= 0xff;
+ if (pixelPos + repeat > imageLength)
+ {
+ mat.Dispose();
+ throw new FileLoadException($"Image ran off the end: {pixelPos}+ {repeat} = {pixelPos + repeat}, expecting: {imageLength}");
+ }
+
// We only need to set the non-zero pixels
mat.FillSpan(ref pixelPos, repeat, color);
@@ -772,18 +778,12 @@ private Mat DecodePW0()
//i++;
break;
}
-
- if (pixelPos > imageLength)
- {
- mat.Dispose();
- throw new FileLoadException($"Error image ran off the end: {pixelPos - repeat}({repeat}) of {imageLength}");
- }
}
if (pixelPos > 0 && pixelPos != imageLength)
{
mat.Dispose();
- throw new FileLoadException($"Error image ended short: {pixelPos} of {imageLength}");
+ throw new FileLoadException($"Image ended short: {pixelPos}, expecting: {imageLength}");
}
return mat;
diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs
index e0b56836..6493a532 100644
--- a/UVtools.Core/Layers/Layer.cs
+++ b/UVtools.Core/Layers/Layer.cs
@@ -98,12 +98,40 @@ public uint NonZeroPixelCount
internal set
{
if (!RaiseAndSetIfChanged(ref _nonZeroPixelCount, value)) return;
+ RaisePropertyChanged(nameof(NonZeroPixelRatio));
+ RaisePropertyChanged(nameof(NonZeroPixelPercentage));
RaisePropertyChanged(nameof(Area));
RaisePropertyChanged(nameof(Volume));
MaterialMilliliters = -1; // Recalculate
}
}
+ ///
+ /// Gets the ratio between non zero pixels and display number of pixels
+ ///
+ public double NonZeroPixelRatio
+ {
+ get
+ {
+ var displayPixelCount = SlicerFile.DisplayPixelCount;
+ if (displayPixelCount == 0) return double.NaN;
+ return (double)_nonZeroPixelCount / displayPixelCount;
+ }
+ }
+
+ ///
+ /// Gets the percentage of non zero pixels relative to the display number of pixels
+ ///
+ public double NonZeroPixelPercentage
+ {
+ get
+ {
+ var pixelRatio = NonZeroPixelRatio;
+ if (double.IsNaN(pixelRatio)) return double.NaN;
+ return pixelRatio * 100.0;
+ }
+ }
+
///
/// Gets if this layer is empty/all black pixels
///
@@ -214,7 +242,37 @@ public Layer? PreviousLayer
if (IsFirstLayer || _index > SlicerFile.Count) return null;
return SlicerFile[_index - 1];
}
+ }
+
+ ///
+ /// Gets the previous layer with a different height from the current, returns null if no previous layer
+ ///
+ public Layer? PreviousHeightLayer
+ {
+ get
+ {
+ if (IsFirstLayer || _index > SlicerFile.Count) return null;
+ for (int i = (int)_index - 1; i >= 0; i--)
+ {
+ if (SlicerFile[i].PositionZ < _positionZ) return SlicerFile[i];
+ }
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the previous layer matching at least pixels, returns null if no previous layer
+ ///
+ public Layer? GetPreviousLayerWithAtLeastPixelCountOf(uint numberOfPixels)
+ {
+ if (IsFirstLayer || _index > SlicerFile.Count) return null;
+ for (int i = (int)_index - 1; i >= 0; i--)
+ {
+ if (SlicerFile[i].NonZeroPixelCount >= numberOfPixels) return SlicerFile[i];
+ }
+
+ return null;
}
///
@@ -229,6 +287,37 @@ public Layer? NextLayer
}
}
+ ///
+ /// Gets the next layer with a different height from the current, returns null if no next layer
+ ///
+ public Layer? NextHeightLayer
+ {
+ get
+ {
+ if (_index >= SlicerFile.LastLayerIndex) return null;
+ for (var i = _index + 1; i < SlicerFile.LayerCount; i++)
+ {
+ if (SlicerFile[i].PositionZ > _positionZ) return SlicerFile[i];
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the next layer matching at least pixels, returns null if no next layer
+ ///
+ public Layer? GetNextLayerWithAtLeastPixelCountOf(uint numberOfPixels)
+ {
+ if (_index >= SlicerFile.LastLayerIndex) return null;
+ for (var i = _index + 1; i < SlicerFile.LayerCount; i++)
+ {
+ if (SlicerFile[i].NonZeroPixelCount >= numberOfPixels) return SlicerFile[i];
+ }
+
+ return null;
+ }
+
///
/// Gets the layer index
///
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index 0bafa089..d1b50b42 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -66,6 +66,9 @@ public OperationImportFrom ImportedFrom
set => RaiseAndSetIfChanged(ref _importedFrom, value);
}
+ ///
+ /// Gets or sets the parent
+ ///
[XmlIgnore]
public FileFormat SlicerFile
{
@@ -78,6 +81,9 @@ public FileFormat SlicerFile
}
}
+ ///
+ /// Gets the bounding rectangle of the model, preserved from any change during and after execution
+ ///
[XmlIgnore]
public Rectangle OriginalBoundingRectangle
{
@@ -85,6 +91,9 @@ public Rectangle OriginalBoundingRectangle
private set => RaiseAndSetIfChanged(ref _originalBoundingRectangle, value);
}
+ ///
+ /// Gets or sets any object which is not used internally
+ ///
[XmlIgnore]
public object? Tag { get; set; }
@@ -93,6 +102,9 @@ public Rectangle OriginalBoundingRectangle
///
public string Id => GetType().Name.Remove(0, ClassNameLength);
+ ///
+ /// Gets the starting layer selection
+ ///
public virtual LayerRangeSelection StartLayerRangeSelection => LayerRangeSelection.All;
///
@@ -104,6 +116,9 @@ public LayerRangeSelection LayerRangeSelection
set => RaiseAndSetIfChanged(ref _layerRangeSelection, value);
}
+ ///
+ /// Gets a string representing the layer range, used with profiles
+ ///
public virtual string LayerRangeString
{
get
@@ -228,9 +243,19 @@ public virtual uint LayerIndexEnd
}
}
+ ///
+ /// Gets if any bottom layer is included in the selected layer range
+ ///
public bool LayerRangeHaveBottoms => LayerIndexStart < (SlicerFile.FirstNormalLayer?.Index ?? 0);
+
+ ///
+ /// Gets if any normal layer is included in the selected layer range
+ ///
public bool LayerRangeHaveNormals => LayerIndexEnd >= (SlicerFile.FirstNormalLayer?.Index ?? 0);
+ ///
+ /// Gets the number of selected layers
+ ///
public uint LayerRangeCount => (uint)Math.Max(0, (int)LayerIndexEnd - LayerIndexStart + 1);
///
@@ -242,6 +267,9 @@ public string? ProfileName
set => RaiseAndSetIfChanged(ref _profileName, value);
}
+ ///
+ /// Gets if this profile is the default to load
+ ///
public bool ProfileIsDefault
{
get => _profileIsDefault;
@@ -262,6 +290,9 @@ public Rectangle ROI
}
}
+ ///
+ /// Gets if there is an ROI associated
+ ///
public bool HaveROI => !ROI.IsEmpty;
///
@@ -279,8 +310,14 @@ public Point[][]? MaskPoints
}
}
+ ///
+ /// Gets if there is masks associated
+ ///
public bool HaveMask => _maskPoints is not null && _maskPoints.Length > 0;
+ ///
+ /// Gets if there is roi or masks associated
+ ///
public bool HaveROIorMask => HaveROI || HaveMask;
///
@@ -310,6 +347,9 @@ protected Operation(FileFormat slicerFile) : this()
#region Methods
+ ///
+ /// Gets if the operation can spawn
+ ///
public bool CanSpawn => string.IsNullOrWhiteSpace(ValidateSpawn());
///
@@ -326,6 +366,10 @@ public bool ValidateSpawn(out string? message)
return string.IsNullOrWhiteSpace(message);
}
+ ///
+ /// Validates the operation, return null or empty if validates
+ ///
+ ///
public virtual string? ValidateInternally()
{
if (!ValidateSpawn(out var message))
@@ -345,11 +389,18 @@ public bool ValidateSpawn(out string? message)
return ValidateInternally();
}
+ ///
+ /// Gets if the operation is able to execute
+ ///
+ ///
public bool CanValidate()
{
return string.IsNullOrWhiteSpace(Validate());
}
+ ///
+ /// Selects all layers from first to last layer
+ ///
public void SelectAllLayers()
{
LayerIndexStart = 0;
@@ -357,12 +408,19 @@ public void SelectAllLayers()
LayerRangeSelection = LayerRangeSelection.All;
}
+ ///
+ /// Selects one layer
+ ///
+ /// Layer index to select
public void SelectCurrentLayer(uint layerIndex)
{
LayerIndexStart = LayerIndexEnd = layerIndex;
LayerRangeSelection = LayerRangeSelection.Current;
}
+ ///
+ /// Selects all bottom layers
+ ///
public void SelectBottomLayers()
{
LayerIndexStart = 0;
@@ -370,6 +428,9 @@ public void SelectBottomLayers()
LayerRangeSelection = LayerRangeSelection.Bottom;
}
+ ///
+ /// Selects all normal layers
+ ///
public void SelectNormalLayers()
{
LayerIndexStart = SlicerFile.FirstNormalLayer?.Index ?? 0;
@@ -377,30 +438,49 @@ public void SelectNormalLayers()
LayerRangeSelection = LayerRangeSelection.Normal;
}
+ ///
+ /// Select the first layer (0)
+ ///
public void SelectFirstLayer()
{
LayerIndexStart = LayerIndexEnd = 0;
LayerRangeSelection = LayerRangeSelection.First;
}
+ ///
+ /// Select the last layer
+ ///
public void SelectLastLayer()
{
LayerIndexStart = LayerIndexEnd = SlicerFile.LastLayerIndex;
LayerRangeSelection = LayerRangeSelection.Last;
}
+ ///
+ /// Selects from first to a layer index
+ ///
+ /// To layer index to select
public void SelectFirstToCurrentLayer(uint currentLayerIndex)
{
LayerIndexStart = 0;
LayerIndexEnd = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
}
+ ///
+ /// Selects from a layer index to the last layer
+ ///
+ /// From layer index to select
public void SelectCurrentToLastLayer(uint currentLayerIndex)
{
LayerIndexStart = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
LayerIndexEnd = SlicerFile.LastLayerIndex;
}
+ ///
+ /// Selects layer given a range type
+ ///
+ ///
+ ///
public void SelectLayers(LayerRangeSelection range)
{
switch (range)
@@ -436,67 +516,141 @@ public void SelectLayers(LayerRangeSelection range)
///
public virtual void InitWithSlicerFile() { }
+ ///
+ /// Clears the ROI and set to empty
+ ///
public void ClearROI()
{
ROI = Rectangle.Empty;
}
+ ///
+ /// Clear and
+ ///
public void ClearROIandMasks()
{
ClearROI();
ClearMasks();
}
+ ///
+ /// Set only if not set already
+ ///
+ /// ROI to set
public void SetROIIfEmpty(Rectangle roi)
{
if (HaveROI) return;
ROI = roi;
}
+ ///
+ /// Gets the size, but if empty returns the file resolution size instead
+ ///
+ ///
public Size GetRoiSizeOrDefault() => GetRoiSizeOrDefault(SlicerFile.Resolution);
- public Size GetRoiSizeOrDefault(Mat? defaultMat) => defaultMat is null ? GetRoiSizeOrDefault() : GetRoiSizeOrDefault(defaultMat.Size);
+
+ ///
+ /// Gets the size, but if empty returns size instead
+ ///
+ ///
+ ///
+ public Size GetRoiSizeOrDefault(Mat? src) => src is null ? GetRoiSizeOrDefault() : GetRoiSizeOrDefault(src.Size);
+
+ ///
+ /// Gets the size, but if empty returns the size from instead
+ ///
+ ///
+ ///
public Size GetRoiSizeOrDefault(Rectangle fallbackRectangle) => GetRoiSizeOrDefault(fallbackRectangle.Size);
+
+ ///
+ /// Gets the size, but if empty returns the instead
+ ///
+ ///
+ ///
public Size GetRoiSizeOrDefault(Size fallbackSize)
{
return HaveROI ? _roi.Size : fallbackSize;
}
+ ///
+ /// Gets the size, but if empty returns the model volume bounds size instead
+ ///
+ ///
public Size GetRoiSizeOrVolumeSize() => GetRoiSizeOrVolumeSize(_originalBoundingRectangle.Size);
+ ///
+ /// Gets the size, but if empty returns the instead
+ ///
+ ///
+ ///
public Size GetRoiSizeOrVolumeSize(Size fallbackSize)
{
return HaveROI ? _roi.Size : fallbackSize;
}
-
- public Mat GetRoiOrDefault(Mat defaultMat)
+ ///
+ /// Gets a cropped shared from by the , but if empty return the instead
+ ///
+ ///
+ ///
+ public Mat GetRoiOrDefault(Mat src)
{
- return HaveROI && defaultMat.Size != _roi.Size ? defaultMat.Roi(_roi) : defaultMat;
+ return HaveROI && src.Size != _roi.Size ? src.Roi(_roi) : src;
}
- public Mat GetRoiOrDefault(Mat defaultMat, Rectangle fallbackRoi)
+ ///
+ /// Gets a cropped shared from by the , but if empty crop by
+ ///
+ ///
+ ///
+ ///
+ public Mat GetRoiOrDefault(Mat src, Rectangle fallbackRoi)
{
- if (HaveROI && defaultMat.Size != _roi.Size) return defaultMat.Roi(_roi);
- if (fallbackRoi.IsEmpty) return defaultMat;
- return defaultMat.Size != fallbackRoi.Size ? defaultMat.Roi(fallbackRoi) : defaultMat;
+ if (HaveROI && src.Size != _roi.Size) return src.Roi(_roi);
+ if (fallbackRoi.IsEmpty) return src;
+ return src.Size != fallbackRoi.Size ? src.Roi(fallbackRoi) : src;
}
+ ///
+ /// Gets a cropped shared from by the , but if empty crop by
+ ///
+ ///
+ ///
public Mat GetRoiOrVolumeBounds(Mat defaultMat)
{
return GetRoiOrDefault(defaultMat, _originalBoundingRectangle);
}
+ ///
+ /// Gets the , but if empty returns
+ ///
+ ///
+ public Rectangle GetRoiOrVolumeBounds() => HaveROI ? _roi : _originalBoundingRectangle;
+
+ ///
+ /// Clears all masks
+ ///
public void ClearMasks()
{
MaskPoints = null;
}
+ ///
+ /// Sets masks only if they are empty
+ ///
+ ///
public void SetMasksIfEmpty(Point[][] points)
{
if (HaveMask) return;
MaskPoints = points;
}
+ ///
+ /// Returns a mask given
+ ///
+ ///
+ ///
public Mat? GetMask(Mat mat) => GetMask(_maskPoints, mat);
public Mat? GetMask(Point[][]? points, Mat mat)
@@ -507,6 +661,12 @@ public void SetMasksIfEmpty(Point[][] points)
return GetRoiOrDefault(mask);
}
+ ///
+ /// Apply a mask to a mat
+ ///
+ /// Original untouched mat
+ /// Mat to modify and apply the mask
+ /// Mask
public void ApplyMask(Mat original, Mat result, Mat? mask)
{
if (mask is null) return;
@@ -538,12 +698,23 @@ public void ApplyMask(Mat original, Mat result)
ApplyMask(original, result, mask);
}
-
+ ///
+ /// Execute the operation internally, to be override by class
+ ///
+ ///
+ ///
+ ///
protected virtual bool ExecuteInternally(OperationProgress progress)
{
throw new NotImplementedException();
}
+ ///
+ /// Execute the operation
+ ///
+ ///
+ ///
+ ///
public bool Execute(OperationProgress? progress = null)
{
if (_slicerFile is null) throw new InvalidOperationException($"{Title} can't execute due the lacking of a file parent.");
@@ -566,6 +737,13 @@ public bool Execute(OperationProgress? progress = null)
public Task ExecuteAsync(OperationProgress? progress = null) => Task.Run(() => Execute(progress), progress?.Token ?? default);
+ ///
+ /// Execute the operation on a given
+ ///
+ ///
+ ///
+ ///
+ ///
public virtual bool Execute(Mat mat, params object[]? arguments)
{
throw new NotImplementedException();
@@ -597,12 +775,21 @@ public void CopyConfigurationTo(Operation operation)
operation.MaskPoints = MaskPoints;
}
+ ///
+ /// Serialize class to XML file
+ ///
+ ///
+ ///
public void Serialize(string path, bool indent = false)
{
if(indent) XmlExtensions.SerializeToFile(this, path, XmlExtensions.SettingsIndent);
else XmlExtensions.SerializeToFile(this, path);
}
+ ///
+ /// Clone object
+ ///
+ ///
public virtual Operation Clone()
{
var operation = MemberwiseClone() as Operation;
@@ -623,6 +810,11 @@ public override string ToString()
#region Static Methods
+ ///
+ /// Deserialize from a XML file
+ ///
+ /// XML file path
+ ///
public static Operation? Deserialize(string path)
{
if (!File.Exists(path)) return null;
@@ -641,6 +833,12 @@ public override string ToString()
return Deserialize(path, type);
}
+ ///
+ /// Deserialize from a XML file
+ ///
+ /// XML file path
+ ///
+ ///
public static Operation Deserialize(string path, Type type)
{
var serializer = new XmlSerializer(type);
@@ -650,6 +848,12 @@ public static Operation Deserialize(string path, Type type)
return operation;
}
+ ///
+ /// Deserialize from a XML file
+ ///
+ /// XML file path
+ ///
+ ///
public static Operation Deserialize(string path, Operation operation) => Deserialize(path, operation.GetType());
#endregion
diff --git a/UVtools.Core/Operations/OperationPCBExposure.cs b/UVtools.Core/Operations/OperationPCBExposure.cs
index d8c721da..31772639 100644
--- a/UVtools.Core/Operations/OperationPCBExposure.cs
+++ b/UVtools.Core/Operations/OperationPCBExposure.cs
@@ -195,9 +195,7 @@ public Mat GetMat()
protected override bool ExecuteInternally(OperationProgress progress)
{
using var mat = GetMat();
- var layer = new Layer(mat, SlicerFile);
- layer.SetNoDelays();
-
+
SlicerFile.SuppressRebuildPropertiesWork(() =>
{
SlicerFile.LayerHeight = (float) _layerHeight;
@@ -207,7 +205,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
SlicerFile.LiftHeightTotal = 0;
SlicerFile.SetNoDelays();
- SlicerFile.Layers = new[] { layer };
+ SlicerFile.Layers = new[] { new Layer(mat, SlicerFile) };
}, true);
diff --git a/UVtools.Core/Operations/OperationScripting.cs b/UVtools.Core/Operations/OperationScripting.cs
index ce6835f2..0ec7e6e4 100644
--- a/UVtools.Core/Operations/OperationScripting.cs
+++ b/UVtools.Core/Operations/OperationScripting.cs
@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using System;
+using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using UVtools.Core.FileFormats;
@@ -176,7 +177,7 @@ public void ReloadScriptFromText(string? text = null)
_scriptState = CSharpScript.RunAsync(_scriptText,
ScriptOptions.Default.AddReferences(typeof(About).Assembly).WithAllowUnsafe(true),
ScriptGlobals).Result;
-
+
var result = _scriptState.ContinueWithAsync("ScriptInit();").Result;
RaisePropertyChanged(nameof(CanExecute));
diff --git a/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
index fa818de3..5c846948 100644
--- a/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
+++ b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs
@@ -18,7 +18,7 @@ namespace UVtools.Core.Suggestions;
public sealed class SuggestionWaitTimeBeforeCure : Suggestion
{
#region Enums
- public enum SuggestionWaitTimeBeforeCureSetType
+ public enum SuggestionWaitTimeBeforeCureSetType : byte
{
[Description("Fixed: Use a fixed time")]
Fixed,
@@ -27,10 +27,21 @@ public enum SuggestionWaitTimeBeforeCureSetType
[Description("Proportional to layer area")]
ProportionalLayerArea,
}
+
+ public enum SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom : byte
+ {
+ [Description("Previous mass")]
+ Previous,
+ [Description("Average of previous masses")]
+ Average,
+ [Description("Maximum of previous masses")]
+ Maximum,
+ }
#endregion
#region Members
private SuggestionWaitTimeBeforeCureSetType _setType = SuggestionWaitTimeBeforeCureSetType.Fixed;
+
private decimal _bottomHeight = 1;
private decimal _fixedBottomWaitTimeBeforeCure = 20;
private decimal _fixedWaitTimeBeforeCure = 2;
@@ -41,12 +52,16 @@ public enum SuggestionWaitTimeBeforeCureSetType
private uint _proportionalLayerPixels = 1000000;
private uint _proportionalBottomLayerArea = 1000;
private uint _proportionalLayerArea = 1000;
+ private decimal _proportionalBottomWaitTimeBeforeCureMaximumDifference = 1;
+ private decimal _proportionalWaitTimeBeforeCureMaximumDifference = 1;
+ private SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom _proportionalCalculateMassFrom = SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Previous;
+ private decimal _proportionalMassRelativeHeight = 0.2m;
private decimal _minimumBottomWaitTimeBeforeCure = 5;
private decimal _minimumWaitTimeBeforeCure = 1;
private decimal _maximumBottomWaitTimeBeforeCure = 120;
private decimal _maximumWaitTimeBeforeCure = 12;
private bool _createEmptyFirstLayer = true;
-
+
#endregion
#region Properties
@@ -154,11 +169,29 @@ public SuggestionWaitTimeBeforeCureSetType SetType
public bool IsSetTypeFixed => _setType == SuggestionWaitTimeBeforeCureSetType.Fixed;
public bool IsSetTypeProportionalLayerPixels => _setType == SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels;
public bool IsSetTypeProportionalLayerArea => _setType == SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea;
-
+
+ public SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom ProportionalCalculateMassFrom
+ {
+ get => _proportionalCalculateMassFrom;
+ set
+ {
+ if (!RaiseAndSetIfChanged(ref _proportionalCalculateMassFrom, value)) return;
+ RaisePropertyChanged(nameof(IsProportionalCalculateMassFromPrevious));
+ }
+ }
+
+ public decimal ProportionalMassRelativeHeight
+ {
+ get => _proportionalMassRelativeHeight;
+ set => RaiseAndSetIfChanged(ref _proportionalMassRelativeHeight, Math.Max(0, Math.Round(value, Layer.HeightPrecision)));
+ }
+
+ public bool IsProportionalCalculateMassFromPrevious => _proportionalCalculateMassFrom == SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Previous;
+
public decimal BottomHeight
{
get => _bottomHeight;
- set => RaiseAndSetIfChanged(ref _bottomHeight, value);
+ set => RaiseAndSetIfChanged(ref _bottomHeight, Math.Max(0, value));
}
public decimal FixedBottomWaitTimeBeforeCure
@@ -166,7 +199,7 @@ public decimal FixedBottomWaitTimeBeforeCure
get => _fixedBottomWaitTimeBeforeCure;
set
{
- if(!RaiseAndSetIfChanged(ref _fixedBottomWaitTimeBeforeCure, Math.Round(value, 2))) return;
+ if(!RaiseAndSetIfChanged(ref _fixedBottomWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2))) return;
RaisePropertyChanged(nameof(WaitTimeBeforeCureTransitionDecrement));
}
}
@@ -176,7 +209,7 @@ public decimal FixedWaitTimeBeforeCure
get => _fixedWaitTimeBeforeCure;
set
{
- if (!RaiseAndSetIfChanged(ref _fixedWaitTimeBeforeCure, Math.Round(value, 2))) return;
+ if (!RaiseAndSetIfChanged(ref _fixedWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2))) return;
RaisePropertyChanged(nameof(WaitTimeBeforeCureTransitionDecrement));
}
}
@@ -196,13 +229,13 @@ public byte WaitTimeBeforeCureTransitionLayerCount
public decimal ProportionalBottomWaitTimeBeforeCure
{
get => _proportionalBottomWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _proportionalBottomWaitTimeBeforeCure, Math.Round(value, 2));
+ set => RaiseAndSetIfChanged(ref _proportionalBottomWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2));
}
public decimal ProportionalWaitTimeBeforeCure
{
get => _proportionalWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _proportionalWaitTimeBeforeCure, Math.Round(value, 2));
+ set => RaiseAndSetIfChanged(ref _proportionalWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2));
}
@@ -230,28 +263,40 @@ public uint ProportionalLayerArea
set => RaiseAndSetIfChanged(ref _proportionalLayerArea, Math.Max(1, value));
}
+ public decimal ProportionalBottomWaitTimeBeforeCureMaximumDifference
+ {
+ get => _proportionalBottomWaitTimeBeforeCureMaximumDifference;
+ set => RaiseAndSetIfChanged(ref _proportionalBottomWaitTimeBeforeCureMaximumDifference, Math.Round(Math.Max(0, value), 2));
+ }
+
+ public decimal ProportionalWaitTimeBeforeCureMaximumDifference
+ {
+ get => _proportionalWaitTimeBeforeCureMaximumDifference;
+ set => RaiseAndSetIfChanged(ref _proportionalWaitTimeBeforeCureMaximumDifference, Math.Round(Math.Max(0, value), 2));
+ }
+
public decimal MinimumBottomWaitTimeBeforeCure
{
get => _minimumBottomWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _minimumBottomWaitTimeBeforeCure, Math.Round(value, 2));
+ set => RaiseAndSetIfChanged(ref _minimumBottomWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2));
}
public decimal MinimumWaitTimeBeforeCure
{
get => _minimumWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _minimumWaitTimeBeforeCure, Math.Round(value, 2));
+ set => RaiseAndSetIfChanged(ref _minimumWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2));
}
public decimal MaximumBottomWaitTimeBeforeCure
{
get => _maximumBottomWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _maximumBottomWaitTimeBeforeCure, Math.Round(value, 2));
+ set => RaiseAndSetIfChanged(ref _maximumBottomWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2));
}
public decimal MaximumWaitTimeBeforeCure
{
get => _maximumWaitTimeBeforeCure;
- set => RaiseAndSetIfChanged(ref _maximumWaitTimeBeforeCure, Math.Round(value, 2));
+ set => RaiseAndSetIfChanged(ref _maximumWaitTimeBeforeCure, Math.Round(Math.Max(0, value), 2));
}
public bool CreateEmptyFirstLayer
@@ -410,23 +455,97 @@ public float CalculateWaitTime(bool isBottomLayer, Layer? layer = null)
}
if (layer.NonZeroPixelCount <= 1) return 0; // Empty layer, don't need wait time
-
- return _setType switch
+
+ float mass = 0;
+ if (layer.Index > 0 && _proportionalCalculateMassFrom != SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Previous && _proportionalMassRelativeHeight > 0)
+ {
+ //var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2); // Skip all previous empty layer
+ //if (previousLayer is not null) layer = previousLayer;
+ uint count = 0;
+
+ Layer? previousLayer = layer;
+
+ while ((previousLayer = previousLayer!.PreviousLayer) is not null && (layer.PositionZ - previousLayer.PositionZ) <= (float)_proportionalMassRelativeHeight)
+ {
+ if(previousLayer.NonZeroPixelCount < 2) continue; // Skip empty layers
+
+ count++;
+ switch (_proportionalCalculateMassFrom)
+ {
+ case SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Average:
+ mass += _setType == SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels
+ ? previousLayer.NonZeroPixelCount
+ : previousLayer.GetArea();
+ break;
+ case SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Maximum:
+ mass = Math.Max(_setType == SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels
+ ? previousLayer.NonZeroPixelCount
+ : previousLayer.GetArea(), mass);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ if (_proportionalCalculateMassFrom == SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Average && mass > 0 && count > 0)
+ {
+ mass /= count;
+ }
+ }
+
+ if (mass <= 0)
+ {
+ var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2); // Skip all previous empty layer
+ if (previousLayer is null)
+ {
+ return isBottomLayer ? (float)_fixedBottomWaitTimeBeforeCure : (float)_fixedWaitTimeBeforeCure;
+ }
+ mass = _setType == SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels
+ ? previousLayer.NonZeroPixelCount
+ : previousLayer.GetArea();
+ }
+
+ float value = _setType switch
{
- SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels => (float)Math.Round(
- (isBottomLayer
- ? layer.NonZeroPixelCount * _proportionalBottomWaitTimeBeforeCure / _proportionalBottomLayerPixels
- : layer.NonZeroPixelCount * _proportionalWaitTimeBeforeCure / _proportionalLayerPixels).Clamp(
- isBottomLayer ? _minimumBottomWaitTimeBeforeCure : _minimumWaitTimeBeforeCure,
- isBottomLayer ? _maximumBottomWaitTimeBeforeCure : _maximumWaitTimeBeforeCure), 2),
- SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea => (float) Math.Round(
- (isBottomLayer
- ? (decimal)layer.GetArea() * _proportionalBottomWaitTimeBeforeCure / _proportionalBottomLayerArea
- : (decimal)layer.GetArea() * _proportionalWaitTimeBeforeCure / _proportionalLayerArea).Clamp(
- isBottomLayer ? _minimumBottomWaitTimeBeforeCure : _minimumWaitTimeBeforeCure,
- isBottomLayer ? _maximumBottomWaitTimeBeforeCure : _maximumWaitTimeBeforeCure), 2),
+ SuggestionWaitTimeBeforeCureSetType.ProportionalLayerPixels => (float) (isBottomLayer
+ ? (decimal)mass * _proportionalBottomWaitTimeBeforeCure / _proportionalBottomLayerPixels
+ : (decimal)mass * _proportionalWaitTimeBeforeCure / _proportionalLayerPixels),
+ SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea => (float) (isBottomLayer
+ ? (decimal)mass * _proportionalBottomWaitTimeBeforeCure / _proportionalBottomLayerArea
+ : (decimal)mass * _proportionalWaitTimeBeforeCure / _proportionalLayerArea),
_ => throw new ArgumentOutOfRangeException()
};
+
+ if (isBottomLayer)
+ {
+ if (_proportionalBottomWaitTimeBeforeCureMaximumDifference > 0)
+ {
+ var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2);
+ if(previousLayer is not null)
+ {
+ value = value.Clamp(
+ Math.Max(0, previousLayer.WaitTimeBeforeCure - (float)_proportionalBottomWaitTimeBeforeCureMaximumDifference),
+ previousLayer.WaitTimeBeforeCure + (float)_proportionalBottomWaitTimeBeforeCureMaximumDifference);
+ }
+ }
+ }
+ else
+ {
+ if (_proportionalWaitTimeBeforeCureMaximumDifference > 0)
+ {
+ var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2);
+ if (previousLayer is not null)
+ {
+ value = value.Clamp(
+ Math.Max(0, previousLayer.WaitTimeBeforeCure - (float)_proportionalWaitTimeBeforeCureMaximumDifference),
+ previousLayer.WaitTimeBeforeCure + (float)_proportionalWaitTimeBeforeCureMaximumDifference);
+ }
+ }
+ }
+
+ return (float)Math.Round((decimal)value, 2).Clamp(
+ isBottomLayer ? _minimumBottomWaitTimeBeforeCure : _minimumWaitTimeBeforeCure,
+ isBottomLayer ? _maximumBottomWaitTimeBeforeCure : _maximumWaitTimeBeforeCure);
}
public float CalculateWaitTime(Layer layer) => CalculateWaitTime(false, layer);
diff --git a/UVtools.Core/SystemOS/SystemAware.cs b/UVtools.Core/SystemOS/SystemAware.cs
index a7d75735..2b892176 100644
--- a/UVtools.Core/SystemOS/SystemAware.cs
+++ b/UVtools.Core/SystemOS/SystemAware.cs
@@ -255,6 +255,16 @@ public static void StartProcess(string name, string? arguments = null, bool wait
}
}
+ public static void StartThisApplication(string? arguments = null)
+ {
+ var executable = Environment.ProcessPath;
+
+ if (File.Exists(executable)) // Direct execute
+ {
+ StartProcess(executable, arguments);
+ }
+ }
+
public static string GetExecutableName(string executable)
{
return OperatingSystem.IsWindows() ? $"{executable}.exe" : executable;
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index f9417e32..7c95a6ad 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
https://github.com/sn4k3/UVtools
https://github.com/sn4k3/UVtools
MSLA/DLP, file analysis, calibration, repair, conversion and manipulation
- 3.4.1
+ 3.4.2
Copyright © 2020 PTRTECH
UVtools.png
AnyCPU;x64
@@ -66,11 +66,11 @@
-
+
-
+
diff --git a/UVtools.Installer/Code/Product.wxs b/UVtools.Installer/Code/Product.wxs
index d392cc84..b14c5f7c 100644
--- a/UVtools.Installer/Code/Product.wxs
+++ b/UVtools.Installer/Code/Product.wxs
@@ -35,6 +35,7 @@
+
diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
index e28de766..5e61ad8a 100644
--- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs
+++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
@@ -2,7 +2,7 @@
-
+
@@ -1554,8 +1554,8 @@
-
-
+
+