两端的画线偏移

时间:2016-10-03 11:38:21

标签: c# winforms drawing

我正在尝试使用DrawLine方法绘制虚线。但是,它会在末端增加一点点偏移,使线条变得难看。如何防止向末端添加偏移?

protected override void OnPaint (PaintEventArgs e) {
   Graphics g = e.Graphics;
   g.DrawLine (Dot, new Point (10, 50), new Point (70, 50));
   g.DrawLine (Dot, new Point (70, 50), new Point (70, 100));
}

Pen Dot = new Pen (Brushes.Black, 2) { DashStyle = DashStyle.Dot };

输出

enter image description here

预期结果

enter image description here

2 个答案:

答案 0 :(得分:2)

目标简单明了:

  • 我们希望我们的行以点开头。

游戏规则也很简单:

  • 点和间隙均默认为正方形。
  • 所有行均使用PenAlignment.Center
  • 绘制

不幸的是,这种组合的后果相当复杂,所以请耐心等待......

让我们先来看看第一条规则;让我们忽略其他DashStyles并留在DashStyle.Dot。默认情况下,每个点和每个间隙的两边都有Pen.Width像素。这导致了第一个问题:

  • 如果我们的行的宽度不能除以Pen.Width,我们就会遇到麻烦。
  • 要以点开头和结尾,我们希望有n点和n-1间隙。

还有更多,但让我们接下来看看第二条规则;为了说明这一点,我绘制了这个10倍放大的图像:

enter image description here

这是创建彩色部分的代码:

g.FillRectangle(Brushes.Yellow, 15, 15, 10, 10);
g.DrawRectangle(Pens.Orange, 10, 10, 10, 10);
g.DrawLine(Pens.OrangeRed, 10, 5, 40, 5);
using (Pen pen = new Pen(Color.Red, 2f) { DashStyle = DashStyle.Dot })
    g.DrawLine(pen, 10, 30, 48, 30);
using (Pen pen = new Pen(Color.Crimson, 2f))
    g.DrawLine(pen, 10, 40, 48, 40);
using (Pen pen = new Pen(Color.DarkMagenta, 3f))
    g.DrawLine(pen, 10, 50, 48, 50);

仔细观察线条是如何绘制的!

(旁白:您可能还想看DrawRectangleFillRectangle的差异!)

  • 水平线在右边坐标处开始和结束,但它们向下扩展(如果它们的Pen.Width = 1)或在y-coodinate之上和之下。
  • 当然,垂直线会做同样的事情。

问题在于它们不会在(外)边缘处组合在一起。

那我们该怎么办?我不认为DashOffset可以提供帮助。但是还有另一个选项来调整Pen:我们可以将其DashPattern设置为使用自定义值

我们需要的值是两个floats,其中包含点和间隙的缩放。默认情况下,这两个值均为1f。我决定将点保持正方形并仅修改间隙。这是一个通过

解决问题的功能
  • 在两侧将线宽扩展半个笔宽,使外边缘相交
  • 根据需要扩大间隙以适应行长

这是画线功能;它需要Graphics个对象,Pen,两个结尾Points和一个byte来告诉我们这条线是独立还是连接< / strong>到其他行,如我们的示例中所示。

为了建立一个良好的连接,这对于半透明画笔非常有效,我们需要能够在开头或结尾跳过一个点a,甚至两者都跳过例如当我们想在下面的测试中插入一条正交线时。

跳过值为0以跳过无,1 or 2跳过第一个或最后一个点,3跳过两者。当然,您可能希望使用enumeration

void DrawDottedLine(Graphics g, Pen pen_, Point pa_, Point pb, byte skipDots)
{
    float pw = pen_.Width;
    float pw2 = pen_.Width / 2f;
    Pen pen = (Pen)pen_.Clone();
    // find out directions:
    int sigX = Math.Sign(pb_.X - pa_.X);
    int sigY = Math.Sign(pb_.Y - pa_.Y);
    // move the end points out a bit:
    PointF pa = new PointF(pa_.X - pw2 * sigX, pa_.Y - pw2 * sigY);
    PointF pb = new PointF(pb_.X + pw2 * sigX, pb_.Y + pw2 * sigY);
    // find line new length:
    float lw = (float)(Math.Abs(pb.X - pa.X));
    float lh = (float)(Math.Abs(pb.Y - pa.Y));
    float ll = (float)(Math.Sqrt(lw * lw + lh * lh));
    // dot length:
    float dl = ll / pw;
    // dot+gap count: round to nearest odd int:
    int dc = (int)(2 * Math.Round((dl + 1) / 2) - 1);
    //  gap count:
    int gc = dc / 2 ;
    // dot count:
    dc = gc + 1;
    // gap scaling
    float gs = (ll - dc * pw) / (pw * gc);
    // our custom dashpattern 
    pen.DashPattern = new float[] { 1, gs };
    // maybe skip 1st and/or last dots:
    if (skipDots % 2 == 1) pa = new PointF(pa_.X + pw * sigX, pa_.Y + pw * sigY);
    if (skipDots > 1) pb = new PointF(pb_.X - pw * sigX, pb_.Y - pw * sigY);
    // finally we can draw the line:
    g.DrawLine(pen, pa, pb);
    // dispose of pen clone
    pen.Dispose();
}

经过一些明显的准备后,我将点移出一点,然后计算垂直或水平线的点数和间隙数。然后我计算修改后的差距刻度。

结果,按比例放大了4倍,绘制了四条线,形成一个宽度不等的宽度为1/3 - 10/3的矩形:

enter image description here

这是我用过的试验台;请注意使用半透明黑色来说明角落是如何正确绘制的,即不重叠:

Pen Dot = new Pen(Color.Black, 1f);
Point pa = new Point(10, 50);
Point pb = new Point(70, 50);
Point pc = new Point(70, 100);
Point pd = new Point(10, 100);

for (int i = 1; i < 10; i++ )
{
    Dot = new Pen(Color.FromArgb(128, Color.Black), i / 3f){ DashStyle = DashStyle.Dot };
    g.TranslateTransform(10, 10);
    DrawDottedLine(g, Dot, pa, pb, 2);
    DrawDottedLine(g, Dot, pb, pc, 2);
    DrawDottedLine(g, Dot, pc, pd, 2);
    DrawDottedLine(g, Dot, pd, pa, 2);
    DrawDottedLine(g, Dot, pd, pb, 3);
}

我真的希望通过简单地使用DrawLines来避免连接问题,但是这不起作用,在弄清楚这个解决方案之后,我并不感到惊讶它没有...

答案 1 :(得分:1)

offset line by width of line

您提供的坐标是指到结果行的左上角位置。因此,当您绘制两个像素宽的线时,您应该计算您的开始点和终点以包括该线的宽度。

在这种情况下,您可能希望将垂直线略微偏移到左侧和顶部(确切地说是borderwith)。

因此,通过添加(或减去)等于线宽的偏移量,结果如下:

protected override void OnPaint (PaintEventArgs e) {
   Graphics g = e.Graphics;
   g.DrawLine (Dot, new Point (10, 50), new Point (70, 50));
   g.DrawLine (Dot, new Point (69, 49), new Point (69, 100));
}

Pen Dot = new Pen (Brushes.Black, 2) { DashStyle = DashStyle.Dot };