【问题标题】:Center text on a Polygon在多边形上居中文本
【发布时间】:2017-05-29 03:12:50
【问题描述】:

我试图在用户创建的多边形的中心放置一个标签(字符串)。我检查了 Graphics 类的可用方法,但只找到了 DrawString 方法,它采用字符串、字体和矩形。问题是我使用的是多边形。并且没有将字符串作为参数的 DrawPolygon 重载。

// Get the highest and lowest of both axis to 
// determine the width and height of the polygon
int _lowestX = _slot.Min(o => o.X);
int _highestX = _slot.Max(o => o.X);
int _lowestY = _slot.Min(o => o.Y);
int _highestY = _slot.Max(o => o.Y);

// Draw the polygon
e.Graphics.FillPolygon(Brushes.White, _slot.ToArray()); // _slot is a list of points
e.Graphics.DrawPolygon(Pens.Blue, _slot.ToArray());

Font _font = new Font(FontFamily.GenericSansSerif, _highestX - _lowestX, FontStyle.Regular);
SizeF _textSize = e.Graphics.MeasureString("Slot 1", _font);

// My attempt at drawing the text using the DrawString method 
// by trying to mock a rectangle using the height and width of the polygon
e.Graphics.DrawString("Slot 1", _font, new SolidBrush(Color.Black), (_highestX - _lowestX - _textSize.Width) / 2, 0);

有什么建议吗?

提前致谢。

【问题讨论】:

  • 你想在哪里绘制你的字符串?多边形内部还是外部?...居中还是沿边缘?给我们一些关于如何计算点和/或绘制多边形的代码,然后解释或模拟你希望如何绘制字符串。移动和旋转图形表面是相当容易的,这样你的字符串就会与多边形对齐;我们只需要更多细节。
  • 嗨,我编辑了我的原始帖子以展示我到目前为止所做的事情。我希望将字符串绘制在多边形的中心,在里面。
  • 简单:放置文本,使其质心位于多边形边界框的中心。直截了当:将文本的质心放在多边形的质心。真的很难:将文本放置到所有边缘的距离一致且最大化。
  • 我要试试 Easy 版本。如何找到字符串的质心?

标签: c# winforms graphics polygon


【解决方案1】:

我试图找到多边形所有 X 和 Y 的平均值。然后画出字符串的平均值减去文本的宽度或高度除以二。到目前为止,它现在在中间显示文本。

这是我更新的代码

int _avgX = _slot.Sum(o => o.X) / _slot.Count;
int _avgY = _slot.Sum(o => o.Y) / _slot.Count;

e.Graphics.FillPolygon(Brushes.White, _slot.ToArray());
e.Graphics.DrawPolygon(Pens.Blue, _slot.ToArray());
Font _font = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular);
SizeF _textSize = e.Graphics.MeasureString("Slot 1", _font);
e.Graphics.DrawString("Slot 1", _font, new SolidBrush(Color.Black), _avgX - (_textSize.Width / 2), _avgY - (_textSize.Height / 2));

这仅适用于凸多边形。在凹多边形(例如 L 形)上,文本将像这样绘制在多边形之外。

__________
|        |
|        |
|        |
|        | Slot 1
|        |_____________
|                      |
|                      |
|______________________|

关于如何将其移动到内部的任何想法?

【讨论】:

    【解决方案2】:

    您可以这样做,但请注意,对于非常棘手的形状,将没有空间来包含文本,或者即使有空间也可能很难找到..

    您已经知道如何确定适合文本的大小。

    接下来我们需要测试一下我们的边界矩形是否包含在多边形中。

    这里有一个,它使用了GraphicsPathsRegions..:

    static bool PathContainsRect(GraphicsPath gp, RectangleF rect, Graphics g)
    {
        Region rPath0 = new Region(gp);
        Region rPath1 = new Region(gp);
        Region rRect = new Region(rect);
        rPath1.Union(rRect);
        rPath1.Exclude(rPath0);
        return rPath1.IsEmpty(g); 
    }
    

    您需要输入要用于绘图的Graphics 对象。

    接下来,您需要一些算法来找到要测试的点。最佳选择取决于您的多边形。

    这是一个简单的例子:它从中心开始,在 4 个方向上逐步移动:左上、上、右下、下。如果您想添加其他 4 个方向或省略一些方向,则应该很容易适应..:

    static Point NearestCenterLocation(GraphicsPath gp, Size sz, Graphics g, int step)
    {
        RectangleF rB = gp.GetBounds();
        Point center = new Point((int)(rB.Left + rB.Width / 2f - sz.Width / 2), 
                                 (int)(rB.Top + rB.Height /2f - sz.Height/ 2));
        Point ncTL = center;      Point ncBR = center;
        Point ncT = center;       Point ncB = center;
        RectangleF nTLRect = new RectangleF(center, sz);
        RectangleF nBRRect = new RectangleF(center, sz);
        RectangleF nTRect = new RectangleF(center, sz);
        RectangleF nBRect = new RectangleF(center, sz);
        Point hit = Point.Empty;
        do
        {
            ncTL.Offset(-step, -step);
            ncBR.Offset(step, step);
            ncT.Offset(-step, 0);
            ncB.Offset(step, 0);
            nTLRect = new RectangleF(ncTL, sz);
            nBRRect = new RectangleF(ncBR, sz);
            nTRect = new RectangleF(ncT, sz);
            nBRect = new RectangleF(ncB, sz);
            hit = (PathContainsRect(gp, nTLRect, g) && ncTL.X > 0) ? ncTL : hit;
            hit = (PathContainsRect(gp, nBRRect, g) ) ? ncBR : hit;
            hit = (PathContainsRect(gp, nTRect, g)) ? ncT : hit;
            hit = (PathContainsRect(gp, nBRect, g) ) ? ncB : hit;
    
            g.DrawRectangle(Pens.Green, Rectangle.Round(nTLRect));  //  test only
            g.DrawRectangle(Pens.Blue, Rectangle.Round(nBRRect));   //  test only
            g.DrawRectangle(Pens.Cyan, Rectangle.Round(nTRect));    //  test only
            g.DrawRectangle(Pens.Khaki, Rectangle.Round(nBRect));   //  test only
        } while (hit == Point.Empty);
        g.DrawRectangle(Pens.Tomato, new Rectangle(center, sz));   //  test only
        return hit;
    }
    

    它包含绘图调用,以显示它如何在四个方向上搜索,直到找到第一个命中。中心和结果Rectangles 为红色。

    这是创建测试台的代码:

    Size sz = new Size(70, 35);
    
    GraphicsPath gp1 = new GraphicsPath();
    gp1.FillMode = FillMode.Winding;
    gp1.AddRectangle(new Rectangle(0, 0, 350, 120));
    gp1.AddRectangle(new Rectangle(0, 0, 120, 300));
    gp1.AddRectangle(new Rectangle(250, 0, 100, 300));
    
    Point px = NearestCenterLocation(gp1, sz, g , 10);
    using (SolidBrush brush = new SolidBrush(Color.FromArgb(66, Color.Gold)))
        g.FillPath(brush, gp1);
    g.DrawRectangle(Pens.Tomato, new Rectangle(px, sz));
    

    当然,您可能只想先向下搜索,然后向上搜索,然后向左搜索,然后向右搜索,然后再或从不对角线等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-23
      • 1970-01-01
      • 2017-11-13
      • 1970-01-01
      • 2022-12-10
      • 2016-11-08
      • 2019-07-10
      相关资源
      最近更新 更多