极坐标系统在许多工程应用中具有重要作用,特别是在处理圆周运动、角度定位和雷达显示等场景。本文将详细介绍如何在C# WinForm中实现一个完整的极坐标系统,并展示其在实际应用中的使用场景。
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)
);
}
}
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);
}
}
}
本文详细介绍了在C# WinForm环境下实现极坐标系的完整方案。该实现适用于各种需要极坐标显示的场景,特别是在工业自动化、测量系统等领域。代码结构清晰,易于扩展,可以根据具体需求进行定制化开发。通过提供的功能和示例,开发者可以快速构建基于极坐标系的应用程序。
本文作者:rick
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!