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

实现步骤

  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.pswp.cn/diannao/97478.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97478.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/97478.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

洛谷 P2052 [NOI2011] 道路修建-普及/提高-

P2052 [NOI2011] 道路修建 题目描述 在 W 星球上有 nnn 个国家。为了各自国家的经济发展,他们决定在各个国家之间建设双向道路使得国家之间连通。但是每个国家的国王都很吝啬,他们只愿意修建恰好 n−1n - 1n−1 条双向道路。 每条道路的修建都要付出一定…

springboot连接不上redis,但是redis客户端是能连接上的

除了常规排查,还有一个就是检查配置文件格式。这个旧版本格式会导致读取不到配置,spring:# 对应 RedisProperties 类redis:host: 127.0.0.1port: 6379 # password: 123456 # Redis 服务器密码,默认为空。生产中,一定要设置 Red…

GitBook 完整使用指南:从安装到部署

文章目录 环境准备 Node.js 安装 GitBook CLI 安装 项目初始化 创建项目结构 (可选) npm 初始化 目录结构配置 开发与调试 本地服务启动 构建静态文件 配置文件详解 插件系统 常用插件推荐 插件安装与配置 自定义样式 部署指南 GitHub Pages 部署 Netlify 部署 高级功能 多语言…

VS安装 .NETFramework,Version=v4.6.x

一、前言 在使用VS2019打开项目时提示MSB3644 找不到 .NETFramework,Versionv4.6.2 的引用程序集的错误 二、解决方案 1.百度......找到了解决方法了 2.打开Visual Studio Install 3.点击修改 4.点击单个组件,安装相对应的版本即可

Visual Studio Code中launch.json的解析笔记

<摘要> launch.json 是 Visual Studio Code 中用于配置调试任务的核心文件。本文解析了其最常用的配置字段&#xff0c;涵盖了基本调试设置、程序控制、环境配置和高级调试功能。理解这些字段能帮助开发者高效配置调试环境&#xff0c;提升开发效率。<解析> 1. 背景…

试试 Xget 加速 GitHub 克隆仓库

引言 在全球化软件开发环境中&#xff0c;开发者经常面临跨地域访问GitHub等平台的网络挑战&#xff1a;下载速度缓慢、连接不稳定、甚至完全无法访问。这些问题严重影响了开发效率和协作体验。Xget作为一个开源的高性能资源获取加速引擎&#xff0c;通过智能路由、多节点分发…

优雅处理Go中的SIGTERM信

在Go语言中优雅处理SIGTERM信号需通过os/signal包实现&#xff0c;核心流程包括信号注册、异步监听和资源清理。SIGTERM 是一种常见的进程终止信号&#xff0c;它允许程序在退出前执行必要的清理操作。与之不同&#xff0c;SIGKILL 信号无法被进程捕获或忽略。未处理的 SIGTERM…

《R for Data Science (2e)》免费中文翻译 (第6章) --- scripts and projects

写在前面 本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github&#xff0c;欢迎大家参与贡献&#xff0c;详细信息见&#xff1a; Books-zh-cn 项目介绍&#xff1a; Books-zh-cn&#xff1a;开源免费的中文书籍社区 r4ds-zh-cn …

GitHub Spark深度体验:是革命前夜,还是又一个“大厂玩具”?

最近&#xff0c;AI 编码工具层出不穷&#xff0c;几乎每天都有新概念诞生。而当 GitHub 这样的行业巨头携“Vibe Coding”概念入场时&#xff0c;所有开发者的期待值都被瞬间拉满。GitHub Spark&#xff0c;一个承诺能用自然语言将你的想法直接变成全栈应用的工具&#xff0c;…

科学研究系统性思维的方法体系:研究设计相关模版

一、研究设计方案模板 模板说明本模板基于《研究设计原理与方法》深度解读报告的理论框架&#xff0c;帮助研究者制定系统性的研究设计方案。模板整合了因果推断理论、效度控制框架和现代实验设计原理。1. 研究问题界定与假设陈述 1.1 研究问题核心要素 研究问题&#xff08;明…

法律审查prompt收集

当前DeepSeek等大模型已经具备初步合同审查能力。 这里收集合同审查及相关prompt&#xff0c;不管是做Coze等Agent&#xff0c;还是开发LLM应用&#xff0c;都有可能用到这些prompt。 https://github.com/LeeXYZABC/law_propmpts.git 1 条款分析 system_prompt&#xff0c;L…

贪心算法解决活动选择问题:最多不重叠活动数量求解

题目描述问题背景活动选择问题是贪心算法的经典应用场景之一。假设有若干个活动&#xff0c;每个活动都有独立的开始时间和结束时间&#xff0c;且同一时间只能进行一个活动。要求从这些活动中选择出最大数量的不重叠活动&#xff0c;即任意两个选中的活动&#xff0c;前一个活…

2025年如何批量下载雪球帖子和文章导出pdf?

之前分享过雪球文章下载 2025 批量下载市场高标解读/配置喵/wangdizhe 雪球帖子/文章导出excel和pdf 这里以市场高标解读这个号为例 抓取下载的所有帖子excel数据包含文章日期&#xff0c;文章标题&#xff0c;文章链接&#xff0c;文章简介&#xff0c;点赞数&#xff0c;转…

【C++】红黑树(详解)

文章目录上文链接一、什么是红黑树二、红黑树的性质1. 颜色规则2. 红黑树的规则为什么可以控制平衡3. 红黑树的效率三、红黑树的整体结构四、红黑树的插入1. 空树的插入2. 插入节点的父亲为黑色3. 插入节点的父亲为红色(1) 叔叔为红色&#xff1a;变色(2) 叔叔为空或为黑色&…

AI提升SEO关键词效果新策略

内容概要 在2025年&#xff0c;人工智能&#xff08;AI&#xff09;技术正全面革新搜索引擎优化&#xff08;SEO&#xff09;的关键词优化模式。通过智能分析用户搜索意图与语义关联&#xff0c;AI能够精准匹配关键词并进行高效布局。本文将深入探讨AI驱动的关键词策略升级方案…

手动安装的node到nvm吧版本管理的过程。

前言 本文记录个人在使用nvm包管理器安装node 14版本 npm安装失败&#xff0c;进行手动安装的node到nvm吧版本管理的过程。 安装node 14 时 npm总是安装失败&#xff0c;如下图 通过手动下载对于版本 node下载地址 下载解压点击所需的版本下载后解压 修改解压后的文件夹名称…

Python爬虫实战:构建Widgets 小组件数据采集和分析系统

1. 引言 1.1 研究背景 在当今数字化时代,Widgets 作为用户界面的基本组成元素,广泛应用于移动应用、网站和桌面软件中,其设计质量直接影响用户体验。随着市场竞争的加剧,了解市场上各类 Widgets 产品的特征、价格区间、用户评价等信息,对于产品设计和商业决策具有重要价…

1.1 Internet简介

1.网络, 计算机网络, 互联网 2.不同的角度认识Internet1.网络, 计算机网络, 互联网 网络表示连接两点以上的通路系统比如:a.你家到邻居家的小路 -> 一个小网络b.一个村子的所有道路 -> 一个更大的网络c.送外卖的小哥骑车走的路线 -> 一个配送网络计算机网络表示专门传…

pytest使用allure测试报告

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 选用的项目为Selenium自动化测试Pytest框架实战&#xff0c;在这个项目的基础上说allure报告。 allure安装 首先安装python的allure-pytest包 pip install allu…

PortSwigger靶场之SQL injection with filter bypass via XML encoding通关秘籍

一、题目分析该实验室的库存查询功能存在 SQL 注入漏洞。查询结果为这些信息会出现在应用程序的响应中&#xff0c;因此您可以利用“联合”攻击来从其他表中获取数据。该数据库中有一个“用户”表&#xff0c;该表包含了已注册用户的用户名和密码。要解决&#xff0c;需进行一次…