From c1528bbfe1af905475afeaae1ae79690e63f7e8c Mon Sep 17 00:00:00 2001 From: Vellu Sorvari <70940730+Kuoste@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:34:58 +0200 Subject: [PATCH 1/2] Migrate WritePoint to unsafe native pointer and split LasPoint.cs - Extract LasZipPointNative to its own file with new WriteFromLasPoint method - Extract LasZipPointStruct to its own file, marked [Obsolete] - Rewrite WritePoint to use LasZipPointNative* instead of Marshal.StructureToPtr - Remove legacy ConvertPoint(LasZipPointStruct) overload from LasPoint - Fix bug where old WritePoint mutated caller's point coordinates Closes #11 --- LasZipNetStandard/LasPoint.cs | 176 +++---------------------- LasZipNetStandard/LasZip.cs | 81 +++++++++--- LasZipNetStandard/LasZipPointNative.cs | 155 ++++++++++++++++++++++ LasZipNetStandard/LasZipPointStruct.cs | 140 ++++++++++++++++++++ 4 files changed, 378 insertions(+), 174 deletions(-) create mode 100644 LasZipNetStandard/LasZipPointNative.cs create mode 100644 LasZipNetStandard/LasZipPointStruct.cs diff --git a/LasZipNetStandard/LasPoint.cs b/LasZipNetStandard/LasPoint.cs index 1c46419..933b74a 100644 --- a/LasZipNetStandard/LasPoint.cs +++ b/LasZipNetStandard/LasPoint.cs @@ -1,9 +1,8 @@ -using System; -using System.Runtime.InteropServices; +using System; +using System.Runtime.CompilerServices; namespace Kuoste.LasZipNetStandard { - public class LasPoint { public double X { get; set; } @@ -23,24 +22,25 @@ public class LasPoint public ushort Green { get; set; } public ushort Blue { get; set; } - internal static void ConvertPoint(LasZipPointStruct pointStruct, ref LasPoint lasPoint) - { - lasPoint.X = pointStruct.X; - lasPoint.Y = pointStruct.Y; - lasPoint.Z = pointStruct.Z; - lasPoint.Intensity = pointStruct.Intensity; - lasPoint.ReturnNumber = pointStruct.ReturnNumber; - lasPoint.NumberOfReturns = pointStruct.NumberOfReturns; - lasPoint.ScanDirectionFlag = pointStruct.ScanDirectionFlag; - lasPoint.EdgeOfFlightLine = pointStruct.EdgeOfFlightLine; - lasPoint.Classification = pointStruct.Classification; - lasPoint.ScanAngleRank = pointStruct.ScanAngleRank; - lasPoint.UserData = pointStruct.UserData; - lasPoint.PointSourceId = pointStruct.PointSourceID; - lasPoint.GpsTime = pointStruct.GpsTime; - lasPoint.Red = pointStruct.Rgb[0]; - lasPoint.Green = pointStruct.Rgb[1]; - lasPoint.Blue = pointStruct.Rgb[2]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void ConvertPoint(LasZipPointNative* pointStruct, ref LasPoint lasPoint, double scaleFactorX, double scaleFactorY, double scaleFactorZ, double offsetX, double offsetY, double offsetZ) + { + lasPoint.X = pointStruct->X * scaleFactorX + offsetX; + lasPoint.Y = pointStruct->Y * scaleFactorY + offsetY; + lasPoint.Z = pointStruct->Z * scaleFactorZ + offsetZ; + lasPoint.Intensity = pointStruct->Intensity; + lasPoint.ReturnNumber = pointStruct->ReturnNumber; + lasPoint.NumberOfReturns = pointStruct->NumberOfReturns; + lasPoint.ScanDirectionFlag = pointStruct->ScanDirectionFlag; + lasPoint.EdgeOfFlightLine = pointStruct->EdgeOfFlightLine; + lasPoint.Classification = pointStruct->Classification; + lasPoint.ScanAngleRank = pointStruct->ScanAngleRank; + lasPoint.UserData = pointStruct->UserData; + lasPoint.PointSourceId = pointStruct->PointSourceID; + lasPoint.GpsTime = pointStruct->GpsTime; + lasPoint.Red = pointStruct->Rgb[0]; + lasPoint.Green = pointStruct->Rgb[1]; + lasPoint.Blue = pointStruct->Rgb[2]; } public override bool Equals(object obj) @@ -74,138 +74,4 @@ public override int GetHashCode() return HashCode.Combine(GpsTime, Z); } } - - [StructLayout(LayoutKind.Sequential)] - public struct LasZipPointStruct - { - public int X; - public int Y; - public int Z; - public ushort Intensity; - private byte bitField1; // ReturnNumber : 3, NumberOfReturns : 3, ScanDirectionFlag : 1, EdgeOfFlightLine : 1 - private byte bitField2; // Classification : 5, SyntheticFlag : 1, KeypointFlag : 1, WithheldFlag : 1 - public sbyte ScanAngleRank; - public byte UserData; - public ushort PointSourceID; - - // LAS 1.4 only - public short ExtendedScanAngle; - private byte bitField3; // ExtendedPointType : 2, ExtendedScannerChannel : 2, ExtendedClassificationFlags : 4 - public byte ExtendedClassification; - private byte bitField4; // ExtendedReturnNumber : 4, ExtendedNumberOfReturns : 4 - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] - public byte[] Dummy; - - public double GpsTime; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public ushort[] Rgb; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 29)] - public byte[] WavePacket; - - public int NumExtraBytes; - - public IntPtr ExtraBytes; // Use IntPtr instead of byte* for safe pointer handling - - public byte ReturnNumber - { - get => (byte)(bitField1 & 0b00000111); - set => bitField1 = (byte)((bitField1 & 0b11111000) | (value & 0b00000111)); - } - - public byte NumberOfReturns - { - get => (byte)((bitField1 >> 3) & 0b00000111); - set => bitField1 = (byte)((bitField1 & 0b11000111) | ((value & 0b00000111) << 3)); - } - - public byte ScanDirectionFlag - { - get => (byte)((bitField1 >> 6) & 0b00000001); - set => bitField1 = (byte)((bitField1 & 0b10111111) | ((value & 0b00000001) << 6)); - } - - public byte EdgeOfFlightLine - { - get => (byte)((bitField1 >> 7) & 0b00000001); - set => bitField1 = (byte)((bitField1 & 0b01111111) | ((value & 0b00000001) << 7)); - } - - public byte Classification - { - get => (byte)(bitField2 & 0b00011111); - set => bitField2 = (byte)((bitField2 & 0b11100000) | (value & 0b00011111)); - } - - public byte SyntheticFlag - { - get => (byte)((bitField2 >> 5) & 0b00000001); - set => bitField2 = (byte)((bitField2 & 0b11011111) | ((value & 0b00000001) << 5)); - } - - public byte KeypointFlag - { - get => (byte)((bitField2 >> 6) & 0b00000001); - set => bitField2 = (byte)((bitField2 & 0b10111111) | ((value & 0b00000001) << 6)); - } - - public byte WithheldFlag - { - get => (byte)((bitField2 >> 7) & 0b00000001); - set => bitField2 = (byte)((bitField2 & 0b01111111) | ((value & 0b00000001) << 7)); - } - - public byte ExtendedPointType - { - get => (byte)(bitField3 & 0b00000011); - set => bitField3 = (byte)((bitField3 & 0b11111100) | (value & 0b00000011)); - } - - public byte ExtendedScannerChannel - { - get => (byte)((bitField3 >> 2) & 0b00000011); - set => bitField3 = (byte)((bitField3 & 0b11110011) | ((value & 0b00000011) << 2)); - } - - public byte ExtendedClassificationFlags - { - get => (byte)((bitField3 >> 4) & 0b00001111); - set => bitField3 = (byte)((bitField3 & 0b00001111) | ((value & 0b00001111) << 4)); - } - - public byte ExtendedReturnNumber - { - get => (byte)(bitField4 & 0b00001111); - set => bitField4 = (byte)((bitField4 & 0b11110000) | (value & 0b00001111)); - } - - public byte ExtendedNumberOfReturns - { - get => (byte)((bitField4 >> 4) & 0b00001111); - set => bitField4 = (byte)((bitField4 & 0b00001111) | ((value & 0b00001111) << 4)); - } - - internal static LasZipPointStruct ConvertPoint(LasPoint lasPoint) - { - return new LasZipPointStruct() - { - X = (int)(lasPoint.X + 0.5), - Y = (int)(lasPoint.Y + 0.5), - Z = (int)(lasPoint.Z + 0.5), - Intensity = lasPoint.Intensity, - ReturnNumber = lasPoint.ReturnNumber, - NumberOfReturns = lasPoint.NumberOfReturns, - ScanDirectionFlag = lasPoint.ScanDirectionFlag, - EdgeOfFlightLine = lasPoint.EdgeOfFlightLine, - Classification = lasPoint.Classification, - ScanAngleRank = lasPoint.ScanAngleRank, - UserData = lasPoint.UserData, - PointSourceID = lasPoint.PointSourceId, - GpsTime = lasPoint.GpsTime, - Rgb = new ushort[] { lasPoint.Red, lasPoint.Green, lasPoint.Blue, 0 } - }; - } - } } diff --git a/LasZipNetStandard/LasZip.cs b/LasZipNetStandard/LasZip.cs index 1c51bde..9655847 100644 --- a/LasZipNetStandard/LasZip.cs +++ b/LasZipNetStandard/LasZip.cs @@ -1,10 +1,12 @@ -using System; +using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Kuoste.LasZipNetStandard { - public class LasZip + public class LasZip : IDisposable { + private bool _disposed = false; private const string _lasZipDll = "laszip64"; private IntPtr _pLasZipReader; @@ -135,7 +137,8 @@ public void SetWriterHeader(LaszipHeaderStruct header) /// /// Point data is read to this reference. /// Reading failed. - public void ReadPoint(ref LasPoint point) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadPoint(ref LasPoint point) { // Get the memory location for the point in LasZip library if (_pPointReader == IntPtr.Zero) @@ -152,22 +155,18 @@ public void ReadPoint(ref LasPoint point) throw new Exception("Failed to read LasZip point"); } - // Copy point data from C++ struct to C# class - LasPoint.ConvertPoint(Marshal.PtrToStructure(_pPointReader), ref point); - - // Scale the coordinates and add offsets. Not using the laszip_get_coordinates in order to make things faster. - point.X = point.X * _headerReader.ScaleFactorX + _headerReader.OffsetX; - point.Y = point.Y * _headerReader.ScaleFactorY + _headerReader.OffsetY; - point.Z = point.Z * _headerReader.ScaleFactorZ + _headerReader.OffsetZ; + LasPoint.ConvertPoint((LasZipPointNative*)_pPointReader, ref point, + _headerReader.ScaleFactorX, _headerReader.ScaleFactorY, _headerReader.ScaleFactorZ, + _headerReader.OffsetX, _headerReader.OffsetY, _headerReader.OffsetZ); } /// /// Writes the point given as reference. /// - /// LasPoint to write. Note that the method changes the coordinates - /// of the point by applying the Offsets and ScaleFactors. + /// LasPoint to write. The coordinates are scaled and offset + /// using the header values before writing. /// Writing failed. - public void WritePoint(ref LasPoint point) + public unsafe void WritePoint(ref LasPoint point) { // Get the memory location for the point in LasZip library if (_pPointWriter == IntPtr.Zero) @@ -178,12 +177,9 @@ public void WritePoint(ref LasPoint point) } } - // Scale the coordinates and add offsets. Not using the laszip_set_coordinates in order to make things faster. - point.X = (point.X - _headerWriter.OffsetX) / _headerWriter.ScaleFactorX; - point.Y = (point.Y - _headerWriter.OffsetY) / _headerWriter.ScaleFactorY; - point.Z = (point.Z - _headerWriter.OffsetZ) / _headerWriter.ScaleFactorZ; - - Marshal.StructureToPtr(LasZipPointStruct.ConvertPoint(point), _pPointWriter, false); + LasZipPointNative.WriteFromLasPoint((LasZipPointNative*)_pPointWriter, point, + _headerWriter.ScaleFactorX, _headerWriter.ScaleFactorY, _headerWriter.ScaleFactorZ, + _headerWriter.OffsetX, _headerWriter.OffsetY, _headerWriter.OffsetZ); // Write point if (laszip_write_point(_pLasZipWriter) != 0) @@ -232,5 +228,52 @@ public void DestroyWriter() _pLasZipWriter = IntPtr.Zero; } + + /// + /// Releases all resources used by the LasZip instance. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources and optionally releases the managed resources. + /// + /// True to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + // Clean up unmanaged resources + if (_pLasZipReader != IntPtr.Zero) + { + laszip_close_reader(_pLasZipReader); + laszip_destroy(_pLasZipReader); + _pLasZipReader = IntPtr.Zero; + _pPointReader = IntPtr.Zero; + } + + if (_pLasZipWriter != IntPtr.Zero) + { + laszip_close_writer(_pLasZipWriter); + laszip_destroy(_pLasZipWriter); + _pLasZipWriter = IntPtr.Zero; + _pPointWriter = IntPtr.Zero; + _pHeaderWriter = IntPtr.Zero; + } + + _disposed = true; + } + + /// + /// Finalizer to ensure unmanaged resources are released. + /// + ~LasZip() + { + Dispose(false); + } } } diff --git a/LasZipNetStandard/LasZipPointNative.cs b/LasZipNetStandard/LasZipPointNative.cs new file mode 100644 index 0000000..8c22302 --- /dev/null +++ b/LasZipNetStandard/LasZipPointNative.cs @@ -0,0 +1,155 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Kuoste.LasZipNetStandard +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct LasZipPointNative + { + public int X; + public int Y; + public int Z; + public ushort Intensity; + private byte bitField1; + private byte bitField2; + public sbyte ScanAngleRank; + public byte UserData; + public ushort PointSourceID; + public short ExtendedScanAngle; + private byte bitField3; + public byte ExtendedClassification; + private byte bitField4; + public fixed byte Dummy[7]; + public double GpsTime; + public fixed ushort Rgb[4]; + public fixed byte WavePacket[29]; + public int NumExtraBytes; + public IntPtr ExtraBytes; + + public byte ReturnNumber + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)(bitField1 & 0b00000111); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField1 = (byte)((bitField1 & 0b11111000) | (value & 0b00000111)); + } + + public byte NumberOfReturns + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField1 >> 3) & 0b00000111); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField1 = (byte)((bitField1 & 0b11000111) | ((value & 0b00000111) << 3)); + } + + public byte ScanDirectionFlag + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField1 >> 6) & 0b00000001); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField1 = (byte)((bitField1 & 0b10111111) | ((value & 0b00000001) << 6)); + } + + public byte EdgeOfFlightLine + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField1 >> 7) & 0b00000001); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField1 = (byte)((bitField1 & 0b01111111) | ((value & 0b00000001) << 7)); + } + + public byte Classification + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)(bitField2 & 0b00011111); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField2 = (byte)((bitField2 & 0b11100000) | (value & 0b00011111)); + } + + public byte SyntheticFlag + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField2 >> 5) & 0b00000001); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField2 = (byte)((bitField2 & 0b11011111) | ((value & 0b00000001) << 5)); + } + + public byte KeypointFlag + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField2 >> 6) & 0b00000001); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField2 = (byte)((bitField2 & 0b10111111) | ((value & 0b00000001) << 6)); + } + + public byte WithheldFlag + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField2 >> 7) & 0b00000001); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField2 = (byte)((bitField2 & 0b01111111) | ((value & 0b00000001) << 7)); + } + + public byte ExtendedPointType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)(bitField3 & 0b00000011); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField3 = (byte)((bitField3 & 0b11111100) | (value & 0b00000011)); + } + + public byte ExtendedScannerChannel + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField3 >> 2) & 0b00000011); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField3 = (byte)((bitField3 & 0b11110011) | ((value & 0b00000011) << 2)); + } + + public byte ExtendedClassificationFlags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField3 >> 4) & 0b00001111); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField3 = (byte)((bitField3 & 0b00001111) | ((value & 0b00001111) << 4)); + } + + public byte ExtendedReturnNumber + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)(bitField4 & 0b00001111); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField4 = (byte)((bitField4 & 0b11110000) | (value & 0b00001111)); + } + + public byte ExtendedNumberOfReturns + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (byte)((bitField4 >> 4) & 0b00001111); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => bitField4 = (byte)((bitField4 & 0b00001111) | ((value & 0b00001111) << 4)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void WriteFromLasPoint(LasZipPointNative* native, LasPoint point, double scaleFactorX, double scaleFactorY, double scaleFactorZ, double offsetX, double offsetY, double offsetZ) + { + native->X = (int)((point.X - offsetX) / scaleFactorX + 0.5); + native->Y = (int)((point.Y - offsetY) / scaleFactorY + 0.5); + native->Z = (int)((point.Z - offsetZ) / scaleFactorZ + 0.5); + native->Intensity = point.Intensity; + native->ReturnNumber = point.ReturnNumber; + native->NumberOfReturns = point.NumberOfReturns; + native->ScanDirectionFlag = point.ScanDirectionFlag; + native->EdgeOfFlightLine = point.EdgeOfFlightLine; + native->Classification = point.Classification; + native->ScanAngleRank = point.ScanAngleRank; + native->UserData = point.UserData; + native->PointSourceID = point.PointSourceId; + native->GpsTime = point.GpsTime; + native->Rgb[0] = point.Red; + native->Rgb[1] = point.Green; + native->Rgb[2] = point.Blue; + } + } +} diff --git a/LasZipNetStandard/LasZipPointStruct.cs b/LasZipNetStandard/LasZipPointStruct.cs new file mode 100644 index 0000000..d5ab047 --- /dev/null +++ b/LasZipNetStandard/LasZipPointStruct.cs @@ -0,0 +1,140 @@ +using System; +using System.Runtime.InteropServices; + +namespace Kuoste.LasZipNetStandard +{ + [Obsolete("Use LasZipPointNative for new code. This type will be removed in a future release.")] + [StructLayout(LayoutKind.Sequential)] + public struct LasZipPointStruct + { + public int X; + public int Y; + public int Z; + public ushort Intensity; + private byte bitField1; // ReturnNumber : 3, NumberOfReturns : 3, ScanDirectionFlag : 1, EdgeOfFlightLine : 1 + private byte bitField2; // Classification : 5, SyntheticFlag : 1, KeypointFlag : 1, WithheldFlag : 1 + public sbyte ScanAngleRank; + public byte UserData; + public ushort PointSourceID; + + // LAS 1.4 only + public short ExtendedScanAngle; + private byte bitField3; // ExtendedPointType : 2, ExtendedScannerChannel : 2, ExtendedClassificationFlags : 4 + public byte ExtendedClassification; + private byte bitField4; // ExtendedReturnNumber : 4, ExtendedNumberOfReturns : 4 + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] Dummy; + + public double GpsTime; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public ushort[] Rgb; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 29)] + public byte[] WavePacket; + + public int NumExtraBytes; + + public IntPtr ExtraBytes; // Use IntPtr instead of byte* for safe pointer handling + + public byte ReturnNumber + { + get => (byte)(bitField1 & 0b00000111); + set => bitField1 = (byte)((bitField1 & 0b11111000) | (value & 0b00000111)); + } + + public byte NumberOfReturns + { + get => (byte)((bitField1 >> 3) & 0b00000111); + set => bitField1 = (byte)((bitField1 & 0b11000111) | ((value & 0b00000111) << 3)); + } + + public byte ScanDirectionFlag + { + get => (byte)((bitField1 >> 6) & 0b00000001); + set => bitField1 = (byte)((bitField1 & 0b10111111) | ((value & 0b00000001) << 6)); + } + + public byte EdgeOfFlightLine + { + get => (byte)((bitField1 >> 7) & 0b00000001); + set => bitField1 = (byte)((bitField1 & 0b01111111) | ((value & 0b00000001) << 7)); + } + + public byte Classification + { + get => (byte)(bitField2 & 0b00011111); + set => bitField2 = (byte)((bitField2 & 0b11100000) | (value & 0b00011111)); + } + + public byte SyntheticFlag + { + get => (byte)((bitField2 >> 5) & 0b00000001); + set => bitField2 = (byte)((bitField2 & 0b11011111) | ((value & 0b00000001) << 5)); + } + + public byte KeypointFlag + { + get => (byte)((bitField2 >> 6) & 0b00000001); + set => bitField2 = (byte)((bitField2 & 0b10111111) | ((value & 0b00000001) << 6)); + } + + public byte WithheldFlag + { + get => (byte)((bitField2 >> 7) & 0b00000001); + set => bitField2 = (byte)((bitField2 & 0b01111111) | ((value & 0b00000001) << 7)); + } + + public byte ExtendedPointType + { + get => (byte)(bitField3 & 0b00000011); + set => bitField3 = (byte)((bitField3 & 0b11111100) | (value & 0b00000011)); + } + + public byte ExtendedScannerChannel + { + get => (byte)((bitField3 >> 2) & 0b00000011); + set => bitField3 = (byte)((bitField3 & 0b11110011) | ((value & 0b00000011) << 2)); + } + + public byte ExtendedClassificationFlags + { + get => (byte)((bitField3 >> 4) & 0b00001111); + set => bitField3 = (byte)((bitField3 & 0b00001111) | ((value & 0b00001111) << 4)); + } + + public byte ExtendedReturnNumber + { + get => (byte)(bitField4 & 0b00001111); + set => bitField4 = (byte)((bitField4 & 0b11110000) | (value & 0b00001111)); + } + + public byte ExtendedNumberOfReturns + { + get => (byte)((bitField4 >> 4) & 0b00001111); + set => bitField4 = (byte)((bitField4 & 0b00001111) | ((value & 0b00001111) << 4)); + } + + internal static LasZipPointStruct ConvertPoint(LasPoint lasPoint) + { + return new LasZipPointStruct() + { + X = (int)(lasPoint.X + 0.5), + Y = (int)(lasPoint.Y + 0.5), + Z = (int)(lasPoint.Z + 0.5), + Intensity = lasPoint.Intensity, + ReturnNumber = lasPoint.ReturnNumber, + NumberOfReturns = lasPoint.NumberOfReturns, + ScanDirectionFlag = lasPoint.ScanDirectionFlag, + EdgeOfFlightLine = lasPoint.EdgeOfFlightLine, + Classification = lasPoint.Classification, + ScanAngleRank = lasPoint.ScanAngleRank, + UserData = lasPoint.UserData, + PointSourceID = lasPoint.PointSourceId, + GpsTime = lasPoint.GpsTime, + Rgb = new ushort[] { lasPoint.Red, lasPoint.Green, lasPoint.Blue, 0 } + }; + } + } +} From f3fe1f02e6f11883b61dd99bd066d206f20ebcbf Mon Sep 17 00:00:00 2001 From: git Date: Wed, 11 Mar 2026 13:57:32 +0200 Subject: [PATCH 2/2] adding missing change to csproj --- LasZipNetStandard/LasZipNetStandard.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/LasZipNetStandard/LasZipNetStandard.csproj b/LasZipNetStandard/LasZipNetStandard.csproj index 3886e9c..84590e0 100644 --- a/LasZipNetStandard/LasZipNetStandard.csproj +++ b/LasZipNetStandard/LasZipNetStandard.csproj @@ -12,6 +12,7 @@ README.md Apache-2.0 Kuoste.LasZipNetStandard + true