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

news/2023/11/29 6:08:26

在已知矩形中心点、长宽和旋转角度(定义为矩形最长边与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.ppmy.cn/news/1122759.html

相关文章

提升群辉AudioStation音乐体验,实现公网音乐播放

文章目录 本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是本教程使用环境&#xff1a;1 群晖系统安装audiostation套件2 下载移动端app3 内网穿透&#xff0c;映射至公网 很多老铁想在上班路上听点喜欢的歌或者相声解解闷儿&#xff0c;于是打开手…

【List篇】LinkedList 详解

目录 成员变量属性构造方法add(), 插入节点方法remove(), 删除元素方法set(), 修改节点元素方法get(), 取元素方法ArrayList 与 LinkedList的区别Java中的LinkedList是一种实现了List接口的 双向链表数据结构。链表是由一系列 节点(Node)组成的,每个节点包含了指向 上一个…

云上亚运:所使用的高新技术,你知道吗?

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 目录 前言 一.什么是云上亚运会 二.为什么要使用云…

remote: The project you were looking for could not be found

git拉取公司项目时报错&#xff1a; remote: The project you were looking for could not be found 发生这个问题的原因&#xff0c;在于git账号可能并未真正登录。 我们可以通过打开电脑的凭据管理器&#xff0c;查看git当前的登录是否正常。 参考链接&#xff1a;参考

python经典百题之删除指定字母

题目&#xff1a;删除一个字符串中的指定字母&#xff0c;如&#xff1a;字符串 “aca”&#xff0c;删除其中的 a 字母 程序分析 我们需要编写一个程序&#xff0c;删除字符串中的指定字母。给定一个字符串和要删除的字母&#xff0c;我们需要将字符串中的指定字母全部删除。…

C语言数组和指针笔试题(四)(一定要看)

目录 二维数组例题一例题二例题三例题四例题五例题六例题七例题八例题九例题十例题十一 结果 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412;个人主页 &#x1f978;&#x1f978;&#x1f978;C语言 &#x1f43f;️…

flarum 论坛 User Statistics插件修改

此插件在中国使用日期不是很理想&#xff0c;于是决定修改代码 下面是插件信息&#xff1a; User Statistics A Flarum extension. Add some user statistics in flarum posts, this extension require clarkwinkelmann/flarum-ext-likes-received and will be installed au…

Linux安装包 | Git使用 | NFC搭建

dpgt使用 当谈到基于 Debian 的操作系统中的软件包管理工具时&#xff0c;dpkg 是一个重要的工具。它是 Debian 系统中用于安装、升级、配置和卸载软件包的命令行工具。以下是对 dpkg 的详细介绍&#xff1a; 软件包管理&#xff1a;dpkg 可以管理系统中的软件包。它可以安装单…

SLAM从入门到精通(用c++实现机器人运动控制)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前的一篇文章&#xff0c;我们知道了可以通过wpr_simulation包仿真出机器人和现场环境。如果需要控制机器人&#xff0c;这个时候就需要rqt_robo…

pom文件被忽略

项目场景&#xff1a; 在idea中创建了一个项目&#xff0c;新建了一个模块 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 发现新建的模块建错了&#xff0c;然后移除模块&#xff0c;删除相关文件后&#xff0c;又新建一个相同名字的模块时&#xff0c;出…

Spring Boot的新篇章:探索2.0版的创新功能

文章目录 引言1. Spring Boot 2.0的响应式编程2. 自动配置的改进3. Spring Boot 2.0的嵌入式Web服务器4. Spring Boot 2.0的Actuator端点5. Spring Boot 2.0的Spring Data改进6. Spring Boot 2.0的安全性增强7. Spring Boot 2.0的监控和追踪8. Spring Boot 2.0的测试改进结论 &…

拓扑排序【学习算法】

拓扑排序【学习算法】 前言版权推荐拓扑排序核心思想207. 课程表解法一解法二 最后 前言 2023-9-24 15:32:23 以下内容源自《【学习算法】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是https://blog.csd…

多进程编程- POSIX无名信号量

基本概念 无名信号量&#xff08;也称为匿名信号量&#xff09;是一个同步原语&#xff0c;通常用于线程之间的同步&#xff0c;而不是进程之间。与命名信号量&#xff08;用于进程间同步&#xff09;相比&#xff0c;无名信号量的生命周期通常受限于创建它的进程&#xff0c;…

35.浅谈贪心算法

概述 相信大家或多或少都对贪心算法有所耳闻&#xff0c;今天我们从一个应用场景展开 假设存在下面需要付费的广播台&#xff0c;以及广播台信号可以覆盖的地区。 如何选择最少的广播台&#xff0c;让所有的地区都可以接收到信号&#xff1f; 广播台覆盖地区k1北京、上海、天津…

wordpress使用category order and taxonomy terms order插件实现分类目录的拖拽排序

文章目录 引入实现效果安装插件使用插件 引入 使用docker快速搭建wordpress服务&#xff0c;并指定域名访问 上一节我们使用docker快速搭建了wordpress服务&#xff0c;可以看到基础的wordpress服务已经集成基础的用户管理、文章发布、页面编辑、文章分类等功能&#xff0c;但…

C++ -- 类型转换

目录 C语言中的类型转换 为什么C需要四种类型转换 C 类型转换 static_cast reinterpret_cast const_cast 添加关键字 volatile dynamic_cast 补充 RTTI 总结 C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型…

华为OD机试真题-会议接待-2023年OD统一考试(B卷)

题目描述: 某组织举行会议,来了多个代表团同时到达,接待处只有一辆汽车,可以同时接待多个代表团,为了提高车辆利用率,请帮接待员计算可以坐满车的接待方案,输出方案数量。 约束: 1、一个团只能上一辆车,并且代表团人数(代表团数量小于30,每个代表团人数小于30)小于…

SpringCloud Alibaba 入门到精通 - Sentinel

SpringCloud Alibaba 入门到精通 - Sentinel 一、基础结构搭建1.父工程创建2.子工程创建 二、Sentinel的整合SpringCloud1.微服务可能存在的问题2.SpringCloud集成Sentinel搭建Dashboard3 SpringCloud 整合Sentinel 三、服务降级1 服务降级-Sentinel2 Sentinel 整合 OpenFeign3…

安装k8s集群

一、前置环境配置 安装两台centos 实验环境&#xff0c;一台pc配有docker环境&#xff0c;有两个centsos7容器&#xff0c;其中一个容器作为master&#xff0c;一个作为node。如果master与node都是用默认端口&#xff0c;会存在冲突&#xff0c;所以在此基础上做细微的调整。…

秋招面经记录

MySQL 9.Mysql中有1000万条数据&#xff0c;每次查询10条&#xff0c;该如何优化&#xff08;答&#xff1a;Limit子查询优化&#xff09; 10.有了解过mysql索引吗 11.项目中使用到索引的情况&#xff08;答&#xff1a;覆盖索引&#xff0c;避免回表&#xff09; 12.B树和b…
最新文章