简体   繁体   中英

How to find closest point (point being (x,y), in a list of different points) to a given point?

Currently I have a random point being generated from Random. I am trying to check if the given random point is too close to any other existing points in the plane, if it is far enough away from ALL other points, it will be added to the list of other points. I start with one given point and the first hundered points in the list are being generated this way. My problem is, when I go to draw all the points on the list onto the screen, often the points are much closer than they should be allowed to be.

public void generateFirstMap()
    {
        int count = 0;
        do
        {
            int randXpixels = Main.rand.Next(24, Main.screenWidth - 16); //leave outer 16 pixels of screen empty (planet sprite has diameter of 8 pixels)
            int randYpixels = Main.rand.Next(27, Main.screenHeight - 27);
            Tuple<int, int> coord = Tuple.Create(randXpixels, randYpixels);
            if (distance(closestPoint(coord), coord) < 200)
            {
                continue;
            }
            points.Add(coord); //List<Tuple<int,int>> points;

            count++;
        } while(count < 100);


public Tuple<int, int> closestPoint (Tuple<int, int> p1)
    {
        Tuple<int, int> p2 = Tuple.Create(0, 0);
        bool firstRun = true;
            foreach (Tuple<int, int> point in points)
            {
                if (firstRun)
                {
                    p2 = point;
                    firstRun = false;
                }
                else if (distance(p1, p2) < distance(p1, point))
                {
                    p2 = point;
                }
            }
            return p2;
    }

public double distance(Tuple<int, int> p1, Tuple<int, int> p2)
    {
        Vector2 line = new Vector2((p2.Item1 - p1.Item1), (p2.Item2 - p1.Item2));
        return Math.Abs(line.Length());
    }

Edit: to be clear, the number for how close they can be is just a number I threw out there (for now) it can be anything >~30

Edit2: Changed all tuples to Vector2 and used suggested code, still hasn't changed the problem

Edit 3: Using a for loop to loop through all the points (inside of the while loops) and using break seems to have fixed the problem.

This should do: (Requires you to remove alls Tuples and replace them with the Vector2 type, which makes far more sense in general. Also you want to adjust your naming.

public Vector2 GetClosestPoint (Vector2 v1)
{
    return points.OrderByDescending(v2 => GetDistance(v1,v2)).First();
}

It seems like you'd want this to be fairly fast because it's a pretty slow algorithm (it's O(N^2) I think). An obvious optimisation is to just calculate the distance squared and use that to compare distances - this does away with a square root operation.

However you have a much more serious problem. If your OP is correct, then you are trying to place 100 points on a screen such that none of them are closer than 200 pixels apart. This is not likely to happen with randomly selected points, because the chances are that they will occur in positions such that after a number have been placed there simply isn't room for any more.

Anyway, here's some code which will attempt to calculate the points - but it will never terminate, because the points won't fit unless you make the screen VERY big. Try running it with different seeds for the Random() constructor and see what happens.

using System;
using System.Collections.Generic;
using System.Drawing;

namespace ConsoleApplication2
{
    class Program
    {
        private static void Main()
        {
            var points = new List<Point>();

            int screenWidth     = 1920;
            int screenHeight    = 1280;
            int numPointsWanted =  100;

            long nearestAllowedSquared = 200*200;

            Random rng = new Random(12345);

            while (points.Count < numPointsWanted)
            {
                int randXpixels = rng.Next(24, screenWidth - 16);
                int randYpixels = rng.Next(27, screenHeight - 27);
                var point = new Point(randXpixels, randYpixels);

                if (distanceToNearestPointSquared(point, points) >= nearestAllowedSquared)
                {
                    points.Add(point);
                    Console.WriteLine(points.Count);
                }
            }
        }

        private static long distanceToNearestPointSquared(Point point, IEnumerable<Point> points)
        {
            long nearestDistanceSquared = long.MaxValue;

            foreach (var p in points)
            {
                int dx = p.X - point.X;
                int dy = p.Y - point.Y;

                long distanceSquared = dx*dx + dy*dy;

                nearestDistanceSquared = Math.Min(nearestDistanceSquared, distanceSquared);
            }

            return nearestDistanceSquared;
        }
    }
}

您可以遍历平面中的点并使用公式计算距离,并获得新的随机点。

What about something like this?

public void generateFirstMap()
{
    int count = 0;
    do
    {
        bool addPoint = true;
        int randXpixels = Main.rand.Next(24, Main.screenWidth - 16);
        int randYpixels = Main.rand.Next(27, Main.screenHeight - 27);
        for (int a = 0; a < points.Count; a++)
        {
            int dX = randXpixels - points[a].X;
            int dY = randYpixels - points[a].Y;
            if (dX * dX + dY * dY < 200)
            {
                addPoint = false;
                break;
            }
        }
        if(addPoint)
            points.Add(new Point(randXpixels,randYpixels));

        count++;
    } while (count < 100);
}

Just change the Point class to your tuple or whatever you are using

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM