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

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2022-07-29 20:10:48 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-07-29 20:10:48 +0300
commit0805133048ae2661901dedbf72232ba5f503cfb6 (patch)
treeb255aef81847e720332080344c514ee5855bcf6f
parent315ce3bc0dfc7a950138473f66ac83e0fa119f6c (diff)
v3.5.6v3.5.6
- **Tools** - **PCB Exposure:** - (Add) Able to choose the size midpoint rounding method (#520) - (Fix) Allow to flash alone D03 commands (#520) - (Fix) Correct line thickness to have at least 1px error (#523) - (Improvement) Layer arithmetic: Use ; to split and start a new arithmetic operation - (Add) Cmd: Convert command now allow to pass 'auto' as target type to auto convert specific files, valid for SL1 files configured with FILEFORMAT_xxx (#522) - (Add) GCode: Command to sync and wait for movement completion [Only enabled for cws format] (#514) - (Add) VDT: Transition layer count - (Upgrade) AvaloniaUI from 0.10.16 to 0.10.17
-rw-r--r--CHANGELOG.md13
-rw-r--r--RELEASE_NOTES.md14
-rw-r--r--UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj2
-rw-r--r--UVtools.Cmd/Symbols/ConvertCommand.cs39
-rw-r--r--UVtools.Cmd/UVtools.Cmd.csproj2
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs43
-rw-r--r--UVtools.Core/FileFormats/CWSFile.cs1
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs5
-rw-r--r--UVtools.Core/FileFormats/OSFFile.cs565
-rw-r--r--UVtools.Core/FileFormats/VDTFile.cs17
-rw-r--r--UVtools.Core/GCode/GCodeBuilder.cs20
-rw-r--r--UVtools.Core/Gerber/Apertures/Aperture.cs2
-rw-r--r--UVtools.Core/Gerber/Apertures/CircleAperture.cs9
-rw-r--r--UVtools.Core/Gerber/Apertures/EllipseAperture.cs8
-rw-r--r--UVtools.Core/Gerber/Apertures/MacroAperture.cs4
-rw-r--r--UVtools.Core/Gerber/Apertures/PoygonAperture.cs4
-rw-r--r--UVtools.Core/Gerber/Apertures/RectangleAperture.cs5
-rw-r--r--UVtools.Core/Gerber/Enumerations.cs22
-rw-r--r--UVtools.Core/Gerber/GerberDocument.cs113
-rw-r--r--UVtools.Core/Gerber/Macro.cs27
-rw-r--r--UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs13
-rw-r--r--UVtools.Core/Gerber/Primitives/CirclePrimitive.cs10
-rw-r--r--UVtools.Core/Gerber/Primitives/CommentPrimitive.cs6
-rw-r--r--UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs8
-rw-r--r--UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs12
-rw-r--r--UVtools.Core/Gerber/Primitives/Primitive.cs12
-rw-r--r--UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs12
-rw-r--r--UVtools.Core/Objects/UInt24BigEndian.cs59
-rw-r--r--UVtools.Core/Operations/OperationLayerArithmetic.cs290
-rw-r--r--UVtools.Core/Operations/OperationPCBExposure.cs26
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.InstallerMM/UVtools.InstallerMM.wxs2
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml16
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj16
34 files changed, 1115 insertions, 284 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3e9a8c..35c6894 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog
+## 29/07/2022 - v3.5.6
+
+- **Tools**
+ - **PCB Exposure:**
+ - (Add) Able to choose the size midpoint rounding method (#520)
+ - (Fix) Allow to flash alone D03 commands (#520)
+ - (Fix) Correct line thickness to have at least 1px error (#523)
+ - (Improvement) Layer arithmetic: Use ; to split and start a new arithmetic operation
+- (Add) Cmd: Convert command now allow to pass 'auto' as target type to auto convert specific files, valid for SL1 files configured with FILEFORMAT_xxx (#522)
+- (Add) GCode: Command to sync and wait for movement completion [Only enabled for cws format] (#514)
+- (Add) VDT: Transition layer count
+- (Upgrade) AvaloniaUI from 0.10.16 to 0.10.17
+
## 18/07/2022 - v3.5.5
- **File formats:**
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 762eac8..9fe95e0 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,11 @@
-- **File formats:**
- - (Add) `LayerImageType`: Gets the layer image data type used on this file format
- - (Improvement) jxs, rgb.cws and xml.cws: Improve the layer image read/write performance by a significant amount
- - (Fix) xml.cws: Wanhao printers need 32 bit png instead of 8 bit png (#514)
+- **Tools**
+ - **PCB Exposure:**
+ - (Add) Able to choose the size midpoint rounding method (#520)
+ - (Fix) Allow to flash alone D03 commands (#520)
+ - (Fix) Correct line thickness to have at least 1px error (#523)
+ - (Improvement) Layer arithmetic: Use ; to split and start a new arithmetic operation
+- (Add) Cmd: Convert command now allow to pass 'auto' as target type to auto convert specific files, valid for SL1 files configured with FILEFORMAT_xxx (#522)
+- (Add) GCode: Command to sync and wait for movement completion [Only enabled for cws format] (#514)
+- (Add) VDT: Transition layer count
+- (Upgrade) AvaloniaUI from 0.10.16 to 0.10.17
diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
index b177036..a47c56e 100644
--- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
+++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
@@ -38,7 +38,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="0.10.16" />
+ <PackageReference Include="Avalonia" Version="0.10.17" />
</ItemGroup>
<ItemGroup>
diff --git a/UVtools.Cmd/Symbols/ConvertCommand.cs b/UVtools.Cmd/Symbols/ConvertCommand.cs
index 89a4fdb..66a46d2 100644
--- a/UVtools.Cmd/Symbols/ConvertCommand.cs
+++ b/UVtools.Cmd/Symbols/ConvertCommand.cs
@@ -7,6 +7,7 @@
*/
using System;
using System.CommandLine;
+using System.Globalization;
using System.IO;
using System.Linq;
using UVtools.Core.Extensions;
@@ -21,7 +22,7 @@ internal static class ConvertCommand
var command = new Command("convert", "Convert input file into a output file format by a known type or extension")
{
GlobalArguments.InputFileArgument,
- new Argument<string>("target-type/ext", "Target format type or extension"),
+ new Argument<string>("target-type/ext", "Target format type or extension. Use 'auto' for SL1 files with specified FILEFORMAT_xxx"),
GlobalArguments.OutputFileArgument,
new Option<ushort>(new[] {"-v", "--version"}, "Sets the file format version"),
@@ -30,6 +31,42 @@ internal static class ConvertCommand
command.SetHandler((FileInfo inputFile, string targetTypeExt, FileInfo? outputFile, ushort version, bool noOverwrite) =>
{
+ if (string.Equals(targetTypeExt, "auto", StringComparison.InvariantCultureIgnoreCase))
+ {
+ using var testFile = Program.OpenInputFile(inputFile, FileFormat.FileDecodeType.Partial);
+ string? convertFileExtension;
+ switch (testFile)
+ {
+ case SL1File sl1File:
+ convertFileExtension = sl1File.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null);
+ break;
+ case VDTFile vdtFile:
+ if (string.IsNullOrWhiteSpace(vdtFile.ManifestFile.Machine.UVToolsConvertTo) || vdtFile.ManifestFile.Machine.UVToolsConvertTo == "None")
+ convertFileExtension = vdtFile.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null);
+ else
+ convertFileExtension = vdtFile.ManifestFile.Machine.UVToolsConvertTo;
+ break;
+ default:
+ Program.WriteLineError($"The file '{testFile.Filename}' is not a valid candidate for auto conversion. Please specify the target format instead.");
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(convertFileExtension))
+ {
+ Program.WriteLineError($"The file '{testFile.Filename}' does not specify a target format, unable to guess. Please specify the target format instead.");
+ return;
+ }
+
+ convertFileExtension = convertFileExtension.ToLower(CultureInfo.InvariantCulture);
+ var fileExtension = FileFormat.FindExtension(convertFileExtension);
+ if (fileExtension is null)
+ {
+ Program.WriteLineError($"Unable to find a valid target type from '{convertFileExtension}' extension.");
+ }
+ targetTypeExt = fileExtension!.GetFileFormat()!.GetType().Name;
+ outputFile = new FileInfo(Path.Combine(testFile.DirectoryPath!, $"{testFile.FilenameNoExt}.{convertFileExtension}"));
+ }
+
var targetType = FileFormat.FindByType(targetTypeExt);
if (targetType is null)
{
diff --git a/UVtools.Cmd/UVtools.Cmd.csproj b/UVtools.Cmd/UVtools.Cmd.csproj
index edd17c2..448c116 100644
--- a/UVtools.Cmd/UVtools.Cmd.csproj
+++ b/UVtools.Cmd/UVtools.Cmd.csproj
@@ -5,7 +5,7 @@
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>UVtoolsCmd</AssemblyName>
<ApplicationIcon>UVtools.ico</ApplicationIcon>
- <Version>1.0.1</Version>
+ <Version>1.0.2</Version>
<Authors>Tiago Conceição, sn4k3</Authors>
<Company>PTRTECH</Company>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 4b75aea..bb12f39 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -997,6 +997,49 @@ public static class EmguExtensions
#region Draw Methods
/// <summary>
+ /// Correct openCV thickness which always results larger than specified
+ /// </summary>
+ /// <param name="thickness">Thickness to correct</param>
+ /// <returns></returns>
+ public static int CorrectThickness(int thickness)
+ {
+ if (thickness < 3) return thickness;
+ return thickness - 1;
+ }
+
+ public static void DrawLineAccurate(this Mat src, Point pt1, Point pt2, MCvScalar color, int thickness, LineType lineType = LineType.EightConnected)
+ {
+ /*var deltaX = pt2.X - pt1.X;
+ var deltaY = pt2.Y - pt1.Y;
+ var deg = Math.Atan2(deltaY, deltaX) * (180 / Math.PI);
+ src.DrawRotatedRectangle(
+ new Size(Math.Abs(deltaX), thickness),
+ new Point(pt1.X + deltaX / 2, pt1.Y + deltaY / 2),
+ color, (int)deg, -1, lineType);*/
+
+ if (thickness >= 3)
+ {
+ thickness--;
+ /*var lastNumber = thickness % 10;
+ switch (lastNumber)
+ {
+ case 1:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ thickness--;
+ break;
+ }*/
+ }
+
+ CvInvoke.Line(src, pt1, pt2, color, thickness, lineType);
+ }
+
+ /// <summary>
/// Draw a rotated square around a center point
/// </summary>
/// <param name="src"></param>
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs
index 808a8a3..91417d5 100644
--- a/UVtools.Core/FileFormats/CWSFile.cs
+++ b/UVtools.Core/FileFormats/CWSFile.cs
@@ -581,6 +581,7 @@ public class CWSFile : FileFormat
};
GCode.CommandShowImageM6054.Set(";<Slice>", "{0}");
GCode.CommandWaitG4.Set(";<Delay>", "{0}");
+ GCode.CommandSyncMovements.Enabled = true;
}
#endregion
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 27dce29..322cfd1 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -384,6 +384,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
//new CXDLPv1File(), // Creality Box v1
new CXDLPFile(), // Creality Box
new LGSFile(), // LGS, LGS30
+ //new OSFFile(), // OSF
new FlashForgeSVGXFile(), // SVGX
new GenericZIPFile(), // Generic zip files
new VDAFile(), // VDA
@@ -2792,11 +2793,11 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
get => _materialMilliliters;
set
{
- if (value <= 0)
+ if (value <= 0) // Recalculate
{
value = (float)Math.Round(this.Where(layer => layer is not null).Sum(layer => layer.MaterialMilliliters), 3);
}
- else
+ else // Set from value
{
value = (float)Math.Round(value, 3);
}
diff --git a/UVtools.Core/FileFormats/OSFFile.cs b/UVtools.Core/FileFormats/OSFFile.cs
new file mode 100644
index 0000000..2fb3f7f
--- /dev/null
+++ b/UVtools.Core/FileFormats/OSFFile.cs
@@ -0,0 +1,565 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Threading.Tasks;
+using UVtools.Core.Converters;
+using UVtools.Core.Extensions;
+using UVtools.Core.Layers;
+using UVtools.Core.Objects;
+using UVtools.Core.Operations;
+
+namespace UVtools.Core.FileFormats;
+
+public class OSFFile : FileFormat
+{
+ #region Sub Classes
+
+ #region Header
+
+ public class Header
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint HeaderLength { get; set; } = 350001;
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort Version { get; set; } = 4;
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public byte ImageLog { get; set; } = 2;
+
+ /// <summary>
+ /// 148 * 80 * 2
+ /// </summary>
+ //[FieldOrder(3)] public Uint24BitBigEndian Preview1Length { get; set; } = new(23680);
+ [FieldOrder(3)] [FieldEndianness(Endianness.Big)] [FieldBitLength(24)] public uint Preview1Length { get; set; } = 23680;
+
+ /// <summary>
+ /// RGB565
+ /// </summary>
+ //[FieldOrder(4)] [FieldLength(nameof(Preview1Length.Value))] public byte[] Preview1Data { get; set; }
+
+ /*/// <summary>
+ /// 300 * 140 * 2
+ /// </summary>
+ [FieldOrder(5)][FieldBitLength(24)] public uint Preview2Length { get; set; } = 84000;*/
+
+ /*/// <summary>
+ /// RGB565
+ /// </summary>
+ [FieldOrder(6)] [FieldLength(nameof(Preview2Length))] public byte[] Preview2Data { get; set; }*/
+
+
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; }
+ [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; }
+ [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort PixelUmMagnified100Times { get; set; }
+
+ /// <summary>
+ /// (0x00 not mirrored, 0x01 X-axis mirroring, 0x02 Y-axis mirroring, 0x03 XY-axis mirroring)
+ /// </summary>
+ [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public byte Mirror { get; set; }
+ [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public byte BottomLightPWM { get; set; } = DefaultBottomLightPWM;
+ [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public byte LightPWM { get; set; } = DefaultLightPWM;
+ [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public byte AntiAliasEnabled { get; set; }
+ [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public byte DistortionEnabled { get; set; }
+ [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public byte DelayedExposureActivationEnabled { get; set; }
+ [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public uint LayerCount { get; set; }
+ [FieldOrder(20)] [FieldEndianness(Endianness.Big)] public ushort NumberParameterSets { get; set; } = 1;
+ [FieldOrder(21)] [FieldEndianness(Endianness.Big)] public uint LastLayerIndex { set; get; }
+ [FieldOrder(22)] [FieldEndianness(Endianness.Big)] public uint LayerHeightUmMagnified100Times { set; get; }
+ [FieldOrder(23)] [FieldEndianness(Endianness.Big)] public byte BottomLayerCount { set; get; }
+ [FieldOrder(24)] [FieldEndianness(Endianness.Big)] public uint ExposureTimeMagnified100Times { set; get; }
+ [FieldOrder(25)] [FieldEndianness(Endianness.Big)] public uint BottomExposureTimeMagnified100Times { set; get; }
+ [FieldOrder(26)] [FieldEndianness(Endianness.Big)] public uint SupportDelayTimeMagnified100Times { set; get; }
+ [FieldOrder(27)] [FieldEndianness(Endianness.Big)] public uint BottomSupportDelayTimeMagnified100Times { set; get; }
+ [FieldOrder(28)] [FieldEndianness(Endianness.Big)] public byte TransitionLayers { set; get; }
+ /// <summary>
+ /// (0x00 linear transition)
+ /// </summary>
+ [FieldOrder(29)] [FieldEndianness(Endianness.Big)] public byte TransitionType { set; get; }
+ /*
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint TransitionLayerIntervalTimeDifferenceMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint WaitTimeAfterCureMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint WaitTimeAfterLiftMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint WaitTimeBeforeCureMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint BottomLiftHeightSlowMagnified1000Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint BottomLiftHeightTotalMagnified1000Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint LiftHeightSlowMagnified1000Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint LiftHeightTotalMagnified1000Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint BottomRetractHeightTotalMagnified1000Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint RetractHeightSlowMagnified1000Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public uint RetractHeightTotalMagnified1000Times { set; get; }
+
+ /// <summary>
+ /// (0x00: S-shaped acceleration, 0x01: T-shaped acceleration, Default Value: S-shaped acceleration, currently only supports S-shaped acceleration)
+ /// </summary>
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte AccelerationType { set; get; }
+
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedStartMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedSlowMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedFastMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte BottomLiftAccelerationChange { set; get; } // 5
+
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedStartMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedSlowMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedFastMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte LiftAccelerationChange { set; get; } // 5
+
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedStartMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedSlowMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractFastMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte BottomRetractAccelerationChange { set; get; } // 5
+
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedStartMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedSlowMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort RetractFastMagnified100Times { set; get; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte RetractAccelerationChange { set; get; } // 5
+
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] [FieldCount(23)] public byte[] Reserved { set; get; } = new byte[23]; // 23
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public byte ProtocolType { set; get; } // 0
+ */
+
+ }
+
+ #endregion
+
+ #region LayerDef
+
+ public class LayerDef
+ {
+ [Ignore] public LGSFile Parent { get; set; } = null!;
+
+ [FieldOrder(0)]
+ public uint DataSize { get; set; }
+
+ [FieldOrder(1)]
+ [FieldLength(nameof(DataSize))]
+ public byte[] EncodedRle { get; set; } = null!;
+
+ public LayerDef() { }
+
+ public LayerDef(LGSFile parent)
+ {
+ Parent = parent;
+ }
+
+ public unsafe byte[] Encode(Mat mat)
+ {
+ List<byte> rawData = new();
+ List<byte> chunk = new();
+
+ if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ {
+ CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90Clockwise);
+ }
+
+ var spanMat = mat.GetBytePointer();
+ var imageLength = mat.GetLength();
+
+ uint span = 0;
+ byte lc = 0;
+
+ void addSpan(){
+ chunk.Clear();
+ for (; span > 0; span >>= 4) {
+ chunk.Insert(0, (byte)((byte)(span & 0xf) | (lc & 0xf0)));
+ }
+ rawData.AddRange(chunk.ToArray());
+ }
+
+ for (int i = 0; i < imageLength; i++)
+ {
+ byte c = (byte) (spanMat[i] & 0xf0);
+
+ if (c == lc)
+ {
+ span++;
+ }
+ else
+ {
+ addSpan();
+ span = 1;
+ }
+
+ lc = c;
+ }
+
+ addSpan();
+ EncodedRle = rawData.ToArray();
+ DataSize = (uint) EncodedRle.Length;
+
+ if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ {
+ CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
+ }
+
+ return EncodedRle;
+ }
+
+ public Mat Decode(bool consumeRle = true)
+ {
+ // lgs10/30 -------->
+ // lgs120/4k From Y bottom to top Y
+ var mat = EmguExtensions.InitMat(Parent.HeaderSettings.PrinterModel is 4000 or 4500 ? Parent.Resolution.Exchange() : Parent.Resolution);
+ //var matSpan = mat.GetBytePointer();
+ var imageLength = mat.GetLength();
+
+ int pixelPos = 0;
+
+ for (var i = 0; i < EncodedRle.Length; i++)
+ {
+ var b = EncodedRle[i];
+ byte colorNibble = (byte)(b >> 4);
+ byte color = (byte)(colorNibble << 4 | colorNibble);
+ int repeat = b & 0xf;
+
+ while (i + 1 < EncodedRle.Length && (EncodedRle[i + 1] >> 4) == colorNibble)
+ {
+ i++;
+ repeat = (repeat << 4) | (EncodedRle[i] & 0xf);
+ }
+
+ if (pixelPos >= imageLength)
+ {
+ throw new FileLoadException($"Too much buffer, expected: {imageLength}, got: {pixelPos}");
+ }
+
+ mat.FillSpan(ref pixelPos, repeat, color);
+
+ //if (repeat <= 0) continue;
+ /*while (repeat-- > 0)
+ {
+ matSpan[pixel++] = color;
+ }*/
+
+ }
+
+ if (pixelPos != imageLength)
+ {
+ throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {pixelPos}");
+ }
+
+ if (consumeRle)
+ EncodedRle = null!;
+
+ if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
+ {
+ CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
+ }
+
+ return mat;
+ }
+ }
+ #endregion
+
+ #endregion
+
+ #region Properties
+
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public override FileFormatType FileType => FileFormatType.Binary;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new (typeof(OSFFile), "osf", "Vlare Open File Format"),
+ };
+
+ public override PrintParameterModifier[]? PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
+
+ PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
+
+ PrintParameterModifier.BottomExposureTime,
+ PrintParameterModifier.ExposureTime,
+
+ PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+
+ };
+
+ public override Size[]? ThumbnailsOriginalSize { get; } =
+ {
+ new(148, 80),
+ new(300, 140),
+ new(208, 116),
+ new(404, 240),
+ };
+
+ public override uint ResolutionX
+ {
+ get => HeaderSettings.ResolutionX;
+ set
+ {
+ HeaderSettings.ResolutionX = (ushort) value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public override uint ResolutionY
+ {
+ get => (uint)HeaderSettings.ResolutionY;
+ set
+ {
+ HeaderSettings.ResolutionY = (ushort)value;
+ RaisePropertyChanged();
+ }
+ }
+
+
+
+ public override FlipDirection DisplayMirror
+ {
+ get => HeaderSettings.Mirror switch
+ {
+ 1 => FlipDirection.Horizontally,
+ 2 => FlipDirection.Vertically,
+ 3 => FlipDirection.Both,
+ _ => FlipDirection.None
+ };
+ set
+ {
+ HeaderSettings.Mirror = value switch
+ {
+ FlipDirection.None => 0,
+ FlipDirection.Horizontally => 1,
+ FlipDirection.Vertically => 2,
+ FlipDirection.Both => 3,
+ _ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
+ };
+ }
+ }
+
+ public override float LayerHeight
+ {
+ get => Layer.RoundHeight(HeaderSettings.LayerHeightUmMagnified100Times / 100000f);
+ set
+ {
+ HeaderSettings.LayerHeightUmMagnified100Times = (ushort)(value * 100000);
+ RaisePropertyChanged();
+ }
+ }
+
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set
+ {
+ base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
+ HeaderSettings.LastLayerIndex = HeaderSettings.LayerCount - 1;
+ }
+ }
+
+ public override ushort BottomLayerCount
+ {
+ get => HeaderSettings.BottomLayerCount;
+ set
+ {
+ HeaderSettings.BottomLayerCount = (byte)value;
+ base.BottomLayerCount = value;
+ }
+ }
+
+ /*public override float BottomLightOffDelay
+ {
+ get => TimeConverter.MillisecondsToSeconds(HeaderSettings.BottomLightOffDelayMs);
+ set
+ {
+ HeaderSettings.BottomLightOffDelayMs = TimeConverter.SecondsToMilliseconds(value);
+ base.BottomLightOffDelay = value;
+ }
+ }
+
+ public override float LightOffDelay
+ {
+ get => TimeConverter.MillisecondsToSeconds(HeaderSettings.LightOffDelayMs);
+ set
+ {
+ HeaderSettings.LightOffDelayMs = TimeConverter.SecondsToMilliseconds(value);
+ base.LightOffDelay = value;
+ }
+ }
+
+ public override float BottomWaitTimeBeforeCure
+ {
+ get => base.BottomWaitTimeBeforeCure;
+ set
+ {
+ SetBottomLightOffDelay(value);
+ base.BottomWaitTimeBeforeCure = value;
+ }
+ }
+
+ public override float WaitTimeBeforeCure
+ {
+ get => base.WaitTimeBeforeCure;
+ set
+ {
+ SetNormalLightOffDelay(value);
+ base.WaitTimeBeforeCure = value;
+ }
+ }
+
+ public override float BottomExposureTime
+ {
+ get => TimeConverter.MillisecondsToSeconds(HeaderSettings.BottomExposureTimeMs);
+ set
+ {
+ HeaderSettings.BottomExposureTimeMs = TimeConverter.SecondsToMilliseconds(value);
+ base.BottomExposureTime = value;
+ }
+ }
+
+ public override float ExposureTime
+ {
+ get => TimeConverter.MillisecondsToSeconds(HeaderSettings.ExposureTimeMs);
+ set
+ {
+ HeaderSettings.ExposureTimeMs = TimeConverter.SecondsToMilliseconds(value);
+ base.ExposureTime = value;
+ }
+ }
+
+ public override float BottomLiftHeight
+ {
+ get => HeaderSettings.BottomLiftHeight;
+ set => base.BottomLiftHeight = HeaderSettings.BottomLiftHeight = value;
+ }
+
+ public override float LiftHeight
+ {
+ get => HeaderSettings.LiftHeight;
+ set => base.LiftHeight = HeaderSettings.LiftHeight = value;
+ }
+
+ public override float BottomLiftSpeed
+ {
+ get => HeaderSettings.BottomLiftSpeed;
+ set => base.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed_ = value;
+ }
+
+ public override float LiftSpeed
+ {
+ get => HeaderSettings.LiftSpeed;
+ set => base.LiftSpeed = HeaderSettings.LiftSpeed = HeaderSettings.LiftSpeed_ = value;
+ }
+ */
+ public override float BottomRetractSpeed => RetractSpeed;
+
+ public override float RetractSpeed => LiftSpeed;
+
+
+ public override object[] Configs => new object[] { HeaderSettings };
+
+ #endregion
+
+ #region Constructors
+ public OSFFile()
+ {
+ }
+ #endregion
+
+ #region Methods
+
+ protected override void EncodeInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Create, FileAccess.Write);
+ outputFile.WriteSerialize(HeaderSettings);
+ outputFile.WriteBytes(EncodeImage(DATATYPE_RGB565_BE, Thumbnails[0]!));
+
+
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var layerData = new LayerDef[LayerCount];
+
+ foreach (var batch in BatchLayersIndexes())
+ {
+ Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
+ {
+ /*using (var mat = this[layerIndex].LayerMat)
+ {
+ layerData[layerIndex] = new LayerDef(this);
+ layerData[layerIndex].Encode(mat);
+ }*/
+ progress.LockAndIncrement();
+ });
+
+ foreach (var layerIndex in batch)
+ {
+ progress.ThrowIfCancellationRequested();
+ outputFile.WriteSerialize(layerData[layerIndex]);
+ layerData[layerIndex].EncodedRle = null!; // Free this
+ }
+ }
+
+
+ progress.ItemName = "Saving layers";
+ progress.ProcessedItems = 0;
+
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.ThrowIfCancellationRequested();
+ outputFile.WriteSerialize(layerData[layerIndex]);
+ progress++;
+ }
+
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
+
+ protected override void DecodeInternally(OperationProgress progress)
+ {
+ using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read);
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+
+ //var previewSize = HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2;
+ //var previewData = inputFile.ReadBytes(previewSize);
+ //Thumbnails[0] = DecodeImage(DATATYPE_RGB565_BE, previewData, HeaderSettings.PreviewSizeX, HeaderSettings.PreviewSizeY);
+
+
+ Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ var layerData = new LayerDef[LayerCount];
+
+ if (DecodeType == FileDecodeType.Full)
+ {
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ foreach (var batch in BatchLayersIndexes())
+ {
+ foreach (var layerIndex in batch)
+ {
+ progress.ThrowIfCancellationRequested();
+
+ layerData[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
+ }
+
+ Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
+ {
+ using var mat = layerData[layerIndex].Decode();
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+
+ progress.LockAndIncrement();
+ });
+ }
+ }
+
+ RebuildLayersProperties();
+ }
+
+ protected override void PartialSaveInternally(OperationProgress progress)
+ {
+ using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(0, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs
index bf82ad7..b82145b 100644
--- a/UVtools.Core/FileFormats/VDTFile.cs
+++ b/UVtools.Core/FileFormats/VDTFile.cs
@@ -14,7 +14,6 @@ using System.IO;
using System.IO.Compression;
using System.Text.Json;
using System.Text.Json.Serialization;
-using System.Xml;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
using UVtools.Core.Operations;
@@ -100,7 +99,8 @@ public class VDTFile : FileFormat
public sealed class VDTPrint
{
[JsonPropertyName("layer_thickness")] public float LayerHeight { get; set; }
- [JsonPropertyName("bottom_layers")] public ushort BottomLayers { get; set; } = DefaultBottomLayerCount;
+ [JsonPropertyName("bottom_layers")] public ushort BottomLayerCount { get; set; } = DefaultBottomLayerCount;
+ [JsonPropertyName("transition_layer")] public ushort TransitionLayerCount { get; set; } = DefaultTransitionLayerCount;
[JsonPropertyName("bottom_light_off_delay")] public float BottomLightOffDelay { get; set; }
[JsonPropertyName("light_off_delay")] public float LightOffDelay { get; set; }
[JsonPropertyName("bottom_wait_time_before_cure")] public float BottomWaitTimeBeforeCure { get; set; }
@@ -203,6 +203,7 @@ public class VDTFile : FileFormat
public override PrintParameterModifier[]? PrintParameterModifiers { get; } = {
PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.TransitionLayerCount,
PrintParameterModifier.BottomLightOffDelay,
PrintParameterModifier.LightOffDelay,
@@ -378,8 +379,16 @@ public class VDTFile : FileFormat
public override ushort BottomLayerCount
{
- get => ManifestFile.Print.BottomLayers;
- set => base.BottomLayerCount = ManifestFile.Print.BottomLayers = value;
+ get => ManifestFile.Print.BottomLayerCount;
+ set => base.BottomLayerCount = ManifestFile.Print.BottomLayerCount = value;
+ }
+
+ public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software;
+
+ public override ushort TransitionLayerCount
+ {
+ get => ManifestFile.Print.TransitionLayerCount;
+ set => base.TransitionLayerCount = ManifestFile.Print.TransitionLayerCount = (ushort)Math.Min(value, MaximumPossibleTransitionLayerCount);
}
public override float BottomLightOffDelay
diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs
index 1cacb93..04b0fe6 100644
--- a/UVtools.Core/GCode/GCodeBuilder.cs
+++ b/UVtools.Core/GCode/GCodeBuilder.cs
@@ -40,7 +40,10 @@ public class GCodeBuilder : BindableBase
public GCodeCommand CommandMoveG0 { get; } = new("G0", "Z{0} F{1}", "Move Z");
public GCodeCommand CommandMoveG1 { get; } = new("G1", "Z{0} F{1}", "Move Z");
+ public GCodeCommand CommandSyncMovements { get; } = new("G4", "P0", "Sync movements", false);
+
public GCodeCommand CommandWaitG4 { get; } = new("G4", "P{0}", "Delay");
+
public GCodeCommand CommandShowImageM6054 = new("M6054", "\"{0}\"", "Show image");
public GCodeCommand CommandClearImage = new(";<Slice> Blank"); // Clear image
public GCodeCommand CommandTurnLEDM106 { get; } = new("M106", "S{0}", "Turn LED");
@@ -321,6 +324,7 @@ public class GCodeBuilder : BindableBase
AppendLightOffM106();
AppendClearImage();
AppendHomeZG28();
+ AppendSyncMovements();
AppendLineIfCanComment(EndStartGCodeComments);
AppendLine();
}
@@ -376,6 +380,8 @@ public class GCodeBuilder : BindableBase
var time = ConvertFromSeconds(seconds);
if (seconds > 0) AppendWaitG4($"0{time}", "Sync movement");
}
+
+ AppendSyncMovements();
}
}
}
@@ -403,6 +409,7 @@ public class GCodeBuilder : BindableBase
}
}
+ AppendSyncMovements();
AppendMotorsOff();
AppendLineIfCanComment(EndEndGCodeComments);
}
@@ -450,6 +457,11 @@ public class GCodeBuilder : BindableBase
AppendLine(CommandHomeG28);
}
+ public void AppendSyncMovements()
+ {
+ AppendLine(CommandSyncMovements);
+ }
+
public void AppendMoveGx(float z, float feedRate)
{
if(_layerMoveCommand == GCodeMoveCommands.G0)
@@ -490,6 +502,8 @@ public class GCodeBuilder : BindableBase
AppendWaitG4($"0{time}", "Sync movement");
}
+ AppendSyncMovements();
+
if (waitAfterLift > 0)
{
AppendWaitG4(waitAfterLift, "Wait after lift");
@@ -510,6 +524,8 @@ public class GCodeBuilder : BindableBase
AppendWaitG4($"0{time}", "Sync movement");
}
+ AppendSyncMovements();
+
if (waitAfterRetract > 0)
{
AppendWaitG4(waitAfterRetract, "Wait after retract");
@@ -547,6 +563,8 @@ public class GCodeBuilder : BindableBase
AppendWaitG4($"0{time}", "Sync movement");
}
+ AppendSyncMovements();
+
if (waitAfterLift > 0)
{
AppendWaitG4(waitAfterLift, "Wait after lift");
@@ -567,6 +585,8 @@ public class GCodeBuilder : BindableBase
AppendWaitG4($"0{time}", "Sync movement");
}
+ AppendSyncMovements();
+
if (waitAfterRetract > 0)
{
AppendWaitG4(waitAfterRetract, "Wait after retract");
diff --git a/UVtools.Core/Gerber/Apertures/Aperture.cs b/UVtools.Core/Gerber/Apertures/Aperture.cs
index 071ef7f..84aee08 100644
--- a/UVtools.Core/Gerber/Apertures/Aperture.cs
+++ b/UVtools.Core/Gerber/Apertures/Aperture.cs
@@ -49,7 +49,7 @@ public abstract class Aperture
}
protected Aperture(GerberDocument document, int index, string name) : this(document, index) { Name = name; }
- public abstract void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected);
+ public abstract void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected);
public static Aperture? Parse(string line, GerberDocument document)
{
diff --git a/UVtools.Core/Gerber/Apertures/CircleAperture.cs b/UVtools.Core/Gerber/Apertures/CircleAperture.cs
index 0071b0f..219def1 100644
--- a/UVtools.Core/Gerber/Apertures/CircleAperture.cs
+++ b/UVtools.Core/Gerber/Apertures/CircleAperture.cs
@@ -10,7 +10,6 @@ using System.Drawing;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
-using UVtools.Core.Extensions;
namespace UVtools.Core.Gerber.Apertures;
@@ -29,10 +28,10 @@ public class CircleAperture : Aperture
}
#endregion
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
- CvInvoke.Circle(mat,
- GerberDocument.PositionMmToPx(at, xyPpmm),
- GerberDocument.SizeMmToPx(Diameter / 2, xyPpmm.Max()), color, -1, lineType);
+ CvInvoke.Circle(mat,
+ Document.PositionMmToPx(at),
+ Document.SizeMmToPx(Diameter / 2), color, -1, lineType);
}
} \ No newline at end of file
diff --git a/UVtools.Core/Gerber/Apertures/EllipseAperture.cs b/UVtools.Core/Gerber/Apertures/EllipseAperture.cs
index c2d967e..e65e43c 100644
--- a/UVtools.Core/Gerber/Apertures/EllipseAperture.cs
+++ b/UVtools.Core/Gerber/Apertures/EllipseAperture.cs
@@ -33,11 +33,11 @@ public class EllipseAperture : Aperture
}
#endregion
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
- CvInvoke.Ellipse(mat,
- GerberDocument.PositionMmToPx(at, xyPpmm),
- GerberDocument.SizeMmToPx(Axes.Width / 2, Axes.Height / 2, xyPpmm),
+ CvInvoke.Ellipse(mat,
+ Document.PositionMmToPx(at),
+ Document.SizeMmToPx(Axes.Width / 2, Axes.Height / 2),
0, 0, 360, color, -1, lineType);
}
} \ No newline at end of file
diff --git a/UVtools.Core/Gerber/Apertures/MacroAperture.cs b/UVtools.Core/Gerber/Apertures/MacroAperture.cs
index af403cb..55878d8 100644
--- a/UVtools.Core/Gerber/Apertures/MacroAperture.cs
+++ b/UVtools.Core/Gerber/Apertures/MacroAperture.cs
@@ -29,12 +29,12 @@ public class MacroAperture : Aperture
}
#endregion
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
foreach (var primitive in Macro)
{
//if(primitive.Name == "Comment") continue;
- primitive.DrawFlashD3(mat, xyPpmm, at, color, lineType);
+ primitive.DrawFlashD3(mat, at, color, lineType);
}
}
} \ No newline at end of file
diff --git a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs
index 0b8bb9a..1be1578 100644
--- a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs
+++ b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs
@@ -33,8 +33,8 @@ public class PolygonAperture : Aperture
#endregion
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
- mat.DrawPolygon(Vertices, GerberDocument.SizeMmToPx(Diameter / 2, xyPpmm.Max()), GerberDocument.PositionMmToPx(at, xyPpmm), color, 0, -1, lineType);
+ mat.DrawPolygon(Vertices, Document.SizeMmToPx(Diameter / 2), Document.PositionMmToPx(at), color, 0, -1, lineType);
}
} \ No newline at end of file
diff --git a/UVtools.Core/Gerber/Apertures/RectangleAperture.cs b/UVtools.Core/Gerber/Apertures/RectangleAperture.cs
index 3be7bc4..d49dde5 100644
--- a/UVtools.Core/Gerber/Apertures/RectangleAperture.cs
+++ b/UVtools.Core/Gerber/Apertures/RectangleAperture.cs
@@ -32,10 +32,9 @@ public class RectangleAperture : Aperture
}
#endregion
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color,
- LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
at = new PointF(Math.Max(0, at.X - Size.Width / 2), Math.Max(0, at.Y - Size.Height / 2));
- CvInvoke.Rectangle(mat, new Rectangle(GerberDocument.PositionMmToPx(at, xyPpmm), GerberDocument.SizeMmToPx(Size, xyPpmm)), color, -1, lineType);
+ CvInvoke.Rectangle(mat, new Rectangle(Document.PositionMmToPx(at), Document.SizeMmToPx(Size)), color, -1, lineType);
}
} \ No newline at end of file
diff --git a/UVtools.Core/Gerber/Enumerations.cs b/UVtools.Core/Gerber/Enumerations.cs
index d4ba962..01ce449 100644
--- a/UVtools.Core/Gerber/Enumerations.cs
+++ b/UVtools.Core/Gerber/Enumerations.cs
@@ -5,6 +5,10 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+
+using System;
+using System.ComponentModel;
+
namespace UVtools.Core.Gerber;
public enum GerberZerosSuppressionType : byte
@@ -50,4 +54,22 @@ public enum GerberMoveType : byte
Arc,
// G03
ArcCounterClockwise
+}
+
+public enum GerberMidpointRounding
+{
+ [Description("To even: The strategy of rounding to the nearest number, and when a number is halfway between two others, it's rounded toward the nearest even number.")]
+ ToEven = MidpointRounding.ToEven,
+
+ [Description("Away from zero: The strategy of rounding to the nearest number, and when a number is halfway between two others, it's rounded toward the nearest number that's away from zero.")]
+ AwayFromZero = MidpointRounding.AwayFromZero,
+
+ [Description("To zero: The strategy of directed rounding toward zero, with the result closest to and no greater in magnitude than the infinitely precise result.")]
+ ToZero = MidpointRounding.ToZero,
+
+ [Description("To negative inifity: The strategy of downwards-directed rounding, with the result closest to and no greater than the infinitely precise result.")]
+ ToNegativeInfinity = MidpointRounding.ToNegativeInfinity,
+
+ [Description("To positive inifity: The strategy of upwards-directed rounding, with the result closest to and no less than the infinitely precise result.")]
+ ToPositiveInfinity = MidpointRounding.ToPositiveInfinity
} \ No newline at end of file
diff --git a/UVtools.Core/Gerber/GerberDocument.cs b/UVtools.Core/Gerber/GerberDocument.cs
index 2e20db4..eca46fb 100644
--- a/UVtools.Core/Gerber/GerberDocument.cs
+++ b/UVtools.Core/Gerber/GerberDocument.cs
@@ -13,6 +13,7 @@ using System.IO;
using System.Text.RegularExpressions;
using Emgu.CV;
using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
using Emgu.CV.Util;
using UVtools.Core.Extensions;
using UVtools.Core.Gerber.Apertures;
@@ -31,6 +32,7 @@ public class GerberDocument
public GerberUnitType UnitType { get; set; } = GerberUnitType.Millimeter;
public GerberPolarityType Polarity { get; set; } = GerberPolarityType.Dark;
public GerberMoveType MoveType { get; set; } = GerberMoveType.Linear;
+ public GerberMidpointRounding SizeMidpointRounding { get; set; } = GerberMidpointRounding.AwayFromZero;
public byte CoordinateXIntegers { get; set; } = 3;
public byte CoordinateXFractionalDigits { get; set; } = 6;
@@ -45,6 +47,10 @@ public class GerberDocument
public Dictionary<int, Aperture> Apertures { get; } = new();
public Dictionary<string, Macro> Macros { get; } = new();
+ public SizeF XYppmm { get; init; }
+
+ public MCvScalar PolarityColor => Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor;
+
#endregion
@@ -56,10 +62,14 @@ public class GerberDocument
{
}
- public static GerberDocument ParseAndDraw(string filePath, Mat mat, SizeF xyPpmm, bool enableAntialiasing = false)
+ public static GerberDocument ParseAndDraw(string filePath, Mat mat, SizeF xyPpmm, GerberMidpointRounding sizeMidpointRounding = GerberMidpointRounding.AwayFromZero, bool enableAntialiasing = false)
{
using var file = new StreamReader(filePath);
- var document = new GerberDocument();
+ var document = new GerberDocument
+ {
+ SizeMidpointRounding = sizeMidpointRounding,
+ XYppmm = xyPpmm
+ };
int FSlength = "%FSLAX46Y46*%".Length;
int MOlength = "%MOMM*%".Length;
@@ -197,7 +207,7 @@ public class GerberDocument
if (regionPoints.Count > 0)
{
using var vec = new VectorOfPoint(regionPoints.ToArray());
- CvInvoke.FillPoly(mat, vec, document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.FillPoly(mat, vec, document.PolarityColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
}
//CvInvoke.Imshow("G37", mat);
//CvInvoke.WaitKey();
@@ -207,7 +217,7 @@ public class GerberDocument
if (line.StartsWith("%AM"))
{
- currentMacro = Macro.Parse(line);
+ currentMacro = Macro.Parse(document, line);
if (currentMacro is null) continue;
document.Macros.Add(currentMacro.Name, currentMacro);
//document.Apertures.Add(aperture.Index, aperture);
@@ -231,14 +241,15 @@ public class GerberDocument
if (!int.TryParse(matchD.Groups[1].Value, out var d)) continue;
- if(d < 10) continue;
- if (!document.Apertures.TryGetValue(d, out currentAperture)) continue;
-
- continue;
+ if (d >= 10)
+ {
+ document.Apertures.TryGetValue(d, out currentAperture);
+ continue;
+ }
}
- if (line[0] == 'X' || line[0] == 'Y')
+ if (line[0] == 'X' || line[0] == 'Y' || line[0] == 'D')
{
var matchX = Regex.Match(line, @"X-?(\d+)?");
var matchY = Regex.Match(line, @"Y-?(\d+)?");
@@ -300,12 +311,12 @@ public class GerberDocument
if (regionPoints.Count > 0)
{
using var vec = new VectorOfPoint(regionPoints.ToArray());
- CvInvoke.FillPoly(mat, vec, document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
+ CvInvoke.FillPoly(mat, vec, document.PolarityColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
}
regionPoints.Clear();
}
- var pt = PositionMmToPx(nowX, nowY, xyPpmm);
+ var pt = document.PositionMmToPx(nowX, nowY);
if (regionPoints.Count == 0 || (regionPoints.Count > 0 && regionPoints[^1] != pt)) regionPoints.Add(pt);
}
else if(currentAperture is not null)
@@ -352,10 +363,10 @@ public class GerberDocument
if (currentX == nowX && currentY == nowY) // Closed circle
{
CvInvoke.Ellipse(mat,
- PositionMmToPx(nowX + xOffset, nowY + yOffset, xyPpmm),
- SizeMmToPx(Math.Abs(xOffset), Math.Abs(xOffset), xyPpmm),
- 0, 0, 360.0, document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor,
- SizeMmToPx(circleAperture.Diameter, xyPpmm.Max()),
+ document.PositionMmToPx(nowX + xOffset, nowY + yOffset),
+ document.SizeMmToPx(Math.Abs(xOffset), Math.Abs(xOffset)),
+ 0, 0, 360.0, document.PolarityColor,
+ document.SizeMmToPx(circleAperture.Diameter),
enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected
);
}
@@ -365,7 +376,7 @@ public class GerberDocument
throw new NotImplementedException("Partial arcs are not yet implemented (G03)\nTo be fixed in the future...");
/*CvInvoke.Ellipse(mat, new Point((int)((nowX + xOffset) * xyPpmm.Width), (int)((currentY) * xyPpmm.Height)),
new Size((int)(Math.Abs(xOffset) * xyPpmm.Width), (int)(Math.Abs(yOffset) * xyPpmm.Width)),
- 0, Math.Abs(currentY - nowY), 360.0 / Math.Abs(currentX - nowX), document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor,
+ 0, Math.Abs(currentY - nowY), 360.0 / Math.Abs(currentX - nowX), document.PolarityColor,
(int)(circleAperture.Diameter * xyPpmm.Max()),
enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected
);*/
@@ -375,19 +386,35 @@ public class GerberDocument
else
{
CvInvoke.Line(mat,
- PositionMmToPx(currentX, currentY, xyPpmm),
+ document.PositionMmToPx(currentX, currentY),
+ document.PositionMmToPx(nowX, nowY),
+ document.PolarityColor,
+ EmguExtensions.CorrectThickness(document.SizeMmToPx(circleAperture.Diameter)),
+ enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
+ /*mat.DrawLineAccurate(PositionMmToPx(currentX, currentY, xyPpmm),
PositionMmToPx(nowX, nowY, xyPpmm),
- document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor,
+ document.PolarityColor
SizeMmToPx(circleAperture.Diameter, xyPpmm.Max()),
- enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
+ enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);*/
+
+ /*CvInvoke.DrawContours(mat, new VectorOfVectorOfPoint(new[]
+ {
+ new []
+ {
+ PositionMmToPx(currentX, currentY, xyPpmm),
+ PositionMmToPx(nowX, nowY, xyPpmm),
+ }
+ }), -1, document.PolarityColor, SizeMmToPx(circleAperture.Diameter, xyPpmm.Max()));*/
+ //CvInvoke.Imshow("Line", mat);
+ //CvInvoke.WaitKey();
}
}
}
else if (d == 3)
{
- currentAperture.DrawFlashD3(mat, xyPpmm, new PointF((float) nowX, (float) nowY),
- document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
+ currentAperture.DrawFlashD3(mat, new PointF((float) nowX, (float) nowY),
+ document.PolarityColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
//CvInvoke.Imshow("G37", mat);
//CvInvoke.WaitKey();
}
@@ -426,32 +453,38 @@ public class GerberDocument
return new PointF(point.X * (float)UnitExtensions.InchToMillimeter, point.Y * (float)UnitExtensions.InchToMillimeter);
}
- public static Point PositionMmToPx(PointF atMm, SizeF xyPpmm)
- => new((int)Math.Round(atMm.X * xyPpmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atMm.Y * xyPpmm.Height, MidpointRounding.AwayFromZero));
+ public Point PositionMmToPx(PointF atMm)
+ => new((int)Math.Round(atMm.X * XYppmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atMm.Y * XYppmm.Height, MidpointRounding.AwayFromZero));
+
+ public Point PositionMmToPx(double atXmm, double atYmm)
+ => new((int)Math.Round(atXmm * XYppmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atYmm * XYppmm.Height, MidpointRounding.AwayFromZero));
+
+ public Point PositionMmToPx(float atXmm, float atYmm)
+ => new((int)Math.Round(atXmm * XYppmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atYmm * XYppmm.Height, MidpointRounding.AwayFromZero));
- public static Point PositionMmToPx(double atXmm, double atYmm, SizeF xyPpmm)
- => new((int)Math.Round(atXmm * xyPpmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atYmm * xyPpmm.Height, MidpointRounding.AwayFromZero));
+ public Size SizeMmToPx(SizeF sizeMm)
+ => new((int)Math.Max(1, Math.Round(sizeMm.Width * XYppmm.Width, (MidpointRounding)SizeMidpointRounding)),
+ (int)Math.Max(1, Math.Round(sizeMm.Height * XYppmm.Height, (MidpointRounding)SizeMidpointRounding)));
- public static Point PositionMmToPx(float atXmm, float atYmm, SizeF xyPpmm)
- => new((int)Math.Round(atXmm * xyPpmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atYmm * xyPpmm.Height, MidpointRounding.AwayFromZero));
+ public Size SizeMmToPx(double sizeXmm, double sizeYmm)
+ => new((int)Math.Max(1, Math.Round(sizeXmm * XYppmm.Width, (MidpointRounding)SizeMidpointRounding)),
+ (int)Math.Max(1, Math.Round(sizeYmm * XYppmm.Height, (MidpointRounding)SizeMidpointRounding)));
- public static Size SizeMmToPx(SizeF sizeMm, SizeF xyPpmm)
- => new((int)Math.Max(1, Math.Round(sizeMm.Width * xyPpmm.Width, MidpointRounding.AwayFromZero)),
- (int)Math.Max(1, Math.Round(sizeMm.Height * xyPpmm.Height, MidpointRounding.AwayFromZero)));
+ public Size SizeMmToPx(float sizeXmm, float sizeYmm)
+ => new((int)Math.Max(1, Math.Round(sizeXmm * XYppmm.Width, (MidpointRounding)SizeMidpointRounding)),
+ (int)Math.Max(1, Math.Round(sizeYmm * XYppmm.Height, (MidpointRounding)SizeMidpointRounding)));
- public static Size SizeMmToPx(double sizeXmm, double sizeYmm, SizeF xyPpmm)
- => new((int)Math.Max(1, Math.Round(sizeXmm * xyPpmm.Width, MidpointRounding.AwayFromZero)),
- (int)Math.Max(1, Math.Round(sizeYmm * xyPpmm.Height, MidpointRounding.AwayFromZero)));
+ public int SizeMmToPx(float sizeMm)
+ => (int) Math.Max(1, Math.Round(sizeMm * XYppmm.Max(), (MidpointRounding)SizeMidpointRounding));
- public static Size SizeMmToPx(float sizeXmm, float sizeYmm, SizeF xyPpmm)
- => new((int)Math.Max(1, Math.Round(sizeXmm * xyPpmm.Width, MidpointRounding.AwayFromZero)),
- (int)Math.Max(1, Math.Round(sizeYmm * xyPpmm.Height, MidpointRounding.AwayFromZero)));
+ public int SizeMmToPx(double sizeMm)
+ => (int)Math.Max(1, Math.Round(sizeMm * XYppmm.Max(), (MidpointRounding)SizeMidpointRounding));
- public static int SizeMmToPx(float sizeMm, float ppmm)
- => (int) Math.Max(1, Math.Round(sizeMm * ppmm, MidpointRounding.AwayFromZero));
+ public int SizeMmToPxOverride(float sizeMm, float ppmm)
+ => (int)Math.Max(1, Math.Round(sizeMm * ppmm, (MidpointRounding)SizeMidpointRounding));
- public static int SizeMmToPx(double sizeMm, float ppmm)
- => (int)Math.Max(1, Math.Round(sizeMm * ppmm, MidpointRounding.AwayFromZero));
+ public int SizeMmToPxOverride(double sizeMm, float ppmm)
+ => (int)Math.Max(1, Math.Round(sizeMm * ppmm, (MidpointRounding)SizeMidpointRounding));
}
diff --git a/UVtools.Core/Gerber/Macro.cs b/UVtools.Core/Gerber/Macro.cs
index 6a5d3c1..aa1b721 100644
--- a/UVtools.Core/Gerber/Macro.cs
+++ b/UVtools.Core/Gerber/Macro.cs
@@ -18,6 +18,8 @@ public class Macro : IReadOnlyList<Primitive>
{
#region Properties
+ public GerberDocument Document { get; init; }
+
/// <summary>
/// Gets the macro name
/// </summary>
@@ -26,9 +28,12 @@ public class Macro : IReadOnlyList<Primitive>
public List<Primitive> Primitives { get; } = new();
#endregion
- public Macro() { }
+ public Macro(GerberDocument document)
+ {
+ Document = document;
+ }
- public Macro(string name)
+ public Macro(GerberDocument document, string name) : this(document)
{
Name = name;
}
@@ -40,7 +45,7 @@ public class Macro : IReadOnlyList<Primitive>
// 0 Comment: A comment string
if (line[0] == '0')
{
- if(line.Length > 2) Primitives.Add(new CommentPrimitive(line[2..]));
+ if(line.Length > 2) Primitives.Add(new CommentPrimitive(Document, line[2..]));
return;
}
@@ -51,7 +56,7 @@ public class Macro : IReadOnlyList<Primitive>
// 1 Circle: Exposure, Diameter, Center X, Center Y[, Rotation]
case CirclePrimitive.Code:
{
- var primitive = new CirclePrimitive(commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4]);
+ var primitive = new CirclePrimitive(Document, commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4]);
if (commaSplit.Length > 5) primitive.RotationExpression = commaSplit[5];
Primitives.Add(primitive);
break;
@@ -59,7 +64,7 @@ public class Macro : IReadOnlyList<Primitive>
// 20 Vector Line: Exposure, Width, Start X, Start Y, End X, End Y, Rotation
case VectorLinePrimitive.Code:
{
- var primitive = new VectorLinePrimitive(commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4], commaSplit[5], commaSplit[6]);
+ var primitive = new VectorLinePrimitive(Document, commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4], commaSplit[5], commaSplit[6]);
if (commaSplit.Length > 7) primitive.RotationExpression = commaSplit[7];
Primitives.Add(primitive);
break;
@@ -67,7 +72,7 @@ public class Macro : IReadOnlyList<Primitive>
// 21 Center Line: Exposure, Width, Hight, Center X, Center Y, Rotation
case CenterLinePrimitive.Code:
{
- var primitive = new CenterLinePrimitive(commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4], commaSplit[5]);
+ var primitive = new CenterLinePrimitive(Document, commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4], commaSplit[5]);
if (commaSplit.Length > 6) primitive.RotationExpression = commaSplit[6];
Primitives.Add(primitive);
break;
@@ -75,13 +80,13 @@ public class Macro : IReadOnlyList<Primitive>
// 4 Outline: Exposure, # vertices, Start X, Start Y, Subsequent points..., Rotation
case OutlinePrimitive.Code:
{
- Primitives.Add(new OutlinePrimitive(commaSplit[1], commaSplit[3..^1], commaSplit[^1]));
+ Primitives.Add(new OutlinePrimitive(Document, commaSplit[1], commaSplit[3..^1], commaSplit[^1]));
break;
}
// 5 Outline: Exposure, # vertices, Start X, Start Y, Subsequent points..., Rotation
case PolygonPrimitive.Code:
{
- var primitive = new PolygonPrimitive(commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4], commaSplit[5]);
+ var primitive = new PolygonPrimitive(Document, commaSplit[1], commaSplit[2], commaSplit[3], commaSplit[4], commaSplit[5]);
if (commaSplit.Length > 6) primitive.RotationExpression = commaSplit[6];
Primitives.Add(primitive);
break;
@@ -91,7 +96,7 @@ public class Macro : IReadOnlyList<Primitive>
public Macro Clone()
{
- var macro = new Macro(Name);
+ var macro = new Macro(Document, Name);
foreach (var primitive in Primitives)
{
macro.Primitives.Add(primitive.Clone());
@@ -101,12 +106,12 @@ public class Macro : IReadOnlyList<Primitive>
}
- public static Macro? Parse(string line)
+ public static Macro? Parse(GerberDocument document, string line)
{
var match = Regex.Match(line, @"\%AM(\S+)\*");
if (!match.Success || match.Groups.Count < 2) return null;
- return new Macro(match.Groups[1].Value);
+ return new Macro(document, match.Groups[1].Value);
}
public IEnumerator<Primitive> GetEnumerator()
diff --git a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs
index 6e6d751..8b3c3b6 100644
--- a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs
+++ b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs
@@ -74,9 +74,9 @@ public class CenterLinePrimitive : Primitive
public float Rotation { get; set; } = 0;
#endregion
- protected CenterLinePrimitive() { }
+ protected CenterLinePrimitive(GerberDocument document) : base(document) { }
- public CenterLinePrimitive(string exposureExpression, string widthExpression = "0", string heightExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0")
+ public CenterLinePrimitive(GerberDocument document, string exposureExpression, string widthExpression = "0", string heightExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0") : base(document)
{
ExposureExpression = exposureExpression;
WidthExpression = widthExpression;
@@ -87,8 +87,7 @@ public class CenterLinePrimitive : Primitive
}
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color,
- LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
if (!IsParsed) return;
if (Width <= 0 || Height <= 0) return;
@@ -97,9 +96,9 @@ public class CenterLinePrimitive : Primitive
else if(color.V0 == 0) color = EmguExtensions.WhiteColor;
var halfWidth = Width / 2;
- var pt1 = GerberDocument.PositionMmToPx(at.X + CenterX - halfWidth, at.Y + CenterY, xyPpmm);
- var pt2 = GerberDocument.PositionMmToPx(at.X + CenterX + halfWidth, at.Y + CenterY, xyPpmm);
- CvInvoke.Line(mat, pt1, pt2, color, GerberDocument.SizeMmToPx(Height, xyPpmm.Height), lineType);
+ var pt1 = Document.PositionMmToPx(at.X + CenterX - halfWidth, at.Y + CenterY);
+ var pt2 = Document.PositionMmToPx(at.X + CenterX + halfWidth, at.Y + CenterY);
+ CvInvoke.Line(mat, pt1, pt2, color, EmguExtensions.CorrectThickness(Document.SizeMmToPxOverride(Height, Document.XYppmm.Height)), lineType);
//CvInvoke.Rectangle(mat, rectangle, color, -1, lineType);
}
diff --git a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs
index 5364e0d..a06b627 100644
--- a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs
+++ b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs
@@ -67,9 +67,9 @@ public class CirclePrimitive : Primitive
public float Rotation { get; set; } = 0;
#endregion
- protected CirclePrimitive() { }
+ protected CirclePrimitive(GerberDocument document) : base(document) { }
- public CirclePrimitive(string exposureExpression = "1", string diameterExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0")
+ public CirclePrimitive(GerberDocument document, string exposureExpression = "1", string diameterExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0") : base(document)
{
ExposureExpression = exposureExpression;
DiameterExpression = diameterExpression;
@@ -78,7 +78,7 @@ public class CirclePrimitive : Primitive
RotationExpression = rotationExpression;
}
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color,
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color,
LineType lineType = LineType.EightConnected)
{
if (!IsParsed) return;
@@ -88,8 +88,8 @@ public class CirclePrimitive : Primitive
else if (color.V0 == 0) color = EmguExtensions.WhiteColor;
CvInvoke.Circle(mat,
- GerberDocument.PositionMmToPx(at.X + CenterX, at.Y + CenterY, xyPpmm),
- GerberDocument.SizeMmToPx(Diameter / 2, xyPpmm.Max()), color, -1, lineType);
+ Document.PositionMmToPx(at.X + CenterX, at.Y + CenterY),
+ Document.SizeMmToPx(Diameter / 2), color, -1, lineType);
}
public override void ParseExpressions(GerberDocument document, params string[] args)
diff --git a/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs b/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs
index a909911..a6539ec 100644
--- a/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs
+++ b/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs
@@ -33,19 +33,19 @@ public class CommentPrimitive : Primitive
public string Comment { get; set; } = string.Empty;
#endregion
- public CommentPrimitive()
+ public CommentPrimitive(GerberDocument document) : base(document)
{
IsParsed = true;
}
- public CommentPrimitive(string comment)
+ public CommentPrimitive(GerberDocument document, string comment) : base(document)
{
Comment = comment;
IsParsed = true;
}
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
}
diff --git a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs
index 80ecfaf..08b17ee 100644
--- a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs
+++ b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs
@@ -66,9 +66,9 @@ public class OutlinePrimitive : Primitive
public float Rotation { get; set; } = 0;
#endregion
- protected OutlinePrimitive() { }
+ protected OutlinePrimitive(GerberDocument document) : base(document) { }
- public OutlinePrimitive(string exposureExpression, string[] coordinatesExpression, string rotationExpression)
+ public OutlinePrimitive(GerberDocument document, string exposureExpression, string[] coordinatesExpression, string rotationExpression) : base(document)
{
ExposureExpression = exposureExpression;
CoordinatesExpression = coordinatesExpression;
@@ -76,7 +76,7 @@ public class OutlinePrimitive : Primitive
}
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected)
{
if (Coordinates.Length < 3) return;
@@ -86,7 +86,7 @@ public class OutlinePrimitive : Primitive
var points = new List<Point>();
for (int i = 0; i < Coordinates.Length-1; i++)
{
- var pt = GerberDocument.PositionMmToPx(at.X + Coordinates[i].X, at.Y + Coordinates[i].Y, xyPpmm);
+ var pt = Document.PositionMmToPx(at.X + Coordinates[i].X, at.Y + Coordinates[i].Y);
if(i > 0 && points[i-1] == pt) continue; // Prevent duplicates
points.Add(pt);
}
diff --git a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs
index 2bb7289..1566b75 100644
--- a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs
+++ b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs
@@ -72,9 +72,9 @@ public class PolygonPrimitive : Primitive
public float Rotation { get; set; } = 0;
#endregion
- protected PolygonPrimitive() { }
+ protected PolygonPrimitive(GerberDocument document) : base(document) { }
- public PolygonPrimitive(string exposureExpression, string verticesCountExpression, string centerXExpression = "0", string centerYExpression = "0", string diameterExpression = "0", string rotationExpression = "0")
+ public PolygonPrimitive(GerberDocument document, string exposureExpression, string verticesCountExpression, string centerXExpression = "0", string centerYExpression = "0", string diameterExpression = "0", string rotationExpression = "0") : base(document)
{
ExposureExpression = exposureExpression;
VerticesCountExpression = verticesCountExpression;
@@ -84,7 +84,7 @@ public class PolygonPrimitive : Primitive
RotationExpression = rotationExpression;
}
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color,
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color,
LineType lineType = LineType.EightConnected)
{
if (!IsParsed) return;
@@ -94,9 +94,9 @@ public class PolygonPrimitive : Primitive
else if (color.V0 == 0) color = EmguExtensions.WhiteColor;
- mat.DrawPolygon(VerticesCount,
- GerberDocument.SizeMmToPx(Diameter / 2, xyPpmm.Max()),
- GerberDocument.PositionMmToPx(at.X + CenterX, at.Y + CenterY, xyPpmm),
+ mat.DrawPolygon(VerticesCount,
+ Document.SizeMmToPx(Diameter / 2),
+ Document.PositionMmToPx(at.X + CenterX, at.Y + CenterY),
color, 0, -1, lineType);
}
diff --git a/UVtools.Core/Gerber/Primitives/Primitive.cs b/UVtools.Core/Gerber/Primitives/Primitive.cs
index 0a7740f..75a20a7 100644
--- a/UVtools.Core/Gerber/Primitives/Primitive.cs
+++ b/UVtools.Core/Gerber/Primitives/Primitive.cs
@@ -6,7 +6,6 @@
* of this license document, but changing it is not allowed.
*/
-using System;
using System.Drawing;
using Emgu.CV;
using Emgu.CV.CvEnum;
@@ -21,11 +20,18 @@ public abstract class Primitive
public bool IsParsed { get; protected set; } = false;
+ public GerberDocument Document { get; init; }
+
#endregion
- protected Primitive() { }
+ //protected Primitive() { }
+
+ protected Primitive(GerberDocument document)
+ {
+ Document = document;
+ }
- public abstract void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected);
+ public abstract void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected);
public abstract void ParseExpressions(GerberDocument document, params string[] args);
diff --git a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs
index 9916a1d..624e4ad 100644
--- a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs
+++ b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs
@@ -83,9 +83,9 @@ public class VectorLinePrimitive : Primitive
public float Rotation { get; set; } = 0;
#endregion
- protected VectorLinePrimitive() { }
+ protected VectorLinePrimitive(GerberDocument document) : base(document) { }
- public VectorLinePrimitive(string exposureExpression, string lineWidthExpression, string startXExpression, string startYExpression, string endXExpression, string endYExpression, string rotationExpression = "0")
+ public VectorLinePrimitive(GerberDocument document, string exposureExpression, string lineWidthExpression, string startXExpression, string startYExpression, string endXExpression, string endYExpression, string rotationExpression = "0") : base(document)
{
ExposureExpression = exposureExpression;
LineWidthExpression = lineWidthExpression;
@@ -96,7 +96,7 @@ public class VectorLinePrimitive : Primitive
RotationExpression = rotationExpression;
}
- public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color,
+ public override void DrawFlashD3(Mat mat, PointF at, MCvScalar color,
LineType lineType = LineType.EightConnected)
{
if (!IsParsed) return;
@@ -105,9 +105,9 @@ public class VectorLinePrimitive : Primitive
if (Exposure == 0) color = EmguExtensions.BlackColor;
else if (color.V0 == 0) color = EmguExtensions.WhiteColor;
- var pt1 = GerberDocument.PositionMmToPx(at.X + StartX, at.Y + StartY, xyPpmm);
- var pt2 = GerberDocument.PositionMmToPx(at.X + EndX, at.Y + EndY, xyPpmm);
- CvInvoke.Line(mat, pt1, pt2, color, GerberDocument.SizeMmToPx(LineWidth, xyPpmm.Height), lineType);
+ var pt1 = Document.PositionMmToPx(at.X + StartX, at.Y + StartY);
+ var pt2 = Document.PositionMmToPx(at.X + EndX, at.Y + EndY);
+ CvInvoke.Line(mat, pt1, pt2, color, EmguExtensions.CorrectThickness(Document.SizeMmToPxOverride(LineWidth, Document.XYppmm.Height)), lineType);
//CvInvoke.Rectangle(mat, rectangle, color, -1, lineType);
}
diff --git a/UVtools.Core/Objects/UInt24BigEndian.cs b/UVtools.Core/Objects/UInt24BigEndian.cs
new file mode 100644
index 0000000..9515ea3
--- /dev/null
+++ b/UVtools.Core/Objects/UInt24BigEndian.cs
@@ -0,0 +1,59 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using BinarySerialization;
+using UVtools.Core.Extensions;
+
+namespace UVtools.Core.Objects;
+
+/// <summary>
+/// A string that always end with 0x00 if not null
+/// It contains the string length as uint
+/// </summary>
+public sealed class UInt24BigEndian
+{
+ [FieldOrder(0)] [FieldCount(3)] public byte[] Bytes { get; set; } = new byte[3];
+
+
+ [Ignore]
+ public uint Value
+ {
+ get => BitExtensions.ToUIntBigEndian(0, Bytes[0], Bytes[1], Bytes[2]);
+ set
+ {
+ var bytes = BitExtensions.ToBytesBigEndian(value);
+ Bytes[0] = bytes[1];
+ Bytes[1] = bytes[2];
+ Bytes[2] = bytes[3];
+ }
+ }
+
+ public UInt24BigEndian() { }
+
+ public UInt24BigEndian(uint value)
+ {
+ Value = value;
+ }
+
+ public override string ToString() => Value.ToString();
+
+ private bool Equals(UInt24BigEndian other)
+ {
+ return Value == other.Value;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return ReferenceEquals(this, obj) || obj is UInt24BigEndian other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return (int)Value;
+ }
+} \ No newline at end of file
diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs
index b8ddb40..7e027a7 100644
--- a/UVtools.Core/Operations/OperationLayerArithmetic.cs
+++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs
@@ -9,8 +9,8 @@
using Emgu.CV;
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Serialization;
using UVtools.Core.FileFormats;
@@ -51,6 +51,16 @@ public class OperationLayerArithmetic : Operation
Operator = layerArithmeticOperator;
}
}
+
+ public sealed class ArithmeticOperationGroup
+ {
+ public List<uint> SetLayers { get; set; } = new();
+ public List<ArithmeticOperation> Operations { get; set; } = new();
+
+ public bool IsValid =>
+ SetLayers.Count > 0 && Operations.Count > 0 &&
+ !(SetLayers.Count == 1 && Operations.Count == 1 && SetLayers[0] == Operations[0].LayerIndex);
+ }
#endregion
#region Overrides
@@ -70,7 +80,8 @@ public class OperationLayerArithmetic : Operation
"Example 2: 10,11,12 = 11+12-10*5 Same as: 10:12 = 11+12-10*5\n" +
"On example 1 the layer 10 will be set with the result of layer 10 plus layer 11.\n" +
"On example 2 the layers 10,11,12 will be set with the result of layer 11 plus 12 minus 10 all multiplied by layer 5.\n\n" +
- "Note: Calculation are made sequential, math order rules wont apply here.";
+ "Note: Calculation are made sequential, math order rules wont apply here.\n" +
+ "Use ; to split and start a new arithmetic operation.";
public override string ConfirmationText =>
$"perform this arithmetic operation";
@@ -87,10 +98,10 @@ public class OperationLayerArithmetic : Operation
sb.AppendLine("The sentence is empty.");
else if(!Parse())
sb.AppendLine("Unable to parse the sentence, malformed or incomplete.");
- else if (SetLayers.Count == 0)
+ /*else if (SetLayers.Count == 0)
sb.AppendLine("No layers to assign.");
- else if (Operations.Count == 0)
- sb.AppendLine("No operations to perform.");
+ else if (Core.Operations.Count == 0)
+ sb.AppendLine("No operations to perform.");*/
else if (!IsValid)
sb.AppendLine("The operation will have no impact and will not be performed.");
@@ -112,13 +123,9 @@ public class OperationLayerArithmetic : Operation
set => RaiseAndSetIfChanged(ref _sentence, value);
}
[XmlIgnore]
- public List<ArithmeticOperation> Operations { get; private set; } = new();
-
- [XmlIgnore]
- public List<uint> SetLayers { get; private set; } = new();
+ public List<ArithmeticOperationGroup> Operations { get; private set; } = new();
- public bool IsValid => SetLayers.Count > 0 && Operations.Count > 0 &&
- !(SetLayers.Count == 1 && Operations.Count == 1 && SetLayers[0] == Operations[0].LayerIndex);
+ public bool IsValid => Operations.Count > 0;
#endregion
#region Constructor
@@ -134,109 +141,99 @@ public class OperationLayerArithmetic : Operation
public bool Parse()
{
if (string.IsNullOrEmpty(_sentence)) return false;
- SetLayers.Clear();
Operations.Clear();
- var splitSentence = _sentence.Split('=');
- var operations = splitSentence[0];
- if (splitSentence.Length >= 2)
+
+ var sentences = Regex.Replace(_sentence, @"\s+", string.Empty)
+ .Split(';', StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (var sentence in sentences)
{
- operations = splitSentence[1];
- var setLayers = splitSentence[0].Replace(" ", string.Empty).Split(',', StringSplitOptions.TrimEntries);
- foreach (var layer in setLayers)
+ var group = new ArithmeticOperationGroup();
+ var splitSentence = sentence.Split('=');
+ var operations = splitSentence[0];
+ if (splitSentence.Length >= 2)
{
- var rangeSplit = layer.Split(':', StringSplitOptions.TrimEntries);
- if (rangeSplit.Length > 1)
+ operations = splitSentence[1];
+ var setLayers = splitSentence[0].Split(',', StringSplitOptions.RemoveEmptyEntries);
+ foreach (var layer in setLayers)
{
- uint.TryParse(rangeSplit[0].Trim(), out var startLayer);
- if (!uint.TryParse(rangeSplit[1].Trim(), out var endLayer)) endLayer = SlicerFile.LastLayerIndex;
- for (var index = startLayer; index <= endLayer; index++)
+ var rangeSplit = layer.Split(':', StringSplitOptions.RemoveEmptyEntries);
+ if (rangeSplit.Length > 1)
{
- if (SetLayers.Contains(index)) continue;
- SetLayers.Add(index);
+ uint.TryParse(rangeSplit[0], out var startLayer);
+ if (!uint.TryParse(rangeSplit[1], out var endLayer)) endLayer = SlicerFile.LastLayerIndex;
+ endLayer = Math.Min(endLayer, SlicerFile.LastLayerIndex);
+ for (var index = startLayer; index <= endLayer; index++)
+ {
+ if (group.SetLayers.Contains(index)) continue;
+ group.SetLayers.Add(index);
+ }
+ continue;
}
- continue;
- }
- if (!uint.TryParse(layer.Trim(), out var layerIndex)) continue;
- if (SetLayers.Contains(layerIndex)) continue;
- SetLayers.Add(layerIndex);
+ if (!uint.TryParse(layer, out var layerIndex)) continue;
+ if (group.SetLayers.Contains(layerIndex)) continue;
+ group.SetLayers.Add(layerIndex);
+ }
}
- }
- SetLayers = SetLayers.Where(layerIndex => layerIndex <= SlicerFile.LastLayerIndex).ToList();
- SetLayers.Sort();
+ group.SetLayers.Sort();
- operations = operations.Replace(" ", string.Empty);
- if (string.IsNullOrWhiteSpace(operations)) return false;
+ if (string.IsNullOrWhiteSpace(operations)) return false;
- string layerIndexStr = string.Empty;
- foreach (char c in operations)
- {
- if (c >= '0' && c <= '9')
+ string layerIndexStr = string.Empty;
+ foreach (char c in operations)
{
- layerIndexStr += c;
- continue;
- }
-
- LayerArithmeticOperators op = LayerArithmeticOperators.None;
- switch (c)
- {
- case '+':
- op = LayerArithmeticOperators.Add;
- break;
- case '-':
- op = LayerArithmeticOperators.Subtract;
- break;
- case '*':
- op = LayerArithmeticOperators.Multiply;
- break;
- case '/':
- op = LayerArithmeticOperators.Divide;
- break;
- case '&':
- op = LayerArithmeticOperators.BitwiseAnd;
- break;
- case '|':
- op = LayerArithmeticOperators.BitwiseOr;
- break;
- case '^':
- op = LayerArithmeticOperators.BitwiseXor;
- break;
- case '$':
- op = LayerArithmeticOperators.AbsDiff;
- break;
- }
+ if (c is >= '0' and <= '9')
+ {
+ layerIndexStr += c;
+ continue;
+ }
- if (op == LayerArithmeticOperators.None // No valid operator
- || string.IsNullOrWhiteSpace(layerIndexStr) // Started with a operator instead of layer
- ) continue;
+ var op = c switch
+ {
+ '+' => LayerArithmeticOperators.Add,
+ '-' => LayerArithmeticOperators.Subtract,
+ '*' => LayerArithmeticOperators.Multiply,
+ '/' => LayerArithmeticOperators.Divide,
+ '&' => LayerArithmeticOperators.BitwiseAnd,
+ '|' => LayerArithmeticOperators.BitwiseOr,
+ '^' => LayerArithmeticOperators.BitwiseXor,
+ '$' => LayerArithmeticOperators.AbsDiff,
+ _ => LayerArithmeticOperators.None
+ };
+
+ if (op == LayerArithmeticOperators.None // No valid operator
+ || string.IsNullOrWhiteSpace(layerIndexStr) // Started with a operator instead of layer
+ ) continue;
+
+
+ if (uint.TryParse(layerIndexStr, out var layerIndex) && layerIndex <= SlicerFile.LastLayerIndex)
+ {
+ group.Operations.Add(new ArithmeticOperation(layerIndex, op));
+ }
+ // Reset layer string
+ layerIndexStr = string.Empty;
+ }
- if (uint.TryParse(layerIndexStr, out var layerIndex))
+ // Append the left over
+ if (!string.IsNullOrWhiteSpace(layerIndexStr))
{
- Operations.Add(new ArithmeticOperation(layerIndex, op));
+ if (uint.TryParse(layerIndexStr, out var layerIndex) && layerIndex <= SlicerFile.LastLayerIndex)
+ {
+ group.Operations.Add(new ArithmeticOperation(layerIndex, LayerArithmeticOperators.None));
+ }
}
- // Reset layer string
- layerIndexStr = string.Empty;
- }
-
- // Append the left over
- if (!string.IsNullOrWhiteSpace(layerIndexStr))
- {
- if (uint.TryParse(layerIndexStr, out var layerIndex))
+ //if (Operations.Count == 0) return false;
+ if (group.SetLayers.Count == 0 && group.Operations.Count > 0)
{
- Operations.Add(new ArithmeticOperation(layerIndex, LayerArithmeticOperators.None));
+ group.SetLayers.Add(group.Operations[0].LayerIndex);
}
- }
- Operations = Operations.Where(op => op.LayerIndex <= SlicerFile.LastLayerIndex).ToList();
-
- //if (Operations.Count == 0) return false;
- if (SetLayers.Count == 0 && Operations.Count > 0)
- {
- SetLayers.Add(Operations[0].LayerIndex);
+ Operations.Add(group);
}
return true;
@@ -246,65 +243,72 @@ public class OperationLayerArithmetic : Operation
{
if (!IsValid) return false;
- using var result = SlicerFile[Operations[0].LayerIndex].LayerMat;
- using var resultRoi = GetRoiOrDefault(result);
- using var imageMask = GetMask(resultRoi);
-
- progress.ItemCount = (uint) Operations.Count;
- for (int i = 1; i < Operations.Count; i++)
+ foreach (var operation in Operations)
{
+ if(!operation.IsValid) continue;
+
progress.ThrowIfCancellationRequested();
- using var image = SlicerFile[Operations[i].LayerIndex].LayerMat;
- var imageRoi = GetRoiOrDefault(image);
-
- switch (Operations[i - 1].Operator)
+ using var result = SlicerFile[operation.Operations[0].LayerIndex].LayerMat;
+ using var resultRoi = GetRoiOrDefault(result);
+ using var imageMask = GetMask(resultRoi);
+
+ progress.ItemCount = (uint)operation.Operations.Count;
+ for (int i = 1; i < operation.Operations.Count; i++)
{
- case LayerArithmeticOperators.Add:
- CvInvoke.Add(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.Subtract:
- CvInvoke.Subtract(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.Multiply:
- CvInvoke.Multiply(resultRoi, imageRoi, resultRoi);
- break;
- case LayerArithmeticOperators.Divide:
- CvInvoke.Divide(resultRoi, imageRoi, resultRoi);
- break;
- case LayerArithmeticOperators.BitwiseAnd:
- CvInvoke.BitwiseAnd(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.BitwiseOr:
- CvInvoke.BitwiseOr(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.BitwiseXor:
- CvInvoke.BitwiseXor(resultRoi, imageRoi, resultRoi, imageMask);
- break;
- case LayerArithmeticOperators.AbsDiff:
- CvInvoke.AbsDiff(resultRoi, imageRoi, resultRoi);
- break;
- }
+ progress.ThrowIfCancellationRequested();
+ using var image = SlicerFile[operation.Operations[i].LayerIndex].LayerMat;
+ var imageRoi = GetRoiOrDefault(image);
- progress++;
- }
+ switch (operation.Operations[i - 1].Operator)
+ {
+ case LayerArithmeticOperators.Add:
+ CvInvoke.Add(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.Subtract:
+ CvInvoke.Subtract(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.Multiply:
+ CvInvoke.Multiply(resultRoi, imageRoi, resultRoi);
+ break;
+ case LayerArithmeticOperators.Divide:
+ CvInvoke.Divide(resultRoi, imageRoi, resultRoi);
+ break;
+ case LayerArithmeticOperators.BitwiseAnd:
+ CvInvoke.BitwiseAnd(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.BitwiseOr:
+ CvInvoke.BitwiseOr(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.BitwiseXor:
+ CvInvoke.BitwiseXor(resultRoi, imageRoi, resultRoi, imageMask);
+ break;
+ case LayerArithmeticOperators.AbsDiff:
+ CvInvoke.AbsDiff(resultRoi, imageRoi, resultRoi);
+ break;
+ }
- progress.Reset("Applied layers", (uint) SetLayers.Count);
- Parallel.ForEach(SetLayers, CoreSettings.GetParallelOptions(progress), layerIndex =>
- {
- progress.LockAndIncrement();
- if (Operations.Count == 1 || HaveROIorMask)
- {
- using var mat = SlicerFile[layerIndex].LayerMat;
- var matRoi = GetRoiOrDefault(mat);
- resultRoi.CopyTo(matRoi, imageMask);
- SlicerFile[layerIndex].LayerMat = mat;
- return;
+ progress++;
}
- //ApplyMask(mat, resultRoi, imageMask);
+ progress.Reset("Applied layers", (uint)operation.SetLayers.Count);
+ Parallel.ForEach(operation.SetLayers, CoreSettings.GetParallelOptions(progress), layerIndex =>
+ {
+ progress.LockAndIncrement();
+ if (operation.Operations.Count == 1 || HaveROIorMask)
+ {
+ using var mat = SlicerFile[layerIndex].LayerMat;
+ var matRoi = GetRoiOrDefault(mat);
+ resultRoi.CopyTo(matRoi, imageMask);
+ SlicerFile[layerIndex].LayerMat = mat;
+ return;
+ }
- SlicerFile[layerIndex].LayerMat = result;
- });
+ //ApplyMask(mat, resultRoi, imageMask);
+
+ SlicerFile[layerIndex].LayerMat = result;
+ });
+ }
+
return !progress.Token.IsCancellationRequested;
}
diff --git a/UVtools.Core/Operations/OperationPCBExposure.cs b/UVtools.Core/Operations/OperationPCBExposure.cs
index 45aefce..3b50b45 100644
--- a/UVtools.Core/Operations/OperationPCBExposure.cs
+++ b/UVtools.Core/Operations/OperationPCBExposure.cs
@@ -26,7 +26,7 @@ namespace UVtools.Core.Operations;
[Serializable]
public class OperationPCBExposure : Operation
{
- #region
+ #region Static
public static string[] ValidExtensions => new[]
{
@@ -48,6 +48,7 @@ public class OperationPCBExposure : Operation
private bool _mergeFiles;
private decimal _layerHeight;
private decimal _exposureTime;
+ private GerberMidpointRounding _sizeMidpointRounding = GerberMidpointRounding.AwayFromZero;
private bool _mirror;
private bool _invertColor;
private bool _enableAntiAliasing;
@@ -101,7 +102,7 @@ public class OperationPCBExposure : Operation
public override string ToString()
{
- var result = $"{string.Join(" / ", _files)} [Exposure: {_exposureTime}s] [Mirror: {_mirror}] [Invert: {_invertColor}]";
+ var result = $"{string.Join(" / ", _files)} [Exposure: {_exposureTime}s] [Rounding: {_sizeMidpointRounding}] [Mirror: {_mirror}] [Invert: {_invertColor}]";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
@@ -135,13 +136,6 @@ public class OperationPCBExposure : Operation
set => RaiseAndSetIfChanged(ref _mergeFiles, value);
}
- /*public string? FilePath
- {
- get => _filePath;
- set => RaiseAndSetIfChanged(ref _filePath, value);
- }
- public bool FileExists => !string.IsNullOrWhiteSpace(_filePath) && File.Exists(_filePath);*/
-
public decimal LayerHeight
{
get => _layerHeight;
@@ -153,7 +147,13 @@ public class OperationPCBExposure : Operation
get => _exposureTime;
set => RaiseAndSetIfChanged(ref _exposureTime, Math.Round(value, 2));
}
-
+
+ public GerberMidpointRounding SizeMidpointRounding
+ {
+ get => _sizeMidpointRounding;
+ set => RaiseAndSetIfChanged(ref _sizeMidpointRounding, value);
+ }
+
public bool Mirror
{
get => _mirror;
@@ -178,7 +178,7 @@ public class OperationPCBExposure : Operation
protected bool Equals(OperationPCBExposure other)
{
- return _files.Equals(other._files) && _mergeFiles == other._mergeFiles && _layerHeight == other._layerHeight && _exposureTime == other._exposureTime && _mirror == other._mirror && _invertColor == other._invertColor && _enableAntiAliasing == other._enableAntiAliasing;
+ return _files.Equals(other._files) && _mergeFiles == other._mergeFiles && _layerHeight == other._layerHeight && _exposureTime == other._exposureTime && _sizeMidpointRounding == other._sizeMidpointRounding && _mirror == other._mirror && _invertColor == other._invertColor && _enableAntiAliasing == other._enableAntiAliasing;
}
public override bool Equals(object? obj)
@@ -191,7 +191,7 @@ public class OperationPCBExposure : Operation
public override int GetHashCode()
{
- return HashCode.Combine(_files, _mergeFiles, _layerHeight, _exposureTime, _mirror, _invertColor, _enableAntiAliasing);
+ return HashCode.Combine(_files, _mergeFiles, _layerHeight, _exposureTime, (int) _sizeMidpointRounding, _mirror, _invertColor, _enableAntiAliasing);
}
#endregion
@@ -242,7 +242,7 @@ public class OperationPCBExposure : Operation
{
var mat = SlicerFile.CreateMat();
if (!File.Exists(file)) return mat;
- GerberDocument.ParseAndDraw(file, mat, SlicerFile.Ppmm, _enableAntiAliasing);
+ GerberDocument.ParseAndDraw(file, mat, SlicerFile.Ppmm, _sizeMidpointRounding, _enableAntiAliasing);
//var boundingRectangle = CvInvoke.BoundingRectangle(mat);
//var cropped = mat.Roi(new Size(boundingRectangle.Right, boundingRectangle.Bottom));
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 7cfb01f..783bd22 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description>
- <Version>3.5.5</Version>
+ <Version>3.5.6</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
index c96d2f0..afa2e14 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.5.5"?>
+ <?define SourceDir="..\publish\UVtools_win-x64_v3.5.6"?>
<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/Controls/Tools/ToolPCBExposureControl.axaml b/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml
index 204f74d..6a42fe0 100644
--- a/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml
@@ -63,7 +63,7 @@
MaxHeight="400"
Items="{Binding Operation.Files}" />
- <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto"
+ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto"
ColumnDefinitions="Auto,10,400">
<ToggleSwitch Grid.Row="0" Grid.Column="2"
@@ -93,8 +93,18 @@
Maximum="200"
FormatString="F2"
Value="{Binding Operation.ExposureTime}"/>
-
- <StackPanel Grid.Row="6" Grid.Column="2"
+
+ <TextBlock Grid.Row="6" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Size rounding:"/>
+
+ <ComboBox Grid.Row="6" Grid.Column="2"
+ HorizontalAlignment="Stretch"
+ Items="{Binding Operation.SizeMidpointRounding, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Operation.SizeMidpointRounding, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+
+ <StackPanel Grid.Row="8" Grid.Column="2"
Orientation="Horizontal" Spacing="20">
<CheckBox VerticalAlignment="Center"
IsChecked="{Binding Operation.Mirror}"
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 821f61a..988e66a 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.5.5</Version>
+ <Version>3.5.6</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
@@ -39,14 +39,14 @@
<NoWarn>1701;1702;</NoWarn>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="0.10.16" />
- <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.16" />
- <PackageReference Include="Avalonia.Desktop" Version="0.10.16" />
- <PackageReference Include="Avalonia.Diagnostics" Version="0.10.16" />
+ <PackageReference Include="Avalonia" Version="0.10.17" />
+ <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.17" />
+ <PackageReference Include="Avalonia.Desktop" Version="0.10.17" />
+ <PackageReference Include="Avalonia.Diagnostics" Version="0.10.17" />
<PackageReference Include="MessageBox.Avalonia" Version="2.0.2" />
- <PackageReference Include="Projektanker.Icons.Avalonia" Version="5.1.0" />
- <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="5.1.0" />
- <PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="5.1.0" />
+ <PackageReference Include="Projektanker.Icons.Avalonia" Version="5.2.0" />
+ <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="5.2.0" />
+ <PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="5.2.0" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.14" />
</ItemGroup>
<ItemGroup>