views:

55

answers:

2

Hello.

Im trying to draw objects (in this case lines) that have same geometry. Since they would appear on top of each other i have found that using CompoundArray property i can visualize the offset as desired. The code is just a test code, so if some one wants to have a look let me know. Can some1 explain why im getting this weird glitch in the inner corners of lines that have CompoundArray applied? Any suggestion would be appreciated.

alt text

Here is a simple "test" class for this problem:

public partial class Form1 : Form
{
    int penWidth;
    int penOffset;
    Color penColor;

    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        Point[] linePoints = this.LinePoints();
        GraphicsPath path1 = new GraphicsPath();
        path1.AddLines(linePoints);

        // red line no offset
        this.penWidth = 10;
        this.penOffset = 0;
        this.penColor = Color.Red;
        Pen linePen1 = this.CreatePen(penWidth, penOffset, penColor);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawPath(linePen1, path1);

        // blue line offset 10
        this.penWidth = 10;
        this.penOffset = 10;
        this.penColor = Color.Blue;
        Pen linePen2 = this.CreatePen(penWidth, penOffset, penColor);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawPath(linePen2, path1);

        // green line offset -30
        this.penWidth = 1;
        this.penOffset = -30;
        this.penColor = Color.Green;
        Pen linePen3 = this.CreatePen(penWidth, penOffset, penColor);
        linePen3.MiterLimit = 2;
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawPath(linePen3, path1);

        // black rectangle no offset
        this.penWidth = 10;
        this.penOffset = 0;
        this.penColor = Color.Black;
        Pen rectanglePen = this.CreatePen(penWidth, penOffset, penColor);
        Rectangle rect = new Rectangle(600, 200, 100, 100);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawRectangle(rectanglePen, rect);

        // blue rectangle offset 10
        this.penWidth = 10;
        this.penOffset = 10;
        this.penColor = Color.Blue;
        Pen rectanglePen1 = this.CreatePen(penWidth, penOffset, penColor);
        Rectangle rect1 = new Rectangle(600, 200, 100, 100);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawRectangle(rectanglePen1, rect1);

        // teal rectangle offset 50
        this.penWidth = 10;
        this.penOffset = 50;
        this.penColor = Color.Teal;
        Pen rectanglePen4 = this.CreatePen(penWidth, penOffset, penColor);
        Rectangle rect4 = new Rectangle(600, 200, 100, 100);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawRectangle(rectanglePen4, rect4);

        // violet rectangle offset -10
        this.penWidth = 10;
        this.penOffset = -10;
        this.penColor = Color.Violet;
        Pen rectanglePen2 = this.CreatePen(penWidth, penOffset, penColor);
        Rectangle rect2 = new Rectangle(600, 200, 100, 100);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawRectangle(rectanglePen2, rect2);

        // tomato rectangle offset -30
        this.penWidth = 10;
        this.penOffset = -30;
        this.penColor = Color.Tomato;
        Pen rectanglePen3 = this.CreatePen(penWidth, penOffset, penColor);
        Rectangle rect3 = new Rectangle(600, 200, 100, 100);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.DrawRectangle(rectanglePen3, rect3);
    }

    public Pen CreatePen (int width, int offset, Color color)
    {
        Pen pen = new Pen(color, width);
        if (offset == 0)
        {
            return pen;
        }
        else
        {
            return (this.ModifyPenWithOffset(pen, offset));
        }

    }

    public Pen ModifyPenWithOffset(Pen pen, int offset)
    {
        var originalPenWidth = pen.Width;
        pen.Width = 2 * (pen.Width + Math.Abs(offset));
        if (offset > 0)
        {
            var calculation = originalPenWidth / pen.Width;
            pen.CompoundArray = new Single[] { 0.0F, calculation };
            return pen;
        }
        else
        {
            var calculation = (pen.Width - originalPenWidth) / pen.Width;
            pen.CompoundArray = new Single[] { calculation, 1.0F };
            return pen;
        }

    }

    public Point[] LinePoints()
    {
        Point[] points = new Point[12];
        points[0] = new Point(0, 0);
        points[1] = new Point(50, 100);
        points[2] = new Point(100, 220);
        points[3] = new Point(60, 300);
        points[4] = new Point(40, 330);
        points[5] = new Point(60, 360);
        points[6] = new Point(180, 200);
        points[7] = new Point(200, 60);
        points[8] = new Point(270, 60);
        points[9] = new Point(270, 200);
        points[10] = new Point(350, 200);
        points[11] = new Point(350, 150);
        return points;
    }
}
+1  A: 

You are getting this 'glitch' because you aren't allowing anything for the corners. When you shift two lines by an offset amount you also need to adjust for the shift in the location of their intersection.

For example, in your second image the green line runs parallel to the red line from the left of the image. Because the green line is on the inside of the red line, it needs to be shorter than the red line. You haven't allowed for this. Both of the green lines that form this corner extend too far. The small triangle formed in the corner is due to the overshoot caused by the green lines being the same length as the red lines. The long side of the triangle is caused by 'not lifting the pen' when moving from the end of the first line to the beginning of the second line.

Kirk Broadhurst
ok that sounds fair enough. But im doing this because i need to draw several objects on same geometry. In this case three polylines with same geometry need to be presented somehow (i decided to use offset). So i cannot change the data of a polyline itself ... what solution do you suggest?
no9
To paraphrase (I think): without adjustment, the reference line always needs to be the one on the *inside* of the corner.
Benjol
My first instinct is that you firstly need to shift the lines as you've done, but forget about the length of the lines. To do this you could define them as a point and a slope/gradient. Then calculate the (new) intersection points for the lines. This will give you the points that you need to draw.
Kirk Broadhurst
@Benjol - The image has changed since I first posted this answer, but in the original image when the reference line was on the inside the new line was still mitred, but in the opposite manner; i.e. to jump a gap between the lines rather than folding back as it does on the inside.
Kirk Broadhurst
@Benjol: since the lines can be "any shape" there is no actual "inside or outside" of the corner.
no9
@Kirk: i will try your advice, yet at this point i dont see the solution ... yet :). I was thinking of lenght of the lines but i didnt thought of that untill you pointed out.
no9
@no9 - The solution is, unfortunately, to write your own 'offset' method rather than the library method. The library method doesn't take into account the corners as discussed above; yours would have to take this into account to meet your needs.
Kirk Broadhurst
@Kirk - Thanx for the advice. Now i wish i would found some guide to get me started ... im not experienced enough to shake this right out of my sleeve ;)
no9
@no9 - Sorry I don't have time to solve this one for you right now!
Kirk Broadhurst
@Kirk - I never expected that you will. I only can thank you for investing time to my problem.
no9
Maybe there is another method to offset objects? Mathematical method would be offseting all points by calculating direction vector and based on that creating an offset. This seems like consuming method, yet possible. Maybe i could somehow offset object when rendering on pixel scale? Any idea?
no9
maybe there is some GDI+ alternative that has offset functionality implemented on the level i require?
no9
besides Direct2d, that is limited to Vista SP2 / Windows 7 clients ...
no9
A: 

I think the problem in this example is that the polygon has corners that are too tight for the width of the offset pen. For example, suppose you draw a polygon with a line that's 50 pixels wide but it has a bend where two lines are less than 50 pixels apart.

(The forum won't let me post a picture) This picture uses a compound line that's 20 pixels wide but the horizontal segment at the bottom is only 5 pixels long. That means there isn't room for the line to make that corner without overlapping itself.

I think to make it work, you need the shortest segment on the polygon to be at least as long as the pen's width.

Rod Stephens