编辑
2025-02-03
C# 应用
00
请注意,本文编写于 92 天前,最后修改于 90 天前,其中某些信息可能已经过时。

目录

应用场景
完整代码实现
极坐标系控件类
实际应用示例
雷达扫描效果
总结

极坐标系统在许多工程应用中具有重要作用,特别是在处理圆周运动、角度定位和雷达显示等场景。本文将详细介绍如何在C# WinForm中实现一个完整的极坐标系统,并展示其在实际应用中的使用场景。

应用场景

  1. 工业自动化
    • 旋转工作台位置控制
    • 圆周加工轨迹规划
    • 机械臂角度控制
  2. 测量与检测
    • 雷达扫描显示
    • 声纳信号显示
    • 角度传感器数据可视化
  3. 科学计算
    • 极坐标函数绘制
    • 圆周运动分析
    • 扇形统计图表

完整代码实现

极坐标系控件类

C#
using System; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; public class PolarCoordinateControl : Control { private float scale = 1.0f; // 缩放比例 private Point origin; // 原点位置 private Point mouseDownLocation; // 鼠标按下位置 private bool isPanning = false; // 是否正在平移 private const int CIRCLE_COUNT = 5; // 同心圆数量 private const int ANGLE_STEP = 15; // 角度刻度步进(度) public PolarCoordinateControl() { // 设置控件属性 this.DoubleBuffered = true; this.BackColor = Color.White; this.Resize += PolarCoordinateControl_Resize; this.MouseDown += PolarCoordinateControl_MouseDown; this.MouseMove += PolarCoordinateControl_MouseMove; this.MouseUp += PolarCoordinateControl_MouseUp; this.MouseWheel += PolarCoordinateControl_MouseWheel; // 初始化原点位置 UpdateOrigin(); } private void UpdateOrigin() { origin = new Point(Width / 2, Height / 2); } private void PolarCoordinateControl_Resize(object sender, EventArgs e) { UpdateOrigin(); Invalidate(); } private void PolarCoordinateControl_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { isPanning = true; mouseDownLocation = e.Location; Cursor = Cursors.Hand; } } private void PolarCoordinateControl_MouseMove(object sender, MouseEventArgs e) { if (isPanning) { int dx = e.X - mouseDownLocation.X; int dy = e.Y - mouseDownLocation.Y; origin.X += dx; origin.Y += dy; mouseDownLocation = e.Location; Invalidate(); } } private void PolarCoordinateControl_MouseUp(object sender, MouseEventArgs e) { isPanning = false; Cursor = Cursors.Default; } private void PolarCoordinateControl_MouseWheel(object sender, MouseEventArgs e) { float oldScale = scale; if (e.Delta > 0) scale *= 1.1f; else scale /= 1.1f; // 限制缩放范围 scale = Math.Max(0.1f, Math.Min(5.0f, scale)); Invalidate(); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 绘制同心圆 DrawConcentricCircles(g); // 绘制角度线 DrawAngleLines(g); // 绘制刻度 DrawScale(g); } private void DrawConcentricCircles(Graphics g) { using (Pen circlePen = new Pen(Color.LightGray, 1)) { int baseRadius = Math.Min(Width, Height) / 3; float radiusStep = baseRadius / CIRCLE_COUNT; for (int i = 1; i <= CIRCLE_COUNT; i++) { float radius = i * radiusStep * scale; g.DrawEllipse(circlePen, origin.X - radius, origin.Y - radius, radius * 2, radius * 2); // 绘制半径值 using (Font font = new Font("Arial", 8)) using (Brush brush = new SolidBrush(Color.Black)) { string radiusText = (i * radiusStep).ToString("F1"); g.DrawString(radiusText, font, brush, origin.X + 5, origin.Y - radius); } } } } private void DrawAngleLines(Graphics g) { using (Pen anglePen = new Pen(Color.LightGray, 1)) { int radius = (int)(Math.Min(Width, Height) / 2 * scale); for (int angle = 0; angle < 360; angle += ANGLE_STEP) { double radians = angle * Math.PI / 180; Point endPoint = new Point( (int)(origin.X + radius * Math.Cos(radians)), (int)(origin.Y + radius * Math.Sin(radians)) ); g.DrawLine(anglePen, origin, endPoint); // 绘制角度值 if (angle % 30 == 0) { using (Font font = new Font("Arial", 8)) using (Brush brush = new SolidBrush(Color.Black)) { Point textPoint = new Point( (int)(origin.X + (radius + 20) * Math.Cos(radians)), (int)(origin.Y + (radius + 20) * Math.Sin(radians)) ); g.DrawString(angle.ToString() + "°", font, brush, textPoint); } } } } } private void DrawScale(Graphics g) { // 绘制坐标轴 using (Pen axisPen = new Pen(Color.Black, 2)) { int radius = (int)(Math.Min(Width, Height) / 2 * scale); // X轴 g.DrawLine(axisPen, origin.X - radius, origin.Y, origin.X + radius, origin.Y); // Y轴 g.DrawLine(axisPen, origin.X, origin.Y - radius, origin.X, origin.Y + radius); } } // 坐标转换:极坐标到笛卡尔坐标 public PointF PolarToCartesian(float r, float theta) { float x = r * (float)Math.Cos(theta); float y = r * (float)Math.Sin(theta); return new PointF(x, y); } // 坐标转换:笛卡尔坐标到极坐标 public (float r, float theta) CartesianToPolar(float x, float y) { float r = (float)Math.Sqrt(x * x + y * y); float theta = (float)Math.Atan2(y, x); return (r, theta); } // 屏幕坐标到极坐标 public (float r, float theta) ScreenToPolar(Point screenPoint) { float x = (screenPoint.X - origin.X) / scale; float y = (screenPoint.Y - origin.Y) / scale; return CartesianToPolar(x, y); } // 极坐标到屏幕坐标 public Point PolarToScreen(float r, float theta) { PointF cartesian = PolarToCartesian(r, theta); return new Point( (int)(origin.X + cartesian.X * scale), (int)(origin.Y + cartesian.Y * scale) ); } }

image.png

实际应用示例

雷达扫描效果

C#
using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.Tasks; using Timer = System.Windows.Forms.Timer; namespace AppMotionControl { public class PolarCoordinateControl : Control { private float scale = 1.0f; // 缩放比例 private Point origin; // 原点位置 private Point mouseDownLocation; // 鼠标按下位置 private bool isPanning = false; // 是否正在平移 private const int CIRCLE_COUNT = 5; // 同心圆数量 private const int ANGLE_STEP = 15; // 角度刻度步进(度) private Timer _scanTimer; private float _scanAngle = 0f; // 扫描线当前角度 public bool isRadarEnabled {get;set;}=false; // 雷达扫描开关 public PolarCoordinateControl() { // 设置控件属性 this.DoubleBuffered = true; this.BackColor = Color.White; this.Resize += PolarCoordinateControl_Resize; this.MouseDown += PolarCoordinateControl_MouseDown; this.MouseMove += PolarCoordinateControl_MouseMove; this.MouseUp += PolarCoordinateControl_MouseUp; this.MouseWheel += PolarCoordinateControl_MouseWheel; // 初始化原点位置 UpdateOrigin(); _scanTimer = new Timer(); _scanTimer.Interval = 50; // 扫描速度,可根据需要调节 _scanTimer.Tick += (s, e) => { // 让扫描角度在 0~360 度间循环 _scanAngle = (_scanAngle + 5) % 360f; Invalidate(); }; _scanTimer.Start(); } private void UpdateOrigin() { origin = new Point(Width / 2, Height / 2); } private void PolarCoordinateControl_Resize(object sender, EventArgs e) { UpdateOrigin(); Invalidate(); } private void PolarCoordinateControl_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { isPanning = true; mouseDownLocation = e.Location; Cursor = Cursors.Hand; } } private void PolarCoordinateControl_MouseMove(object sender, MouseEventArgs e) { if (isPanning) { int dx = e.X - mouseDownLocation.X; int dy = e.Y - mouseDownLocation.Y; origin.X += dx; origin.Y += dy; mouseDownLocation = e.Location; Invalidate(); } } private void PolarCoordinateControl_MouseUp(object sender, MouseEventArgs e) { isPanning = false; Cursor = Cursors.Default; } private void PolarCoordinateControl_MouseWheel(object sender, MouseEventArgs e) { float oldScale = scale; if (e.Delta > 0) scale *= 1.1f; else scale /= 1.1f; // 限制缩放范围 scale = Math.Max(0.1f, Math.Min(5.0f, scale)); Invalidate(); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 绘制同心圆 DrawConcentricCircles(g); // 绘制角度线 DrawAngleLines(g); // 绘制刻度 DrawScale(g); if (isRadarEnabled) { DrawRadarScan(g); } } private void DrawRadarScan(Graphics g) { int radius = (int)(Math.Min(Width, Height) / 2 * scale); double radians = _scanAngle * Math.PI / 180; // 绘制扫描扇形 using (LinearGradientBrush brush = new LinearGradientBrush( new Point(origin.X, origin.Y), new Point( (int)(origin.X + radius * Math.Cos(radians)), (int)(origin.Y + radius * Math.Sin(radians)) ), Color.FromArgb(80, 0, 255, 0), // 半透明绿色 Color.FromArgb(0, 0, 255, 0))) // 完全透明 { g.FillPie(brush, origin.X - radius, origin.Y - radius, radius * 2, radius * 2, _scanAngle - 30, 30); } // 绘制扫描线 using (Pen scanPen = new Pen(Color.Green, 2)) { Point endPoint = new Point( (int)(origin.X + radius * Math.Cos(radians)), (int)(origin.Y + radius * Math.Sin(radians)) ); g.DrawLine(scanPen, origin, endPoint); } } private void DrawConcentricCircles(Graphics g) { using (Pen circlePen = new Pen(Color.LightGray, 1)) { int baseRadius = Math.Min(Width, Height) / 3; float radiusStep = baseRadius / CIRCLE_COUNT; for (int i = 1; i <= CIRCLE_COUNT; i++) { float radius = i * radiusStep * scale; g.DrawEllipse(circlePen, origin.X - radius, origin.Y - radius, radius * 2, radius * 2); // 绘制半径值 using (Font font = new Font("Arial", 8)) using (Brush brush = new SolidBrush(Color.Black)) { string radiusText = (i * radiusStep).ToString("F1"); g.DrawString(radiusText, font, brush, origin.X + 5, origin.Y - radius); } } } } private void DrawAngleLines(Graphics g) { using (Pen anglePen = new Pen(Color.LightGray, 1)) { int radius = (int)(Math.Min(Width, Height) / 2 * scale); for (int angle = 0; angle < 360; angle += ANGLE_STEP) { double radians = angle * Math.PI / 180; Point endPoint = new Point( (int)(origin.X + radius * Math.Cos(radians)), (int)(origin.Y + radius * Math.Sin(radians)) ); g.DrawLine(anglePen, origin, endPoint); // 绘制角度值 if (angle % 30 == 0) { using (Font font = new Font("Arial", 8)) using (Brush brush = new SolidBrush(Color.Black)) { Point textPoint = new Point( (int)(origin.X + (radius + 20) * Math.Cos(radians)), (int)(origin.Y + (radius + 20) * Math.Sin(radians)) ); g.DrawString(angle.ToString() + "°", font, brush, textPoint); } } } } } private void DrawScale(Graphics g) { // 绘制坐标轴 using (Pen axisPen = new Pen(Color.Black, 2)) { int radius = (int)(Math.Min(Width, Height) / 2 * scale); // X轴 g.DrawLine(axisPen, origin.X - radius, origin.Y, origin.X + radius, origin.Y); // Y轴 g.DrawLine(axisPen, origin.X, origin.Y - radius, origin.X, origin.Y + radius); } } // 坐标转换:极坐标到笛卡尔坐标 public PointF PolarToCartesian(float r, float theta) { float x = r * (float)Math.Cos(theta); float y = r * (float)Math.Sin(theta); return new PointF(x, y); } // 坐标转换:笛卡尔坐标到极坐标 public (float r, float theta) CartesianToPolar(float x, float y) { float r = (float)Math.Sqrt(x * x + y * y); float theta = (float)Math.Atan2(y, x); return (r, theta); } // 屏幕坐标到极坐标 public (float r, float theta) ScreenToPolar(Point screenPoint) { float x = (screenPoint.X - origin.X) / scale; float y = (screenPoint.Y - origin.Y) / scale; return CartesianToPolar(x, y); } // 极坐标到屏幕坐标 public Point PolarToScreen(float r, float theta) { PointF cartesian = PolarToCartesian(r, theta); return new Point( (int)(origin.X + cartesian.X * scale), (int)(origin.Y + cartesian.Y * scale) ); } protected override void Dispose(bool disposing) { if (disposing) { _scanTimer.Stop(); _scanTimer.Dispose(); } base.Dispose(disposing); } } }

image.png

总结

本文详细介绍了在C# WinForm环境下实现极坐标系的完整方案。该实现适用于各种需要极坐标显示的场景,特别是在工业自动化、测量系统等领域。代码结构清晰,易于扩展,可以根据具体需求进行定制化开发。通过提供的功能和示例,开发者可以快速构建基于极坐标系的应用程序。

本文作者:rick

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!