Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2021-04-30 20:20:58 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2021-04-30 20:20:58 +0300
commit6d6be66ced23339f8d30f4aed2d32b83d14384af (patch)
tree80ba0c9891eabda762fc896f7d9fa74972535f07 /UVtools.Core
parent6cfedea4cc36200caab87301e0dd121e97ddc8f2 (diff)
v2.9.2v2.9.2
- (Upgrade) AvaloniaUI from 0.10 to 0.10.2 - (Remove) Unused assemblies - **Issues** - Improve the performance when loading big lists of issues into the DataGrid - Auto refresh issues on the vertical highlight tracker once cath a modification on the Issues list - **Layer preview - Difference:** - Layer difference will now only check the pixels inside the union of previous, current and next layer bounding rectangle, increasing the performance and speed - Previous and next layer pixels if both exists was not showing with the configured color and using the next layer color instead - Respect Anti-Aliasing pixels and fade colors accordingly - Unlock the possiblity of using the layer difference on first and last layer - Add a option to show similar pixels instead of the difference - Change previous default color from (255, 0, 255) to (81, 131, 82) for better depth preception - Change next default color from (0, 255, 255) to (81, 249, 252) for better depth preception - Change previous & next default color from (255, 0, 0) to (246, 240, 216) for better depth preception - **(Fix) Pixel editor:** - Modification was append instead of prepend on the list - Modification was not updating the index number on the list - (Fix) PrusaSlicer printer: Bene4 Mono screen, bed and height size
Diffstat (limited to 'UVtools.Core')
-rw-r--r--UVtools.Core/Extensions/PathExtensions.cs8
-rw-r--r--UVtools.Core/Extensions/StreamExtensions.cs8
-rw-r--r--UVtools.Core/Extensions/StringExtensions.cs4
-rw-r--r--UVtools.Core/Extensions/ZipArchiveExtensions.cs144
-rw-r--r--UVtools.Core/FileFormats/SL1File.cs4
-rw-r--r--UVtools.Core/Managers/ClipboardManager.cs2
-rw-r--r--UVtools.Core/Managers/MaterialManager.cs17
-rw-r--r--UVtools.Core/Objects/RangeObservableCollection.cs703
-rw-r--r--UVtools.Core/Operations/OperationCalibrateExposureFinder.cs13
-rw-r--r--UVtools.Core/Operations/OperationDynamicLayerHeight.cs10
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs45
-rw-r--r--UVtools.Core/UVtools.Core.csproj6
12 files changed, 824 insertions, 140 deletions
diff --git a/UVtools.Core/Extensions/PathExtensions.cs b/UVtools.Core/Extensions/PathExtensions.cs
index e40290b..1b3be72 100644
--- a/UVtools.Core/Extensions/PathExtensions.cs
+++ b/UVtools.Core/Extensions/PathExtensions.cs
@@ -29,11 +29,9 @@ namespace UVtools.Core.Extensions
foreach (var extension in extensions)
{
var dotExtension = $".{extension}";
- if (path.EndsWith(dotExtension))
- {
- strippedExtension = extension;
- return path.Remove(path.Length - dotExtension.Length);
- }
+ if (!path.EndsWith(dotExtension)) continue;
+ strippedExtension = extension;
+ return path.Remove(path.Length - dotExtension.Length);
}
return path;
diff --git a/UVtools.Core/Extensions/StreamExtensions.cs b/UVtools.Core/Extensions/StreamExtensions.cs
index f85f917..c1404e0 100644
--- a/UVtools.Core/Extensions/StreamExtensions.cs
+++ b/UVtools.Core/Extensions/StreamExtensions.cs
@@ -19,11 +19,9 @@ namespace UVtools.Core.Extensions
/// <returns>Byte array data</returns>
public static byte[] ToArray(this Stream stream)
{
- using (var memoryStream = new MemoryStream())
- {
- stream.CopyTo(memoryStream);
- return memoryStream.ToArray();
- }
+ using var memoryStream = new MemoryStream();
+ stream.CopyTo(memoryStream);
+ return memoryStream.ToArray();
}
}
}
diff --git a/UVtools.Core/Extensions/StringExtensions.cs b/UVtools.Core/Extensions/StringExtensions.cs
index 5f4a77b..9967c66 100644
--- a/UVtools.Core/Extensions/StringExtensions.cs
+++ b/UVtools.Core/Extensions/StringExtensions.cs
@@ -31,7 +31,7 @@ namespace UVtools.Core.Extensions
{
case null: throw new ArgumentNullException(nameof(input));
case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
- default: return input.First().ToString().ToUpper() + input.Substring(1);
+ default: return input.First().ToString().ToUpper() + input[1..];
}
}
@@ -47,7 +47,7 @@ namespace UVtools.Core.Extensions
public static T Convert<T>(this string input)
{
var converter = TypeDescriptor.GetConverter(typeof(T));
- if (converter != null)
+ if (converter is not null)
{
//Cast ConvertFromString(string text) : object to (T)
return (T)converter.ConvertFromString(input);
diff --git a/UVtools.Core/Extensions/ZipArchiveExtensions.cs b/UVtools.Core/Extensions/ZipArchiveExtensions.cs
index 6be91bc..5d8924a 100644
--- a/UVtools.Core/Extensions/ZipArchiveExtensions.cs
+++ b/UVtools.Core/Extensions/ZipArchiveExtensions.cs
@@ -58,10 +58,8 @@ namespace UVtools.Core.Extensions
public static void ImprovedExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
{
//Opens the zip file up to be read
- using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
- {
- archive.ImprovedExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, overwriteMethod);
- }
+ using var archive = ZipFile.OpenRead(sourceArchiveFileName);
+ archive.ImprovedExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, overwriteMethod);
}
/// <summary>
@@ -82,7 +80,7 @@ namespace UVtools.Core.Extensions
public static void ImprovedExtractToDirectory(this ZipArchive archive, string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
{
//Loops through each file in the zip file
- foreach (ZipArchiveEntry file in archive.Entries)
+ foreach (var file in archive.Entries)
{
file.ImprovedExtractToFile(destinationDirectoryName, overwriteMethod);
}
@@ -207,73 +205,71 @@ namespace UVtools.Core.Extensions
}
//Opens the zip file in the mode we specified
- using (ZipArchive zipFile = ZipFile.Open(archiveFullName, mode))
+ using ZipArchive zipFile = ZipFile.Open(archiveFullName, mode);
+ //This is a bit of a hack and should be refactored - I am
+ //doing a similar foreach loop for both modes, but for Create
+ //I am doing very little work while Update gets a lot of
+ //code. This also does not handle any other mode (of
+ //which there currently wouldn't be one since we don't
+ //use Read here).
+ if (mode == ZipArchiveMode.Create)
{
- //This is a bit of a hack and should be refactored - I am
- //doing a similar foreach loop for both modes, but for Create
- //I am doing very little work while Update gets a lot of
- //code. This also does not handle any other mode (of
- //which there currently wouldn't be one since we don't
- //use Read here).
- if (mode == ZipArchiveMode.Create)
+ foreach (string file in files)
{
- foreach (string file in files)
- {
- //Adds the file to the archive
- zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
- }
+ //Adds the file to the archive
+ zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
}
- else
+ }
+ else
+ {
+ foreach (string file in files)
{
- foreach (string file in files)
+ var fileInZip = (from f in zipFile.Entries
+ where f.Name == Path.GetFileName(file)
+ select f).FirstOrDefault();
+
+ switch (fileOverwrite)
{
- var fileInZip = (from f in zipFile.Entries
- where f.Name == Path.GetFileName(file)
- select f).FirstOrDefault();
+ case Overwrite.Always:
+ //Deletes the file if it is found
+ if (fileInZip != null)
+ {
+ fileInZip.Delete();
+ }
- switch (fileOverwrite)
- {
- case Overwrite.Always:
- //Deletes the file if it is found
- if (fileInZip != null)
- {
- fileInZip.Delete();
- }
+ //Adds the file to the archive
+ zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
- //Adds the file to the archive
- zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
-
- break;
- case Overwrite.IfNewer:
- //This is a bit trickier - we only delete the file if it is
- //newer, but if it is newer or if the file isn't already in
- //the zip file, we will write it to the zip file
- if (fileInZip != null)
+ break;
+ case Overwrite.IfNewer:
+ //This is a bit trickier - we only delete the file if it is
+ //newer, but if it is newer or if the file isn't already in
+ //the zip file, we will write it to the zip file
+ if (fileInZip != null)
+ {
+ //Deletes the file only if it is older than our file.
+ //Note that the file will be ignored if the existing file
+ //in the archive is newer.
+ if (fileInZip.LastWriteTime < File.GetLastWriteTime(file))
{
- //Deletes the file only if it is older than our file.
- //Note that the file will be ignored if the existing file
- //in the archive is newer.
- if (fileInZip.LastWriteTime < File.GetLastWriteTime(file))
- {
- fileInZip.Delete();
+ fileInZip.Delete();
- //Adds the file to the archive
- zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
- }
- }
- else
- {
- //The file wasn't already in the zip file so add it to the archive
+ //Adds the file to the archive
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
}
- break;
- case Overwrite.Never:
- //Don't do anything - this is a decision that you need to
- //consider, however, since this will mean that no file will
- //be written. You could write a second copy to the zip with
- //the same name (not sure that is wise, however).
- break;
- }
+ }
+ else
+ {
+ //The file wasn't already in the zip file so add it to the archive
+ zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
+ }
+ break;
+ case Overwrite.Never:
+ //Don't do anything - this is a decision that you need to
+ //consider, however, since this will mean that no file will
+ //be written. You could write a second copy to the zip with
+ //the same name (not sure that is wise, however).
+ break;
}
}
}
@@ -311,15 +307,11 @@ namespace UVtools.Core.Extensions
}
if (string.IsNullOrEmpty(content)) return entry;
- using (Stream stream = entry.Open())
- {
- if (mode == ZipArchiveMode.Update) stream.SetLength(0);
- using (TextWriter tw = new StreamWriter(stream))
- {
- tw.Write(content);
- tw.Close();
- }
- }
+ using var stream = entry.Open();
+ if (mode == ZipArchiveMode.Update) stream.SetLength(0);
+ using TextWriter tw = new StreamWriter(stream);
+ tw.Write(content);
+ tw.Close();
return entry;
}
@@ -343,13 +335,11 @@ namespace UVtools.Core.Extensions
entry = input.CreateEntry(filename);
}
- if (ReferenceEquals(content, null)) return entry;
- using (Stream stream = entry.Open())
- {
- if (mode == ZipArchiveMode.Update) stream.SetLength(0);
- stream.Write(content, 0, content.Length);
- stream.Close();
- }
+ if (content is null) return entry;
+ using var stream = entry.Open();
+ if (mode == ZipArchiveMode.Update) stream.SetLength(0);
+ stream.Write(content, 0, content.Length);
+ stream.Close();
return entry;
}
}
diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs
index d17adab..e7ca005 100644
--- a/UVtools.Core/FileFormats/SL1File.cs
+++ b/UVtools.Core/FileFormats/SL1File.cs
@@ -180,7 +180,7 @@ namespace UVtools.Core.FileFormats
public float SupportHeadPenetration { get; set; }
public float SupportHeadWidth { get; set; }
- public byte SupportPillarWideningFactor { set; get; }
+ public ushort SupportPillarWideningFactor { set; get; }
public float SupportPillarDiameter { get; set; }
public string SupportSmallPillarDiameterPercent { get; set; }
public float SupportMaxBridgesOnPillar { get; set; }
@@ -198,7 +198,7 @@ namespace UVtools.Core.FileFormats
public float SupportMaxPillarLinkDistance { get; set; }
- public byte SupportPointsDensityRelative { get; set; }
+ public ushort SupportPointsDensityRelative { get; set; }
public float SupportPointsMinimalDistance { get; set; }
#endregion
diff --git a/UVtools.Core/Managers/ClipboardManager.cs b/UVtools.Core/Managers/ClipboardManager.cs
index 6577f6b..e26dda3 100644
--- a/UVtools.Core/Managers/ClipboardManager.cs
+++ b/UVtools.Core/Managers/ClipboardManager.cs
@@ -70,7 +70,7 @@ namespace UVtools.Core.Managers
{
#region Properties
- public ObservableCollection<ClipboardItem> Items { get; } = new();
+ public RangeObservableCollection<ClipboardItem> Items { get; } = new();
public FileFormat SlicerFile { get; set; }
diff --git a/UVtools.Core/Managers/MaterialManager.cs b/UVtools.Core/Managers/MaterialManager.cs
index 461e7b3..397a633 100644
--- a/UVtools.Core/Managers/MaterialManager.cs
+++ b/UVtools.Core/Managers/MaterialManager.cs
@@ -39,13 +39,13 @@ namespace UVtools.Core.Managers
#region Members
- private ObservableCollection<Material> _materials = new();
+ private RangeObservableCollection<Material> _materials = new();
#endregion
#region Properties
- public ObservableCollection<Material> Materials
+ public RangeObservableCollection<Material> Materials
{
get => _materials;
set => RaiseAndSetIfChanged(ref _materials, value);
@@ -184,6 +184,11 @@ namespace UVtools.Core.Managers
if (save) Save();
}
+ public void RemoveRange(IEnumerable<Material> collection)
+ {
+ _materials.RemoveRange(collection);
+ }
+
public int Count => _materials.Count;
public bool IsReadOnly => false;
public int IndexOf(Material item) => _materials.IndexOf(item);
@@ -266,13 +271,11 @@ namespace UVtools.Core.Managers
}
}
- #endregion
-
public void SortByName()
{
- var materials = _materials.ToList();
- materials.Sort((material, material1) => string.Compare(material.Name, material1.Name, StringComparison.Ordinal));
- Materials = new ObservableCollection<Material>(materials);
+ _materials.Sort((material, material1) => string.Compare(material.Name, material1.Name, StringComparison.Ordinal));
}
+
+ #endregion
}
} \ No newline at end of file
diff --git a/UVtools.Core/Objects/RangeObservableCollection.cs b/UVtools.Core/Objects/RangeObservableCollection.cs
new file mode 100644
index 0000000..2c52e67
--- /dev/null
+++ b/UVtools.Core/Objects/RangeObservableCollection.cs
@@ -0,0 +1,703 @@
+namespace System.Collections.ObjectModel
+{
+ // Licensed to the .NET Foundation under one or more agreements.
+ // The .NET Foundation licenses this file to you under the MIT license.
+ // See the LICENSE file in the project root for more information.
+
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.ComponentModel;
+ using System.Diagnostics;
+ using System.Linq;
+
+ /// <summary>
+ /// Implementation of a dynamic data collection based on generic Collection&lt;T&gt;,
+ /// implementing INotifyCollectionChanged to notify listeners
+ /// when items get added, removed or the whole list is refreshed.
+ /// </summary>
+ public class RangeObservableCollection<T> : ObservableCollection<T>
+ {
+ //------------------------------------------------------
+ //
+ // Private Fields
+ //
+ //------------------------------------------------------
+
+ #region Private Fields
+ [NonSerialized]
+ private DeferredEventsCollection? _deferredEvents;
+ #endregion Private Fields
+
+
+ //------------------------------------------------------
+ //
+ // Constructors
+ //
+ //------------------------------------------------------
+
+ #region Constructors
+ /// <summary>
+ /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity.
+ /// </summary>
+ public RangeObservableCollection() { }
+
+ /// <summary>
+ /// Initializes a new instance of the ObservableCollection class that contains
+ /// elements copied from the specified collection and has sufficient capacity
+ /// to accommodate the number of elements copied.
+ /// </summary>
+ /// <param name="collection">The collection whose elements are copied to the new list.</param>
+ /// <remarks>
+ /// The elements are copied onto the ObservableCollection in the
+ /// same order they are read by the enumerator of the collection.
+ /// </remarks>
+ /// <exception cref="ArgumentNullException"> collection is a null reference </exception>
+ public RangeObservableCollection(IEnumerable<T> collection) : base(collection) { }
+
+ /// <summary>
+ /// Initializes a new instance of the ObservableCollection class
+ /// that contains elements copied from the specified list
+ /// </summary>
+ /// <param name="list">The list whose elements are copied to the new list.</param>
+ /// <remarks>
+ /// The elements are copied onto the ObservableCollection in the
+ /// same order they are read by the enumerator of the list.
+ /// </remarks>
+ /// <exception cref="ArgumentNullException"> list is a null reference </exception>
+ public RangeObservableCollection(List<T> list) : base(list) { }
+
+ #endregion Constructors
+
+ //------------------------------------------------------
+ //
+ // Public Properties
+ //
+ //------------------------------------------------------
+
+ #region Public Properties
+ EqualityComparer<T>? _Comparer;
+ public EqualityComparer<T> Comparer
+ {
+ get => _Comparer ??= EqualityComparer<T>.Default;
+ private set => _Comparer = value;
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this collection acts as a <see cref="HashSet{T}"/>,
+ /// disallowing duplicate items, based on <see cref="Comparer"/>.
+ /// This might indeed consume background performance, but in the other hand,
+ /// it will pay off in UI performance as less required UI updates are required.
+ /// </summary>
+ public bool AllowDuplicates { get; set; } = true;
+
+ #endregion Public Properties
+
+ //------------------------------------------------------
+ //
+ // Public Methods
+ //
+ //------------------------------------------------------
+
+ #region Public Methods
+
+ /// <summary>
+ /// Clear the list and set a collection in place
+ /// </summary>
+ /// <param name="collection">
+ /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
+ /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
+ /// </param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void ReplaceCollection(IEnumerable<T> collection)
+ {
+ Clear();
+ AddRange(collection);
+ }
+
+ /// <summary>
+ /// Adds the elements of the specified collection to the end of the <see cref="ObservableCollection{T}"/>.
+ /// </summary>
+ /// <param name="collection">
+ /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
+ /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
+ /// </param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void AddRange(IEnumerable<T> collection)
+ {
+ InsertRange(Count, collection);
+ }
+
+ /// <summary>
+ /// Inserts the elements of a collection into the <see cref="ObservableCollection{T}"/> at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index at which the new elements should be inserted.</param>
+ /// <param name="collection">The collection whose elements should be inserted into the List<T>.
+ /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is not in the collection range.</exception>
+ public void InsertRange(int index, IEnumerable<T> collection)
+ {
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ if (!AllowDuplicates)
+ collection =
+ collection
+ .Distinct(Comparer)
+ .Where(item => !Items.Contains(item, Comparer))
+ .ToList();
+
+ if (collection is ICollection<T> countable)
+ {
+ if (countable.Count == 0)
+ return;
+ }
+ else if (!collection.Any())
+ return;
+
+ CheckReentrancy();
+
+ //expand the following couple of lines when adding more constructors.
+ var target = (List<T>)Items;
+ target.InsertRange(index, collection);
+
+ OnEssentialPropertiesChanged();
+
+ /*if (!(collection is IList list))
+ list = new List<T>(collection);
+
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, list, index));*/
+ OnCollectionReset();
+ }
+
+
+ /// <summary>
+ /// Removes the first occurence of each item in the specified collection from the <see cref="ObservableCollection{T}"/>.
+ /// </summary>
+ /// <param name="collection">The items to remove.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void RemoveRange(IEnumerable<T> collection)
+ {
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+
+ if (Count == 0)
+ return;
+ else if (collection is ICollection<T> countable)
+ {
+ switch (countable.Count)
+ {
+ case 0:
+ return;
+ case 1:
+ {
+ using IEnumerator<T> enumerator = countable.GetEnumerator();
+ enumerator.MoveNext();
+ Remove(enumerator.Current);
+ return;
+ }
+ }
+ }
+ else if (!collection.Any())
+ return;
+
+ CheckReentrancy();
+
+ //var clusters = new Dictionary<int, List<T>>();
+ //var lastIndex = -1;
+ //List<T>? lastCluster = null;
+ foreach (T item in collection)
+ {
+ var index = IndexOf(item);
+ if (index < 0)
+ continue;
+
+ Items.RemoveAt(index);
+
+ /*if (lastIndex == index && lastCluster != null)
+ lastCluster.Add(item);
+ else
+ clusters[lastIndex = index] = lastCluster = new List<T> { item };*/
+ }
+
+ OnEssentialPropertiesChanged();
+ OnCollectionReset();
+
+ /*if (Count == 0)
+ OnCollectionReset();
+ else
+ foreach (var cluster in clusters)
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster.Value, cluster.Key));*/
+
+ }
+
+ /// <summary>
+ /// Iterates over the collection and removes all items that satisfy the specified match.
+ /// </summary>
+ /// <remarks>The complexity is O(n).</remarks>
+ /// <param name="match"></param>
+ /// <returns>Returns the number of elements that where </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
+ public int RemoveAll(Predicate<T> match)
+ {
+ return RemoveAll(0, Count, match);
+ }
+
+ /// <summary>
+ /// Iterates over the specified range within the collection and removes all items that satisfy the specified match.
+ /// </summary>
+ /// <remarks>The complexity is O(n).</remarks>
+ /// <param name="index">The index of where to start performing the search.</param>
+ /// <param name="count">The number of items to iterate on.</param>
+ /// <param name="match"></param>
+ /// <returns>Returns the number of elements that where </returns>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
+ public int RemoveAll(int index, int count, Predicate<T> match)
+ {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count));
+ if (index + count > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (match == null)
+ throw new ArgumentNullException(nameof(match));
+
+ if (Count == 0)
+ return 0;
+
+ //List<T>? cluster = null;
+ var clusterIndex = -1;
+ var removedCount = 0;
+
+ using (BlockReentrancy())
+ using (DeferEvents())
+ {
+ for (var i = 0; i < count; i++, index++)
+ {
+ T item = Items[index];
+ if (match(item))
+ {
+ Items.RemoveAt(index);
+ removedCount++;
+
+ /*if (clusterIndex == index)
+ {
+ Debug.Assert(cluster != null);
+ cluster!.Add(item);
+ }
+ else
+ {
+ cluster = new List<T> { item };
+ clusterIndex = index;
+ }*/
+
+ index--;
+ }
+ /*else if (clusterIndex > -1)
+ {
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
+ clusterIndex = -1;
+ cluster = null;
+ }*/
+ }
+
+ //if (clusterIndex > -1)
+ // OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
+ }
+
+ if (removedCount > 0)
+ {
+ OnEssentialPropertiesChanged();
+ OnCollectionReset();
+ }
+
+ return removedCount;
+ }
+
+ /// <summary>
+ /// Removes a range of elements from the <see cref="ObservableCollection{T}"/>>.
+ /// </summary>
+ /// <param name="index">The zero-based starting index of the range of elements to remove.</param>
+ /// <param name="count">The number of elements to remove.</param>
+ /// <exception cref="ArgumentOutOfRangeException">The specified range is exceeding the collection.</exception>
+ public void RemoveRange(int index, int count)
+ {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count));
+ if (index + count > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ if (count == 0)
+ return;
+
+ if (count == 1)
+ {
+ RemoveItem(index);
+ return;
+ }
+
+ //Items will always be List<T>, see constructors
+ var items = (List<T>)Items;
+ List<T> removedItems = items.GetRange(index, count);
+
+ CheckReentrancy();
+
+ items.RemoveRange(index, count);
+
+ OnEssentialPropertiesChanged();
+
+ if (Count == 0)
+ OnCollectionReset();
+ else
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, index));
+ }
+
+ /// <summary>
+ /// Clears the current collection and replaces it with the specified collection,
+ /// using <see cref="Comparer"/>.
+ /// </summary>
+ /// <param name="collection">The items to fill the collection with, after clearing it.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ public void ReplaceRange(IEnumerable<T> collection)
+ {
+ ReplaceRange(0, Count, collection);
+ }
+
+ /// <summary>
+ /// Removes the specified range and inserts the specified collection in its position, leaving equal items in equal positions intact.
+ /// </summary>
+ /// <param name="index">The index of where to start the replacement.</param>
+ /// <param name="count">The number of items to be replaced.</param>
+ /// <param name="collection">The collection to insert in that location.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
+ public void ReplaceRange(int index, int count, IEnumerable<T> collection)
+ {
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count));
+ if (index + count > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+
+ if (!AllowDuplicates)
+ collection =
+ collection
+ .Distinct(Comparer)
+ .ToList();
+
+ if (collection is ICollection<T> countable)
+ {
+ if (countable.Count == 0)
+ {
+ RemoveRange(index, count);
+ return;
+ }
+ }
+ else if (!collection.Any())
+ {
+ RemoveRange(index, count);
+ return;
+ }
+
+ if (index + count == 0)
+ {
+ InsertRange(0, collection);
+ return;
+ }
+
+ if (!(collection is IList<T> list))
+ list = new List<T>(collection);
+
+ using (BlockReentrancy())
+ using (DeferEvents())
+ {
+ var rangeCount = index + count;
+ var addedCount = list.Count;
+
+ var changesMade = false;
+ List<T>?
+ newCluster = null,
+ oldCluster = null;
+
+
+ int i = index;
+ for (; i < rangeCount && i - index < addedCount; i++)
+ {
+ //parallel position
+ T old = this[i], @new = list[i - index];
+ if (Comparer.Equals(old, @new))
+ {
+ OnRangeReplaced(i, newCluster!, oldCluster!);
+ continue;
+ }
+ else
+ {
+ Items[i] = @new;
+
+ if (newCluster == null)
+ {
+ Debug.Assert(oldCluster == null);
+ newCluster = new List<T> { @new };
+ oldCluster = new List<T> { old };
+ }
+ else
+ {
+ newCluster.Add(@new);
+ oldCluster!.Add(old);
+ }
+
+ changesMade = true;
+ }
+ }
+
+ OnRangeReplaced(i, newCluster!, oldCluster!);
+
+ //exceeding position
+ if (count != addedCount)
+ {
+ var items = (List<T>)Items;
+ if (count > addedCount)
+ {
+ var removedCount = rangeCount - addedCount;
+ T[] removed = new T[removedCount];
+ items.CopyTo(i, removed, 0, removed.Length);
+ items.RemoveRange(i, removedCount);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, i));
+ }
+ else
+ {
+ var k = i - index;
+ T[] added = new T[addedCount - k];
+ for (int j = k; j < addedCount; j++)
+ {
+ T @new = list[j];
+ added[j - k] = @new;
+ }
+ items.InsertRange(i, added);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, i));
+ }
+
+ OnEssentialPropertiesChanged();
+ }
+ else if (changesMade)
+ {
+ OnIndexerPropertyChanged();
+ }
+ }
+ }
+
+ public void Sort()
+ {
+ Sort(null);
+ }
+
+ public void Sort(Comparison<T> comparison)
+ {
+ if (Count == 0)
+ {
+ return;
+ }
+
+ var list = this.ToList();
+ list.Sort(comparison);
+ ReplaceCollection(list);
+ }
+
+ #endregion Public Methods
+
+
+ //------------------------------------------------------
+ //
+ // Protected Methods
+ //
+ //------------------------------------------------------
+
+ #region Protected Methods
+
+ /// <summary>
+ /// Called by base class Collection&lt;T&gt; when the list is being cleared;
+ /// raises a CollectionChanged event to any listeners.
+ /// </summary>
+ protected override void ClearItems()
+ {
+ if (Count == 0)
+ return;
+
+ //CheckReentrancy();
+ base.ClearItems();
+ //OnEssentialPropertiesChanged();
+ //OnCollectionReset();
+ }
+
+ /// <inheritdoc/>
+ protected override void InsertItem(int index, T item)
+ {
+ if (!AllowDuplicates && Items.Contains(item))
+ return;
+
+ base.InsertItem(index, item);
+ }
+
+ /// <inheritdoc/>
+ protected override void SetItem(int index, T item)
+ {
+ if (AllowDuplicates)
+ {
+ if (Comparer.Equals(this[index], item))
+ return;
+ }
+ else
+ if (Items.Contains(item, Comparer))
+ return;
+
+ CheckReentrancy();
+ T oldItem = this[index];
+ base.SetItem(index, item);
+
+ OnIndexerPropertyChanged();
+ OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem!, item!, index);
+ }
+
+ /// <summary>
+ /// Raise CollectionChanged event to any listeners.
+ /// Properties/methods modifying this ObservableCollection will raise
+ /// a collection changed event through this virtual method.
+ /// </summary>
+ /// <remarks>
+ /// When overriding this method, either call its base implementation
+ /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
+ /// </remarks>
+ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ if (_deferredEvents != null)
+ {
+ _deferredEvents.Add(e);
+ return;
+ }
+ base.OnCollectionChanged(e);
+ }
+
+ protected virtual IDisposable DeferEvents() => new DeferredEventsCollection(this);
+
+ #endregion Protected Methods
+
+
+ //------------------------------------------------------
+ //
+ // Private Methods
+ //
+ //------------------------------------------------------
+
+ #region Private Methods
+
+ /// <summary>
+ /// Helper to raise Count property and the Indexer property.
+ /// </summary>
+ void OnEssentialPropertiesChanged()
+ {
+ OnPropertyChanged(EventArgsCache.CountPropertyChanged);
+ OnIndexerPropertyChanged();
+ }
+
+ /// <summary>
+ /// /// Helper to raise a PropertyChanged event for the Indexer property
+ /// /// </summary>
+ void OnIndexerPropertyChanged() =>
+ OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
+
+ /// <summary>
+ /// Helper to raise CollectionChanged event to any listeners
+ /// </summary>
+ void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
+
+ /// <summary>
+ /// Helper to raise CollectionChanged event with action == Reset to any listeners
+ /// </summary>
+ void OnCollectionReset() =>
+ OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
+
+ /// <summary>
+ /// Helper to raise event for clustered action and clear cluster.
+ /// </summary>
+ /// <param name="followingItemIndex">The index of the item following the replacement block.</param>
+ /// <param name="newCluster"></param>
+ /// <param name="oldCluster"></param>
+ //TODO should have really been a local method inside ReplaceRange(int index, int count, IEnumerable<T> collection, IEqualityComparer<T> comparer),
+ //move when supported language version updated.
+ void OnRangeReplaced(int followingItemIndex, ICollection<T> newCluster, ICollection<T> oldCluster)
+ {
+ if (oldCluster == null || oldCluster.Count == 0)
+ {
+ Debug.Assert(newCluster == null || newCluster.Count == 0);
+ return;
+ }
+
+ OnCollectionChanged(
+ new NotifyCollectionChangedEventArgs(
+ NotifyCollectionChangedAction.Replace,
+ new List<T>(newCluster),
+ new List<T>(oldCluster),
+ followingItemIndex - oldCluster.Count));
+
+ oldCluster.Clear();
+ newCluster.Clear();
+ }
+
+ #endregion Private Methods
+
+ //------------------------------------------------------
+ //
+ // Private Types
+ //
+ //------------------------------------------------------
+
+ #region Private Types
+ sealed class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
+ {
+ readonly RangeObservableCollection<T> _collection;
+ public DeferredEventsCollection(RangeObservableCollection<T> collection)
+ {
+ Debug.Assert(collection != null);
+ Debug.Assert(collection._deferredEvents == null);
+ _collection = collection;
+ _collection._deferredEvents = this;
+ }
+
+ public void Dispose()
+ {
+ _collection._deferredEvents = null;
+ foreach (var args in this)
+ _collection.OnCollectionChanged(args);
+ }
+ }
+
+ #endregion Private Types
+
+ }
+
+ /// <remarks>
+ /// To be kept outside <see cref="ObservableCollection{T}"/>, since otherwise, a new instance will be created for each generic type used.
+ /// </remarks>
+ internal static class EventArgsCache
+ {
+ internal static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs("Count");
+ internal static readonly PropertyChangedEventArgs IndexerPropertyChanged = new PropertyChangedEventArgs("Item[]");
+ internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
+ }
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
index 7f60893..e039b8a 100644
--- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
@@ -12,7 +12,6 @@ using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
@@ -46,7 +45,7 @@ namespace UVtools.Core.Operations
private decimal _normalExposure = 12;
private decimal _topBottomMargin = 5;
private decimal _leftRightMargin = 10;
- private byte _chamferLayers = 5;
+ private byte _chamferLayers = 0;
private byte _erodeBottomIterations = 0;
private decimal _partMargin = 0;
private bool _enableAntiAliasing = true;
@@ -88,7 +87,7 @@ namespace UVtools.Core.Operations
private decimal _exposureGenManualLayerHeight;
private decimal _exposureGenManualBottom;
private decimal _exposureGenManualNormal;
- private ObservableCollection<ExposureItem> _exposureTable = new();
+ private RangeObservableCollection<ExposureItem> _exposureTable = new();
private CalibrateExposureFinderShapes _holeShape = CalibrateExposureFinderShapes.Square;
#endregion
@@ -680,7 +679,7 @@ namespace UVtools.Core.Operations
public ExposureItem ExposureManualEntry => new (_exposureGenManualLayerHeight, _exposureGenManualBottom, _exposureGenManualNormal);
- public ObservableCollection<ExposureItem> ExposureTable
+ public RangeObservableCollection<ExposureItem> ExposureTable
{
get => _exposureTable;
set => RaiseAndSetIfChanged(ref _exposureTable, value);
@@ -827,16 +826,14 @@ namespace UVtools.Core.Operations
public void SortExposureTable()
{
- var list = _exposureTable.ToList();
- list.Sort();
- ExposureTable = new(list);
+ _exposureTable.Sort();
}
public void SanitizeExposureTable()
{
List<ExposureItem> list = _exposureTable.ToList().Distinct().ToList();
list.Sort();
- ExposureTable = new(list);
+ _exposureTable.ReplaceCollection(list);
}
public void GenerateExposure()
diff --git a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
index 9d123b4..9559d0b 100644
--- a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
+++ b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs
@@ -69,8 +69,8 @@ namespace UVtools.Core.Operations
private ExposureSetTypes _exposureSetType = ExposureSetTypes.Linear;
private decimal _bottomExposureStep = 0.5m;
private decimal _exposureStep = 0.2m;
- private ObservableCollection<ExposureItem> _automaticExposureTable = new();
- private ObservableCollection<ExposureItem> _manualExposureTable = new();
+ private RangeObservableCollection<ExposureItem> _automaticExposureTable = new();
+ private RangeObservableCollection<ExposureItem> _manualExposureTable = new();
#endregion
@@ -270,7 +270,7 @@ namespace UVtools.Core.Operations
}
[XmlIgnore]
- public ObservableCollection<ExposureItem> AutomaticExposureTable
+ public RangeObservableCollection<ExposureItem> AutomaticExposureTable
{
get
{
@@ -280,14 +280,14 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _automaticExposureTable, value);
}
- public ObservableCollection<ExposureItem> ManualExposureTable
+ public RangeObservableCollection<ExposureItem> ManualExposureTable
{
get => _manualExposureTable;
set => RaiseAndSetIfChanged(ref _manualExposureTable, value);
}
[XmlIgnore]
- public ObservableCollection<ExposureItem> ExposureTable => IsExposureSetTypeManual ? _manualExposureTable : AutomaticExposureTable;
+ public RangeObservableCollection<ExposureItem> ExposureTable => IsExposureSetTypeManual ? _manualExposureTable : AutomaticExposureTable;
/// <summary>
/// Gets the exposure table into a dictionary where key is the layer height
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index 367e725..c1be566 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -46,7 +46,7 @@ namespace UVtools.Core.Operations
private bool _extendBeyondLayerCount = true;
private bool _discardUnmodifiedLayers;
private ushort _stackMargin = 50;
- private ObservableCollection<ValueDescription> _files = new();
+ private RangeObservableCollection<ValueDescription> _files = new();
#endregion
#region Overrides
@@ -107,7 +107,7 @@ namespace UVtools.Core.Operations
StringBuilder sb = new();
- if (Files.Count == 0)
+ if (_files.Count == 0)
{
sb.AppendLine("No files to import.");
}
@@ -161,13 +161,13 @@ namespace UVtools.Core.Operations
}
[XmlIgnore]
- public ObservableCollection<ValueDescription> Files
+ public RangeObservableCollection<ValueDescription> Files
{
get => _files;
set => RaiseAndSetIfChanged(ref _files, value);
}
- public uint Count => (uint) Files.Count;
+ public uint Count => (uint)_files.Count;
#endregion
#region Constructor
@@ -183,19 +183,12 @@ namespace UVtools.Core.Operations
public void AddFile(string file)
{
- Files.Add(new ValueDescription(file, Path.GetFileNameWithoutExtension(file)));
+ _files.Add(new ValueDescription(file, Path.GetFileNameWithoutExtension(file)));
}
public void Sort()
{
- var sortedFiles = Files.ToList();
- sortedFiles.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.ValueAsString), Path.GetFileNameWithoutExtension(file2.ValueAsString), StringComparison.Ordinal));
- Files.Clear();
- foreach (var file in sortedFiles)
- {
- Files.Add(file);
- }
- //Files.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1), Path.GetFileNameWithoutExtension(file2), StringComparison.Ordinal));
+ _files.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.ValueAsString), Path.GetFileNameWithoutExtension(file2.ValueAsString), StringComparison.Ordinal));
}
@@ -232,12 +225,12 @@ namespace UVtools.Core.Operations
// Order raw images
for (int i = 0; i < Count; i++)
{
- if (!Files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
- !Files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
- !Files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) &&
- !Files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) &&
- !Files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
- keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, Files[i].ValueAsString));
+ if (!_files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) &&
+ !_files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
+ keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, _files[i].ValueAsString));
}
// Create virtual file format with images
@@ -264,15 +257,15 @@ namespace UVtools.Core.Operations
// Order remaining possible file formats
for (int i = 0; i < Count; i++)
{
- if (Files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ||
- Files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) ||
- Files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) ||
- Files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ||
- Files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
+ if (_files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ||
+ _files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue;
- var fileFormat = FileFormat.FindByExtension(Files[i].ValueAsString, true, true);
+ var fileFormat = FileFormat.FindByExtension(_files[i].ValueAsString, true, true);
if (fileFormat is null) continue;
- fileFormat.FileFullPath = Files[i].ValueAsString;
+ fileFormat.FileFullPath = _files[i].ValueAsString;
fileFormats.Add(fileFormat);
}
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index f3e2fba..8dce5fe 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,14 +10,16 @@
<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>2.9.1</Version>
+ <Version>2.9.2</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
+ <SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <DocumentationFile></DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -46,7 +48,7 @@
<ItemGroup>
<PackageReference Include="BinarySerializer" Version="8.6.0" />
<PackageReference Include="Emgu.CV" Version="4.5.1.4349" />
- <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0-1.final" />
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0-2.final" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
<PackageReference Include="System.Memory" Version="4.5.4" />