在预定义的行上的文本

3
我有一条断开的线 (PointF[]),一些 stringGraphics 对象。现在我想在我的线上画出这个字符串。
以下是一个例子:

enter image description here 有没有最简单的算法可以实现这个功能?
[编辑] 好的,我已经尝试了@endofzero的代码并做了一些修改。下面是整个解决方案(包括角度和距离计算):
private static void DrawBrokenString(Graphics g, IList<PointF> line, string text, Font font)
{
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.TextRenderingHint = TextRenderingHint.AntiAlias;
    Pen pen = new Pen(Brushes.Black);

    for (int index = 0; index < line.Count - 1; index++)
    {
        float distance = GetDistance(line[index], line[index + 1]);

        if (text.Length > 0)
        {
            for (int cIndex = text.Length; cIndex >= 0; cIndex--)
            {
                SizeF textSize = g.MeasureString(text.Substring(0, cIndex).Trim(), font);
                if (textSize.Width <= distance)
                {
                    float rotation = GetAngle(line[index], line[index + 1]);

                    g.TranslateTransform(line[index].X, line[index].Y);
                    g.RotateTransform(rotation);

                    if (index != line.Count - 2)
                    {
                        g.DrawString(text.Substring(0, cIndex).Trim(), font, new SolidBrush(Color.Black),
                                 new PointF(0, -textSize.Height));
                    }
                    else
                    {
                        g.DrawString(text.Trim(), font, new SolidBrush(Color.Black),
                                 new PointF(0, -textSize.Height));
                    }

                    g.RotateTransform(-rotation);
                    g.TranslateTransform(-line[index].X, -line[index].Y);

                    text = text.Remove(0, cIndex);
                    break;
                }
            }    
        }
        else
        {
            break;
        }

        g.DrawLine(pen, line[index].X, line[index].Y, line[index + 1].X, line[index + 1].Y);
    }
}

private static float GetDistance(PointF p1, PointF p2)
{
    return (float) Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
}

private static float GetAngle(PointF p1, PointF p2)
{
    float c = (float) Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));

    if (c == 0)
        return 0;

    if (p1.X > p2.X)
        return (float) (Math.Asin((p1.Y - p2.Y)/c)*180/Math.PI - 180);

    return (float) (Math.Asin((p2.Y - p1.Y)/c)*180/Math.PI);
}

现在,我需要解决最后一个问题。我不希望字符串重叠在一起。有什么想法吗?啊,当我们无法将字符串画在路径上时(因为断点太多),它应该画在线的上方(中间顶部)。
这是不想要重叠的示例: enter image description here

2
输入的字符串是“Some text on the line”还是[“Some Text”,“on the line”]? - Robert Cooper
我得到了整个字符串,所以我必须自己拆分它,例如:"Some text on the line"。 - Nickon
1
您可以在适当的RotateTransform之后使用DrawString绘制旋转文本(更多信息请参见:http://msdn.microsoft.com/en-us/library/a0z3f662(v=vs.100).aspx),并且可以使用MeasureString方法查看字符串的宽度(http://msdn.microsoft.com/en-us/library/system.drawing.graphics.measurestring(v=vs.100).aspx)。这可以帮助您分割文本。 - BlackBear
1个回答

1

这是一个简单的示例,演示如何沿着折线绘制文本。

    private static void DrawBrokenLine(PointF[] line, string text, Graphics g, Font font)
    {
        for (int index = 0; index < line.Length - 1; index++)
        {
            float distance = distanceBetween(line[index], line[index + 1]);

            if (text.Length > 0)
            {
                for (int cIndex = text.Length; cIndex >= 0; cIndex--)
                {
                    SizeF textSize = g.MeasureString(text.Substring(0, cIndex).Trim(), font);
                    if (textSize.Width <= distance)
                    {
                        float rotation = angleBetween(line[index], line[index + 1]);

                        g.TranslateTransform(line[index].X, line[index].Y);
                        g.RotateTransform(rotation);

                        g.DrawString(text.Substring(0, cIndex).Trim(), font, new SolidBrush(Color.Black),
                            new PointF(0, -textSize.Height));

                        g.RotateTransform(-rotation);
                        g.TranslateTransform(-line[index].X, -line[index].Y);

                        text = text.Remove(0, cIndex);
                        break;
                    }
                }
            }
            else
                break;
        }
    }

如果您希望它仅在单词之间的空格处断开,那么您将不得不更改它在测量时缩短字符串的方式。
编辑:
为了解决角落上的文本重叠问题,我添加了一个计算。
    private static void DrawBrokenLine(PointF[] line, string text, Graphics g, Font font)
    {
        float lastOverlap = 0;

        for (int index = 0; index < line.Length - 1; index++)
        {
            float distance = distanceBetween(line[index], line[index + 1]);

            float angleOfLines = 180;
            if (index < line.Length - 2)
            {
                angleOfLines = angleBetweenLines(line[index], line[index + 1], line[index + 2]);
            }

            if (text.Length > 0)
            {
                SizeF textSize = g.MeasureString(text, font);
                float overlap = 0;
                if (angleOfLines < 180)
                {
                    if (angleOfLines <= 90)
                    {
                        overlap = (textSize.Height / 1.5f) / (
                            Convert.ToSingle(Math.Tan((angleOfLines / 2) * Math.PI / 180))
                            );
                    }
                    else
                    {
                        overlap = Convert.ToSingle(
                            Math.Sin(angleOfLines * Math.PI / 180) * (textSize.Height / 1.5f));
                    }
                }

                for (int cIndex = text.Length; cIndex >= 0; cIndex--)
                {
                    textSize = g.MeasureString(text.Substring(0, cIndex).Trim(), font);

                    if (textSize.Width <= distance - overlap - lastOverlap)   //notice the subtraction of overlap
                    {
                        float rotation = angleBetween(line[index], line[index + 1]);

                        g.TranslateTransform(line[index].X, line[index].Y);
                        g.RotateTransform(rotation);

                        g.DrawString(text.Substring(0, cIndex).Trim(), font, new SolidBrush(Color.Black),
                            new PointF(lastOverlap, -textSize.Height));

                        g.RotateTransform(-rotation);
                        g.TranslateTransform(-line[index].X, -line[index].Y);

                        text = text.Remove(0, cIndex);
                        break;
                    }
                }

                lastOverlap = overlap;
            }
            else
                break;
        }
    }

    private static float angleBetweenLines(PointF A, PointF B, PointF C)
    {
        float angle1 = 360 - angleBetween(A, B);
        float angle2 = 360 - angleBetween(B, C);

        if (angle1 < 0)
            angle1 += 360;
        if (angle2 < 0)
            angle2 += 360;

        float delta = 180 + angle1 - angle2;

        if (delta < 0)
            delta += 360;
        if (delta > 360)
            delta -= 360;

        return delta;
    }

enter image description here

我的示例有一个警告:如果你注意到,我将文本高度除以了一个特殊的数字。我在使用MeasureString时遇到了问题,它没有返回准确的文本高度,因此我试图手动纠正这个问题。

关于在线条上方绘制字符串(因为换行太多),这取决于您如何检测到这种情况(什么构成了太多,文本应该放在哪里,是否应该倾斜?)。也许,如果您可以提供一个您所想象的这种情况的示例图像,那将有助于澄清。


谢谢,它有效:) 我几乎自己完成了,但由于矩阵变换不好导致出现了X-Y偏移:P(我的错)。但是你的解决方案很好:) 请你能检查一下我的编辑吗? - Nickon
1
@Nickon 我更新了关于文本重叠的答案。 - endofzero
最后一件事:如果有一条包含数百个2-3像素子线的断线怎么办?有没有什么方法可以修改它?或者我应该尝试使用某种算法?:S - Nickon
1
你可以尝试使用MeasureString和较小的字体,直到它适合为止,但如果它们只有2-3个像素,则可能无法使字符足够小。你也可以尝试检测何时出现多个这些小行,捕获距离变量多个迭代,然后通过注意最高点在哪里,并在其上方绘制文本来进行绘制“以上”的线条。 - endofzero
是的,我已经纠正了它,但问题是,我不知道如何规范化角度(有些情况下,字母旋转得很糟糕)。目前,我获取“过度迭代”子行的第一个和最后一个点。然后,我根据这两个点之间的线倾斜它。效果还不错,但仍然存在一些情况... :P - Nickon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接