ansys-pyensight-core 0.10.6__py3-none-any.whl → 0.10.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ansys-pyensight-core might be problematic. Click here for more details.
- ansys/pyensight/core/utils/views.py +235 -3
- {ansys_pyensight_core-0.10.6.dist-info → ansys_pyensight_core-0.10.7.dist-info}/METADATA +1 -1
- {ansys_pyensight_core-0.10.6.dist-info → ansys_pyensight_core-0.10.7.dist-info}/RECORD +5 -5
- {ansys_pyensight_core-0.10.6.dist-info → ansys_pyensight_core-0.10.7.dist-info}/WHEEL +0 -0
- {ansys_pyensight_core-0.10.6.dist-info → ansys_pyensight_core-0.10.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,9 +18,13 @@ Example to set an isometric view:
|
|
|
18
18
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
+
|
|
21
22
|
import math
|
|
23
|
+
from types import ModuleType
|
|
22
24
|
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
|
|
23
25
|
|
|
26
|
+
import numpy as np
|
|
27
|
+
|
|
24
28
|
if TYPE_CHECKING:
|
|
25
29
|
try:
|
|
26
30
|
import ensight
|
|
@@ -28,14 +32,239 @@ if TYPE_CHECKING:
|
|
|
28
32
|
from ansys.api.pyensight import ensight_api
|
|
29
33
|
|
|
30
34
|
|
|
35
|
+
VIEW_DICT = {
|
|
36
|
+
"x+": (1, 0, 0),
|
|
37
|
+
"x-": (-1, 0, 0),
|
|
38
|
+
"y+": (0, 1, 0),
|
|
39
|
+
"y-": (0, -1, 0),
|
|
40
|
+
"z+": (0, 0, 1),
|
|
41
|
+
"z-": (0, 0, -1),
|
|
42
|
+
"isometric": (1, 1, 1),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _Simba:
|
|
47
|
+
"""Hidden class to manage the interactor layer in simba"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, ensight: Union["ensight_api.ensight", "ensight"], views: "Views"):
|
|
50
|
+
self.ensight = ensight
|
|
51
|
+
self.views = views
|
|
52
|
+
self._original_look_at = None
|
|
53
|
+
self._original_look_from = None
|
|
54
|
+
self._original_parallel_scale = None
|
|
55
|
+
self._original_view_angle = None
|
|
56
|
+
self._original_view_up = None
|
|
57
|
+
|
|
58
|
+
def _initialize_simba_view(self):
|
|
59
|
+
"""Initialize the data for resetting the camera."""
|
|
60
|
+
vport = self.ensight.objs.core.VPORTS[0]
|
|
61
|
+
near_clip = vport.ZCLIPLIMITS[0]
|
|
62
|
+
view_angle = 2 * vport.PERSPECTIVEANGLE
|
|
63
|
+
self._original_parallel_scale = near_clip * math.tan(math.radians(view_angle) / 2)
|
|
64
|
+
self._original_view_angle = view_angle
|
|
65
|
+
(
|
|
66
|
+
self._original_look_from,
|
|
67
|
+
self._original_look_at,
|
|
68
|
+
self._original_view_up,
|
|
69
|
+
self._original_parallel_scale,
|
|
70
|
+
) = self.compute_camera_from_ensight_opengl()
|
|
71
|
+
self.ensight.annotation.axis_global("off")
|
|
72
|
+
self.ensight.annotation.axis_local("off")
|
|
73
|
+
self.ensight.annotation.axis_model("off")
|
|
74
|
+
|
|
75
|
+
def get_center_of_rotation(self):
|
|
76
|
+
"""Get EnSight center of rotation."""
|
|
77
|
+
return self.ensight.objs.core.VPORTS[0].TRANSFORMCENTER
|
|
78
|
+
|
|
79
|
+
def auto_scale(self):
|
|
80
|
+
"""Auto scale view."""
|
|
81
|
+
self.ensight.view_transf.fit()
|
|
82
|
+
self._initialize_simba_view()
|
|
83
|
+
self.render()
|
|
84
|
+
return self.get_camera()
|
|
85
|
+
|
|
86
|
+
def set_view(self, value: str):
|
|
87
|
+
"""Set the view."""
|
|
88
|
+
if value != "isometric":
|
|
89
|
+
new_value = value[1].upper() + value[0]
|
|
90
|
+
self.ensight.view_transf.view_recall(new_value)
|
|
91
|
+
else:
|
|
92
|
+
self.views.set_view_direction(
|
|
93
|
+
1, 1, 1, perspective=self.ensight.objs.core.vports[0].PERSPECTIVE
|
|
94
|
+
)
|
|
95
|
+
self.auto_scale()
|
|
96
|
+
return self.get_camera()
|
|
97
|
+
|
|
98
|
+
def get_camera(self):
|
|
99
|
+
"""Get EnSight camera settings in VTK format."""
|
|
100
|
+
vport = self.ensight.objs.core.VPORTS[0]
|
|
101
|
+
position, focal_point, view_up, parallel_scale = self.compute_camera_from_ensight_opengl()
|
|
102
|
+
vport = self.ensight.objs.core.VPORTS[0]
|
|
103
|
+
view_angle = 2 * vport.PERSPECTIVEANGLE
|
|
104
|
+
# The parameter parallel scale is the actual parallel scale only
|
|
105
|
+
# if the vport is in orthographic mode. If not, it is defined as the
|
|
106
|
+
# inverge of the tangent of half of the field of view
|
|
107
|
+
parallel_scale = parallel_scale
|
|
108
|
+
return {
|
|
109
|
+
"orthographic": not vport.PERSPECTIVE,
|
|
110
|
+
"view_up": view_up,
|
|
111
|
+
"position": position,
|
|
112
|
+
"focal_point": focal_point,
|
|
113
|
+
"view_angle": view_angle,
|
|
114
|
+
"parallel_scale": parallel_scale,
|
|
115
|
+
"reset_focal_point": self._original_look_at,
|
|
116
|
+
"reset_position": self._original_look_from,
|
|
117
|
+
"reset_parallel_scale": self._original_parallel_scale,
|
|
118
|
+
"reset_view_up": self._original_view_up,
|
|
119
|
+
"reset_view_angle": self._original_view_angle,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def normalize(v):
|
|
124
|
+
"""Normalize a numpy vector."""
|
|
125
|
+
norm = np.linalg.norm(v)
|
|
126
|
+
return v / norm if norm > 0 else v
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def rotation_matrix_to_quaternion(m):
|
|
130
|
+
"""Convert a numpy rotation matrix to a quaternion."""
|
|
131
|
+
trace = np.trace(m)
|
|
132
|
+
if trace > 0:
|
|
133
|
+
s = 0.5 / np.sqrt(trace + 1.0)
|
|
134
|
+
w = 0.25 / s
|
|
135
|
+
x = (m[2, 1] - m[1, 2]) * s
|
|
136
|
+
y = (m[0, 2] - m[2, 0]) * s
|
|
137
|
+
z = (m[1, 0] - m[0, 1]) * s
|
|
138
|
+
else:
|
|
139
|
+
if m[0, 0] > m[1, 1] and m[0, 0] > m[2, 2]:
|
|
140
|
+
s = 2.0 * np.sqrt(1.0 + m[0, 0] - m[1, 1] - m[2, 2])
|
|
141
|
+
w = (m[2, 1] - m[1, 2]) / s
|
|
142
|
+
x = 0.25 * s
|
|
143
|
+
y = (m[0, 1] + m[1, 0]) / s
|
|
144
|
+
z = (m[0, 2] + m[2, 0]) / s
|
|
145
|
+
elif m[1, 1] > m[2, 2]:
|
|
146
|
+
s = 2.0 * np.sqrt(1.0 + m[1, 1] - m[0, 0] - m[2, 2])
|
|
147
|
+
w = (m[0, 2] - m[2, 0]) / s
|
|
148
|
+
x = (m[0, 1] + m[1, 0]) / s
|
|
149
|
+
y = 0.25 * s
|
|
150
|
+
z = (m[1, 2] + m[2, 1]) / s
|
|
151
|
+
else:
|
|
152
|
+
s = 2.0 * np.sqrt(1.0 + m[2, 2] - m[0, 0] - m[1, 1])
|
|
153
|
+
w = (m[1, 0] - m[0, 1]) / s
|
|
154
|
+
x = (m[0, 2] + m[2, 0]) / s
|
|
155
|
+
y = (m[1, 2] + m[2, 1]) / s
|
|
156
|
+
z = 0.25 * s
|
|
157
|
+
return np.array([x, y, z, w])
|
|
158
|
+
|
|
159
|
+
def compute_model_rotation_quaternion(self, camera_position, focal_point, view_up):
|
|
160
|
+
"""Compute the quaternion from the input camera."""
|
|
161
|
+
forward = self.normalize(np.array(focal_point) - np.array(camera_position))
|
|
162
|
+
right = self.normalize(np.cross(forward, view_up))
|
|
163
|
+
true_up = np.cross(right, forward)
|
|
164
|
+
camera_rotation = np.vstack([right, true_up, -forward]).T
|
|
165
|
+
model_rotation = camera_rotation.T
|
|
166
|
+
quat = self.rotation_matrix_to_quaternion(model_rotation)
|
|
167
|
+
return quat
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def quaternion_multiply(q1, q2):
|
|
171
|
+
x1, y1, z1, w1 = q1
|
|
172
|
+
x2, y2, z2, w2 = q2
|
|
173
|
+
w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
|
|
174
|
+
x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
|
|
175
|
+
y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
|
|
176
|
+
z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
|
|
177
|
+
return np.array([x, y, z, w])
|
|
178
|
+
|
|
179
|
+
def quaternion_to_euler(self, q):
|
|
180
|
+
q = self.normalize(q)
|
|
181
|
+
x, y, z, w = q
|
|
182
|
+
sinr_cosp = 2 * (w * x + y * z)
|
|
183
|
+
cosr_cosp = 1 - 2 * (x * x + y * y)
|
|
184
|
+
roll = np.arctan2(sinr_cosp, cosr_cosp)
|
|
185
|
+
|
|
186
|
+
sinp = 2 * (w * y - z * x)
|
|
187
|
+
if abs(sinp) >= 1:
|
|
188
|
+
pitch = np.pi / 2 * np.sign(sinp)
|
|
189
|
+
else:
|
|
190
|
+
pitch = np.arcsin(sinp)
|
|
191
|
+
siny_cosp = 2 * (w * z + x * y)
|
|
192
|
+
cosy_cosp = 1 - 2 * (y * y + z * z)
|
|
193
|
+
yaw = np.arctan2(siny_cosp, cosy_cosp)
|
|
194
|
+
|
|
195
|
+
return np.degrees([roll, pitch, yaw])
|
|
196
|
+
|
|
197
|
+
def compute_camera_from_ensight_opengl(self):
|
|
198
|
+
"""Simulate a rotating camera using the current quaternion."""
|
|
199
|
+
if isinstance(self.ensight, ModuleType):
|
|
200
|
+
data = self.ensight.objs.core.VPORTS[0].simba_camera()
|
|
201
|
+
else:
|
|
202
|
+
data = self.ensight._session.cmd("ensight.objs.core.VPORTS[0].simba_camera())")
|
|
203
|
+
camera_position = [data[0], data[1], data[2]]
|
|
204
|
+
focal_point = [data[3], data[4], data[5]]
|
|
205
|
+
view_up = [data[6], data[7], data[8]]
|
|
206
|
+
parallel_scale = 1 / data[9]
|
|
207
|
+
return camera_position, focal_point, self.views._normalize_vector(view_up), parallel_scale
|
|
208
|
+
|
|
209
|
+
def set_camera(
|
|
210
|
+
self, orthographic, view_up=None, position=None, focal_point=None, view_angle=None
|
|
211
|
+
):
|
|
212
|
+
"""Set the EnSight camera settings from the VTK input."""
|
|
213
|
+
perspective = "OFF" if orthographic else "ON"
|
|
214
|
+
if orthographic:
|
|
215
|
+
self.ensight.view.perspective(perspective)
|
|
216
|
+
vport = self.ensight.objs.core.VPORTS[0]
|
|
217
|
+
if view_angle:
|
|
218
|
+
vport.PERSPECTIVEANGLE = view_angle / 2
|
|
219
|
+
|
|
220
|
+
if view_up and position and focal_point:
|
|
221
|
+
q_current = self.normalize(np.array(vport.ROTATION.copy()))
|
|
222
|
+
q_target = self.normalize(
|
|
223
|
+
self.compute_model_rotation_quaternion(position, focal_point, view_up)
|
|
224
|
+
)
|
|
225
|
+
q_relative = self.quaternion_multiply(
|
|
226
|
+
q_target, np.array([-q_current[0], -q_current[1], -q_current[2], q_current[3]])
|
|
227
|
+
)
|
|
228
|
+
angles = self.quaternion_to_euler(q_relative)
|
|
229
|
+
self.ensight.view_transf.rotate(*angles)
|
|
230
|
+
self.render()
|
|
231
|
+
|
|
232
|
+
def set_perspective(self, value):
|
|
233
|
+
vport = self.ensight.objs.core.VPORTS[0]
|
|
234
|
+
self.ensight.view.perspective(value)
|
|
235
|
+
vport.PERSPECTIVE = value == "ON"
|
|
236
|
+
self.ensight.view_transf.zoom(1)
|
|
237
|
+
self.ensight.view_transf.rotate(0, 0, 0)
|
|
238
|
+
self.render()
|
|
239
|
+
return self.get_camera()
|
|
240
|
+
|
|
241
|
+
def screen_to_world(self, mousex, mousey, invert_y=False, set_center=False):
|
|
242
|
+
mousex = int(mousex)
|
|
243
|
+
mousey = int(mousey)
|
|
244
|
+
if isinstance(self.ensight, ModuleType):
|
|
245
|
+
model_point = self.ensight.objs.core.VPORTS[0].screen_to_coords(
|
|
246
|
+
mousex, mousey, invert_y, set_center
|
|
247
|
+
)
|
|
248
|
+
else:
|
|
249
|
+
model_point = self.ensight._session.cmd(
|
|
250
|
+
f"ensight.objs.core.VPORTS[0].screen_to_coords({mousex}, {mousey}, {invert_y}, {set_center})"
|
|
251
|
+
)
|
|
252
|
+
self.render()
|
|
253
|
+
return {"model_point": model_point, "camera": self.get_camera()}
|
|
254
|
+
|
|
255
|
+
def render(self):
|
|
256
|
+
self.ensight.render()
|
|
257
|
+
self.ensight.refresh(1)
|
|
258
|
+
|
|
259
|
+
|
|
31
260
|
class Views:
|
|
32
261
|
"""Controls the view in the current EnSight ``Session`` instance."""
|
|
33
262
|
|
|
34
263
|
def __init__(self, ensight: Union["ensight_api.ensight", "ensight"]):
|
|
35
264
|
self.ensight = ensight
|
|
36
265
|
self._views_dict: Dict[str, Tuple[int, List[float]]] = {}
|
|
266
|
+
self._simba = _Simba(ensight, self)
|
|
37
267
|
|
|
38
|
-
# Utilities
|
|
39
268
|
@staticmethod
|
|
40
269
|
def _normalize_vector(direction: List[float]) -> List[float]:
|
|
41
270
|
"""Return the normalized input (3D) vector.
|
|
@@ -295,14 +524,17 @@ class Views:
|
|
|
295
524
|
vportindex : int, optional
|
|
296
525
|
Viewport to set the view direction for. The default is ``0``.
|
|
297
526
|
"""
|
|
298
|
-
self.ensight.view.perspective("OFF")
|
|
299
|
-
direction = [xdir, ydir, zdir]
|
|
300
527
|
vport = self.ensight.objs.core.VPORTS[vportindex]
|
|
528
|
+
if not perspective:
|
|
529
|
+
self.ensight.view.perspective("OFF")
|
|
530
|
+
vport.PERSPECTIVE = False
|
|
531
|
+
direction = [xdir, ydir, zdir]
|
|
301
532
|
rots = vport.ROTATION.copy()
|
|
302
533
|
rots[0:4] = self._convert_view_direction_to_quaternion(direction, up_axis=up_axis)
|
|
303
534
|
vport.ROTATION = rots
|
|
304
535
|
if perspective:
|
|
305
536
|
self.ensight.view.perspective("ON")
|
|
537
|
+
vport.PERSPECTIVE = True
|
|
306
538
|
self.save_current_view(name=name, vportindex=vportindex)
|
|
307
539
|
|
|
308
540
|
def save_current_view(
|
|
@@ -29,9 +29,9 @@ ansys/pyensight/core/utils/query.py,sha256=OXKDbf1sOTX0sUvtKcp64LhVl-BcrEsE43w8u
|
|
|
29
29
|
ansys/pyensight/core/utils/readers.py,sha256=_IluAWz8mmoe5SM3hAewHIqlhtKMfEqrUJoQOlJ4U4I,12138
|
|
30
30
|
ansys/pyensight/core/utils/support.py,sha256=QI3z9ex7zJxjFbkCPba9DWqWgPFIThORqr0nvRfVjuc,4089
|
|
31
31
|
ansys/pyensight/core/utils/variables.py,sha256=ZUiJdDIeRcowrnLXaJQqGwA0RbrfXhc1s4o4v9A4PiY,95133
|
|
32
|
-
ansys/pyensight/core/utils/views.py,sha256=
|
|
32
|
+
ansys/pyensight/core/utils/views.py,sha256=uJO1eVJEZrS4vYgeb5mscaTalrRrk8UpfFurxv8PWt8,21389
|
|
33
33
|
ansys/pyensight/core/utils/resources/Materials/000_sky.exr,sha256=xAR1gFd2uxPZDnvgfegdhEhRaqKtZldQDiR_-1rHKO0,8819933
|
|
34
|
-
ansys_pyensight_core-0.10.
|
|
35
|
-
ansys_pyensight_core-0.10.
|
|
36
|
-
ansys_pyensight_core-0.10.
|
|
37
|
-
ansys_pyensight_core-0.10.
|
|
34
|
+
ansys_pyensight_core-0.10.7.dist-info/licenses/LICENSE,sha256=K6LiJHOa9IbWFelXmXNRzFr3zG45SOGZIN7vdLdURGU,1097
|
|
35
|
+
ansys_pyensight_core-0.10.7.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
36
|
+
ansys_pyensight_core-0.10.7.dist-info/METADATA,sha256=EEq6p9yGwjwh7U_c-etnvVzA7G2A1qNgCS9RZBhnbYs,12232
|
|
37
|
+
ansys_pyensight_core-0.10.7.dist-info/RECORD,,
|
|
File without changes
|
{ansys_pyensight_core-0.10.6.dist-info → ansys_pyensight_core-0.10.7.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|