[學習] 笛卡爾坐標系的任意移動與旋轉詳解

笛卡爾坐標系的任意移動與旋轉詳解

文章目錄

      • 笛卡爾坐標系的任意移動與旋轉詳解
        • **1. 笛卡爾坐標系基礎**
        • **2. 坐標變換原理**
          • **2.1 平移變換**
          • **2.2 旋轉變換**
        • **3. 組合變換**
      • Python仿真與動態展示
        • **動畫說明**:
        • **關鍵數學原理**:

1. 笛卡爾坐標系基礎

笛卡爾坐標系用(x,y)(x,y)(x,y)表示平面內任意點的位置,原點為(0,0)(0,0)(0,0)。幾何圖形可視為點的集合。

2. 坐標變換原理
2.1 平移變換

將圖形整體移動(tx,ty)(t_x, t_y)(tx?,ty?)
{x′=x+txy′=y+ty\begin{cases} x' = x + t_x \\ y' = y + t_y \end{cases}{x=x+tx?y=y+ty??

矩陣形式
[x′y′]=[xy]+[txty]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} t_x \\ t_y \end{bmatrix} [xy?]=[xy?]+[tx?ty??]

2.2 旋轉變換
  • 繞原點旋轉(逆時針為正方向):
    {x′=xcos?θ?ysin?θy′=xsin?θ+ycos?θ\begin{cases} x' = x \cos \theta - y \sin \theta \\ y' = x \sin \theta + y \cos \theta \end{cases}{x=xcosθ?ysinθy=xsinθ+ycosθ?

    矩陣形式
    [x′y′]=[cos?θ?sin?θsin?θcos?θ][xy]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} [xy?]=[cosθsinθ??sinθcosθ?][xy?]

  • 繞任意點(cx,cy)(c_x, c_y)(cx?,cy?)旋轉

    1. 平移使旋轉中心到原點
    2. 繞原點旋轉
    3. 平移回原位置
      [x′y′]=[cos?θ?sin?θsin?θcos?θ][x?cxy?cy]+[cxcy]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{bmatrix} \begin{bmatrix} x - c_x \\ y - c_y \end{bmatrix} + \begin{bmatrix} c_x \\ c_y \end{bmatrix} [xy?]=[cosθsinθ??sinθcosθ?][x?cx?y?cy??]+[cx?cy??]
3. 組合變換

復雜變換可分解為平移與旋轉的序列操作,矩陣乘法滿足結合律:
Ttotal=Ttranslate?RrotateT_{\text{total}} = T_{\text{translate}} \cdot R_{\text{rotate}}Ttotal?=Ttranslate??Rrotate?


Python仿真與動態展示

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.patches import Polygon
import matplotlib# 設置全局字體大小
matplotlib.rcParams.update({'font.size': 12})# 定義三角形頂點 (齊次坐標)
triangle = np.array([[0, 0, 1], [1, 0, 1], [0.5, 1, 1]])# 變換矩陣函數
def translation_matrix(tx, ty):return np.array([[1, 0, tx],[0, 1, ty],[0, 0, 1]])def rotation_matrix(theta):c, s = np.cos(theta), np.sin(theta)return np.array([[c, -s, 0],[s, c, 0],[0, 0, 1]])def rotation_around_point(theta, cx, cy):T1 = translation_matrix(-cx, -cy)R = rotation_matrix(theta)T2 = translation_matrix(cx, cy)return T2 @ R @ T1# 創建三個獨立的畫布
fig1, ax1 = plt.subplots(figsize=(8, 6))
fig2, ax2 = plt.subplots(figsize=(8, 6))
fig3, ax3 = plt.subplots(figsize=(8, 6))# 設置每個畫布的標題和坐標范圍
fig1.suptitle('平移變換演示', fontsize=16)
fig2.suptitle('繞原點旋轉變換演示', fontsize=16)
fig3.suptitle('繞任意點旋轉變換演示', fontsize=16)# 設置坐標范圍
for ax in [ax1, ax2, ax3]:ax.set_xlim(-3, 4)ax.set_ylim(-3, 4)ax.grid(True, linestyle='--', alpha=0.7)ax.set_aspect('equal')ax.set_xlabel('X軸')ax.set_ylabel('Y軸')# 初始化每個畫布的三角形
triangle1 = Polygon(triangle[:, :2], fill=None, edgecolor='blue', linewidth=2.5, alpha=0.9)
triangle2 = Polygon(triangle[:, :2], fill=None, edgecolor='blue', linewidth=2.5, alpha=0.9)
triangle3 = Polygon(triangle[:, :2], fill=None, edgecolor='blue', linewidth=2.5, alpha=0.9)ax1.add_patch(triangle1)
ax2.add_patch(triangle2)
ax3.add_patch(triangle3)# 添加頂點標簽
def add_vertex_labels(ax, vertices):ax.text(vertices[0, 0], vertices[0, 1], 'A', fontsize=14, ha='right', va='bottom', weight='bold', color='darkred', bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))ax.text(vertices[1, 0], vertices[1, 1], 'B', fontsize=14, ha='left', va='bottom', weight='bold', color='darkgreen', bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))ax.text(vertices[2, 0], vertices[2, 1], 'C', fontsize=14, ha='center', va='top', weight='bold', color='darkblue', bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))add_vertex_labels(ax1, triangle)
add_vertex_labels(ax2, triangle)
add_vertex_labels(ax3, triangle)# 創建軌跡線 (每個頂點一種顏色)
trail_length = 50  # 拖尾軌跡長度# 使用漸變色軌跡
traj1_A, = ax1.plot([], [], 'r-', lw=2.0, alpha=1.0, label='A')
traj1_B, = ax1.plot([], [], 'g-', lw=2.0, alpha=1.0, label='B')
traj1_C, = ax1.plot([], [], 'b-', lw=2.0, alpha=1.0, label='C')
ax1.legend(title='頂點軌跡', loc='upper right', fontsize=10)traj2_A, = ax2.plot([], [], 'r-', lw=2.0, alpha=1.0, label='A')
traj2_B, = ax2.plot([], [], 'g-', lw=2.0, alpha=1.0, label='B')
traj2_C, = ax2.plot([], [], 'b-', lw=2.0, alpha=1.0, label='C')
ax2.legend(title='頂點軌跡', loc='upper right', fontsize=10)traj3_A, = ax3.plot([], [], 'r-', lw=2.0, alpha=1.0, label='A')
traj3_B, = ax3.plot([], [], 'g-', lw=2.0, alpha=1.0, label='B')
traj3_C, = ax3.plot([], [], 'b-', lw=2.0, alpha=1.0, label='C')
ax3.legend(title='頂點軌跡', loc='upper right', fontsize=10)# 存儲軌跡數據
traj_data1 = {'A': {'x': [], 'y': []}, 'B': {'x': [], 'y': []}, 'C': {'x': [], 'y': []}}
traj_data2 = {'A': {'x': [], 'y': []}, 'B': {'x': [], 'y': []}, 'C': {'x': [], 'y': []}}
traj_data3 = {'A': {'x': [], 'y': []}, 'B': {'x': [], 'y': []}, 'C': {'x': [], 'y': []}}# 平移動畫更新函數
def update_translation(frame):# 平移參數tx = frame * 0.1ty = frame * 0.05M = translation_matrix(tx, ty)# 應用變換transformed = (M @ triangle.T).T# 更新三角形triangle1.set_xy(transformed[:, :2])# 更新軌跡數據 - 拖尾方式,保留最近50個點# 頂點Atraj_data1['A']['x'].append(transformed[0, 0])traj_data1['A']['y'].append(transformed[0, 1])if len(traj_data1['A']['x']) > trail_length:traj_data1['A']['x'] = traj_data1['A']['x'][-trail_length:]traj_data1['A']['y'] = traj_data1['A']['y'][-trail_length:]# 頂點Btraj_data1['B']['x'].append(transformed[1, 0])traj_data1['B']['y'].append(transformed[1, 1])if len(traj_data1['B']['x']) > trail_length:traj_data1['B']['x'] = traj_data1['B']['x'][-trail_length:]traj_data1['B']['y'] = traj_data1['B']['y'][-trail_length:]# 頂點Ctraj_data1['C']['x'].append(transformed[2, 0])traj_data1['C']['y'].append(transformed[2, 1])if len(traj_data1['C']['x']) > trail_length:traj_data1['C']['x'] = traj_data1['C']['x'][-trail_length:]traj_data1['C']['y'] = traj_data1['C']['y'][-trail_length:]# 更新軌跡線traj1_A.set_data(traj_data1['A']['x'], traj_data1['A']['y'])traj1_B.set_data(traj_data1['B']['x'], traj_data1['B']['y'])traj1_C.set_data(traj_data1['C']['x'], traj_data1['C']['y'])# 更新頂點標簽位置ax1.texts[0].set_position((transformed[0, 0], transformed[0, 1]))ax1.texts[1].set_position((transformed[1, 0], transformed[1, 1]))ax1.texts[2].set_position((transformed[2, 0], transformed[2, 1]))ax1.set_title(f"平移: tx={tx:.1f}, ty={ty:.1f}", fontsize=14)return triangle1, traj1_A, traj1_B, traj1_C# 繞原點旋轉動畫更新函數(360°持續旋轉)
def update_rotation_origin(frame):# 旋轉參數 - 持續旋轉theta = frame * 0.05  # 每幀增加0.05弧度# 計算旋轉矩陣M = rotation_matrix(theta)# 應用變換transformed = (M @ triangle.T).T# 更新三角形triangle2.set_xy(transformed[:, :2])# 更新軌跡數據 - 拖尾方式,保留最近50個點# 頂點Atraj_data2['A']['x'].append(transformed[0, 0])traj_data2['A']['y'].append(transformed[0, 1])if len(traj_data2['A']['x']) > trail_length:traj_data2['A']['x'] = traj_data2['A']['x'][-trail_length:]traj_data2['A']['y'] = traj_data2['A']['y'][-trail_length:]# 頂點Btraj_data2['B']['x'].append(transformed[1, 0])traj_data2['B']['y'].append(transformed[1, 1])if len(traj_data2['B']['x']) > trail_length:traj_data2['B']['x'] = traj_data2['B']['x'][-trail_length:]traj_data2['B']['y'] = traj_data2['B']['y'][-trail_length:]# 頂點Ctraj_data2['C']['x'].append(transformed[2, 0])traj_data2['C']['y'].append(transformed[2, 1])if len(traj_data2['C']['x']) > trail_length:traj_data2['C']['x'] = traj_data2['C']['x'][-trail_length:]traj_data2['C']['y'] = traj_data2['C']['y'][-trail_length:]# 更新軌跡線traj2_A.set_data(traj_data2['A']['x'], traj_data2['A']['y'])traj2_B.set_data(traj_data2['B']['x'], traj_data2['B']['y'])traj2_C.set_data(traj_data2['C']['x'], traj_data2['C']['y'])# 更新頂點標簽位置ax2.texts[0].set_position((transformed[0, 0], transformed[0, 1]))ax2.texts[1].set_position((transformed[1, 0], transformed[1, 1]))ax2.texts[2].set_position((transformed[2, 0], transformed[2, 1]))# 顯示當前角度(轉換為度數)degrees = np.degrees(theta) % 360ax2.set_title(f"繞原點旋轉: {degrees:.0f}°", fontsize=14)return triangle2, traj2_A, traj2_B, traj2_C# 繞任意點旋轉動畫更新函數(360°持續旋轉)
def update_rotation_arbitrary(frame):# 旋轉參數 - 持續旋轉theta = frame * 0.05  # 每幀增加0.05弧度cx, cy = 1.5, 0.5  # 旋轉中心# 標記旋轉中心(只在第一幀標記)if frame == 0:ax3.plot(cx, cy, 'ro', markersize=10, alpha=0.9)ax3.text(cx, cy+0.4, '旋轉中心', ha='center', va='bottom', fontsize=12, weight='bold', color='red',bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', boxstyle='round,pad=0.2'))# 計算繞任意點旋轉的變換矩陣M = rotation_around_point(theta, cx, cy)# 應用變換transformed = (M @ triangle.T).T# 更新三角形triangle3.set_xy(transformed[:, :2])# 更新軌跡數據 - 拖尾方式,保留最近50個點# 頂點Atraj_data3['A']['x'].append(transformed[0, 0])traj_data3['A']['y'].append(transformed[0, 1])if len(traj_data3['A']['x']) > trail_length:traj_data3['A']['x'] = traj_data3['A']['x'][-trail_length:]traj_data3['A']['y'] = traj_data3['A']['y'][-trail_length:]# 頂點Btraj_data3['B']['x'].append(transformed[1, 0])traj_data3['B']['y'].append(transformed[1, 1])if len(traj_data3['B']['x']) > trail_length:traj_data3['B']['x'] = traj_data3['B']['x'][-trail_length:]traj_data3['B']['y'] = traj_data3['B']['y'][-trail_length:]# 頂點Ctraj_data3['C']['x'].append(transformed[2, 0])traj_data3['C']['y'].append(transformed[2, 1])if len(traj_data3['C']['x']) > trail_length:traj_data3['C']['x'] = traj_data3['C']['x'][-trail_length:]traj_data3['C']['y'] = traj_data3['C']['y'][-trail_length:]# 更新軌跡線traj3_A.set_data(traj_data3['A']['x'], traj_data3['A']['y'])traj3_B.set_data(traj_data3['B']['x'], traj_data3['B']['y'])traj3_C.set_data(traj_data3['C']['x'], traj_data3['C']['y'])# 更新頂點標簽位置ax3.texts[0].set_position((transformed[0, 0], transformed[0, 1]))ax3.texts[1].set_position((transformed[1, 0], transformed[1, 1]))ax3.texts[2].set_position((transformed[2, 0], transformed[2, 1]))# 顯示當前角度(轉換為度數)degrees = np.degrees(theta) % 360ax3.set_title(f"繞點({cx},{cy})旋轉: {degrees:.0f}°", fontsize=14)return triangle3, traj3_A, traj3_B, traj3_C# 創建三個獨立的動畫
ani1 = FuncAnimation(fig1, update_translation, frames=30, interval=50, blit=True)
# 旋轉動畫設置為無限循環
ani2 = FuncAnimation(fig2, update_rotation_origin, frames=200, interval=50, blit=True, repeat=True)
ani3 = FuncAnimation(fig3, update_rotation_arbitrary, frames=200, interval=50, blit=True, repeat=True)# 調整窗口位置避免重疊
fig1.canvas.manager.window.wm_geometry("+100+100")
fig2.canvas.manager.window.wm_geometry("+600+100")
fig3.canvas.manager.window.wm_geometry("+1100+100")plt.tight_layout()
plt.show()
動畫說明
  1. 平移階段(0-30幀):三角形沿向量(2,1.5)(2,1.5)(2,1.5)移動
    在這里插入圖片描述

  2. 繞原點旋轉(31-60幀):三角形繞(0,0)(0,0)(0,0)逆時針旋轉360°
    在這里插入圖片描述

  3. 繞任意點旋轉(61-90幀):三角形繞紅點(1.5,0.8)(1.5,0.8)(1.5,0.8)逆時針旋轉360°
    在這里插入圖片描述

關鍵數學原理
  • 平移是向量加法
  • 旋轉是矩陣乘法
  • 繞任意點旋轉 = 平移至原點 → 旋轉 → 平移回原位

通過組合基本變換,可實現復雜剛體運動仿真,廣泛應用于機器人學、計算機圖形學等領域。


研究學習不易,點贊易。
工作生活不易,收藏易,點收藏不迷茫 :)


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915645.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915645.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915645.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

論文筆記:Parameter Competition Balancing for Model Merging

neurips 20241 intro近年來,模型融合(model merging)技術迅速發展,使得可以將多個分別針對不同任務微調后的模型直接集成為一個統一模型,從而實現多任務處理能力,而無需重新訪問原始訓練數據。然而&#xf…

逆向難度真相:僅用IDA靜態分析的極限挑戰

逆向難度真相:僅用IDA靜態分析的極限挑戰 純IDA逆向難度重排(從難到易) Python > Go > Java > E語言 > CPython (地獄級難度) IDA困境: 主邏輯完全封裝在PYZ/PYC資源中,IDA無法解析字節碼結構字符串表只顯…

vxe-table 通過配置 ajax 方式自動請求數據,適用于簡單場景的列表

vxe-table 通過配置 ajax 方式自動請求數據,適用于簡單場景的列表 當系統中很多頁面都是簡單列表時,每次都要手動去請求接口后再賦值,過程就會比較冗余繁瑣。解決方式一般就是將封裝一下。本章的方式是通過 vxe-grid 配置 ajax 來實現自動請求…

Zabbix 企業級分布式監控系統深度解析

一、監控系統核心認知1.1 監控的本質與價值監控(Monitoring)的核心是 “檢測與預防”,在 IT 運維中占據約 30% 的權重。其核心價值體現在:風險預判:通過實時監測指標異常,提前發現潛在故障(如服…

使用 .NET 6.0 的簡單 WebSocket 客戶端和服務器應用程序

幾個月前,有同事來找我,問能否用 .NET 創建一個簡單的 WebSocket 服務器(以及之后的客戶端)。據我了解,他想用它來控制對方電腦上的進程。或許對其他人也有用,所以我把它發布在這里。讓我們從服務器開始。我…

【ASP.NET Core】ASP.NET Core中Redis分布式緩存的應用

系列文章目錄 鏈接: 【ASP.NET Core】REST與RESTful詳解,從理論到實現 鏈接: 【ASP.NET Core】深入理解Controller的工作機制 鏈接: 【ASP.NET Core】內存緩存(MemoryCache)原理、應用及常見問題解析 文章目錄系列文章目錄前言一、Redis1.1 …

5.6 指令流水線 (答案見原書 P267)

5.6 指令流水線 (答案見原書 P267) 01. 下列關于流水CPU基本概念的描述中,正確的是( D )。 題目原文 下列關于流水CPU基本概念的描述中,正確的是( )。 A. 流水CPU是以空間并行性為原理構造的處理器 B. 流水…

NIO簡單介紹和運用

NIO簡單介 NIO 非阻塞IO模型,基于緩沖區(Buffer)讀寫數據,讀寫后的數據通過通道(Channel)進行傳輸,采用選擇器(Selector)管理多個通道從而實現高并發。 核心組件:1. Buffer 為一個內存數組作為數據容器,代替傳統的Inpu…

LeetCode 658.找到K個最接近的元素

給定一個 排序好 的數組 arr &#xff0c;兩個整數 k 和 x &#xff0c;從數組中找到最靠近 x&#xff08;兩數之差最小&#xff09;的 k 個數。返回的結果必須要是按升序排好的。 整數 a 比整數 b 更接近 x 需要滿足&#xff1a; |a - x| < |b - x| 或者 |a - x| |b - x| …

制作一款打飛機游戲83:炸彈機制

游戲中的炸彈系統&#xff0c;包括以下核心功能&#xff1a;炸彈爆炸效果與動畫實現炸彈傷害范圍判定機制子彈轉化為能量道具的系統炸彈使用時的無敵幀處理各種邊界情況的修復與優化技術實現細節1. 炸彈基礎系統?炸彈動畫狀態機?&#xff1a; 我們采用三階段狀態機控制炸彈效…

Linux CentOS 虛擬機升級內核至4.x以上版本

1、安裝組件 yum install -y wget && yum install -y net-tools yum groupinstall “Development Tools” yum install ncurses-devel bc openssl-devel elfutils-libelf-devel yum install -y ncurses-devel yum install -y elfutils-libelf-devel yum install -y ope…

QT跨平臺應用程序開發框架(11)—— Qt系統相關

目錄 一&#xff0c;事件 1.1 關于事件 1.2 處理事件 1.3 處理鼠標事件 1.3.1 點擊事件 1.3.2 釋放事件 1.3.3 雙擊事件 1.3.4 滾輪事件 1.3.5 注意事項 1.4 處理鍵盤事件 1.5 定時器事件 1.6 窗口移動和大小改變事件 二&#xff0c;文件操作 2.1 文件操作概述 2.2 QFile 介紹…

sqli-labs通關筆記-第11關 POST字符型注入(單引號閉合 手工注入+腳本注入兩種方法)

目錄 一、字符型注入 二、limit函數 三、GET方法與POST方法 四、源碼分析 1、代碼審計 2、SQL注入安全分析 五、滲透實戰 1、進入靶場 2、注入點分析 &#xff08;1&#xff09;SQL語句 &#xff08;2&#xff09;萬能密碼登錄 3、手工注入 &#xff08;1&#xf…

網絡安全基礎作業三

回顧web前端的代碼<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>用戶登錄</title><st…

基于單片機的溫濕度報警系統設計與實現

摘 要 本項研究對溫濕度警報系統的需求進行了詳盡分析&#xff0c;并成功研制出一套以單片機為技術核心的溫濕度警報系統。該系統由硬件搭建和軟件編程兩大模塊構成。在硬件搭建方面&#xff0c;系統整合了STM32主控芯片、DS18B20溫度傳感器、濕敏電阻、按鍵組件、OLED顯示屏、…

(八)復習(拆分微服務)

文章目錄項目地址一、Ticketing模塊拆分1.1 創建web api1. 添加引用2. 添加需要的包和配置3. program.cs4. docker-compose修改項目地址 教程作者&#xff1a;教程地址&#xff1a; 代碼倉庫地址&#xff1a; 所用到的框架和插件&#xff1a; dbt airflow一、Ticketing模塊拆…

DearMom以“新生兒安全系統”重塑嬰兒車價值,攬獲CBME雙項大獎

7月16日&#xff0c;在剛剛開幕的2025 CBME中國孕嬰童展上&#xff0c;備受矚目的CBME中國孕嬰童產業獎正式揭曉。深耕嬰兒車品類的專業品牌DearMom&#xff0c;憑借其卓越的創新實力與對新生兒安全出行的深刻洞察&#xff0c;一舉摘得重量級獎項——“杰出品牌創新獎”。同時&…

瀚高數據庫開啟Oracle兼容模塊

文章目錄環境癥狀問題原因解決方案環境 系統平臺&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 癥狀 不能使用Oracle兼容&#xff1b; 問題原因 在瀚高數據庫V45中oracle兼容模塊需要單獨開啟默認是關閉狀態。 解決方案 使用sysdba執行修改…

final修飾符不可變的底層

final修飾符的底層原理在 Java 中&#xff0c;final 修飾符的底層實現涉及 編譯器優化 和 JVM 字節碼層面的約束其核心目標是保證被修飾元素的【不可變性】或 【不可重寫 / 繼承性】一、final 修飾類&#xff1a;禁止繼承的底層約束當一個類被 final 修飾時&#xff0c;例如 St…

如何排查服務器 CPU 飆高

服務器 CPU 飆高&#xff08;CPU 使用率持續超過 80% 甚至接近 100%&#xff09;是典型的性能瓶頸問題&#xff0c;可能由應用邏輯缺陷、資源競爭、外部壓力或硬件/系統異常引起。以下是系統化的排查步驟&#xff0c;覆蓋從現象確認到根因定位的全流程。?一、確認 CPU 飆高的現…