.. Copyright (c) 2018-2025 William Emerison Six Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is available at https://www.gnu.org/licenses/fdl-1.3.html. ******************* Rotations - Demo 07 ******************* Objective ^^^^^^^^^ Attempt to rotate the paddles around their center. Learn about rotations. This demo does not work correctly, because of a misunderstanding of how to interpret a sequence of transformations. .. figure:: static/screenshots/demo07.png :class: no-scale :align: center :alt: Demo 07 :figclass: align-center Demo 07 How to Execute ^^^^^^^^^^^^^^ Load src/modelviewprojection/demo07.py in Spyder and hit the play button. Move the Paddles using the Keyboard ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ============== ================================ Keyboard Input Action ============== ================================ *w* Move Left Paddle Up *s* Move Left Paddle Down *k* Move Right Paddle Down *i* Move Right Paddle Up *d* Increase Left Paddle's Rotation *a* Decrease Left Paddle's Rotation *l* Increase Right Paddle's Rotation *j* Decrease Right Paddle's Rotation ============== ================================ .. TODO -- discuss method chaining For another person's explanation of the trigonometry_ of rotating in 2D, see .. _trigonometry: https://www.alanzucconi.com/2016/02/03/2d-rotations/ Rotate the Paddles About their Center ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Besides translate and scale, the third main operation in computer graphics is to rotate an object. Rotation Around Origin (0,0) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We can rotate an object around (0,0) by rotating all of the object's vertices around (0,0). In high school math, you will have learned about sin, cos, and tangent. Typically the angles are described on the unit circle, where a rotation starts from the positive x axis. .. figure:: static/cc0/Stephan_Kulla/Sinus_und_Kosinus_am_Einheitskreis_1.svg :class: no-scale :align: center :alt: Demo 07 :figclass: align-center We can expand on this knowledge, allowing us to rotate all vertices, wherever they are, around the origin (0,0), by some angle :math:`\theta`. Let's take any arbitrary point .. math:: \vec{a} .. math:: \vec{r}(\vec{a}; \theta) .. figure:: static/cc0/williamesix/rotate-goal.svg :class: no-scale :align: center :alt: rotate-goal :figclass: align-center From high school geometry, remember that we can describe a Cartesian (x,y) point by its length r, and its cosine and sine of its angle :math:`\theta`. .. figure:: static/cc0/williamesix/rotate1.svg :class: no-scale :align: center :alt: rotate1 :figclass: align-center Also remember that when calculating sine and cosine, because right triangles are proportional, that sine and cosine are preserved for a right triangle, even if the length sides are scaled up or down. So we'll make a right triangle on the unit circle, but we will remember the length of (x,y), which we'll call "r", and we'll call the angle of (x,y) to be ":math:`\beta`". As a reminder, we want to rotate by a different angle, called ":math:`\theta`". .. figure:: static/cc0/williamesix/rotate2.svg :class: no-scale :align: center :alt: rotate2 :figclass: align-center Before we can rotate by ":math:`\theta`", first we need to be able to rotate by 90 degrees, or :math:`\pi`/2. So to rotate (cos(:math:`\beta`), sin(:math:`\beta`)) by :math:`\pi`/2, we get (cos(:math:`\beta` + :math:`\pi`/2), sin(:math:`\beta` + :math:`\pi`/2)). .. figure:: static/cc0/williamesix/rotate3.svg :class: no-scale :align: center :alt: rotate3 :figclass: align-center Now let's give each of those vertices new names, x' and y', for the purpose of ignoring details for now that we'll return to later, just let we did for length "r" above. .. figure:: static/cc0/williamesix/rotate4.svg :class: no-scale :align: center :alt: rotate4 :figclass: align-center Now forget about ":math:`\beta`", and remember that our goal is to rotate by angle ":math:`\theta`". Look at the picture below, while turning your head slightly to the left. x' and y' look just like our normal Cartesian plane and unit circle, combined with the ":math:`\theta`"; it looks like what we already know from high school geometry. .. figure:: static/cc0/williamesix/rotate5.svg :class: no-scale :align: center :alt: rotate5 :figclass: align-center So with this new frame of reference, we can rotate x' by ":math:`\theta`", and draw a right triangle on the unit circle using this new frame of reference. .. figure:: static/cc0/williamesix/rotate6.svg :class: no-scale :align: center :alt: rotate6 :figclass: align-center So the rotated point can be constructed by the following .. math:: \vec{r}(\vec{a}; theta) = cos(\theta)*\vec{x'} + sin(\theta)*\vec{y'} .. figure:: static/cc0/williamesix/rotate7.svg :class: no-scale :align: center :alt: rotate7 :figclass: align-center Now that we've found the direction on the unit circle, we remember to make it length "r". .. figure:: static/cc0/williamesix/rotate8.svg :class: no-scale :align: center :alt: rotate8 :figclass: align-center Ok, we are now going to stop thinking about geometry, and we will only be thinking about algebra. Please don't try to look at the formula and try to draw any diagrams. Ok, now it is time to remember what the values that x' and y' are defined as. .. math:: \vec{r}(\vec{a}; \theta) & = r*(cos(\theta)*\vec{x'} + sin(\theta)*\vec{y'}) \\ & = r*(cos(\theta)*\begin{bmatrix} cos(\beta) \\ sin(\beta) \\ \end{bmatrix} + sin(\theta)*\begin{bmatrix} cos(\beta + \pi/2) \\ sin(\beta + \pi/2) \\ \end{bmatrix}) A problem we have now is how to calculate cosine and sine of :math:`\beta` + :math:`\pi`/2, because we haven't actually calculated :math:`\beta`; we've calculated sine and cosine of :math:`\beta` by dividing the x value by the magnitude of the a, and the sine of :math:`\beta` by dividing the y value by the magnitude of a. .. math:: cos(\theta) = \vec{a}_{x} / r sine(\theta) = \vec{a}_{y} / r We could try to take the inverse sine or inverse cosine of theta, but there is no need given properties of trigonometry. .. math:: cos(\theta + \pi/2) = -sin(\theta) sin(\theta + \pi/2) = cos(\theta) Therefore .. math:: cos(\theta) = \vec{a}_{x} / r sin(\theta) = \vec{a}_{y} / r .. math:: \vec{r}(\vec{a}; \theta) & = r*(cos(\theta)*\vec{x'} + sin(\theta)*\vec{y'}) \\ & = r*(cos(\theta)*\begin{bmatrix} cos(\beta) \\ sin(\beta) \\ \end{bmatrix} + sin(\theta)*\begin{bmatrix} cos(\beta + \pi/2) \\ sin(\beta + \pi/2) \\ \end{bmatrix}) \\ & = r*(cos(\theta)*\begin{bmatrix} \vec{a}_{x} / r \\ \vec{a}_{y} / r \\ \end{bmatrix} + sin(\theta)*\begin{bmatrix} -\vec{a}_{y} / r \\ \vec{a}_{x} / r\\ \end{bmatrix}) \\ & = (cos(\theta)*\begin{bmatrix} \vec{a}_{x} \\ \vec{a}_{y} \\ \end{bmatrix} + sin(\theta)*\begin{bmatrix} -\vec{a}_{y} \\ \vec{a}_{x} \\ \end{bmatrix}) \\ & = (cos(\theta)*\vec{a} + sin(\theta)*\begin{bmatrix} -\vec{a}_{y} \\ \vec{a}_{x} \\ \end{bmatrix}) \vec{r}(\vec{a}; \theta) & = \begin{bmatrix} -\vec{a}_{y} \\ \vec{a}_{x} \\ \end{bmatrix} \text{ if } (\theta = \pi/2) \\ & = (cos(\theta)*\vec{a} + sin(\theta)*\vec{r}(\vec{a}; \pi/2 ) \text{ if } (\theta \ne \pi/2) .. literalinclude:: ../../src/modelviewprojection/mathutils2d.py :language: python :start-after: doc-region-begin define vector class :end-before: doc-region-end define vector class :linenos: :lineno-match: :caption: src/modelviewprojection/mathutils2d.py .. literalinclude:: ../../src/modelviewprojection/mathutils2d.py :language: python :start-after: doc-region-begin define rotate :end-before: doc-region-end define rotate :linenos: :lineno-match: :caption: src/modelviewprojection/mathutils2d.py * Note the definition of rotate, from the description above. cos and sin are defined in the math module. .. literalinclude:: ../../src/modelviewprojection/demo07.py :language: python :start-after: doc-region-begin define paddle class :end-before: doc-region-end define paddle class :linenos: :lineno-match: :caption: src/modelviewprojection/demo07.py * a rotation instance variable is defined, with a default value of 0 .. literalinclude:: ../../src/modelviewprojection/demo07.py :language: python :start-after: doc-region-begin define handle movement of paddles :end-before: doc-region-end define handle movement of paddles :linenos: :lineno-match: :caption: src/modelviewprojection/demo07.py Cayley Graph ^^^^^^^^^^^^ .. figure:: static/demo07.png :class: no-scale :align: center :alt: Demo 06 :figclass: align-center Code ^^^^ The Event Loop ~~~~~~~~~~~~~~ .. literalinclude:: ../../src/modelviewprojection/demo07.py :language: python :start-after: doc-region-begin begin event loop :end-before: doc-region-end begin event loop :linenos: :lineno-match: :caption: src/modelviewprojection/demo07.py So to rotate paddle 1 about its center, we should translate to its position, and then rotate around the paddle's center. .. literalinclude:: ../../src/modelviewprojection/demo07.py :language: python :start-after: doc-region-begin draw paddle 1 :end-before: doc-region-end draw paddle 1 :linenos: :lineno-match: :caption: src/modelviewprojection/demo07.py .. math:: \vec{f}_{p1}^{w} .. math:: \vec{f}_{w}^{ndc} :: ... Likewise, to rotate paddle 2 about its center, we should translate to its position, and then rotate around the paddle's center. .. literalinclude:: ../../src/modelviewprojection/demo07.py :language: python :start-after: doc-region-begin draw paddle 2 :end-before: doc-region-end draw paddle 2 :linenos: :lineno-match: :caption: src/modelviewprojection/demo07.py .. math:: \vec{f}_{p2}^{w} .. math:: \vec{f}_{w}^{ndc} Why it is Wrong ^^^^^^^^^^^^^^^ Turns out, our program doesn't work as predicted, even though translate, scale, and rotate are all defined correctly. The paddles are not rotating around their center. Let's take a look in detail about what our paddle-space to world space transformations are doing. .. literalinclude:: ../../src/modelviewprojection/demo07.py :language: python :start-after: doc-region-begin compose transformations on paddle 1 :end-before: doc-region-end compose transformations on paddle 1 :linenos: :lineno-match: :caption: src/modelviewprojection/demo07.py See :mod:`modelviewprojection.mathutils.compose` * :term:`modelspace` vertices .. figure:: static/incorrectrotate-forwards-1.svg :class: no-scale :align: center :alt: :figclass: align-center * Translate .. figure:: static/incorrectrotate-forwards-2.svg :class: no-scale :align: center :alt: :figclass: align-center * Reset the coordinate system .. figure:: static/incorrectrotate-forwards-3.svg :class: no-scale :align: center :alt: :figclass: align-center Modelspace * Rotate around World Spaces's origin .. figure:: static/incorrectrotate-forwards-4.svg :class: no-scale :align: center :alt: :figclass: align-center Modelspace * Reset the coordinate system .. figure:: static/incorrectrotate-forwards-5.svg :class: no-scale :align: center :alt: :figclass: align-center Modelspace * Final world space coordinates .. figure:: static/incorrectrotate-forwards-6.svg :class: no-scale :align: center :alt: :figclass: align-center Modelspace So then what the heck are we supposed to do in order to rotate around an object's center? The next section provides a solution.