两种常见矩形框旋转方法推导及其C++实现

在已知矩形中心点、长宽和旋转角度(定义为矩形最长边与X轴正方向的夹角),如何确定矩形四个顶点的坐标,通常有以下两种处理方法。

法一:直接对顶点进行旋转

比如下图虚线框矩形是实线框矩形绕矩形中心点旋转后得到。在已知矩形中心点坐标和长宽的前提下,实线框四顶点坐标可直接换算得到。然后就是分析计算经旋转后的虚线框矩形的四顶点坐标。

由于是绕矩形中心点旋转,因此可以将坐标系原点平移到矩形中心点位置。然后将矩形框四顶点用极坐标表示,并转换成直角坐标。

旋转前A顶点坐标:(其中r表示矩形框四个顶点距离坐标原点的距离,α表示顶点与坐标原点连线与X轴的夹角)

A=\left ( r\cdot \cos \alpha ,r\cdot \sin \alpha \right )=\left ( x-c_{x},y-c_{y} \right )

则绕坐标原点旋转θ角度后A'顶点坐标:

A'=\left ( r\cdot \cos \left ( \alpha+\theta \right ) ,r\cdot \sin \left ( \alpha+\theta \right ) \right )

对A'顶点坐标按照三角函数的和差角公式展开:

A'=\left ( r\cdot \cos \alpha \cdot \cos \theta -r\cdot \sin \alpha \cdot \sin \theta ,r\cdot \sin \alpha \cdot \cos \theta +r\cdot \sin \theta \cdot \cos \alpha \right )

将A顶点坐标代入A'顶点坐标则有:

A'=\left ( \left ( x-c_{x} \right ) \cdot \cos \theta -\left ( y-c_{y} \right ) \cdot \sin \theta ,\left ( y-c_{y} \right ) \cdot \cos \theta +\left ( x-c_{x} \right )\cdot \sin \theta \right )

用矩阵形式表示:

A'=\begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix}\cdot \left ( \begin{bmatrix} x\\ y \end{bmatrix}-\begin{bmatrix} c_{x}\\ c_{y} \end{bmatrix} \right )

由于坐标系原点被平移到矩形中心点位置,因此最终还需将A'顶点坐标平移回去:

A'=\begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix}\cdot \left ( \begin{bmatrix} x\\ y \end{bmatrix}-\begin{bmatrix} c_{x}\\ c_{y} \end{bmatrix} \right )+\begin{bmatrix} c_{x}\\ c_{y} \end{bmatrix}

  

法二:根据三角形几何性质换算顶点坐标

针对四顶点分别绘制出下图所示辅助线,通过相似三角形不难得到下图中两相等辅助角。

矩形四顶点坐标分别为:

A:\left\{\begin{matrix} x=c_{x}+\frac{l}{2}\cdot \cos \theta-\frac{w}{2}\cdot \sin \theta \\ y=c_{y}+\frac{l}{2}\cdot \sin \theta+\frac{w}{2}\cdot \cos \theta \end{matrix}\right. 

B:\left\{\begin{matrix} x=c_{x}-\frac{l}{2}\cdot \cos \theta-\frac{w}{2}\cdot \sin \theta \\ y=c_{y}-\frac{l}{2}\cdot \sin \theta+\frac{w}{2}\cdot \cos \theta \end{matrix}\right.

C:\left\{\begin{matrix} x=c_{x}-\frac{l}{2}\cdot \cos \theta+\frac{w}{2}\cdot \sin \theta \\ y=c_{y}-\frac{l}{2}\cdot \sin \theta-\frac{w}{2}\cdot \cos \theta \end{matrix}\right. 

D:\left\{\begin{matrix} x=c_{x}+\frac{l}{2}\cdot \cos \theta+\frac{w}{2}\cdot \sin \theta \\ y=c_{y}+\frac{l}{2}\cdot \sin \theta-\frac{w}{2}\cdot \cos \theta \end{matrix}\right.

由于A与C、B与D分别是关于\left ( c_{x},c_{y} \right )的对称点,所以各项正负号相反。

  

C++代码实现

#include <iostream>
#include <cmath>
#include <vector>// #include <Eigen/Core>
#include <eigen3/Eigen/Core>#define MATH_PI 3.14159265358979323846264338327950288419716939937510Ltemplate <typename T>
struct Point2D {T x = 0;T y = 0;
};
typedef Point2D<float> Point2DF;
typedef Point2D<double> Point2DD;typedef struct {Point2DF center;float length;float width;float theta; //rad, (-pi,pi]
} ST_BOX_INFO;typedef struct {Point2DF a;Point2DF b;Point2DF c;Point2DF d;
} ST_BOX_FOUR_VERTICES;void RotateBoxVerticesMethod1(const ST_BOX_INFO& origin_box, ST_BOX_FOUR_VERTICES& rotated_box);
void RotateBoxVerticesMethod2(const ST_BOX_INFO& origin_box, ST_BOX_FOUR_VERTICES& rotated_box);int main(void) {ST_BOX_INFO origin_box;origin_box.center.x = 4;origin_box.center.y = 3;origin_box.length = 4;origin_box.width = 2;origin_box.theta = 0.5 * MATH_PI;// origin_box.theta = 0.5 * 0.5 * MATH_PI;ST_BOX_FOUR_VERTICES rotated_box1, rotated_box2;RotateBoxVerticesMethod1(origin_box, rotated_box1);RotateBoxVerticesMethod2(origin_box, rotated_box2);return 0;
}void RotateBoxVerticesMethod1(const ST_BOX_INFO& origin_box, ST_BOX_FOUR_VERTICES& rotated_box) {Eigen::MatrixXd R = Eigen::MatrixXd::Zero(8, 8);Eigen::VectorXd t(8);Eigen::VectorXd vertices(8);const auto l_half = 0.5 * origin_box.length;const auto w_half = 0.5 * origin_box.width;auto theta = origin_box.theta;if (1.0e-6 > (MATH_PI - std::fabs(theta))) {theta = 0.0;} else if (1.0e-6 > std::fabs((0.5 * MATH_PI) - std::fabs(theta))) {;} else if ((0.5 * MATH_PI) < theta) {theta = theta - MATH_PI;} else if ((-0.5 * MATH_PI) > theta) {theta = theta + MATH_PI;}rotated_box.a.x = origin_box.center.x + l_half;rotated_box.a.y = origin_box.center.y + w_half;rotated_box.b.x = origin_box.center.x - l_half;rotated_box.b.y = origin_box.center.y + w_half;rotated_box.c.x = origin_box.center.x - l_half;rotated_box.c.y = origin_box.center.y - w_half;rotated_box.d.x = origin_box.center.x + l_half;rotated_box.d.y = origin_box.center.y - w_half;std::cout << "before rotated: (" << rotated_box.a.x << ',' << rotated_box.a.y << ')'<< '(' << rotated_box.b.x << ',' << rotated_box.b.y << ')'<< '(' << rotated_box.c.x << ',' << rotated_box.c.y << ')'<< '(' << rotated_box.d.x << ',' << rotated_box.d.y << ')' << std::endl;R(0, 0) = R(1, 1) = R(2, 2) = R(3, 3) = R(4, 4) = R(5, 5) = R(6, 6) = R(7, 7) = std::cos(theta);R(1, 0) = R(3, 2) = R(5, 4) = R(7, 6) = std::sin(theta);R(0, 1) = R(2, 3) = R(4, 5) = R(6, 7) = -R(1, 0);t << origin_box.center.x, origin_box.center.y, origin_box.center.x, origin_box.center.y,origin_box.center.x, origin_box.center.y, origin_box.center.x, origin_box.center.y;vertices << rotated_box.a.x, rotated_box.a.y,rotated_box.b.x, rotated_box.b.y,rotated_box.c.x, rotated_box.c.y,rotated_box.d.x, rotated_box.d.y;const Eigen::VectorXd rslt = R * (vertices - t) + t;rotated_box.a.x = rslt(0);rotated_box.a.y = rslt(1);rotated_box.b.x = rslt(2);rotated_box.b.y = rslt(3);rotated_box.c.x = rslt(4);rotated_box.c.y = rslt(5);rotated_box.d.x = rslt(6);rotated_box.d.y = rslt(7);std::cout << "after rotated: (" << rotated_box.a.x << ',' << rotated_box.a.y << ')'<< '(' << rotated_box.b.x << ',' << rotated_box.b.y << ')'<< '(' << rotated_box.c.x << ',' << rotated_box.c.y << ')'<< '(' << rotated_box.d.x << ',' << rotated_box.d.y << ')' << std::endl;
}void RotateBoxVerticesMethod2(const ST_BOX_INFO& origin_box, ST_BOX_FOUR_VERTICES& rotated_box) {auto theta = origin_box.theta;if (1.0e-6 > (MATH_PI - std::fabs(theta))) {theta = 0.0;} else if (1.0e-6 > std::fabs((0.5 * MATH_PI) - std::fabs(theta))) {;} else if ((0.5 * MATH_PI) < theta) {theta = theta - MATH_PI;} else if ((-0.5 * MATH_PI) > theta) {theta = theta + MATH_PI;}Eigen::Vector2d direction(std::cos(theta), std::sin(theta));Eigen::Vector2d orthog_dir(-direction.y(), direction.x());Eigen::Vector2d delta_x = 0.5 * origin_box.length * direction;Eigen::Vector2d delta_y = 0.5 * origin_box.width * orthog_dir;Eigen::Vector2d center = Eigen::Vector2d(origin_box.center.x, origin_box.center.y);std::vector<Eigen::Vector2d> vertices(4);vertices[0] = center + (delta_x + delta_y);vertices[1] = center + (-delta_x + delta_y);vertices[2] = center + (-delta_x - delta_y);vertices[3] = center + (delta_x - delta_y);rotated_box.a.x = vertices[0](0);rotated_box.a.y = vertices[0](1);rotated_box.b.x = vertices[1](0);rotated_box.b.y = vertices[1](1);rotated_box.c.x = vertices[2](0);rotated_box.c.y = vertices[2](1);rotated_box.d.x = vertices[3](0);rotated_box.d.y = vertices[3](1);std::cout << "after rotated: (" << rotated_box.a.x << ',' << rotated_box.a.y << ')'<< '(' << rotated_box.b.x << ',' << rotated_box.b.y << ')'<< '(' << rotated_box.c.x << ',' << rotated_box.c.y << ')'<< '(' << rotated_box.d.x << ',' << rotated_box.d.y << ')' << std::endl;
}

若是要计算三维长方体的的八个顶点(已知中心点、长宽高和旋转角度,且pitch、roll角恒为0°),则用以上同样的方法先计算底部长方形的四顶点坐标,然后再将长方体的高累加到四顶点坐标对应轴上,即可得到顶部长方形四顶点的坐标。

其它方法可在评论区留言补充。

      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/140226.html

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

相关文章

深度学习实战基础案例——卷积神经网络(CNN)基于Xception的猫狗识别|第2例

文章目录 一、环境准备二、数据预处理三、构建模型四、实例化模型五、训练模型5.1 构建训练函数5.2 构建测试函数5.3 开始正式训练 六、可视化精度和损失七、个体预测总结 今天使用轻量级的一个网络Xception做一个简单的猫狗识别案例&#xff0c;我的环境具体如下&#xff1a; …

记一次STM32F4 HAL IAP开发过程踩坑

第一次在HAL库上做IAP&#xff0c;不太熟悉库结构&#xff0c;被坑了一早上… MCU上做了一个shell&#xff0c;实现了goto命令跳转到APP区执行&#xff08;只是为了开发时方便&#xff09;。跳转到APP前和以前一样清理了所有初始化过的外设&#xff0c;也对中断进行了处理&…

MySQL数据库的索引和事务

目录 一、索引 1.1Mysql索引 1.2索引的作用 1.3 创建索引的依据 1.4 普通索引 修改表方式创建索引 删除索引 1.5 唯一索引 修改表方式创建 删除索引 1.6 主键索引 修改表方式创建 1.7 组合索引 1.8 全文索引 1.9查看索引 二、事务 2.1事务概念 2.2事务的ACID特…

rocketmq-spring-boot-starter 2.1.0 事务消息移除参数txProducerGroup

statrer引入 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.3</version></dependency> starter 2.0.2对应rocketmq 4.4.0 starter 2.1.0对应rocke…

NSDT 3D孪生场景搭建:阵列摆放详解

阵列摆放概念 阵列摆放是指将物体、设备或元件按照一定的规则和间距排列组合的方式。在工程和科学领域中&#xff0c;阵列式摆放常常用于优化空间利用、提高效率或增强性能。 阵列摆放通常需要考虑间距、角度、方向、对称性等因素&#xff0c;以满足特定的要求和设计目标。不同…

Seata流程源码梳理下篇-TC

我们上篇简单梳理了下TM、RM的一些流程&#xff08;离现在过得挺久的了&#xff0c;这篇我们这篇来梳理下TC的内容。 TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态&#xff0c;驱动全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器 定…

C++ Primer 第5章 语句

C Primer 第5章 语句 5.1 简单语句一、空语句二、别漏写分号&#xff0c;也别多写分号三、复合语句&#xff08;块&#xff09; 5.2 语句作用域5.3 条件语句5.3.1 if语句一、使用if else语句二、嵌套if语句三、注意使用花括号四、悬垂else五、使用花括号控制执行路径 5.3.2 swi…

Oracle分区的使用详解:创建、修改和删除分区,处理分区已满或不存在的插入数据,以及分区历史数据与近期数据的操作指南

一、前言 什么是表分区: Oracle的分区是一种将表或索引数据分割为更小、更易管理的部分的技术。它可以提高查询性能、简化维护操作,并提供更好的数据组织和管理。 表分区和表空间的区别和联系: 在Oracle数据库中,表空间(Tablespace)是用于存储表、索引和其他数据库对…

Baichuan2 技术报告笔记

文章目录 预训练预训练数据模型架构TokenizerPositional EmbeddingsAcitivations and NormalizationsOptimizations 对齐Supervised Fine-TuningRLHF 安全性预训练阶段对齐阶段 参考资料 对Baichuan2技术报告阅读后的笔记 Baichuan2 与其他大模型的对比如下表 预训练 预训练数…

【Java开发】Redis位图实现统计日活周活月活

最近研究了使用 Redis 的位图功能统计日活周活等数据&#xff0c;特来和大家分享下&#xff0c;Redis 位图还可用于记录用户签到情况、判断某个元素是否存在于集合中等。 1 Redis 位图介绍 Redis 位图是一种特殊的数据结构&#xff0c;它由一系列位组成&#xff0c;每个位只能…

洛谷P8815:逻辑表达式 ← CSP-J 2022 复赛第3题

【题目来源】https://www.luogu.com.cn/problem/P8815https://www.acwing.com/problem/content/4733/【题目描述】 逻辑表达式是计算机科学中的重要概念和工具&#xff0c;包含逻辑值、逻辑运算、逻辑运算优先级等内容。 在一个逻辑表达式中&#xff0c;元素的值只有两种可能&a…

Appilot发布:打造面向DevOps场景的开源AI助手

今日&#xff0c;数澈软件Seal &#xff08;以下简称“Seal”&#xff09;宣布推出面向 DevOps 场景的 AI 助手 Appilot&#xff0c;这款产品将充分利用 AI 大语言模型的能力为用户提供变革性的部署和应用管理体验。Seal 此次发布的 Appilot 项目&#xff0c;可以让用户直接输入…

leetcode 22. 括号生成

2023.9.24 看到组合两个字&#xff0c;想到了回溯。 大致思路是将所有可能的组合列出来&#xff0c;通过中止条件筛选掉无效的括号。 第一个中止条件&#xff1a;如果右括号数量大于左括号&#xff0c;那括号肯定无效。 第二个中止条件&#xff1a;当左右括号数量相等&#x…

古代有没有电子元器件?

手机&#xff0c;电脑&#xff0c;电视等等电子产品&#xff0c;无时无刻充斥在我们的生活中&#xff0c;如果有一天突然没有了这些功能多样的电子产品&#xff0c;估计大部分人都会一时之间难以适应。 这就好比正在上网&#xff0c;结果突然被人断了网&#xff0c;导致无网络连…

Go语言入门篇

目录 一、基础数据类型 1.1 变量的定义方式 1.2 用%T输出变量的类型 二、复合数据类型 2.1 数组 2.1.2、数组的遍历 2.1.3 数组传参 2.2. 切片slice 2.2.1. 初始化切片 2.2.2. append向切片中追加元素 2.2.3. 切片的截取 2.3. map 2.3.1. map初始化 2.3.2. 添加和…

操作系统权限提升(三十)之数据库提权-SQL Server sp_oacreate+sp_oamethod(dba权限)提权

SQL Server sp_oacreate+sp_oamethod(dba权限)提权 sp_oacreate+sp_oamethod介绍 在xp_cmdshell被删除或不能利用是可以考虑利用sp_oacreate,利用前提需要sqlserver sysadmin账户服务器权限为system(sqlserver2019默认被降权为mssql)。sp_oacreate 是一个存储过程,可以…

【无标题】mysql 普通用户连接报错: MySql server has gone away

1、mysql 普通用户连接报错&#xff1a; MySql server has gone away 2、进入mysql错误日志位置查看输出日志显示错误为&#xff1a; [Warning] [MY-013130] [Server] Aborted connection 47 to db: unconnected user: tjcx host: 10.195.11.4 (init_connect command failed; …

基于docker进行Grafana + prometheus实现服务监听

基于docker进行Grafana Prometheus实现服务监听 Grafana安装Prometheus安装Jvm监控配置服务器主机监控(基础cpu&#xff0c;内存&#xff0c;磁盘&#xff0c;网络) Grafana安装 docker pull grafana/grafanamkdir /server/grafanachmod 777 /server/grafanadocker run -d -p…

汽车OTA

汽车OTA&#xff08;Over-The-Air&#xff09;技术是指通过无线网络对汽车进行软件升级、数据传输和远程诊断等功能的技术。随着汽车行业的数字化和智能化发展&#xff0c;OTA技术在汽车领域的应用越来越广泛&#xff0c;对于提高汽车性能、降低维修成本和提升用户体验具有重要…

Linux(ubuntu)系统更新后不能进入图形界面

最近需要跑一个深度学习的程序&#xff0c;把许久没用的ubuntu系统调了出来&#xff0c;手欠的我更新了一下系统&#xff0c;结果再启动&#xff0c;系统就只停留在光标闪动那里&#xff0c;不能看到图形界面了。网上查了一下&#xff0c;说是因为更新后&#xff0c;显卡驱动没…