tags:

views:

126

answers:

3

I see that Region.IsVisible(rectangle) is not working as I expect.
So, is me who expect that should not, or is the method that is doing not that it should ??!

I have the following situation:

alt text alt text

And the following code:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Point[] points1 = new Point[] {
        new Point(50, 30),
        new Point(70, 30),
        new Point(40, 40),
        new Point(60, 70),
        new Point(30, 50)
    };

    Point[] points2 = new Point[] {
        new Point(70, 150),
        new Point(50, 110 ),
        new Point(60, 80),
        new Point(90, 80),
        new Point(140, 60)                
    };

    Point[] points3 = new Point[] {
        new Point(100, 10),
        new Point(130, 40)
    };

    GraphicsPath path1 = new GraphicsPath();
    GraphicsPath path2 = new GraphicsPath();
    GraphicsPath path3 = new GraphicsPath();

    path1.AddLines(points1);
    path2.AddLines(points2);
    path3.AddLines(points3);

    e.Graphics.DrawPath(Pens.DarkBlue, path1);
    e.Graphics.DrawPath(Pens.DarkGreen, path2);
    e.Graphics.DrawPath(Pens.DarkOrange, path3);

    Region r1 = new Region(path1);
    Region r2 = new Region(path2);
    Region r3 = new Region(path3);

    // Create the first rectangle and draw it to the screen in blue.
    Rectangle blueRect = new Rectangle(20, 20, 100, 100);
    e.Graphics.DrawRectangle(Pens.Blue, blueRect);

    bool contained;

    // Display the result.                        
    ControlPaint.DrawGrid(e.Graphics, this.ClientRectangle,
        new Size(10, 10), Color.Red);

    contained = r1.IsVisible(blueRect);
    e.Graphics.DrawString("Path blue contained = " + contained.ToString(),
        Font, myBrush, new PointF(20, 160));

    contained = r2.IsVisible(blueRect);
    e.Graphics.DrawString("Path green contained = " + contained.ToString(),
        Font, Brushes.Black, new PointF(20, 180));

    contained = r3.IsVisible(blueRect);
    e.Graphics.DrawString("Path orange contained = " + contained.ToString(),
        Font, Brushes.Black, new PointF(20, 200));
}

Also, a path that is not in the region could be "visible":

Point[] points3 = new Point[] {
  new Point(15, 35),
  new Point(15, 130),
  new Point(60 ,130)
};

EDIT:
Even Intersect does not work for the second L path:

Point[] points3 = new Point[] {
    new Point(10, 40),
    new Point(10, 130),
    new Point(50 ,130)
};

r3.Intersect(blueRect);
bool contained = !(r1.IsEmpty(e.Graphics)); 
e.Graphics.DrawString("Path orange contained = " + contained.ToString(),
    Font, Brushes.Black, new PointF(20, 200)); // TRUE! instead of desired FALSE
+1  A: 

It may be that the Region.IsVisible method just checks whether any of the endpoints of its component line segments are within the rectangle or not. Thus, the blue and green lines (which each have multiple segment endpoints within the rectangle) are true, while the orange line (which has 0 endpoints within the rectangle) is false.

Technically, your code is actually trying to determine whether each irregular Path contains the blue rectangle (rather than the other way around). An alternative way of doing what you're actually trying to do (but that will probably return the same results) is this:

r1.Intersect(blueRect);
e.Graphics.DrawString("Path blue contained = " + 
    (!r1.IsEmpty(e.Graphics)).ToString(), Font, Brushes.Black, 
    new PointF(20, 200));   
MusiGenesis
This was my idea first too, but I think the problem is not that there is no point of path3 inside the rectangle. Instead, I think the problem might be that path3 only consists of two points. I changed points3 to `Point[] points3 = new Point[] { new Point(90, 10), new Point(100, 10), new Point(130, 40) };` and got the expected result.
0xA3
@0xA3: you have reason... there is something strange...
serhio
In fact, the problem seems to be that points3 is a one-dimensional object, as `Point[] points3 = new Point[] { new Point(90, 0), new Point(100, 10), new Point(130, 40) };` is also not correct. Even more weird, `Point[] points3 = new Point[] { new Point(90, 1), new Point(100, 10), new Point(130, 40) };` and `Point[] points3 = new Point[] { new Point(90, 2), new Point(100, 10), new Point(130, 40) };` give different results. Looks like a bug to me...
0xA3
Building a region from a single line is somewhat a degenerated case - even after closing the path it is still only a line and therefore has zero area. I am not really surprised that the test fails and it might well be the correct behavior.
Daniel Brückner
@Daniel Brückner: As mentioned Lucero the documentation tells clear what should do the method. So, the *correct* behavior is that Microsoft described.
serhio
Yes, it says "Tests whether any portion of the specified Rectangle structure is contained within this Region." And I would not expect that any portion of a rectangle is contained in a region of size zero. Therefore returning `false` seems correct to me.
Daniel Brückner
@Daniel: The point (110, 20) is a "portion" of rectangle, and the region contains it. So, it should return `true`.
serhio
The region generated from the two points (100, 10) and (130, 40) is only a line, therefore has zero area, and contains nothing at all.
Daniel Brückner
Admittedly one could argue if the boundary of a region is part of the region or not - that is if a region is an open or closed set. If a region is a closed set, then some points (we have to consider the non-zero width of the "line") of the blue "line" are contained in the region. But they are still invisible because of the cross-section of width zero.
Daniel Brückner
@MusiGenesis: Why do you use "Union" and not Intersection?
serhio
@serhio: because I'm an idiot. Updating my answer ...
MusiGenesis
@Daniel Brückner: I think you're right, and you should post your comment as an (maybe *the*) actual answer.
MusiGenesis
@MusiGenesis: I thought about adding an answer, but my thoughts are based on the math of the problem (as I understand and would implement it) but I am by no means and GDI expert.
Daniel Brückner
@Daniel: There is no need to be an GDI expert to add an answerand your explanation seems to be reasonable. That from one part. From other part, I would to complain that Microsoft does not specified this in its documentation, so for all of us remains only to guess why some MS code does not exactly that most of us expect.
serhio
updated with one more "strange" example.
serhio
@serhio: From the orange example I see you are misunderstanding what your code does. Building a region from a path *does not* yield a region in the form of the drawn path. Building a region from the L-shaped orange path does not yield a L-shaped region. The path gets closed by connecting both ends forming a triangle. The region is then the interior of that triangle and the blue rectangle is obviously partial contained in that region.
Daniel Brückner
What you actually want to do is performing a visibility test of the path against the rectangular region of the blue rectangle. As far as I know there is no build-in support for doing this.
Daniel Brückner
@Daniel: So, this solution will not work for the L shape...
serhio
+3  A: 

From the orange L-shaped example I see you are misunderstanding what your code does.

Building a region from a path does not yield a region in the form of the drawn path. Building a region from the L-shaped orange path does not yield a L-shaped one pixel width region. The path gets closed by connecting both ends forming a triangle. The region is then the interior of that triangle and the blue rectangle is obviously partial contained in that region.

For the initial example of the single orange line the resulting region is a degenerated polygon with two corners only - still looking like a line and with zero width in the direction orthogonal to the line(s). Therefore the region has zero area and does not contain anything (except maybe points on the border of the region if a region is a closed set).

What you actually want to do is performing a visibility test of a path against the rectangular region of the blue rectangle. As far as I know there is no build-in support for doing this.

Daniel Brückner
So, you are about to say that the bellow solution of MusiGenesis will not work for the L path.
serhio
MusiGenesis' solution should return true, too. The intersection of the region of the blue rectangle with the triangular region created from the L-shaped path is again a triangle - and it is non-empty. But just try it - I did not.
Daniel Brückner
@Daniel: Tested, returns True, see the edit...
serhio
Returning true should not surprise you - it absolutely obvious. The region created from the L-shaped path overlaps the blue rectangle. Just try `Graphics.FillRegion()` to visualize the regions you are creating - I think you are still expecting the regions to look completely different from what they actually look like. Could you also add the resulting images to your question because they might help others? You are probably not the only one having this slight misconception in mind.
Daniel Brückner
+1  A: 

So, a solution for this was to use... Widen() method for paths before checking its visibility:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Point[] points1 = new Point[] {
                new Point(50, 30),
                new Point(70, 30),
                new Point(40, 40),
                new Point(60, 70),
                new Point(30, 50)
            };

    Point[] points2 = new Point[] {
                new Point(70, 150),
                new Point(50, 110 ),
                new Point(60, 80),
                new Point(90, 80),
                new Point(140, 60)
            };

    Point[] points4 = new Point[] {
        new Point(50, 50),
        new Point(90, 90)
      };

    Point[] points3 = new Point[] {
                new Point(15, 35),
                new Point(15, 130),
                new Point(60 ,130)
            };

    GraphicsPath path1 = new GraphicsPath();
    GraphicsPath path2 = new GraphicsPath();
    GraphicsPath path3 = new GraphicsPath();
    GraphicsPath path4 = new GraphicsPath();

    path1.AddLines(points1);
    path2.AddLines(points2);
    path3.AddLines(points3);
    path4.AddLines(points4);

    e.Graphics.DrawPath(Pens.DarkBlue, path1);
    e.Graphics.DrawPath(Pens.DarkGreen, path2);
    e.Graphics.DrawPath(Pens.DarkRed, path3);
    e.Graphics.DrawPath(Pens.DarkGoldenrod, path4);

    // <<<< HERE >>>>>
    path3.Widen(Pens.DarkRed);
    path4.Widen(Pens.DarkGoldenrod);

    Region r1 = new Region(path1);
    Region r2 = new Region(path2);
    Region r3 = new Region(path3);
    Region r4 = new Region(path4);

    // Create the first rectangle and draw it to the screen in blue.
    Rectangle blueRect = new Rectangle(20, 20, 100, 100);
    e.Graphics.DrawRectangle(Pens.Blue, blueRect);

    bool contained;

    // Display the result.            
    ControlPaint.DrawGrid(e.Graphics, this.ClientRectangle,
         new Size(10, 10), Color.Red);

    contained = r1.IsVisible(blueRect);
    e.Graphics.DrawString("Path blue contained = " + contained.ToString(),
         Font, Brushes.Black, new PointF(20, 160));

    contained = r2.IsVisible(blueRect);
    e.Graphics.DrawString("Path green contained = " + contained.ToString(),
         Font, Brushes.Black, new PointF(20, 180));

    contained = r3.IsVisible(blueRect);
    e.Graphics.DrawString("Path orange contained = " + contained.ToString(),
         Font, Brushes.Black, new PointF(20, 200));

    contained = r4.IsVisible(blueRect);
    e.Graphics.DrawString("Path DarkGoldenrod contained = " + contained.ToString(),
         Font, Brushes.Black, new PointF(20, 220));
}

As for the question in the thread title: Yes, actually IsVisible of Region is not working as documented in MSDN.

Microsoft should add some precision notes in their documentation.

serhio