Click here to Skip to main content
15,911,762 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello, hopefully this is super simple for someone experienced with graphics and can explain the problem here (I'm guessing it has to do with Rounding.

I'm drawing a triangle here and I'm using IsVisible and doing a hit test on a point (to see if the point is inside the triangle. For testing purposes I'm scanning a few points in a couple of lines. The triangle is (0,0),(10,0),(10,10). I would've expected the point 5,5 to be right on the line (i.e. unsure if it hits), but certainly nothing earlier in X (e.g. 4.9,5) to be a hit. However if you look at my results below, this starts to evaluate true far earlier at 4.5,5. Is this just a rounding error? (sort of weird since it's so far).

Is there a better way to do a hit test? I can't just do equations as I want my code to be fairly generic.

C#
private static GraphicsPath RunHitTestsWithGP()
{
    var gp = new GraphicsPath();
    gp.StartFigure();
    gp.AddLines(new PointF[]
        {
            new PointF(0, 0),
            new PointF(10, 0),
            new PointF(10, 10),
            new PointF(0, 0)
        });
    gp.CloseFigure();

    for (int i = 0; i < 20; i++)
    {
        var t1 = new PointF(4f + i / 10f, 5);
        System.Diagnostics.Debug.WriteLine("Testing: {0} Yields: {1}", t1, gp.IsVisible(t1));
    }

    for (int i = 0; i < 20; i++)
    {
        var t1 = new PointF(4f + i / 10f, 6);
        System.Diagnostics.Debug.WriteLine("Testing: {0} Yields: {1}", t1, gp.IsVisible(t1));
    }
    return gp;
}


Testing: {X=4, Y=5} Yields: False
Testing: {X=4.1, Y=5} Yields: False
Testing: {X=4.2, Y=5} Yields: False
Testing: {X=4.3, Y=5} Yields: False
Testing: {X=4.4, Y=5} Yields: False
Testing: {X=4.5, Y=5} Yields: True
Testing: {X=4.6, Y=5} Yields: True
Testing: {X=4.7, Y=5} Yields: True
Testing: {X=4.8, Y=5} Yields: True
Testing: {X=4.9, Y=5} Yields: True
Testing: {X=5, Y=5} Yields: True
Testing: {X=5.1, Y=5} Yields: True
Testing: {X=5.2, Y=5} Yields: True
Testing: {X=5.3, Y=5} Yields: True
Testing: {X=5.4, Y=5} Yields: True
Testing: {X=5.5, Y=5} Yields: True
Testing: {X=5.6, Y=5} Yields: True
Testing: {X=5.7, Y=5} Yields: True
Testing: {X=5.8, Y=5} Yields: True
Testing: {X=5.9, Y=5} Yields: True


Testing: {X=4, Y=6} Yields: False
Testing: {X=4.1, Y=6} Yields: False
Testing: {X=4.2, Y=6} Yields: False
Testing: {X=4.3, Y=6} Yields: False
Testing: {X=4.4, Y=6} Yields: False
Testing: {X=4.5, Y=6} Yields: False
Testing: {X=4.6, Y=6} Yields: False
Testing: {X=4.7, Y=6} Yields: False
Testing: {X=4.8, Y=6} Yields: False
Testing: {X=4.9, Y=6} Yields: False
Testing: {X=5, Y=6} Yields: False
Testing: {X=5.1, Y=6} Yields: False
Testing: {X=5.2, Y=6} Yields: False
Testing: {X=5.3, Y=6} Yields: False
Testing: {X=5.4, Y=6} Yields: False
Testing: {X=5.5, Y=6} Yields: True
Testing: {X=5.6, Y=6} Yields: True
Testing: {X=5.7, Y=6} Yields: True
Testing: {X=5.8, Y=6} Yields: True
Testing: {X=5.9, Y=6} Yields: True
Posted

Known issue. Floating point values are rounded to integers when calling graphicspath.isvisible.

unbelievable and sad, but true. I am working on another way. I will update you shortly.

Update:

After thinking about it, it makes sense, like double.equals you would need to define a difference measure, or precision. Otherwise it would be infinitesimally precise. So a simple mathematical workaround could be to scale up and then do the hit test. In other words determine at what decimal place do you want to round(i.e. 4,9, 4.99, 4.999). So the solution below works, but maybe you could refine it a bit, because it will only work to a point and then it overflows. I checked it for 5 decimal places and it works. You also have to consider the precision of the entries, like for graphics in real world scenarios with pixels, for example, you can't detect a fractional pixel. Not sure what the purpose of the app is, but this should point you in the right direction.

C#
private static GraphicsPath RunHitTestsWithGP(int DecimalPlaces)
{
	dynamic gp = new GraphicsPath();
	int Scale = Math.Pow(10, DecimalPlaces);
	gp.StartFigure();
	gp.AddLines(new PointF[] {
		ScalePointF(new PointF(0, 0), DecimalPlaces),
		ScalePointF(new PointF(10, 0), DecimalPlaces),
		ScalePointF(new PointF(10, 10), DecimalPlaces),
		ScalePointF(new PointF(0, 0), DecimalPlaces)
	});
	gp.CloseFigure();
	for (int i = 0; i <= 19; i++) {
		dynamic t1 = new PointF(4f + i / 10f, 5);
		Console.WriteLine("Testing: {0} Yields: {1}", t1, gp.IsVisible(ScalePointF(t1, DecimalPlaces)));
	}

	for (int i = 0; i <= 19; i++) {
		dynamic t1 = new PointF(4f + i / 10f, 6);
		Console.WriteLine("Testing: {0} Yields: {1}", t1, gp.IsVisible(ScalePointF(t1, DecimalPlaces)));
	}
	return gp;
}

private static PointF ScalePointF(PointF PointFToScale, int DecimalPlaces)
{
	return new PointF(PointFToScale.X * (Math.Pow(10, DecimalPlaces)), PointFToScale.Y * (Math.Pow(10, DecimalPlaces)));
}
 
Share this answer
 
v2
Ah, I did a little further reading based on your input, thanks. Kind of annoying that they seem to advertise floating point graphics when in fact it is not well supported.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900