两端画线偏移 [英] Drawline offset at both ends
问题描述
我正在尝试使用 DrawLine
方法绘制虚线。但是,它会使端部略有偏移,从而使线条难看。
I am trying to draw dotted lines using the DrawLine
method. However, it adds a slight offset to the ends which makes the lines ugly. How can prevent adding offset to the ends?
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 };
输出
预期结果
推荐答案
目标简单而合理:
- 我们希望我们的行以点开始结束。
游戏规则也很简单:
- 默认情况下,点和间隙均为正方形。 li>
- 所有线条均使用
PenAlignment.Center
绘制。
- Both the dots and the gaps are by default squares.
- All lines are drawn with
PenAlignment.Center
.
不幸的是,合并的结果相当复杂,所以请耐心等待...
Unfortunately the consequences of the combination are rather complicated, so bear with me...
让我们首先看第一条规则;让我们忽略其他 DashStyles
并继续使用 DashStyle.Dot
。默认情况下,每个点和每个间隙的两边都有 Pen.Width
像素。这就引出了第一个问题:
Let's first look at the 1st rule; let's ignore other DashStyles
and stay with DashStyle.Dot
. By default each dot and each gap have Pen.Width
pixels for both sides. This leads right into the first issues:
- 如果行的宽度不能除以
Pen。宽度
,我们遇到了麻烦。 - 要以点开头和结尾,我们希望有
n
个点和n-1
差距。
- If the width of our lines can't be divided by
Pen.Width
we are in trouble. - To begin and end in a dot we want to have
n
dots andn-1
gaps.
还有更多,但让我们接下来看一下第二条规则;为了说明这一点,我画了这张10倍的放大图像:
There is more but let's next look into the 2nd rule; to illustrate it I drew this 10x upscaled image:
这是创建彩色部分的代码:
This is the code that created the colored parts:
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);
仔细看看线条是如何绘制的!
Look closely to see how the lines are drawn!
(另外:您可能还想看 DrawRectangle
和 FillRectangle
!)
(Aside: You may also want to watch the difference of DrawRectangle
and FillRectangle
!)
- 水平线在正确的坐标处开始和结束,但它们向下扩展(如果使用Pen的话)。宽度= 1)或大于或小于y-coodinate。
- 当然,垂直线也一样。
问题在于它们只是无法在(外部)边缘融合在一起。
The problem is that they just won't fit together at the (outer) edges.
那么我们该怎么办?我认为 DashOffset
不能帮助您。但是还有另一种方法可以调整 Pen
:我们可以将其 DashPattern
设置为使用自定义值
So what can we do? I don't think a DashOffset
can help. But there is another option to tweak a Pen
: We can set its DashPattern
to use custom values.
我们需要的值是两个 floats
,其中包含 scaling 点和间隙。默认情况下,两个值均为 1f
。我决定使点保持正方形,仅修改间隙。这是一个可以通过
The values we need are two floats
, containing the scaling for the dots and the gaps. By default both values are 1f
. I decided to keep the dots square and modify only the gaps. Here is a function which solves the issue by
- 将线宽两侧的笔宽扩大一半以使外边缘相遇的方法来解决该问题> li>
- 根据需要扩大间距以适合线长
- expanding the line width by half a pen width on both sides so the outer edges meet
- expanding the gaps as needed to fit in the line length
这里是线条绘制功能;它需要一个 Graphics
对象,一个 Pen
对象,两个末端的 Points
和 byte
来告诉我们该行是应该独立运行还是将连接连接到其他行,如本例所示。
Here is the line drawing function; it takes the Graphics
object, a Pen
, the two end Points
and a byte
that tells us if the line is meant to stand alone or will connect to other lines, as in our example cases.
要建立良好的连接,使其与半透明画笔一起使用,我们需要能够跳过在开头或结尾或什至两者都加一个点a,例如当我们想在下面的测试中插入一条对角线时。
To make a good connection, that will work well with semi-tranparent brushes we need the ability to skip a dot a at the beginning or end or even both, e.g. when we want to insert a diaogonal line as in my test below.
跳过值是 0
以跳过任何一条, 1或2
跳过第一个或最后一个点,而 3
跳过两个点。当然,您可能想使用枚举
。
The skip values are 0
to skip none, 1 or 2
to skip the 1st or last dot and 3
to skip both. Of course you may want to use an enumeration
instead.
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();
}
经过一些明显的准备后,我将分数略微移开,然后计算出数字点和垂直线或水平线的间隙。然后我计算出修改后的间隙比例。
After some obvious preparations I move the points out a bit and then calculate the number of dots and of gaps for vertical or horizontal lines. Then I calculate the modified the gap scale.
这里是放大4倍的结果,它绘制了4条线以形成一个矩形,矩形的笔宽从<$ c开始$ c> 1/3-10/3 :
Here is the result, scaled up 4x, of drawing four lines to form a rectangle with varying pen widths going from 1/3 - 10/3
:
这是我使用的测试床;请注意使用半透明的黑色来说明如何正确绘制角,即不重叠:
This is the testbed I used; note the use of semi-transparent black to illustrate how the corners are drawn correctly, i.e. non-overlapping:
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);
}
我真的希望有人可以通过使用<$ c $来避免连接问题c> DrawLines ,但这没用,弄清楚这个解决方案后,我并不感到惊讶。
I really wish one could avoid the connection problems by simply using DrawLines
, but this didn't work and after figuring out this solution I'm not really amazed it didn't..
这篇关于两端画线偏移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!