CsGrafeq:用C#实现一个几何画板 CsGrafeq:用C#实现一个几何画板一、引言什么是几何画板几何画板是一种交互式绘图工具允许用户通过数学公式或几何约束来创建和操作图形对象。传统几何画板如GeoGebra、几何画板The Geometer’s Sketchpad等通常用于数学教学和探索。而CsGrafeq是一个基于C#的开源项目它用代码实现了类似的功能——通过编程方式绘制几何图形、处理坐标系、支持交互操作。本文将带你从零开始逐步构建一个简单的几何画板核心功能包括坐标系管理、基本图形绘制、交互操作和数学表达式解析。即使你没有C#基础也能通过本文理解核心思想。## 二、基础概念坐标系与像素映射几何画板的核心是将数学坐标系如笛卡尔坐标系映射到屏幕像素坐标。数学坐标系中y轴向上为正屏幕坐标系中y轴向下为正。我们需要一个转换函数。### 2.1 坐标转换类csharpusing System;using System.Drawing;/// summary/// 数学坐标系与屏幕坐标系的映射/// /summarypublic class CoordinateSystem{ // 屏幕中心点像素坐标 public PointF ScreenCenter { get; set; } // 每单位数学长度对应的像素数 public float Scale { get; set; } public CoordinateSystem(PointF screenCenter, float scale) { ScreenCenter screenCenter; Scale scale; } /// summary /// 将数学坐标转换为屏幕像素坐标 /// /summary public PointF MathToScreen(float mathX, float mathY) { float screenX ScreenCenter.X mathX * Scale; float screenY ScreenCenter.Y - mathY * Scale; // y轴取反 return new PointF(screenX, screenY); } /// summary /// 将屏幕坐标转换为数学坐标用于鼠标交互 /// /summary public PointF ScreenToMath(float screenX, float screenY) { float mathX (screenX - ScreenCenter.X) / Scale; float mathY (ScreenCenter.Y - screenY) / Scale; return new PointF(mathX, mathY); }}这个类提供了双向转换是几何画板的基础。Scale参数控制缩放比例例如Scale50表示1个单位长度对应50像素。## 三、绘制基本几何图形有了坐标系我们就可以在Paint事件中绘制图形。下面实现一个函数绘制坐标轴、网格线和圆。### 3.1 绘制坐标轴与网格csharpusing System.Drawing;using System.Windows.Forms;public partial class GeometryBoard : Form{ private CoordinateSystem cs; private Timer refreshTimer; public GeometryBoard() { // 初始化坐标系屏幕中心(400,300)缩放50像素/单位 cs new CoordinateSystem(new PointF(400, 300), 50f); this.Paint GeometryBoard_Paint; this.Size new Size(800, 600); this.Text CsGrafeq - 几何画板示例; } private void GeometryBoard_Paint(object sender, PaintEventArgs e) { Graphics g e.Graphics; g.SmoothingMode System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 绘制网格每隔0.5单位画一条线 Pen gridPen new Pen(Color.LightGray, 0.5f); for (float x -8; x 8; x 0.5f) { PointF p1 cs.MathToScreen(x, -6); PointF p2 cs.MathToScreen(x, 6); g.DrawLine(gridPen, p1, p2); } for (float y -6; y 6; y 0.5f) { PointF p1 cs.MathToScreen(-8, y); PointF p2 cs.MathToScreen(8, y); g.DrawLine(gridPen, p1, p2); } // 绘制坐标轴 Pen axisPen new Pen(Color.Black, 2f); PointF origin cs.MathToScreen(0, 0); PointF xEnd cs.MathToScreen(8, 0); PointF yEnd cs.MathToScreen(0, 6); g.DrawLine(axisPen, cs.MathToScreen(-8, 0), xEnd); g.DrawLine(axisPen, cs.MathToScreen(0, -6), yEnd); // 绘制一个圆圆心(2,1)半径3 DrawCircle(g, new PointF(2, 1), 3, Color.Blue); } /// summary /// 在数学坐标系中绘制圆 /// /summary private void DrawCircle(Graphics g, PointF center, float radius, Color color) { PointF screenCenter cs.MathToScreen(center.X, center.Y); float screenRadius radius * cs.Scale; Pen pen new Pen(color, 2); g.DrawEllipse(pen, screenCenter.X - screenRadius, screenCenter.Y - screenRadius, screenRadius * 2, screenRadius * 2); }}运行这段代码你会看到一个带网格和坐标轴的窗口中心位置绘制了一个半径为3的蓝色圆。## 四、交互操作鼠标拖拽与动态更新几何画板的魅力在于交互。我们可以让用户通过鼠标拖拽来平移视图平移坐标系或者滚动滚轮缩放。### 4.1 实现平移与缩放csharpprivate PointF lastMousePos; // 鼠标上次位置数学坐标private bool isDragging false;// 在构造函数中添加事件绑定public GeometryBoard(){ // ... 之前的初始化代码 ... this.MouseDown GeometryBoard_MouseDown; this.MouseMove GeometryBoard_MouseMove; this.MouseUp GeometryBoard_MouseUp; this.MouseWheel GeometryBoard_MouseWheel;}private void GeometryBoard_MouseDown(object sender, MouseEventArgs e){ if (e.Button MouseButtons.Left) { isDragging true; // 记录鼠标按下时的数学坐标 lastMousePos cs.ScreenToMath(e.X, e.Y); }}private void GeometryBoard_MouseMove(object sender, MouseEventArgs e){ if (isDragging) { // 计算鼠标移动的数学偏移量 PointF currentMath cs.ScreenToMath(e.X, e.Y); float dx currentMath.X - lastMousePos.X; float dy currentMath.Y - lastMousePos.Y; // 平移坐标系反向移动实现拖拽效果 cs.ScreenCenter new PointF( cs.ScreenCenter.X dx * cs.Scale, cs.ScreenCenter.Y - dy * cs.Scale ); // 更新上次位置 lastMousePos currentMath; this.Invalidate(); // 触发重绘 }}private void GeometryBoard_MouseUp(object sender, MouseEventArgs e){ isDragging false;}private void GeometryBoard_MouseWheel(object sender, MouseEventArgs e){ // 滚轮缩放滚轮向上放大向下缩小 float zoomFactor e.Delta 0 ? 1.1f : 0.9f; cs.Scale * zoomFactor; // 限制缩放范围避免死循环 if (cs.Scale 10) cs.Scale 10; if (cs.Scale 500) cs.Scale 500; this.Invalidate();}现在你可以用鼠标拖拽平移视图滚动滚轮缩放。每次交互都会触发Invalidate()重新绘制所有图形。## 五、高级功能引入表达式解析器真正的几何画板允许用户输入数学表达式如y x^2来绘制函数曲线。我们可以使用C#的DataTable.Compute或第三方库如NCalc实现表达式解析。下面用简单方法实现函数绘图。### 5.1 函数绘图器csharp/// summary/// 绘制数学函数 y f(x) 的曲线/// /summaryprivate void DrawFunction(Graphics g, string expression, Color color){ // 使用DataTable计算表达式简单但有限制 System.Data.DataTable table new System.Data.DataTable(); table.Columns.Add(x, typeof(double)); table.Columns.Add(y, typeof(double)); Pen pen new Pen(color, 2); PointF? lastPoint null; // 在x范围[-8,8]内采样步长0.01 for (float x -8; x 8; x 0.01f) { // 替换表达式中的x变量 string expr expression.Replace(x, x.ToString(0.000)); try { double y Convert.ToDouble(table.Compute(expr, )); if (double.IsNaN(y) || double.IsInfinity(y)) continue; PointF current cs.MathToScreen(x, (float)y); if (lastPoint.HasValue) { g.DrawLine(pen, lastPoint.Value, current); } lastPoint current; } catch { // 忽略计算错误如除零 lastPoint null; } }}在Paint事件中调用csharp// 在GeometryBoard_Paint方法末尾添加DrawFunction(g, x*x, Color.Red); // 抛物线 yx^2DrawFunction(g, Sin(x), Color.Green); // 正弦曲线注意DataTable.Compute不支持三角函数的弧度计算实际项目中应使用Math.Sin等原生函数。这里仅示意思路。## 六、完整代码整合与运行将上述所有代码合并到一个GeometryBoard.cs文件中添加Main入口csharpstatic class Program{ [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new GeometryBoard()); }}运行后你将看到- 带网格和坐标轴的画布- 一个蓝色圆圆心(2,1)半径3- 红色抛物线yx^2和绿色正弦曲线- 鼠标左键拖拽平移滚轮缩放## 总结本文从零开始用C#实现了一个简化版的几何画板核心功能1.坐标系映射解决了数学坐标与屏幕像素的转换问题。2.图形绘制展示了如何绘制网格、坐标轴和圆。3.交互操作实现了鼠标拖拽平移和滚轮缩放提升了用户体验。4.表达式解析初步实现了函数曲线的动态绘制。CsGrafeq的完整版本还包括更多高级特性点选与拖动对象、几何约束求解、动画生成、导出图片等。通过本文的学习你已经掌握了构建几何画板的基础框架可以继续扩展功能比如添加矩形、直线、角度测量等对象甚至实现类似于GeoGebra的交互式几何学习工具。C#的Windows Forms结合GDI提供了强大的绘图能力而数学与编程的结合正是几何画板项目的魅力所在。希望你能以此为基础创造出属于自己的数学可视化工具