{ "cells": [ { "cell_type": "markdown", "id": "95b8d692-46ba-4d7c-beb8-c7b2ecdbe883", "metadata": {}, "source": [ "# Blender" ] }, { "cell_type": "markdown", "id": "cffb7e10-1aa1-46d9-8eac-7fa9fab3daae", "metadata": {}, "source": [ "## Rotation formalisms" ] }, { "attachments": {}, "cell_type": "markdown", "id": "df40f778-f020-4026-a5c8-2ede7957a2c2", "metadata": {}, "source": [ "See [Rotation formalisms in three dimensions](https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions). Blender's user interface only allows for rotations to be entered as as Quaternions, Euler rotations, or an Euler axis-angle. If you have a rotation in some other form, how do you enter it in Blender? See [Rotation — SciPy Manual](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.html#scipy.spatial.transform.Rotation):" ] }, { "cell_type": "code", "execution_count": 91, "id": "d4248180-dbdc-4f1c-8cc0-89ea294e6676", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from scipy.spatial.transform import Rotation as R\n", "\n", "def show_rot(rot: R):\n", " print('Matrix:')\n", " display(rot.as_matrix())\n", " print('\\nBlender \"Quaternion\":')\n", " display(rot.as_quat(canonical=True, scalar_first=True))\n", " print('\\nBlender \"XYZ Euler\":')\n", " display(rot.as_euler('xyz', degrees=True))\n", " print('\\nBlender \"Axis Angle\":')\n", " rot_vec = rot.as_rotvec(degrees=True)\n", " norm = np.linalg.norm(rot_vec)\n", " display(norm, rot_vec / norm)" ] }, { "cell_type": "code", "execution_count": 90, "id": "d65d3481-2753-4ff0-be25-fb991548c20c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Matrix:\n" ] }, { "data": { "text/plain": [ "array([[-2.77555756e-17, -6.42962682e-01, -7.65897506e-01],\n", " [ 0.00000000e+00, -7.65897506e-01, 6.42962682e-01],\n", " [-1.00000000e+00, 0.00000000e+00, -2.77555756e-17]])" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Blender \"Quaternion\":\n" ] }, { "data": { "text/plain": [ "array([ 0.2419207 , -0.66443538, 0.2419207 , 0.66443538])" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Blender \"XYZ Euler\":\n" ] }, { "data": { "text/plain": [ "array([-139.98690433, 90. , 0. ])" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Blender \"Axis Angle\":\n" ] }, { "data": { "text/plain": [ "np.float64(152.00014158445592)" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "array([-0.68477595, 0.24932669, 0.68477595])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_rot(R.from_matrix([\n", "[ 0.000, -0.643, -0.766],\n", "[ 0.000, -0.766, 0.643],\n", "[-1.000, 0.000, 0.000],]))" ] }, { "cell_type": "code", "execution_count": 88, "id": "8e632615-e826-48b4-b88b-d921eee33ef0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Matrix:\n" ] }, { "data": { "text/plain": [ "array([[ 0., 0., 1.],\n", " [-1., 0., 0.],\n", " [ 0., -1., 0.]])" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Blender \"Quaternion\":\n" ] }, { "data": { "text/plain": [ "array([ 0.5, -0.5, 0.5, -0.5])" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Blender \"XYZ Euler\":\n" ] }, { "data": { "text/plain": [ "array([-90., 0., -90.])" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Blender \"Axis Angle\":\n" ] }, { "data": { "text/plain": [ "np.float64(119.99999999999999)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_rot(R.from_matrix([\n", "[ 0.000, 0.000, 1.000],\n", "[-1.000, 0.000, 0.000],\n", "[ 0.000, -1.000, 0.000],]))" ] }, { "cell_type": "markdown", "id": "7f4269d8-0a90-4a53-8b2e-1b940ec8fbc4", "metadata": {}, "source": [ "See also [Rotation Modes - Blender 4.5 LTS Manual](https://docs.blender.org/manual/en/latest/advanced/appendices/rotations.html). Many Blender tutorials try to explain Gimbal lock, but the best references still seem to be [Euler (gimbal lock) Explained - YouTube](https://www.youtube.com/watch?v=zc8b2Jo7mno) and [Gimbal lock - Wikipedia](https://en.wikipedia.org/wiki/Gimbal_lock)." ] }, { "cell_type": "markdown", "id": "2bb2fea7-61d9-4d6c-aab8-7b72e4290ff5", "metadata": {}, "source": [ "In Eigen, quaternion-vector multiplication is [just multiplication](https://stackoverflow.com/a/52239772/622049). In Eigen the coeffs command [produces XYZW](https://eigen.tuxfamily.org/dox/classEigen_1_1QuaternionBase.html#a193e79f616335a0067e3e784c7cf85fa), but internally the data is XYZW. If you [install the Eigen pretty printers](https://stackoverflow.com/a/25088214/622049), it's explicit:\n", "```\n", "$1 = Eigen::Quaternion (data ptr: 0x7fffffffd7d0) = {[x] = 0, [y] = 0.7071067811865841, [z] = 0, [w] = 0.70710678118651094}\n", "```" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.8" } }, "nbformat": 4, "nbformat_minor": 5 }