capytaine 2.3.1__cp314-cp314t-macosx_14_0_arm64.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.
- capytaine/.dylibs/libgcc_s.1.1.dylib +0 -0
- capytaine/.dylibs/libgfortran.5.dylib +0 -0
- capytaine/.dylibs/libquadmath.0.dylib +0 -0
- capytaine/__about__.py +16 -0
- capytaine/__init__.py +36 -0
- capytaine/bem/__init__.py +0 -0
- capytaine/bem/airy_waves.py +111 -0
- capytaine/bem/engines.py +441 -0
- capytaine/bem/problems_and_results.py +600 -0
- capytaine/bem/solver.py +594 -0
- capytaine/bodies/__init__.py +4 -0
- capytaine/bodies/bodies.py +1221 -0
- capytaine/bodies/dofs.py +19 -0
- capytaine/bodies/predefined/__init__.py +6 -0
- capytaine/bodies/predefined/cylinders.py +151 -0
- capytaine/bodies/predefined/rectangles.py +111 -0
- capytaine/bodies/predefined/spheres.py +70 -0
- capytaine/green_functions/FinGreen3D/.gitignore +1 -0
- capytaine/green_functions/FinGreen3D/FinGreen3D.f90 +3589 -0
- capytaine/green_functions/FinGreen3D/LICENSE +165 -0
- capytaine/green_functions/FinGreen3D/Makefile +16 -0
- capytaine/green_functions/FinGreen3D/README.md +24 -0
- capytaine/green_functions/FinGreen3D/test_program.f90 +39 -0
- capytaine/green_functions/LiangWuNoblesse/.gitignore +1 -0
- capytaine/green_functions/LiangWuNoblesse/LICENSE +504 -0
- capytaine/green_functions/LiangWuNoblesse/LiangWuNoblesseWaveTerm.f90 +751 -0
- capytaine/green_functions/LiangWuNoblesse/Makefile +16 -0
- capytaine/green_functions/LiangWuNoblesse/README.md +2 -0
- capytaine/green_functions/LiangWuNoblesse/test_program.f90 +28 -0
- capytaine/green_functions/__init__.py +2 -0
- capytaine/green_functions/abstract_green_function.py +64 -0
- capytaine/green_functions/delhommeau.py +507 -0
- capytaine/green_functions/hams.py +204 -0
- capytaine/green_functions/libs/Delhommeau_float32.cpython-314t-darwin.so +0 -0
- capytaine/green_functions/libs/Delhommeau_float64.cpython-314t-darwin.so +0 -0
- capytaine/green_functions/libs/__init__.py +0 -0
- capytaine/io/__init__.py +0 -0
- capytaine/io/bemio.py +153 -0
- capytaine/io/legacy.py +328 -0
- capytaine/io/mesh_loaders.py +1086 -0
- capytaine/io/mesh_writers.py +692 -0
- capytaine/io/meshio.py +38 -0
- capytaine/io/wamit.py +479 -0
- capytaine/io/xarray.py +668 -0
- capytaine/matrices/__init__.py +16 -0
- capytaine/matrices/block.py +592 -0
- capytaine/matrices/block_toeplitz.py +325 -0
- capytaine/matrices/builders.py +89 -0
- capytaine/matrices/linear_solvers.py +232 -0
- capytaine/matrices/low_rank.py +395 -0
- capytaine/meshes/__init__.py +6 -0
- capytaine/meshes/clipper.py +465 -0
- capytaine/meshes/collections.py +342 -0
- capytaine/meshes/geometry.py +409 -0
- capytaine/meshes/mesh_like_protocol.py +37 -0
- capytaine/meshes/meshes.py +890 -0
- capytaine/meshes/predefined/__init__.py +6 -0
- capytaine/meshes/predefined/cylinders.py +314 -0
- capytaine/meshes/predefined/rectangles.py +261 -0
- capytaine/meshes/predefined/spheres.py +62 -0
- capytaine/meshes/properties.py +276 -0
- capytaine/meshes/quadratures.py +80 -0
- capytaine/meshes/quality.py +448 -0
- capytaine/meshes/surface_integrals.py +63 -0
- capytaine/meshes/symmetric.py +462 -0
- capytaine/post_pro/__init__.py +6 -0
- capytaine/post_pro/free_surfaces.py +88 -0
- capytaine/post_pro/impedance.py +92 -0
- capytaine/post_pro/kochin.py +54 -0
- capytaine/post_pro/rao.py +60 -0
- capytaine/tools/__init__.py +0 -0
- capytaine/tools/cache_on_disk.py +26 -0
- capytaine/tools/deprecation_handling.py +18 -0
- capytaine/tools/lists_of_points.py +52 -0
- capytaine/tools/lru_cache.py +49 -0
- capytaine/tools/optional_imports.py +27 -0
- capytaine/tools/prony_decomposition.py +150 -0
- capytaine/tools/symbolic_multiplication.py +149 -0
- capytaine/tools/timer.py +66 -0
- capytaine/ui/__init__.py +0 -0
- capytaine/ui/cli.py +28 -0
- capytaine/ui/rich.py +5 -0
- capytaine/ui/vtk/__init__.py +3 -0
- capytaine/ui/vtk/animation.py +329 -0
- capytaine/ui/vtk/body_viewer.py +28 -0
- capytaine/ui/vtk/helpers.py +82 -0
- capytaine/ui/vtk/mesh_viewer.py +461 -0
- capytaine-2.3.1.dist-info/LICENSE +674 -0
- capytaine-2.3.1.dist-info/METADATA +750 -0
- capytaine-2.3.1.dist-info/RECORD +92 -0
- capytaine-2.3.1.dist-info/WHEEL +6 -0
- capytaine-2.3.1.dist-info/entry_points.txt +3 -0
capytaine/ui/rich.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""VTK animation for the free surface elevation."""
|
|
2
|
+
# Copyright (C) 2017-2019 Matthieu Ancellin
|
|
3
|
+
# See LICENSE file at <https://github.com/mancellin/capytaine>
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from numpy import pi
|
|
9
|
+
|
|
10
|
+
from capytaine.ui.vtk.helpers import compute_node_data, compute_vtk_polydata
|
|
11
|
+
from capytaine.tools.optional_imports import import_optional_dependency
|
|
12
|
+
|
|
13
|
+
vtk = import_optional_dependency("vtk")
|
|
14
|
+
|
|
15
|
+
LOG = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Animation:
|
|
19
|
+
"""Class to generate an animation of a result of Capytaine,
|
|
20
|
+
including the elevation of the free surface.
|
|
21
|
+
|
|
22
|
+
The animation is made of a short loop of a single period of the solution in frequency domain.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
loop_duration: float
|
|
27
|
+
Duration in the loop. For real time animation, the period of the motion.
|
|
28
|
+
fps: int, optional
|
|
29
|
+
Number of frames per second in the animation (default: 24).
|
|
30
|
+
|
|
31
|
+
Attributes
|
|
32
|
+
----------
|
|
33
|
+
frames_per_loop: int
|
|
34
|
+
Number of frames in one loop
|
|
35
|
+
actors: list of vtk actor objects
|
|
36
|
+
The objects in the scene.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, loop_duration, fps=24):
|
|
40
|
+
self.fps = fps
|
|
41
|
+
self.frames_per_loop = int(fps * loop_duration)
|
|
42
|
+
self.actors = []
|
|
43
|
+
|
|
44
|
+
self._precomputed_polydatas = {}
|
|
45
|
+
self._current_frame = 0
|
|
46
|
+
|
|
47
|
+
# @classmethod
|
|
48
|
+
# def from_result(self, result):
|
|
49
|
+
# from capytaine.bodies import TRANSLATION_DOFS_DIRECTIONS, ROTATION_DOFS_AXIS
|
|
50
|
+
#
|
|
51
|
+
# if display_dof.lower() in TRANSLATION_DOFS_DIRECTIONS:
|
|
52
|
+
# direction = np.asarray(TRANSLATION_DOFS_DIRECTIONS[display_dof.lower()])
|
|
53
|
+
# def translation_motion(self, frame):
|
|
54
|
+
# nonlocal direction
|
|
55
|
+
# pos = np.asarray(self.body_actor.GetPosition())
|
|
56
|
+
# pos = (1 - direction) * pos + \
|
|
57
|
+
# direction * np.cos(2*np.pi*(frame % self.frames_per_loop)/self.frames_per_loop)
|
|
58
|
+
# self.body_actor.SetPosition(*pos)
|
|
59
|
+
# self.update_body_position = translation_motion
|
|
60
|
+
#
|
|
61
|
+
# elif display_dof.lower() in ROTATION_DOFS_AXIS:
|
|
62
|
+
# direction = np.asarray(ROTATION_DOFS_AXIS[display_dof.lower()])
|
|
63
|
+
# def rotation_motion(self, frame):
|
|
64
|
+
# nonlocal direction
|
|
65
|
+
# pos = np.asarray(self.body_actor.GetOrientation())
|
|
66
|
+
# pos = (1 - direction) * pos + \
|
|
67
|
+
# direction * np.cos(2*np.pi*(frame % self.frames_per_loop)/self.frames_per_loop)
|
|
68
|
+
# self.body_actor.SetOrientation(*pos)
|
|
69
|
+
# self.update_body_position = rotation_motion
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _add_actor(self, mesh, faces_motion=None, faces_colors=None, edges=False):
|
|
73
|
+
"""Add an animated object to the scene."""
|
|
74
|
+
if faces_motion is not None:
|
|
75
|
+
nodes_motion = compute_node_data(mesh.merged(), faces_motion)
|
|
76
|
+
else:
|
|
77
|
+
nodes_motion = None
|
|
78
|
+
|
|
79
|
+
base_polydata = compute_vtk_polydata(mesh.merged())
|
|
80
|
+
mapper = vtk.vtkPolyDataMapper()
|
|
81
|
+
mapper.SetInputData(base_polydata)
|
|
82
|
+
|
|
83
|
+
actor = vtk.vtkActor()
|
|
84
|
+
if edges:
|
|
85
|
+
actor.GetProperty().EdgeVisibilityOn()
|
|
86
|
+
actor.GetProperty().SetInterpolationToGouraud()
|
|
87
|
+
actor.SetMapper(mapper)
|
|
88
|
+
|
|
89
|
+
if nodes_motion is not None or faces_colors is not None:
|
|
90
|
+
LOG.info(f"Precompute motions of {mesh.name} before animation.")
|
|
91
|
+
self._precomputed_polydatas[actor] = []
|
|
92
|
+
|
|
93
|
+
for i_frame in range(self.frames_per_loop):
|
|
94
|
+
new_polydata = vtk.vtkPolyData()
|
|
95
|
+
new_polydata.DeepCopy(base_polydata)
|
|
96
|
+
|
|
97
|
+
if nodes_motion is not None:
|
|
98
|
+
# Change points positions at frame i
|
|
99
|
+
current_deformation = (
|
|
100
|
+
np.abs(nodes_motion)*np.cos(np.angle(nodes_motion)-2*pi*i_frame/self.frames_per_loop)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
points = new_polydata.GetPoints()
|
|
104
|
+
for j in range(mesh.nb_vertices):
|
|
105
|
+
point = points.GetPoint(j)
|
|
106
|
+
point = np.asarray(point) + current_deformation[j]
|
|
107
|
+
points.SetPoint(j, tuple(point))
|
|
108
|
+
|
|
109
|
+
if faces_colors is not None:
|
|
110
|
+
# Evaluate scalar field at frame i
|
|
111
|
+
current_colors = (
|
|
112
|
+
np.abs(faces_colors)*np.cos(np.angle(faces_colors)-2*pi*i_frame/self.frames_per_loop)
|
|
113
|
+
)
|
|
114
|
+
max_val = max(abs(faces_colors))
|
|
115
|
+
vtk_faces_colors = vtk.vtkFloatArray()
|
|
116
|
+
for i, color in enumerate(current_colors):
|
|
117
|
+
vtk_faces_colors.InsertValue(i, (color+max_val)/(2*max_val))
|
|
118
|
+
new_polydata.GetCellData().SetScalars(vtk_faces_colors)
|
|
119
|
+
|
|
120
|
+
self._precomputed_polydatas[actor].append(new_polydata)
|
|
121
|
+
else:
|
|
122
|
+
self._precomputed_polydatas[actor] = None
|
|
123
|
+
|
|
124
|
+
self.actors.append(actor)
|
|
125
|
+
|
|
126
|
+
return actor
|
|
127
|
+
|
|
128
|
+
def add_body(self, body, faces_motion=None, faces_colors=None, edges=False):
|
|
129
|
+
"""Add an floating body to the scene.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
body: FloatingBody
|
|
134
|
+
The object to include in the scene.
|
|
135
|
+
faces_motion: dof, optional
|
|
136
|
+
The motion of the body defined at the center of the faces.
|
|
137
|
+
faces_colors: iterable of complex numbers, optional
|
|
138
|
+
Scalar field over the surface of the body that should be displayed with colors.
|
|
139
|
+
edges: bool, optional
|
|
140
|
+
Draw the edges of the mesh in the scene.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
vtk actor object
|
|
145
|
+
"""
|
|
146
|
+
actor = self._add_actor(body.mesh.merged(), faces_motion=faces_motion,
|
|
147
|
+
faces_colors=faces_colors, edges=edges)
|
|
148
|
+
if faces_colors is None:
|
|
149
|
+
actor.GetProperty().SetColor((1, 1, 0))
|
|
150
|
+
else:
|
|
151
|
+
lut = vtk.vtkLookupTable()
|
|
152
|
+
lut.SetNumberOfColors(50)
|
|
153
|
+
lut.SetHueRange(0, 0.6)
|
|
154
|
+
lut.SetSaturationRange(0.5, 0.5)
|
|
155
|
+
lut.SetValueRange(0.8, 0.8)
|
|
156
|
+
lut.Build()
|
|
157
|
+
actor.GetMapper().SetLookupTable(lut)
|
|
158
|
+
|
|
159
|
+
return actor
|
|
160
|
+
|
|
161
|
+
def add_free_surface(self, free_surface, faces_elevation):
|
|
162
|
+
"""Add the free surface to the scene.
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
free_surface: FreeSurface
|
|
167
|
+
The free surface object
|
|
168
|
+
faces_elevation: array of complex numbers
|
|
169
|
+
The elevation of each face of the meshed free surface given as a complex number.
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
vtk actor object
|
|
174
|
+
"""
|
|
175
|
+
faces_motion = np.array([(0, 0, elevation) for elevation in faces_elevation])
|
|
176
|
+
actor = self._add_actor(free_surface.mesh, faces_motion=faces_motion, faces_colors=faces_motion[:, 2])
|
|
177
|
+
|
|
178
|
+
lut = vtk.vtkLookupTable()
|
|
179
|
+
lut.SetNumberOfColors(50)
|
|
180
|
+
lut.SetHueRange(0.58, 0.58)
|
|
181
|
+
lut.SetSaturationRange(0.5, 0.5)
|
|
182
|
+
lut.SetValueRange(0.4, 0.6)
|
|
183
|
+
lut.Build()
|
|
184
|
+
actor.GetMapper().SetLookupTable(lut)
|
|
185
|
+
|
|
186
|
+
return actor
|
|
187
|
+
|
|
188
|
+
def _callback(self, renderer, event):
|
|
189
|
+
for actor in self.actors:
|
|
190
|
+
if self._precomputed_polydatas[actor] is not None:
|
|
191
|
+
actor.GetMapper().SetInputData(self._precomputed_polydatas[actor][self._current_frame % self.frames_per_loop])
|
|
192
|
+
renderer.GetRenderWindow().Render()
|
|
193
|
+
self._current_frame += 1
|
|
194
|
+
|
|
195
|
+
def run(self, camera_position=(-10.0, -10.0, 10.0), resolution=(1280, 720), top_light_intensity=0.5):
|
|
196
|
+
"""Run the animation.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
camera_position: 3-ple of floats, optional
|
|
201
|
+
The starting position of the camera in the scene.
|
|
202
|
+
resolution: 2-ple of ints, optional
|
|
203
|
+
Resolution of the video in pixels.
|
|
204
|
+
top_light_intensity: float between 0 and 1
|
|
205
|
+
Intensity of the light source at the top of the scene (default: 0.5)
|
|
206
|
+
"""
|
|
207
|
+
# Setup a renderer, render window, and interactor
|
|
208
|
+
renderer = vtk.vtkRenderer()
|
|
209
|
+
renderer.SetBackground(1, 1, 1) # Background color white
|
|
210
|
+
for actor in self.actors:
|
|
211
|
+
renderer.AddActor(actor)
|
|
212
|
+
renderer.Modified()
|
|
213
|
+
|
|
214
|
+
camera = vtk.vtkCamera()
|
|
215
|
+
camera.SetPosition(*camera_position)
|
|
216
|
+
camera.SetFocalPoint(0, 0, 0)
|
|
217
|
+
camera.SetViewUp(0, 0, 1)
|
|
218
|
+
renderer.SetActiveCamera(camera)
|
|
219
|
+
|
|
220
|
+
light = vtk.vtkLight()
|
|
221
|
+
light.SetLightTypeToHeadlight()
|
|
222
|
+
renderer.AddLight(light)
|
|
223
|
+
|
|
224
|
+
if top_light_intensity > 0.0:
|
|
225
|
+
light = vtk.vtkLight()
|
|
226
|
+
light.SetDirectionAngle(0, 0)
|
|
227
|
+
light.SetLightTypeToSceneLight()
|
|
228
|
+
light.SetIntensity(top_light_intensity)
|
|
229
|
+
renderer.AddLight(light)
|
|
230
|
+
|
|
231
|
+
render_window = vtk.vtkRenderWindow()
|
|
232
|
+
render_window.SetSize(*resolution)
|
|
233
|
+
render_window.SetWindowName("Capytaine animation")
|
|
234
|
+
render_window.AddRenderer(renderer)
|
|
235
|
+
|
|
236
|
+
render_window_interactor = vtk.vtkRenderWindowInteractor()
|
|
237
|
+
render_window_interactor.SetRenderWindow(render_window)
|
|
238
|
+
render_window_interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
|
|
239
|
+
|
|
240
|
+
render_window.Render()
|
|
241
|
+
|
|
242
|
+
render_window_interactor.Initialize() # Initialize must be called prior to creating timer events.
|
|
243
|
+
|
|
244
|
+
render_window_interactor.AddObserver('TimerEvent', self._callback)
|
|
245
|
+
render_window_interactor.CreateRepeatingTimer(int(1000 / self.fps))
|
|
246
|
+
|
|
247
|
+
render_window_interactor.Start()
|
|
248
|
+
|
|
249
|
+
# Run until stopped by user.
|
|
250
|
+
|
|
251
|
+
del render_window_interactor
|
|
252
|
+
del render_window
|
|
253
|
+
|
|
254
|
+
def save(self, filepath, nb_loops=1, camera_position=(-10.0, -10.0, 10.0), resolution=(1280, 720), top_light_intensity=0.5):
|
|
255
|
+
"""Save the animation in a video file.
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
filepath: string
|
|
260
|
+
Path of the output file.
|
|
261
|
+
nb_loop: int, optional
|
|
262
|
+
Number of periods to save in the file.
|
|
263
|
+
camera_position: 3-ple of floats, optional
|
|
264
|
+
The starting position of the camera in the scene.
|
|
265
|
+
resolution: 2-ple of ints, optional
|
|
266
|
+
Resolution of the video in pixels.
|
|
267
|
+
top_light_intensity: float between 0 and 1
|
|
268
|
+
Intensity of the light source at the top of the scene (default: 0.5)
|
|
269
|
+
"""
|
|
270
|
+
renderer = vtk.vtkRenderer()
|
|
271
|
+
renderer.SetBackground(1, 1, 1) # Background color white
|
|
272
|
+
for actor in self.actors:
|
|
273
|
+
renderer.AddActor(actor)
|
|
274
|
+
renderer.Modified()
|
|
275
|
+
|
|
276
|
+
camera = vtk.vtkCamera()
|
|
277
|
+
camera.SetPosition(*camera_position)
|
|
278
|
+
camera.SetFocalPoint(0, 0, 0)
|
|
279
|
+
camera.SetViewUp(0, 0, 1)
|
|
280
|
+
renderer.SetActiveCamera(camera)
|
|
281
|
+
|
|
282
|
+
light = vtk.vtkLight()
|
|
283
|
+
light.SetLightTypeToHeadlight()
|
|
284
|
+
renderer.AddLight(light)
|
|
285
|
+
|
|
286
|
+
if top_light_intensity > 0.0:
|
|
287
|
+
light = vtk.vtkLight()
|
|
288
|
+
light.SetDirectionAngle(0, 0)
|
|
289
|
+
light.SetLightTypeToSceneLight()
|
|
290
|
+
light.SetIntensity(top_light_intensity)
|
|
291
|
+
renderer.AddLight(light)
|
|
292
|
+
|
|
293
|
+
render_window = vtk.vtkRenderWindow()
|
|
294
|
+
render_window.SetSize(*resolution)
|
|
295
|
+
render_window.OffScreenRenderingOn()
|
|
296
|
+
render_window.AddRenderer(renderer)
|
|
297
|
+
|
|
298
|
+
image_filter = vtk.vtkWindowToImageFilter()
|
|
299
|
+
image_filter.SetInput(render_window)
|
|
300
|
+
image_filter.SetInputBufferTypeToRGB()
|
|
301
|
+
image_filter.ReadFrontBufferOff()
|
|
302
|
+
|
|
303
|
+
writer = vtk.vtkOggTheoraWriter()
|
|
304
|
+
writer.SetInputConnection(image_filter.GetOutputPort())
|
|
305
|
+
writer.SetFileName(filepath)
|
|
306
|
+
writer.SetRate(self.fps)
|
|
307
|
+
|
|
308
|
+
writer.Start()
|
|
309
|
+
|
|
310
|
+
for i_frame in range(nb_loops*self.frames_per_loop):
|
|
311
|
+
self._callback(renderer, None)
|
|
312
|
+
image_filter.Modified()
|
|
313
|
+
writer.Write()
|
|
314
|
+
|
|
315
|
+
writer.End()
|
|
316
|
+
render_window.Finalize()
|
|
317
|
+
|
|
318
|
+
del image_filter
|
|
319
|
+
del writer
|
|
320
|
+
del render_window
|
|
321
|
+
|
|
322
|
+
def embed_in_notebook(self, resolution=(640, 360), **kwargs):
|
|
323
|
+
from tempfile import mkstemp
|
|
324
|
+
from IPython.core.display import Video
|
|
325
|
+
# Requires Ipython 7.14 or higher, for this patch: https://github.com/ipython/ipython/pull/12212/
|
|
326
|
+
|
|
327
|
+
filepath = mkstemp(suffix=".ogv")[1]
|
|
328
|
+
self.save(filepath, nb_loops=1, resolution=resolution, **kwargs)
|
|
329
|
+
return Video(filepath, embed=True, width=resolution[0], html_attributes="controls loop autoplay")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""3D display of floating body with VTK."""
|
|
2
|
+
# Copyright (C) 2017-2019 Matthieu Ancellin
|
|
3
|
+
# See LICENSE file at <https://github.com/mancellin/capytaine>
|
|
4
|
+
|
|
5
|
+
from capytaine.tools.optional_imports import import_optional_dependency
|
|
6
|
+
from capytaine.ui.vtk.mesh_viewer import MeshViewer
|
|
7
|
+
|
|
8
|
+
vtk = import_optional_dependency("vtk")
|
|
9
|
+
|
|
10
|
+
class FloatingBodyViewer(MeshViewer):
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.dofs_data = {}
|
|
15
|
+
|
|
16
|
+
def add_body(self, body, **kwargs):
|
|
17
|
+
self.add_mesh(body.mesh, **kwargs)
|
|
18
|
+
if body.lid_mesh is not None:
|
|
19
|
+
self.add_mesh(body.lid_mesh, **kwargs)
|
|
20
|
+
|
|
21
|
+
for dof in body.dofs:
|
|
22
|
+
vtk_data_array = vtk.vtkFloatArray()
|
|
23
|
+
vtk_data_array.SetNumberOfComponents(3)
|
|
24
|
+
vtk_data_array.SetNumberOfTuples(body.mesh.nb_faces)
|
|
25
|
+
for i, vector in enumerate(body.dofs[dof]):
|
|
26
|
+
vtk_data_array.SetTuple3(i, *vector)
|
|
27
|
+
self.dofs_data[dof] = vtk_data_array
|
|
28
|
+
# vtk_polydata.GetCellData().SetVectors(vtk_data_array)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Tools for 3D displays with VTK."""
|
|
2
|
+
# Copyright (C) 2017-2019 Matthieu Ancellin
|
|
3
|
+
# See LICENSE file at <https://github.com/mancellin/capytaine>
|
|
4
|
+
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
from capytaine.meshes.meshes import Mesh
|
|
8
|
+
from capytaine.meshes.collections import CollectionOfMeshes
|
|
9
|
+
from capytaine.tools.optional_imports import import_optional_dependency
|
|
10
|
+
|
|
11
|
+
vtk = import_optional_dependency("vtk")
|
|
12
|
+
|
|
13
|
+
def compute_vtk_polydata(mesh: Union[Mesh, CollectionOfMeshes]):
|
|
14
|
+
"""Transform a mesh into vtkPolydata."""
|
|
15
|
+
|
|
16
|
+
# Create a vtkPoints object and store the points in it
|
|
17
|
+
points = vtk.vtkPoints()
|
|
18
|
+
for point in mesh.vertices:
|
|
19
|
+
points.InsertNextPoint(point)
|
|
20
|
+
|
|
21
|
+
# Create a vtkCellArray to store faces
|
|
22
|
+
faces = vtk.vtkCellArray()
|
|
23
|
+
for face_ids in mesh.faces:
|
|
24
|
+
if face_ids[0] == face_ids[-1]:
|
|
25
|
+
# Triangle
|
|
26
|
+
curface = face_ids[:3]
|
|
27
|
+
vtk_face = vtk.vtkTriangle()
|
|
28
|
+
else:
|
|
29
|
+
# Quadrangle
|
|
30
|
+
curface = face_ids[:4]
|
|
31
|
+
vtk_face = vtk.vtkQuad()
|
|
32
|
+
|
|
33
|
+
for idx, id in enumerate(curface):
|
|
34
|
+
vtk_face.GetPointIds().SetId(idx, id)
|
|
35
|
+
|
|
36
|
+
faces.InsertNextCell(vtk_face)
|
|
37
|
+
|
|
38
|
+
vtk_polydata = vtk.vtkPolyData()
|
|
39
|
+
vtk_polydata.SetPoints(points)
|
|
40
|
+
vtk_polydata.SetPolys(faces)
|
|
41
|
+
|
|
42
|
+
return vtk_polydata
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def compute_node_data(mesh: Union[Mesh, CollectionOfMeshes],
|
|
46
|
+
face_data):
|
|
47
|
+
"""Transform data defined at the center of the faces to data defined at the nodes of the mesh
|
|
48
|
+
by a simple averaging of the values of the neighboring faces.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
mesh: Mesh or CollectionOfMeshes
|
|
53
|
+
the mesh on which the face face_data are defined
|
|
54
|
+
face_data: numpy array of shape (mesh.nb_faces, ...)
|
|
55
|
+
the data defined on the center of the faces of the mesh
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
node_data: numpy array of shape (mesh.nb_vertices, ...)
|
|
60
|
+
the same data averaged on the nodes
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
import numpy as np
|
|
64
|
+
|
|
65
|
+
mesh = mesh.merged()
|
|
66
|
+
assert face_data.shape[0] == mesh.nb_faces
|
|
67
|
+
|
|
68
|
+
# Initialize output array
|
|
69
|
+
node_data_shape = (mesh.vertices.shape[0], ) + face_data.shape[1:]
|
|
70
|
+
node_data = np.zeros(node_data_shape, dtype=complex)
|
|
71
|
+
|
|
72
|
+
# Keep track of the number of faces near each vertex
|
|
73
|
+
faces_near_nodes_shape = (mesh.vertices.shape[0], ) + (1, ) * len(face_data.shape[1:])
|
|
74
|
+
nb_faces_near_nodes = np.zeros(faces_near_nodes_shape, dtype=np.int8)
|
|
75
|
+
|
|
76
|
+
for i, vertices in enumerate(mesh.faces):
|
|
77
|
+
for vertex in vertices:
|
|
78
|
+
nb_faces_near_nodes[vertex] += 1
|
|
79
|
+
node_data[vertex, ...] += face_data[i, ...]
|
|
80
|
+
|
|
81
|
+
node_data /= nb_faces_near_nodes
|
|
82
|
+
return node_data
|