mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-08 17:42:31 +02:00
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
This commit is contained in:
@@ -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)
|
||||
|
||||
+31
-3
@@ -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
|
||||
-
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia" Version="0.10.14" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -489,7 +489,7 @@ public static class EmguExtensions
|
||||
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);
|
||||
|
||||
@@ -18,7 +18,6 @@ using System.Linq;
|
||||
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;
|
||||
|
||||
@@ -172,7 +172,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
|
||||
#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 abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
|
||||
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 abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
|
||||
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 abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
|
||||
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 abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding rectangle of the object
|
||||
/// Gets the bounding rectangle of the model
|
||||
/// </summary>
|
||||
public Rectangle BoundingRectangle
|
||||
{
|
||||
@@ -1492,6 +1492,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
|
||||
/// </summary>
|
||||
public abstract uint ResolutionY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display total number of pixels (<see cref="ResolutionX"/> * <see cref="ResolutionY"/>)
|
||||
/// </summary>
|
||||
public uint DisplayPixelCount => ResolutionX * ResolutionY;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of display in millimeters
|
||||
/// </summary>
|
||||
@@ -5320,7 +5325,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
|
||||
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)
|
||||
|
||||
@@ -623,7 +623,7 @@ public class PhotonWorkshopFile : FileFormat
|
||||
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 @@ public class PhotonWorkshopFile : FileFormat
|
||||
|
||||
//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 @@ public class PhotonWorkshopFile : FileFormat
|
||||
//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;
|
||||
|
||||
@@ -98,12 +98,40 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
|
||||
internal set
|
||||
{
|
||||
if (!RaiseAndSetIfChanged(ref _nonZeroPixelCount, value)) return;
|
||||
RaisePropertyChanged(nameof(NonZeroPixelRatio));
|
||||
RaisePropertyChanged(nameof(NonZeroPixelPercentage));
|
||||
RaisePropertyChanged(nameof(Area));
|
||||
RaisePropertyChanged(nameof(Volume));
|
||||
MaterialMilliliters = -1; // Recalculate
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ratio between non zero pixels and display number of pixels
|
||||
/// </summary>
|
||||
public double NonZeroPixelRatio
|
||||
{
|
||||
get
|
||||
{
|
||||
var displayPixelCount = SlicerFile.DisplayPixelCount;
|
||||
if (displayPixelCount == 0) return double.NaN;
|
||||
return (double)_nonZeroPixelCount / displayPixelCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the percentage of non zero pixels relative to the display number of pixels
|
||||
/// </summary>
|
||||
public double NonZeroPixelPercentage
|
||||
{
|
||||
get
|
||||
{
|
||||
var pixelRatio = NonZeroPixelRatio;
|
||||
if (double.IsNaN(pixelRatio)) return double.NaN;
|
||||
return pixelRatio * 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this layer is empty/all black pixels
|
||||
/// </summary>
|
||||
@@ -214,7 +242,37 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
|
||||
if (IsFirstLayer || _index > SlicerFile.Count) return null;
|
||||
return SlicerFile[_index - 1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous layer with a different height from the current, returns null if no previous layer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous layer matching at least <param name="numberOfPixels"/> pixels, returns null if no previous layer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -229,6 +287,37 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next layer with a different height from the current, returns null if no next layer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next layer matching at least <param name="numberOfPixels"/> pixels, returns null if no next layer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layer index
|
||||
/// </summary>
|
||||
|
||||
@@ -66,6 +66,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
set => RaiseAndSetIfChanged(ref _importedFrom, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent <see cref="FileFormat"/>
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public FileFormat SlicerFile
|
||||
{
|
||||
@@ -78,6 +81,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding rectangle of the model, preserved from any change during and after execution
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public Rectangle OriginalBoundingRectangle
|
||||
{
|
||||
@@ -85,6 +91,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
private set => RaiseAndSetIfChanged(ref _originalBoundingRectangle, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets any object which is not used internally
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public object? Tag { get; set; }
|
||||
|
||||
@@ -93,6 +102,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
/// </summary>
|
||||
public string Id => GetType().Name.Remove(0, ClassNameLength);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the starting layer selection
|
||||
/// </summary>
|
||||
public virtual LayerRangeSelection StartLayerRangeSelection => LayerRangeSelection.All;
|
||||
|
||||
/// <summary>
|
||||
@@ -104,6 +116,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
set => RaiseAndSetIfChanged(ref _layerRangeSelection, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representing the layer range, used with profiles
|
||||
/// </summary>
|
||||
public virtual string LayerRangeString
|
||||
{
|
||||
get
|
||||
@@ -228,9 +243,19 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if any bottom layer is included in the selected layer range
|
||||
/// </summary>
|
||||
public bool LayerRangeHaveBottoms => LayerIndexStart < (SlicerFile.FirstNormalLayer?.Index ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets if any normal layer is included in the selected layer range
|
||||
/// </summary>
|
||||
public bool LayerRangeHaveNormals => LayerIndexEnd >= (SlicerFile.FirstNormalLayer?.Index ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of selected layers
|
||||
/// </summary>
|
||||
public uint LayerRangeCount => (uint)Math.Max(0, (int)LayerIndexEnd - LayerIndexStart + 1);
|
||||
|
||||
/// <summary>
|
||||
@@ -242,6 +267,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
set => RaiseAndSetIfChanged(ref _profileName, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this profile is the default to load
|
||||
/// </summary>
|
||||
public bool ProfileIsDefault
|
||||
{
|
||||
get => _profileIsDefault;
|
||||
@@ -262,6 +290,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if there is an ROI associated
|
||||
/// </summary>
|
||||
public bool HaveROI => !ROI.IsEmpty;
|
||||
|
||||
/// <summary>
|
||||
@@ -279,8 +310,14 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if there is masks associated
|
||||
/// </summary>
|
||||
public bool HaveMask => _maskPoints is not null && _maskPoints.Length > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if there is roi or masks associated
|
||||
/// </summary>
|
||||
public bool HaveROIorMask => HaveROI || HaveMask;
|
||||
|
||||
/// <summary>
|
||||
@@ -310,6 +347,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the operation can spawn
|
||||
/// </summary>
|
||||
public bool CanSpawn => string.IsNullOrWhiteSpace(ValidateSpawn());
|
||||
|
||||
/// <summary>
|
||||
@@ -326,6 +366,10 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
return string.IsNullOrWhiteSpace(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the operation, return null or empty if validates
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual string? ValidateInternally()
|
||||
{
|
||||
if (!ValidateSpawn(out var message))
|
||||
@@ -345,11 +389,18 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
return ValidateInternally();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the operation is able to execute
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool CanValidate()
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(Validate());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects all layers from first to last layer
|
||||
/// </summary>
|
||||
public void SelectAllLayers()
|
||||
{
|
||||
LayerIndexStart = 0;
|
||||
@@ -357,12 +408,19 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
LayerRangeSelection = LayerRangeSelection.All;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects one layer
|
||||
/// </summary>
|
||||
/// <param name="layerIndex">Layer index to select</param>
|
||||
public void SelectCurrentLayer(uint layerIndex)
|
||||
{
|
||||
LayerIndexStart = LayerIndexEnd = layerIndex;
|
||||
LayerRangeSelection = LayerRangeSelection.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects all bottom layers
|
||||
/// </summary>
|
||||
public void SelectBottomLayers()
|
||||
{
|
||||
LayerIndexStart = 0;
|
||||
@@ -370,6 +428,9 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
LayerRangeSelection = LayerRangeSelection.Bottom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects all normal layers
|
||||
/// </summary>
|
||||
public void SelectNormalLayers()
|
||||
{
|
||||
LayerIndexStart = SlicerFile.FirstNormalLayer?.Index ?? 0;
|
||||
@@ -377,30 +438,49 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
LayerRangeSelection = LayerRangeSelection.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select the first layer (0)
|
||||
/// </summary>
|
||||
public void SelectFirstLayer()
|
||||
{
|
||||
LayerIndexStart = LayerIndexEnd = 0;
|
||||
LayerRangeSelection = LayerRangeSelection.First;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select the last layer
|
||||
/// </summary>
|
||||
public void SelectLastLayer()
|
||||
{
|
||||
LayerIndexStart = LayerIndexEnd = SlicerFile.LastLayerIndex;
|
||||
LayerRangeSelection = LayerRangeSelection.Last;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects from first to a layer index
|
||||
/// </summary>
|
||||
/// <param name="currentLayerIndex">To layer index to select</param>
|
||||
public void SelectFirstToCurrentLayer(uint currentLayerIndex)
|
||||
{
|
||||
LayerIndexStart = 0;
|
||||
LayerIndexEnd = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects from a layer index to the last layer
|
||||
/// </summary>
|
||||
/// <param name="currentLayerIndex">From layer index to select</param>
|
||||
public void SelectCurrentToLastLayer(uint currentLayerIndex)
|
||||
{
|
||||
LayerIndexStart = Math.Min(currentLayerIndex, SlicerFile.LastLayerIndex);
|
||||
LayerIndexEnd = SlicerFile.LastLayerIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects layer given a range type
|
||||
/// </summary>
|
||||
/// <param name="range"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public void SelectLayers(LayerRangeSelection range)
|
||||
{
|
||||
switch (range)
|
||||
@@ -436,67 +516,141 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
/// </summary>
|
||||
public virtual void InitWithSlicerFile() { }
|
||||
|
||||
/// <summary>
|
||||
/// Clears the ROI and set to empty
|
||||
/// </summary>
|
||||
public void ClearROI()
|
||||
{
|
||||
ROI = Rectangle.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear <see cref="ROI"/> and <see cref="MaskPoints"/>
|
||||
/// </summary>
|
||||
public void ClearROIandMasks()
|
||||
{
|
||||
ClearROI();
|
||||
ClearMasks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set <see cref="ROI"/> only if not set already
|
||||
/// </summary>
|
||||
/// <param name="roi">ROI to set</param>
|
||||
public void SetROIIfEmpty(Rectangle roi)
|
||||
{
|
||||
if (HaveROI) return;
|
||||
ROI = roi;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ROI"/> size, but if empty returns the file resolution size instead
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Size GetRoiSizeOrDefault() => GetRoiSizeOrDefault(SlicerFile.Resolution);
|
||||
public Size GetRoiSizeOrDefault(Mat? defaultMat) => defaultMat is null ? GetRoiSizeOrDefault() : GetRoiSizeOrDefault(defaultMat.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ROI"/> size, but if empty returns <see cref="src"/> size instead
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <returns></returns>
|
||||
public Size GetRoiSizeOrDefault(Mat? src) => src is null ? GetRoiSizeOrDefault() : GetRoiSizeOrDefault(src.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ROI"/> size, but if empty returns the size from <see cref="fallbackRectangle"/> instead
|
||||
/// </summary>
|
||||
/// <param name="fallbackRectangle"></param>
|
||||
/// <returns></returns>
|
||||
public Size GetRoiSizeOrDefault(Rectangle fallbackRectangle) => GetRoiSizeOrDefault(fallbackRectangle.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ROI"/> size, but if empty returns the <see cref="fallbackSize"/> instead
|
||||
/// </summary>
|
||||
/// <param name="fallbackSize"></param>
|
||||
/// <returns></returns>
|
||||
public Size GetRoiSizeOrDefault(Size fallbackSize)
|
||||
{
|
||||
return HaveROI ? _roi.Size : fallbackSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ROI"/> size, but if empty returns the model volume bounds size instead
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Size GetRoiSizeOrVolumeSize() => GetRoiSizeOrVolumeSize(_originalBoundingRectangle.Size);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ROI"/> size, but if empty returns the <see cref="fallbackSize"/> instead
|
||||
/// </summary>
|
||||
/// <param name="fallbackSize"></param>
|
||||
/// <returns></returns>
|
||||
public Size GetRoiSizeOrVolumeSize(Size fallbackSize)
|
||||
{
|
||||
return HaveROI ? _roi.Size : fallbackSize;
|
||||
}
|
||||
|
||||
|
||||
public Mat GetRoiOrDefault(Mat defaultMat)
|
||||
/// <summary>
|
||||
/// Gets a cropped shared <see cref="Mat"/> from <see cref="src"/> by the <see cref="ROI"/>, but if empty return the <see cref="src"/> instead
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <returns></returns>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Gets a cropped shared <see cref="Mat"/> from <see cref="src"/> by the <see cref="ROI"/>, but if empty crop by <see cref="fallbackRoi"/>
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <param name="fallbackRoi"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a cropped shared <see cref="Mat"/> from <see cref="src"/> by the <see cref="ROI"/>, but if empty crop by <see cref="OriginalBoundingRectangle"/>
|
||||
/// </summary>
|
||||
/// <param name="defaultMat"></param>
|
||||
/// <returns></returns>
|
||||
public Mat GetRoiOrVolumeBounds(Mat defaultMat)
|
||||
{
|
||||
return GetRoiOrDefault(defaultMat, _originalBoundingRectangle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ROI"/>, but if empty returns <see cref="OriginalBoundingRectangle"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Rectangle GetRoiOrVolumeBounds() => HaveROI ? _roi : _originalBoundingRectangle;
|
||||
|
||||
/// <summary>
|
||||
/// Clears all masks
|
||||
/// </summary>
|
||||
public void ClearMasks()
|
||||
{
|
||||
MaskPoints = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets masks only if they are empty
|
||||
/// </summary>
|
||||
/// <param name="points"></param>
|
||||
public void SetMasksIfEmpty(Point[][] points)
|
||||
{
|
||||
if (HaveMask) return;
|
||||
MaskPoints = points;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a mask given <see cref="MaskPoints"/>
|
||||
/// </summary>
|
||||
/// <param name="mat"></param>
|
||||
/// <returns></returns>
|
||||
public Mat? GetMask(Mat mat) => GetMask(_maskPoints, mat);
|
||||
|
||||
public Mat? GetMask(Point[][]? points, Mat mat)
|
||||
@@ -507,6 +661,12 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
return GetRoiOrDefault(mask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a mask to a mat <see cref="result"/>
|
||||
/// </summary>
|
||||
/// <param name="original">Original untouched mat</param>
|
||||
/// <param name="result">Mat to modify and apply the mask</param>
|
||||
/// <param name="mask">Mask</param>
|
||||
public void ApplyMask(Mat original, Mat result, Mat? mask)
|
||||
{
|
||||
if (mask is null) return;
|
||||
@@ -538,12 +698,23 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
ApplyMask(original, result, mask);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Execute the operation internally, to be override by class
|
||||
/// </summary>
|
||||
/// <param name="progress"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
protected virtual bool ExecuteInternally(OperationProgress progress)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the operation
|
||||
/// </summary>
|
||||
/// <param name="progress"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
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 abstract class Operation : BindableBase, IDisposable
|
||||
|
||||
public Task<bool> ExecuteAsync(OperationProgress? progress = null) => Task.Run(() => Execute(progress), progress?.Token ?? default);
|
||||
|
||||
/// <summary>
|
||||
/// Execute the operation on a given <see cref="Mat"/>
|
||||
/// </summary>
|
||||
/// <param name="mat"></param>
|
||||
/// <param name="arguments"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public virtual bool Execute(Mat mat, params object[]? arguments)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -597,12 +775,21 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
operation.MaskPoints = MaskPoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize class to XML file
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="indent"></param>
|
||||
public void Serialize(string path, bool indent = false)
|
||||
{
|
||||
if(indent) XmlExtensions.SerializeToFile(this, path, XmlExtensions.SettingsIndent);
|
||||
else XmlExtensions.SerializeToFile(this, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone object
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual Operation Clone()
|
||||
{
|
||||
var operation = MemberwiseClone() as Operation;
|
||||
@@ -623,6 +810,11 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize <see cref="Operation"/> from a XML file
|
||||
/// </summary>
|
||||
/// <param name="path">XML file path</param>
|
||||
/// <returns></returns>
|
||||
public static Operation? Deserialize(string path)
|
||||
{
|
||||
if (!File.Exists(path)) return null;
|
||||
@@ -641,6 +833,12 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
return Deserialize(path, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize <see cref="Operation"/> from a XML file
|
||||
/// </summary>
|
||||
/// <param name="path">XML file path</param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static Operation Deserialize(string path, Type type)
|
||||
{
|
||||
var serializer = new XmlSerializer(type);
|
||||
@@ -650,6 +848,12 @@ public abstract class Operation : BindableBase, IDisposable
|
||||
return operation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize <see cref="Operation"/> from a XML file
|
||||
/// </summary>
|
||||
/// <param name="path">XML file path</param>
|
||||
/// <param name="operation"></param>
|
||||
/// <returns></returns>
|
||||
public static Operation Deserialize(string path, Operation operation) => Deserialize(path, operation.GetType());
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -195,9 +195,7 @@ public class OperationPCBExposure : Operation
|
||||
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 @@ public class OperationPCBExposure : Operation
|
||||
SlicerFile.LiftHeightTotal = 0;
|
||||
SlicerFile.SetNoDelays();
|
||||
|
||||
SlicerFile.Layers = new[] { layer };
|
||||
SlicerFile.Layers = new[] { new Layer(mat, SlicerFile) };
|
||||
}, true);
|
||||
|
||||
|
||||
|
||||
@@ -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 sealed class OperationScripting : Operation
|
||||
_scriptState = CSharpScript.RunAsync(_scriptText,
|
||||
ScriptOptions.Default.AddReferences(typeof(About).Assembly).WithAllowUnsafe(true),
|
||||
ScriptGlobals).Result;
|
||||
|
||||
|
||||
var result = _scriptState.ContinueWithAsync("ScriptInit();").Result;
|
||||
|
||||
RaisePropertyChanged(nameof(CanExecute));
|
||||
|
||||
@@ -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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
[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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
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 sealed class SuggestionWaitTimeBeforeCure : Suggestion
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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),
|
||||
//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) (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);
|
||||
|
||||
@@ -255,6 +255,16 @@ public static class SystemAware
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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.4.1</Version>
|
||||
<Version>3.4.2</Version>
|
||||
<Copyright>Copyright © 2020 PTRTECH</Copyright>
|
||||
<PackageIcon>UVtools.png</PackageIcon>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
@@ -66,11 +66,11 @@
|
||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.5.5.4823" />
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.2.16" />
|
||||
<PackageReference Include="KdTree" Version="1.4.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.2.0" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.3" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreparePackageReleaseNotesFromFile" BeforeTargets="GenerateNuspec">
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<!-- Install directory -->
|
||||
<RegistryKey Root="HKCU" Key="Software\UVtools" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes" />
|
||||
<RegistryValue Root="HKCU" Key="Software\UVtools" Name="InstallDir" Value="[INSTALLLOCATION]" Type="string" />
|
||||
<RegistryValue Root="HKCU" Key="Software\UVtools" Name="Version" Value="$(var.MSIProductVersion)" Type="string" />
|
||||
|
||||
<!-- Open file with UVtools -->
|
||||
<RegistryKey Root="HKCR" Key="*\shell\UVtools" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes" />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<?define ComponentRules="OneToOne"?>
|
||||
<!-- SourceDir instructs IsWiX the location of the directory that contains files for this merge module -->
|
||||
<?define SourceDir="..\publish\UVtools_win-x64_v3.4.1"?>
|
||||
<?define SourceDir="..\publish\UVtools_win-x64_v3.4.2"?>
|
||||
<Module Id="UVtools" Language="1033" Version="1.0.0.0">
|
||||
<Package Id="12aaa1cf-ff06-4a02-abd5-2ac01ac4f83b" Manufacturer="PTRTECH" InstallerVersion="200" Keywords="MSLA, DLP" Description="MSLA/DLP, file analysis, repair, conversion and manipulation" InstallScope="perMachine" Platform="x64" />
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
@@ -1554,8 +1554,8 @@
|
||||
<Component Id="owcD2245B900E5C448485AD96FE57629D9D" Guid="D2245B90-0E5C-4484-85AD-96FE57629D9D">
|
||||
<File Id="owfD2245B900E5C448485AD96FE57629D9D" Source="$(var.SourceDir)\UVtoolsCmd.runtimeconfig.json" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="owc47E738FB97A34F13812E87CF7F2B4643" Guid="47E738FB-97A3-4F13-812E-87CF7F2B4643">
|
||||
<File Id="owf47E738FB97A34F13812E87CF7F2B4643" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_6.0.422.16404.dll" KeyPath="yes" />
|
||||
<Component Id="owc5D9B7363F82B4971A54B614C6E9F5EF1" Guid="5D9B7363-F82B-4971-A54B-614C6E9F5EF1">
|
||||
<File Id="owf5D9B7363F82B4971A54B614C6E9F5EF1" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_6.0.522.21309.dll" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
|
||||
+90
-52
@@ -12,6 +12,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
@@ -25,7 +26,6 @@ using UVtools.Core;
|
||||
using UVtools.Core.FileFormats;
|
||||
using UVtools.Core.Managers;
|
||||
using UVtools.Core.SystemOS;
|
||||
using UVtools.WPF.Extensions;
|
||||
using UVtools.WPF.Structures;
|
||||
using UVtools.WPF.Windows;
|
||||
using Bitmap = Avalonia.Media.Imaging.Bitmap;
|
||||
@@ -180,10 +180,6 @@ public class App : Application
|
||||
UserSettings.Load();
|
||||
UserSettings.SetVersion();
|
||||
|
||||
MaterialManager.Load();
|
||||
OperationProfiles.Load();
|
||||
SuggestionManager.Load();
|
||||
|
||||
/*ThemeSelector = ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes"));
|
||||
ThemeSelector.LoadSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
|
||||
if (ThemeSelector.SelectedTheme.Name == "UVtoolsDark" || ThemeSelector.SelectedTheme.Name == "Light")
|
||||
@@ -208,9 +204,92 @@ public class App : Application
|
||||
ApplyTheme();
|
||||
}
|
||||
|
||||
try
|
||||
if (Program.IsCrashReport)
|
||||
{
|
||||
if (CvInvoke.Init())
|
||||
//Program.Args = new[] {"--crash-report", "test2", "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." };
|
||||
if (string.IsNullOrWhiteSpace(Program.Args[1])) return;
|
||||
if(string.IsNullOrWhiteSpace(Program.Args[2])) return;
|
||||
var category = Program.Args[1];
|
||||
var message = $"{Program.Args[2]}\nCategory: {category}";
|
||||
|
||||
var bugReportMessageMk = $"# Report\n```\n{message}\n```";
|
||||
|
||||
try
|
||||
{
|
||||
var append = $"\n\n# System\n{AboutWindow.GetEssentialInformationStatic()}";
|
||||
message += append;
|
||||
bugReportMessageMk += append;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
var append2 = $"\n\nMachine date time: {DateTime.Now}\n UTC date time: {DateTime.UtcNow}";
|
||||
message += append2;
|
||||
bugReportMessageMk += $"{append2}\n\n# Additional information and Workflow\nComplete with additional information and the workflow that caused this crash.";
|
||||
|
||||
try
|
||||
{
|
||||
Current?.Clipboard?.SetTextAsync(bugReportMessageMk);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
|
||||
var reportButton = MessageWindow.CreateButton("Report", "fas fa-bug");
|
||||
reportButton.Click += (sender, e) =>
|
||||
{
|
||||
Current?.Clipboard?.SetTextAsync(bugReportMessageMk);
|
||||
using var reader = new StringReader(message);
|
||||
SystemAware.OpenBrowser($"https://github.com/sn4k3/UVtools/issues/new?assignees=sn4k3&labels=&template=bug_report.md&title={HttpUtility.UrlEncode($"[Crash] {reader.ReadLine()}")}&body={HttpUtility.UrlEncode("<!--\n# Instructions:\n1. Click on this box;\n2. Select all it text (Ctrl + A);\n3. Paste the report content (Ctrl + V);\n4. Review the content;\n5. Submit the issue.\n!-->")}");
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var helpButton = MessageWindow.CreateButton("Help", "fas fa-question");
|
||||
helpButton.Click += (sender, e) =>
|
||||
{
|
||||
Current?.Clipboard?.SetTextAsync(bugReportMessageMk);
|
||||
SystemAware.OpenBrowser("https://github.com/sn4k3/UVtools/discussions/categories/q-a");
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var restartButton = MessageWindow.CreateButton("Restart", "fas fa-redo-alt");
|
||||
restartButton.Click += (sender, e) =>
|
||||
{
|
||||
SystemAware.StartThisApplication();
|
||||
};
|
||||
|
||||
desktop.MainWindow = new MessageWindow($"{About.SoftwareWithVersion} - Crash report",
|
||||
"far fa-frown",
|
||||
$"{About.Software} crashed due an unexpected {category.ToLowerInvariant()} error.\nYou can report this error if you find necessary.\nFind more details below:\n",
|
||||
message,
|
||||
new[]
|
||||
{
|
||||
reportButton,
|
||||
helpButton,
|
||||
restartButton,
|
||||
MessageWindow.CreateCloseButton("fas fa-sign-out-alt")
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!CvInvoke.Init())
|
||||
{
|
||||
desktop.MainWindow = new CantRunWindow();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
desktop.MainWindow = new CantRunWindow();
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
|
||||
if (desktop.MainWindow is null)
|
||||
{
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
@@ -226,56 +305,15 @@ public class App : Application
|
||||
};
|
||||
}
|
||||
|
||||
MainWindow = new MainWindow();
|
||||
if (UserSettings.Instance.General.StartMaximized)
|
||||
{
|
||||
MainWindow.WindowState = WindowState.Maximized;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UserSettings.Instance.General.RestoreWindowLastSize)
|
||||
{
|
||||
MainWindow.Width = UserSettings.Instance.General.LastWindowBounds.Width;
|
||||
MainWindow.Height = UserSettings.Instance.General.LastWindowBounds.Height;
|
||||
}
|
||||
MaterialManager.Load();
|
||||
OperationProfiles.Load();
|
||||
SuggestionManager.Load();
|
||||
|
||||
if (UserSettings.Instance.General.RestoreWindowLastPosition)
|
||||
{
|
||||
MainWindow.Position = new PixelPoint(UserSettings.Instance.General.LastWindowBounds.Location.X, UserSettings.Instance.General.LastWindowBounds.Location.Y);
|
||||
}
|
||||
}
|
||||
MainWindow = new MainWindow();
|
||||
desktop.MainWindow = MainWindow;
|
||||
}
|
||||
else
|
||||
{
|
||||
desktop.MainWindow = new CantRunWindow();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
desktop.MainWindow = new CantRunWindow();
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
|
||||
|
||||
/*try
|
||||
{
|
||||
if(!CvInvoke.Init())
|
||||
await MainWindow.MessageBoxError("UVtools can not init OpenCV library\n" +
|
||||
"Please build or install this dependencies in order to run UVtools\n" +
|
||||
"Check manual or page at 'Requirements' section for help",
|
||||
"UVtools can not run");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await MainWindow.MessageBoxError("UVtools can not run due lack of dependencies from cvextern/OpenCV\n" +
|
||||
"Please build or install this dependencies in order to run UVtools\n" +
|
||||
"Check manual or page at 'Requirements' section for help\n\n" +
|
||||
"Additional information:\n" +
|
||||
$"{e}", "UVtools can not run");
|
||||
return;
|
||||
}*/
|
||||
|
||||
|
||||
//desktop.Exit += (sender, e) => ThemeSelector.SaveSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public static class ConsoleArguments
|
||||
/// <returns>True if is a valid argument, otherwise false</returns>
|
||||
public static bool ParseArgs(string[] args)
|
||||
{
|
||||
if(args is null || args.Length == 0) return false;
|
||||
if(args.Length == 0) return false;
|
||||
|
||||
if (args[0] is "--cmd" && args.Length > 1)
|
||||
{
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
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"
|
||||
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="600"
|
||||
x:Class="UVtools.WPF.Controls.Suggestions.SuggestionWaitTimeBeforeCureControl">
|
||||
<Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,2,Auto,10,Auto,10,Auto,10,Auto,2,Auto,10,Auto"
|
||||
<Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,2,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,2,Auto,10,Auto"
|
||||
ColumnDefinitions="Auto,10,190,5,Auto,5,190">
|
||||
|
||||
<CheckBox Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="5"
|
||||
@@ -107,7 +107,12 @@
|
||||
IsVisible="{Binding Suggestion.IsSetTypeFixed}"
|
||||
Value="{Binding Suggestion.WaitTimeBeforeCureTransitionLayerCount}"/>
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="6"
|
||||
<TextBlock Grid.Row="10" Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Text="-"/>
|
||||
|
||||
<TextBlock Grid.Row="10" Grid.Column="6"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="Decrement step per layer"
|
||||
IsEnabled="{Binding Suggestion.WaitTimeBeforeCureTransitionLayerCount}"
|
||||
@@ -149,7 +154,12 @@
|
||||
IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerPixels}"
|
||||
Value="{Binding Suggestion.ProportionalBottomLayerPixels}"/>
|
||||
|
||||
<NumericUpDown Grid.Row="12" Grid.Column="6"
|
||||
<TextBlock Grid.Row="12" Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Text="-"/>
|
||||
|
||||
<NumericUpDown Grid.Row="12" Grid.Column="6"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_px"
|
||||
Minimum="1"
|
||||
@@ -182,25 +192,101 @@
|
||||
IsVisible="{Binding Suggestion.IsSetTypeProportionalLayerArea}"
|
||||
Value="{Binding Suggestion.ProportionalLayerArea}"/>
|
||||
|
||||
<TextBlock Grid.Row="14" Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="Sets the maximum allowed time difference relative to the previous layer.
|
||||

Use this option if large time variances are introducing visible layer lines in the model.
|
||||

Use 0 to disable this constrain."
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Text="Max. time difference:"/>
|
||||
|
||||
<TextBlock Grid.Row="14" Grid.Column="2"
|
||||
<NumericUpDown Grid.Row="14" Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_s"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
FormatString="F2"
|
||||
Increment="0.50"
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Value="{Binding Suggestion.ProportionalBottomWaitTimeBeforeCureMaximumDifference}"/>
|
||||
|
||||
<TextBlock Grid.Row="14" Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Text="-"/>
|
||||
|
||||
<NumericUpDown Grid.Row="14" Grid.Column="6"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_s"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
FormatString="F2"
|
||||
Increment="0.50"
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Value="{Binding Suggestion.ProportionalWaitTimeBeforeCureMaximumDifference}"/>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="16" Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="Sets the method to get the proportional mass value"
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Text="Proportional to:"/>
|
||||
|
||||
<ComboBox Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding !Suggestion.IsSetTypeFixed}"
|
||||
Items="{Binding Suggestion.ProportionalCalculateMassFrom, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
|
||||
SelectedItem="{Binding Suggestion.ProportionalCalculateMassFrom, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="18" Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="Relative back height in millimeters to look back and calculate the average or the maximum within the defined range"
|
||||
Text="Look back height:">
|
||||
<TextBlock.IsVisible>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="!Suggestion.IsSetTypeFixed"/>
|
||||
<Binding Path="!Suggestion.IsProportionalCalculateMassFromPrevious"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.IsVisible>
|
||||
</TextBlock>
|
||||
|
||||
<NumericUpDown Grid.Row="18" Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_mm"
|
||||
Minimum="0"
|
||||
Maximum="1000"
|
||||
FormatString="F3"
|
||||
Increment="0.01"
|
||||
Value="{Binding Suggestion.ProportionalMassRelativeHeight}">
|
||||
<NumericUpDown.IsVisible>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="!Suggestion.IsSetTypeFixed"/>
|
||||
<Binding Path="!Suggestion.IsProportionalCalculateMassFromPrevious"/>
|
||||
</MultiBinding>
|
||||
</NumericUpDown.IsVisible>
|
||||
</NumericUpDown>
|
||||
|
||||
|
||||
|
||||
<TextBlock Grid.Row="20" Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Text="Minimum:"/>
|
||||
|
||||
<TextBlock Grid.Row="14" Grid.Column="6"
|
||||
<TextBlock Grid.Row="20" Grid.Column="6"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Text="Maximum:"/>
|
||||
|
||||
<TextBlock Grid.Row="16" Grid.Column="0"
|
||||
<TextBlock Grid.Row="22" Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="Sets the limits for the bottom layers (Min-Max)"
|
||||
Text="Limits (bottom):"/>
|
||||
|
||||
<NumericUpDown Grid.Row="16" Grid.Column="2"
|
||||
<NumericUpDown Grid.Row="22" Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_s"
|
||||
Minimum="0"
|
||||
@@ -209,11 +295,11 @@
|
||||
Increment="{Binding SlicerFile.LayerHeight}"
|
||||
Value="{Binding Suggestion.MinimumBottomWaitTimeBeforeCure}"/>
|
||||
|
||||
<TextBlock Grid.Row="16" Grid.Column="4"
|
||||
<TextBlock Grid.Row="22" Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
Text="-"/>
|
||||
|
||||
<NumericUpDown Grid.Row="16" Grid.Column="6"
|
||||
<NumericUpDown Grid.Row="22" Grid.Column="6"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_s"
|
||||
Minimum="0"
|
||||
@@ -222,12 +308,12 @@
|
||||
Increment="0.50"
|
||||
Value="{Binding Suggestion.MaximumBottomWaitTimeBeforeCure}"/>
|
||||
|
||||
<TextBlock Grid.Row="18" Grid.Column="0"
|
||||
<TextBlock Grid.Row="24" Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip.Tip="Sets the limits for the normal layers (Min-Max)"
|
||||
Text="Limits (normal):"/>
|
||||
|
||||
<NumericUpDown Grid.Row="18" Grid.Column="2"
|
||||
<NumericUpDown Grid.Row="24" Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_s"
|
||||
Minimum="0"
|
||||
@@ -236,11 +322,11 @@
|
||||
Increment="0.50"
|
||||
Value="{Binding Suggestion.MinimumWaitTimeBeforeCure}"/>
|
||||
|
||||
<TextBlock Grid.Row="18" Grid.Column="4"
|
||||
<TextBlock Grid.Row="24" Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
Text="-"/>
|
||||
|
||||
<NumericUpDown Grid.Row="18" Grid.Column="6"
|
||||
<NumericUpDown Grid.Row="24" Grid.Column="6"
|
||||
VerticalAlignment="Center"
|
||||
Classes="ValueLabel ValueLabel_s"
|
||||
Minimum="0"
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<Grid
|
||||
Name="grid"
|
||||
RowDefinitions="Auto"
|
||||
ColumnDefinitions="Auto,Auto,*,Auto,Auto"
|
||||
ColumnDefinitions="Auto,Auto,*,Auto"
|
||||
VerticalAlignment="Center"
|
||||
ShowGridLines="True">
|
||||
|
||||
@@ -77,12 +77,6 @@
|
||||
<TextBlock
|
||||
Grid.Column="3"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Padding="15"
|
||||
Text="Unit"/>
|
||||
<TextBlock
|
||||
Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Padding="15"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
@@ -27,7 +29,7 @@ public class ToolEditParametersControl : ToolControl
|
||||
public TextBlock Name { get; }
|
||||
public TextBlock OldValue { get; }
|
||||
public NumericUpDown NewValue { get; }
|
||||
public TextBlock Unit { get; }
|
||||
//public TextBlock Unit { get; }
|
||||
public Button ResetButton { get; }
|
||||
|
||||
public RowControl(FileFormat.PrintParameterModifier modifier)
|
||||
@@ -73,13 +75,19 @@ public class ToolEditParametersControl : ToolControl
|
||||
NewValue.FormatString = $"F{modifier.DecimalPlates}";
|
||||
}
|
||||
|
||||
Unit = new TextBlock
|
||||
if (!string.IsNullOrWhiteSpace(modifier.ValueUnit))
|
||||
{
|
||||
var valueLabel = modifier.ValueUnit == "☀" ? "sun" : modifier.ValueUnit.Replace("/", string.Empty);
|
||||
NewValue.Classes = new Classes("ValueLabel", $"ValueLabel_{valueLabel}");
|
||||
}
|
||||
|
||||
/*Unit = new TextBlock
|
||||
{
|
||||
Text = modifier.ValueUnit,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Padding = new Thickness(10, 0, 15, 0),
|
||||
Tag = this
|
||||
};
|
||||
};*/
|
||||
|
||||
ResetButton = new Button
|
||||
{
|
||||
@@ -141,7 +149,7 @@ public class ToolEditParametersControl : ToolControl
|
||||
grid.Children.Add(rowControl.Name);
|
||||
grid.Children.Add(rowControl.OldValue);
|
||||
grid.Children.Add(rowControl.NewValue);
|
||||
grid.Children.Add(rowControl.Unit);
|
||||
//grid.Children.Add(rowControl.Unit);
|
||||
grid.Children.Add(rowControl.ResetButton);
|
||||
Grid.SetRow(rowControl.Name, rowIndex);
|
||||
Grid.SetColumn(rowControl.Name, column++);
|
||||
@@ -152,8 +160,8 @@ public class ToolEditParametersControl : ToolControl
|
||||
Grid.SetRow(rowControl.NewValue, rowIndex);
|
||||
Grid.SetColumn(rowControl.NewValue, column++);
|
||||
|
||||
Grid.SetRow(rowControl.Unit, rowIndex);
|
||||
Grid.SetColumn(rowControl.Unit, column++);
|
||||
//Grid.SetRow(rowControl.Unit, rowIndex);
|
||||
//Grid.SetColumn(rowControl.Unit, column++);
|
||||
|
||||
Grid.SetRow(rowControl.ResetButton, rowIndex);
|
||||
Grid.SetColumn(rowControl.ResetButton, column++);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
|
||||
@@ -17,6 +17,7 @@ using Avalonia.Styling;
|
||||
using UVtools.Core.FileFormats;
|
||||
using UVtools.Core.SystemOS;
|
||||
using UVtools.WPF.Extensions;
|
||||
using Size = Avalonia.Size;
|
||||
|
||||
namespace UVtools.WPF.Controls;
|
||||
|
||||
@@ -63,7 +64,17 @@ public class WindowEx : Window, INotifyPropertyChanged, IStyleable
|
||||
_propertyChanged?.Invoke(this, e);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Enum
|
||||
|
||||
public enum WindowConstrainsMaxSizeType : byte
|
||||
{
|
||||
None,
|
||||
UserSettings,
|
||||
Ratio
|
||||
}
|
||||
#endregion
|
||||
|
||||
Type IStyleable.StyleKey => typeof(Window);
|
||||
|
||||
public DialogResults DialogResult { get; set; } = DialogResults.Unknown;
|
||||
@@ -78,6 +89,21 @@ public class WindowEx : Window, INotifyPropertyChanged, IStyleable
|
||||
|
||||
public double WindowMaxHeight => this.GetScreenWorkingArea().Height - UserSettings.Instance.General.WindowsVerticalMargin;
|
||||
|
||||
public Size WindowMaxSize
|
||||
{
|
||||
get
|
||||
{
|
||||
var size = this.GetScreenWorkingArea();
|
||||
return new Size(size.Width - UserSettings.Instance.General.WindowsHorizontalMargin,
|
||||
this.GetScreenWorkingArea().Height - UserSettings.Instance.General.WindowsVerticalMargin);
|
||||
}
|
||||
}
|
||||
|
||||
public WindowConstrainsMaxSizeType WindowConstrainMaxSize { get; set; } = WindowConstrainsMaxSizeType.UserSettings;
|
||||
|
||||
public double WindowsWidthMaxSizeRatio { get; set; } = 1;
|
||||
public double WindowsHeightMaxSizeRatio { get; set; } = 1;
|
||||
|
||||
public UserSettings Settings => UserSettings.Instance;
|
||||
|
||||
public virtual FileFormat SlicerFile
|
||||
@@ -97,10 +123,42 @@ public class WindowEx : Window, INotifyPropertyChanged, IStyleable
|
||||
protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
if (!CanResize && WindowState == WindowState.Normal)
|
||||
AutoConstainsWindowMaxSize();
|
||||
}
|
||||
|
||||
/*protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
var result = base.MeasureOverride(availableSize);
|
||||
if (SizeToContent == SizeToContent.Manual) return result;
|
||||
|
||||
if (MaxWidth > 0 && MaxWidth < result.Width)
|
||||
{
|
||||
MaxWidth = WindowMaxWidth;
|
||||
MaxHeight = WindowMaxHeight;
|
||||
result = result.WithWidth(MaxWidth);
|
||||
}
|
||||
if (MaxHeight > 0 && MaxHeight < result.Height)
|
||||
{
|
||||
result = result.WithHeight(MaxHeight);
|
||||
}
|
||||
|
||||
return result;
|
||||
}*/
|
||||
|
||||
public void AutoConstainsWindowMaxSize()
|
||||
{
|
||||
if (!CanResize && WindowState == WindowState.Normal && WindowConstrainMaxSize != WindowConstrainsMaxSizeType.None)
|
||||
{
|
||||
switch (WindowConstrainMaxSize)
|
||||
{
|
||||
case WindowConstrainsMaxSizeType.UserSettings:
|
||||
MaxWidth = WindowMaxWidth;
|
||||
MaxHeight = WindowMaxHeight;
|
||||
break;
|
||||
case WindowConstrainsMaxSizeType.Ratio:
|
||||
var size = this.GetScreenWorkingArea();
|
||||
if (WindowsWidthMaxSizeRatio is > 0 and < 1) MaxWidth = size.Width * WindowsWidthMaxSizeRatio;
|
||||
if (WindowsHeightMaxSizeRatio is > 0 and < 1) MaxHeight = size.Height * WindowsHeightMaxSizeRatio;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public static class ErrorLog
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
Debug.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public static class WindowExtensions
|
||||
public static Screen GetCurrentScreen(this Window window)
|
||||
{
|
||||
return //window.Screens.ScreenFromVisual(window) ??
|
||||
window.Screens.ScreenFromVisual(App.MainWindow) ??
|
||||
window.Screens.ScreenFromVisual(App.MainWindow ?? window) ??
|
||||
window.Screens.Primary ??
|
||||
window.Screens.All[0];
|
||||
}
|
||||
|
||||
@@ -491,8 +491,7 @@ public partial class MainWindow
|
||||
get
|
||||
{
|
||||
if (!LayerCache.IsCached) return "Pixels: 0";
|
||||
var pixelPercent = Math.Round(LayerCache.Layer.NonZeroPixelCount * 100.0 / (SlicerFile.ResolutionX * SlicerFile.ResolutionY), 2);
|
||||
var text = $"Pixels: {LayerCache.Layer.NonZeroPixelCount} ({pixelPercent}%)";
|
||||
var text = $"Pixels: {LayerCache.Layer.NonZeroPixelCount} ({LayerCache.Layer.NonZeroPixelPercentage:F2}%)";
|
||||
var volume = LayerCache.Layer.Volume;
|
||||
if (volume > 0)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Org.BouncyCastle.Operators;
|
||||
using UVtools.AvaloniaControls;
|
||||
using UVtools.Core;
|
||||
using UVtools.Core.Extensions;
|
||||
@@ -250,6 +251,30 @@ public partial class MainWindow : WindowEx
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
if (Settings.General.StartMaximized)
|
||||
{
|
||||
WindowState = WindowState.Maximized;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Settings.General.RestoreWindowLastPosition)
|
||||
{
|
||||
Position = new PixelPoint(Settings.General.LastWindowBounds.Location.X, Settings.General.LastWindowBounds.Location.Y);
|
||||
}
|
||||
|
||||
if (Settings.General.RestoreWindowLastSize)
|
||||
{
|
||||
Width = Settings.General.LastWindowBounds.Width;
|
||||
Height = Settings.General.LastWindowBounds.Height;
|
||||
}
|
||||
|
||||
var windowSize = this.GetScreenWorkingArea();
|
||||
if (Width >= windowSize.Width || Height >= windowSize.Height)
|
||||
{
|
||||
WindowState = WindowState.Maximized;
|
||||
}
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
//App.ThemeSelector?.EnableThemes(this);
|
||||
@@ -703,7 +728,6 @@ public partial class MainWindow : WindowEx
|
||||
protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
var windowSize = this.GetScreenWorkingArea();
|
||||
|
||||
var clientSizeObs = this.GetObservable(ClientSizeProperty);
|
||||
clientSizeObs.Subscribe(size =>
|
||||
@@ -726,11 +750,6 @@ public partial class MainWindow : WindowEx
|
||||
ProcessFiles(args.Data.GetFileNames()?.ToArray());
|
||||
});
|
||||
|
||||
if (!Settings.General.StartMaximized && (ClientSize.Width >= windowSize.Width || ClientSize.Height >= windowSize.Height))
|
||||
{
|
||||
WindowState = WindowState.Maximized;
|
||||
}
|
||||
|
||||
AddLog($"{About.Software} start", Program.ProgramStartupTime.Elapsed.TotalSeconds);
|
||||
|
||||
if (Settings.General.CheckForUpdatesOnStartup)
|
||||
@@ -1067,7 +1086,7 @@ public partial class MainWindow : WindowEx
|
||||
App.ApplyTheme();
|
||||
}
|
||||
|
||||
if (oldLayerCompressionCodec != Settings.General.LayerCompressionCodec)
|
||||
if (oldLayerCompressionCodec != Settings.General.LayerCompressionCodec && IsFileLoaded)
|
||||
{
|
||||
IsGUIEnabled = false;
|
||||
ShowProgressWindow($"Changing layers compression codec from {oldLayerCompressionCodec.ToString().ToUpper()} to {Settings.General.LayerCompressionCodec.ToString().ToUpper()}");
|
||||
@@ -1210,7 +1229,7 @@ public partial class MainWindow : WindowEx
|
||||
private void UpdateTitle()
|
||||
{
|
||||
var title = $"{About.Software} ";
|
||||
|
||||
|
||||
if (IsFileLoaded)
|
||||
{
|
||||
title += $"File: {SlicerFile.Filename} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) ";
|
||||
@@ -1254,17 +1273,39 @@ public partial class MainWindow : WindowEx
|
||||
try
|
||||
{
|
||||
var operation = Operation.Deserialize(files[i]);
|
||||
await ShowRunOperation(operation);
|
||||
operation.SlicerFile = SlicerFile;
|
||||
if ((_globalModifiers & KeyModifiers.Shift) != 0) await RunOperation(operation);
|
||||
else await ShowRunOperation(operation);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (files[i].EndsWith(".cs") || files[i].EndsWith(".csx"))
|
||||
{
|
||||
if (!IsFileLoaded) continue;
|
||||
try
|
||||
{
|
||||
var operation = new OperationScripting(SlicerFile);
|
||||
operation.ReloadScriptFromFile(files[i]);
|
||||
if (operation.CanExecute)
|
||||
{
|
||||
if ((_globalModifiers & KeyModifiers.Shift) != 0) await RunOperation(operation);
|
||||
else await ShowRunOperation(operation);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (i == 0 && !openNewWindow && (_globalModifiers & KeyModifiers.Shift) == 0)
|
||||
{
|
||||
@@ -1293,7 +1334,7 @@ public partial class MainWindow : WindowEx
|
||||
var fileNameOnly = Path.GetFileName(fileName);
|
||||
SlicerFile = FileFormat.FindByExtensionOrFilePath(fileName, true);
|
||||
if (SlicerFile is null) return;
|
||||
|
||||
|
||||
IsGUIEnabled = false;
|
||||
ShowProgressWindow($"Opening: {fileNameOnly}");
|
||||
|
||||
@@ -2024,6 +2065,8 @@ public partial class MainWindow : WindowEx
|
||||
public async Task<bool> RunOperation(Operation baseOperation)
|
||||
{
|
||||
if (baseOperation is null) return false;
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
baseOperation.SlicerFile ??= SlicerFile;
|
||||
|
||||
switch (baseOperation)
|
||||
{
|
||||
|
||||
+37
-26
@@ -1,17 +1,12 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Emgu.CV;
|
||||
using Emgu.CV.Structure;
|
||||
using Projektanker.Icons.Avalonia;
|
||||
using Projektanker.Icons.Avalonia.FontAwesome;
|
||||
using Projektanker.Icons.Avalonia.MaterialDesign;
|
||||
using UVtools.Core.Extensions;
|
||||
using UVtools.Core.Gerber;
|
||||
using UVtools.WPF.Extensions;
|
||||
using UVtools.Core.SystemOS;
|
||||
|
||||
namespace UVtools.WPF;
|
||||
|
||||
@@ -19,7 +14,8 @@ namespace UVtools.WPF;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static string[] Args = null!;
|
||||
public static string[] Args = Array.Empty<string>();
|
||||
public static bool IsCrashReport;
|
||||
|
||||
public static Stopwatch ProgramStartupTime = null!;
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
@@ -43,6 +39,11 @@ public static class Program
|
||||
return;
|
||||
}
|
||||
|
||||
if (Args.Length > 2 && Args[0] == "--crash-report")
|
||||
{
|
||||
IsCrashReport = true;
|
||||
}
|
||||
|
||||
/*Slicer slicer = new(Size.Empty, SizeF.Empty, "D:\\Cube100x100x100.stl");
|
||||
var slices = slicer.SliceModel(0.05f);
|
||||
|
||||
@@ -60,33 +61,43 @@ public static class Program
|
||||
//var machinesText = Machine.GenerateMachinePresetsFromPrusaSlicer();
|
||||
|
||||
// Add the event handler for handling non-UI thread exceptions to the event.
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) => HandleUnhandledException("Non-UI", (Exception)e.ExceptionObject);
|
||||
TaskScheduler.UnobservedTaskException += (sender, e) => HandleUnhandledException("Task", e.Exception);
|
||||
//AppDomain.CurrentDomain.FirstChanceException += CurrentDomainOnFirstChanceException;
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
|
||||
try
|
||||
{
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
HandleUnhandledException("Application", e);
|
||||
}
|
||||
|
||||
|
||||
// Closing
|
||||
}
|
||||
|
||||
private static void CurrentDomainOnFirstChanceException(object? sender, FirstChanceExceptionEventArgs e)
|
||||
private static void HandleUnhandledException(string category, Exception ex)
|
||||
{
|
||||
ErrorLog.AppendLine("First chance exception", e.Exception.ToString());
|
||||
}
|
||||
ErrorLog.AppendLine($"Fatal {category} Error", ex.ToString());
|
||||
|
||||
|
||||
private static async void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var ex = (Exception)e.ExceptionObject;
|
||||
ErrorLog.AppendLine("Fatal Non-UI Error", ex.ToString());
|
||||
|
||||
try
|
||||
if (!IsCrashReport)
|
||||
{
|
||||
var errorMsg = $"An application error occurred. Please contact the administrator with the following information:\n\n{ex}";
|
||||
await App.MainWindow.MessageBoxError(errorMsg, "Fatal Non-UI Error");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.WriteLine(exception);
|
||||
try
|
||||
{
|
||||
SystemAware.StartThisApplication($"--crash-report \"{category}\" \"{ex}\"");
|
||||
//var errorMsg = $"An application error occurred. Please contact the administrator with the following information:\n\n{ex}";
|
||||
//await App.MainWindow.MessageBoxError(errorMsg, "Fatal Non-UI Error");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.WriteLine(exception);
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<Version>3.4.1</Version>
|
||||
<Version>3.4.2</Version>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<PackageIcon>UVtools.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
@@ -39,15 +39,15 @@
|
||||
<NoWarn>1701;1702;</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.13" />
|
||||
<PackageReference Include="Avalonia" Version="0.10.14" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.14" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.14" />
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.14" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="2.0.0" />
|
||||
<PackageReference Include="Projektanker.Icons.Avalonia" Version="4.4.0" />
|
||||
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="4.4.0" />
|
||||
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="4.4.0" />
|
||||
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.12" />
|
||||
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.13" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\UVtools.AvaloniaControls\UVtools.AvaloniaControls.csproj" />
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Xml.Serialization;
|
||||
using Avalonia.Media;
|
||||
using JetBrains.Annotations;
|
||||
@@ -1394,6 +1395,7 @@ public sealed class UserSettings : BindableBase
|
||||
public sealed class ToolsUserSettings : BindableBase
|
||||
{
|
||||
private bool _expandDescriptions = true;
|
||||
private bool _promptForConfirmation = true;
|
||||
private bool _restoreLastUsedSettings;
|
||||
private bool _lastUsedSettingsKeepOnCloseFile = true;
|
||||
private bool _lastUsedSettingsPriorityOverDefaultProfile = true;
|
||||
@@ -1404,6 +1406,12 @@ public sealed class UserSettings : BindableBase
|
||||
set => RaiseAndSetIfChanged(ref _expandDescriptions, value);
|
||||
}
|
||||
|
||||
public bool PromptForConfirmation
|
||||
{
|
||||
get => _promptForConfirmation;
|
||||
set => RaiseAndSetIfChanged(ref _promptForConfirmation, value);
|
||||
}
|
||||
|
||||
public bool RestoreLastUsedSettings
|
||||
{
|
||||
get => _restoreLastUsedSettings;
|
||||
|
||||
@@ -132,6 +132,24 @@ public class AboutWindow : WindowEx
|
||||
|
||||
public void OpenLicense() => SystemAware.OpenBrowser(About.LicenseUrl);
|
||||
|
||||
public static string GetEssentialInformationStatic()
|
||||
{
|
||||
var message = new StringBuilder();
|
||||
message.AppendLine($"{About.SoftwareWithVersionArch}");
|
||||
message.AppendLine($"Operative system: {OSDescription}");
|
||||
message.AppendLine($"Processor: {ProcessorName}");
|
||||
message.AppendLine($"Processor cores: {ProcessorCount}");
|
||||
message.AppendLine($"Memory RAM: {MemoryRAMDescription}");
|
||||
message.AppendLine($"Runtime: {RuntimeDescription}");
|
||||
message.AppendLine($"Framework: {FrameworkDescription}");
|
||||
message.AppendLine($"AvaloniaUI: {AvaloniaUIDescription}");
|
||||
message.AppendLine($"OpenCV: {OpenCVVersion}");
|
||||
message.AppendLine();
|
||||
message.AppendLine($"Path: {App.ApplicationPath}");
|
||||
message.AppendLine($"Executable: {App.AppExecutable}");
|
||||
return message.ToString();
|
||||
}
|
||||
|
||||
private string GetEssentialInformation()
|
||||
{
|
||||
var message = new StringBuilder();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
<uc:WindowEx 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"
|
||||
@@ -99,4 +99,4 @@
|
||||
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
</uc:WindowEx>
|
||||
|
||||
@@ -6,21 +6,18 @@
|
||||
* of this license document, but changing it is not allowed.
|
||||
*/
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using UVtools.Core.SystemOS;
|
||||
using UVtools.WPF.Controls;
|
||||
|
||||
namespace UVtools.WPF.Windows
|
||||
{
|
||||
public partial class CantRunWindow : Window
|
||||
public partial class CantRunWindow : WindowEx
|
||||
{
|
||||
public CantRunWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<uc:WindowEx 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"
|
||||
xmlns:uc="clr-namespace:UVtools.WPF.Controls"
|
||||
xmlns:i="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="UVtools.WPF.Windows.MessageWindow"
|
||||
Icon="/Assets/Icons/UVtools.ico"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False"
|
||||
SizeToContent="Manual"
|
||||
MinWidth="300"
|
||||
MinHeight="200"
|
||||
Width="800"
|
||||
Height="600"
|
||||
WindowConstrainMaxSize="Ratio"
|
||||
WindowsWidthMaxSizeRatio="0.75"
|
||||
WindowsHeightMaxSizeRatio="0.75"
|
||||
Title="UVtools Message">
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<Border Grid.Row="0"
|
||||
Padding="20"
|
||||
IsVisible="{Binding HeaderIsVisible}"
|
||||
Classes="Header">
|
||||
|
||||
<Grid ColumnDefinitions="Auto,10,*">
|
||||
<i:Icon Grid.Column="0"
|
||||
FontSize="{Binding HeaderIconSize}"
|
||||
FontWeight="Bold"
|
||||
IsVisible="{Binding HeaderIcon, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
||||
Value="{Binding HeaderIcon}"
|
||||
Foreground="{StaticResource LogoColor}"
|
||||
HorizontalAlignment="Center"/>
|
||||
|
||||
<TextBox Grid.Column="2"
|
||||
Classes="TransparentReadOnly"
|
||||
FontWeight="Bold"
|
||||
IsVisible="{Binding HeaderTitle, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
||||
Text="{Binding HeaderTitle}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
|
||||
<TextBox Grid.Row="1"
|
||||
Classes="TransparentReadOnly"
|
||||
Padding="20"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding Message}"/>
|
||||
|
||||
<Border Grid.Row="2"
|
||||
Margin="0"
|
||||
Classes="FooterActions">
|
||||
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<Button Grid.Column="0"
|
||||
IsVisible="{Binding AboutButtonIsVisible}"
|
||||
Command="{Binding OpenAboutWindow}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<Image Source="/Assets/Icons/UVtools.ico"
|
||||
Height="26"/>
|
||||
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="About"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
||||
Name="ButtonsRightPanel"
|
||||
Spacing="5"
|
||||
HorizontalAlignment="Right">
|
||||
|
||||
<uc:ButtonWithIcon Command="{Binding Close}"
|
||||
IsCancel="True"
|
||||
Padding="10"
|
||||
VerticalContentAlignment="Center"
|
||||
Icon="fas fa-sign-out-alt"
|
||||
Text="Close"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
||||
|
||||
|
||||
</Border>
|
||||
</Grid>
|
||||
</uc:WindowEx>
|
||||
@@ -0,0 +1,141 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using UVtools.WPF.Controls;
|
||||
|
||||
namespace UVtools.WPF.Windows
|
||||
{
|
||||
public partial class MessageWindow : WindowEx
|
||||
{
|
||||
private string? _headerIcon;
|
||||
private ushort _headerIconSize = 64;
|
||||
private string _headerTitle;
|
||||
private bool _aboutButtonIsVisible = true;
|
||||
private string _message;
|
||||
|
||||
private readonly StackPanel _buttonsRightPanel;
|
||||
|
||||
public string? HeaderIcon
|
||||
{
|
||||
get => _headerIcon;
|
||||
set
|
||||
{
|
||||
if (!RaiseAndSetIfChanged(ref _headerIcon, value)) return;
|
||||
RaisePropertyChanged(nameof(HeaderIsVisible));
|
||||
}
|
||||
}
|
||||
|
||||
public ushort HeaderIconSize
|
||||
{
|
||||
get => _headerIconSize;
|
||||
set => RaiseAndSetIfChanged(ref _headerIconSize, value);
|
||||
}
|
||||
|
||||
public string? HeaderTitle
|
||||
{
|
||||
get => _headerTitle;
|
||||
set
|
||||
{
|
||||
if(!RaiseAndSetIfChanged(ref _headerTitle, value)) return;
|
||||
RaisePropertyChanged(nameof(HeaderIsVisible));
|
||||
}
|
||||
}
|
||||
|
||||
public bool HeaderIsVisible => !string.IsNullOrWhiteSpace(HeaderIcon) || !string.IsNullOrWhiteSpace(HeaderTitle);
|
||||
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => RaiseAndSetIfChanged(ref _message, value);
|
||||
}
|
||||
|
||||
public bool AboutButtonIsVisible
|
||||
{
|
||||
get => _aboutButtonIsVisible;
|
||||
set => RaiseAndSetIfChanged(ref _aboutButtonIsVisible, value);
|
||||
}
|
||||
|
||||
public MessageWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
CanResize = Settings.General.WindowsCanResize;
|
||||
if (WindowStartupLocation == WindowStartupLocation.CenterOwner && Owner is null)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
}
|
||||
|
||||
AutoConstainsWindowMaxSize();
|
||||
|
||||
_buttonsRightPanel = this.FindControl<StackPanel>("ButtonsRightPanel");
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
public MessageWindow(string title, string? headerIcon, string? headerTitle, string message, ButtonWithIcon[]? buttons = null) : this()
|
||||
{
|
||||
Title = title;
|
||||
HeaderIcon = headerIcon;
|
||||
HeaderTitle = headerTitle;
|
||||
Message = message;
|
||||
|
||||
if (buttons is not null && buttons.Length > 0)
|
||||
{
|
||||
_buttonsRightPanel.Children.Clear();
|
||||
_buttonsRightPanel.Children.AddRange(buttons);
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
button.Click += (sender, e) =>
|
||||
{
|
||||
if (e.Handled) return;
|
||||
Close(button);
|
||||
e.Handled = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MessageWindow(string title, string message, ButtonWithIcon[]? buttons = null) : this(title, null, null, message, buttons) { }
|
||||
|
||||
/*protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
}*/
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public async void OpenAboutWindow()
|
||||
{
|
||||
await new AboutWindow().ShowDialog(this);
|
||||
}
|
||||
|
||||
public static ButtonWithIcon CreateButton(string? text, string? icon, int padding = 10) =>
|
||||
new()
|
||||
{
|
||||
Icon = icon,
|
||||
Text = text,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Padding = new Thickness(padding)
|
||||
};
|
||||
|
||||
public static ButtonWithIcon CreateButton(string? text, int padding = 10) => CreateButton(text, null, padding);
|
||||
|
||||
public static ButtonWithIcon CreateCancelButton(string? icon = null, int padding = 10)
|
||||
{
|
||||
var btn = CreateButton("Cancel", icon, padding);
|
||||
btn.IsCancel = true;
|
||||
return btn;
|
||||
}
|
||||
|
||||
public static ButtonWithIcon CreateCloseButton(string? icon = null, int padding = 10)
|
||||
{
|
||||
var btn = CreateButton("Close", icon, padding);
|
||||
btn.IsCancel = true;
|
||||
return btn;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1886,6 +1886,9 @@
|
||||
ToolTip.Tip="Disable this if you want to gain some vertical space for windows, you can still toggle the descriptions and read them if required."
|
||||
Content="Expand and show descriptions by default"/>
|
||||
|
||||
<CheckBox IsChecked="{Binding Settings.Tools.PromptForConfirmation}"
|
||||
Content="Always prompt for confirmation before execute the operation"/>
|
||||
|
||||
<CheckBox IsChecked="{Binding Settings.Tools.RestoreLastUsedSettings}"
|
||||
Content="Keep and restore the last used settings on operations per user session/instance"/>
|
||||
|
||||
|
||||
@@ -815,10 +815,9 @@ public class ToolWindow : WindowEx
|
||||
ToolControl.BaseOperation.SetMasksIfEmpty(Masks);
|
||||
|
||||
if (!await ToolControl.ValidateForm()) return;
|
||||
if (!string.IsNullOrEmpty(ToolControl.BaseOperation.ConfirmationText))
|
||||
if (Settings.Tools.PromptForConfirmation && !string.IsNullOrEmpty(ToolControl.BaseOperation.ConfirmationText))
|
||||
{
|
||||
var result =
|
||||
await this.MessageBoxQuestion(
|
||||
var result = await this.MessageBoxQuestion(
|
||||
$"Are you sure you want to {ToolControl.BaseOperation.ConfirmationText}");
|
||||
if (result != ButtonResult.Yes) return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user