public static HeightMap Load(string path)
        {
            HeightMap map = new HeightMap();

            XmlReader r = XmlReader.Create(path);

            map.MaxHeight = double.MinValue;
            map.MinHeight = double.MaxValue;

            while (r.Read())
            {
                if (!r.IsStartElement())
                {
                    continue;
                }

                switch (r.Name)
                {
                case "heightmap":
                    map.Min    = new Vector2(double.Parse(r["MinX"].Replace(',', '.'), NumberFormatInfo.InvariantInfo), double.Parse(r["MinY"].Replace(',', '.'), NumberFormatInfo.InvariantInfo));
                    map.Max    = new Vector2(double.Parse(r["MaxX"].Replace(',', '.'), NumberFormatInfo.InvariantInfo), double.Parse(r["MaxY"].Replace(',', '.'), NumberFormatInfo.InvariantInfo));
                    map.SizeX  = int.Parse(r["SizeX"].Replace(',', '.'), NumberFormatInfo.InvariantInfo);
                    map.SizeY  = int.Parse(r["SizeY"].Replace(',', '.'), NumberFormatInfo.InvariantInfo);
                    map.Points = new double?[map.SizeX, map.SizeY];
                    break;

                case "point":
                    int    x = int.Parse(r["X"].Replace(',', '.')), y = int.Parse(r["Y"].Replace(',', '.'), NumberFormatInfo.InvariantInfo);
                    double height = double.Parse(r.ReadInnerXml().Replace(',', '.'), NumberFormatInfo.InvariantInfo);

                    map.Points[x, y] = height;

                    if (height > map.MaxHeight)
                    {
                        map.MaxHeight = height;
                    }
                    if (height < map.MinHeight)
                    {
                        map.MinHeight = height;
                    }

                    break;
                }
            }

            //       r.Dispose();

            for (int x = 0; x < map.SizeX; x++)
            {
                for (int y = 0; y < map.SizeY; y++)
                {
                    if (!map.Points[x, y].HasValue)
                    {
                        map.NotProbed.Enqueue(new Tuple <int, int>(x, y));
                    }
                }

                if (++x >= map.SizeX)
                {
                    break;
                }

                for (int y = map.SizeY - 1; y >= 0; y--)
                {
                    if (!map.Points[x, y].HasValue)
                    {
                        map.NotProbed.Enqueue(new Tuple <int, int>(x, y));
                    }
                }
            }

            return(map);
        }
        private void btnStartHeightScan_Click(object sender, EventArgs e)
        {
            enableControls(scanStarted);
            if (!scanStarted)
            {
                isMapOk    = false;
                timeInit   = DateTime.UtcNow;
                elapsed    = TimeSpan.Zero;
                elapsedOld = elapsed;
                btnStartHeightScan.Text = "STOP scanning Height Map";
                decimal x1, x2, y1, y2;
                x1 = Math.Min(nUDX1.Value, nUDX2.Value);
                x2 = Math.Max(nUDX1.Value, nUDX2.Value);
                y1 = Math.Min(nUDY1.Value, nUDY2.Value);
                y2 = Math.Max(nUDY1.Value, nUDY2.Value);
                if (x1 == x2)
                {
                    x2 = x1 + 10;
                }
                if (y1 == y2)
                {
                    y2 = y1 + 10;
                }
                nUDX1.Value = x1; nUDX2.Value = x2; nUDY1.Value = y1; nUDY2.Value = y2;
                //decimal stepX = (x2 - x1) / (nUDGridX.Value - 1);
                //decimal stepY = (y2 - y1) / (nUDGridY.Value - 1);
                cntSent           = 0; cntReceived = 0;
                gcode.reduceGCode = true;   // reduce number format to #.# in gcode.frmtNum()

                Map      = new HeightMap((double)nUDGridX.Value, new Vector2((double)x1, (double)y1), new Vector2((double)x2, (double)y2));
                MapIndex = new List <Point>();

                lblXDim.Text = string.Format("X Min:{0} Max:{1} Step:{2}", Map.Min.X, Map.Max.X, Map.SizeX);
                lblYDim.Text = string.Format("Y Min:{0} Max:{1} Step:{2}", Map.Min.Y, Map.Max.Y, Map.SizeY);

                textBox1.Clear();
                textBox1.Text += Map.SizeX.ToString() + "  " + Map.SizeY.ToString();
                int pixX, pixY;
                BMPsizeX     = 240;
                BMPsizeY     = Map.SizeY * BMPsizeX / Map.SizeX;
                heightMapBMP = new Bitmap(BMPsizeX, BMPsizeY);
                using (Graphics graph = Graphics.FromImage(heightMapBMP))
                {
                    Rectangle ImageSize = new Rectangle(0, 0, BMPsizeX, BMPsizeY);
                    graph.FillRectangle(Brushes.White, ImageSize);
                }
                Vector2 tmp;

                scanCode = new StringBuilder();
                scanCode.AppendFormat("G90F{0}\r\n", gcode.frmtNum((float)nUDProbeSpeed.Value));
                for (int iy = 0; iy < Map.SizeY; iy++)
                {
                    tmp = Map.GetCoordinates(0, iy);
                    scanCode.AppendFormat("G0Z{0}\r\n", gcode.frmtNum((float)nUDProbeUp.Value));
                    scanCode.AppendFormat("G0Y{0}\r\n", gcode.frmtNum((float)tmp.Y));
                    pixY = iy * BMPsizeY / Map.SizeY;
                    for (int ix = 0; ix < Map.SizeX; ix++)
                    {
                        pixX = ix * BMPsizeX / Map.SizeX;
                        heightMapBMP.SetPixel(pixX, pixY, Color.FromArgb(255, 00, 00));
                        tmp = Map.GetCoordinates(ix, iy);
                        MapIndex.Add(new Point(ix, iy));
                        scanCode.AppendFormat("G0Z{0}\r\n", gcode.frmtNum((float)nUDProbeUp.Value));
                        scanCode.AppendFormat("X{0}\r\n", gcode.frmtNum((float)tmp.X));
                        if (nUDProbeDown.Value == 0)
                        {
                            scanCode.AppendFormat("($PROBE)\r\n");
                        }
                        else
                        {
                            scanCode.AppendFormat("G38.3Z{0}\r\n", gcode.frmtNum((float)nUDProbeDown.Value));
                        }
                        cntSent++;
                    }
                    if (iy < Map.SizeY - 1)
                    {
                        iy++;
                        tmp = Map.GetCoordinates(0, iy);       //?
                        scanCode.AppendFormat("G0Z{0}\r\n", gcode.frmtNum((float)nUDProbeUp.Value));
                        scanCode.AppendFormat("G0Y{0}\r\n", gcode.frmtNum((float)tmp.Y));
                        for (int ix = Map.SizeX - 1; ix >= 0; ix--)
                        {
                            pixX = ix * BMPsizeX / Map.SizeX;
                            heightMapBMP.SetPixel(pixX, pixY, Color.FromArgb(100, 100, 100));
                            tmp = Map.GetCoordinates(ix, iy);
                            MapIndex.Add(new Point(ix, iy));
                            scanCode.AppendFormat("G0Z{0}\r\n", gcode.frmtNum((float)nUDProbeUp.Value));
                            scanCode.AppendFormat("X{0}\r\n", gcode.frmtNum((float)tmp.X));
                            if (nUDProbeDown.Value == 0)
                            {
                                scanCode.AppendFormat("($PROBE)\r\n");
                            }
                            else
                            {
                                scanCode.AppendFormat("G38.3Z{0}\r\n", gcode.frmtNum((float)nUDProbeDown.Value));
                            }
                            cntSent++;
                        }
                    }
                }
                scanCode.AppendFormat("G0 Z{0}\r\n", gcode.frmtNum((float)nUDProbeUp.Value));
                tmp = Map.GetCoordinates(0, 0);
                scanCode.AppendFormat("G0 X{0} Y{1}\r\n", gcode.frmtNum((float)tmp.X), gcode.frmtNum((float)tmp.Y));
                scanCode.AppendLine("M30");     // finish

                textBox1.Text       += "Code sent\r\n" + scanCode.ToString();
                progressBar1.Maximum = cntSent;
                lblProgress.Text     = string.Format("{0}%", (100 * cntReceived / progressBar1.Maximum));
                pictureBox1.Image    = new Bitmap(heightMapBMP);
                pictureBox1.Refresh();
            }
            else
            {
                btnStartHeightScan.Text = "Generate Height Map";
            }
            scanStarted = !scanStarted;
        }