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

目录

应用场景
完整代码实现
坐标系控件类
功能特点
实际应用示例
运动轨迹显示
实时位置更新
总结

在运动控制和图形显示领域,笛卡尔坐标系是一个非常重要的概念。本文将详细介绍如何在C# WinForm应用程序中实现一个完整的笛卡尔坐标系统,并展示其在实际应用中的使用场景。

应用场景

笛卡尔坐标系在以下场景中特别有用:

  1. 运动控制系统
    • CNC机床位置显示
    • 机器人运动轨迹规划
    • 自动化设备位置监控
  2. 数据可视化
    • 生产数据趋势图
    • 质量控制图表
    • 传感器数据实时显示
  3. 工业自动化
    • 工件加工路径显示
    • 设备运动范围监控
    • 碰撞检测可视化

完整代码实现

坐标系控件类

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppMotionControl { public class CartesianCoordinateControl : Control { private float scale = 1.0f; // 缩放比例 private Point origin; // 原点位置 private Point mouseDownLocation; // 鼠标按下位置 private bool isPanning = false; // 是否正在平移 private const int GRID_SIZE = 50; // 网格大小 public CartesianCoordinateControl() { // 设置控件属性 this.DoubleBuffered = true; this.BackColor = Color.White; this.Resize += CartesianCoordinateControl_Resize; this.MouseDown += CartesianCoordinateControl_MouseDown; this.MouseMove += CartesianCoordinateControl_MouseMove; this.MouseUp += CartesianCoordinateControl_MouseUp; this.MouseWheel += CartesianCoordinateControl_MouseWheel; // 初始化原点位置 UpdateOrigin(); } private void UpdateOrigin() { origin = new Point(Width / 2, Height / 2); } private void CartesianCoordinateControl_Resize(object sender, EventArgs e) { UpdateOrigin(); Invalidate(); } private void CartesianCoordinateControl_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { isPanning = true; mouseDownLocation = e.Location; Cursor = Cursors.Hand; } } private void CartesianCoordinateControl_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 CartesianCoordinateControl_MouseUp(object sender, MouseEventArgs e) { isPanning = false; Cursor = Cursors.Default; } private void CartesianCoordinateControl_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; // 绘制网格 DrawGrid(g); // 绘制坐标轴 DrawAxes(g); // 绘制刻度 DrawScale(g); } private void DrawGrid(Graphics g) { using (Pen gridPen = new Pen(Color.LightGray, 1)) { // 计算网格范围 int scaledGridSize = (int)(GRID_SIZE * scale); int startX = origin.X % scaledGridSize; int startY = origin.Y % scaledGridSize; // 绘制垂直线 for (int x = startX; x < Width; x += scaledGridSize) { g.DrawLine(gridPen, x, 0, x, Height); } // 绘制水平线 for (int y = startY; y < Height; y += scaledGridSize) { g.DrawLine(gridPen, 0, y, Width, y); } } } private void DrawAxes(Graphics g) { using (Pen axisPen = new Pen(Color.Black, 2)) { // X轴 g.DrawLine(axisPen, 0, origin.Y, Width, origin.Y); // Y轴 g.DrawLine(axisPen, origin.X, 0, origin.X, Height); // 绘制箭头 DrawArrow(g, new Point(Width - 10, origin.Y), true); DrawArrow(g, new Point(origin.X, 10), false); } } private void DrawArrow(Graphics g, Point point, bool isHorizontal) { using (Pen arrowPen = new Pen(Color.Black, 2)) { if (isHorizontal) { g.DrawLine(arrowPen, point.X, point.Y, point.X - 10, point.Y - 5); g.DrawLine(arrowPen, point.X, point.Y, point.X - 10, point.Y + 5); } else { g.DrawLine(arrowPen, point.X, point.Y, point.X - 5, point.Y + 10); g.DrawLine(arrowPen, point.X, point.Y, point.X + 5, point.Y + 10); } } } private void DrawScale(Graphics g) { using (Font font = new Font("Arial", 8)) using (Brush brush = new SolidBrush(Color.Black)) { // X轴刻度 int scaledGridSize = (int)(GRID_SIZE * scale); for (int x = origin.X; x < Width; x += scaledGridSize) { int value = (x - origin.X) / scaledGridSize; g.DrawString(value.ToString(), font, brush, x - 10, origin.Y + 5); } for (int x = origin.X - scaledGridSize; x > 0; x -= scaledGridSize) { int value = (x - origin.X) / scaledGridSize; g.DrawString(value.ToString(), font, brush, x - 10, origin.Y + 5); } // Y轴刻度 for (int y = origin.Y; y < Height; y += scaledGridSize) { int value = -((y - origin.Y) / scaledGridSize); g.DrawString(value.ToString(), font, brush, origin.X + 5, y - 10); } for (int y = origin.Y - scaledGridSize; y > 0; y -= scaledGridSize) { int value = -((y - origin.Y) / scaledGridSize); g.DrawString(value.ToString(), font, brush, origin.X + 5, y - 10); } } } // 坐标转换方法:实际坐标到屏幕坐标 public Point WorldToScreen(PointF worldPoint) { return new Point( (int)(origin.X + worldPoint.X * GRID_SIZE * scale), (int)(origin.Y - worldPoint.Y * GRID_SIZE * scale) ); } // 坐标转换方法:屏幕坐标到实际坐标 public PointF ScreenToWorld(Point screenPoint) { return new PointF( (screenPoint.X - origin.X) / (GRID_SIZE * scale), (origin.Y - screenPoint.Y) / (GRID_SIZE * scale) ); } } }

image.png

功能特点

  1. 交互功能
    • 鼠标拖动平移坐标系
    • 鼠标滚轮缩放
    • 自动调整网格大小
    • 实时坐标转换
  2. 显示功能
    • 坐标轴与箭头
    • 网格线
    • 刻度标注
    • 抗锯齿渲染
  3. 坐标转换
    • 世界坐标与屏幕坐标互转
    • 支持不同比例尺显示

实际应用示例

运动轨迹显示

C#
// 记录运动路径 private List<PointF> motionPath = new List<PointF>(); private Pen pathPen = new Pen(Color.Blue, 2); // 更新运动路径 public void AddPathPoint(PointF point) { motionPath.Add(point); Invalidate(); // 重绘控件 } // 清除路径 public void ClearPath() { motionPath.Clear(); Invalidate(); }

OnPaint 中调用

C#
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Graphics g = e.Graphics; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 绘制网格 DrawGrid(g); // 绘制坐标轴 DrawAxes(g); // 绘制刻度 DrawScale(g); // 绘制运动路径 DrawMotionPath(g); }
C#
// 实现DrawMotionPath方法 private void DrawMotionPath(Graphics g) { if (motionPath.Count < 2) return; // 创建点数组来存储转换后的屏幕坐标 Point[] screenPoints = new Point[motionPath.Count]; // 将世界坐标转换为屏幕坐标 for (int i = 0; i < motionPath.Count; i++) { screenPoints[i] = WorldToScreen(motionPath[i]); } // 绘制路径线段 g.DrawLines(pathPen, screenPoints); // 在路径的起点绘制一个绿色圆点 using (SolidBrush startBrush = new SolidBrush(Color.Green)) { g.FillEllipse(startBrush, screenPoints[0].X - 4, screenPoints[0].Y - 4, 8, 8); } // 在路径的终点绘制一个红色圆点 using (SolidBrush endBrush = new SolidBrush(Color.Red)) { g.FillEllipse(endBrush, screenPoints[screenPoints.Length - 1].X - 4, screenPoints[screenPoints.Length - 1].Y - 4, 8, 8); } }

image.png

实时位置更新

C#
public void UpdatePosition(PointF position) { using (Graphics g = CreateGraphics()) { Point screenPos = WorldToScreen(position); using (SolidBrush brush = new SolidBrush(Color.Red)) { g.FillEllipse(brush, screenPos.X - 5, screenPos.Y - 5, 10, 10); } } }

总结

本文详细介绍了在C# WinForm环境下实现笛卡尔坐标系的完整方案,包括核心功能实现、实际应用场景、性能优化等方面。通过这个实现,可以满足大多数工业自动化、运动控制等领域的可视化需求。代码结构清晰,易于扩展,可以根据具体需求进行定制化开发。

本文作者:rick

本文链接:

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