/// <summary>
        /// Read a heightmap report
        /// </summary>
        /// <param name="from">Origin</param>
        /// <param name="map">Deserialized heightmap</param>
        /// <returns>Number of bytes read</returns>
        public static int ReadHeightMap(ReadOnlySpan <byte> from, out Heightmap map)
        {
            HeightMapHeader header = MemoryMarshal.Cast <byte, HeightMapHeader>(from)[0];

            map = new Heightmap
            {
                XMin     = header.XMin,
                XMax     = header.XMax,
                XSpacing = header.XSpacing,
                YMin     = header.YMin,
                YMax     = header.YMax,
                YSpacing = header.YSpacing,
                Radius   = header.Radius,
                NumX     = header.NumX,
                NumY     = header.NumY
            };

            if (from.Length > Marshal.SizeOf(header))
            {
                ReadOnlySpan <byte> zCoordinates = from.Slice(Marshal.SizeOf(header), Marshal.SizeOf <float>() * map.NumX * map.NumY);
                map.ZCoordinates = MemoryMarshal.Cast <byte, float>(zCoordinates).ToArray();
            }
            else
            {
                map.NumX         = map.NumY = 0;
                map.ZCoordinates = Array.Empty <float>();
            }
            return(Marshal.SizeOf(header) + map.ZCoordinates.Length * Marshal.SizeOf <float>());
        }
        /// <summary>
        /// Write a heightmap as read by G29 S1
        /// </summary>
        /// <param name="to">Destination</param>
        /// <param name="map">Heightmap to write</param>
        /// <returns>Number of bytes written</returns>
        public static int WriteHeightMap(Span <byte> to, Heightmap map)
        {
            HeightMapHeader header = new HeightMapHeader
            {
                XMin     = map.XMin,
                XMax     = map.XMax,
                XSpacing = map.XSpacing,
                YMin     = map.YMin,
                YMax     = map.YMax,
                YSpacing = map.YSpacing,
                Radius   = map.Radius,
                NumX     = (ushort)map.NumX,
                NumY     = (ushort)map.NumY
            };

            MemoryMarshal.Write(to, ref header);

            Span <float> coords = MemoryMarshal.Cast <byte, float>(to.Slice(Marshal.SizeOf(header)));

            for (int i = 0; i < map.NumX * map.NumY; i++)
            {
                coords[i] = map.ZCoordinates[i];
            }

            return(Marshal.SizeOf(header) + Marshal.SizeOf <float>() * map.NumX * map.NumY);
        }
        /// <summary>
        /// Read a heightmap report
        /// </summary>
        /// <param name="from">Origin</param>
        /// <param name="map">Deserialized heightmap</param>
        /// <returns>Number of bytes read</returns>
        public static int ReadHeightMap(ReadOnlySpan <byte> from, out Heightmap map)
        {
            HeightMapHeader header = MemoryMarshal.Read <HeightMapHeader>(from);

            map = new Heightmap
            {
                XMin     = header.XMin,
                XMax     = header.XMax,
                XSpacing = header.XSpacing,
                YMin     = header.YMin,
                YMax     = header.YMax,
                YSpacing = header.YSpacing,
                Radius   = header.Radius,
                NumX     = header.NumX,
                NumY     = header.NumY
            };

            int headerSize = Marshal.SizeOf <HeightMapHeader>(), dataLength = Marshal.SizeOf <float>() * map.NumX * map.NumY;

            if (from.Length > headerSize)
            {
                ReadOnlySpan <byte> zCoordinates = from.Slice(headerSize, dataLength);
                map.ZCoordinates = MemoryMarshal.Cast <byte, float>(zCoordinates).ToArray();
            }
            else
            {
                map.NumX         = map.NumY = dataLength = 0;
                map.ZCoordinates = Array.Empty <float>();
            }
            return(headerSize + dataLength);
        }