当前位置: 首页 > news >正文

【C#】在一个任意旋转的矩形(由四个顶点定义)内绘制一个内切椭圆

核心点:在一个任意旋转的矩形(由四个顶点定义)内绘制一个内切椭圆

实现步骤

  1. 计算矩形中心:作为旋转中心点

  2. 创建椭圆路径:在未旋转状态下定义椭圆

  3. 应用旋转变换:使用矩阵绕中心点旋转路径

  4. 绘制变换后的路径

实现方法

public void DrawRotatedEllipse(Graphics g, PointF[] rotatedCorners, float rotationAngle)
{// 确保输入是四个点if (rotatedCorners.Length != 4)throw new ArgumentException("必须提供矩形的四个顶点");// 初始化画笔(根据您的原有逻辑)if (g != m_LastGraphic){m_ControlPen = new Pen(Color.FromKnownColor(AnnotationSysINI.GetSetToolsColor()), AnnotationSysINI.GetSetToolsLineWide());m_BackPen = new Pen(Color.FromKnownColor(KnownColor.Black), 5);m_LastGraphic = g;}// 设置高质量绘图g.SmoothingMode = SmoothingMode.AntiAlias;g.PixelOffsetMode = PixelOffsetMode.HighQuality;g.CompositingQuality = CompositingQuality.HighQuality;// 1. 计算旋转矩形的中心点PointF center = new PointF((rotatedCorners[0].X + rotatedCorners[1].X + rotatedCorners[2].X + rotatedCorners[3].X) / 4,(rotatedCorners[0].Y + rotatedCorners[1].Y + rotatedCorners[2].Y + rotatedCorners[3].Y) / 4);// 2. 计算矩形的实际宽度和高度float width = (float)Math.Sqrt(Math.Pow(rotatedCorners[1].X - rotatedCorners[0].X, 2) +Math.Pow(rotatedCorners[1].Y - rotatedCorners[0].Y, 2));float height = (float)Math.Sqrt(Math.Pow(rotatedCorners[3].X - rotatedCorners[0].X, 2) +Math.Pow(rotatedCorners[3].Y - rotatedCorners[0].Y, 2));// 3. 创建椭圆路径(在原点处创建)using (GraphicsPath path = new GraphicsPath()){// 创建以原点为中心的椭圆RectangleF baseRect = new RectangleF(-width / 2, -height / 2, width, height);path.AddEllipse(baseRect);// 4. 应用变换:先旋转后平移using (Matrix transform = new Matrix()){// 注意顺序:先旋转后平移transform.Rotate(rotationAngle);transform.Translate(center.X, center.Y, MatrixOrder.Append);path.Transform(transform);}// 5. 绘制椭圆if (AnnotationSysINI.GetSetToolsLineBackPen())g.DrawPath(m_BackPen, path);g.DrawPath(m_ControlPen, path);}// 6. (可选) 绘制旋转矩形边界用于验证using (Pen debugPen = new Pen(Color.Red, 1)){g.DrawPolygon(debugPen, rotatedCorners);}
}

使用示例

// 创建旋转后的矩形四个顶点
PointF[] rotatedCorners = new PointF[4]
{new PointF(100, 50),   // 旋转后的左上new PointF(200, 70),   // 旋转后的右上new PointF(180, 170),  // 旋转后的右下new PointF(80, 150)    // 旋转后的左下
};// 计算旋转角度(如果需要)
float angle = 30; // 已知的旋转角度// 在Paint事件中调用
DrawRotatedEllipse(e.Graphics, rotatedCorners, angle);

 

关键点说明

  1. 直接使用旋转后的顶点

    • 方法接受旋转后矩形的四个顶点作为输入

    • 不再依赖原始未旋转的左上/右下坐标

  2. 正确计算尺寸

    // 宽度 = 左上到右上的距离
    float width = Distance(rotatedCorners[0], rotatedCorners[1]);// 高度 = 左上到左下的距离
    float height = Distance(rotatedCorners[0], rotatedCorners[3]);
  3. 变换顺序至关重要

    // 正确顺序:先旋转后平移
    transform.Rotate(rotationAngle);
    transform.Translate(center.X, center.Y, MatrixOrder.Append);
  4. 原点中心法

    • 先在原点(0,0)创建椭圆

    • 然后通过变换移动到实际位置

如果只有原始矩形和旋转角度

public PointF[] CalculateRotatedCorners(PointF topLeft, PointF bottomRight, float angle)
{float width = bottomRight.X - topLeft.X;float height = bottomRight.Y - topLeft.Y;PointF center = new PointF(topLeft.X + width/2, topLeft.Y + height/2);PointF[] corners = new PointF[4]{topLeft, // 原始左上new PointF(topLeft.X + width, topLeft.Y), // 原始右上bottomRight, // 原始右下new PointF(topLeft.X, topLeft.Y + height) // 原始左下};using (Matrix matrix = new Matrix()){matrix.RotateAt(angle, center);matrix.TransformPoints(corners);}return corners;
}// 使用:
PointF topLeft = new PointF(50, 50);
PointF bottomRight = new PointF(250, 150);
float angle = 30f;PointF[] rotatedCorners = CalculateRotatedCorners(topLeft, bottomRight, angle);
DrawRotatedEllipse(g, rotatedCorners, angle);

 

常见问题解决

  1. 椭圆位置偏移

    • 检查中心点计算是否正确

    • 验证顶点顺序是否一致(顺时针/逆时针)

    • 使用调试边界框可视化

  2. 椭圆变形

    • 确保宽度/高度计算正确

    • 检查旋转角度单位(度/弧度)

    • 确认矩形顶点构成直角

  3. 性能优化:这种方法保证了椭圆始终精确适配旋转后的矩形边界,无论旋转角度如何变化。

// 类级别缓存
private PointF[] _lastCorners;
private GraphicsPath _cachedPath;// 在方法中:
if (_lastCorners != rotatedCorners)
{// 重新计算路径_cachedPath?.Dispose();_cachedPath = CreateEllipsePath(rotatedCorners, angle);_lastCorners = (PointF[])rotatedCorners.Clone();
}
g.DrawPath(pen, _cachedPath);

 在旋转矩形内绘制椭圆并实现鼠标碰撞检测

 

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;public class RotatedEllipseTool
{// 存储椭圆路径用于碰撞检测private GraphicsPath _ellipsePath;// 画笔设置private Pen _controlPen;private Pen _backPen;private Graphics _lastGraphic;// 椭圆属性public PointF[] RotatedCorners { get; private set; }public float RotationAngle { get; private set; }public RotatedEllipseTool(){// 初始化画笔_controlPen = new Pen(Color.Blue, 2);_backPen = new Pen(Color.Black, 5);}// 创建旋转椭圆路径(用于绘制和碰撞检测)public void CreateRotatedEllipse(PointF[] rotatedCorners, float rotationAngle){// 确保输入是四个点if (rotatedCorners.Length != 4)throw new ArgumentException("必须提供矩形的四个顶点");// 保存参数RotatedCorners = rotatedCorners;RotationAngle = rotationAngle;// 计算旋转矩形的中心点PointF center = new PointF((rotatedCorners[0].X + rotatedCorners[1].X + rotatedCorners[2].X + rotatedCorners[3].X) / 4,(rotatedCorners[0].Y + rotatedCorners[1].Y + rotatedCorners[2].Y + rotatedCorners[3].Y) / 4);// 计算矩形的实际宽度和高度float width = Distance(rotatedCorners[0], rotatedCorners[1]);float height = Distance(rotatedCorners[0], rotatedCorners[3]);// 创建椭圆路径_ellipsePath?.Dispose(); // 释放旧路径_ellipsePath = new GraphicsPath();// 创建以原点为中心的椭圆RectangleF baseRect = new RectangleF(-width / 2, -height / 2, width, height);_ellipsePath.AddEllipse(baseRect);// 应用变换:先旋转后平移using (Matrix transform = new Matrix()){// 注意顺序:先旋转后平移transform.Rotate(rotationAngle);transform.Translate(center.X, center.Y, MatrixOrder.Append);_ellipsePath.Transform(transform);}}// 绘制椭圆public void Draw(Graphics g){if (_ellipsePath == null) return;// 初始化画笔(如果需要)if (g != _lastGraphic){// 这里使用您原有的配置逻辑_controlPen = new Pen(AnnotationSysINI.GetSetToolsColor(), AnnotationSysINI.GetSetToolsLineWide());_backPen = new Pen(Color.Black, 5);_lastGraphic = g;}// 设置高质量绘图g.SmoothingMode = SmoothingMode.AntiAlias;g.PixelOffsetMode = PixelOffsetMode.HighQuality;g.CompositingQuality = CompositingQuality.HighQuality;// 绘制椭圆if (AnnotationSysINI.GetSetToolsLineBackPen())g.DrawPath(_backPen, _ellipsePath);g.DrawPath(_controlPen, _ellipsePath);// 绘制边界框(调试用)using (Pen debugPen = new Pen(Color.Red, 1)){g.DrawPolygon(debugPen, RotatedCorners);}}// 鼠标碰撞检测public bool HitTest(PointF point){if (_ellipsePath == null) return false;// 使用路径的IsVisible方法进行碰撞检测return _ellipsePath.IsVisible(point);}// 辅助方法:计算两点距离private float Distance(PointF p1, PointF p2){float dx = p2.X - p1.X;float dy = p2.Y - p1.Y;return (float)Math.Sqrt(dx * dx + dy * dy);}// 计算旋转后的矩形顶点public static PointF[] CalculateRotatedCorners(PointF topLeft, PointF bottomRight, float angle){float width = bottomRight.X - topLeft.X;float height = bottomRight.Y - topLeft.Y;PointF center = new PointF(topLeft.X + width/2, topLeft.Y + height/2);PointF[] corners = new PointF[4]{topLeft, // 原始左上new PointF(topLeft.X + width, topLeft.Y), // 原始右上bottomRight, // 原始右下new PointF(topLeft.X, topLeft.Y + height) // 原始左下};using (Matrix matrix = new Matrix()){matrix.RotateAt(angle, center);matrix.TransformPoints(corners);}return corners;}// 释放资源public void Dispose(){_ellipsePath?.Dispose();_controlPen?.Dispose();_backPen?.Dispose();}
}// 使用示例
public class DrawingForm : Form
{private RotatedEllipseTool _ellipseTool = new RotatedEllipseTool();public DrawingForm(){this.DoubleBuffered = true;this.Size = new Size(800, 600);// 创建旋转矩形PointF topLeft = new PointF(100, 100);PointF bottomRight = new PointF(300, 200);float rotationAngle = 30f;// 计算旋转后的顶点PointF[] rotatedCorners = RotatedEllipseTool.CalculateRotatedCorners(topLeft, bottomRight, rotationAngle);// 创建椭圆路径_ellipseTool.CreateRotatedEllipse(rotatedCorners, rotationAngle);// 设置鼠标事件this.MouseClick += DrawingForm_MouseClick;this.MouseMove += DrawingForm_MouseMove;}protected override void OnPaint(PaintEventArgs e){base.OnPaint(e);_ellipseTool.Draw(e.Graphics);}private void DrawingForm_MouseClick(object sender, MouseEventArgs e){if (_ellipseTool.HitTest(e.Location)){MessageBox.Show("点击了椭圆内部!");}}private void DrawingForm_MouseMove(object sender, MouseEventArgs e){// 改变鼠标指针当在椭圆内部时this.Cursor = _ellipseTool.HitTest(e.Location) ? Cursors.Hand : Cursors.Default;}protected override void Dispose(bool disposing){if (disposing){_ellipseTool.Dispose();}base.Dispose(disposing);}
}

关键实现说明

1. 路径创建与保存

  • CreateRotatedEllipse方法计算并保存椭圆路径

  • 路径存储在_ellipsePath字段中,用于绘制和碰撞检测

  • 路径创建过程:

    1. 计算旋转矩形的中心点

    2. 计算矩形的实际宽度和高度

    3. 创建以原点为中心的椭圆

    4. 应用旋转变换矩阵

2. 鼠标碰撞检测

  • HitTest方法使用GraphicsPath.IsVisible()检测点是否在路径内

  • 该方法利用GDI+的内置功能进行精确碰撞检测

  • 在鼠标事件中调用该方法实现交互效果

3. 使用示例

  • 在窗体中创建旋转椭圆工具实例

  • 计算旋转后的矩形顶点

  • 创建椭圆路径

  • 在Paint事件中绘制椭圆

  • 在鼠标事件中检测碰撞

4. 辅助功能

  • CalculateRotatedCorners:从原始矩形计算旋转后的顶点

  • Distance:计算两点间距离

  • Dispose:正确释放GDI资源

实现效果

  1. 在旋转矩形内绘制完美适配的椭圆

  2. 椭圆随矩形旋转角度变化

  3. 鼠标在椭圆内部时指针变为手形

  4. 点击椭圆内部显示提示信息

  5. 使用高质量抗锯齿渲染

注意事项

  1. 资源管理

    • 使用后务必调用Dispose()释放资源

    • 路径在重新创建前释放旧路径

  2. 碰撞检测精度

    • IsVisible方法考虑了路径的填充区域

    • 对于空心椭圆,可能需要调整检测方式

  3. 性能优化

    • 路径创建后缓存起来,避免重复计算

    • 只在必要时重新创建路径(如矩形改变时)

URL

C# 图像旋转一定角度后,对应坐标怎么计算?_c# 求点旋转之后的坐标-CSDN博客文章浏览阅读1.8k次,点赞16次,收藏10次。本文详细解释了如何在二维空间中计算图像内坐标在旋转特定角度后的新坐标,包括中心点、角度转换、旋转公式以及使用C#代码示例展示如何应用这些概念。同时提到了处理边界问题和使用GDI+或OpenCV库进行旋转的功能。 https://blog.csdn.net/wangnaisheng/article/details/137919395?spm=1011.2415.3001.5331 C# Bitmap实现角度旋转_c# bitmap 旋转-CSDN博客文章浏览阅读2.2k次,点赞10次,收藏8次。本文介绍了在C#中对Bitmap对象进行旋转的三种方法:使用Bitmap.RotateFlip方法进行90度旋转,使用System.Drawing.Drawing2D.Matrix类进行任意角度旋转,以及利用第三方库如ImageSharp进行高级图像处理。 https://blog.csdn.net/wangnaisheng/article/details/138160242?spm=1011.2415.3001.5331

 

http://www.dtcms.com/a/359383.html

相关文章:

  • 在AlmaLinux或CentOS 8上编译安装ZLMediaKit流媒体服务器
  • Mysql中事务隔离级别有哪些?
  • 【行业洞察】多智能体的风口浪尖--微软MagenticOne/UI
  • android中常见布局及其约束
  • 鸿蒙创新赛活动——Mac提交压缩失败后续
  • [linux仓库]解剖Linux内核:文件描述符(fd)的‘前世今生’与内核数据结构探秘
  • 如何绕过 disable-devtool.js 打开控制台
  • mac Monterey 安装erlang23
  • 【高级】系统架构师 | 信息系统基础
  • Wi-Fi技术——MAC特性
  • Java提供高效后端支撑,Vue呈现直观交互界面,共同打造的MES管理系统,含完整可运行源码,实现生产计划、执行、追溯一站式管理,提升制造执行效率
  • 基于EHO与BP神经网络分类模型的特征选择方法研究(Python实现)
  • 现代C++性能陷阱:std::function的成本、异常处理的真实开销
  • HarmonyOS 应用开发:基于API 12+的现代化实践
  • 第4章从一条记录说起-InnoDB记录结构
  • openssl使用SM2进行数据加密和数据解密
  • Linux中卸载和安装Nginx
  • 第24章学习笔记|用正则表达式解析文本文件(PowerShell 实战)
  • Git版本管理工具零基础学习
  • ThinkPHP8学习篇(五):数据库(一)
  • windows docker 中的mysql 无法被外部浏览器访问如何解决
  • windows环境下安装dify到本地
  • 线程池、锁策略
  • Qt中UDP回显服务器和客户端
  • 第三十二天:数组
  • 如何保证redis和mysql的数据一致性
  • Spring Boot 3.x 微服务架构实战指南
  • 基于单片机停车场管理系统/车位管理/智慧停车系统
  • 大模型——xAI 发布 Grok Code Fast 1 编程模型,快、便宜、免费
  • 华为研发投资与管理实践(IPD)读书笔记