在运动控制和图形显示领域,笛卡尔坐标系是一个非常重要的概念。本文将详细介绍如何在C# WinForm应用程序中实现一个完整的笛卡尔坐标系统,并展示其在实际应用中的使用场景。
笛卡尔坐标系在以下场景中特别有用:
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)
);
}
}
}
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);
}
}
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 许可协议。转载请注明出处!