You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# Windows Forms中实现风羽的技术方案咨询

解决C# Windows Forms中风羽绘制的定位与旋转问题

问题描述

在C# Windows Forms项目中需要绘制/展示风羽,尝试使用ESRI Weather字体时遇到定位不准确和旋转混乱的问题,期望实现风羽末端固定在交叉点且不受角度调整影响。同时考虑过SVG图片方案,但未找到合适的免费资源,附上相关代码寻求解决思路。

用户提供的测试代码:

int _rotcount = 0;
int _rotlog = 80;

private void button2_Click(object sender, EventArgs e)
{
    Font fn = new Font("ESRI Weather", 40,FontStyle.Bold);
    string text = ((char)0227).ToString();
    Graphics gr = PnlBase.CreateGraphics();
    GraphicsState st = gr.Save();
    gr.ResetTransform();

    if (_rotcount > 0)
    {
        _rotlog += 10;
        gr.RotateTransform(270);
        _rotcount++;
    }
    else
    {
        _rotlog += 10;
        gr.RotateTransform(180);
        _rotcount++;
    }

    gr.TranslateTransform((float)55, (float)50,MatrixOrder.Append);

    Pen blackPen = new Pen(Color.Black, 3);

    using (StringFormat sf = new StringFormat())
    {
        sf.Alignment = StringAlignment.Center;
        sf.LineAlignment = StringAlignment.Center;
        gr.DrawString(text, fn, new SolidBrush(Color.Black), 0, 0, sf);
    }

    gr.Restore(st);
}

解决方案

一、修复ESRI Weather字体的旋转与定位问题

原代码核心问题是变换顺序错误:先旋转再平移会导致锚点偏移。正确逻辑是先平移到目标交叉点,再围绕风羽末端旋转,最后调整文本位置确保末端固定。

修改后的代码:

int _rotcount = 0;
int currentAngle = 180; // 初始角度,需根据ESRI字体的风向定义调整

private void button2_Click(object sender, EventArgs e)
{
    using (Font fn = new Font("ESRI Weather", 40, FontStyle.Bold))
    using (Graphics gr = PnlBase.CreateGraphics())
    using (SolidBrush brush = new SolidBrush(Color.Black))
    {
        gr.Clear(PnlBase.BackColor); // 清除画布避免重叠
        string text = ((char)0227).ToString();

        // 计算文本尺寸,确定风羽末端(交叉点)的锚点位置
        SizeF textSize = gr.MeasureString(text, fn);
        PointF anchor = new PointF(textSize.Width / 2, textSize.Height); // 锚点设为文本中心底部,可根据字体微调

        GraphicsState st = gr.Save();
        try
        {
            // 1. 平移到目标交叉点(55,50)
            gr.TranslateTransform(55, 50);
            // 2. 围绕锚点旋转角度
            gr.RotateTransform(currentAngle, MatrixOrder.Append);
            // 3. 反向平移,让风羽末端刚好落在交叉点
            gr.TranslateTransform(-anchor.X, -anchor.Y, MatrixOrder.Append);

            // 绘制风羽,用左上角对齐避免偏移
            using (StringFormat sf = new StringFormat())
            {
                sf.Alignment = StringAlignment.Near;
                sf.LineAlignment = StringAlignment.Near;
                gr.DrawString(text, fn, brush, 0, 0, sf);
            }

            // 绘制红色标记验证交叉点位置
            using (Pen pen = new Pen(Color.Red, 3))
            {
                gr.ResetTransform();
                gr.DrawEllipse(pen, 55 - 3, 50 - 3, 6, 6);
            }
        }
        finally
        {
            gr.Restore(st);
        }

        // 更新角度,循环360度
        currentAngle += 10;
        if (currentAngle >= 360) currentAngle = 0;
        _rotcount++;
    }
}

二、自定义绘制风羽(最可控方案)

如果字体依赖不稳定,直接用GDI+绘制风羽是最优解,完全自定义形状、锚点和旋转逻辑,无需外部资源:

private void DrawWindBarb(Graphics gr, PointF fixedAnchor, float windAngle, float windSpeed)
{
    gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
    using (Pen pen = new Pen(Color.Black, 2))
    {
        GraphicsState st = gr.Save();
        try
        {
            // 平移到固定交叉点,再旋转到风向角度
            gr.TranslateTransform(fixedAnchor.X, fixedAnchor.Y);
            gr.RotateTransform(windAngle);

            // 绘制风杆(从交叉点向外延伸)
            const float shaftLength = 50;
            gr.DrawLine(pen, 0, 0, shaftLength, 0);

            // 根据风速绘制风羽:长羽=10节,短羽=5节
            const float barbLength = 15;
            const float barbSpacing = 5;
            int fullBarbs = (int)(windSpeed / 10);
            int halfBarbs = (int)((windSpeed % 10) / 5);

            // 绘制长羽
            for (int i = 0; i < fullBarbs; i++)
            {
                float yPos = -barbSpacing * (i + 1);
                gr.DrawLine(pen, shaftLength - barbLength, yPos, shaftLength, 0);
            }
            // 绘制短羽
            for (int i = 0; i < halfBarbs; i++)
            {
                float yPos = -barbSpacing * (fullBarbs + i + 1);
                gr.DrawLine(pen, shaftLength - barbLength / 2, yPos, shaftLength, 0);
            }
        }
        finally
        {
            gr.Restore(st);
        }
    }
}

// 使用示例
private void button2_Click(object sender, EventArgs e)
{
    static int currentAngle = 180;
    using (Graphics gr = PnlBase.CreateGraphics())
    {
        gr.Clear(PnlBase.BackColor);
        // 固定交叉点在(55,50),风速25节,角度每次加10度
        DrawWindBarb(gr, new PointF(55, 50), currentAngle, 25);
        
        currentAngle += 10;
        if (currentAngle >= 360) currentAngle = 0;
    }
}

该方案优势:

  • 交叉点100%固定,旋转无偏移
  • 可直接根据风速动态生成对应风羽
  • 无需依赖任何外部资源,兼容性强

三、免费SVG风羽资源方案

若坚持使用SVG:

  • 用免费矢量工具(如Inkscape)自行绘制:将交叉点设为SVG原点,导出后用SvgImageBox等控件加载,旋转时围绕原点即可固定位置
  • 搜索开源气象项目:部分GitHub气象可视化仓库会提供免费风羽SVG资源
  • 免费矢量图库:筛选"wind barb"关键词,选择可商用的SVG文件

内容的提问来源于stack exchange,提问作者saeed gholampoor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:35:01