1

So I got a circle in a Windows Forms Application and have to place 20 random points in this circle. My idea was to split the circle into 4 parts to make it more balanced. My problem is that the points are all generated around the middle and I have no idea how to fix this...

    Graphics g;
    Pen p;
    Random r = new Random();
    int[] KegelX = new int[20];
    int[] KegelY = new int[20];
    private void Form1_Load(object sender, EventArgs e)
    {

    }

    private void button1_Click(object sender, EventArgs e)
    {
        Kegelplatzierung();
        p = new Pen(Color.Black);
        g = this.CreateGraphics();
        g.DrawEllipse(p, new Rectangle(50, 50, 400, 400));
        for (int i = 0; i < 20; i++)
        {
            g.DrawEllipse(p, new Rectangle(KegelX[i], KegelY[i], 1, 1));
        }
        p.Dispose();
        g.Dispose();
    }

    private void Kegelplatzierung() {
        for (int i = 0; i < 5; i++)
        {
            bool Kriterium = false;
            while (!Kriterium)
            {
                KegelX[i] = r.Next(50, 250);
                KegelY[i] = r.Next(50, 250);
                if (Math.Sqrt((250 - KegelX[i]) ^ 2 + (KegelY[i] - 250) ^ 2) < 200)
                {
                    Kriterium = true;
                }
            }
        }
        for (int i = 5; i < 10; i++)
        {
            bool Kriterium = false;
            while (!Kriterium)
            {
                KegelX[i] = r.Next(250, 450);
                KegelY[i] = r.Next(50, 250);
                if (Math.Sqrt((KegelX[i] - 250) ^ 2 + (KegelY[i] - 250) ^ 2) < 200)
                {
                    Kriterium = true;
                }
            }
        }
        for (int i = 10; i < 15; i++)
        {
            bool Kriterium = false;
            while (!Kriterium)
            {
                KegelX[i] = r.Next(50, 250);
                KegelY[i] = r.Next(250, 450);
                if (Math.Sqrt((250 - KegelX[i]) ^ 2 + (250 - KegelY[i]) ^ 2) < 200)
                {
                    Kriterium = true;
                }
            }
        }
        for (int i = 15; i < 20; i++)
        {
            bool Kriterium = false;
            while (!Kriterium)
            {
                KegelX[i] = r.Next(250, 450);
                KegelY[i] = r.Next(250, 450);
                if (Math.Sqrt((KegelX[i] - 250) ^ 2 + (250 - KegelY[i]) ^ 2) < 200)
                {
                    Kriterium = true;
                }
            }
        }

    }

Examples: http://puu.sh/gB6Dg/e81f8c3486.png http://puu.sh/gB6Ec/306f61424c.png

Thanks for help!

fbs1997
  • 17
  • 1
  • 3
  • 2
    "20 random points" and "more balanced" cause a conflict. Choose one. – Sam Axe Mar 15 '15 at 11:27
  • @Dan-o I am not sure why fbs1997 has to choose one of those phrases; having 5 random points in each quarter of the circle is a meaningful requirement. For example, The Random.Next() method allow us to specify boundary for "random" number. – Ravi M Patel Mar 15 '15 at 11:37
  • 1
    If instead of choosing a random *x* and *y* you choose a random radius and angle then you will not have to keep trying new random pairs until one fits in the circle. – Andrew Morton Mar 15 '15 at 12:05
  • Andrew, I think this will also bring more hits in the center. – TaW Mar 15 '15 at 12:56
  • Create points in the rectangle that bounds the circle and draw only those that actually are inside! You could use pythagoras or `bool GraphicsPath.IsVisible(Point)` – TaW Mar 15 '15 at 12:59
  • 1
    @TaW Chris A. shows how to get an even distribution in [Generate a random point within a circle (uniformly)](http://stackoverflow.com/a/5837686/1115360). Basically, you use the square root of a random number in the range [0, 1) and multiply that by the maximum radius. – Andrew Morton Mar 15 '15 at 13:13
  • Yes. That works. Lots of math, though ;-) – TaW Mar 15 '15 at 13:32

4 Answers4

3

This will do it as you planned it. Note however, that the symmetry between the four quadrants is only necessary if you really need it to balance very few points. For greater numbers it is not necessary and you could reduce the code to around half of its lines..!

private void panel2_Paint(object sender, PaintEventArgs e)
{
    int dotsPerQuadrant = 666;
    Random R = new Random();
    Size s1x1 = new System.Drawing.Size(2, 2);
    int radius = 200;
    int rad2 = radius / 2;
    int off = 20;
    Rectangle bounds = new Rectangle(off, off, radius, radius);
    GraphicsPath gp = new GraphicsPath();
    gp.AddEllipse(bounds);
    Rectangle rectQ1 = new Rectangle(off, off, rad2, rad2);
    Rectangle rectQ2 = new Rectangle(off + rad2, off, rad2, rad2);
    Rectangle rectQ3 = new Rectangle(off, off + rad2, rad2, rad2);
    Rectangle rectQ4 = new Rectangle(off + rad2, off + rad2, rad2, rad2);
    List<Rectangle> quadrants = new List<Rectangle> { rectQ1, rectQ2, rectQ3, rectQ4 };
    e.Graphics.Clear(Color.AntiqueWhite);
    e.Graphics.DrawEllipse(Pens.Black, bounds);

    foreach (Rectangle rect in quadrants)
    {
        int count = 0;
        do
        {
            Point p = new Point(rect.X + R.Next(rad2), rect.Y + R.Next(rad2));
            if (gp.IsVisible(p))
            {
                e.Graphics.FillEllipse(Brushes.Red, new Rectangle(p, s1x1));
                count++;
            }
        } while (count < dotsPerQuadrant);
    }

}

Here is the result. The dots spread over the whole circle uniformly, not clustering in the middle:

4x666 random dots

The direct code, sans quadrants;

private void panel2_Paint(object sender, PaintEventArgs e)
{
    int dotstoDraw = 666*4;
    Random R = new Random();
    Size s1x1 = new System.Drawing.Size(2, 2);
    int radius = 200;
    int off = 20;
    Rectangle bounds = new Rectangle(off, off, radius, radius);
    GraphicsPath gp = new GraphicsPath();
    gp.AddEllipse(bounds);

    e.Graphics.Clear(Color.AntiqueWhite);
    e.Graphics.DrawEllipse(Pens.Black, bounds);

    int count = 0;
    do
    {
        Point p = new Point(off + R.Next(radius), off + R.Next(radius));
        if (gp.IsVisible(p))
        {
            e.Graphics.FillEllipse(Brushes.Red, new Rectangle(p, s1x1));
            count++;
        }
    } while (count < dotstoDraw);
 }
TaW
  • 53,122
  • 8
  • 69
  • 111
2

You can do this by choosing a random radius and angle for each point.

To avoid noticeable quantisation of the angle component into radial spokes, I use (double)rand.Next() / int.MaxValue to get a random number between 0 and 1 and multiply that by 2π.

To avoid the points being bunched up near the centre of the circle, I used Chris A.'s formula (at Generate a random point within a circle (uniformly)) to generate the radius:

Random rand = new Random();

private List<Point> GetRandomPoints(double rMax, int nPoints)
{
    var randPoints = new List<Point>();
    for (int i = 0; i < nPoints; i++)
    {
        var r = Math.Sqrt((double)rand.Next() / int.MaxValue) * rMax;
        var theta = (double)rand.Next() / int.MaxValue * 2 * Math.PI;
        randPoints.Add(new Point((int)(r * Math.Cos(theta)), (int)(r * Math.Sin(theta))));
    }
    return randPoints;
}

private void button1_Click(object sender, EventArgs e)
{
    using (Graphics g = this.CreateGraphics())
    {
        using (Pen p = new Pen(Color.Black))
        {
            var left = 50;
            var top = 50;
            var r = 200;
            g.DrawEllipse(p, new Rectangle(left, top, r * 2, r * 2));

            int nPoints = 20;
            var randomPoints = GetRandomPoints(r - 1, nPoints);

            for (int i = 0; i < nPoints; i++)
            {
                g.DrawEllipse(p, new Rectangle(randomPoints[i].X + left + r, randomPoints[i].Y + top + r, 1, 1));
            }
        }
    }
}

To make sure objects are disposed of cleanly, you can use the using construct - it makes sure you don't accidentally forget.

It's usually better to assign things like the radius of the circle to a variable as then you can easily change it in one place, and it makes the code easier to read if you use meaningful variable names.

Here's an example of the output, but with the radius set to 100 and generating 200 points:

Sample output

Community
  • 1
  • 1
Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
2

The problem is that in C#, ^ is the logical xor operator. You need to use Math.Pow instead. So...

if (Math.Sqrt(Math.Pow(250 - KegelX[i], 2) + Math.Pow(KegelY[i] - 250, 2)) < 200)

and so on.

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
1

Generates a random set of points inside a circle of a given radius...

 private void button1_Click(object sender, EventArgs e)
            {
                int radius = 100;
                var p = new Pen(Color.Black);
                var g = this.CreateGraphics();
                g.DrawEllipse(p, 0,0,radius*2, radius*2);
                var pointGen = new RandomPointGenerator();
                var randomPoints = pointGen.GetPointsInACircle(radius, 20);
                p.Color = Color.Red;
                foreach (var point in randomPoints)
                {

                    g.DrawEllipse(p, point.X + radius, point.Y+radius, 2, 2);
                }
            }



public class RandomPointGenerator
    {
        private Random _randy = new Random();
        public List<Point> GetPointsInACircle(int radius, int numberOfPoints)
        {
            List<Point> points = new List<Point>();
            for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
            {
                int distance = _randy.Next(radius);
                double angleInRadians = _randy.Next(360)/(2 * Math.PI) ;

                int x = (int)(distance * Math.Cos(angleInRadians));
                int y = (int)(distance * Math.Sin(angleInRadians));
                Point randomPoint = new Point(x, y);
                points.Add(randomPoint);
            }
            return points;
        }
    }
dbugger
  • 15,868
  • 9
  • 31
  • 33