/// <summary>
        /// returns an ideal grid with perfectly regular spacing
        /// </summary>
        /// <param name="grid"></param>
        /// <param name="image_width"></param>
        /// <param name="image_height"></param>
        /// <returns>
        /// </returns>
        private static grid2D GetIdealGrid(CalibrationDot[,] grid,
                                           int image_width, int image_height)
        {
            grid2D ideal_grid = null;
            float ideal_spacing = 0;
            double centre_x = image_width / 2;
            double centre_y = image_height / 2;            
            double min_dist = double.MaxValue;
            
            int grid_cx=0, grid_cy=0;
            
            for (int x = 0; x < grid.GetLength(0); x++)
            {
                for (int y = 0; y < grid.GetLength(1); y++)
                {
                    if (grid[x, y] != null)
                    {
                        double dx = grid[x, y].x - centre_x;
                        double dy = grid[x, y].y - centre_y;
                        double dist = dx*dx + dy*dy;
                        if (dist < min_dist)
                        {
                            min_dist = dist;
                            grid_cx = x;
                            grid_cy = y;
                        }
                    }
                }
            }
            
            if (grid_cx > 0)
            {
                int[] orientation_histogram = new int[361];
                List<double>[] orientations = new List<double>[361];
                double average_dist = 0;
                int hits = 0;
                
                int local_search = 2;
                for (int x = grid_cx - local_search; x <= grid_cx + local_search; x++)
                {
                    if ((x >= 0) && (x < grid.GetLength(0)-1))
                    {
                        for (int y = grid_cy - local_search; y <= grid_cy + local_search; y++)
                        {
                            if ((y >= 1) && (y < grid.GetLength(1)-1))
                            {
                                if (grid[x, y] != null)
                                {
                                    for (int i = 0; i < grid[x, y].Links.Count; i++)
                                    {
                                        CalibrationLink link = (CalibrationLink)grid[x, y].Links[i];
                                        CalibrationDot from_dot = (CalibrationDot)link.From;
                                        
                                        double dx = grid[x, y].x - from_dot.x;
                                        double dy = grid[x, y].y - from_dot.y;
                                        double dist = Math.Sqrt(dx*dx + dy*dy);
                                        
                                        if (dist > 0)
                                        {
                                            double orientation = Math.Asin(dx / dist);
                                            if (orientation < 0) orientation += Math.PI;
                                            if (dy < 0) orientation = (Math.PI * 2) - orientation;
                                            orientation = orientation / Math.PI * 180;
                                            int bucket = (int)orientation;
                                            orientation_histogram[bucket]++;
                                            if (orientations[bucket] == null) orientations[bucket] = new List<double>();
                                            orientations[bucket].Add(orientation);
                                        
                                            average_dist += Math.Sqrt(dx*dx + dy*dy);
                                            hits++;
                                        }
                                    }
                                    
                                }
                            }
                        }
                    }
                }
                if (hits > 0) average_dist = average_dist / hits;
                ideal_spacing = (float)average_dist;
                
                int max_orientation_response = 0;
                int best_ang = 0;
                for (int ang = 0; ang < 90; ang++)
                {
                    int response = orientation_histogram[ang] +
                                   orientation_histogram[ang + 90] + 
                                   orientation_histogram[ang + 180];
                    if (response > max_orientation_response)
                    {
                        max_orientation_response = response;
                        best_ang = ang;
                    }
                }
                double average_orientation = 0;
                hits = 0;
                for (int offset = 0; offset < 3; offset++)
                {
                    if (orientations[best_ang + offset] != null)
                    {
                        for (int ang = 0; ang < orientations[best_ang + offset].Count; ang++)
                        {
                            average_orientation += orientations[best_ang + offset][ang] - (Math.PI*offset/2);
                            hits++;
                        }
                    }
                }
                if (hits > 0)
                {
                    average_orientation /= hits;
                    float pattern_orientation = (float)average_orientation;
                    
                    float offset_x = (float)grid[grid_cx, grid_cy].x;
                    float offset_y = (float)grid[grid_cx, grid_cy].y;
                    float r = ideal_spacing * dots_across;
                    
                    polygon2D perimeter = new polygon2D();
                    perimeter.Add(-r + offset_x, -r + offset_y);
                    perimeter.Add(r + offset_x, -r + offset_y);
                    perimeter.Add(r + offset_x, r + offset_y);
                    perimeter.Add(-r + offset_x, r + offset_y);
                    perimeter.rotate((float)average_orientation / 180 * (float)Math.PI, offset_x, offset_y);
                    ideal_grid = new grid2D(dots_across*2, dots_across*2, perimeter, 0, false);
                    
                    int grid_width = grid.GetLength(0);
                    int grid_height = grid.GetLength(1);
                    int idx = 0;
                    int xx=0, yy=0;
                    float ideal_x, ideal_y;
       
                    for (int orient = 0; orient < 8; orient++)
                    {
                        for (int x = 0; x < grid_width; x++)
                        {
                            for (int y = 0; y < grid_height; y++)
                            {
                                if (grid[x, y] != null)
                                {
                                    switch(orient)
                                    {
                                        case 0:
                                        {
                                            xx = (x - grid_cx) + dots_across;
                                            yy = (y - grid_cy) + dots_across;
                                            break;
                                        }
                                        case 1:
                                        {
                                            xx = (x - grid_cx) + dots_across;
                                            yy = (grid_cy - y) + dots_across;
                                            break;
                                        }
                                        case 2:
                                        {
                                            xx = (grid_cx - x) + dots_across;
                                            yy = (y - grid_cy) + dots_across;
                                            break;
                                        }
                                        case 3:
                                        {
                                            xx = (grid_cx - x) + dots_across;
                                            yy = (grid_cy - y) + dots_across;
                                            break;
                                        }
                                        case 4:
                                        {
                                            xx = (y - grid_cy) + dots_across;
                                            yy = (x - grid_cx) + dots_across;
                                            break;
                                        }
                                        case 5:
                                        {
                                            xx = (y - grid_cy) + dots_across;
                                            yy = (grid_cx - x) + dots_across;
                                            break;
                                        }
                                        case 6:
                                        {
                                            xx = (grid_cy - y) + dots_across;
                                            yy = (x - grid_cx) + dots_across;
                                            break;
                                        }
                                        case 7:
                                        {
                                            xx = (grid_cy - y) + dots_across;
                                            yy = (grid_cx - x) + dots_across;
                                            break;
                                        }
                                    }
                                    
                                    if ((xx >= 0) && (xx < ideal_grid.cell.Length) &&
                                        (yy >= 0) && (yy < ideal_grid.cell[0].Length))
                                    {
                                        ideal_x = ideal_grid.cell[xx][yy].perimeter.x_points[idx];
                                        ideal_y = ideal_grid.cell[xx][yy].perimeter.y_points[idx];
                                    
                                        if (orient == 0)
                                        {
                                            grid[x, y].rectified_x = ideal_x;
                                            grid[x, y].rectified_y = ideal_y;
                                        }
                                        else
                                        {
                                            double diff_x1 = grid[x, y].rectified_x - grid[x, y].x;
                                            double diff_y1 = grid[x, y].rectified_y - grid[x, y].y;
                                            double dist1 = diff_x1*diff_x1 + diff_y1*diff_y1;

                                            double diff_x2 = ideal_x - grid[x, y].x;
                                            double diff_y2 = ideal_y - grid[x, y].y;
                                            double dist2 = diff_x2*diff_x2 + diff_y2*diff_y2;
                                            
                                            if (dist2 < dist1)
                                            {
                                                grid[x, y].rectified_x = ideal_x;
                                                grid[x, y].rectified_y = ideal_y;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                
            }
            
            return(ideal_grid);
        }