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

news2025/1/11 2:51:50

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

template <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.coloradmin.cn/o/1037619.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

贴片电容耐压值选取和特性(包含实际电路和PCB)

一、一般电容的特性 ①容值大的电容&#xff0c;一般通低频率&#xff1b;  ②容值小的电容&#xff0c;一般通高频率。   注&#xff1a;详细请看这位博主的篇文章&#xff1a; 大电容为什么虑低频小电容为什么又虑高频?(个人整理) 二、贴片电容的耐压选取 ①贴片电容有2…

“构建完善的用户认证与数据交互系统“

目录 引言1.ElementUI完成登录注册1. 登录页面设计与实现2. 注册页面设计与实现 2.axios之get请求3.axios之post请求4.跨域问题的解决方案5.总结 引言 在现代Web应用程序开发中&#xff0c;用户认证和数据交互是至关重要的功能。本文将介绍如何使用ElementUI、axios和解决跨域…

PyTorch 模型性能分析与优化--第1部分

一、说明 这篇文章的重点将是GPU上的PyTorch培训。更具体地说&#xff0c;我们将专注于 PyTorch 的内置性能分析器 PyTorch Profiler&#xff0c;以及查看其结果的方法之一&#xff0c;即 PyTorch Profiler TensorBoard 插件。 二、深度框架 训练深度学习模型&#xff0c;尤其是…

Understanding Host Network Stack Overheads论文阅读笔记

RFS (Receive Flow Steering) RFS&#xff08;Receive flow steering&#xff09;和 RPS 配合使用。RPS 试图在 CPU 之间平衡收包&#xff0c;但是没考虑 数据的本地性问题&#xff0c;如何最大化 CPU 缓存的命中率。RFS 将属于相同 flow 的包送到相同的 CPU 进行处理&#xf…

通用深度模型UniverSeg:分割医疗图像

医学图像分割是医学分析的中心问题。它很昂贵&#xff0c;需要大量数据&#xff0c;而且是特定于任务的。如果我们可以拥有一个可以应用于任何分割任务的模型怎么办&#xff1f; 医学图像分割&#xff1a;为什么我们需要以及为什么它很困难&#xff1f; 分割的例子。一般来说&…

2808. 使循环数组所有元素相等的最少秒数;1015. 可被 K 整除的最小整数;1001. 网格照明

2808. 使循环数组所有元素相等的最少秒数 核心思想&#xff1a;枚举每个元素作为相等元素最多需要多少秒&#xff0c;然后维护它的最小值。最多需要多少秒是怎么计算的&#xff0c;我们可以把相等值的下标拿出来&#xff0c;然后你会发现两个相邻下标&#xff08;相邻下标只的…

three.js——宝马汽车产品展示

宝马汽车产品展示 前言效果图1、创建基本设置(场景和背景等&#xff09;2、加载模型和展厅灯光3、添加点击事件 前言 宝马汽车产品展示和原理讲解 演示网址&#xff1a;http://f-h-y.gitee.io/bmw-car-display-project/#/ 效果图 1、创建基本设置(场景和背景等&#xff09; …

ChatGPT详细搭建教程+支持AI绘画

一、AI创作系统 SparkAi系统是基于很火的GPT提问进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT系统&#xff1f;小编这里写一个详细图文教程吧&#x…

The 2023 ICPC Asia Regionals Online Contest (1) E. Magical Pair(数论 欧拉函数)

题目 T(T<10)组样例&#xff0c;每次给出一个n(2<n<1e18)&#xff0c; 询问多少对&#xff0c;满足 答案对998244353取模&#xff0c;保证n-1不是998244353倍数 思路来源 OEIS、SSerxhs、官方题解 2023 ICPC 网络赛 第一场简要题解 - 知乎 题解 官方题解还没有…

【Springboot】整合Xxl-job

目录 一&#xff0c;下载1.1 官方文档地址1.2 源码仓库地址 二&#xff0c; 配置部署 "调度中心"2.1 初始化 "调度数据库"2.2 修改配置文件2.3 启动调度中心 三&#xff0c;springboot项目整合3.1 添加依赖3.2 修改配置文件3.3 执行器组件配置4.4 编写测试…

雷达编程实战之静态杂波滤除与到达角估计

雷达中经过混频的中频信号常常混有直流分量等一系列硬件设计引入的固定频率杂波&#xff0c;我们称之位静态杂波&#xff0c;雷达信号处理需要把这些静态杂波滤除从而有效的提高信噪比&#xff0c;实现准确的目标检测功能。 目标的到达角估计作为常规车载雷达信号处理的末端&am…

【数据结构】什么是数据结构?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 数据结构的定义 数据结构(Data Structure)是计算机存储,组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合. 这么讲可能有些抽象,放一张图大家可能好理解一点…

Python 打印素数

"""打印素数介绍&#xff1a;素数是指只有两个正因数&#xff08;1和它本身&#xff09;的自然数&#xff0c;而且必须大于1。例如&#xff1a;2、3、5、7、11、13、17、19、23、29等等都是素数。小于2的数不是素数&#xff0c;因为它没有两个正因数。例如&…

编译原理龙书-词法分析

词法分析 词法分析器的作用 词法分析器的主要任务是读入源程序的输入字符&#xff0c;将它们组成词素&#xff0c;生成并输出一个词法单元序列&#xff0c;每个词法单元对应于一个词素 有时&#xff0c;词法分析器可以分成两个级联的处理阶段&#xff1a; 扫描阶段主要负责完…

Base64编码与打印标签(label)实例

文章目录 前言一、Base64 的简单认识二、Base64的使用场景三、 Base64的解码注意问题举个最近与后端配合的例子 四、 base64 转换为arrayBuffer&#xff0c;blod总结 前言 文件格式转换&#xff0c;在日常编程尤为常见&#xff0c;例如pdf文件大小的压缩存储&#xff0c;前端用…

Arduino驱动MMA7260三轴加速度传感器(惯性测量传感器篇)

目录 1、传感器特性 2、控制器和传感器连线图 3、驱动程序 Arduino驱动MMA7260三轴加速度传感器芯片,可以应用到摩托车和汽车放倒报警、遥控航模、游戏手柄、人形机器人跌倒检测、硬盘冲击保护、倾斜度测量等场合。 1

Java初级到中级:技术提升的策略与方法

文章目录 1 问题背景2 前言3 方法 1 问题背景 截至2023年7月&#xff0c;笔者已经毕业2年了&#xff0c;每天都在搬砖打螺丝&#xff0c;自我感觉需要沉淀、思考、总结一下。思考和总结一下如何从Java初级提升到Java中级&#xff0c;需要掌握一些什么方法论&#xff0c;有效地去…

使用命令行快速创建Vite项目

一、构建项目 在终端中使用如下命令行&#xff1a; npm create vite 二、定义项目名称 三、选择项目类型 Vanilla是我们常用的JavaScript&#xff0c;Vue和React是常用前端框架&#xff0c;可以根据自己的需要进行选择 通过上下键进行选择&#xff0c;按下回车进行确认 创建…

CMU15-445 format\clang-format\clang-tidy 失败

CMU15-445 format\clang-format\clang-tidy 失败 问题修改 问题 -- Setting build type to Debug as none was specified. -- Youre using Clang 14.0.0 CMake Warning at CMakeLists.txt:67 (message):BusTub/main couldnt find clang-format.CMake Warning at CMakeLists.tx…

VS2019创建GIt仓库时剔除文件或目录

假设本地有解决方案“SomeSolution” 1、首先”团队资源管理器“-“创建Git存储库”&#xff0c;选择“仅限本地”、“创建” VS会在解决方案目录下自动生成.gitattributes、.gitignore 2、编辑gitignore&#xff0c;直接拖到VS里或者用记事本打开。添加要剔除的文件或文件夹…