《计算机图形学》第二课笔记-----二维变换的推导
前言:为什么这么突兀的把这一节内容放在了第二课,第一是因为我急于求成,第二是因为这一章节太重要了,这几乎是二维三维变换的最核心的东西,理解了这一章节内容,后面的就会像打通了任督二脉一样,so,那让我们开始吧,我们只用初中的知识把这一章说清楚
所谓的二维变换核心的东西其实就是求一个点绕一个点旋转后的位置
让我们对这段话进行一些约束,在一个去掉了Z轴的笛卡尔坐标系内,让我们再大白话点,在一个平面坐标系内,让我们再大白话点,就是小时候上数学课时候老师在黑板上化的那个一横一竖,上面一个箭头,前面一个箭头那种坐标系内
我们终于白话完了,正文开始,在一个坐标系内一个点绕着原点o旋转一定的度数,旋转到新的一个点,求这个点在这个坐标系内的位置,也就是新的点位的x’的长度和y’的长度。
结合下图描述了一个已知的点[x,y],沿原点o旋转了β度到达了新的点[x’,y’],求[x’,y’]的位置
我们需要了解的前提是[x,y]是已知的
那么x’也可以写成r*cos(α+β),因为我们是绕原点旋转,等于在绕原点画圆,so,每一个半径都应该是相等的,记住这个前提,后面要用。

x ′ = r ∗ c o s ( α + β ) x'=r*cos(α+β) x′=r∗cos(α+β)
上面这个式子应该很好理解,如果不理解可以只补一下三角函数的cos 和 sin即可
接下来我们展开这个式子,有人可能会在这里卡住一下,但这其实都是固定推导您只需复制展开前的式子问下一ai,他会说的比我清楚多了,总而言之如果您不想深究,不用理会为什么展开以后是这样子,直接过即可
x ′ = r ∗ c o s α ∗ c o s β − r ∗ s i n α ∗ s i n β x'=r*cosα*cosβ-r*sinα*sinβ x′=r∗cosα∗cosβ−r∗sinα∗sinβ
当我们观察展开的式子,这个时候神奇的事情发生了,r*cosα,不就是 r x r r\frac{x}{r} rrx,约分以后不就是x么
我们再观察r*sinα,那不就是 r y r r\frac{y}{r} rry,约分以后那不就是y么
然而x,y又是已知的,那不就是用已知的x,y去乘我们旋转的β角的cos和sin么,至此答案已经很清晰了,可以说这篇博客要说的已经说完了,您仅需初中知识就可以理解这一切,后续的剩下的推导也只是上述思考过程的重复,当然我们还是要写完这一切。
x ′ = x ∗ c o s β − y ∗ s i n β x'=x*cosβ-y*sinβ x′=x∗cosβ−y∗sinβ
y ′ = x ∗ s i n β + y ∗ c o s β y'=x*sinβ+y*cosβ y′=x∗sinβ+y∗cosβ
最后我们把他写成矩阵的形式就是下面的式子,我们需要注意的是矩阵乘法并不满足交换律,so,我们不能调换位置
[ c o s β − s i n β s i n β c o s β ] [ x y ] \begin{bmatrix} cosβ & -sinβ \\ sinβ & cosβ \\ \end{bmatrix}\begin{bmatrix} x\\ y\\ \end{bmatrix} [cosβsinβ−sinβcosβ][xy]
当然我们需要注意,当前我们讨论的是逆时针旋转这种情况,顺时针的推到过程在下下张图
那么剩下的顺时针推导我们就不写这么详细了,因为一切已经不言自明了,下一节我们就去到代码层面去实现它,平移我们也就不在此解释了,因为那些比这个容易理解的多。

原因如上,顺时针的推导就不赘述了

现在我们来到代码层面,UI框架选择c++ qt5,因为我更熟悉这个框架,而且如果我们仅仅知识为了演示二维变换的推导,qt5足够了,主要原因还是我足够熟悉。不用opengl是因为我们像尽其可能的展示细节,而不是直接调用显卡为我们实现好的接口。
头文件
#ifndef TWOCUBE_1_H // 防止头文件被重复包含
#define TWOCUBE_1_H#include <QWidget> // 包含QWidget类,用于创建窗口
#include <QTimer> // 包含QTimer类,用于定时器功能// TwoCube_1类继承自QWidget,用于创建自定义窗口
class TwoCube_1: public QWidget
{Q_OBJECT // 启用Qt的元对象系统,支持信号和槽机制public:// 构造函数,参数为父窗口指针TwoCube_1(QWidget* parent);// 析构函数~TwoCube_1();protected:// 重写paintEvent函数,用于处理窗口的绘制事件void paintEvent(QPaintEvent* event) override;private:QList<QPointF> rectPoints; // 用于存储矩形的顶点坐标(2D坐标)int angle = 0; // 旋转角度,初始化为0float rWidth = 300; // 矩形的宽度float rHeight = 180; // 矩形的高度
};#endif // TWOCUBE_1_H // 结束头文件定义
cpp文件
#include "twocube_1.h"
#include <QPainter>
#include <QDebug>
#include <cmath>// 构造函数,初始化窗口和矩形点集
TwoCube_1::TwoCube_1(QWidget* parent): QWidget(parent)
{// 设置窗口的最小大小为800x800setMinimumSize(800, 800);// 初始化矩形的四个顶点坐标rectPoints.append(QPointF(0,0)); // 左上角rectPoints.append(QPointF(0,rHeight)); // 左下角rectPoints.append(QPointF(rWidth,rHeight)); // 右下角rectPoints.append(QPointF(rWidth,0)); // 右上角// 创建定时器用于动画效果QTimer* timer = new QTimer(this);// 连接定时器的timeout信号到lambda表达式,每16ms触发一次connect(timer, &QTimer::timeout, this, [this]() {angle -= 1; // 每次更新角度减少1度,顺时针旋转update(); // 触发重绘事件});timer->start(16); // 定时器每16ms触发一次,约60fps
}// 析构函数
TwoCube_1::~TwoCube_1()
{// 析构函数为空,没有需要释放的资源
}// 重写paintEvent函数,处理窗口的绘制事件
void TwoCube_1::paintEvent(QPaintEvent *event)
{QPainter painter(this); // 创建QPainter对象,用于绘制// 将绘制原点移动到窗口中心painter.translate(width() / 2, height() / 2);// 设置画笔颜色为黑色,线宽为1painter.setPen(QPen(Qt::black, 1));// 绘制X轴和Y轴painter.drawLine(-width() / 2, 0, width(), 0); // X轴painter.drawLine(0, -height() / 2, 0, height()); // Y轴// 用于存储旋转后的矩形顶点QList<QPointF> tempRectPoints;// 对矩形的每个顶点进行旋转变换for (int i = 0; i < rectPoints.length(); i++){float x = rectPoints[i].x(); // 获取当前顶点的X坐标float y = rectPoints[i].y(); // 获取当前顶点的Y坐标// 计算旋转后的新坐标float newX = x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180);float newY = -x * sin(angle * M_PI / 180) + y * cos(angle * M_PI / 180);// 更新旋转后的坐标x = newX;y = newY;// 将旋转后的点添加到临时列表中QPointF tempPoint(x, y);tempRectPoints.append(tempPoint);}// 设置画笔颜色为绿色,线宽为5painter.setPen(QPen(Qt::green, 5));// 绘制旋转后的矩形painter.drawLine(tempRectPoints[0], tempRectPoints[1]); // 左边painter.drawLine(tempRectPoints[1], tempRectPoints[2]); // 下边painter.drawLine(tempRectPoints[2], tempRectPoints[3]); // 右边painter.drawLine(tempRectPoints[3], tempRectPoints[0]); // 上边
}
最后放上一张效果图
我们演示使用的是顺时针旋转,在一个定时器内每个16ms角度自身减1,重新触发绘制,这里一定要注意,一定是角度递减才是顺时针旋转,才能套用顺时针旋转的算法,让我们把代码摘出来再捋一遍
这行代码代表顺时针旋转
angle -= 1; // 每次更新角度减少1度,顺时针旋转
这段代码就是套用了顺时针旋转的算法,里面做了角度转弧度的处理,因为大部分编程语言只认弧度
// 计算旋转后的新坐标float newX = x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180);float newY = -x * sin(angle * M_PI / 180) + y * cos(angle * M_PI / 180);

相关文章:
《计算机图形学》第二课笔记-----二维变换的推导
前言:为什么这么突兀的把这一节内容放在了第二课,第一是因为我急于求成,第二是因为这一章节太重要了,这几乎是二维三维变换的最核心的东西,理解了这一章节内容,后面的就会像打通了任督二脉一样,…...
无需微调的对齐方法URIAL
无需微调的对齐方法URIAL 研究背景与目的:LLMs的对齐调优通常采用监督微调(SFT)和强化学习从人类反馈(RLHF),但LIMA研究表明少量示例的SFT也能实现较好对齐,暗示对齐调优可能存在“表面性质”。本研究旨在探究对齐调优对基础LLMs的具体影响,并提出不依赖SFT或RLHF的对齐…...
机器学习(七)
一,监督学习和无监督学习聚类的数据集比较: 监督学习: 数据集包括输入的数据和与之对应的标签 无监督学习: 数据集仅含有输入的数据,要求算法自己通过所给的数据集来确定决策边界 二,聚类(Clustering): 聚…...
利用labelimg实现yolov8数据集的制作
我们在使用yolov8进行物体检测识别的时候,由于其内置的n,s,m等模型只包含90多种物体(很多其他物品并未包含在其中),导致我们无法直接使用其模型进行视频或者图片的检测识别。这个时候,我们就需要自己制作数据集进行训练…...
【0x80070666】-已安装另一个版本...(Tableau 安装失败)
第一种是之前安装过tableau相关软件,但是没卸载干净。 方法1:卸载旧版本 打开 控制面板 → 程序和功能(或 添加/删除程序)。查找 Tableau Desktop,如果已安装旧版本,卸载它。重新启动电脑后再尝试安装。 …...
在rv1106上部署vue3
创建vue3项目 $ npm create vuelatest Need to install the following packages: create-vue3.15.1 Ok to proceed? (y) y > npx > create-vue┌ Vue.js - The Progressive JavaScript Framework │ ◇ 请输入项目名称: │ ip_cam │ ◇ 请选择要包含的…...
海量数据查询加速:Presto、Trino、Apache Arrow
1. 引言 在大数据分析场景下,查询速度往往是影响业务决策效率的关键因素。随着数据量的增长,传统的行存储数据库难以满足低延迟的查询需求,因此,基于列式存储、向量化计算等技术的查询引擎应运而生。本篇文章将深入探讨 Presto、Trino、Apache Arrow 三种主流的查询优化工…...
Word填写窗口功能详解:如何让文档填写更高效?
在日常办公中,我们经常需要让他人填写一些固定格式的文档,比如合同、申请表、调查问卷等。如果直接使用普通文本编辑,填写时可能会破坏排版,甚至修改了不该改动的内容。这时候,Word的填写窗口(即“内容控件…...
Oracle数据库存储结构--逻辑存储结构
数据库存储结构:分为物理存储结构和逻辑存储结构。 物理存储结构:操作系统层面如何组织和管理数据 逻辑存储结构:Oracle数据库内部数据组织和管理数据,数据库管理系统层面如何组织和管理数据 Oracle逻辑存储结构 数据库的逻…...
kali之nmap
kali之nmap Nmap(Network Mapper)是 Kali Linux 中最著名的网络扫描工具之一,广泛用于网络发现、端口扫描、服务识别、操作系统检测等任务。它是一个功能强大且灵活的开源工具,适用于渗透测试、网络管理和安全审计。 1. Nmap 的主…...
Kubernetes学习笔记-移除Nacos迁移至K8s
项目服务的配置管理和服务注册发现由原先的Nacos全面迁移到Kubernetes上。 一、移除Nacos 移除Nacos组件依赖。 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <…...
简单创建一个Django项目并配置neo4j数据库
创建项目,项目的文件夹就是项目的名称 创建项目的基本框架 安装djangorestframework 单击运行 查看浏览器运行效果: 运行效果如下: 创建应用(假如说是创建一个名为myapp的应用): python manage.py startapp myapp创建之后的…...
java实现智能家居控制系统——入门版
文章目录 一、需求二、业务分析三、具体实现创建一个功能接口,实现设备的开关创建一个家电类,作为功能接口的实现类,定义名字和状态分别创建电视机、洗衣机、电灯的类,继承家电类Tv类WashMachine类Lamp类 定义智能控制系统类&…...
k8s基础架构介绍
k8s基础架构介绍 k8s 是对容器进行编排的一种工具。通过k8s可以实现对容器的编排、部署、更新等 学习k8s之前,先了解相关的一些使用和配置k8s的一些工具。 k8s的常用工具 在 kubernetes 中,主要有三个日常使用的工具,这些工具使用 kube 前…...
VSCode C/C++ 开发环境完整配置及常见问题(自用)
这里主要记录了一些与配置相关的内容。由于网上教程众多,部分解决方法并不能完全契合我遇到的问题,因此我选择以自己偏好的方式,对 VSCode 进行完整的配置,并记录在使用过程中遇到的问题及解决方案。后续内容也会持续更新和完善。…...
QGraphicsDropShadowEffect 的效果不能同时被多个控件使用
问题的原因是 QGraphicsDropShadowEffect 不能同时被多个控件共享。每个 QGraphicsDropShadowEffect 实例只能绑定到一个控件上。如果你将同一个 taskbarShadow 同时设置给 ui->filemanager_memeryArea 和 ui->workspaceWidget,只有最后一个设置会生效。 解决…...
实用小工具——快速获取数据库时间写法
最近我遇到了一个比较棘手的问题:在工作中,各个项目所使用的数据库类型各不相同。这导致我习惯性地使用Oracle的SQL语句进行编写,但每次完成后都会遇到报错,最终才意识到项目的数据库并非Oracle。为了避免这种情况,我需…...
回文字串(信息学奥赛一本通-2044)
【题目描述】 输入一串字符,字符个数不超过100,且以“.”结束。 判断它们是否构成回文。 【输入】 一行字符串。 【输出】 是否为回文串。是输出“Yes”,否输出“No。” 【输入样例】 abccb 【输出样例】 No 【题解代码】 #include<bits/stdc.h> using names…...
用Maven创建只有POM文件的项目
使用 mvn 创建一个仅包含 pom.xml 文件的父项目,可以借助 maven-archetype-quickstart 原型,然后移除不必要的文件,或者直接通过命令生成最简的 pom.xml 文件。以下是具体操作步骤: 一、方法一:使用原型创建后清理 1…...
基于ssm的宠物医院信息管理系统(全套)
一、系统架构 前端:html | layui | vue | element-ui 后端:spring | springmvc | mybatis 环境:jdk1.8 | mysql | maven | tomcat | idea | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页…...
从 YOLOv1 到 YOLOv2:目标检测的进化之路
引言 你有没有想过,当你用手机拍一张照片,里面的人、车、狗是怎么被自动识别出来的?这背后靠的就是目标检测技术。目标检测是计算机视觉中的一个重要领域,它不仅要回答“图片里有什么”,还要告诉你“这些东西在哪里”…...
RTDETR融合[CVPR205]ARConv中的自适应矩阵卷积
RT-DETR使用教程: RT-DETR使用教程 RT-DETR改进汇总贴:RT-DETR更新汇总贴 《Adaptive Rectangular Convolution for Remote Sensing Pansharpening》 一、 模块介绍 论文链接:https://arxiv.org/pdf/2503.00467 代码链接:https:/…...
【linux】使用 crontab 新增定时任务
目录 1. 打开 crontab 编辑器2. 添加定时任务3. 保存并退出4. 确认定时任务 1. 打开 crontab 编辑器 1.在终端中输入以下命令: crontab -e2. 添加定时任务 1.在打开的 crontab 文件中,添加你的定时任务。2.假设我们希望每天的 5 点 30 分执行一个脚本…...
项目-个人博客测试报告
目录 一、项目背景 二、项目功能 三、测试计划 (1)功能测试 (2)自动化测试 (3)性能测试 一、项目背景 1、个人博客系统是一个操作简单的基于Spring前后端分离的项目,同时使用MySQL数据库来进…...
软考计算机知识-流水线
计算机流水线类似工业生产过程的流水线,在同一时间,m个部件进行不同的操作,完成对不同对象的处理。 理解重叠:让不同的指令在时间上重叠地解释。在解释第k条指令的操作完成之前,就可以开始解释第k1条指令。 题1&#…...
Linux驱动开发实战(五):Qt应用程序点RGB灯(保姆级快速入门!)
Linux驱动开发实战(五):Qt应用程序点RGB灯(保姆级快速入门!) 文章目录 Linux驱动开发实战(五):Qt应用程序点RGB灯(保姆级快速入门!)前…...
前端登录鉴权全解析:主流方案对比与实现指南
文章目录 一、常见登录鉴权方式概览1.1 主流方案对比1.2 技术特性对比 二、Session/Cookie方案2.1 实现原理2.2 代码实现2.3 优缺点分析 三、JWT方案3.1 实现原理3.2 代码实现3.3 优缺点分析 四、OAuth方案4.1 实现原理4.2 代码实现4.3 优缺点分析 五、SSO方案5.1 实现原理5.2 …...
【C++】每日一练(链表的中间结点)
本篇博客给大家带来的是用C语言来解答找中间结点! 🐟🐟文章专栏:每日一练 🚀🚀若有问题评论区下讨论,我会及时回答 ❤❤欢迎大家点赞、收藏、分享! 今日思想:不服输的…...
『大模型笔记』什么是MCP?将AI智能体与数据库和API集成
什么是MCP?将AI智能体与数据库和API集成 文章目录 MCP 由多个组件组成,最重要的有以下三个:MCP 体系结构MCP 的实际应用示例为什么要关注 MCP?官方MCP架构图总结如果你正在构建 AI 智能体,可能听说过 MCP(Model Context Protocol,模型上下文协议)。MCP 是一个开源标准…...
使用Redis如何实现分布式锁?(超卖)
分布式锁概念 在多线程环境下,为了保证数据的线程安全,锁保证同一时刻,只有一个可以访问和更新共享数据。在单机系统我们可以使用 synchronized 锁、Lock 锁保证线程安全。 synchronized 锁是 Java 提供的一种内置锁,在单个 JVM …...
