winforms 在反向Y轴上绘制文本

vc9ivgsu  于 2023-04-07  发布在  其他
关注(0)|答案(3)|浏览(95)

我正在编写一个可视化应用程序:它从XML中加载建筑物的几何形状,并将其绘制在屏幕上。建筑物由矩形房间组成,其中许多-所以我想在上面绘制他们的名字。
我使用this教程翻转Y轴在我的形式,因为建筑数据存储在笛卡尔坐标。并将它们转换为经典的Windows“y向下生长”系统,而绘图看起来很奇怪。
我还需要缩放和翻译我的“场景”到左下角。
而且,我最后的痛苦,我需要再次翻转我的文字-因为它也会翻转!
正如tutorial所说,我需要:
1.翻转Y轴,缩放并将场景移动到所需位置
1.在笛卡尔坐标系中绘制建筑物几何图形(只是矩形)
1.回到“Y向下生长”的系统,规模再移动
1.在这个“经典”系统中绘制文本
但文本的坐标无效!
从正确的位置向下移动:(
所以这是我的问题-如何计算新的文本坐标在Windows窗体对象 * 正确 *。

void VisualizerForm_Paint(object sender, PaintEventArgs e)
{
    // Setup graphics output settings
    var g = e.Graphics;
    g.Clear(Color.White);
    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
    g.PageUnit = GraphicsUnit.Pixel;
    
    // Move coordinate system center to the bottom left corner,
    // scale it to user-defined scale value and flip Y axis (mul it scale to -1)
    g.ScaleTransform(m_scale, -m_scale, MatrixOrder.Append);
    g.TranslateTransform(50, Height - 50, MatrixOrder.Append);
    
    // ... Draw some complex building geometry ...
    
    // Draw building room
    var customPen = new Pen(Color.Black, 1.0f / g.DpiX);
    var rect = new RectangleF(box.X1, box.Y1, (box.X2 - box.X1), (box.Y2 - box.Y1));
    g.DrawRectangle(customPen, box.X1, box.Y1, (box.X2 - box.X1), (box.Y2 - box.Y1));

    GraphicsState gs = g.Save();
    
    // First reset transform matrix
    g.ResetTransform();
    // Then again scale and move scene, but now with classic down-incresed Y axis
    g.ScaleTransform(m_scale, m_scale, MatrixOrder.Append);
    g.TranslateTransform(50, Height - 50, MatrixOrder.Append);
    
    // All Y coords now must be inverted :/ *sigh*
    box.Y1 *= -1.0f;
    box.Y2 *= -1.0f;
    rect = new RectangleF(box.X1, box.Y1, Math.Abs(box.X2 - box.X1), Math.Abs(box.Y2 - box.Y1));

    // FIXME: This text is drawing in incorrect place
    var fnt = new Font("Arial", 40f / g.DpiX, FontStyle.Bold, GraphicsUnit.Pixel);
    g.DrawString("ID: " + box.Id, fnt, Brushes.Black, rect, stringFormat);

    g.Restore(gs);
}
qqrboqgw

qqrboqgw1#

试试这个:

void DrawDigonalString(Graphics G, string S, Font F, Brush B, PointF P, int Angle)
{
    SizeF MySize = G.MeasureString(S, F);
    G.TranslateTransform(P.X + MySize.Width / 2, P.Y + MySize.Height / 2);
    G.RotateTransform(Angle);
    G.DrawString(S, F, B, new PointF(-MySize.Width / 2, -MySize.Height / 2));
    G.RotateTransform(-Angle);
    G.TranslateTransform(-P.X - MySize.Width / 2, -P.Y- MySize.Height / 2);
}
1aaf6o9v

1aaf6o9v2#

终于拿到了!
下面的类在其中心内Y反转的场景中绘制具有指定文本的矩形。
文本自动缩放以适应实际矩形大小。

class RectangleWithText
{
    RectangleF m_extent = new RectangleF();
    string m_text = "";

    Font m_textFont = null;
    RectangleF m_textRect = new RectangleF();

    public RectangleWithText( RectangleF extent, string text )
    {
        m_extent = extent;
        m_text = text;
    }

    public void Draw( Graphics g )
    {
        var dashedGrayPen = new Pen( Color.Gray, 1.0f / g.DpiX ) { DashStyle = DashStyle.Dash };
        var brownPen = new Pen( Color.Brown, 1.0f / g.DpiX );

        // Draw rectangle itself
        g.DrawRectangle( brownPen, m_extent.X, m_extent.Y, m_extent.Width, m_extent.Height );

        // Draw text on it
        var extentCenter = new PointF( ( m_extent.Left + m_extent.Right ) / 2, ( m_extent.Bottom + m_extent.Top ) / 2 );
        DrawText( g, m_text, extentCenter, m_extent );

        }
    }

    private void DrawText( Graphics g, string text, PointF ptStart, RectangleF extent )
    {
        var gs = g.Save();

        // Inverse Y axis again - now it grow down;
        // if we don't do this, text will be drawn inverted
        g.ScaleTransform( 1.0f, -1.0f, MatrixOrder.Prepend );

        if ( m_textFont == null )
        {
            // Find the maximum appropriate text size to fix the extent
            float fontSize = 100.0f;
            Font fnt;
            SizeF textSize;
            do
            {
                fnt = new Font( "Arial", fontSize / g.DpiX, FontStyle.Bold, GraphicsUnit.Pixel );
                textSize = g.MeasureString( text, fnt );
                m_textRect = new RectangleF( new PointF( ptStart.X - textSize.Width / 2.0f, -ptStart.Y - textSize.Height / 2.0f ), textSize );

                var textRectInv = new RectangleF( m_textRect.X, -m_textRect.Y, m_textRect.Width, m_textRect.Height );
                if ( extent.Contains( textRectInv ) )
                    break;

                fontSize -= 1.0f;
                if ( fontSize <= 0 )
                {
                    fontSize = 1.0f;
                    break;
                }
            } while ( true );

            m_textFont = fnt;
        }

        // Create a StringFormat object with the each line of text, and the block of text centered on the page
        var stringFormat = new StringFormat()
        {
            Alignment = StringAlignment.Center,
            LineAlignment = StringAlignment.Center
        };
        g.DrawString( text, m_textFont, Brushes.Black, m_textRect, stringFormat );

        g.Restore( gs );
    }
}
sy5wg1nm

sy5wg1nm3#

我最近遇到了这个线程时,我想做完全相同的事情作为操作。这是令人沮丧的尝试和拼凑一切在一起,特别是因为上面的例子,其中划分字体大小的DPI有点人为,特别是在较新的屏幕上具有较高的DPI。
因此,我创建了自己的示例,并粘贴在下面。('ResizeEnd'处理程序不是真的必要的,但它是一种通过稍微调整Form 1窗口大小来重新运行程序的方便方法)。然后用下面的代码替换Paint和resize处理程序的内容,并复制粘贴两个辅助函数的ConvertRectangleToPointFArray()'和'TextInsideRectangle()'添加到项目中。如所编写的,示例程序将在窗体底部附近绘制一个坐标系,并正确标记“X”和“Y”轴(这要求在绘制'Y'字符串之前将其反转。如果'verbose' bool变量设置为'true',一些诊断信息将被写入输出窗口。2请享用!

private void Form1_Paint(object sender, PaintEventArgs e)
  {
    //Step1: Change from Y-down to Y-up increasing using
    Graphics g = e.Graphics;

    g.PageUnit = GraphicsUnit.Millimeter; //  04/02/23 Changed Page units from pix to mm

    g.ScaleTransform(1.0f, -1.0f);//flip y
    Rectangle clientrect = this.ClientRectangle; //this is in pixels, NOT mm!

    PointF[] rectpoints = {new PointF(clientrect.X, clientrect.Y),new PointF(clientrect.X, clientrect.Bottom)};

    PointF[] ptf2 = { new PointF(clientrect.X, clientrect.Bottom) }; //have to have an array to use g.TransformPoints()
    g.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, ptf2);

    g.TranslateTransform(ptf2[0].X, -ptf2[0].Y); //move origin to bottom-left corner
    g.TranslateTransform(20, 20);//move origin to right 20 and up 20 mm

    //Step2: draw coordinate system lines in new coordinate system
    g.DrawLine(new Pen(Color.Black, 1), 0, 0, 100, 0);
    g.DrawLine(new Pen(Color.Black, 1), 0, 0, 0, 100);

    //Step3: Save the transforms with g.Save()
    GraphicsState transState = g.Save(); //do this so can restore later

    //"Y" string.
    Rectangle Yrect = new Rectangle(-5,100, 10, 10); //this is in mm with origin at (20,20)mm from bottom left-hand corner of window
    StringFormat YdrawFormat = new StringFormat();
    YdrawFormat.Alignment = StringAlignment.Center;
    YdrawFormat.LineAlignment = StringAlignment.Center;

    //"X" string.
    Rectangle Xrect = new Rectangle(100,-5, 10, 10); //this is in mm with origin at (20,20)mm from bottom left-hand corner of window
    StringFormat XdrawFormat = new StringFormat();
    XdrawFormat.Alignment = StringAlignment.Center;
    XdrawFormat.LineAlignment = StringAlignment.Center;

    //now draw desired text
    //bool IsVerbose = true;
    bool IsVerbose = false;
    TextInsideRectangle(e, Yrect, "Y", YdrawFormat, IsVerbose);
    TextInsideRectangle(e, Xrect, "X", XdrawFormat, IsVerbose);
  }
  private void TextInsideRectangle(PaintEventArgs e, Rectangle rect, string text, StringFormat fmt, bool verbose)
  {
    //Purpose:  Place and size the given text inside the given rectangle
    //Inputs:
    //  PaintEventArgs e assumed to be set for y-up, origin at bottom left, mm scaling
    //  ptf = 2-element PointF array containing (rect.X, rect.Y), (rect.Width, rect.Height) in desired coord sys
    //  rect = Rectangle object in (x,y, width,height) format
    //  text = string object containing text to be displayed
    //Procedure:
    //  Step1: Save the current transform
    //  Step2: draw the given rectangle onto the Form surface (DEBUG only)
    //  Step3: Flip y axis so text is drawn correctly
    //  Step3: Draw the given text, centered in the given rectangle
    //  Step4: Iteratively modify the font size such that the given text fits inside the given rectangle
    //  Step5: Refresh the screen and redraw

    //Step1: Save the current transform
    Graphics g = e.Graphics;
    GraphicsState transState = g.Save(); //do this so can restore later

    //Step2: draw the given rectangle onto the Form surface (for debug only)
    if (verbose)
    {
        RectangleF mm_tgt_rect = rect;
        g.DrawRectangle(new Pen(Color.DarkCyan), Rectangle.Round(mm_tgt_rect));
    }

    //Step3: Flip y axis so text is drawn correctly
    g.ScaleTransform(1.0f, -1.0f, MatrixOrder.Prepend); //origin still at bottom left of screen

    //Step3: Draw the given text, centered in the given rectangle
    PointF rect_ctr = new PointF((rect.X + rect.Width/2), -rect.Y - rect.Height/2);

    // Create font and brush.
    int fontsize = 1;
    Font drawFont = new Font("Arial", fontsize);
    SolidBrush drawBrush = new SolidBrush(Color.Black);

    //Step4: Iteratively modify the font size such that the given text fits inside the given rectangle
    SizeF textSize = g.MeasureString(text, drawFont);

    if (verbose)
    {
        System.Diagnostics.Debug.WriteLine("text size is {0} x {1}, enclosing rect size is {2}, {3} ",
          textSize.Width, textSize.Height, rect.Width, rect.Height);
    }

    while (textSize.Width <= rect.Width && textSize.Height <= rect.Height)
    {
        if (verbose)
        {
          System.Diagnostics.Debug.WriteLine("text size of {0} x {1} is smaller than rect", textSize.Width, textSize.Height);
        }
        fontsize++;
        drawFont = new Font("Arial", fontsize);
        textSize = g.MeasureString(text, drawFont);
        e.Graphics.DrawString(text, drawFont, drawBrush, rect_ctr.X, rect_ctr.Y, fmt);//this is the 'inverted/reversed' text
    }

    if (verbose)
    {
        System.Diagnostics.Debug.WriteLine("text size of {0} x {1} is smaller than rect", textSize.Width, textSize.Height);
        System.Diagnostics.Debug.WriteLine("final text size = {0} x {1}: enclosing rect size is {2}, {3}",
          textSize.Width, textSize.Height, rect.Width, rect.Height);
    }

    //Step5: Refresh the screen and redraw
    e.Graphics.DrawString(text, drawFont, drawBrush, rect_ctr.X, rect_ctr.Y, fmt);
  }
  private void Form1_ResizeEnd(object sender, EventArgs e)
  {
    this.Invalidate();
  }
  private PointF[] ConvertRectangleToPointFArray(RectangleF rectF)
  {
    //Purpose: convert a Rectangle or RectangleF object into a 2-element array of PointF objects
    //Inputs:
    //  rectF = Rectangle or RectangleF object
    //Outputs:
    //  returns a 2-element array of PointF objects
    //  points[0] = PointF(rectF.X, rectF.Y)
    //  points[1] = PointF(rectF.Width,rectF.Height)

    PointF[] points = new PointF[2];
    points[0] = new PointF(rectF.X, rectF.Y);
    points[1] = new PointF(rectF.Width,rectF.Height);

    return points;
  }

相关问题