深度解析 Android Matrix 变换(二):组合变换 pre、post

前言

在上一篇文章中,我们讲解了 Canvas 中单个变换的原理和效果,即缩放、旋转和平移。但是单个旋转仅仅是基础,Canvas 变换最重要的是能够随意组合各种变换以实现想要的效果。在这种情况下,就需要了解如何组合变换,以及组合变换背后的矩阵是如何计算出来的。

阅读本文之前,建议先阅读:深度解析 Android Matrix 变换(一):缩放 scale、旋转 rotate、平移 translate

在这篇文章中,我就带领大家理解组合变换以及其背后的原理。通过这篇文章,大家肯定能随意组合变换以达到想要的效果。

从一个点开始

在二维平面中,表示一个点只是需要其 x,y 坐标,但是在表换过程中,并不是使用两个值的表示方式,而是用齐次坐标来表示。简单来说,就是 x,y 坐标再加上一个 1 来表示。当用列向量来表示这个点的坐标时,因为其具有三行,因此可以与各种变换的矩阵相乘,因为矩阵都是 3*3 的,可以与 3*1 的向量相乘。

我们用 Ms 表示缩放矩阵,Mr 表示旋转矩阵,Mt 表示平移矩阵,如果对于一个点 P0,我们对它依次应用旋转平移和缩放,那么变换后得到的点 P1 为:

P1 = Ms * Mt * Mr * P0

注意相乘的顺序,我们必须从又往左相乘,但是由于矩阵乘法满足结合律,因此这三个矩阵可以合并为一个与 P0 相乘。

在了解了如何组合矩阵之后我们就从代码层面理解如何构建想要的组合矩阵。

构建矩阵

在上篇文章中,我们介绍过,缩放旋转和平移都有对应的 post 和 pre 版本。在这一节中,我们来介绍这两个种类的方法的具体意义:

  1. Matrix.preScale(float sx, float sy);
  2. Matrix.postScale(float sx, float sy);
  3. Matrix.preRotate(float degrees);
  4. Matrix.postRotate(float degrees);
  5. Matrix.preTranslate(float dx, float dy);
  6. Matrix.postTranslate(float dx, float dy);

在上面的方法中,我们先过滤掉 preXXXpostXXX 的方法名和参数,因为这些方法实际上都是通过方法名和参数来构建一个矩阵,然后 pre 或者 post 这个矩阵。也就是说,上述的方法可以总结为两个方法:

  1. Matrix.preConcat(Matrix other)
  2. Matrix.postConcat(Matrix other)

那么这两个方法有什么区别呢?说简单一点就是 pre 是左乘,post 是右乘。举个例子:

  • A.preConcat(B) 表示 A * B 结果设置到 A 中;
  • A.preConcat(B) 表示 B * A 结果设置到 A 中;

变换顺序

在构建矩阵时,变换顺序是非常重要的。上面只是对变换时矩阵的乘法进行了讲解,但是在实际编码时,我们通常是按照 先缩放,再旋转,最后平移 的顺序来进行的。

对于一个变换,我们必须考虑按照这个顺序来完成,特别是平移需要最后完成。否则后续的旋转或缩放会影响平移变换,变换的结果可能完全不同,甚至不符合直觉。

而按照这个变换做的好处是:

  • 缩放发生在局部坐标系,不影响旋转或平移的方向。
  • 旋转发生在缩放之后,不会影响缩放比例。
  • 平移最后执行,不会受到缩放或旋转的干扰。

实例

最后我们组合一个变换,并通过动画的形式演示变换过程。

我们还是以上篇文章的图片为基础:

我们先对这个图片进行缩放(1.5,0.5),再旋转30度,最后移动(500, 200)。

根据上面对构建矩阵的描述,我们通过下面的代码来生成变换矩阵:

Matrix matrix = new Matrix();
matrix.postScale(1.5F, 0.5F);
matrix.postRotate(30);
matrix.postTranslate(500, 200);

在将这个 matrix 应用到 Canvas 后,可以看到变换后的结果:

其实,对于某一个结果,可以通过多种方式来构造变换矩阵。例如这个变换,我们还可以通过下面的矩阵生成:

Matrix matrix = new Matrix();
matrix.setRotate(30);
matrix.preScale(1.5F, 0.5F);
matrix.postTranslate(500, 200);

既然同一个变换有多种构建方式,那么应该用哪种构建矩阵的方式呢?这里还是建议用复杂的方式,至少,别人越是看不懂,越是显得你的重要性。

最后,通过动画,来演示一下这个组合变换是如何生效的:

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

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

相关文章

Java并发编程之CountDownLatch

1. 基本原理 计数器 CountDownLatch 在创建时需要指定一个初始计数值。这个值通常代表需要等待完成的任务数或线程数。 等待与递减 等待:调用 await() 方法的线程会被阻塞,直到计数器变为 0。递减:每当一个任务完成后,应调用 cou…

C++|GLog开源库的使用 如何实现自定义类型消息日志

参考: C glog使用教程与代码演示 C第三方日志库Glog的安装与使用超详解 GLOG从入门到入门 glog 设置日志级别_glog C版本代码分析 文章目录 日志等级自定义消息创建使用宏定义 日志等级 在 glog 中,日志的严重性是通过 LogSeverity 来区分的&#xff0c…

FAQ - VMware vSphere Web 控制台中鼠标控制不了怎么办?

问题描述 在VMware vSphere vCenter Server 的 Web 控制台中新建了一台 Windows Server 2008 R2 虚拟机,但是鼠标进入控制台后,可以看见鼠标光标,但是移动却没有反应。 根因分析 暂无。 解决方案 选中虚拟机>操作>编辑设置>添加新…

Rust+WebAssembly:开启浏览器3D渲染新时代

引言 在当今的 Web 开发领域,随着用户对网页交互体验的要求日益提高,3D 渲染技术在 Web 应用中的应用愈发广泛。从沉浸式的 Web 游戏,到逼真的虚拟展示场景,3D 渲染引擎承担着将虚拟 3D 世界呈现到用户浏览器中的关键任务。其性能…

在小米AX6000中添加tailscale monitor

经过测试,发现小米路由器中的tailscale可能会因为某种原因状态异常, 为了让tailscale恢复正常,所以又写了monitor用来监控: #!/bin/sh# Define Tailscale related paths TAILSCALED_PATH"/tmp/tailscale/tailscale_1.80.3_a…

表达式括号匹配(stack)(信息学奥赛一本通-1353)

【题目描述】 假设一个表达式有英文字母(小写)、运算符(,—,∗,/)和左右小(圆)括号构成,以“ ”作为表达式的结束符。请编写一个程序检查表达式中的左右圆括号…

IM 基于 WebRtc 视频通信功能

IM(即时通讯)基于 WebRTC(Web Real-Time Communication,网页实时通讯) 原理 WebRTC 是一种支持网页浏览器进行实时语音通话或视频通话的技术,它提供了一组 JavaScript API,使得在浏览器之间无…

关于极端场景下,数据库更新与 MQ 消息一致性保障方案的详细总结

目录 一、核心问题场景 二、RocketMQ 事务消息方案 1. 核心机制 2. 执行流程 3. 关键优势 4. 局限性 三、消息表方案 1. 核心机制 2. 执行流程 3. 关键优势 4. 局限性 四、方案对比与选择 五、实施建议 六、总结 一、核心问题场景 当数据库更新后,若 MQ 消息未…

【设计模式】单件模式

七、单件模式 单件(Singleton) 模式也称单例模式/单态模式,是一种创建型模式,用于创建只能产生 一个对象实例 的类。该模式比较特殊,其实现代码中没有用到设计模式中经常提起的抽象概念,而是使用了一种比较特殊的语法结构&#x…

【redis】主从复制:拓扑结构、原理和psync命令解析

文章目录 拓扑一主一从相关问题 一主多从相关问题 树形主从结构相关问题 主从复制原理复制流程 psync 命令命令解析replicatonidoffset总结 运行流程 拓扑 若干个节点之间按照什么样的方式来进行组织连接 一主一从 都可以读,从节点可以帮主节点分担一部分的压力只…

[RoarCTF 2019]Easy Calc-3.23BUUCTF练习day5(2)

[RoarCTF 2019]Easy Calc-3.23BUUCTF练习day5(2) 解题过程 查看源码 发现calc.php页面,访问一下 分析代码 首先获取$_GET[num]的值并赋给变量$str。然后定义了一个黑名单数组$blacklist,包含了一系列被禁止的字符或转义字符,如空格、制表…

阻塞队列:原理、应用及实现

阻塞队列:原理、应用及实现 什么是阻塞队列以生产消费者模型形象地理解阻塞队列阻塞队列实现生产消费者模型模拟实现阻塞队列实现生产消费者模型 什么是阻塞队列 阻塞队列是一种特殊且实用的队列数据结构,它同样遵循 “先进先出” 的原则。与普通队列不…

【开源宝藏】30天学会CSS - DAY5 第五课 脉冲动画

以下是一个完整的渐进式教程,拆解如何用 HTML CSS 构建“Pulsar”水波脉冲动画。通过阅读,你将理解每个核心属性与关键帧如何配合,让一个小圆不断散发动态波纹,并且文字始终停留在圆心。 第 0 步:项目概览 文件结构示…

2060 裁纸刀

2060 裁纸刀 ⭐️难度:简单 🌟考点:2022、规律、思维 📖 📚 import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {static int N 100010…

TextView、AppCompatTextView和MaterialTextView该用哪一个?Android UI 组件发展史与演进对照表

在 Android 开发中,UI 组件一直在不断演进,从最初的原生组件,到 Support Library(AppCompat 兼容库),再到如今的 Material Design 组件。这篇文章将梳理 Android UI 组件的发展历史,并提供详细的…

python学习笔记--实现简单的爬虫(一)

任务:爬取豆瓣最受欢迎的250个电影的资料 链接:豆瓣电影 Top 250 用浏览器打开后,使用F12或鼠标右键--检查,查看网页的源代码,分析网页结构,如下图所示: 分析后得知: 1.电影名位于…

Postgresql 删除数据库报错

1、删除数据库时,报错存在其他会话连接 ## 错误现象,存在其他的会话连接正在使用数据库 ERROR: database "cs" is being accessed by other users DETAIL: There is 1 other session using the database.2、解决方法 ## 终止被删除数据库下…

self Attention为何除以根号dk?(全新角度)

全网最独特解析:self Attention为何除根号dk? 一、假设条件:查询向量和键向量服从正态分布 假设查询向量 q i q_i qi​和键向量 k j k_j kj​的每个分量均为独立同分布的随机变量,且服从标准正态分布,即:…

numpy学习笔记10:arr *= 2向量化操作性能优化

numpy学习笔记10:arr * 2向量化操作性能优化 在 NumPy 中,直接对整个数组进行向量化操作(如 arr * 2)的效率远高于显式循环(如 for i in range(len(arr)): arr[i] * 2)。以下是详细的解释: 1. …

Cursor+Claude-3.5生成Android app

一、Android Studio下载 https://developer.android.com/studio?hlzh-tw#get-android-studio 等待安装完成 二、新建工程 点击new project 选择Empty Activity 起一个工程名 当弹出这个框时 可以在settings里面选择No proxy 新建好后如下 点击右边模拟器&#xff0c…