forked from glumpy/glumpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rotate.py
140 lines (107 loc) · 4.03 KB
/
rotate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# -----------------------------------------------------------------------------
# Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
# Distributed under the (new) BSD License.
# -----------------------------------------------------------------------------
import math
import numpy as np
from glumpy import library
from glumpy.transforms.transform import Transform
def _rotation(angle, x, y, z):
angle = math.pi * angle / 180
c, s = math.cos(angle), math.sin(angle)
n = math.sqrt(x * x + y * y + z * z)
x,y,z = x/n, y/n, z/n
cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z
return np.array([[cx * x + c, cy * x - z * s, cz * x + y * s, 0],
[cx * y + z * s, cy * y + c, cz * y - x * s, 0],
[cx * z - y * s, cy * z + x * s, cz * z + c, 0],
[0, 0, 0, 1]], dtype=np.float32).T
class Rotate(Transform):
"""
Rotation transform
:param 3-tuple axis:
Rotation axis. Default is (0,0,1).
:param float angle:
Rotation angle. Default is 0.
:param 3-tuple origin:
Rotation origin. Default is (0,0,0).
The transform is connected to the following events:
* ``on_attach``: Transform initialization
**Usage example**:
.. code:: python
vertex = '''
attribute vec2 position;
void main()
{
gl_Position = <transform>;
} '''
...
window = app.Window(width=800, height=800)
program = gloo.Program(vertex, fragment, count=4)
...
program['transform'] = Rotate("position", angle=15)
window.attach(program['transform'])
...
"""
aliases = { "axis" : "rotate_axis",
"angle" : "rotate_angle",
"origin" : "rotate_origin",
"forward" : "rotate_forward_matrix",
"inverse" : "rotate_inverse_matrix" }
def __init__(self, *args, **kwargs):
""" Initialize the transform. """
self._forward = np.zeros((4,4), dtype=np.float32)
self._inverse = np.zeros((4,4), dtype=np.float32)
self._axis = Transform._get_kwarg("axis", kwargs, (0,0,1))
self._angle = Transform._get_kwarg("angle", kwargs, 0.0)
self._origin = Transform._get_kwarg("origin", kwargs, (0.,0.,0.))
code = library.get("transforms/rotate.glsl")
Transform.__init__(self, code, *args, **kwargs)
# Force building of rotation matrices
self.axis = self._axis
@property
def axis(self):
""" Rotation axis """
return self._axis
@axis.setter
def axis(self, value):
""" Rotation axis """
self._axis = np.array(value, dtype=np.float32)
x,y,z = self._axis
self._forward = _rotation(+self._angle, x, y, z)
self._inverse = _rotation(-self._angle, x, y, z)
if self.is_attached:
self["axis"] = self._axis
self["forward"] = self._forward
self["inverse"] = self._inverse
@property
def origin(self):
""" Rotation origin """
return self._origin
@origin.setter
def origin(self, value):
""" Rotation origin """
self._origin = np.array(value, dtype=np.float32)
if self.is_attached:
self["origin"] = self._origin
@property
def angle(self):
""" Rotation angle (degrees) """
return self._angle
@angle.setter
def angle(self, value):
""" Rotation angle (degrees) """
self._angle = float(value)
x,y,z = self._axis
self._forward[...] = _rotation(+self._angle, x, y, z)
self._inverse[...] = _rotation(-self._angle, x, y, z)
if self.is_attached:
self["angle"] = self._angle
self["forward"] = self._forward
self["inverse"] = self._inverse
def on_attach(self, program):
self["axis"] = self._axis
self["angle"] = self._angle
self["origin"] = self._origin
self["forward"] = self._forward
self["inverse"] = self._inverse