diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-04-02 05:14:28 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-04-02 05:14:28 +0300 |
commit | d38e9ee62dccf8c7cabc770c6d74f93ae9d5fe51 (patch) | |
tree | 1788f280278d969b3edb3c23278a6523c43a87b1 | |
parent | 6d1972ed8f9dcdf85cad41aa5337b64ce27b573e (diff) |
v3.2.1v3.2.1
- **AnyCubic file format:**
- (Fix) Lift height and speed are not being correctly set on old version, keeping a constant value (#441)
- (Fix) Retract speed getter was not return value from TSMC if using version 516
- **Tool - Infill:**
- (Add) Waves infill type
- (Add) Concentric infill type
- (Add) Gyroid infill type (#443)
- (Change) Increase the default spacing from 200px to 300px
- (Improvement) Fastter infill processing by use the model bounds
- (Add) `FormatSpeedUnit` property to file formats to get the speed unit which the file use internally
- (Fix) UI: ROI rectangle can overlap scroll bars while selecting
-rw-r--r-- | CHANGELOG.md | 14 | ||||
-rw-r--r-- | UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs | 210 | ||||
-rw-r--r-- | UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj | 2 | ||||
-rw-r--r-- | UVtools.Core/Extensions/EmguExtensions.cs | 59 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/CXDLPFile.cs | 14 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 11 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/JXSFile.cs | 12 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/PhotonWorkshopFile.cs | 101 | ||||
-rw-r--r-- | UVtools.Core/Operations/Operation.cs | 7 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationInfill.cs | 204 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 140 | ||||
-rw-r--r-- | UVtools.InstallerMM/UVtools.InstallerMM.wxs | 2 | ||||
-rw-r--r-- | UVtools.WPF/MainWindow.axaml | 3996 | ||||
-rw-r--r-- | UVtools.WPF/Program.cs | 35 | ||||
-rw-r--r-- | UVtools.WPF/UVtools.WPF.csproj | 8 | ||||
-rw-r--r-- | build/createRelease.ps1 | 25 |
16 files changed, 2585 insertions, 2255 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e860122..600081d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 02/04/2022 - v3.2.1 + +- **AnyCubic file format:** + - (Fix) Lift height and speed are not being correctly set on old version, keeping a constant value (#441) + - (Fix) Retract speed getter was not return value from TSMC if using version 516 +- **Tool - Infill:** + - (Add) Waves infill type + - (Add) Concentric infill type + - (Add) Gyroid infill type (#443) + - (Change) Increase the default spacing from 200px to 300px + - (Improvement) Fastter infill processing by use the model bounds +- (Add) `FormatSpeedUnit` property to file formats to get the speed unit which the file use internally +- (Fix) UI: ROI rectangle can overlap scroll bars while selecting + ## 26/03/2022 - v3.2.0 - **Core:** diff --git a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs index 71d8ed8..e2133df 100644 --- a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs +++ b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs @@ -10,6 +10,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Drawing; using System.Runtime.CompilerServices; using Avalonia; @@ -1074,7 +1075,10 @@ public class AdvancedImageBox : UserControl HorizontalScrollBar.Scroll += ScrollBarOnScroll; VerticalScrollBar.Scroll += ScrollBarOnScroll; - ViewPort.PointerWheelChanged += FillContainerOnPointerWheelChanged; + ViewPort.PointerPressed += ViewPortOnPointerPressed; + ViewPort.PointerLeave += ViewPortOnPointerLeave; + ViewPort.PointerMoved += ViewPortOnPointerMoved; + ViewPort.PointerWheelChanged += ViewPortOnPointerWheelChanged; } private void InitializeComponent() @@ -1088,7 +1092,6 @@ public class AdvancedImageBox : UserControl { if (!_canRender) return; if (renderOnlyCursorTracker && _trackerImage is null) return; - InvalidateVisual(); } @@ -1096,7 +1099,8 @@ public class AdvancedImageBox : UserControl { //Debug.WriteLine($"Render: {DateTime.Now.Ticks}"); base.Render(context); - + + var viewPortSize = ViewPortSize; // Draw Grid var gridCellSize = GridCellSize; if (ShowGrid & gridCellSize > 0 && (!IsHorizontalBarVisible || !IsVerticalBarVisible)) @@ -1105,11 +1109,11 @@ public class AdvancedImageBox : UserControl var gridColor = GridColor; var altColor = GridColorAlternate; var currentColor = gridColor; - for (int y = 0; y < ViewPortSize.Height; y += gridCellSize) + for (int y = 0; y < viewPortSize.Height; y += gridCellSize) { var firstRowColor = currentColor; - for (int x = 0; x < ViewPortSize.Width; x += gridCellSize) + for (int x = 0; x < viewPortSize.Width; x += gridCellSize) { context.FillRectangle(currentColor, new Rect(x, y, gridCellSize, gridCellSize)); currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor; @@ -1127,15 +1131,17 @@ public class AdvancedImageBox : UserControl var image = Image; if (image is null) return; + var imageViewPort = GetImageViewPort(); + + // Draw iamge context.DrawImage(image, GetSourceImageRegion(), - GetImageViewPort() + imageViewPort ); var zoomFactor = ZoomFactor; - - + if (HaveTrackerImage && _pointerPosition.X >= 0 && _pointerPosition.Y >= 0) { var destSize = TrackerImageAutoZoom @@ -1155,22 +1161,21 @@ public class AdvancedImageBox : UserControl // Draw pixel grid if (zoomFactor > PixelGridZoomThreshold && SizeMode == SizeModes.Normal) { - var viewport = GetImageViewPort(); var offsetX = Offset.X % zoomFactor; var offsetY = Offset.Y % zoomFactor; Pen pen = new(PixelGridColor); - for (double x = viewport.X + zoomFactor - offsetX; x < viewport.Right; x += zoomFactor) + for (double x = imageViewPort.X + zoomFactor - offsetX; x < imageViewPort.Right; x += zoomFactor) { - context.DrawLine(pen, new Point(x, viewport.X), new Point(x, viewport.Bottom)); + context.DrawLine(pen, new Point(x, imageViewPort.X), new Point(x, imageViewPort.Bottom)); } - for (double y = viewport.Y + zoomFactor - offsetY; y < viewport.Bottom; y += zoomFactor) + for (double y = imageViewPort.Y + zoomFactor - offsetY; y < imageViewPort.Bottom; y += zoomFactor) { - context.DrawLine(pen, new Point(viewport.Y, y), new Point(viewport.Right, y)); + context.DrawLine(pen, new Point(imageViewPort.Y, y), new Point(imageViewPort.Right, y)); } - context.DrawRectangle(pen, viewport); + context.DrawRectangle(pen, imageViewPort); } if (!SelectionRegion.IsEmpty) @@ -1178,7 +1183,7 @@ public class AdvancedImageBox : UserControl var rect = GetOffsetRectangle(SelectionRegion); var selectionColor = SelectionColor; context.FillRectangle(selectionColor, rect); - Color color = Color.FromArgb(255, selectionColor.Color.R, selectionColor.Color.G, selectionColor.Color.B); + var color = Color.FromArgb(255, selectionColor.Color.R, selectionColor.Color.G, selectionColor.Color.B); context.DrawRectangle(new Pen(color.ToUint32()), rect); } } @@ -1248,7 +1253,7 @@ public class AdvancedImageBox : UserControl base.OnScrollChanged(e); }*/ - private void FillContainerOnPointerWheelChanged(object? sender, PointerWheelEventArgs e) + private void ViewPortOnPointerWheelChanged(object? sender, PointerWheelEventArgs e) { e.Handled = true; if (Image is null) return; @@ -1265,9 +1270,8 @@ public class AdvancedImageBox : UserControl } } - protected override void OnPointerPressed(PointerPressedEventArgs e) + private void ViewPortOnPointerPressed(object? sender, PointerPressedEventArgs e) { - base.OnPointerPressed(e); if (e.Handled || _isPanning || _isSelecting @@ -1307,6 +1311,49 @@ public class AdvancedImageBox : UserControl _startMousePosition = location; } + /*protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (e.Handled + || _isPanning + || _isSelecting + || Image is null) return; + + var pointer = e.GetCurrentPoint(this); + + if (SelectionMode != SelectionModes.None) + { + if (!( + pointer.Properties.IsLeftButtonPressed && (SelectWithMouseButtons & MouseButtons.LeftButton) != 0 || + pointer.Properties.IsMiddleButtonPressed && (SelectWithMouseButtons & MouseButtons.MiddleButton) != 0 || + pointer.Properties.IsRightButtonPressed && (SelectWithMouseButtons & MouseButtons.RightButton) != 0 + ) + ) return; + IsSelecting = true; + } + else + { + if (!( + pointer.Properties.IsLeftButtonPressed && (PanWithMouseButtons & MouseButtons.LeftButton) != 0 || + pointer.Properties.IsMiddleButtonPressed && (PanWithMouseButtons & MouseButtons.MiddleButton) != 0 || + pointer.Properties.IsRightButtonPressed && (PanWithMouseButtons & MouseButtons.RightButton) != 0 + ) + || !AutoPan + || SizeMode != SizeModes.Normal + + ) return; + + IsPanning = true; + } + + var location = pointer.Position; + + if (location.X > ViewPortSize.Width) return; + if (location.Y > ViewPortSize.Height) return; + _startMousePosition = location; + }*/ + protected override void OnPointerReleased(PointerReleasedEventArgs e) { base.OnPointerReleased(e); @@ -1316,20 +1363,111 @@ public class AdvancedImageBox : UserControl IsSelecting = false; } - protected override void OnPointerLeave(PointerEventArgs e) + private void ViewPortOnPointerLeave(object? sender, PointerEventArgs e) + { + PointerPosition = new Point(-1, -1); + TriggerRender(true); + e.Handled = true; + } + + /*protected override void OnPointerLeave(PointerEventArgs e) { base.OnPointerLeave(e); PointerPosition = new Point(-1, -1); TriggerRender(true); e.Handled = true; + }*/ + + private void ViewPortOnPointerMoved(object? sender, PointerEventArgs e) + { + if (e.Handled) return; + + var pointer = e.GetCurrentPoint(ViewPort); + PointerPosition = pointer.Position; + + if (!_isPanning && !_isSelecting) + { + TriggerRender(true); + return; + } + + if (_isPanning) + { + double x; + double y; + + if (!InvertMousePan) + { + x = _startScrollPosition.X + (_startMousePosition.X - _pointerPosition.X); + y = _startScrollPosition.Y + (_startMousePosition.Y - _pointerPosition.Y); + } + else + { + x = (_startScrollPosition.X - (_startMousePosition.X - _pointerPosition.X)); + y = (_startScrollPosition.Y - (_startMousePosition.Y - _pointerPosition.Y)); + } + + Offset = new Vector(x, y); + } + else if (_isSelecting) + { + var viewPortPoint = new Point( + Math.Min(_pointerPosition.X, ViewPort.Bounds.Right), + Math.Min(_pointerPosition.Y, ViewPort.Bounds.Bottom)); + + double x; + double y; + double w; + double h; + + var imageOffset = GetImageViewPort().Position; + + if (viewPortPoint.X < _startMousePosition.X) + { + x = viewPortPoint.X; + w = _startMousePosition.X - viewPortPoint.X; + } + else + { + x = _startMousePosition.X; + w = viewPortPoint.X - _startMousePosition.X; + } + + if (viewPortPoint.Y < _startMousePosition.Y) + { + y = viewPortPoint.Y; + h = _startMousePosition.Y - viewPortPoint.Y; + } + else + { + y = _startMousePosition.Y; + h = viewPortPoint.Y - _startMousePosition.Y; + } + + x -= imageOffset.X - Offset.X; + y -= imageOffset.Y - Offset.Y; + + var zoomFactor = ZoomFactor; + x /= zoomFactor; + y /= zoomFactor; + w /= zoomFactor; + h /= zoomFactor; + + if (w > 0 && h > 0) + { + SelectionRegion = FitRectangle(new Rect(x, y, w, h)); + } + } + + e.Handled = true; } - protected override void OnPointerMoved(PointerEventArgs e) + /*protected override void OnPointerMoved(PointerEventArgs e) { base.OnPointerMoved(e); - if (e.Handled) return; + if (e.Handled || !ViewPort.IsPointerOver) return; - var pointer = e.GetCurrentPoint(this); + var pointer = e.GetCurrentPoint(ViewPort); PointerPosition = pointer.Position; if (!_isPanning && !_isSelecting) @@ -1396,14 +1534,15 @@ public class AdvancedImageBox : UserControl w /= zoomFactor; h /= zoomFactor; - if (w != 0 && h != 0) + if (w > 0 && h > 0) { + SelectionRegion = FitRectangle(new Rect(x, y, w, h)); } } e.Handled = true; - } + }*/ #endregion #region Zoom and Size modes @@ -1729,7 +1868,7 @@ public class AdvancedImageBox : UserControl var scaled = GetScaledRectangle(source); var offsetX = viewport.Left - Offset.X; var offsetY = viewport.Top - Offset.Y; - + return new(new Point(scaled.Left + offsetX, scaled.Top + offsetY), scaled.Size); } @@ -2151,7 +2290,8 @@ public class AdvancedImageBox : UserControl /// <returns></returns> public Rect GetImageViewPort() { - if (!IsImageLoaded || (ViewPortSize.Width == 0 && ViewPortSize.Height == 0)) return Rect.Empty; + var viewPortSize = ViewPortSize; + if (!IsImageLoaded || (viewPortSize.Width == 0 && viewPortSize.Height == 0)) return Rect.Empty; double xOffset = 0; double yOffset = 0; @@ -2163,28 +2303,28 @@ public class AdvancedImageBox : UserControl case SizeModes.Normal: if (AutoCenter) { - xOffset = (!IsHorizontalBarVisible ? (ViewPortSize.Width - ScaledImageWidth) / 2 : 0); - yOffset = (!IsVerticalBarVisible ? (ViewPortSize.Height - ScaledImageHeight) / 2 : 0); + xOffset = (!IsHorizontalBarVisible ? (viewPortSize.Width - ScaledImageWidth) / 2 : 0); + yOffset = (!IsVerticalBarVisible ? (viewPortSize.Height - ScaledImageHeight) / 2 : 0); } - width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), ViewPortSize.Width); - height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), ViewPortSize.Height); + width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), viewPortSize.Width); + height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), viewPortSize.Height); break; case SizeModes.Stretch: - width = ViewPortSize.Width; - height = ViewPortSize.Height; + width = viewPortSize.Width; + height = viewPortSize.Height; break; case SizeModes.Fit: var image = Image; - double scaleFactor = Math.Min(ViewPortSize.Width / image!.Size.Width, ViewPortSize.Height / image.Size.Height); + double scaleFactor = Math.Min(viewPortSize.Width / image!.Size.Width, viewPortSize.Height / image.Size.Height); width = Math.Floor(image.Size.Width * scaleFactor); height = Math.Floor(image.Size.Height * scaleFactor); if (AutoCenter) { - xOffset = (ViewPortSize.Width - width) / 2; - yOffset = (ViewPortSize.Height - height) / 2; + xOffset = (viewPortSize.Width - width) / 2; + yOffset = (viewPortSize.Height - height) / 2; } break; diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj index 5a23b57..676fb95 100644 --- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj +++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj @@ -14,7 +14,7 @@ <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> <Description>AvaloniaUI Controls - AdvancedImageBox: Pan, zoom, cursor, pixel grid and selections image box</Description> - <Version>2.0.0</Version> + <Version>2.0.1</Version> <Nullable>enable</Nullable> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <PackageReadmeFile>README.md</PackageReadmeFile> diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index aae8bd6..243b669 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -174,24 +174,18 @@ public static class EmguExtensions /// <param name="mat"></param> /// <returns></returns> public static unsafe byte* GetBytePointer(this Mat mat) - { - return (byte*)mat.DataPointer.ToPointer(); - } + => (byte*)mat.DataPointer.ToPointer(); /// <summary> /// Gets the whole data span to manipulate or read pixels /// </summary> /// <param name="mat"></param> /// <returns></returns> - public static unsafe Span<byte> GetDataByteSpan(this Mat mat) - { - return new(mat.DataPointer.ToPointer(), mat.GetLength()); - } + public static unsafe Span<byte> GetDataByteSpan(this Mat mat) + => new(mat.DataPointer.ToPointer(), mat.GetLength()); - public static unsafe Span<byte> GetDataByteSpan(this Mat mat, int length, int offset = 0) - { - return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length); - } + public static unsafe Span<byte> GetDataByteSpan(this Mat mat, int length, int offset = 0) + => new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length); /// <summary> /// Gets the data span to manipulate or read pixels given a length and offset @@ -201,10 +195,8 @@ public static class EmguExtensions /// <param name="length"></param> /// <param name="offset"></param> /// <returns></returns> - public static unsafe Span<T> GetDataSpan<T>(this Mat mat, int length = 0, int offset = 0) - { - return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length); - } + public static unsafe Span<T> GetDataSpan<T>(this Mat mat, int length = 0, int offset = 0) + => new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length); /// <summary> /// Gets a single pixel span to manipulate or read pixels @@ -214,10 +206,8 @@ public static class EmguExtensions /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> - public static Span<T> GetPixelSpan<T>(this Mat mat, int x, int y) - { - return mat.GetDataSpan<T>(mat.NumberOfChannels, mat.GetPixelPos(x, y)); - } + public static Span<T> GetPixelSpan<T>(this Mat mat, int x, int y) + => mat.GetDataSpan<T>(mat.NumberOfChannels, mat.GetPixelPos(x, y)); /// <summary> /// Gets a single pixel span to manipulate or read pixels @@ -226,10 +216,8 @@ public static class EmguExtensions /// <param name="mat"></param> /// <param name="pos"></param> /// <returns></returns> - public static Span<T> GetPixelSpan<T>(this Mat mat, int pos) - { - return mat.GetDataSpan<T>(mat.NumberOfChannels, pos); - } + public static Span<T> GetPixelSpan<T>(this Mat mat, int pos) + => mat.GetDataSpan<T>(mat.NumberOfChannels, pos); /// <summary> /// Gets a row span to manipulate or read pixels @@ -240,10 +228,8 @@ public static class EmguExtensions /// <param name="length"></param> /// <param name="offset"></param> /// <returns></returns> - public static unsafe Span<T> GetRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0) - { - return new(IntPtr.Add(mat.DataPointer, y * mat.GetRealStep() + offset).ToPointer(), length <= 0 ? mat.GetRealStep() : length); - } + public static unsafe Span<T> GetRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0) + => new(IntPtr.Add(mat.DataPointer, y * mat.GetRealStep() + offset).ToPointer(), length <= 0 ? mat.GetRealStep() : length); /// <summary> /// Gets a col span to manipulate or read pixels @@ -325,7 +311,7 @@ public static class EmguExtensions } /// <summary> - /// Gets the total length of this <see cref="Mat"/></param> + /// Gets the total length of this <see cref="Mat"/> /// </summary> /// <param name="mat"></param> /// <returns>The total length of this <see cref="Mat"/></returns> @@ -471,6 +457,23 @@ public static class EmguExtensions return src.CreateMask(vec); } + public static Mat TrimByBounds(this Mat src) + { + var rect = CvInvoke.BoundingRectangle(src); + if (rect.Size == Size.Empty) return src.New(); + if (src.Size == rect.Size) return src.Clone(); + using var roi = src.Roi(rect); + return roi.Clone(); + } + + public static void TrimByBounds(this Mat src, Mat dst) + { + var mat = src.TrimByBounds(); + CvInvoke.Swap(mat, dst); + src.Dispose(); + } + + #endregion #region Copy methods diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index a90faf2..eea39a5 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -407,6 +407,8 @@ public class CXDLPFile : FileFormat new(typeof(CXDLPFile), "cxdlp", "Creality CXDLP"), }; + public override SpeedUnit FormatSpeedUnit => SpeedUnit.MillimetersPerSecond; + public override PrintParameterModifier[]? PrintParameterModifiers { get; } = { PrintParameterModifier.BottomLayerCount, @@ -577,8 +579,8 @@ public class CXDLPFile : FileFormat public override float BottomLiftSpeed { - get => SpeedConverter.Convert(SlicerInfoSettings.BottomLiftSpeed, SpeedUnit.MillimetersPerSecond, DefaultSpeedUnit); - set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)SpeedConverter.Convert(value, DefaultSpeedUnit, SpeedUnit.MillimetersPerSecond); + get => SpeedConverter.Convert(SlicerInfoSettings.BottomLiftSpeed, FormatSpeedUnit, CoreSpeedUnit); + set => base.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = SlicerInfoSettings.BottomLiftSpeed = (ushort)SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); } public override float LiftHeight @@ -589,16 +591,16 @@ public class CXDLPFile : FileFormat public override float LiftSpeed { - get => SpeedConverter.Convert(SlicerInfoSettings.LiftSpeed, SpeedUnit.MillimetersPerSecond, DefaultSpeedUnit); - set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)SpeedConverter.Convert(value, DefaultSpeedUnit, SpeedUnit.MillimetersPerSecond); + get => SpeedConverter.Convert(SlicerInfoSettings.LiftSpeed, FormatSpeedUnit, CoreSpeedUnit); + set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); } public override float BottomRetractSpeed => RetractSpeed; public override float RetractSpeed { - get => SpeedConverter.Convert(SlicerInfoSettings.RetractSpeed, SpeedUnit.MillimetersPerSecond, DefaultSpeedUnit); - set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)SpeedConverter.Convert(value, DefaultSpeedUnit, SpeedUnit.MillimetersPerSecond); + get => SpeedConverter.Convert(SlicerInfoSettings.RetractSpeed, FormatSpeedUnit, CoreSpeedUnit); + set => base.RetractSpeed = SlicerInfoSettings.RetractSpeed = (ushort)SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); } public override byte BottomLightPWM diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index d651bdc..8f10c8e 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -44,7 +44,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor { #region Constants - public const SpeedUnit DefaultSpeedUnit = SpeedUnit.MillimetersPerMinute; + public const SpeedUnit CoreSpeedUnit = SpeedUnit.MillimetersPerMinute; public const string TemporaryFileAppend = ".tmp"; public const ushort ExtraPrintTime = 300; @@ -994,6 +994,8 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor #region Properties + public FileDecodeType DecodeType { get; private set; } = FileDecodeType.Full; + /// <summary> /// Gets the file format type /// </summary> @@ -1004,8 +1006,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor /// </summary> public abstract FileExtension[] FileExtensions { get; } - public FileDecodeType DecodeType { get; private set; } = FileDecodeType.Full; - + /// <summary> + /// The speed unit used by this file format in his internal data + /// </summary> + public virtual SpeedUnit FormatSpeedUnit => CoreSpeedUnit; + /// <summary> /// Gets the available <see cref="PrintParameterModifier"/> /// </summary> diff --git a/UVtools.Core/FileFormats/JXSFile.cs b/UVtools.Core/FileFormats/JXSFile.cs index bc8758a..098042a 100644 --- a/UVtools.Core/FileFormats/JXSFile.cs +++ b/UVtools.Core/FileFormats/JXSFile.cs @@ -53,11 +53,11 @@ public class JXSFile : FileFormat [DisplayName("nJobName")] public string JobName { get; set; } = string.Empty; [DisplayName("NumBottomLayers")] public ushort BottomLayerCount { get; set; } = DefaultBottomLayerCount; [DisplayName("LiftDistance1")] public float LiftHeight1 { get; set; } = DefaultBottomLiftHeight; - [DisplayName("LiftFeedrate1")] public float LiftSpeed1 { get; set; } = SpeedConverter.Convert(DefaultLiftSpeed, SpeedUnit.MillimetersPerMinute, SpeedUnit.MillimetersPerSecond); + [DisplayName("LiftFeedrate1")] public float LiftSpeed1 { get; set; } = DefaultLiftSpeed; [DisplayName("LiftDistance2")] public float LiftHeight2 { get; set; } = DefaultLiftHeight2; - [DisplayName("LiftFeedrate2")] public float LiftSpeed2 { get; set; } = SpeedConverter.Convert(DefaultLiftSpeed2, SpeedUnit.MillimetersPerMinute, SpeedUnit.MillimetersPerSecond); - [DisplayName("BottomLiftFeedrate")] public float BottomLiftSpeed { get; set; } = SpeedConverter.Convert(DefaultBottomLiftSpeed, SpeedUnit.MillimetersPerMinute, SpeedUnit.MillimetersPerSecond); - [DisplayName("RetractFeedrate")] public float RetractSpeed { get; set; } = SpeedConverter.Convert(DefaultRetractSpeed, SpeedUnit.MillimetersPerMinute, SpeedUnit.MillimetersPerSecond); + [DisplayName("LiftFeedrate2")] public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2; + [DisplayName("BottomLiftFeedrate")] public float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed; + [DisplayName("RetractFeedrate")] public float RetractSpeed { get; set; } = DefaultRetractSpeed; [DisplayName("ZMoveTimeCompensation")] public uint WaitTimeBeforeCure { get; set; } = 1500; [DisplayName("ZMoveTimeCompensationBottom")] public uint BottomWaitTimeBeforeCure { get; set; } = 8000; public string OnLightCode { get; set; } = "M106 S255"; @@ -80,8 +80,8 @@ public class JXSFile : FileFormat #endregion #region Properties - public JXSConfig ConfigFile { get; set; } = new (); - public JXSControl ControlFile { get; set; } = new (); + public JXSConfig ConfigFile { get; set; } = new(); + public JXSControl ControlFile { get; set; } = new(); public override FileFormatType FileType => FileFormatType.Archive; diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 0e656ef..19b1d4d 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -15,6 +15,7 @@ using System.Drawing; using System.IO; using System.Linq; using System.Threading.Tasks; +using UVtools.Core.Converters; using UVtools.Core.Extensions; using UVtools.Core.Layers; using UVtools.Core.Operations; @@ -287,18 +288,18 @@ public class PhotonWorkshopFile : FileFormat /// <summary> /// 58 /// </summary> - [FieldOrder(7)] public float LiftHeight { get; set; } = 6; + [FieldOrder(7)] public float LiftHeight { get; set; } = DefaultLiftHeight; /// <summary> /// Gets the lift speed in mm/s /// 5C /// </summary> - [FieldOrder(8)] public float LiftSpeed { get; set; } = 3; // mm/s + [FieldOrder(8)] public float LiftSpeed { get; set; } = SpeedConverter.Convert(DefaultLiftSpeed, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); // mm/s /// <summary> /// Gets the retract speed in mm/s /// 60 /// </summary> - [FieldOrder(9)] public float RetractSpeed { get; set; } = 3; // mm/s + [FieldOrder(9)] public float RetractSpeed { get; set; } = SpeedConverter.Convert(DefaultRetractSpeed, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); // mm/s /// <summary> /// 64 @@ -557,7 +558,7 @@ public class PhotonWorkshopFile : FileFormat LayerHeight = layer.RelativePositionZ; ExposureTime = layer.ExposureTime; LiftHeight = layer.LiftHeight; - LiftSpeed = (float) Math.Round(layer.LiftSpeed / 60, 2); + LiftSpeed = SpeedConverter.Convert(layer.LiftSpeed, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); NonZeroPixelCount = layer.NonZeroPixelCount; } @@ -566,7 +567,7 @@ public class PhotonWorkshopFile : FileFormat // Don't forget to compute LayerHeight outside here layer.ExposureTime = ExposureTime; layer.LiftHeight = LiftHeight; - layer.LiftSpeed = (float)Math.Round(LiftSpeed * 60, 2); + layer.LiftSpeed = SpeedConverter.Convert(LiftSpeed, SpeedUnit.MillimetersPerSecond, CoreSpeedUnit); } public Mat Decode(bool consumeData = true) @@ -942,18 +943,18 @@ public class PhotonWorkshopFile : FileFormat [FieldOrder(1)] public uint Unknown0 { get; set; } = 24; [FieldOrder(2)] public uint Unknown1 { get; set; } = 2; [FieldOrder(3)] public float BottomLiftHeight1 { get; set; } - [FieldOrder(4)] public float BottomLiftSpeed1 { get; set; } - [FieldOrder(5)] public float BottomRetractSpeed1 { get; set; } + [FieldOrder(4)] public float BottomLiftSpeed1 { get; set; } = SpeedConverter.Convert(DefaultBottomLiftSpeed, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); + [FieldOrder(5)] public float BottomRetractSpeed1 { get; set; } = SpeedConverter.Convert(DefaultBottomRetractSpeed, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); [FieldOrder(6)] public float BottomLiftHeight2 { get; set; } - [FieldOrder(7)] public float BottomLiftSpeed2 { get; set; } - [FieldOrder(8)] public float BottomRetractSpeed2 { get; set; } + [FieldOrder(7)] public float BottomLiftSpeed2 { get; set; } = SpeedConverter.Convert(DefaultBottomLiftSpeed2, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); + [FieldOrder(8)] public float BottomRetractSpeed2 { get; set; } = SpeedConverter.Convert(DefaultBottomRetractSpeed2, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); [FieldOrder(9)] public uint Unknown2 { get; set; } = 2; [FieldOrder(10)] public float LiftHeight1 { get; set; } - [FieldOrder(11)] public float LiftSpeed1 { get; set; } - [FieldOrder(12)] public float RetractSpeed1 { get; set; } + [FieldOrder(11)] public float LiftSpeed1 { get; set; } = SpeedConverter.Convert(DefaultLiftSpeed, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); + [FieldOrder(12)] public float RetractSpeed1 { get; set; } = SpeedConverter.Convert(DefaultRetractSpeed, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); [FieldOrder(13)] public float LiftHeight2 { get; set; } - [FieldOrder(14)] public float LiftSpeed2 { get; set; } - [FieldOrder(15)] public float RetractSpeed2 { get; set; } + [FieldOrder(14)] public float LiftSpeed2 { get; set; } = SpeedConverter.Convert(DefaultLiftSpeed2, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); + [FieldOrder(15)] public float RetractSpeed2 { get; set; } = SpeedConverter.Convert(DefaultRetractSpeed2, CoreSpeedUnit, SpeedUnit.MillimetersPerSecond); public Extra() { @@ -1043,10 +1044,10 @@ public class PhotonWorkshopFile : FileFormat new(typeof(PhotonWorkshopFile), "pwms", "Photon Mono SE (PWMS)"), new(typeof(PhotonWorkshopFile), "pwma", "Photon Mono 4K (PWMA)"), new(typeof(PhotonWorkshopFile), "pmsq", "Photon Mono SQ (PMSQ)"), - - }; + public override SpeedUnit FormatSpeedUnit => SpeedUnit.MillimetersPerSecond; + public override PrintParameterModifier[]? PrintParameterModifiers { get @@ -1297,11 +1298,7 @@ public class PhotonWorkshopFile : FileFormat public override float BottomLiftHeight { - get - { - if (FileMarkSettings.Version >= VERSION_516) return ExtraSettings.BottomLiftHeight1; - return base.BottomLiftHeight; - } + get => FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.BottomLiftHeight1 : base.BottomLiftHeight; set { value = (float)Math.Round(value, 2); @@ -1321,26 +1318,20 @@ public class PhotonWorkshopFile : FileFormat public override float BottomLiftSpeed { - get - { - if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.BottomLiftSpeed1 * 60, 2); - return base.BottomLiftSpeed; - } + get => FileMarkSettings.Version >= VERSION_516 + ? SpeedConverter.Convert(ExtraSettings.BottomLiftSpeed1, FormatSpeedUnit, CoreSpeedUnit) + : base.BottomLiftSpeed; set { value = (float)Math.Round(value, 2); - ExtraSettings.BottomLiftSpeed1 = (float)Math.Round(value / 60, 2); + ExtraSettings.BottomLiftSpeed1 = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); base.BottomLiftSpeed = value; } } public override float LiftHeight { - get - { - if (FileMarkSettings.Version >= VERSION_516) return ExtraSettings.LiftHeight1; - return HeaderSettings.LiftHeight; - } + get => FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.LiftHeight1 : HeaderSettings.LiftHeight; set { value = (float)Math.Round(value, 2); @@ -1354,21 +1345,17 @@ public class PhotonWorkshopFile : FileFormat layer.LiftHeight = base.LiftHeight; } } - else base.LiftHeight = value; + else base.LiftHeight = HeaderSettings.LiftHeight = value; } } public override float LiftSpeed { - get - { - if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.LiftSpeed1 * 60, 2); - return (float)Math.Round(HeaderSettings.LiftSpeed * 60, 2); - } + get => SpeedConverter.Convert(FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.LiftSpeed1 : HeaderSettings.LiftSpeed, FormatSpeedUnit, CoreSpeedUnit); set { value = (float)Math.Round(value, 2); - HeaderSettings.LiftSpeed = ExtraSettings.LiftSpeed1 = (float)Math.Round(value / 60, 2); + HeaderSettings.LiftSpeed = ExtraSettings.LiftSpeed1 = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); base.LiftSpeed = value; } } @@ -1388,12 +1375,12 @@ public class PhotonWorkshopFile : FileFormat public override float BottomLiftSpeed2 { - get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.BottomLiftSpeed2 * 60, 2) : 0; + get => FileMarkSettings.Version >= VERSION_516 ? SpeedConverter.Convert(ExtraSettings.BottomLiftSpeed2, FormatSpeedUnit, CoreSpeedUnit) : 0; set { if (FileMarkSettings.Version < VERSION_516) return; value = (float)Math.Round(value, 2); - ExtraSettings.BottomLiftSpeed2 = (float)Math.Round(value / 60, 2); + ExtraSettings.BottomLiftSpeed2 = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); base.BottomLiftSpeed2 = value; } } @@ -1413,67 +1400,59 @@ public class PhotonWorkshopFile : FileFormat public override float LiftSpeed2 { - get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.LiftSpeed2 * 60, 2) : 0; + get => FileMarkSettings.Version >= VERSION_516 ? SpeedConverter.Convert(ExtraSettings.LiftSpeed2, FormatSpeedUnit, CoreSpeedUnit) : 0; set { if (FileMarkSettings.Version < VERSION_516) return; - base.LiftSpeed2 = ExtraSettings.LiftSpeed2 = (float)Math.Round(value / 60, 2); + value = (float)Math.Round(value, 2); + ExtraSettings.LiftSpeed2 = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); + base.LiftSpeed2 = value; } } public override float BottomRetractSpeed { - get - { - if (FileMarkSettings.Version >= VERSION_516) return (float)Math.Round(ExtraSettings.BottomRetractSpeed1 * 60, 2); - return RetractSpeed; - } + get => FileMarkSettings.Version >= VERSION_516 ? SpeedConverter.Convert(ExtraSettings.BottomRetractSpeed1, FormatSpeedUnit, CoreSpeedUnit) : RetractSpeed; set { value = (float)Math.Round(value, 2); - if (FileMarkSettings.Version >= VERSION_516) - { - ExtraSettings.BottomRetractSpeed1 = (float)Math.Round(value / 60, 2); - } - else - { - RetractSpeed = value; - } + if (FileMarkSettings.Version < VERSION_516) return; + ExtraSettings.BottomRetractSpeed1 = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); } } public override float RetractSpeed { - get => (float)Math.Round(HeaderSettings.RetractSpeed * 60, 2); + get => SpeedConverter.Convert(FileMarkSettings.Version >= VERSION_516 ? ExtraSettings.RetractSpeed1 : HeaderSettings.RetractSpeed, FormatSpeedUnit, CoreSpeedUnit); set { value = (float)Math.Round(value, 2); - ExtraSettings.RetractSpeed1 = HeaderSettings.RetractSpeed = (float) Math.Round(value / 60, 2); + ExtraSettings.RetractSpeed1 = HeaderSettings.RetractSpeed = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); base.RetractSpeed = value; } } public override float BottomRetractSpeed2 { - get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.BottomRetractSpeed2 * 60, 2) : 0; + get => FileMarkSettings.Version >= VERSION_516 ? SpeedConverter.Convert(ExtraSettings.BottomRetractSpeed2, FormatSpeedUnit, CoreSpeedUnit) : 0; set { if (FileMarkSettings.Version < VERSION_516) return; value = (float)Math.Round(value, 2); - ExtraSettings.BottomRetractSpeed2 = (float)Math.Round(value / 60, 2); + ExtraSettings.BottomRetractSpeed2 = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); base.BottomRetractSpeed2 = value; } } public override float RetractSpeed2 { - get => FileMarkSettings.Version >= VERSION_516 ? (float)Math.Round(ExtraSettings.RetractSpeed2 * 60, 2) : 0; + get => FileMarkSettings.Version >= VERSION_516 ? SpeedConverter.Convert(ExtraSettings.RetractSpeed2, FormatSpeedUnit, CoreSpeedUnit) : 0; set { if (FileMarkSettings.Version < VERSION_516) return; value = (float)Math.Round(value, 2); - ExtraSettings.RetractSpeed2 = (float)Math.Round(value / 60, 2); + ExtraSettings.RetractSpeed2 = SpeedConverter.Convert(value, CoreSpeedUnit, FormatSpeedUnit); base.RetractSpeed2 = value; } } diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index c22cee7..a1c51ee 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -461,6 +461,13 @@ public abstract class Operation : BindableBase, IDisposable return HaveROI ? _roi.Size : fallbackSize; } + public Size GetRoiSizeOrVolumeSize() => GetRoiSizeOrVolumeSize(_originalBoundingRectangle.Size); + + public Size GetRoiSizeOrVolumeSize(Size fallbackSize) + { + return HaveROI ? _roi.Size : fallbackSize; + } + public Mat GetRoiOrDefault(Mat defaultMat) { diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs index 5e2e5d6..9085ad3 100644 --- a/UVtools.Core/Operations/OperationInfill.cs +++ b/UVtools.Core/Operations/OperationInfill.cs @@ -10,8 +10,10 @@ using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; using System; +using System.Collections.Generic; using System.ComponentModel; using System.Drawing; +using System.Linq; using System.Threading.Tasks; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; @@ -25,8 +27,8 @@ public sealed class OperationInfill : Operation private InfillAlgorithm _infillType = InfillAlgorithm.CubicCrossAlternating; private ushort _wallThickness = 64; private ushort _infillThickness = 45; - private ushort _infillSpacing = 200; - private ushort _infillBrightness = 255; + private ushort _infillSpacing = 300; + private byte _infillBrightness = 255; private bool _reinforceInfill; #endregion @@ -55,6 +57,12 @@ public sealed class OperationInfill : Operation [Description("Straight pillars (Weak)")] Pillars, + [Description("Concentric (Weak)")] + Concentric, + + [Description("Waves (Medium)")] + Waves, + [Description("Cubic cross: Fixed pilars with crossing sections (Optimal)")] CubicCross, @@ -65,7 +73,10 @@ public sealed class OperationInfill : Operation CubicStar, [Description("Honeycomb (Strong)")] - Honeycomb + Honeycomb, + + [Description("Gyroid (Strong)")] + Gyroid, } #endregion @@ -82,7 +93,7 @@ public sealed class OperationInfill : Operation set => RaiseAndSetIfChanged(ref _wallThickness, value); } - public ushort InfillBrightness + public byte InfillBrightness { get => _infillBrightness; set => RaiseAndSetIfChanged(ref _infillBrightness, value); @@ -149,7 +160,11 @@ public sealed class OperationInfill : Operation Mat? mask = null; if (_infillType == InfillAlgorithm.Honeycomb) { - mask = GetHoneycombMask(GetRoiSizeOrDefault()); + mask = GetHoneycombMask(GetRoiSizeOrVolumeSize()); + } + else if (_infillType == InfillAlgorithm.Concentric) + { + mask = GetConcentricMask(GetRoiSizeOrVolumeSize()); } Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => @@ -176,7 +191,7 @@ public sealed class OperationInfill : Operation Mat? patternMask = null; using Mat erode = new (); using Mat diff = new (); - var target = GetRoiOrDefault(mat); + var target = GetRoiOrVolumeBounds(mat); using var mask = GetMask(mat); bool disposeTargetMask = true; @@ -203,10 +218,6 @@ public sealed class OperationInfill : Operation firstPattern = false; accumulator += _infillThickness; } - else - { - //accumulator += _infillSpacing; - } } if (firstPattern) @@ -319,7 +330,7 @@ public sealed class OperationInfill : Operation CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1, target.Cols / infillPattern.Cols + 1, matPattern); - patternMask = new Mat(matPattern, new Rectangle(0, 0, target.Width, target.Height)); + patternMask = matPattern.Roi(target); disposeTargetMask = true; } else if (_infillType == InfillAlgorithm.Honeycomb) @@ -335,6 +346,124 @@ public sealed class OperationInfill : Operation disposeTargetMask = true; } } + else if (_infillType == InfillAlgorithm.Concentric) + { + if (arguments.Length >= 2) + { + patternMask = (Mat)arguments[1]; + disposeTargetMask = false; + } + else + { + patternMask = GetConcentricMask(target.Size); + disposeTargetMask = true; + } + } + else if (_infillType == InfillAlgorithm.Waves) + { + var sineHeight = 100; + var sineWidth = 100; + var radius = (ushort)(_infillThickness / 2); + + var points = new List<Point>(); + + bool isHorizontal = true; + float accumulator = 0; + for (int i = 0; i <= layerIndex; i++) + { + accumulator += SlicerFile[index].RelativePositionZ; + if (accumulator >= 2) + { + isHorizontal = !isHorizontal; + accumulator = 0; + } + } + + //using var infillPattern = EmguExtensions.InitMat(new Size(_infillSpacing, _infillSpacing)); + //using var matPattern = new Mat(); + using var infillPattern = mat.NewBlank(); + + int maxY = 0; + + if (isHorizontal) + { + for (int x = 0; x < mat.Width; x += radius) + { + int y = (int) (Math.Sin((double) x / sineWidth /*+ sineWidth * layerIndex*/) * sineHeight / 2.0 + + sineHeight / 2.0 + radius); + points.Add(new Point(x, y)); + maxY = Math.Max(maxY, y); + } + var infillPatternRoi = infillPattern.Roi(new Size(infillPattern.Width, maxY + radius + 2 + _infillSpacing)); + CvInvoke.Polylines(infillPatternRoi, points.ToArray(), false, infillColor, _infillThickness); + + CvInvoke.Repeat(infillPatternRoi, target.Rows / infillPatternRoi.Rows + 1, 1, infillPattern); + } + else + { + for (int y = 0; y < mat.Height; y += radius) + { + int x = (int)(Math.Sin((double)y / sineWidth /*+ sineWidth * layerIndex*/) * sineHeight / 2.0 + + sineHeight / 2.0 + radius); + points.Add(new Point(x, y)); + maxY = Math.Max(maxY, x); + } + var infillPatternRoi = infillPattern.Roi(new Size(maxY + radius + 2 + _infillSpacing, infillPattern.Height)); + CvInvoke.Polylines(infillPatternRoi, points.ToArray(), false, infillColor, _infillThickness); + CvInvoke.Repeat(infillPatternRoi, 1, target.Cols / infillPatternRoi.Cols + 1, infillPattern); + + } + points.Clear(); + + patternMask = infillPattern.Roi(target); + disposeTargetMask = true; + } + else if (_infillType == InfillAlgorithm.Gyroid) + { + patternMask = target.NewBlank(); + + var scaleRatio = 0.0012 / (_infillSpacing + _infillThickness / 2); + //var scaleX = 0.04 * _infillSpacing * Math.PI / target.Width; + //var scaleY = 0.04 * _infillSpacing * Math.PI / target.Height; + var scaleX = scaleRatio * mat.Width; + var scaleY = scaleRatio * mat.Height; + + //const double scaleZ = 2.0 * Math.PI; + //var dz = 2.0 * scaleZ / LayerRangeCount; // z step + //var zz = 0.05 * (SlicerFile[index].LayerHeight / 0.05f) * (layerIndex + 1); + float zz = 0; + for (var i = LayerIndexStart; i <= index; i++) + { + zz += SlicerFile[i].RelativePositionZ; + } + + for (int y = 0; y < patternMask.Height; y++) + { + var span = patternMask.GetRowSpan<byte>(y); + var yy = y * scaleY; // y position of pixel + for (int x = 0; x < patternMask.Width; x++) + { + var xx = x * scaleX; // x position of pixel + + var d = Math.Sin(xx) * Math.Cos(yy) // compute gyroid equation + + Math.Sin(yy) * Math.Cos(zz) + + Math.Sin(zz) * Math.Cos(xx); + //if (d > 1e-6) continue; // Far from surface + if (Math.Abs(d) - 0.006*_infillThickness > 0) continue; + //if (d - 0.05 > 1e-6) continue; + + span[x] = _infillBrightness; + } + } + + + //using var contours = patternMask.FindContours(); + //CvInvoke.DrawContours(patternMask, contours, -1, infillColor, _infillThickness); + + disposeTargetMask = true; + } + + //patternMask.Save("D:\\pattern.png"); CvInvoke.Erode(target, erode, kernel, anchor, WallThickness, BorderType.Reflect101, default); @@ -390,5 +519,58 @@ public sealed class OperationInfill : Operation return patternMask; } + public Mat GetConcentricMask(Size targetSize) + { + var patternMask = EmguExtensions.InitMat(targetSize); + + //var halfInfillSpacing = _infillSpacing / 2; + //var halfThickenss = _infillThickness / 2; + int multiplicator = 1; + byte position = 0; + + int x = patternMask.Width / 2; + int y = patternMask.Height / 2; + + Point[] directions = { + new(0, -_infillSpacing), // top + new(_infillSpacing, 0), // right + new(0, _infillSpacing), // bottom + new(-_infillSpacing, 0), // left + }; + + bool[] hitLimits = + { + false, + false, + false, + false + }; + + var points = new List<Point> {new(x, y)}; + + while (hitLimits.Any(hitLimit => !hitLimit)) + { + x += directions[position].X * multiplicator; + y += directions[position].Y * multiplicator; + if (x < 0 || y < 0 || x >= patternMask.Width || y >= patternMask.Height) hitLimits[position] = true; + points.Add(new Point(x, y)); + position++; + if (position == 2) + { + multiplicator++; + } + else if (position == 4) + { + multiplicator++; + position = 0; + } + } + + + CvInvoke.Polylines(patternMask, points.ToArray(), false, new MCvScalar(_infillBrightness), _infillThickness); + + return patternMask; + } + #endregion }
\ No newline at end of file diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index f54b303..c9bc034 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -1,75 +1,85 @@ <Project Sdk="Microsoft.NET.Sdk"> - <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> - <PackageLicenseFile>LICENSE</PackageLicenseFile> - <Company>PTRTECH</Company> - <Authors>Tiago Conceição</Authors> - <RepositoryType>Git</RepositoryType> - <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.2.0</Version> - <Copyright>Copyright © 2020 PTRTECH</Copyright> - <PackageIcon>UVtools.png</PackageIcon> - <Platforms>AnyCPU;x64</Platforms> - <SignAssembly>false</SignAssembly> - <PackageIconUrl /> - <PackageTags>msla, dlp, resin, printer, slicer, 3d printing, image processing, layers</PackageTags> - <ApplicationIcon>UVtools.ico</ApplicationIcon> - <PackageReadmeFile>README.md</PackageReadmeFile> - <Nullable>enable</Nullable> - </PropertyGroup> + <PropertyGroup> + <TargetFramework>net6.0</TargetFramework> + <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> + <PackageLicenseFile>LICENSE</PackageLicenseFile> + <Company>PTRTECH</Company> + <Authors>Tiago Conceição</Authors> + <RepositoryType>Git</RepositoryType> + <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.2.1</Version> + <Copyright>Copyright © 2020 PTRTECH</Copyright> + <PackageIcon>UVtools.png</PackageIcon> + <Platforms>AnyCPU;x64</Platforms> + <SignAssembly>false</SignAssembly> + <PackageIconUrl /> + <PackageTags>msla, dlp, resin, printer, slicer, 3d printing, image processing, layers</PackageTags> + <ApplicationIcon>UVtools.ico</ApplicationIcon> + <PackageReadmeFile>README.md</PackageReadmeFile> + <Nullable>enable</Nullable> + </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - <DocumentationFile></DocumentationFile> - </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <DocumentationFile></DocumentationFile> + </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> - <ItemGroup> - <Content Include="UVtools.ico" /> - </ItemGroup> + <ItemGroup> + <Content Include="UVtools.ico" /> + </ItemGroup> - <ItemGroup> - <None Include="..\LICENSE"> - <Pack>True</Pack> - <PackagePath></PackagePath> - </None> - <None Include="..\README.md"> - <Pack>True</Pack> - <PackagePath>\</PackagePath> - </None> - <None Include="..\UVtools.CAD\UVtools.png"> - <Pack>True</Pack> - <PackagePath></PackagePath> - </None> - </ItemGroup> + <ItemGroup> + <None Include="..\LICENSE"> + <Pack>True</Pack> + <PackagePath></PackagePath> + </None> + <None Include="..\README.md"> + <Pack>True</Pack> + <PackagePath>\</PackagePath> + </None> + <None Include="..\UVtools.CAD\UVtools.png"> + <Pack>True</Pack> + <PackagePath></PackagePath> + </None> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="AnimatedGif" Version="1.0.5" /> + <PackageReference Include="BinarySerializer" Version="8.6.2.2" /> + <PackageReference Include="Emgu.CV" Version="4.5.5.4823" /> + <PackageReference Include="Emgu.CV.runtime.ubuntu.20.04-x64" Version="4.5.4.4788" /> + <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="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.2" /> + </ItemGroup> + + <Target Name="PreparePackageReleaseNotesFromFile" BeforeTargets="GenerateNuspec"> + <ReadLinesFromFile File="../CHANGELOG.md"> + <Output TaskParameter="Lines" ItemName="ReleaseNoteLines" /> + </ReadLinesFromFile> + <PropertyGroup> + <PackageReleaseNotes>@(ReleaseNoteLines, '%0a')</PackageReleaseNotes> + </PropertyGroup> + </Target> - <ItemGroup> - <PackageReference Include="AnimatedGif" Version="1.0.5" /> - <PackageReference Include="BinarySerializer" Version="8.6.2.2" /> - <PackageReference Include="Emgu.CV" Version="4.5.5.4823" /> - <PackageReference Include="Emgu.CV.runtime.ubuntu.20.04-x64" Version="4.5.4.4788" /> - <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="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.2" /> - </ItemGroup> </Project> diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index bd403a9..cc40285 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -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.2.0"?> + <?define SourceDir="..\publish\UVtools_win-x64_v3.2.1"?> <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"> diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index 15c9a9c..95b010c 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -13,2106 +13,2104 @@ MinHeight="600" DragDrop.AllowDrop="True"> - <Grid RowDefinitions="*" ColumnDefinitions="*"> - <DockPanel Grid.Row="0" Grid.Column="0" IsEnabled="{Binding IsGUIEnabled}"> - <Menu DockPanel.Dock="Top"> - <MenuItem Name="MainMenu.File" Header="_File"> - <MenuItem Name="MainMenu.File.Open" - Header="_Open" - HotKey="Ctrl+O" InputGesture="Ctrl+O" - Command="{Binding MenuFileOpenClicked}" - i:MenuItem.Icon="fas fa-file-import"/> - - <MenuItem Name="MainMenu.File.OpenNewWindow" - Header="Open in _new window" - HotKey="Ctrl+Shift+O" InputGesture="Ctrl+Shift+O" - Command="{Binding MenuFileOpenNewWindowClicked}" - i:MenuItem.Icon="fas fa-file-import"/> - - <MenuItem Name="MainMenu.File.OpenRecent" - Header="Open recent" - ToolTip.ShowDelay="2000" - ToolTip.Tip="On a file: + <Grid RowDefinitions="*" ColumnDefinitions="*"> + <DockPanel Grid.Row="0" Grid.Column="0" IsEnabled="{Binding IsGUIEnabled}"> + <Menu DockPanel.Dock="Top"> + <MenuItem Name="MainMenu.File" Header="_File"> + <MenuItem Name="MainMenu.File.Open" + Header="_Open" + HotKey="Ctrl+O" InputGesture="Ctrl+O" + Command="{Binding MenuFileOpenClicked}" + i:MenuItem.Icon="fas fa-file-import"/> + + <MenuItem Name="MainMenu.File.OpenNewWindow" + Header="Open in _new window" + HotKey="Ctrl+Shift+O" InputGesture="Ctrl+Shift+O" + Command="{Binding MenuFileOpenNewWindowClicked}" + i:MenuItem.Icon="fas fa-file-import"/> + + <MenuItem Name="MainMenu.File.OpenRecent" + Header="Open recent" + ToolTip.ShowDelay="2000" + ToolTip.Tip="On a file: 
Shift + Click: Open file in a new window 
Shift + Ctrl + Click: Remove file from list 
Ctrl + Click: Purge non-existing files" - Items="{Binding MenuFileOpenRecentItems}" - i:MenuItem.Icon="fas fa-file-import"/> + Items="{Binding MenuFileOpenRecentItems}" + i:MenuItem.Icon="fas fa-file-import"/> - <MenuItem Name="MainMenu.File.OpenInPartialMode" - Header="Open in partial mode" - Command="{Binding MenuFileOpenInPartialModeClicked}" - ToolTip.Tip="Open a file only to see and/or edit properties. + <MenuItem Name="MainMenu.File.OpenInPartialMode" + Header="Open in partial mode" + Command="{Binding MenuFileOpenInPartialModeClicked}" + ToolTip.Tip="Open a file only to see and/or edit properties. 
Layer images won't be loaded and most tools won't run in this mode." - i:MenuItem.Icon="fas fa-file-import"/> - - <MenuItem Name="MainMenu.File.Reload" - Header="_Reload" - HotKey="Ctrl+F5" InputGesture="Ctrl+F5" - IsEnabled="{Binding IsFileLoaded}" - Command="{Binding ReloadFile}" - i:MenuItem.Icon="mdi-file-restore"/> - - <MenuItem Name="MainMenu.File.Save" - Header="_Save" - HotKey="Ctrl+S" InputGesture="Ctrl+S" - IsEnabled="{Binding CanSave}" - Command="{Binding MenuFileSaveClicked}" - i:MenuItem.Icon="fas fa-save"/> - - <MenuItem Name="MainMenu.File.SaveAs" - Header="Save _as" - HotKey="Ctrl+Shift+S" InputGesture="Ctrl+Shift+S" - IsEnabled="{Binding IsFileLoaded}" - Command="{Binding MenuFileSaveAsClicked}" - i:MenuItem.Icon="fas fa-save"/> - - <MenuItem Name="MainMenu.File.SendTo" - Header="Send to" - IsEnabled="{Binding IsFileLoaded}" - Items="{Binding MenuFileSendToItems}" - i:MenuItem.Icon="fas fa-share-square"/> - - <MenuItem Name="MainMenu.File.Close" - Header="_Close" - HotKey="Ctrl+W" InputGesture="Ctrl+W" - IsEnabled="{Binding IsFileLoaded}" - Command="{Binding OnMenuFileCloseFile}" - i:MenuItem.Icon="fas fa-sign-out-alt"/> - - <Separator/> - - <MenuItem Header="I _printed this file" HotKey="Ctrl+P" InputGesture="Ctrl+P" - IsEnabled="{Binding IsFileLoaded}" - Command="{Binding IPrintedThisFile}" - i:MenuItem.Icon="fas fa-flask"/> - - <MenuItem Name="MainMenu.File.OpenContainingFileFolder" - Header="Open containing fo_lder" - HotKey="Ctrl+Shift+L" InputGesture="Ctrl+Shift+L" - IsEnabled="{Binding IsFileLoaded}" - Command="{Binding MenuFileOpenContainingFolderClicked}" - i:MenuItem.Icon="fas fa-folder-open"/> - - <MenuItem Name="MainMenu.File.Extract" - Header="_Extract file contents" HotKey="Ctrl+E" InputGesture="Ctrl+E" - IsEnabled="{Binding IsFileLoaded}" - Command="{Binding ExtractFile}" - i:MenuItem.Icon="fas fa-box-open"/> - - <MenuItem Name="MainMenu.File.Terminal" - HotKey="Ctrl+Shift+T" InputGesture="Ctrl+Shift+T" - Header="_Terminal" - Command="{Binding OpenTerminal}" - IsEnabled="{Binding IsFileLoaded}" - i:MenuItem.Icon="fas fa-terminal"/> - - - <MenuItem Name="MainMenu.File.Convert" - Header="_Convert to" - IsEnabled="{Binding IsFileLoaded}" - IsVisible="{Binding MenuFileConvertItems, Converter={x:Static ObjectConverters.IsNotNull}}" - Items="{Binding MenuFileConvertItems}" - i:MenuItem.Icon="fas fa-exchange-alt"/> - - <Separator/> - - <MenuItem Name="MainMenu.File.Fullscreen" - Header="_Fullscreen" - InputGesture="F11" HotKey="F11" - Command="{Binding OnMenuFileFullscreen}" - i:MenuItem.Icon="fas fa-window-maximize"/> - - <MenuItem Name="MainMenu.File.Settings" - Header="_Settings" - InputGesture="F12" HotKey="F12" - Command="{Binding MenuFileSettingsClicked}" - i:MenuItem.Icon="fas fa-cog"/> - - <Separator/> - - <MenuItem Name="MainMenu.File.Exit" - Header="_Exit" - InputGesture="Alt+F4" - Command="{Binding Close}" - i:MenuItem.Icon="fas fa-door-open"/> - </MenuItem> - - <MenuItem Header="_Tools" - IsVisible="{Binding IsFileLoaded}" - IsEnabled="{Binding IsFileLoaded}" - Items="{Binding MenuTools}"> - </MenuItem> - - <MenuItem Header="_Calibration" - IsVisible="{Binding IsFileLoaded}" - IsEnabled="{Binding IsFileLoaded}" - Items="{Binding MenuCalibration}"> - </MenuItem> - - - <MenuItem Header="_Help"> - <MenuItem Header="_About" - InputGesture="F1" HotKey="F1" - Command="{Binding MenuHelpAboutClicked}" - i:MenuItem.Icon="fas fa-info-circle"/> - - <MenuItem Header="_Website" - InputGesture="Ctrl + F1" HotKey="Ctrl + F1" - Command="{Binding OpenHomePage}" - i:MenuItem.Icon="fab fa-edge"/> - - <MenuItem Header="Wi_ki & tutorials" - Command="{Binding OpenWebsite}" - CommandParameter="https://github.com/sn4k3/UVtools/wiki" - i:MenuItem.Icon="fab fa-wikipedia-w"/> - - <MenuItem Header="_Facebook group" - Command="{Binding OpenWebsite}" - CommandParameter="https://www.facebook.com/groups/uvtools" - i:MenuItem.Icon="fab fa-facebook"/> - - <MenuItem Header="_Donate" - Command="{Binding OpenDonateWebsite}" - i:MenuItem.Icon="fas fa-donate"/> - - <MenuItem Header="_Sponsor" - Command="{Binding OpenWebsite}" - CommandParameter="https://github.com/sponsors/sn4k3" - i:MenuItem.Icon="fas fa-heart"/> - - <Separator/> - - <MenuItem Header="_Material manager" - HotKey="F10" - InputGesture="F10" - Command="{Binding MenuHelpMaterialManagerClicked}" - i:MenuItem.Icon="fas fa-flask"/> - - <MenuItem Header="_Install profiles into PrusaSlicer" - Command="{Binding MenuHelpInstallProfilesClicked}" - i:MenuItem.Icon="fas fa-list"/> - - <Separator/> - - <MenuItem Header="_Benchmark" - Command="{Binding MenuHelpBenchmarkClicked}" - i:MenuItem.Icon="fas fa-microchip"/> - - <Separator/> - - <MenuItem Header="_Open settings folder" - Command="{Binding MenuHelpOpenSettingsFolderClicked}" - i:MenuItem.Icon="fas fa-folder-open"/> - - <Separator/> - - <MenuItem Header="_Report a issue" - Command="{Binding OpenWebsite}" - CommandParameter="https://github.com/sn4k3/UVtools/issues/new?assignees=sn4k3&labels=&template=bug_report.md&title=%5BBUG%5D+" - i:MenuItem.Icon="fas fa-bug"/> - - <MenuItem Header="Ask a _question" - Command="{Binding OpenWebsite}" - CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/q-a" - i:MenuItem.Icon="fas fa-question"/> - - <MenuItem Header="Suggest an improvement or new features" - Command="{Binding OpenWebsite}" - CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/ideas" - i:MenuItem.Icon="fas fa-lightbulb"/> - - </MenuItem> - - <MenuItem Background="LimeGreen" - IsVisible="{Binding VersionChecker.HaveNewVersion}" - Header="{Binding VersionChecker.VersionAnnouncementText}" - Command="{Binding MenuNewVersionClicked}"> - </MenuItem> - - </Menu> - - <Border Padding="5" DockPanel.Dock="Bottom" IsVisible="{Binding IsFileLoaded}"> - <WrapPanel - Orientation="Horizontal" - VerticalAlignment="Center"> - <TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/> - - <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" - Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/> - - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}" - Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/> - - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" - Text="{Binding SlicerFile.LiftRepresentation, StringFormat=Lift: {0}}"/> - - - <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}" - Text="{Binding SlicerFile.RetractRepresentation, StringFormat=Retract: {0}}"/> - - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}" - Text="{Binding SlicerFile.LightOffDelayRepresentation, StringFormat=Light-off: {0}}"/> - - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyWaitTime}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyWaitTime}" - ToolTip.Tip="Wait time: Before cure / After cure / After lift" - Text="{Binding SlicerFile.WaitTimeRepresentation, StringFormat=Wait time: {0}}"/> - - <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" - Text="{Binding SlicerFile.PrintTimeString, StringFormat=Print time: \{0\}}"/> - - - <TextBlock IsVisible="{Binding SlicerFile.MaterialMilliliters}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.MaterialMilliliters}" - Text="{Binding SlicerFile.MaterialMilliliters, StringFormat=Used material: \{0\}ml}"/> - - - <TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" Text="{Binding SlicerFile.MaterialCost, StringFormat=Material cost: \{0\}€}"/> - - - <TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" - Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" - Text="{Binding SlicerFile.MaterialName, StringFormat=Material: \{0\}}"/> - - - <TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" - Text=" | "/> - <TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" - Text="{Binding SlicerFile.MachineName, StringFormat=Machine: \{0\}}"/> - </WrapPanel> - </Border> - - <TabControl - DockPanel.Dock="Left" - Width="400" - SelectedItem="{Binding SelectedTabItem}"> - <TabControl.Styles> - <Style Selector="TabItem"> - <Setter Property="FontSize" Value="32"/> - </Style> - </TabControl.Styles> - <TabItem - Name="TabInformation" - ToolTip.Tip="Information" - IsEnabled="{Binding IsFileLoaded}"> - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> - <i:Icon Value="fas fa-info-circle"/> - <!--<TextBlock Margin="5,0,0,0">Information</TextBlock>!--> - </StackPanel> - </TabItem.Header> - - <Grid ColumnDefinitions="*" IsVisible="{Binding IsFileLoaded}"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto" MaxHeight="400"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="*" MinHeight="200"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto" MinHeight="220"/> - </Grid.RowDefinitions> - <!-- Thumbnails --> - <StackPanel Grid.Row="0" - IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}" + i:MenuItem.Icon="fas fa-file-import"/> + + <MenuItem Name="MainMenu.File.Reload" + Header="_Reload" + HotKey="Ctrl+F5" InputGesture="Ctrl+F5" + IsEnabled="{Binding IsFileLoaded}" + Command="{Binding ReloadFile}" + i:MenuItem.Icon="mdi-file-restore"/> + + <MenuItem Name="MainMenu.File.Save" + Header="_Save" + HotKey="Ctrl+S" InputGesture="Ctrl+S" + IsEnabled="{Binding CanSave}" + Command="{Binding MenuFileSaveClicked}" + i:MenuItem.Icon="fas fa-save"/> + + <MenuItem Name="MainMenu.File.SaveAs" + Header="Save _as" + HotKey="Ctrl+Shift+S" InputGesture="Ctrl+Shift+S" + IsEnabled="{Binding IsFileLoaded}" + Command="{Binding MenuFileSaveAsClicked}" + i:MenuItem.Icon="fas fa-save"/> + + <MenuItem Name="MainMenu.File.SendTo" + Header="Send to" + IsEnabled="{Binding IsFileLoaded}" + Items="{Binding MenuFileSendToItems}" + i:MenuItem.Icon="fas fa-share-square"/> + + <MenuItem Name="MainMenu.File.Close" + Header="_Close" + HotKey="Ctrl+W" InputGesture="Ctrl+W" + IsEnabled="{Binding IsFileLoaded}" + Command="{Binding OnMenuFileCloseFile}" + i:MenuItem.Icon="fas fa-sign-out-alt"/> + + <Separator/> + + <MenuItem Header="I _printed this file" HotKey="Ctrl+P" InputGesture="Ctrl+P" + IsEnabled="{Binding IsFileLoaded}" + Command="{Binding IPrintedThisFile}" + i:MenuItem.Icon="fas fa-flask"/> + + <MenuItem Name="MainMenu.File.OpenContainingFileFolder" + Header="Open containing fo_lder" + HotKey="Ctrl+Shift+L" InputGesture="Ctrl+Shift+L" + IsEnabled="{Binding IsFileLoaded}" + Command="{Binding MenuFileOpenContainingFolderClicked}" + i:MenuItem.Icon="fas fa-folder-open"/> + + <MenuItem Name="MainMenu.File.Extract" + Header="_Extract file contents" HotKey="Ctrl+E" InputGesture="Ctrl+E" + IsEnabled="{Binding IsFileLoaded}" + Command="{Binding ExtractFile}" + i:MenuItem.Icon="fas fa-box-open"/> + + <MenuItem Name="MainMenu.File.Terminal" + HotKey="Ctrl+Shift+T" InputGesture="Ctrl+Shift+T" + Header="_Terminal" + Command="{Binding OpenTerminal}" + IsEnabled="{Binding IsFileLoaded}" + i:MenuItem.Icon="fas fa-terminal"/> + + + <MenuItem Name="MainMenu.File.Convert" + Header="_Convert to" + IsEnabled="{Binding IsFileLoaded}" + IsVisible="{Binding MenuFileConvertItems, Converter={x:Static ObjectConverters.IsNotNull}}" + Items="{Binding MenuFileConvertItems}" + i:MenuItem.Icon="fas fa-exchange-alt"/> + + <Separator/> + + <MenuItem Name="MainMenu.File.Fullscreen" + Header="_Fullscreen" + InputGesture="F11" HotKey="F11" + Command="{Binding OnMenuFileFullscreen}" + i:MenuItem.Icon="fas fa-window-maximize"/> + + <MenuItem Name="MainMenu.File.Settings" + Header="_Settings" + InputGesture="F12" HotKey="F12" + Command="{Binding MenuFileSettingsClicked}" + i:MenuItem.Icon="fas fa-cog"/> + + <Separator/> + + <MenuItem Name="MainMenu.File.Exit" + Header="_Exit" + InputGesture="Alt+F4" + Command="{Binding Close}" + i:MenuItem.Icon="fas fa-door-open"/> + </MenuItem> + + <MenuItem Header="_Tools" + IsVisible="{Binding IsFileLoaded}" + IsEnabled="{Binding IsFileLoaded}" + Items="{Binding MenuTools}"> + </MenuItem> + + <MenuItem Header="_Calibration" + IsVisible="{Binding IsFileLoaded}" + IsEnabled="{Binding IsFileLoaded}" + Items="{Binding MenuCalibration}"> + </MenuItem> + + + <MenuItem Header="_Help"> + <MenuItem Header="_About" + InputGesture="F1" HotKey="F1" + Command="{Binding MenuHelpAboutClicked}" + i:MenuItem.Icon="fas fa-info-circle"/> + + <MenuItem Header="_Website" + InputGesture="Ctrl + F1" HotKey="Ctrl + F1" + Command="{Binding OpenHomePage}" + i:MenuItem.Icon="fab fa-edge"/> + + <MenuItem Header="Wi_ki & tutorials" + Command="{Binding OpenWebsite}" + CommandParameter="https://github.com/sn4k3/UVtools/wiki" + i:MenuItem.Icon="fab fa-wikipedia-w"/> + + <MenuItem Header="_Facebook group" + Command="{Binding OpenWebsite}" + CommandParameter="https://www.facebook.com/groups/uvtools" + i:MenuItem.Icon="fab fa-facebook"/> + + <MenuItem Header="_Donate" + Command="{Binding OpenDonateWebsite}" + i:MenuItem.Icon="fas fa-donate"/> + + <MenuItem Header="_Sponsor" + Command="{Binding OpenWebsite}" + CommandParameter="https://github.com/sponsors/sn4k3" + i:MenuItem.Icon="fas fa-heart"/> + + <Separator/> + + <MenuItem Header="_Material manager" + HotKey="F10" + InputGesture="F10" + Command="{Binding MenuHelpMaterialManagerClicked}" + i:MenuItem.Icon="fas fa-flask"/> + + <MenuItem Header="_Install profiles into PrusaSlicer" + Command="{Binding MenuHelpInstallProfilesClicked}" + i:MenuItem.Icon="fas fa-list"/> + + <Separator/> + + <MenuItem Header="_Benchmark" + Command="{Binding MenuHelpBenchmarkClicked}" + i:MenuItem.Icon="fas fa-microchip"/> + + <Separator/> + + <MenuItem Header="_Open settings folder" + Command="{Binding MenuHelpOpenSettingsFolderClicked}" + i:MenuItem.Icon="fas fa-folder-open"/> + + <Separator/> + + <MenuItem Header="_Report a issue" + Command="{Binding OpenWebsite}" + CommandParameter="https://github.com/sn4k3/UVtools/issues/new?assignees=sn4k3&labels=&template=bug_report.md&title=%5BBUG%5D+" + i:MenuItem.Icon="fas fa-bug"/> + + <MenuItem Header="Ask a _question" + Command="{Binding OpenWebsite}" + CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/q-a" + i:MenuItem.Icon="fas fa-question"/> + + <MenuItem Header="Suggest an improvement or new features" + Command="{Binding OpenWebsite}" + CommandParameter="https://github.com/sn4k3/UVtools/discussions/categories/ideas" + i:MenuItem.Icon="fas fa-lightbulb"/> + + </MenuItem> + + <MenuItem Background="LimeGreen" + IsVisible="{Binding VersionChecker.HaveNewVersion}" + Header="{Binding VersionChecker.VersionAnnouncementText}" + Command="{Binding MenuNewVersionClicked}"> + </MenuItem> + + </Menu> + + <Border Padding="5" DockPanel.Dock="Bottom" IsVisible="{Binding IsFileLoaded}"> + <WrapPanel + Orientation="Horizontal" + VerticalAlignment="Center"> + <TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/> + + <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" + Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/> + + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}" + Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/> + + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" + Text="{Binding SlicerFile.LiftRepresentation, StringFormat=Lift: {0}}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}" + Text="{Binding SlicerFile.RetractRepresentation, StringFormat=Retract: {0}}"/> + + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}" + Text="{Binding SlicerFile.LightOffDelayRepresentation, StringFormat=Light-off: {0}}"/> + + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyWaitTime}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyWaitTime}" + ToolTip.Tip="Wait time: Before cure / After cure / After lift" + Text="{Binding SlicerFile.WaitTimeRepresentation, StringFormat=Wait time: {0}}"/> + + <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" + Text="{Binding SlicerFile.PrintTimeString, StringFormat=Print time: \{0\}}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.MaterialMilliliters}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.MaterialMilliliters}" + Text="{Binding SlicerFile.MaterialMilliliters, StringFormat=Used material: \{0\}ml}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" Text="{Binding SlicerFile.MaterialCost, StringFormat=Material cost: \{0\}€}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text="{Binding SlicerFile.MaterialName, StringFormat=Material: \{0\}}"/> + + + <TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text=" | "/> + <TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" + Text="{Binding SlicerFile.MachineName, StringFormat=Machine: \{0\}}"/> + </WrapPanel> + </Border> + + <TabControl + DockPanel.Dock="Left" + Width="400" + SelectedItem="{Binding SelectedTabItem}"> + <TabControl.Styles> + <Style Selector="TabItem"> + <Setter Property="FontSize" Value="32"/> + </Style> + </TabControl.Styles> + <TabItem + Name="TabInformation" + ToolTip.Tip="Information" + IsEnabled="{Binding IsFileLoaded}"> + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> + <i:Icon Value="fas fa-info-circle"/> + <!--<TextBlock Margin="5,0,0,0">Information</TextBlock>!--> + </StackPanel> + </TabItem.Header> + + <Grid ColumnDefinitions="*" IsVisible="{Binding IsFileLoaded}"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto" MaxHeight="400"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="*" MinHeight="200"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto" MinHeight="220"/> + </Grid.RowDefinitions> + <!-- Thumbnails --> + <StackPanel Grid.Row="0" + IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}" + Orientation="Horizontal" + Spacing="5" + VerticalAlignment="Center"> + <Button IsEnabled="{Binding ThumbnailCanGoPrevious}" + i:Attached.Icon="fas fa-caret-left" + Command="{Binding ThumbnailGoPrevious}"/> + + <TextBlock VerticalAlignment="Center"> + <TextBlock.Text> + <MultiBinding StringFormat="{}{0}/{1}"> + <Binding Path="VisibleThumbnailIndex"/> + <Binding Path="SlicerFile.CreatedThumbnailsCount"/> + </MultiBinding> + </TextBlock.Text> + </TextBlock> + + <Button IsEnabled="{Binding ThumbnailCanGoNext}" + i:Attached.Icon="fas fa-caret-right" + Command="{Binding ThumbnailGoNext}"/> + + </StackPanel> + + <StackPanel Grid.Row="0" + IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}" + Orientation="Horizontal" + Spacing="2" + HorizontalAlignment="Right" + VerticalAlignment="Center"> + <TextBlock VerticalAlignment="Center" + Text="{Binding VisibleThumbnailResolution}"/> + + <Button IsEnabled="{Binding VisibleThumbnailIndex}" + ToolTip.Tip="Replace the current preview image" + i:Attached.Icon="fas fa-file-image" + Command="{Binding OnClickThumbnailImport}"/> + + <Button IsEnabled="{Binding VisibleThumbnailIndex}" + ToolTip.Tip="Save thumbnail image to a file" + i:Attached.Icon="fas fa-save" + Command="{Binding OnClickThumbnailSave}"/> + + </StackPanel> + + <!-- Preview image --> + <Image Grid.Row="1" + IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}" + Stretch="Uniform" + Source="{Binding VisibleThumbnailImage}"/> + + <GridSplitter Grid.Row="2" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" Height="5"/> + + <!-- Properties --> + <StackPanel + IsVisible="{Binding SlicerProperties.Count}" + Grid.Row="3" Orientation="Horizontal" Spacing="5" VerticalAlignment="Center"> - <Button IsEnabled="{Binding ThumbnailCanGoPrevious}" - i:Attached.Icon="fas fa-caret-left" - Command="{Binding ThumbnailGoPrevious}"/> - - <TextBlock VerticalAlignment="Center"> - <TextBlock.Text> - <MultiBinding StringFormat="{}{0}/{1}"> - <Binding Path="VisibleThumbnailIndex"/> - <Binding Path="SlicerFile.CreatedThumbnailsCount"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> - - <Button IsEnabled="{Binding ThumbnailCanGoNext}" - i:Attached.Icon="fas fa-caret-right" - Command="{Binding ThumbnailGoNext}"/> - - </StackPanel> - - <StackPanel Grid.Row="0" - IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}" + + <TextBlock + VerticalAlignment="Center" + Text="{Binding SlicerProperties.Count, StringFormat=Properties: \{0\}}"/> + + <TextBlock VerticalAlignment="Center" Text="|"/> + + <TextBlock + VerticalAlignment="Center" + Text="{Binding SlicerFile.Configs.Length, StringFormat=Groups: \{0\}}"/> + + </StackPanel> + + <StackPanel + IsVisible="{Binding SlicerProperties.Count}" + Grid.Row="3" Orientation="Horizontal" Spacing="2" HorizontalAlignment="Right" VerticalAlignment="Center"> - <TextBlock VerticalAlignment="Center" - Text="{Binding VisibleThumbnailResolution}"/> - - <Button IsEnabled="{Binding VisibleThumbnailIndex}" - ToolTip.Tip="Replace the current preview image" - i:Attached.Icon="fas fa-file-image" - Command="{Binding OnClickThumbnailImport}"/> - - <Button IsEnabled="{Binding VisibleThumbnailIndex}" - ToolTip.Tip="Save thumbnail image to a file" - i:Attached.Icon="fas fa-save" - Command="{Binding OnClickThumbnailSave}"/> - - </StackPanel> - - <!-- Preview image --> - <Image Grid.Row="1" - IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}" - Stretch="Uniform" - Source="{Binding VisibleThumbnailImage}"/> - - <GridSplitter Grid.Row="2" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" Height="5"/> - - <!-- Properties --> - <StackPanel - IsVisible="{Binding SlicerProperties.Count}" - Grid.Row="3" - Orientation="Horizontal" - Spacing="5" - VerticalAlignment="Center"> - - <TextBlock - VerticalAlignment="Center" - Text="{Binding SlicerProperties.Count, StringFormat=Properties: \{0\}}"/> - <TextBlock VerticalAlignment="Center" Text="|"/> - - <TextBlock - VerticalAlignment="Center" - Text="{Binding SlicerFile.Configs.Length, StringFormat=Groups: \{0\}}"/> - - </StackPanel> - - <StackPanel - IsVisible="{Binding SlicerProperties.Count}" - Grid.Row="3" - Orientation="Horizontal" - Spacing="2" - HorizontalAlignment="Right" - VerticalAlignment="Center"> - - <uc:ButtonWithIcon - Name="PropertiesSaveButton" - IsEnabled="{Binding SlicerFile.CreatedThumbnailsCount}" - ToolTip.Tip="Save properties to a file or clipboard" - Icon="fas fa-save" - Spacing="3" - Text="⮟" - Command="{Binding OpenContextMenu}" - CommandParameter="PropertiesSave"> - <uc:ButtonWithIcon.ContextMenu> - <ContextMenu Name="PropertiesSaveContextMenu" PlacementMode="Bottom"> - <MenuItem - Command="{Binding OnClickPropertiesSaveFile}" - Header="To File" - i:MenuItem.Icon="far fa-save"/> - - <MenuItem - Command="{Binding OnClickPropertiesSaveClipboard}" - Header="To Clipboard" - i:MenuItem.Icon="far fa-clipboard"/> - - </ContextMenu> - </uc:ButtonWithIcon.ContextMenu> - </uc:ButtonWithIcon> - - </StackPanel> - - <DataGrid IsVisible="{Binding SlicerProperties.Count}" - Name="PropertiesGrid" - Grid.Row="4" - CanUserReorderColumns="True" - CanUserResizeColumns="True" - CanUserSortColumns="True" - GridLinesVisibility="Horizontal" - IsReadOnly="True" - SelectionMode="Extended" - ClipboardCopyMode="IncludeHeader" - Items="{Binding SlicerProperties}"> - <DataGrid.Columns> - <DataGridTextColumn Header="Name" - Binding="{Binding Name}" - Width="Auto" /> - <DataGridTextColumn Header="Value" - Binding="{Binding Value}" - Width="Auto" /> - <DataGridTextColumn Header="Group" - Binding="{Binding Group}" - Width="Auto" /> - </DataGrid.Columns> - - </DataGrid> - - <GridSplitter Grid.Row="5" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext"/> - - <TextBlock Grid.Row="5" - Text="Layer data" - ToolTip.Tip="Shows the properties for the current selected layer" - FontWeight="Bold" - TextAlignment="Center"/> - - <!-- Layer data --> - - <DataGrid Grid.Row="6" - IsVisible="{Binding IsFileLoaded}" - Name="CurrentLayerGrid" - CanUserReorderColumns="False" - CanUserResizeColumns="False" - CanUserSortColumns="False" - GridLinesVisibility="Horizontal" - IsReadOnly="True" - ClipboardCopyMode="IncludeHeader" - Items="{Binding CurrentLayerProperties}"> - <DataGrid.Columns> - <DataGridTextColumn Header="Name" - Binding="{Binding Description}" - Width="Auto" /> - <DataGridTextColumn Header="Value" - Binding="{Binding Value}" - Width="Auto" /> - </DataGrid.Columns> - - </DataGrid> - - - - </Grid> - - </TabItem> - - <TabItem - Name="TabGCode" - ToolTip.Tip="GCode" - IsVisible="False" - IsEnabled="{Binding HaveGCode}"> - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> - <i:Icon Value="fas fa-code"/> - <!--<TextBlock + <uc:ButtonWithIcon + Name="PropertiesSaveButton" + IsEnabled="{Binding SlicerFile.CreatedThumbnailsCount}" + ToolTip.Tip="Save properties to a file or clipboard" + Icon="fas fa-save" + Spacing="3" + Text="⮟" + Command="{Binding OpenContextMenu}" + CommandParameter="PropertiesSave"> + <uc:ButtonWithIcon.ContextMenu> + <ContextMenu Name="PropertiesSaveContextMenu" PlacementMode="Bottom"> + <MenuItem + Command="{Binding OnClickPropertiesSaveFile}" + Header="To File" + i:MenuItem.Icon="far fa-save"/> + + <MenuItem + Command="{Binding OnClickPropertiesSaveClipboard}" + Header="To Clipboard" + i:MenuItem.Icon="far fa-clipboard"/> + + </ContextMenu> + </uc:ButtonWithIcon.ContextMenu> + </uc:ButtonWithIcon> + + </StackPanel> + + <DataGrid IsVisible="{Binding SlicerProperties.Count}" + Name="PropertiesGrid" + Grid.Row="4" + CanUserReorderColumns="True" + CanUserResizeColumns="True" + CanUserSortColumns="True" + GridLinesVisibility="Horizontal" + IsReadOnly="True" + SelectionMode="Extended" + ClipboardCopyMode="IncludeHeader" + Items="{Binding SlicerProperties}"> + <DataGrid.Columns> + <DataGridTextColumn Header="Name" + Binding="{Binding Name}" + Width="Auto" /> + <DataGridTextColumn Header="Value" + Binding="{Binding Value}" + Width="Auto" /> + <DataGridTextColumn Header="Group" + Binding="{Binding Group}" + Width="Auto" /> + </DataGrid.Columns> + + </DataGrid> + + <GridSplitter Grid.Row="5" ResizeDirection="Rows" ResizeBehavior="PreviousAndNext"/> + + <TextBlock Grid.Row="5" + Text="Layer data" + ToolTip.Tip="Shows the properties for the current selected layer" + FontWeight="Bold" + TextAlignment="Center"/> + + <!-- Layer data --> + + <DataGrid Grid.Row="6" + IsVisible="{Binding IsFileLoaded}" + Name="CurrentLayerGrid" + CanUserReorderColumns="False" + CanUserResizeColumns="False" + CanUserSortColumns="False" + GridLinesVisibility="Horizontal" + IsReadOnly="True" + ClipboardCopyMode="IncludeHeader" + Items="{Binding CurrentLayerProperties}"> + <DataGrid.Columns> + <DataGridTextColumn Header="Name" + Binding="{Binding Description}" + Width="Auto" /> + <DataGridTextColumn Header="Value" + Binding="{Binding Value}" + Width="Auto" /> + </DataGrid.Columns> + + </DataGrid> + + + + </Grid> + + </TabItem> + + <TabItem + Name="TabGCode" + ToolTip.Tip="GCode" + IsVisible="False" + IsEnabled="{Binding HaveGCode}"> + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> + <i:Icon Value="fas fa-code"/> + <!--<TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" VerticalAlignment="Center" Margin="5,0,0,0">Gcode</TextBlock>!--> - </StackPanel> - </TabItem.Header> - - <Grid RowDefinitions="Auto,*"> - <StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="5"> - <TextBlock - Text="{Binding GCodeLines, StringFormat=Lines: \{0\}}" - VerticalAlignment="Center"/> - - <TextBlock Text="|" VerticalAlignment="Center"/> - - <TextBlock Text="{Binding #GCodeText.Text.Length, StringFormat=Chars: \{0\}}" - VerticalAlignment="Center"/> - </StackPanel> - - <StackPanel Grid.Row="0" - Orientation="Horizontal" - Spacing="2" - HorizontalAlignment="Right" - VerticalAlignment="Center"> - - <ToggleButton - IsChecked="{Binding SlicerFile.SuppressRebuildGCode}" - ToolTip.Tip="Enable this to directly edit and use custom gcode. + </StackPanel> + </TabItem.Header> + + <Grid RowDefinitions="Auto,*"> + <StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="5"> + <TextBlock + Text="{Binding GCodeLines, StringFormat=Lines: \{0\}}" + VerticalAlignment="Center"/> + + <TextBlock Text="|" VerticalAlignment="Center"/> + + <TextBlock Text="{Binding #GCodeText.Text.Length, StringFormat=Chars: \{0\}}" + VerticalAlignment="Center"/> + </StackPanel> + + <StackPanel Grid.Row="0" + Orientation="Horizontal" + Spacing="2" + HorizontalAlignment="Right" + VerticalAlignment="Center"> + + <ToggleButton + IsChecked="{Binding SlicerFile.SuppressRebuildGCode}" + ToolTip.Tip="Enable this to directly edit and use custom gcode. 
While this is active, UVtools won't update/generate the gcode, meaning any future change won't be replicated to gcode, unless you press the 'Refresh' button. 
To save the file with your custom gcode this setting must remain active while saving the file or else it will be re-generated. 
Use with caution and only if you know what you are doing!" - i:Attached.Icon="far fa-edit"/> - - <Button - ToolTip.Tip="Rebuild GCode with current settings" - i:Attached.Icon="fas fa-sync-alt" - Command="{Binding OnClickRebuildGcode}"/> - - <uc:ButtonWithIcon Name="GcodeSaveButton" - ToolTip.Tip="Save gcode to a file or clipboard" - Icon="fas fa-save" - Spacing="3" - Text="⮟" - Command="{Binding OpenContextMenu}" - CommandParameter="GcodeSave"> - <uc:ButtonWithIcon.ContextMenu> - <ContextMenu Name="GcodeSaveContextMenu" PlacementMode="Bottom"> - <MenuItem - Command="{Binding OnClickGCodeSaveFile}" - Header="To File" - i:MenuItem.Icon="far fa-save"/> - - <MenuItem - Command="{Binding OnClickGCodeSaveClipboard}" - Header="To Clipboard" - i:MenuItem.Icon="far fa-clipboard"/> - - </ContextMenu> - </uc:ButtonWithIcon.ContextMenu> - </uc:ButtonWithIcon> - - </StackPanel> - - <TextBox - Name="GCodeText" - Grid.Row="1" - IsReadOnly="{Binding !SlicerFile.SuppressRebuildGCode}" - AcceptsReturn="True" - Text="{Binding SlicerFile.GCodeStr}" /> - - </Grid> - - </TabItem> - - <!-- + i:Attached.Icon="far fa-edit"/> + + <Button + ToolTip.Tip="Rebuild GCode with current settings" + i:Attached.Icon="fas fa-sync-alt" + Command="{Binding OnClickRebuildGcode}"/> + + <uc:ButtonWithIcon Name="GcodeSaveButton" + ToolTip.Tip="Save gcode to a file or clipboard" + Icon="fas fa-save" + Spacing="3" + Text="⮟" + Command="{Binding OpenContextMenu}" + CommandParameter="GcodeSave"> + <uc:ButtonWithIcon.ContextMenu> + <ContextMenu Name="GcodeSaveContextMenu" PlacementMode="Bottom"> + <MenuItem + Command="{Binding OnClickGCodeSaveFile}" + Header="To File" + i:MenuItem.Icon="far fa-save"/> + + <MenuItem + Command="{Binding OnClickGCodeSaveClipboard}" + Header="To Clipboard" + i:MenuItem.Icon="far fa-clipboard"/> + + </ContextMenu> + </uc:ButtonWithIcon.ContextMenu> + </uc:ButtonWithIcon> + + </StackPanel> + + <TextBox + Name="GCodeText" + Grid.Row="1" + IsReadOnly="{Binding !SlicerFile.SuppressRebuildGCode}" + AcceptsReturn="True" + Text="{Binding SlicerFile.GCodeStr}" /> + + </Grid> + + </TabItem> + + <!-- Issues Tab --> - <TabItem - Name="TabIssues" - ToolTip.Tip="Issues" - IsEnabled="{Binding IsFileLoaded}" > - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> - <i:Icon Value="fas fa-radiation-alt"/> - <!--<TextBlock Margin="5,0,0,0">Issues</TextBlock>!--> - </StackPanel> - </TabItem.Header> - - <Grid - RowDefinitions="Auto,*"> - <StackPanel - IsEnabled="{Binding IsFileLoaded}" - Grid.Row="0" - Orientation="Horizontal" - Spacing="2" - VerticalAlignment="Center"> - <RepeatButton VerticalAlignment="Stretch" - HotKey="Ctrl + Shift + Down" - ToolTip.Tip="Go to the previous issue [Ctrl + Shift + Down]" - IsEnabled="{Binding IssueCanGoPrevious}" - Command="{Binding IssueGoPrevious}" - Interval="100" - i:Attached.Icon="fas fa-caret-left"/> - - <TextBlock VerticalAlignment="Center"> - <TextBlock.Text> - <MultiBinding StringFormat="{}{0}/{1}"> - <Binding Path="IssueSelectedIndexStr"/> - <Binding Path="SlicerFile.IssueManager.Count"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> - - <RepeatButton VerticalAlignment="Stretch" - HotKey="Ctrl + Shift + Up" - ToolTip.Tip="Go to the next issue [Ctrl + Shift + Up]" - IsEnabled="{Binding IssueCanGoNext}" - Command="{Binding IssueGoNext}" - Interval="100" - i:Attached.Icon="fas fa-caret-right"/> - - <Button VerticalAlignment="Stretch" - ToolTip.Tip="Hides and ignores the selected issues, they won't be re-detected. + <TabItem + Name="TabIssues" + ToolTip.Tip="Issues" + IsEnabled="{Binding IsFileLoaded}" > + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> + <i:Icon Value="fas fa-radiation-alt"/> + <!--<TextBlock Margin="5,0,0,0">Issues</TextBlock>!--> + </StackPanel> + </TabItem.Header> + + <Grid + RowDefinitions="Auto,*"> + <StackPanel + IsEnabled="{Binding IsFileLoaded}" + Grid.Row="0" + Orientation="Horizontal" + Spacing="2" + VerticalAlignment="Center"> + <RepeatButton VerticalAlignment="Stretch" + HotKey="Ctrl + Shift + Down" + ToolTip.Tip="Go to the previous issue [Ctrl + Shift + Down]" + IsEnabled="{Binding IssueCanGoPrevious}" + Command="{Binding IssueGoPrevious}" + Interval="100" + i:Attached.Icon="fas fa-caret-left"/> + + <TextBlock VerticalAlignment="Center"> + <TextBlock.Text> + <MultiBinding StringFormat="{}{0}/{1}"> + <Binding Path="IssueSelectedIndexStr"/> + <Binding Path="SlicerFile.IssueManager.Count"/> + </MultiBinding> + </TextBlock.Text> + </TextBlock> + + <RepeatButton VerticalAlignment="Stretch" + HotKey="Ctrl + Shift + Up" + ToolTip.Tip="Go to the next issue [Ctrl + Shift + Up]" + IsEnabled="{Binding IssueCanGoNext}" + Command="{Binding IssueGoNext}" + Interval="100" + i:Attached.Icon="fas fa-caret-right"/> + + <Button VerticalAlignment="Stretch" + ToolTip.Tip="Hides and ignores the selected issues, they won't be re-detected. 
ALT + Click to re-enable the ignored issues." - IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" - i:Attached.Icon="fas fa-eye-slash" - Command="{Binding OnClickIssueIgnore}"/> + IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" + i:Attached.Icon="fas fa-eye-slash" + Command="{Binding OnClickIssueIgnore}"/> - <Button VerticalAlignment="Stretch" - ToolTip.Tip="Remove the selected issue(s) when possible. + <Button VerticalAlignment="Stretch" + ToolTip.Tip="Remove the selected issue(s) when possible. 
Islands: All pixels are removed (turn black). 
ResinTrap: All areas are filled with white pixels. 
SuctionCup: Drills a vertical vent hole. 
EmptyLayers: Layers are removed." - IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" - i:Attached.Icon="fas fa-trash-alt" - Command="{Binding OnClickIssueRemove}"/> - - </StackPanel> - - <StackPanel - Grid.Row="0" - Orientation="Horizontal" - Spacing="2" - HorizontalAlignment="Right" - VerticalAlignment="Center"> - - <Button - IsEnabled="{Binding IsFileLoaded}" - ToolTip.Tip="Attempt to repair issues" - VerticalAlignment="Stretch" - i:Attached.Icon="fas fa-toolbox" - Command="{Binding OnClickRepairIssues}"/> - - <uc:ButtonWithIcon VerticalAlignment="Stretch" - ToolTip.Tip="Compute Issues. + IsEnabled="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" + i:Attached.Icon="fas fa-trash-alt" + Command="{Binding OnClickIssueRemove}"/> + + </StackPanel> + + <StackPanel + Grid.Row="0" + Orientation="Horizontal" + Spacing="2" + HorizontalAlignment="Right" + VerticalAlignment="Center"> + + <Button + IsEnabled="{Binding IsFileLoaded}" + ToolTip.Tip="Attempt to repair issues" + VerticalAlignment="Stretch" + i:Attached.Icon="fas fa-toolbox" + Command="{Binding OnClickRepairIssues}"/> + + <uc:ButtonWithIcon VerticalAlignment="Stretch" + ToolTip.Tip="Compute Issues. 
Right click to access settings." - Icon="fas fa-sync-alt" - Spacing="5" - Text="Detect ⮟" - Command="{Binding OnClickDetectIssues}"> - - <uc:ButtonWithIcon.ContextMenu> - <ContextMenu PlacementMode="Bottom"> - <CheckBox - IsChecked="{Binding Settings.Issues.ComputeIslands}" - Content="Islands"/> - <CheckBox - IsChecked="{Binding Settings.Issues.ComputeOverhangs}" - Content="Overhangs"/> - <StackPanel Orientation="Horizontal"> - <CheckBox - VerticalAlignment="Center" - IsChecked="{Binding Settings.Issues.ComputeResinTraps}" - Content="Resin traps"/> - - <NumericUpDown - VerticalAlignment="Center" - Margin="10,0,0,0" - ToolTip.Tip="Starting layer index for resin trap detection which will also be considered a drain layer. + Icon="fas fa-sync-alt" + Spacing="5" + Text="Detect ⮟" + Command="{Binding OnClickDetectIssues}"> + + <uc:ButtonWithIcon.ContextMenu> + <ContextMenu PlacementMode="Bottom"> + <CheckBox + IsChecked="{Binding Settings.Issues.ComputeIslands}" + Content="Islands"/> + <CheckBox + IsChecked="{Binding Settings.Issues.ComputeOverhangs}" + Content="Overhangs"/> + <StackPanel Orientation="Horizontal"> + <CheckBox + VerticalAlignment="Center" + IsChecked="{Binding Settings.Issues.ComputeResinTraps}" + Content="Resin traps"/> + + <NumericUpDown + VerticalAlignment="Center" + Margin="10,0,0,0" + ToolTip.Tip="Starting layer index for resin trap detection which will also be considered a drain layer. 
Use this setting to bypass complicated rafts by select the model first real layer." - Minimum="0" - Maximum="{Binding SlicerFile.LastLayerIndex}" - Increment="1" - Width="110" - Value="{Binding ResinTrapDetectionStartLayer}"/> - - <Button - VerticalAlignment="Center" - Margin="2,0,0,0" - ToolTip.Tip="Set to the first normal layer" - Content="N" - Command="{Binding SetResinTrapDetectionStartLayer}" - CommandParameter="N"/> - <Button - VerticalAlignment="Center" - Margin="2,0,0,0" - ToolTip.Tip="Set to the current layer" - Content="C" - Command="{Binding SetResinTrapDetectionStartLayer}" - CommandParameter="C"/> - </StackPanel> - - <CheckBox - VerticalAlignment="Center" - IsEnabled="{Binding Settings.Issues.ComputeResinTraps}" - IsChecked="{Binding Settings.Issues.ComputeSuctionCups}" - Content="Suction cups"/> - - <CheckBox - IsChecked="{Binding Settings.Issues.ComputeTouchingBounds}" - Content="Touching bounds"/> - <CheckBox - IsChecked="{Binding Settings.Issues.ComputePrintHeight}" - Content="Print height"/> - <CheckBox - IsChecked="{Binding Settings.Issues.ComputeEmptyLayers}" - Content="Empty layers"/> - </ContextMenu> - </uc:ButtonWithIcon.ContextMenu> - </uc:ButtonWithIcon> - - </StackPanel> - - <DataGrid - Name="IssuesGrid" - Grid.Row="1" - CanUserReorderColumns="True" - CanUserResizeColumns="True" - CanUserSortColumns="True" - GridLinesVisibility="Horizontal" - SelectionMode="Extended" - SelectedIndex="{Binding IssueSelectedIndex, Mode=TwoWay}" - IsReadOnly="True" - ClipboardCopyMode="None" - Items="{Binding IssuesGridItems}"> - <DataGrid.Columns> - - <DataGridTextColumn Header="Type" - Binding="{Binding Type}" - Width="Auto" /> - <DataGridTextColumn Header="Layer(s)" - Binding="{Binding LayerInfoStr}" - Width="Auto" /> - <DataGridTextColumn Header="Area" - Binding="{Binding Area, StringFormat={}{0:F0}}" - Width="Auto" /> - </DataGrid.Columns> - - </DataGrid> - - - </Grid> - - - </TabItem> - - <!-- + Minimum="0" + Maximum="{Binding SlicerFile.LastLayerIndex}" + Increment="1" + Width="110" + Value="{Binding ResinTrapDetectionStartLayer}"/> + + <Button + VerticalAlignment="Center" + Margin="2,0,0,0" + ToolTip.Tip="Set to the first normal layer" + Content="N" + Command="{Binding SetResinTrapDetectionStartLayer}" + CommandParameter="N"/> + <Button + VerticalAlignment="Center" + Margin="2,0,0,0" + ToolTip.Tip="Set to the current layer" + Content="C" + Command="{Binding SetResinTrapDetectionStartLayer}" + CommandParameter="C"/> + </StackPanel> + + <CheckBox + VerticalAlignment="Center" + IsEnabled="{Binding Settings.Issues.ComputeResinTraps}" + IsChecked="{Binding Settings.Issues.ComputeSuctionCups}" + Content="Suction cups"/> + + <CheckBox + IsChecked="{Binding Settings.Issues.ComputeTouchingBounds}" + Content="Touching bounds"/> + <CheckBox + IsChecked="{Binding Settings.Issues.ComputePrintHeight}" + Content="Print height"/> + <CheckBox + IsChecked="{Binding Settings.Issues.ComputeEmptyLayers}" + Content="Empty layers"/> + </ContextMenu> + </uc:ButtonWithIcon.ContextMenu> + </uc:ButtonWithIcon> + + </StackPanel> + + <DataGrid + Name="IssuesGrid" + Grid.Row="1" + CanUserReorderColumns="True" + CanUserResizeColumns="True" + CanUserSortColumns="True" + GridLinesVisibility="Horizontal" + SelectionMode="Extended" + SelectedIndex="{Binding IssueSelectedIndex, Mode=TwoWay}" + IsReadOnly="True" + ClipboardCopyMode="None" + Items="{Binding IssuesGridItems}"> + <DataGrid.Columns> + + <DataGridTextColumn Header="Type" + Binding="{Binding Type}" + Width="Auto" /> + <DataGridTextColumn Header="Layer(s)" + Binding="{Binding LayerInfoStr}" + Width="Auto" /> + <DataGridTextColumn Header="Area" + Binding="{Binding Area, StringFormat={}{0:F0}}" + Width="Auto" /> + </DataGrid.Columns> + + </DataGrid> + + + </Grid> + + + </TabItem> + + <!-- Suggestions Tab --> - <TabItem - Name="TabSuggestions" - ToolTip.Tip="Suggestions, automations and analyzer" - IsVisible="{Binding Suggestions.Length}" - IsEnabled="{Binding IsFileLoaded}" > - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> - <i:Icon Value="fas fa-shield-virus"/> - <!--<TextBlock Margin="5,0,0,0">Suggestions</TextBlock>!--> - </StackPanel> - </TabItem.Header> - - <Grid ColumnDefinitions="*"> - <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="2*" MinHeight="200" /> - <RowDefinition Height="Auto" /> - <RowDefinition Height="*" MinHeight="200" /> - </Grid.RowDefinitions> - - <Grid Grid.Row="0" RowDefinitions="Auto" ColumnDefinitions="Auto,2,Auto,*"> - <uc:ButtonWithIcon Grid.Column="0" - Text="Unselect all" - Spacing="5" - Icon="far fa-square" - Command="{Binding #SuggestionsAvailableListBox.UnselectAll}"/> - - <uc:ButtonWithIcon Grid.Column="2" - Text="Select all" - Spacing="5" - Icon="far fa-check-square" - Command="{Binding #SuggestionsAvailableListBox.SelectAll}"/> - - <StackPanel Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="2"> - <uc:ButtonWithIcon IsEnabled="{Binding #SuggestionsAvailableListBox.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" - Icon="fas fa-check" - Spacing="5" - Text="Apply" - Command="{Binding ApplySuggestionsClicked}"/> - - <Button VerticalAlignment="Stretch" - ToolTip.Tip="Configure suggestions" - Command="{Binding ConfigureSuggestionsClicked}" - i:Attached.Icon="fas fa-cog"/> - </StackPanel> - - </Grid> - - <ListBox - Grid.Row="1" - Name="SuggestionsAvailableListBox" - SelectionMode="Multiple,Toggle" - Items="{Binding SuggestionsAvailable}"> - <ListBox.ItemTemplate> - <DataTemplate> - <TextBlock Text="{Binding Message}" - TextWrapping="Wrap" - ToolTip.Tip="{Binding ToolTip}"> - <!--<TextBlock.IsVisible> + <TabItem + Name="TabSuggestions" + ToolTip.Tip="Suggestions, automations and analyzer" + IsVisible="{Binding Suggestions.Length}" + IsEnabled="{Binding IsFileLoaded}" > + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> + <i:Icon Value="fas fa-shield-virus"/> + <!--<TextBlock Margin="5,0,0,0">Suggestions</TextBlock>!--> + </StackPanel> + </TabItem.Header> + + <Grid ColumnDefinitions="*"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="2*" MinHeight="200" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" MinHeight="200" /> + </Grid.RowDefinitions> + + <Grid Grid.Row="0" RowDefinitions="Auto" ColumnDefinitions="Auto,2,Auto,*"> + <uc:ButtonWithIcon Grid.Column="0" + Text="Unselect all" + Spacing="5" + Icon="far fa-square" + Command="{Binding #SuggestionsAvailableListBox.UnselectAll}"/> + + <uc:ButtonWithIcon Grid.Column="2" + Text="Select all" + Spacing="5" + Icon="far fa-check-square" + Command="{Binding #SuggestionsAvailableListBox.SelectAll}"/> + + <StackPanel Grid.Column="3" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="2"> + <uc:ButtonWithIcon IsEnabled="{Binding #SuggestionsAvailableListBox.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" + Icon="fas fa-check" + Spacing="5" + Text="Apply" + Command="{Binding ApplySuggestionsClicked}"/> + + <Button VerticalAlignment="Stretch" + ToolTip.Tip="Configure suggestions" + Command="{Binding ConfigureSuggestionsClicked}" + i:Attached.Icon="fas fa-cog"/> + </StackPanel> + + </Grid> + + <ListBox + Grid.Row="1" + Name="SuggestionsAvailableListBox" + SelectionMode="Multiple,Toggle" + Items="{Binding SuggestionsAvailable}"> + <ListBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding Message}" + TextWrapping="Wrap" + ToolTip.Tip="{Binding ToolTip}"> + <!--<TextBlock.IsVisible> <MultiBinding Converter="{x:Static BoolConverters.And}"> <Binding Path="Enabled"/> <Binding Path="!IsApplied"/> </MultiBinding> </TextBlock.IsVisible> !--> - <TextBlock.ContextMenu> - <ContextMenu> - <MenuItem - Command="{Binding $parent[uc:WindowEx].DataContext.ApplySuggestionClicked}" - CommandParameter="{Binding .}" - Header="Apply" - IsVisible="{Binding !IsInformativeOnly}" - i:MenuItem.Icon="fas fa-check"/> - <MenuItem - Command="{Binding $parent[uc:WindowEx].DataContext.OpenWebsite}" - CommandParameter="{Binding InformationUrl}" - Header="More information (Web)" - IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}" - i:MenuItem.Icon="fas fa-info-circle"/> - <MenuItem - Command="{Binding $parent[uc:WindowEx].DataContext.ConfigureSuggestionClicked}" - CommandParameter="{Binding .}" - Header="Configure" - i:MenuItem.Icon="fas fa-cog"/> - </ContextMenu> - </TextBlock.ContextMenu> - </TextBlock> - </DataTemplate> - </ListBox.ItemTemplate> - </ListBox> - - <GridSplitter Grid.Row="2" ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"/> - - <TextBlock Grid.Row="2" Text="Applied suggestions:" HorizontalAlignment="Center" FontWeight="Bold"/> - - <ListBox - Grid.Row="3" - SelectionMode="AlwaysSelected" - Items="{Binding SuggestionsApplied}"> - <ListBox.ItemTemplate> - <DataTemplate> - <TextBlock Text="{Binding Message}" - TextWrapping="Wrap" - ToolTip.Tip="{Binding ToolTip}"> - <!--<TextBlock.IsVisible> + <TextBlock.ContextMenu> + <ContextMenu> + <MenuItem + Command="{Binding $parent[uc:WindowEx].DataContext.ApplySuggestionClicked}" + CommandParameter="{Binding .}" + Header="Apply" + IsVisible="{Binding !IsInformativeOnly}" + i:MenuItem.Icon="fas fa-check"/> + <MenuItem + Command="{Binding $parent[uc:WindowEx].DataContext.OpenWebsite}" + CommandParameter="{Binding InformationUrl}" + Header="More information (Web)" + IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}" + i:MenuItem.Icon="fas fa-info-circle"/> + <MenuItem + Command="{Binding $parent[uc:WindowEx].DataContext.ConfigureSuggestionClicked}" + CommandParameter="{Binding .}" + Header="Configure" + i:MenuItem.Icon="fas fa-cog"/> + </ContextMenu> + </TextBlock.ContextMenu> + </TextBlock> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + + <GridSplitter Grid.Row="2" ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"/> + + <TextBlock Grid.Row="2" Text="Applied suggestions:" HorizontalAlignment="Center" FontWeight="Bold"/> + + <ListBox + Grid.Row="3" + SelectionMode="AlwaysSelected" + Items="{Binding SuggestionsApplied}"> + <ListBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding Message}" + TextWrapping="Wrap" + ToolTip.Tip="{Binding ToolTip}"> + <!--<TextBlock.IsVisible> <MultiBinding Converter="{x:Static BoolConverters.And}"> <Binding Path="Enabled"/> <Binding Path="IsApplied"/> </MultiBinding> </TextBlock.IsVisible> !--> - <TextBlock.ContextMenu> - <ContextMenu> - <MenuItem - Command="{Binding $parent[uc:WindowEx].DataContext.OpenWebsite}" - CommandParameter="{Binding InformationUrl}" - Header="More information (Web)" - IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}" - i:MenuItem.Icon="fas fa-info-circle"/> - <MenuItem - Command="{Binding $parent[uc:WindowEx].DataContext.ConfigureSuggestionClicked}" - CommandParameter="{Binding .}" - Header="Configure" - i:MenuItem.Icon="fas fa-cog"/> - </ContextMenu> - </TextBlock.ContextMenu> - </TextBlock> - </DataTemplate> - </ListBox.ItemTemplate> - </ListBox> - </Grid> - - </TabItem> - - <!-- Pixel Editor !--> - <TabItem - Name="TabPixelEditor" - ToolTip.Tip="Pixel editor" - IsVisible="{Binding IsPixelEditorActive}" - IsEnabled="{Binding IsPixelEditorActive}"> - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> - <i:Icon Value="fas fa-drafting-compass"/> - <!--<TextBlock Margin="5,0,0,0">Pixel editor</TextBlock>!--> - </StackPanel> - </TabItem.Header> - - <Grid RowDefinitions="Auto,20,Auto,*"> - <TabControl - Padding="10,0" - SelectedIndex="{Binding SelectedPixelOperationTabIndex}"> - <!-- Drawing !--> - <TabControl.Styles> - <Style Selector="TabItem"> - <Setter Property="FontSize" Value="24"/> - </Style> - </TabControl.Styles> - <TabItem ToolTip.Tip="Drawing"> - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> - <i:Icon Value="fas fa-paint-brush"/> - <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Drawing" /> - </StackPanel> - </TabItem.Header> - - <StackPanel Spacing="10"> - <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> - <TextBlock Padding="10" Text="Shift+Left click to add white pixels + <TextBlock.ContextMenu> + <ContextMenu> + <MenuItem + Command="{Binding $parent[uc:WindowEx].DataContext.OpenWebsite}" + CommandParameter="{Binding InformationUrl}" + Header="More information (Web)" + IsVisible="{Binding InformationUrl, Converter={x:Static ObjectConverters.IsNotNull}}" + i:MenuItem.Icon="fas fa-info-circle"/> + <MenuItem + Command="{Binding $parent[uc:WindowEx].DataContext.ConfigureSuggestionClicked}" + CommandParameter="{Binding .}" + Header="Configure" + i:MenuItem.Icon="fas fa-cog"/> + </ContextMenu> + </TextBlock.ContextMenu> + </TextBlock> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Grid> + + </TabItem> + + <!-- Pixel Editor !--> + <TabItem + Name="TabPixelEditor" + ToolTip.Tip="Pixel editor" + IsVisible="{Binding IsPixelEditorActive}" + IsEnabled="{Binding IsPixelEditorActive}"> + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> + <i:Icon Value="fas fa-drafting-compass"/> + <!--<TextBlock Margin="5,0,0,0">Pixel editor</TextBlock>!--> + </StackPanel> + </TabItem.Header> + + <Grid RowDefinitions="Auto,20,Auto,*"> + <TabControl + Padding="10,0" + SelectedIndex="{Binding SelectedPixelOperationTabIndex}"> + <!-- Drawing !--> + <TabControl.Styles> + <Style Selector="TabItem"> + <Setter Property="FontSize" Value="24"/> + </Style> + </TabControl.Styles> + <TabItem ToolTip.Tip="Drawing"> + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> + <i:Icon Value="fas fa-paint-brush"/> + <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Drawing" /> + </StackPanel> + </TabItem.Header> + + <StackPanel Spacing="10"> + <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> + <TextBlock Padding="10" Text="Shift+Left click to add white pixels 
Shift+Right click to remove pixels"/> - </Border> - - <Grid - RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,130,5,40"> - - <TextBlock - Grid.Row="0" - Grid.Column="0" - VerticalAlignment="Center" - Text="Line type:" /> - <ComboBox - Grid.Row="0" - Grid.Column="2" - Grid.ColumnSpan="3" - Width="180" - Items="{Binding DrawingPixelDrawing.LineTypes}" - SelectedItem="{Binding DrawingPixelDrawing.LineType}"/> - - <TextBlock - Grid.Row="2" - Grid.Column="0" - VerticalAlignment="Center" - Text="Brush shape:" /> - <ComboBox - Grid.Row="2" - Grid.Column="2" - Grid.ColumnSpan="3" - Width="180" - Items="{Binding DrawingPixelDrawing.BrushShapeTypes}" - SelectedItem="{Binding DrawingPixelDrawing.BrushShape}"/> - - <TextBlock Grid.Row="4" Grid.Column="0" - VerticalAlignment="Center" - Text="Rotation angle:" /> - <NumericUpDown Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_deg" - FormatString="F2" - Minimum="-360" - Maximum="360" - Value="{Binding DrawingPixelDrawing.RotationAngle}"/> - - <TextBlock Grid.Row="6" Grid.Column="0" - VerticalAlignment="Center" - Text="Brush diameter:" /> - <NumericUpDown Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_px" - Minimum="1" - Maximum="4000" - HorizontalAlignment="Stretch" - Value="{Binding DrawingPixelDrawing.BrushSize}"/> - - <TextBlock Grid.Row="8" Grid.Column="0" - VerticalAlignment="Center" - Text="Thickness:" /> - <NumericUpDown Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_px" - Minimum="-1" - Maximum="255" - HorizontalAlignment="Stretch" - Value="{Binding DrawingPixelDrawing.Thickness}"/> - - <TextBlock Grid.Row="10" Grid.Column="0" - VerticalAlignment="Center" - Text="Remove pixel brightness:" /> - <NumericUpDown Grid.Row="10" Grid.Column="2" - Minimum="0" - Maximum="255" - HorizontalAlignment="Stretch" - Value="{Binding DrawingPixelDrawing.RemovePixelBrightness}"/> - <TextBlock - Grid.Row="10" - Grid.Column="4" - VerticalAlignment="Center" - Text="{Binding DrawingPixelDrawing.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" /> - - <TextBlock - Grid.Row="12" - Grid.Column="0" - VerticalAlignment="Center" - Text="Add pixel brightness:" /> - <NumericUpDown - Grid.Row="12" - Grid.Column="2" - Minimum="1" - Maximum="255" + </Border> + + <Grid + RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,130,5,40"> + + <TextBlock + Grid.Row="0" + Grid.Column="0" + VerticalAlignment="Center" + Text="Line type:" /> + <ComboBox + Grid.Row="0" + Grid.Column="2" + Grid.ColumnSpan="3" + Width="180" + Items="{Binding DrawingPixelDrawing.LineTypes}" + SelectedItem="{Binding DrawingPixelDrawing.LineType}"/> + + <TextBlock + Grid.Row="2" + Grid.Column="0" + VerticalAlignment="Center" + Text="Brush shape:" /> + <ComboBox + Grid.Row="2" + Grid.Column="2" + Grid.ColumnSpan="3" + Width="180" + Items="{Binding DrawingPixelDrawing.BrushShapeTypes}" + SelectedItem="{Binding DrawingPixelDrawing.BrushShape}"/> + + <TextBlock Grid.Row="4" Grid.Column="0" + VerticalAlignment="Center" + Text="Rotation angle:" /> + <NumericUpDown Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_deg" + FormatString="F2" + Minimum="-360" + Maximum="360" + Value="{Binding DrawingPixelDrawing.RotationAngle}"/> + + <TextBlock Grid.Row="6" Grid.Column="0" + VerticalAlignment="Center" + Text="Brush diameter:" /> + <NumericUpDown Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_px" + Minimum="1" + Maximum="4000" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.BrushSize}"/> + + <TextBlock Grid.Row="8" Grid.Column="0" + VerticalAlignment="Center" + Text="Thickness:" /> + <NumericUpDown Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_px" + Minimum="-1" + Maximum="255" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.Thickness}"/> + + <TextBlock Grid.Row="10" Grid.Column="0" + VerticalAlignment="Center" + Text="Remove pixel brightness:" /> + <NumericUpDown Grid.Row="10" Grid.Column="2" + Minimum="0" + Maximum="255" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.RemovePixelBrightness}"/> + <TextBlock + Grid.Row="10" + Grid.Column="4" + VerticalAlignment="Center" + Text="{Binding DrawingPixelDrawing.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" /> + + <TextBlock + Grid.Row="12" + Grid.Column="0" + VerticalAlignment="Center" + Text="Add pixel brightness:" /> + <NumericUpDown + Grid.Row="12" + Grid.Column="2" + Minimum="1" + Maximum="255" - HorizontalAlignment="Stretch" - Value="{Binding DrawingPixelDrawing.PixelBrightness}"/> - <TextBlock - Grid.Row="12" - Grid.Column="4" - VerticalAlignment="Center" - Text="{Binding DrawingPixelDrawing.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> - - <TextBlock - Grid.Row="14" - Grid.Column="0" - VerticalAlignment="Center" - Text="Layers depth below:" /> - <NumericUpDown Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_layers" - Minimum="0" - HorizontalAlignment="Stretch" - Value="{Binding DrawingPixelDrawing.LayersBelow}"/> - - - <TextBlock - Grid.Row="16" - Grid.Column="0" - VerticalAlignment="Center" - Text="Layers depth above:" /> - <NumericUpDown Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_layers" - Minimum="0" - HorizontalAlignment="Stretch" - Value="{Binding DrawingPixelDrawing.LayersAbove}"/> - - </Grid> - - </StackPanel> - - </TabItem> - - <!-- Text !--> - <TabItem ToolTip.Tip="Text"> - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> - <i:Icon Value="fas fa-font"/> - <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Text" /> - </StackPanel> - </TabItem.Header> - - <StackPanel Spacing="10"> - <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> - <TextBlock Padding="10" Text="Shift+Left click to add text + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.PixelBrightness}"/> + <TextBlock + Grid.Row="12" + Grid.Column="4" + VerticalAlignment="Center" + Text="{Binding DrawingPixelDrawing.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> + + <TextBlock + Grid.Row="14" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth below:" /> + <NumericUpDown Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_layers" + Minimum="0" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.LayersBelow}"/> + + + <TextBlock + Grid.Row="16" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth above:" /> + <NumericUpDown Grid.Row="16" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_layers" + Minimum="0" + HorizontalAlignment="Stretch" + Value="{Binding DrawingPixelDrawing.LayersAbove}"/> + + </Grid> + + </StackPanel> + + </TabItem> + + <!-- Text !--> + <TabItem ToolTip.Tip="Text"> + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> + <i:Icon Value="fas fa-font"/> + <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Text" /> + </StackPanel> + </TabItem.Header> + + <StackPanel Spacing="10"> + <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> + <TextBlock Padding="10" Text="Shift+Left click to add text 
Shift+Right click to remove text"/> - </Border> - - <Grid - RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,130,5,40"> - - <TextBlock - Grid.Row="0" - Grid.Column="0" - VerticalAlignment="Center" - Text="Line type:" /> - <ComboBox - Grid.Row="0" - Grid.Column="2" - Grid.ColumnSpan="3" - Width="180" - Items="{Binding DrawingPixelText.LineTypes}" - SelectedItem="{Binding DrawingPixelText.LineType}"/> - - <TextBlock - Grid.Row="2" - Grid.Column="0" - VerticalAlignment="Center" - Text="Font face:" /> - <ComboBox - Grid.Row="2" - Grid.Column="2" - Grid.ColumnSpan="3" - Width="180" - Items="{Binding DrawingPixelText.FontFaces}" - SelectedItem="{Binding DrawingPixelText.Font}"/> - - <TextBlock - Grid.Row="4" - Grid.Column="0" - VerticalAlignment="Center" - Text="Font scale:" /> - <NumericUpDown - Grid.Row="4" - Grid.Column="2" - Grid.ColumnSpan="3" - Minimum="0.1" - Maximum="255" - Increment="0.1" + </Border> + + <Grid + RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,130,5,40"> + + <TextBlock + Grid.Row="0" + Grid.Column="0" + VerticalAlignment="Center" + Text="Line type:" /> + <ComboBox + Grid.Row="0" + Grid.Column="2" + Grid.ColumnSpan="3" + Width="180" + Items="{Binding DrawingPixelText.LineTypes}" + SelectedItem="{Binding DrawingPixelText.LineType}"/> + + <TextBlock + Grid.Row="2" + Grid.Column="0" + VerticalAlignment="Center" + Text="Font face:" /> + <ComboBox + Grid.Row="2" + Grid.Column="2" + Grid.ColumnSpan="3" + Width="180" + Items="{Binding DrawingPixelText.FontFaces}" + SelectedItem="{Binding DrawingPixelText.Font}"/> + + <TextBlock + Grid.Row="4" + Grid.Column="0" + VerticalAlignment="Center" + Text="Font scale:" /> + <NumericUpDown + Grid.Row="4" + Grid.Column="2" + Grid.ColumnSpan="3" + Minimum="0.1" + Maximum="255" + Increment="0.1" - Value="{Binding DrawingPixelText.FontScale}"/> - - - <TextBlock - Grid.Row="6" - Grid.Column="0" - VerticalAlignment="Center" - Text="Thickness:" /> - <NumericUpDown Grid.Row="6" Grid.Column="2" - Classes="ValueLabel ValueLabel_px" - Grid.ColumnSpan="3" - Minimum="1" - Maximum="255" - Value="{Binding DrawingPixelText.Thickness}"/> - - <TextBlock - Grid.Row="8" - Grid.Column="0" - VerticalAlignment="Center" - Text="Text:" /> - <TextBox - Grid.Row="8" - Grid.Column="2" - Grid.ColumnSpan="3" - AcceptsReturn="True" - Height="90" - Text="{Binding DrawingPixelText.Text}"/> - - <TextBlock - Grid.Row="10" - Grid.Column="0" - VerticalAlignment="Center" - Text="Line alignment:" /> - <ComboBox - Grid.Row="10" - Grid.Column="2" - Grid.ColumnSpan="3" - Width="180" - Items="{Binding DrawingPixelText.LineAlignment, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" - SelectedItem="{Binding DrawingPixelText.LineAlignment, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> - - <CheckBox - Grid.Row="12" - Grid.Column="2" - Grid.ColumnSpan="3" - Content="Flip text Vertically" - IsChecked="{Binding DrawingPixelText.Mirror}"/> - - <TextBlock - Grid.Row="14" - Grid.Column="0" - VerticalAlignment="Center" - Text="Rotation angle:" /> - <NumericUpDown Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_deg" - FormatString="F2" - Minimum="-360" - Maximum="360" - Value="{Binding DrawingPixelText.Angle}"/> - - <TextBlock - Grid.Row="16" - Grid.Column="0" - VerticalAlignment="Center" - Text="Remove pixel brightness:" /> - <NumericUpDown - Grid.Row="16" - Grid.Column="2" - Minimum="0" - Maximum="255" + Value="{Binding DrawingPixelText.FontScale}"/> + + + <TextBlock + Grid.Row="6" + Grid.Column="0" + VerticalAlignment="Center" + Text="Thickness:" /> + <NumericUpDown Grid.Row="6" Grid.Column="2" + Classes="ValueLabel ValueLabel_px" + Grid.ColumnSpan="3" + Minimum="1" + Maximum="255" + Value="{Binding DrawingPixelText.Thickness}"/> + + <TextBlock + Grid.Row="8" + Grid.Column="0" + VerticalAlignment="Center" + Text="Text:" /> + <TextBox + Grid.Row="8" + Grid.Column="2" + Grid.ColumnSpan="3" + AcceptsReturn="True" + Height="90" + Text="{Binding DrawingPixelText.Text}"/> + + <TextBlock + Grid.Row="10" + Grid.Column="0" + VerticalAlignment="Center" + Text="Line alignment:" /> + <ComboBox + Grid.Row="10" + Grid.Column="2" + Grid.ColumnSpan="3" + Width="180" + Items="{Binding DrawingPixelText.LineAlignment, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding DrawingPixelText.LineAlignment, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> + + <CheckBox + Grid.Row="12" + Grid.Column="2" + Grid.ColumnSpan="3" + Content="Flip text Vertically" + IsChecked="{Binding DrawingPixelText.Mirror}"/> + + <TextBlock + Grid.Row="14" + Grid.Column="0" + VerticalAlignment="Center" + Text="Rotation angle:" /> + <NumericUpDown Grid.Row="14" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_deg" + FormatString="F2" + Minimum="-360" + Maximum="360" + Value="{Binding DrawingPixelText.Angle}"/> + + <TextBlock + Grid.Row="16" + Grid.Column="0" + VerticalAlignment="Center" + Text="Remove pixel brightness:" /> + <NumericUpDown + Grid.Row="16" + Grid.Column="2" + Minimum="0" + Maximum="255" - Value="{Binding DrawingPixelText.RemovePixelBrightness}"/> - <TextBlock - Grid.Row="16" - Grid.Column="4" - VerticalAlignment="Center" - Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" /> - - <TextBlock - Grid.Row="18" - Grid.Column="0" - VerticalAlignment="Center" - Text="Add pixel brightness:" /> - <NumericUpDown - Grid.Row="18" - Grid.Column="2" - Minimum="1" - Maximum="255" + Value="{Binding DrawingPixelText.RemovePixelBrightness}"/> + <TextBlock + Grid.Row="16" + Grid.Column="4" + VerticalAlignment="Center" + Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" /> + + <TextBlock + Grid.Row="18" + Grid.Column="0" + VerticalAlignment="Center" + Text="Add pixel brightness:" /> + <NumericUpDown + Grid.Row="18" + Grid.Column="2" + Minimum="1" + Maximum="255" - Value="{Binding DrawingPixelText.PixelBrightness}"/> - <TextBlock - Grid.Row="18" - Grid.Column="4" - VerticalAlignment="Center" - Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> - - - <TextBlock - Grid.Row="20" - Grid.Column="0" - VerticalAlignment="Center" - Text="Layers depth below:" /> - <NumericUpDown Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_layers" - Minimum="0" - Value="{Binding DrawingPixelText.LayersBelow}"/> - - - <TextBlock - Grid.Row="22" - Grid.Column="0" - VerticalAlignment="Center" - Text="Layers depth above:" /> - <NumericUpDown Grid.Row="22" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_layers" - Minimum="0" - Value="{Binding DrawingPixelText.LayersAbove}"/> - - </Grid> - - </StackPanel> - - </TabItem> - - <!-- Eraser !--> - <TabItem ToolTip.Tip="Eraser"> - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> - <i:Icon Value="fas fa-eraser"/> - <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Eraser" /> - </StackPanel> - </TabItem.Header> - - <StackPanel Spacing="10"> - <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> - <TextBlock Padding="10" Text="Shift+click over a white pixel to remove the linked area. + Value="{Binding DrawingPixelText.PixelBrightness}"/> + <TextBlock + Grid.Row="18" + Grid.Column="4" + VerticalAlignment="Center" + Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> + + + <TextBlock + Grid.Row="20" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth below:" /> + <NumericUpDown Grid.Row="20" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_layers" + Minimum="0" + Value="{Binding DrawingPixelText.LayersBelow}"/> + + + <TextBlock + Grid.Row="22" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth above:" /> + <NumericUpDown Grid.Row="22" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_layers" + Minimum="0" + Value="{Binding DrawingPixelText.LayersAbove}"/> + + </Grid> + + </StackPanel> + + </TabItem> + + <!-- Eraser !--> + <TabItem ToolTip.Tip="Eraser"> + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> + <i:Icon Value="fas fa-eraser"/> + <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Eraser" /> + </StackPanel> + </TabItem.Header> + + <StackPanel Spacing="10"> + <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> + <TextBlock Padding="10" Text="Shift+click over a white pixel to remove the linked area. 
(Fill linked area with black)"/> - </Border> - - <Grid - RowDefinitions="Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,*,10,40"> - - <TextBlock - Grid.Row="0" - Grid.Column="0" - VerticalAlignment="Center" - Text="Pixel brightness:" /> - <NumericUpDown Grid.Row="0" Grid.Column="2" - Classes="ValueLabel ValueLabel_sun" - Minimum="0" - Maximum="255" + </Border> + + <Grid + RowDefinitions="Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,*,10,40"> + + <TextBlock + Grid.Row="0" + Grid.Column="0" + VerticalAlignment="Center" + Text="Pixel brightness:" /> + <NumericUpDown Grid.Row="0" Grid.Column="2" + Classes="ValueLabel ValueLabel_sun" + Minimum="0" + Maximum="255" - Value="{Binding DrawingPixelEraser.PixelBrightness}"/> - <TextBlock - Grid.Row="0" - Grid.Column="4" - VerticalAlignment="Center" - Text="{Binding DrawingPixelEraser.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> - - <TextBlock - Grid.Row="2" - Grid.Column="0" - VerticalAlignment="Center" - Text="Layers depth below:" /> - <NumericUpDown Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_layers" - Minimum="0" - Value="{Binding DrawingPixelEraser.LayersBelow}"/> - - <TextBlock - Grid.Row="4" - Grid.Column="0" - VerticalAlignment="Center" - Text="Layers depth above:" /> - <NumericUpDown Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_layers" - Minimum="0" - Value="{Binding DrawingPixelEraser.LayersAbove}"/> - - - </Grid> - - </StackPanel> - - </TabItem> - - <!-- Supports !--> - <TabItem - ToolTip.Tip="Supports" + Value="{Binding DrawingPixelEraser.PixelBrightness}"/> + <TextBlock + Grid.Row="0" + Grid.Column="4" + VerticalAlignment="Center" + Text="{Binding DrawingPixelEraser.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> + + <TextBlock + Grid.Row="2" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth below:" /> + <NumericUpDown Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_layers" + Minimum="0" + Value="{Binding DrawingPixelEraser.LayersBelow}"/> + + <TextBlock + Grid.Row="4" + Grid.Column="0" + VerticalAlignment="Center" + Text="Layers depth above:" /> + <NumericUpDown Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_layers" + Minimum="0" + Value="{Binding DrawingPixelEraser.LayersAbove}"/> + + + </Grid> + + </StackPanel> + + </TabItem> + + <!-- Supports !--> + <TabItem + ToolTip.Tip="Supports" > - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> - <i:Icon Value="fas fa-code-branch"/> - <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Supports" /> - </StackPanel> - </TabItem.Header> - - <StackPanel Spacing="10" > - <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> - <TextBlock Padding="10" Text="Shift+click under a island to add primitive support. + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> + <i:Icon Value="fas fa-code-branch"/> + <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Supports" /> + </StackPanel> + </TabItem.Header> + + <StackPanel Spacing="10" > + <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> + <TextBlock Padding="10" Text="Shift+click under a island to add primitive support. 
Note: this operation can't be previewed."/> - </Border> - <Grid - RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" - ColumnDefinitions="Auto,10,*,5,35"> - <TextBlock - Grid.Row="0" - Grid.Column="0" - VerticalAlignment="Center" - Text="Tip diameter:" /> - <NumericUpDown Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_px" - Minimum="1" - Maximum="255" - Value="{Binding DrawingPixelSupport.TipDiameter}"/> - - <TextBlock - Grid.Row="2" - Grid.Column="0" - VerticalAlignment="Center" - Text="Pillar diameter:" /> - <NumericUpDown Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_px" - Minimum="1" - Maximum="255" - Value="{Binding DrawingPixelSupport.PillarDiameter}"/> - - <TextBlock - Grid.Row="4" - Grid.Column="0" - VerticalAlignment="Center" - Text="Base diameter:" /> - <NumericUpDown Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" - Classes="ValueLabel ValueLabel_px" - Minimum="1" - Maximum="255" - Value="{Binding DrawingPixelSupport.BaseDiameter}"/> - - <TextBlock - Grid.Row="6" - Grid.Column="0" - VerticalAlignment="Center" - Text="Pixel brightness:" /> - <NumericUpDown Grid.Row="6" Grid.Column="2" - Classes="ValueLabel ValueLabel_sun" - Minimum="0" - Maximum="255" + </Border> + <Grid + RowDefinitions="Auto,10,Auto,10,Auto,10,Auto" + ColumnDefinitions="Auto,10,*,5,35"> + <TextBlock + Grid.Row="0" + Grid.Column="0" + VerticalAlignment="Center" + Text="Tip diameter:" /> + <NumericUpDown Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_px" + Minimum="1" + Maximum="255" + Value="{Binding DrawingPixelSupport.TipDiameter}"/> + + <TextBlock + Grid.Row="2" + Grid.Column="0" + VerticalAlignment="Center" + Text="Pillar diameter:" /> + <NumericUpDown Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_px" + Minimum="1" + Maximum="255" + Value="{Binding DrawingPixelSupport.PillarDiameter}"/> + + <TextBlock + Grid.Row="4" + Grid.Column="0" + VerticalAlignment="Center" + Text="Base diameter:" /> + <NumericUpDown Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="3" + Classes="ValueLabel ValueLabel_px" + Minimum="1" + Maximum="255" + Value="{Binding DrawingPixelSupport.BaseDiameter}"/> + + <TextBlock + Grid.Row="6" + Grid.Column="0" + VerticalAlignment="Center" + Text="Pixel brightness:" /> + <NumericUpDown Grid.Row="6" Grid.Column="2" + Classes="ValueLabel ValueLabel_sun" + Minimum="0" + Maximum="255" - Value="{Binding DrawingPixelSupport.PixelBrightness}"/> - <TextBlock - Grid.Row="6" - Grid.Column="4" - VerticalAlignment="Center" - Text="{Binding DrawingPixelSupport.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> + Value="{Binding DrawingPixelSupport.PixelBrightness}"/> + <TextBlock + Grid.Row="6" + Grid.Column="4" + VerticalAlignment="Center" + Text="{Binding DrawingPixelSupport.PixelBrightnessPercent, StringFormat=\{0:0\}%}" /> - </Grid> - </StackPanel> + </Grid> + </StackPanel> - </TabItem> + </TabItem> - <!-- Drain holes !--> - <TabItem - ToolTip.Tip="Drain holes" + <!-- Drain holes !--> + <TabItem + ToolTip.Tip="Drain holes" > - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> - <i:Icon Value="fas fa-ring"/> - <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Drain holes" /> - </StackPanel> - </TabItem.Header> - - <StackPanel Spacing="10"> - <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> - <TextBlock Padding="10" Text="Shift+click to add a vertical drain hole. + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> + <i:Icon Value="fas fa-ring"/> + <TextBlock IsVisible="{Binding $parent[TabItem].IsSelected}" Text="Drain holes" /> + </StackPanel> + </TabItem.Header> + + <StackPanel Spacing="10"> + <Border Background="{DynamicResource LightBackground}" BorderThickness="1" BorderBrush="Black"> + <TextBlock Padding="10" Text="Shift+click to add a vertical drain hole. 
Note: this operation can't be previewed."/> - </Border> - <Grid - RowDefinitions="Auto" - ColumnDefinitions="Auto,10,*"> - <TextBlock - Grid.Row="0" - Grid.Column="0" - VerticalAlignment="Center" - Text="Hole diameter:" /> - <NumericUpDown Grid.Row="0" Grid.Column="2" - Classes="ValueLabel ValueLabel_px" - Minimum="20" - Maximum="255" - Value="{Binding DrawingPixelDrainHole.Diameter}"/> - </Grid> - </StackPanel> - - </TabItem> - - </TabControl> - - <!-- Toolbar !--> - <StackPanel - Grid.Row="2" - Orientation="Horizontal" Spacing="1"> - <uc:ButtonWithIcon IsEnabled="{Binding #DrawingsGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" - Icon="fas fa-trash-alt" - Spacing="5" - Text="Remove" - Command="{Binding OnClickDrawingRemove}"/> - - <uc:ButtonWithIcon IsEnabled="{Binding Drawings.Count}" - Icon="fas fa-times" - Spacing="5" - Text="Clear" - Command="{Binding OnClickDrawingClear}"/> - </StackPanel> - - <StackPanel - Grid.Row="2" - HorizontalAlignment="Right" - Orientation="Horizontal" - Spacing="10" > - - <uc:ButtonWithIcon IsEnabled="{Binding Drawings.Count}" - Icon="fas fa-check" - Spacing="5" - Text="{Binding Drawings.Count, StringFormat=Apply \{0\} operations}" - Command="{Binding DrawModifications}" - CommandParameter="false"/> - </StackPanel> - - <DataGrid - Name="DrawingsGrid" - Grid.Row="3" - CanUserReorderColumns="True" - CanUserResizeColumns="True" - CanUserSortColumns="True" - GridLinesVisibility="Horizontal" - SelectionMode="Extended" - IsReadOnly="True" - ClipboardCopyMode="IncludeHeader" - Items="{Binding Drawings}"> - <DataGrid.Columns> - <DataGridTextColumn Header="#" - Binding="{Binding Index}" - Width="Auto" /> - <DataGridTextColumn Header="Operation" - Binding="{Binding OperationType}" - Width="Auto" /> - <DataGridTextColumn Header="Layer" - Binding="{Binding LayerIndex}" - Width="Auto" /> - <DataGridTextColumn Header="Position (X, Y)" - Binding="{Binding Location}" - Width="Auto" /> - </DataGrid.Columns> - - </DataGrid> - - </Grid> - - </TabItem> - - - <TabItem - Name="TabClipboard" - IsEnabled="{Binding IsFileLoaded}" - ToolTip.Tip="Clipboard"> - <TabItem.Header> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> - <i:Icon Value="fas fa-clipboard-list"/> - <!--<TextBlock Margin="5,0,0,0">Clipboard</TextBlock>!--> - </StackPanel> - </TabItem.Header> - - <Grid> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition Height="*" MinHeight="140"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="*" MinHeight="180"/> - </Grid.RowDefinitions> - <StackPanel Orientation="Horizontal" Spacing="2"> - - <Button IsEnabled="{Binding Clipboard.CanUndo}" - Command="{Binding ClipboardUndo}" - HotKey="Ctrl + Z" - ToolTip.Tip="Undo [Ctrl + Z] + </Border> + <Grid + RowDefinitions="Auto" + ColumnDefinitions="Auto,10,*"> + <TextBlock + Grid.Row="0" + Grid.Column="0" + VerticalAlignment="Center" + Text="Hole diameter:" /> + <NumericUpDown Grid.Row="0" Grid.Column="2" + Classes="ValueLabel ValueLabel_px" + Minimum="20" + Maximum="255" + Value="{Binding DrawingPixelDrainHole.Diameter}"/> + </Grid> + </StackPanel> + + </TabItem> + + </TabControl> + + <!-- Toolbar !--> + <StackPanel + Grid.Row="2" + Orientation="Horizontal" Spacing="1"> + <uc:ButtonWithIcon IsEnabled="{Binding #DrawingsGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}" + Icon="fas fa-trash-alt" + Spacing="5" + Text="Remove" + Command="{Binding OnClickDrawingRemove}"/> + + <uc:ButtonWithIcon IsEnabled="{Binding Drawings.Count}" + Icon="fas fa-times" + Spacing="5" + Text="Clear" + Command="{Binding OnClickDrawingClear}"/> + </StackPanel> + + <StackPanel + Grid.Row="2" + HorizontalAlignment="Right" + Orientation="Horizontal" + Spacing="10" > + + <uc:ButtonWithIcon IsEnabled="{Binding Drawings.Count}" + Icon="fas fa-check" + Spacing="5" + Text="{Binding Drawings.Count, StringFormat=Apply \{0\} operations}" + Command="{Binding DrawModifications}" + CommandParameter="false"/> + </StackPanel> + + <DataGrid + Name="DrawingsGrid" + Grid.Row="3" + CanUserReorderColumns="True" + CanUserResizeColumns="True" + CanUserSortColumns="True" + GridLinesVisibility="Horizontal" + SelectionMode="Extended" + IsReadOnly="True" + ClipboardCopyMode="IncludeHeader" + Items="{Binding Drawings}"> + <DataGrid.Columns> + <DataGridTextColumn Header="#" + Binding="{Binding Index}" + Width="Auto" /> + <DataGridTextColumn Header="Operation" + Binding="{Binding OperationType}" + Width="Auto" /> + <DataGridTextColumn Header="Layer" + Binding="{Binding LayerIndex}" + Width="Auto" /> + <DataGridTextColumn Header="Position (X, Y)" + Binding="{Binding Location}" + Width="Auto" /> + </DataGrid.Columns> + + </DataGrid> + + </Grid> + + </TabItem> + + + <TabItem + Name="TabClipboard" + IsEnabled="{Binding IsFileLoaded}" + ToolTip.Tip="Clipboard"> + <TabItem.Header> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal"> + <i:Icon Value="fas fa-clipboard-list"/> + <!--<TextBlock Margin="5,0,0,0">Clipboard</TextBlock>!--> + </StackPanel> + </TabItem.Header> + + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="*" MinHeight="140"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="*" MinHeight="180"/> + </Grid.RowDefinitions> + <StackPanel Orientation="Horizontal" Spacing="2"> + + <Button IsEnabled="{Binding Clipboard.CanUndo}" + Command="{Binding ClipboardUndo}" + HotKey="Ctrl + Z" + ToolTip.Tip="Undo [Ctrl + Z] 
Shift + Click to Undo and edit last operation [Ctrl + Shift + Z]" - i:Attached.Icon="fas fa-undo-alt"/> - - <TextBlock VerticalAlignment="Center"> - <TextBlock.Text> - <MultiBinding StringFormat="{}{0}/{1}"> - <Binding Path="Clipboard.CurrentIndexCountStr"/> - <Binding Path="Clipboard.Items.Count"/> - </MultiBinding> - </TextBlock.Text> - </TextBlock> - - <Button - IsEnabled="{Binding Clipboard.CanRedo}" - Command="{Binding ClipboardRedo}" - HotKey="Ctrl + Y" - ToolTip.Tip="Redo [Ctrl + Y]" - i:Attached.Icon="fas fa-redo-alt"/> - - </StackPanel> - - <StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="2" HorizontalAlignment="Right"> - <Button - IsEnabled="{Binding Clipboard.Items.Count}" - Command="{Binding ClipboardClear}" - ToolTip.Tip="Clear all clips" - i:Attached.Icon="fas fa-times"/> - </StackPanel> - - <ListBox - Grid.Row="1" - Name="ClipboardList" - SelectionMode="Single" - AutoScrollToSelectedItem="True" - SelectedIndex="{Binding Clipboard.CurrentIndex}" - Items="{Binding Clipboard.Items}" /> - - <GridSplitter Grid.Row="2" ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"/> - - <Grid Grid.Row="3" RowDefinitions="Auto,*" ColumnDefinitions="*"> - <Grid Grid.Row="0" - RowDefinitions="Auto" - ColumnDefinitions="Auto,*,Auto"> - <TextBlock Grid.Row="0" Grid.Column="0" - VerticalAlignment="Center" - Text="{Binding Logs.Count, StringFormat=Operations: {0}}"/> - - <TextBlock Grid.Row="0" Grid.Column="1" - HorizontalAlignment="Center" - VerticalAlignment="Center" - FontWeight="Bold" Text="Logs"/> - - <StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal" - Spacing="5" - HorizontalAlignment="Right"> - <CheckBox VerticalAlignment="Center" - ToolTip.Tip="Shows extra information useful to debug problems." - IsChecked="{Binding IsVerbose}" - Content="Verbose"/> - - <Button VerticalAlignment="Center" - Command="{Binding Logs.Clear}" - ToolTip.Tip="Clear all logs" - i:Attached.Icon="fas fa-times"/> - </StackPanel> + i:Attached.Icon="fas fa-undo-alt"/> + + <TextBlock VerticalAlignment="Center"> + <TextBlock.Text> + <MultiBinding StringFormat="{}{0}/{1}"> + <Binding Path="Clipboard.CurrentIndexCountStr"/> + <Binding Path="Clipboard.Items.Count"/> + </MultiBinding> + </TextBlock.Text> + </TextBlock> + + <Button + IsEnabled="{Binding Clipboard.CanRedo}" + Command="{Binding ClipboardRedo}" + HotKey="Ctrl + Y" + ToolTip.Tip="Redo [Ctrl + Y]" + i:Attached.Icon="fas fa-redo-alt"/> + + </StackPanel> + + <StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="2" HorizontalAlignment="Right"> + <Button + IsEnabled="{Binding Clipboard.Items.Count}" + Command="{Binding ClipboardClear}" + ToolTip.Tip="Clear all clips" + i:Attached.Icon="fas fa-times"/> + </StackPanel> + + <ListBox + Grid.Row="1" + Name="ClipboardList" + SelectionMode="Single" + AutoScrollToSelectedItem="True" + SelectedIndex="{Binding Clipboard.CurrentIndex}" + Items="{Binding Clipboard.Items}" /> + + <GridSplitter Grid.Row="2" ResizeBehavior="PreviousAndNext" ResizeDirection="Rows"/> + + <Grid Grid.Row="3" RowDefinitions="Auto,*" ColumnDefinitions="*"> + <Grid Grid.Row="0" + RowDefinitions="Auto" + ColumnDefinitions="Auto,*,Auto"> + <TextBlock Grid.Row="0" Grid.Column="0" + VerticalAlignment="Center" + Text="{Binding Logs.Count, StringFormat=Operations: {0}}"/> + + <TextBlock Grid.Row="0" Grid.Column="1" + HorizontalAlignment="Center" + VerticalAlignment="Center" + FontWeight="Bold" Text="Logs"/> + + <StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal" + Spacing="5" + HorizontalAlignment="Right"> + <CheckBox VerticalAlignment="Center" + ToolTip.Tip="Shows extra information useful to debug problems." + IsChecked="{Binding IsVerbose}" + Content="Verbose"/> + + <Button VerticalAlignment="Center" + Command="{Binding Logs.Clear}" + ToolTip.Tip="Clear all logs" + i:Attached.Icon="fas fa-times"/> + </StackPanel> + + </Grid> + + <DataGrid Grid.Row="1" Items="{Binding Logs}" + VerticalAlignment="Stretch" + CanUserReorderColumns="True" + CanUserResizeColumns="True" + CanUserSortColumns="True" + GridLinesVisibility="All" + IsReadOnly="True" + ClipboardCopyMode="IncludeHeader" + SelectionMode="Extended"> + <DataGrid.Columns> + <DataGridTextColumn Header="#" + Binding="{Binding Index}" + Width="Auto" /> + <DataGridTextColumn Header="Started" + Binding="{Binding StartTime}" + Width="Auto" /> + <DataGridTextColumn Header="Time(s)" + Binding="{Binding ElapsedTime}" + Width="Auto" /> + <DataGridTextColumn Header="Description" + Binding="{Binding Description}" + Width="Auto" /> + </DataGrid.Columns> + </DataGrid> + </Grid> + + </Grid> + + + </TabItem> + + </TabControl> + + <Grid + IsEnabled="{Binding IsFileLoaded}" + DockPanel.Dock="Right" + ColumnDefinitions="160" + RowDefinitions="Auto,Auto,*,Auto,Auto,Auto,Auto,Auto" Margin="5"> + <TextBlock + Text="{Binding MaximumLayerString}" + Name="Layer.Navigation.Up" + Margin="0,0,0,10" + HorizontalAlignment="Center" + TextAlignment="Center" + Grid.Row="0"/> + <RepeatButton + Grid.Row="1" + ToolTip.Tip="Navigate to up layer [Ctrl + Up]" + HotKey="Ctrl + Up" + Interval="100" + HorizontalAlignment="Stretch" + IsEnabled="{Binding CanGoUp}" + Command="{Binding GoNextLayer}" + i:Attached.Icon="fas fa-angle-up"/> + + <Grid + Name="LayerNavigationSliderGrid" + Grid.Row="2" ColumnDefinitions="*,20,40"> + <Panel + Grid.Column="0" + Name="LayerNavigationTooltipPanel" + Margin="{Binding LayerNavigationTooltipMargin}" + HorizontalAlignment="Stretch"> + <Border + Name="Layer.Navigation.Tooltip.Border" + Classes="LayerNavigationToolTip" + VerticalAlignment="Top"> + <TextBlock Padding="2" Text="{Binding ActualLayerTooltip}"/> + </Border> + </Panel> + + <!--<Image + Grid.Column="1" + Name="Layer.Navigation.Issues" Width="16"></Image>!--> + <Canvas + Grid.Column="1" + Margin="0,15" + Classes="IssuesTrackerCanvas" + Name="Layer.Navigation.IssuesCanvas" Width="20"/> + + <Slider + Grid.Column="2" + Name="Layer.Navigation.Slider" + Minimum="0" + Maximum="{Binding SliderMaximumValue}" + Value="{Binding ActualLayerSlider}" + TickFrequency="1" + TickPlacement="Outside" + SmallChange="1" + LargeChange="10" + IsSnapToTickEnabled="True" + Margin="0,5" + HorizontalAlignment="Right" + Orientation="Vertical"/> </Grid> - <DataGrid Grid.Row="1" Items="{Binding Logs}" - VerticalAlignment="Stretch" - CanUserReorderColumns="True" - CanUserResizeColumns="True" - CanUserSortColumns="True" - GridLinesVisibility="All" - IsReadOnly="True" - ClipboardCopyMode="IncludeHeader" - SelectionMode="Extended"> - <DataGrid.Columns> - <DataGridTextColumn Header="#" - Binding="{Binding Index}" - Width="Auto" /> - <DataGridTextColumn Header="Started" - Binding="{Binding StartTime}" - Width="Auto" /> - <DataGridTextColumn Header="Time(s)" - Binding="{Binding ElapsedTime}" - Width="Auto" /> - <DataGridTextColumn Header="Description" - Binding="{Binding Description}" - Width="Auto" /> - </DataGrid.Columns> - </DataGrid> - </Grid> - - </Grid> - - - </TabItem> - - </TabControl> - - <Grid - IsEnabled="{Binding IsFileLoaded}" - DockPanel.Dock="Right" - ColumnDefinitions="160" - RowDefinitions="Auto,Auto,*,Auto,Auto,Auto,Auto,Auto" Margin="5"> - <TextBlock - Text="{Binding MaximumLayerString}" - Name="Layer.Navigation.Up" - Margin="0,0,0,10" - HorizontalAlignment="Center" - TextAlignment="Center" - Grid.Row="0"/> - <RepeatButton - Grid.Row="1" - ToolTip.Tip="Navigate to up layer [Ctrl + Up]" - HotKey="Ctrl + Up" - Interval="100" - HorizontalAlignment="Stretch" - IsEnabled="{Binding CanGoUp}" - Command="{Binding GoNextLayer}" - i:Attached.Icon="fas fa-angle-up"/> - - <Grid - Name="LayerNavigationSliderGrid" - Grid.Row="2" ColumnDefinitions="*,20,40"> - <Panel - Grid.Column="0" - Name="LayerNavigationTooltipPanel" - Margin="{Binding LayerNavigationTooltipMargin}" - HorizontalAlignment="Stretch"> - <Border - Name="Layer.Navigation.Tooltip.Border" - Classes="LayerNavigationToolTip" - VerticalAlignment="Top"> - <TextBlock Padding="2" Text="{Binding ActualLayerTooltip}"/> - </Border> - </Panel> - - <!--<Image - Grid.Column="1" - Name="Layer.Navigation.Issues" Width="16"></Image>!--> + <RepeatButton + Grid.Row="3" + ToolTip.Tip="Navigate to down layer [Ctrl + Down]" + HotKey="Ctrl + Down" + Interval="100" + HorizontalAlignment="Stretch" + IsEnabled="{Binding CanGoDown}" + Command="{Binding GoPreviousLayer}" + i:Attached.Icon="fas fa-angle-down"/> + + <NumericUpDown Grid.Row="4" + Margin="0,5" + Minimum="0" + Maximum="{Binding SliderMaximumValue}" + Value="{Binding ActualLayer}" /> + + <Grid Grid.Row="5" RowDefinitions="*" ColumnDefinitions="30,*,30"> + <Button Grid.Column="0" + ToolTip.Tip="Navigate to first layer [Ctrl + Left]" + HotKey="Ctrl + Left" + IsEnabled="{Binding CanGoDown}" + Command="{Binding GoFirstLayer}" + i:Attached.Icon="fas fa-angle-double-down"/> + + <TextBlock + Grid.Column="1" + Text="{Binding MinimumLayerString}" + VerticalAlignment="Center" + HorizontalAlignment="Center" + TextAlignment="Center"/> + + <Button + Grid.Column="2" + ToolTip.Tip="Navigate to last layer [Ctrl + Right]" + HotKey="Ctrl + Right" + IsEnabled="{Binding CanGoUp}" + Command="{Binding GoLastLayer}" + i:Attached.Icon="fas fa-angle-double-up"/> + </Grid> - <Canvas - Grid.Column="1" - Margin="0,15" - Classes="IssuesTrackerCanvas" - Name="Layer.Navigation.IssuesCanvas" Width="20"/> - - <Slider - Grid.Column="2" - Name="Layer.Navigation.Slider" - Minimum="0" - Maximum="{Binding SliderMaximumValue}" - Value="{Binding ActualLayerSlider}" - TickFrequency="1" - TickPlacement="Outside" - SmallChange="1" - LargeChange="10" - IsSnapToTickEnabled="True" - Margin="0,5" - HorizontalAlignment="Right" - Orientation="Vertical"/> - </Grid> - - <RepeatButton - Grid.Row="3" - ToolTip.Tip="Navigate to down layer [Ctrl + Down]" - HotKey="Ctrl + Down" - Interval="100" - HorizontalAlignment="Stretch" - IsEnabled="{Binding CanGoDown}" - Command="{Binding GoPreviousLayer}" - i:Attached.Icon="fas fa-angle-down"/> - - <NumericUpDown Grid.Row="4" - Margin="0,5" - Minimum="0" - Maximum="{Binding SliderMaximumValue}" - Value="{Binding ActualLayer}" /> - - <Grid Grid.Row="5" RowDefinitions="*" ColumnDefinitions="30,*,30"> - <Button Grid.Column="0" - ToolTip.Tip="Navigate to first layer [Ctrl + Left]" - HotKey="Ctrl + Left" - IsEnabled="{Binding CanGoDown}" - Command="{Binding GoFirstLayer}" - i:Attached.Icon="fas fa-angle-double-down"/> - - <TextBlock - Grid.Column="1" - Text="{Binding MinimumLayerString}" - VerticalAlignment="Center" - HorizontalAlignment="Center" - TextAlignment="Center"/> - - <Button - Grid.Column="2" - ToolTip.Tip="Navigate to last layer [Ctrl + Right]" - HotKey="Ctrl + Right" - IsEnabled="{Binding CanGoUp}" - Command="{Binding GoLastLayer}" - i:Attached.Icon="fas fa-angle-double-up"/> - </Grid> - - <StackPanel Grid.Row="6" - Margin="0,1,0,0" - HorizontalAlignment="Center" - VerticalAlignment="Center" - Orientation="Horizontal" Spacing="1"> - <Button - ToolTip.Tip="Navigate to the smallest bottom layer in mass" - Command="{Binding GoMassLayer}" - CommandParameter="SB" - Content="SB"/> - - <Button - ToolTip.Tip="Navigate to the largest bottom layer in mass" - Command="{Binding GoMassLayer}" - CommandParameter="LB" - Content="LB"/> - - <Button - ToolTip.Tip="Navigate to the smallest normal layer in mass" - Command="{Binding GoMassLayer}" - CommandParameter="SN" - Content="SN"/> - - <Button - ToolTip.Tip="Navigate to the largest normal layer in mass" - Command="{Binding GoMassLayer}" - CommandParameter="LN" - Content="LN"/> - </StackPanel> - - </Grid> - - - <!--<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> + <StackPanel Grid.Row="6" + Margin="0,1,0,0" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Orientation="Horizontal" Spacing="1"> + <Button + ToolTip.Tip="Navigate to the smallest bottom layer in mass" + Command="{Binding GoMassLayer}" + CommandParameter="SB" + Content="SB"/> + + <Button + ToolTip.Tip="Navigate to the largest bottom layer in mass" + Command="{Binding GoMassLayer}" + CommandParameter="LB" + Content="LB"/> + + <Button + ToolTip.Tip="Navigate to the smallest normal layer in mass" + Command="{Binding GoMassLayer}" + CommandParameter="SN" + Content="SN"/> + + <Button + ToolTip.Tip="Navigate to the largest normal layer in mass" + Command="{Binding GoMassLayer}" + CommandParameter="LN" + Content="LN"/> + </StackPanel> + + </Grid> + + + <!--<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> <Button Name="zoomtofit">Zoom to fit</Button> <Button Name="center">Center image</Button> </StackPanel>--> - <Grid - IsEnabled="{Binding IsFileLoaded}" - ColumnDefinitions="*" RowDefinitions="Auto,*,Auto" Margin="5"> - <Grid Grid.Row="0" Grid.Column="0" - IsEnabled="{Binding IsFileLoaded}" - ColumnDefinitions="*,Auto" RowDefinitions="Auto" Margin="5"> - <WrapPanel HorizontalAlignment="Left" Grid.Row="0" Orientation="Horizontal"> - <uc:ToggleButtonWithIcon - IsChecked="{Binding ShowLayerImageRotated}" - HotKey="Ctrl + R" - VerticalAlignment="Stretch" - ToolTip.Tip="Auto rotate layer preview image at 90º (This can slow down the layer preview) [CTRL+R]" - Margin="0,0,1,0" - Text="Rotate ⮟" - Spacing="5" - Icon="fas fa-sync-alt"> - <uc:ToggleButtonWithIcon.ContextMenu> - <ContextMenu PlacementMode="Bottom"> - <RadioButton - GroupName="ShowLayerImageRotateDirection" - IsChecked="{Binding ShowLayerImageRotateCWDirection}" - Content="90º Clockwise (CW)"/> - <RadioButton - GroupName="ShowLayerImageRotateDirection" - IsChecked="{Binding ShowLayerImageRotateCCWDirection}" - Content="90º Counter-clockwise (CCW)"/> - </ContextMenu> - </uc:ToggleButtonWithIcon.ContextMenu> - </uc:ToggleButtonWithIcon> - - - <uc:ToggleButtonWithIcon - IsChecked="{Binding ShowLayerImageFlipped}" - HotKey="Ctrl + F" - VerticalAlignment="Stretch" - ToolTip.Tip="Auto flip layer preview image (This can slow down the layer preview) [CTRL+F]" - Margin="0,0,1,0" - Text="Flip ⮟" - Spacing="5" - Icon="mdi-flip-horizontal"> - <uc:ToggleButtonWithIcon.ContextMenu> - <ContextMenu PlacementMode="Bottom"> - <CheckBox - IsChecked="{Binding ShowLayerImageFlippedHorizontally}" - Content="Horizontally"/> - <CheckBox - IsChecked="{Binding ShowLayerImageFlippedVertically}" - Content="Vertically"/> - </ContextMenu> - </uc:ToggleButtonWithIcon.ContextMenu> - </uc:ToggleButtonWithIcon> - - - <uc:ToggleButtonWithIcon - IsChecked="{Binding ShowLayerImageDifference}" - ToolTip.Tip="Show layer differences where darker pixels were also present on previous layer and the white pixels the difference between previous and current layer." - VerticalAlignment="Stretch" - Margin="0,0,1,0" - Text="Difference ⮟" - Spacing="5" - Icon="fas fa-layer-group"> - <uc:ToggleButtonWithIcon.ContextMenu> - <ContextMenu PlacementMode="Bottom"> - <CheckBox - Content="Show layer similarity instead of difference" - ToolTip.Tip="If enabled, it will recolor the current layer pixels in common with the previous and next layer" - IsChecked="{Binding Settings.LayerPreview.LayerDifferenceHighlightSimilarityInstead}"/> - </ContextMenu> - </uc:ToggleButtonWithIcon.ContextMenu> - </uc:ToggleButtonWithIcon> - - <uc:ToggleButtonWithIcon - IsChecked="{Binding ShowLayerImageIssues}" - ToolTip.Tip="Highlight Issues on current layer. Valid only if Issues are calculated." - VerticalAlignment="Stretch" - Margin="0,0,1,0" - Text="Issues ⮟" - Spacing="5" - Icon="fas fa-radiation-alt"> - <Button.ContextMenu> - <ContextMenu PlacementMode="Bottom"> - <CheckBox - ToolTip.Tip="Show crosshairs for selected issues on the current layer" - IsChecked="{Binding ShowLayerImageCrosshairs}"> - <StackPanel Orientation="Horizontal"> - <i:Icon Value="fas fa-crosshairs"/> - <TextBlock Margin="5,0,5,0" Text="Crosshairs"/> - </StackPanel> - </CheckBox> - - </ContextMenu> - </Button.ContextMenu> - </uc:ToggleButtonWithIcon> - - - <uc:ButtonWithIcon Name="LayerPreviewOutlineButton" - ToolTip.Tip="Click to access the various outlines." - Command="{Binding OpenContextMenu}" - CommandParameter="LayerPreviewOutline" - VerticalAlignment="Stretch" - Margin="1,0,0,0" - Text="Outline ⮟" - Spacing="5" - Icon="fas fa-vector-square"> - <uc:ButtonWithIcon.ContextMenu> - <ContextMenu Name="LayerPreviewOutlineContextMenu" PlacementMode="Bottom"> - <CheckBox - IsChecked="{Binding ShowLayerOutlinePrintVolumeBoundary}" - Content="Print volume boundary"/> - <CheckBox - IsChecked="{Binding ShowLayerOutlineLayerBoundary}" - Content="Layer boundary"/> - <CheckBox - IsChecked="{Binding ShowLayerOutlineContourBoundary}" - Content="Blob boundary"/> - <CheckBox - IsChecked="{Binding ShowLayerOutlineHollowAreas}" - Content="Hollow areas"/> - <CheckBox - IsChecked="{Binding ShowLayerOutlineCentroids}" - Content="Centroids"/> - <CheckBox - IsChecked="{Binding ShowLayerOutlineEdgeDetection}" - Content="Edge detection"> - <CheckBox.IsEnabled> - <MultiBinding Converter="{x:Static BoolConverters.And}"> - <Binding Path="!ShowLayerOutlineDistanceDetection"/> - <Binding Path="!ShowLayerOutlineSkeletonize"/> - </MultiBinding> - </CheckBox.IsEnabled> - </CheckBox> - <CheckBox - IsChecked="{Binding ShowLayerOutlineDistanceDetection}" - ToolTip.Tip="Calculates the distance to the closest zero pixel for each pixel" - Content="Distance detection"> - <CheckBox.IsEnabled> - <MultiBinding Converter="{x:Static BoolConverters.And}"> - <Binding Path="!ShowLayerOutlineEdgeDetection"/> - <Binding Path="!ShowLayerOutlineSkeletonize"/> - </MultiBinding> - </CheckBox.IsEnabled> - </CheckBox> - <CheckBox - IsChecked="{Binding ShowLayerOutlineSkeletonize}" - Content="Skeletonize"> - <CheckBox.IsEnabled> - <MultiBinding Converter="{x:Static BoolConverters.And}"> - <Binding Path="!ShowLayerOutlineEdgeDetection"/> - <Binding Path="!ShowLayerOutlineDistanceDetection"/> - </MultiBinding> - </CheckBox.IsEnabled> - </CheckBox> - </ContextMenu> - </uc:ButtonWithIcon.ContextMenu> - </uc:ButtonWithIcon> - - - <uc:ToggleButtonWithIcon HorizontalAlignment="Right" - IsChecked="{Binding IsPixelEditorActive}" - ToolTip.Tip="Edit layer image: Draw pixels, add supports and/or drain holes." - VerticalAlignment="Stretch" - Margin="0,0,0,0" - Text="Pixel editor" - Spacing="5" - Icon="fas fa-drafting-compass"/> - - </WrapPanel> - - - <StackPanel HorizontalAlignment="Right" Grid.Row="0" Grid.Column="1" Orientation="Horizontal"> - <uc:ButtonWithIcon Name="LayerActionsButton" - Command="{Binding OpenContextMenu}" - VerticalAlignment="Stretch" - VerticalContentAlignment="Center" - CommandParameter="LayerActions" - Text="Actions ⮟" - Spacing="5" - Icon="mdi-layers-edit"> - <uc:ButtonWithIcon.ContextMenu> - <ContextMenu Name="LayerActionsContextMenu" PlacementMode="Bottom" Items="{Binding LayerActionsMenu}"/> - </uc:ButtonWithIcon.ContextMenu> - </uc:ButtonWithIcon> - - <Button - Command="{Binding ShowLayer}" - HotKey="F5" - ToolTip.Tip="Refresh current layer [F5]" - VerticalAlignment="Stretch" - Margin="1,0,0,0" - i:Attached.Icon="fas fa-sync-alt"/> - - <uc:ButtonWithIcon - Command="{Binding SaveCurrentLayerImage}" - ToolTip.Tip="Save layer image to a file" - VerticalAlignment="Stretch" - VerticalContentAlignment="Center" - Margin="1,0,0,0" - Text="⮟" - Spacing="3" - Icon="fas fa-save"> - <uc:ButtonWithIcon.ContextMenu> - <ContextMenu PlacementMode="Bottom"> - <MenuItem - Command="{Binding SaveCurrentROIImage}" - Header="Save the selected region (ROI)" - i:MenuItem.Icon="far fa-object-group"/> - </ContextMenu> - </uc:ButtonWithIcon.ContextMenu> - </uc:ButtonWithIcon> - - - </StackPanel> - </Grid> - - <Border - Grid.Row="1" - BorderBrush="{DynamicResource LightBackground}" - BorderThickness="5"> - <uvtava:AdvancedImageBox - ShowGrid="{Binding Settings.LayerPreview.ShowBackgroudGrid}" - GridCellSize="15" - GridColor="{DynamicResource AdvancedImageBoxGridColor}" - GridColorAlternate="{DynamicResource AdvancedImageBoxGridAlternateColor}" - Name="LayerImage"/> - </Border> - - - <Canvas Grid.Row="1" Margin="20" - IsVisible="{Binding IsTooltipOverlayVisible}"> - <Border BorderThickness="1" - BorderBrush="Black" - Background="{Binding Settings.LayerPreview.TooltipOverlayBackgroundBrush}" - Padding="10" - CornerRadius="5"> - <TextBlock Text="{Binding TooltipOverlayText}" Foreground="Black" /> - </Border> - </Canvas> - - - - <Grid - IsEnabled="{Binding IsFileLoaded}" - Grid.Row="2" - ColumnDefinitions="4*,2*" RowDefinitions="Auto" Margin="5"> - <WrapPanel Orientation="Horizontal"> - <StackPanel - ToolTip.Tip="Number of pixels to cure on this layer image and the percentage of them against total lcd pixels and the total cured millimeters." - VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> - <i:Icon Value="mdi-google-downasaur"/> - <TextBlock Text="{Binding LayerPixelCountStr}"/> - </StackPanel> - - <uc:ButtonWithIcon - ToolTip.Tip="Object volume bounds for current layer, position and size in pixels and millimeters. + <Grid IsEnabled="{Binding IsFileLoaded}" + ColumnDefinitions="*" RowDefinitions="Auto,*,Auto" Margin="5"> + <Grid Grid.Row="0" Grid.Column="0" + IsEnabled="{Binding IsFileLoaded}" + ColumnDefinitions="*,Auto" RowDefinitions="Auto" Margin="0,5,0,5"> + <WrapPanel Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" Orientation="Horizontal"> + <uc:ToggleButtonWithIcon + IsChecked="{Binding ShowLayerImageRotated}" + HotKey="Ctrl + R" + VerticalAlignment="Stretch" + ToolTip.Tip="Auto rotate layer preview image at 90º (This can slow down the layer preview) [CTRL+R]" + Margin="0,0,1,0" + Text="Rotate ⮟" + Spacing="5" + Icon="fas fa-sync-alt"> + <uc:ToggleButtonWithIcon.ContextMenu> + <ContextMenu PlacementMode="Bottom"> + <RadioButton + GroupName="ShowLayerImageRotateDirection" + IsChecked="{Binding ShowLayerImageRotateCWDirection}" + Content="90º Clockwise (CW)"/> + <RadioButton + GroupName="ShowLayerImageRotateDirection" + IsChecked="{Binding ShowLayerImageRotateCCWDirection}" + Content="90º Counter-clockwise (CCW)"/> + </ContextMenu> + </uc:ToggleButtonWithIcon.ContextMenu> + </uc:ToggleButtonWithIcon> + + + <uc:ToggleButtonWithIcon + IsChecked="{Binding ShowLayerImageFlipped}" + HotKey="Ctrl + F" + VerticalAlignment="Stretch" + ToolTip.Tip="Auto flip layer preview image (This can slow down the layer preview) [CTRL+F]" + Margin="0,0,1,0" + Text="Flip ⮟" + Spacing="5" + Icon="mdi-flip-horizontal"> + <uc:ToggleButtonWithIcon.ContextMenu> + <ContextMenu PlacementMode="Bottom"> + <CheckBox + IsChecked="{Binding ShowLayerImageFlippedHorizontally}" + Content="Horizontally"/> + <CheckBox + IsChecked="{Binding ShowLayerImageFlippedVertically}" + Content="Vertically"/> + </ContextMenu> + </uc:ToggleButtonWithIcon.ContextMenu> + </uc:ToggleButtonWithIcon> + + + <uc:ToggleButtonWithIcon + IsChecked="{Binding ShowLayerImageDifference}" + ToolTip.Tip="Show layer differences where darker pixels were also present on previous layer and the white pixels the difference between previous and current layer." + VerticalAlignment="Stretch" + Margin="0,0,1,0" + Text="Difference ⮟" + Spacing="5" + Icon="fas fa-layer-group"> + <uc:ToggleButtonWithIcon.ContextMenu> + <ContextMenu PlacementMode="Bottom"> + <CheckBox + Content="Show layer similarity instead of difference" + ToolTip.Tip="If enabled, it will recolor the current layer pixels in common with the previous and next layer" + IsChecked="{Binding Settings.LayerPreview.LayerDifferenceHighlightSimilarityInstead}"/> + </ContextMenu> + </uc:ToggleButtonWithIcon.ContextMenu> + </uc:ToggleButtonWithIcon> + + <uc:ToggleButtonWithIcon + IsChecked="{Binding ShowLayerImageIssues}" + ToolTip.Tip="Highlight Issues on current layer. Valid only if Issues are calculated." + VerticalAlignment="Stretch" + Margin="0,0,1,0" + Text="Issues ⮟" + Spacing="5" + Icon="fas fa-radiation-alt"> + <Button.ContextMenu> + <ContextMenu PlacementMode="Bottom"> + <CheckBox + ToolTip.Tip="Show crosshairs for selected issues on the current layer" + IsChecked="{Binding ShowLayerImageCrosshairs}"> + <StackPanel Orientation="Horizontal"> + <i:Icon Value="fas fa-crosshairs"/> + <TextBlock Margin="5,0,5,0" Text="Crosshairs"/> + </StackPanel> + </CheckBox> + + </ContextMenu> + </Button.ContextMenu> + </uc:ToggleButtonWithIcon> + + + <uc:ButtonWithIcon Name="LayerPreviewOutlineButton" + ToolTip.Tip="Click to access the various outlines." + Command="{Binding OpenContextMenu}" + CommandParameter="LayerPreviewOutline" + VerticalAlignment="Stretch" + Margin="1,0,0,0" + Text="Outline ⮟" + Spacing="5" + Icon="fas fa-vector-square"> + <uc:ButtonWithIcon.ContextMenu> + <ContextMenu Name="LayerPreviewOutlineContextMenu" PlacementMode="Bottom"> + <CheckBox + IsChecked="{Binding ShowLayerOutlinePrintVolumeBoundary}" + Content="Print volume boundary"/> + <CheckBox + IsChecked="{Binding ShowLayerOutlineLayerBoundary}" + Content="Layer boundary"/> + <CheckBox + IsChecked="{Binding ShowLayerOutlineContourBoundary}" + Content="Blob boundary"/> + <CheckBox + IsChecked="{Binding ShowLayerOutlineHollowAreas}" + Content="Hollow areas"/> + <CheckBox + IsChecked="{Binding ShowLayerOutlineCentroids}" + Content="Centroids"/> + <CheckBox + IsChecked="{Binding ShowLayerOutlineEdgeDetection}" + Content="Edge detection"> + <CheckBox.IsEnabled> + <MultiBinding Converter="{x:Static BoolConverters.And}"> + <Binding Path="!ShowLayerOutlineDistanceDetection"/> + <Binding Path="!ShowLayerOutlineSkeletonize"/> + </MultiBinding> + </CheckBox.IsEnabled> + </CheckBox> + <CheckBox + IsChecked="{Binding ShowLayerOutlineDistanceDetection}" + ToolTip.Tip="Calculates the distance to the closest zero pixel for each pixel" + Content="Distance detection"> + <CheckBox.IsEnabled> + <MultiBinding Converter="{x:Static BoolConverters.And}"> + <Binding Path="!ShowLayerOutlineEdgeDetection"/> + <Binding Path="!ShowLayerOutlineSkeletonize"/> + </MultiBinding> + </CheckBox.IsEnabled> + </CheckBox> + <CheckBox + IsChecked="{Binding ShowLayerOutlineSkeletonize}" + Content="Skeletonize"> + <CheckBox.IsEnabled> + <MultiBinding Converter="{x:Static BoolConverters.And}"> + <Binding Path="!ShowLayerOutlineEdgeDetection"/> + <Binding Path="!ShowLayerOutlineDistanceDetection"/> + </MultiBinding> + </CheckBox.IsEnabled> + </CheckBox> + </ContextMenu> + </uc:ButtonWithIcon.ContextMenu> + </uc:ButtonWithIcon> + + + <uc:ToggleButtonWithIcon HorizontalAlignment="Right" + IsChecked="{Binding IsPixelEditorActive}" + ToolTip.Tip="Edit layer image: Draw pixels, add supports and/or drain holes." + VerticalAlignment="Stretch" + Margin="0,0,0,0" + Text="Pixel editor" + Spacing="5" + Icon="fas fa-drafting-compass"/> + </WrapPanel> + + + <StackPanel Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal"> + <uc:ButtonWithIcon Name="LayerActionsButton" + Command="{Binding OpenContextMenu}" + VerticalAlignment="Stretch" + VerticalContentAlignment="Center" + CommandParameter="LayerActions" + Text="Actions ⮟" + Spacing="5" + Icon="mdi-layers-edit"> + <uc:ButtonWithIcon.ContextMenu> + <ContextMenu Name="LayerActionsContextMenu" PlacementMode="Bottom" Items="{Binding LayerActionsMenu}"/> + </uc:ButtonWithIcon.ContextMenu> + </uc:ButtonWithIcon> + + <Button + Command="{Binding ShowLayer}" + HotKey="F5" + ToolTip.Tip="Refresh current layer [F5]" + VerticalAlignment="Stretch" + Margin="1,0,0,0" + i:Attached.Icon="fas fa-sync-alt"/> + + <uc:ButtonWithIcon + Command="{Binding SaveCurrentLayerImage}" + ToolTip.Tip="Save layer image to a file" + VerticalAlignment="Stretch" + VerticalContentAlignment="Center" + Margin="1,0,0,0" + Text="⮟" + Spacing="3" + Icon="fas fa-save"> + <uc:ButtonWithIcon.ContextMenu> + <ContextMenu PlacementMode="Bottom"> + <MenuItem + Command="{Binding SaveCurrentROIImage}" + Header="Save the selected region (ROI)" + i:MenuItem.Icon="far fa-object-group"/> + </ContextMenu> + </uc:ButtonWithIcon.ContextMenu> + </uc:ButtonWithIcon> + + + </StackPanel> + </Grid> + + <Border + Grid.Row="1" + BorderBrush="{DynamicResource LightBackground}" + BorderThickness="5"> + <uvtava:AdvancedImageBox + ShowGrid="{Binding Settings.LayerPreview.ShowBackgroudGrid}" + GridCellSize="15" + GridColor="{DynamicResource AdvancedImageBoxGridColor}" + GridColorAlternate="{DynamicResource AdvancedImageBoxGridAlternateColor}" + Name="LayerImage"/> + </Border> + + + <Canvas Grid.Row="1" Margin="20" + IsVisible="{Binding IsTooltipOverlayVisible}"> + <Border BorderThickness="1" + BorderBrush="Black" + Background="{Binding Settings.LayerPreview.TooltipOverlayBackgroundBrush}" + Padding="10" + CornerRadius="5"> + <TextBlock Text="{Binding TooltipOverlayText}" Foreground="Black" /> + </Border> + </Canvas> + + + + <Grid + IsEnabled="{Binding IsFileLoaded}" + Grid.Row="2" + ColumnDefinitions="4*,2*" RowDefinitions="Auto" Margin="5"> + <WrapPanel Orientation="Horizontal"> + <StackPanel + ToolTip.Tip="Number of pixels to cure on this layer image and the percentage of them against total lcd pixels and the total cured millimeters." + VerticalAlignment="Center" Orientation="Horizontal" Spacing="5"> + <i:Icon Value="mdi-google-downasaur"/> + <TextBlock Text="{Binding LayerPixelCountStr}"/> + </StackPanel> + + <uc:ButtonWithIcon + ToolTip.Tip="Object volume bounds for current layer, position and size in pixels and millimeters. 
Click: go to region" - Command="{Binding ZoomToFitPrintVolume}" - Margin="5,0,0,0" - Text="{Binding LayerBoundsStr}" - Spacing="5" - Icon="fas fa-expand"/> - - <uc:ButtonWithIcon - IsEnabled="{Binding IsFileLoaded}" - Margin="2,0,0,0" - Command="{Binding OnROIClick}" - ToolTip.Tip="Region of interest selection over layer. + Command="{Binding ZoomToFitPrintVolume}" + Margin="5,0,0,0" + Text="{Binding LayerBoundsStr}" + Spacing="5" + Icon="fas fa-expand"/> + + <uc:ButtonWithIcon + IsEnabled="{Binding IsFileLoaded}" + Margin="2,0,0,0" + Command="{Binding OnROIClick}" + ToolTip.Tip="Region of interest selection over layer. 
(NS): Not selected 
Click: go to region 
ESC: Clear ROI & Masks | ESC + Shift: Clear ROI" - Text="{Binding LayerROIStr}" - Spacing="5" - Icon="far fa-object-group"> - <uc:ButtonWithIcon.ContextMenu> - <ContextMenu Name="ROIContextMenu" PlacementMode="Top"> - <MenuItem - Command="{Binding SelectLayerPositiveAreasMask}" - Header="Mask: Select layer positive areas"/> - <MenuItem - Command="{Binding SelectLayerHollowAreasMask}" - Header="Mask: Select layer hollow areas"/> - <Separator/> - <MenuItem - Command="{Binding SelectModelVolumeRoi}" - Header="ROI: Select model volume"/> - <MenuItem - Command="{Binding SelectLayerVolumeRoi}" - Header="ROI: Select layer volume"/> - <Separator/> - <MenuItem - Command="{Binding ClearMask}" - Header="Clear Mask"/> - <MenuItem - Command="{Binding ClearROI}" - Header="Clear ROI"/> - </ContextMenu> - </uc:ButtonWithIcon.ContextMenu> - </uc:ButtonWithIcon> - - </WrapPanel> - - <WrapPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Horizontal"> - <uc:ButtonWithIcon - IsEnabled="{Binding LayerPixelPicker.IsSet}" - Command="{Binding OnLayerPixelPickerClicked}" - ToolTip.Tip="Pixel picker: + Text="{Binding LayerROIStr}" + Spacing="5" + Icon="far fa-object-group"> + <uc:ButtonWithIcon.ContextMenu> + <ContextMenu Name="ROIContextMenu" PlacementMode="Top"> + <MenuItem + Command="{Binding SelectLayerPositiveAreasMask}" + Header="Mask: Select layer positive areas"/> + <MenuItem + Command="{Binding SelectLayerHollowAreasMask}" + Header="Mask: Select layer hollow areas"/> + <Separator/> + <MenuItem + Command="{Binding SelectModelVolumeRoi}" + Header="ROI: Select model volume"/> + <MenuItem + Command="{Binding SelectLayerVolumeRoi}" + Header="ROI: Select layer volume"/> + <Separator/> + <MenuItem + Command="{Binding ClearMask}" + Header="Clear Mask"/> + <MenuItem + Command="{Binding ClearROI}" + Header="Clear ROI"/> + </ContextMenu> + </uc:ButtonWithIcon.ContextMenu> + </uc:ButtonWithIcon> + + </WrapPanel> + + <WrapPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Horizontal"> + <uc:ButtonWithIcon + IsEnabled="{Binding LayerPixelPicker.IsSet}" + Command="{Binding OnLayerPixelPickerClicked}" + ToolTip.Tip="Pixel picker: 
Use CONTROL and over a pixel to get his position and brightness. 
Click: Center at position" - Text="{Binding LayerPixelPicker}" - Spacing="5" - Icon="fas fa-map-marker-alt"/> + Text="{Binding LayerPixelPicker}" + Spacing="5" + Icon="fas fa-map-marker-alt"/> - <uc:ButtonWithIcon - ToolTip.Tip="Layer image zoom level, use mouse scroll to zoom in/out into image. + <uc:ButtonWithIcon + ToolTip.Tip="Layer image zoom level, use mouse scroll to zoom in/out into image. 
Click to set zoom to 100% 
Shift+Click to set zoom to defined value 
Ctrl + 0 OR double right click to scale to fit" - Margin="2,0,0,0" - Command="{Binding ZoomToNormal}" - Text="{Binding LayerZoomStr}" - Spacing="5" - Icon="fas fa-search-plus"/> - - <uc:ButtonWithIcon - ToolTip.Tip="Layer Resolution and display size. + Margin="2,0,0,0" + Command="{Binding ZoomToNormal}" + Text="{Binding LayerZoomStr}" + Spacing="5" + Icon="fas fa-search-plus"/> + + <uc:ButtonWithIcon + ToolTip.Tip="Layer Resolution and display size. 
Click: Zoom to fit" - Command="{Binding ZoomToFitSimple}" - Margin="2,0,0,0" - Text="{Binding LayerResolutionStr}" - Spacing="5" - Icon="fas fa-expand"/> + Command="{Binding ZoomToFitSimple}" + Margin="2,0,0,0" + Text="{Binding LayerResolutionStr}" + Spacing="5" + Icon="fas fa-expand"/> - <TextBlock - ToolTip.Tip="Layer preview computation time." - Margin="5,0,0,0" - VerticalAlignment="Center" Text="{Binding ShowLayerRenderMs, StringFormat=\{0\}ms}"/> - </WrapPanel> - </Grid> - </Grid> + <TextBlock + ToolTip.Tip="Layer preview computation time." + Margin="5,0,0,0" + VerticalAlignment="Center" Text="{Binding ShowLayerRenderMs, StringFormat=\{0\}ms}"/> + </WrapPanel> + </Grid> + </Grid> - </DockPanel> - <Grid Grid.Row="0" Grid.Column="0" + </DockPanel> + <Grid Grid.Row="0" Grid.Column="0" RowDefinitions="*,Auto,*" ColumnDefinitions="*,Auto,*" IsEnabled="{Binding IsProgressVisible}" IsVisible="{Binding IsProgressVisible}"> - <Border Grid.Row="1" Grid.Column="1" MinWidth="450" + <Border Grid.Row="1" Grid.Column="1" MinWidth="450" Classes="ProgressLoading" BorderThickness="5" CornerRadius="5"> - <Grid RowDefinitions="Auto,Auto,Auto,Auto" + <Grid RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="*"> - <TextBlock + <TextBlock Grid.Row="0" Margin="10" Text="{Binding Progress.Title}"/> - <TextBlock + <TextBlock Grid.Row="1" Margin="10,0,10,10" Text="{Binding Progress.ElapsedTimeStr, StringFormat=Elapsed Time: \{0\}}"/> - <TextBlock + <TextBlock Grid.Row="2" Margin="10,0,10,10" Text="{Binding Progress.Description}" HorizontalAlignment="Center"/> - <Grid + <Grid Grid.Row="3" RowDefinitions="30" ColumnDefinitions="*,100"> - <ProgressBar + <ProgressBar Grid.Column="0" Minimum="0" Maximum="100" VerticalAlignment="Stretch" IsIndeterminate="{Binding Progress.IsIndeterminate}" Value="{Binding Progress.ProgressPercent}" ShowProgressText="True"/> - <Button + <Button IsEnabled="{Binding Progress.CanCancel}" Command="{Binding ProgressOnClickCancel}" Grid.Column="1" @@ -2122,9 +2120,9 @@ VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Content="Cancel"/> - </Grid> - </Grid> - </Border> - </Grid> - </Grid> + </Grid> + </Grid> + </Border> + </Grid> + </Grid> </uc:WindowEx> diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs index 27672a8..0fb088d 100644 --- a/UVtools.WPF/Program.cs +++ b/UVtools.WPF/Program.cs @@ -38,42 +38,9 @@ public static class Program return; } - //var mat = EmguExtensions.InitMat(new System.Drawing.Size(2000, 1080)); - /*const byte z = 1; - int pixel = 1000; - - for (int y = 0; y < mat.Height; y+=200) - for (int x = 0; x < mat.Width; x+=1) - { - float x1 = x / (float)mat.Width; - float y1 = y / (float)mat.Height; - - //var result = Math.Sin(x1) * Math.Cos(y1) + Math.Sin(y1) * Math.Cos(1) + Math.Sin(1) * Math.Cos(x1); - //mat.SetByte((int) result* mat.Width, 255); - - //CvInvoke.Circle(mat, new Point(x, (int)pixelY + y), 1, EmguExtensions.WhiteColor, -1, LineType.AntiAlias); - }*/ - - - /*var sineHeight = 100; - var sineWidth = 100; - byte radius = 10; - - for (int y1 = 0; y1 < mat.Height; y1 += sineHeight) - for (int x = 0; x < mat.Width; x++) - { - int y2 = (int)(Math.Sin((double)x / sineWidth) * sineHeight / 2.0 + sineHeight / 2.0 + radius); - - CvInvoke.Circle(mat, new Point(x, y1+y2), radius, EmguExtensions.WhiteColor, -1, LineType.AntiAlias); - } - - CvInvoke.Imshow("gyroid", mat); - CvInvoke.WaitKey(); - return;*/ - /*Slicer slicer = new(Size.Empty, SizeF.Empty, "D:\\Cube100x100x100.stl"); var slices = slicer.SliceModel(0.05f); - + foreach (var slice in slices) { using var mat = EmguExtensions.InitMat(new Size(1000, 1000)); diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 40f70d1..b41492a 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>3.2.0</Version> + <Version>3.2.1</Version> <Platforms>AnyCPU;x64</Platforms> <PackageIcon>UVtools.png</PackageIcon> <PackageReadmeFile>README.md</PackageReadmeFile> @@ -44,9 +44,9 @@ <PackageReference Include="Avalonia.Desktop" Version="0.10.13" /> <PackageReference Include="Avalonia.Diagnostics" Version="0.10.13" /> <PackageReference Include="MessageBox.Avalonia" Version="2.0.0" /> - <PackageReference Include="Projektanker.Icons.Avalonia" Version="4.2.1" /> - <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="4.2.1" /> - <PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="4.2.1" /> + <PackageReference Include="Projektanker.Icons.Avalonia" Version="4.3.0" /> + <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="4.3.0" /> + <PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="4.3.0" /> <PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.12" /> </ItemGroup> <ItemGroup> diff --git a/build/createRelease.ps1 b/build/createRelease.ps1 index 40efdd9..b670883 100644 --- a/build/createRelease.ps1 +++ b/build/createRelease.ps1 @@ -202,6 +202,7 @@ $releaseFolder = "$project\bin\$buildWith\net$netVersion" $objFolder = "$project\obj\$buildWith\net$netVersion" $publishFolder = "publish" $platformsFolder = "$buildFolder\platforms" +$changelogFile = "$rootFolder\CHANGELOG.md" #$version = (Get-Command "$releaseFolder\UVtools.dll").FileVersionInfo.ProductVersion $projectXml = [Xml] (Get-Content "$project\$project.csproj") @@ -277,6 +278,27 @@ $runtimes = } } +# Set release notes on projects +$changelog = Get-Content -Path "$changelogFile" +$foundHashTag = $false +$sb = [System.Text.StringBuilder]::new() +foreach($line in $changelog) { + $line = $line.TrimEnd() + if($line -eq '') { continue } + if($line.StartsWith("##")) { + if(!$foundHashTag) + { + $foundHashTag = $true + continue + } + else { break } + } + elseif($foundHashTag){ + [void]$sb.AppendLine($line) + } +} +Write-Host $sb.ToString() + if($null -ne $enableNugetPublish -and $enableNugetPublish) { @@ -388,7 +410,8 @@ if($null -ne $enableMSI -and $enableMSI) { $deployStopWatch.Restart() $runtime = 'win-x64' - if (Test-Path -Path $msiSourceFiles) { + + if ((Test-Path -Path $msiSourceFiles) -and ((Get-ChildItem "$msiSourceFiles" | Measure-Object).Count) -gt 0) { $msiTargetFile = "$publishFolder\${software}_${runtime}_v$version.msi" Write-Output "################################" Write-Output "Clean and build MSI components manifest file" |