0

This code draws a star shape :

public void draw(double radius, Color color)
    {
        int x = 0, y = Convert.ToInt32(radius);
        double d = (5 / 4) - radius;
        circlePoint(x, y, color);
        while (x < y)
        {
            if (d < 0)
            {
                d += Math.Pow(x, 2) + 3;
                x++;
                y--;
            }
            else
            {
                d += (x - y) * 2 + 5;
                y--;
            }

            circlePoint(x, y, color);
        }

    }

The drawn shape is shown below

a star-like shape made by c#

The Actual Question: I want to write a method (bool hasPoint(Point p) ) to check if p is inside this shape. I know for other shapes like a circle or an ellipse, we can check the point's x and y in relative to the object's formula. My Question is "How can I find the formula using the algorithm in draw() ?

Might be helpful: I was messing around with the formula of circle and ellipse and I encountered this shape. It's obvious that it has a relation with those shapes formula.

EDIT:

This is not a polygon. You can see this as a deformed circle (an ellipse actually). In a circle, if p.X-x0^2 + p.Y-y0^2 is less than radius^2, then p is a point inside. You can easily tell that it is the formula of a cirlce: x^2 + y^2 = r^2 .

EDIT 2:

( (x0,y0) is the center point )

void circlePoint(int x, int y, Color foreColor)
    {
        putPixel(x + x0, y + y0);
        putPixel(y + x0, x + y0);
        putPixel(-x + x0, y + y0);
        putPixel(-y + x0, x + y0);
        putPixel(x + x0, -y + y0);
        putPixel(y + x0, -x + y0);
        putPixel(-x + x0, -y + y0);
        putPixel(-y + x0, -x + y0);
    }

void putPixel(int x, int y, Color color){ bitmap.SetPixel(x, y, color); }

EDIT3:

It appears that this curve is mirrored by 8 lines (2 horizontal, 2 vertical and 4 diagonal):

the curve

EDIT4:

Based on emrgee's answer I've added these 2 functions, I don't know where I am wrong but it seems hasPointInside() never returns true;

    public double contour(double x)
    {
        double x2 = Math.Pow(x,2);
        return -0.190983 + 0.427051 * Math.Sqrt(0.923607 + (0.647214 * x) - (1.37082 * x2));
    }

    public bool hasPointInside(Point p)
    {

        int min = Math.Min(Math.Abs(p.X), Math.Abs(p.Y));
        int max = Math.Max(Math.Abs(p.X), Math.Abs(p.Y));

        return min <= radius * contour(max / radius);

    }
mrahimygk
  • 512
  • 5
  • 20
  • Is it in `wpf` or `winforms`? – EgoPingvina Dec 09 '16 at 13:34
  • 6
    @EgoPingvina Why would that matter? – juharr Dec 09 '16 at 13:35
  • 1
    For a general shape you need to cast a ray from the point in any direction and see how many times it crosses the boundary. If it crosses an odd number of times then it's inside. You do need to take into account where it may just "touch" the boundary (i.e touches at a tangent) and count that as either zero or two crossings. While this is relatively straightforward for polygons I'm not sure how easy it would be to do for a computed shape. – ChrisF Dec 09 '16 at 13:36
  • Add all points of figure to array and use advice of this post http://stackoverflow.com/questions/10673740/how-to-check-if-a-point-x-y-is-inside-a-polygon-in-the-cartesian-coordinate-sy. – EgoPingvina Dec 09 '16 at 13:45
  • @EgoPingvina If This was a polygon made of straight lines and points, That would be a good help. Consider this shape as an 1/8 mirrored curve. – mrahimygk Dec 09 '16 at 14:29
  • @ChrisF The problem is that I don't have the points, and one of my implicit questions is that how can I determine the boundaries of this shape? or how can I find the actual formula of this shape structure. – mrahimygk Dec 09 '16 at 14:32
  • @m.vincent if you approximate the curve with a series of straight lines then you could use the method. The drawback would be that for points near the boundary it would be unreliable (depending on how many points you used). – ChrisF Dec 09 '16 at 14:34
  • 2
    What do you mean by 'I don't have the points'? You use them for drawing, don't you? If you are talking about analyzing the algotrithm, this __may__ be a better fit for Mathematics. I guess you will have to split it up into 4 or five or maybe even 8 or nine shapes. – TaW Dec 09 '16 at 14:34
  • @ChrisF I know that I could save every x and y passed to circlePoint(int, int, Color) in a List. But that seems unnecessary. every one of these 4 wings have the same relative distance from the center ( (x0,y0) which is not included here), like an ellipse. That's because It's more efficient to check the Point with the shape's radius. – mrahimygk Dec 09 '16 at 14:46
  • @TaW Yes, I need to analyze the code and find about how this shape is made. – mrahimygk Dec 09 '16 at 14:48
  • It would help if we had a definition for `circlePoint` and knew what parameters you'd used to generate the shown result. I've tried and failed to try and run this in my head, and with the incomplete code, I don't have the option to actually run it, so I'm failing to picture how it's actually running. – Damien_The_Unbeliever Dec 09 '16 at 14:48
  • Is the shape made up of four intersected circles? – ChrisF Dec 09 '16 at 14:52
  • The code still doesn't compile, but of course is easy to fix. Also: Why call draw with a bitmap but not pass it on to the methods? – TaW Dec 09 '16 at 15:05
  • @ChrisF I edited the Q for you. – mrahimygk Dec 09 '16 at 15:17
  • @TaW The bitmap is a global variable. I deleted it from draw() – mrahimygk Dec 09 '16 at 15:18
  • 1
    I don't think extracting a formula is possible but you can take your point, transform it to remove the influence of `x0` and `y0`. Then, without loss of generality, you can make its coordinates positive and with x less than y. Then you run exactly the same algorithm as above - once it's generating the same X value as your point, you compare the Y values. If the point's Y value is greater than the Y values the algorithm generates, then it's outside. – Damien_The_Unbeliever Dec 09 '16 at 15:20
  • The 'actual question' can be answered by collecting the points in a list, splitting it in two, the left and the right ones and then looking up the points in question there: those that are between a pair are inside. But the formula will have to take the drawing process into account. It will be split into several branches. It is instructive to insert two lines into the draw loop's branches that change the color between two different ones and watch how the 1/8 curve is made up of two distincts parts.. (of course the color must be passed out into the drawing methods..) – TaW Dec 09 '16 at 15:34

1 Answers1

1

The draw method computes the star contour iteratively, starting from its tip at (0, radius). For understanding and maintaining the code, as well as for the point-in-star test you need, a closed formula of the contour would be preferable. Trying to extract such a formula from the code, let's first analyze the true branch of the if, which is the only branch taken for the first few iterations. The accumulation of d can be transformed into a closed form for d: Closed form for sum of (i^2 + 3)

While this already doesn't look like anything related to an ellipse, d gets accumulated with a different formula when in the else branch. I claim it is not possible to transform d into a closed form for the entire algorithm.

Other observations: the slope of the contour can only get -1 or steeper. And there are magic numbers in the formulas not computed from the radius argument. So there are strong indications that the contour resembles an ellipse only accidentally.

Now let's assume you want a star with an ellipsoid contour, with 90° tips and with 90° corners. Let's start with the blue ellipse with major radius 1 in x and minor radius 1/2 in y direction: Ellipse massage

We scale and move it so that the green point ends up on the diagonal and the red point, where the slope is -1, ends up at (1,0). With a bit of formula massage, the contour becomes: Contour formula

or, numerically:

Numerical contour formula

The offset on the x axis is -2 + Sqrt(5) or 0.236068.

Pseudo code for your hasPoint method would then look like this: Pseudo code for point-in-star test

with this result for radius 30: Points in star

In C#, you would provide these methods:

    /// <summary>
    /// Calculates a contour point of the four-pointed star.
    /// </summary>
    /// <param name="x">The abscissa of the contour point.</param>
    /// <returns>Returns the ordinate of the contour point.</returns>
    private static double StarContour(double x)
    {
        return -0.190983d + (0.427051d * Math.Sqrt(0.923607d + (0.647214d * x) - (1.37082d * x * x)));
    }

    /// <summary>
    /// Tests whether a point is inside the four-pointed star of the given radius.
    /// </summary>
    /// <param name="radius">The radius of the star.</param>
    /// <param name="x">The abscissa of the point to test.</param>
    /// <param name="y">The ordinate of the point to test.</param>
    /// <returns>Returns <see langword="true" /> if the point (<paramref name="x" />, <paramref name="y" />
    /// is inside or on the contour of the star with the given <paramref name="radius" />;
    /// otherwise, if the point is outside the star, returns <see langword="false" />.</returns>
    private static bool InFourStar(double radius, double x, double y)
    {
        double min = Math.Abs(x);
        double max = Math.Abs(y);
        if (min > max)
        {
            // swap min and max
            double h = min;
            min = max;
            max = h;
        }

        return min <= radius * StarContour(max / radius);
    }

and then use them like this:

    /// <summary>
    /// Draws the outline of a four-pointed star of given radius and color.
    /// </summary>
    /// <param name="radius">The radius of the star.</param>
    /// <param name="color">The color of the outline.</param>
    private void DrawFourStar(double radius, Color color)
    {
        const double Offset = 0.236068d;
        for (int x = (int)(Offset * radius); x < radius; ++x)
        {
            int y = (int)(radius * StarContour(x / radius));
            this.CirclePoint(x, y, color);
        }
    }

    /// <summary>
    /// Draws sample points inside or on a four-pointed star contour of given radius.
    /// </summary>
    /// <param name="radius">The radius of the star.</param>
    /// <param name="color">The color of the sample points.</param>
    private void DrawSamplesInStar(double radius, Color color)
    {
        const int SamplingStep = 10;
        for (int x = (int)(-radius); x < radius; x += SamplingStep)
        {
            for (int y = (int)(-radius); y < radius; y += SamplingStep)
            {
                if (InFourStar(radius, x, y))
                {
                    this.bitmap.SetPixel(x + this.x0, y + this.y0, color);
                }
            }
        }
    }

which looks like this for radius 200: WinForm solution for points in four-pointed star

emrgee
  • 174
  • 3
  • 8
  • Below the contour diagram, did you assume that the radius is 1 (and it doesn't matter in contouring) ? I don't know if StarContour() can draw this shape without using the radius or not. And does it return anything? Sorry about my lack of knowledge on turning the Pseudo code into c# code. – mrahimygk Dec 12 '16 at 13:26
  • I double checked my copied codes and it worked as you've done. But I cannot get 'True' by calling InFourStar() from another class. The other thing is that (although this is the correct answer) your shape is slightly different than mine, but is is not easily noticeable. – mrahimygk Dec 15 '16 at 15:57