pyedb 0.2.0__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 pyedb might be problematic. Click here for more details.
- pyedb/__init__.py +17 -0
- pyedb/dotnet/__init__.py +0 -0
- pyedb/dotnet/application/Variables.py +2261 -0
- pyedb/dotnet/application/__init__.py +0 -0
- pyedb/dotnet/clr_module.py +103 -0
- pyedb/dotnet/edb.py +4237 -0
- pyedb/dotnet/edb_core/__init__.py +1 -0
- pyedb/dotnet/edb_core/cell/__init__.py +0 -0
- pyedb/dotnet/edb_core/cell/hierarchy/__init__.py +0 -0
- pyedb/dotnet/edb_core/cell/hierarchy/model.py +66 -0
- pyedb/dotnet/edb_core/components.py +2669 -0
- pyedb/dotnet/edb_core/configuration.py +423 -0
- pyedb/dotnet/edb_core/definition/__init__.py +0 -0
- pyedb/dotnet/edb_core/definition/component_def.py +166 -0
- pyedb/dotnet/edb_core/definition/component_model.py +30 -0
- pyedb/dotnet/edb_core/definition/definition_obj.py +18 -0
- pyedb/dotnet/edb_core/definition/definitions.py +12 -0
- pyedb/dotnet/edb_core/dotnet/__init__.py +0 -0
- pyedb/dotnet/edb_core/dotnet/database.py +1218 -0
- pyedb/dotnet/edb_core/dotnet/layout.py +238 -0
- pyedb/dotnet/edb_core/dotnet/primitive.py +1517 -0
- pyedb/dotnet/edb_core/edb_data/__init__.py +0 -0
- pyedb/dotnet/edb_core/edb_data/components_data.py +938 -0
- pyedb/dotnet/edb_core/edb_data/connectable.py +113 -0
- pyedb/dotnet/edb_core/edb_data/control_file.py +1268 -0
- pyedb/dotnet/edb_core/edb_data/design_options.py +35 -0
- pyedb/dotnet/edb_core/edb_data/edbvalue.py +45 -0
- pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +330 -0
- pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +1607 -0
- pyedb/dotnet/edb_core/edb_data/layer_data.py +576 -0
- pyedb/dotnet/edb_core/edb_data/nets_data.py +281 -0
- pyedb/dotnet/edb_core/edb_data/obj_base.py +19 -0
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +2080 -0
- pyedb/dotnet/edb_core/edb_data/ports.py +287 -0
- pyedb/dotnet/edb_core/edb_data/primitives_data.py +1397 -0
- pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +2914 -0
- pyedb/dotnet/edb_core/edb_data/simulation_setup.py +716 -0
- pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1205 -0
- pyedb/dotnet/edb_core/edb_data/sources.py +514 -0
- pyedb/dotnet/edb_core/edb_data/terminals.py +632 -0
- pyedb/dotnet/edb_core/edb_data/utilities.py +148 -0
- pyedb/dotnet/edb_core/edb_data/variables.py +91 -0
- pyedb/dotnet/edb_core/general.py +181 -0
- pyedb/dotnet/edb_core/hfss.py +1646 -0
- pyedb/dotnet/edb_core/layout.py +1244 -0
- pyedb/dotnet/edb_core/layout_validation.py +272 -0
- pyedb/dotnet/edb_core/materials.py +939 -0
- pyedb/dotnet/edb_core/net_class.py +335 -0
- pyedb/dotnet/edb_core/nets.py +1215 -0
- pyedb/dotnet/edb_core/padstack.py +1389 -0
- pyedb/dotnet/edb_core/siwave.py +1427 -0
- pyedb/dotnet/edb_core/stackup.py +2703 -0
- pyedb/edb_logger.py +396 -0
- pyedb/generic/__init__.py +0 -0
- pyedb/generic/constants.py +1063 -0
- pyedb/generic/data_handlers.py +320 -0
- pyedb/generic/design_types.py +104 -0
- pyedb/generic/filesystem.py +150 -0
- pyedb/generic/general_methods.py +1535 -0
- pyedb/generic/plot.py +1840 -0
- pyedb/generic/process.py +285 -0
- pyedb/generic/settings.py +224 -0
- pyedb/ipc2581/__init__.py +0 -0
- pyedb/ipc2581/bom/__init__.py +0 -0
- pyedb/ipc2581/bom/bom.py +21 -0
- pyedb/ipc2581/bom/bom_item.py +32 -0
- pyedb/ipc2581/bom/characteristics.py +37 -0
- pyedb/ipc2581/bom/refdes.py +16 -0
- pyedb/ipc2581/content/__init__.py +0 -0
- pyedb/ipc2581/content/color.py +38 -0
- pyedb/ipc2581/content/content.py +55 -0
- pyedb/ipc2581/content/dictionary_color.py +29 -0
- pyedb/ipc2581/content/dictionary_fill.py +28 -0
- pyedb/ipc2581/content/dictionary_line.py +30 -0
- pyedb/ipc2581/content/entry_color.py +13 -0
- pyedb/ipc2581/content/entry_line.py +14 -0
- pyedb/ipc2581/content/fill.py +15 -0
- pyedb/ipc2581/content/layer_ref.py +10 -0
- pyedb/ipc2581/content/standard_geometries_dictionary.py +72 -0
- pyedb/ipc2581/ecad/__init__.py +0 -0
- pyedb/ipc2581/ecad/cad_data/__init__.py +0 -0
- pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +26 -0
- pyedb/ipc2581/ecad/cad_data/cad_data.py +37 -0
- pyedb/ipc2581/ecad/cad_data/component.py +41 -0
- pyedb/ipc2581/ecad/cad_data/drill.py +30 -0
- pyedb/ipc2581/ecad/cad_data/feature.py +54 -0
- pyedb/ipc2581/ecad/cad_data/layer.py +41 -0
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +151 -0
- pyedb/ipc2581/ecad/cad_data/logical_net.py +32 -0
- pyedb/ipc2581/ecad/cad_data/outline.py +25 -0
- pyedb/ipc2581/ecad/cad_data/package.py +104 -0
- pyedb/ipc2581/ecad/cad_data/padstack_def.py +38 -0
- pyedb/ipc2581/ecad/cad_data/padstack_hole_def.py +24 -0
- pyedb/ipc2581/ecad/cad_data/padstack_instance.py +62 -0
- pyedb/ipc2581/ecad/cad_data/padstack_pad_def.py +26 -0
- pyedb/ipc2581/ecad/cad_data/path.py +89 -0
- pyedb/ipc2581/ecad/cad_data/phy_net.py +80 -0
- pyedb/ipc2581/ecad/cad_data/pin.py +31 -0
- pyedb/ipc2581/ecad/cad_data/polygon.py +169 -0
- pyedb/ipc2581/ecad/cad_data/profile.py +40 -0
- pyedb/ipc2581/ecad/cad_data/stackup.py +31 -0
- pyedb/ipc2581/ecad/cad_data/stackup_group.py +42 -0
- pyedb/ipc2581/ecad/cad_data/stackup_layer.py +21 -0
- pyedb/ipc2581/ecad/cad_data/step.py +275 -0
- pyedb/ipc2581/ecad/cad_header.py +33 -0
- pyedb/ipc2581/ecad/ecad.py +19 -0
- pyedb/ipc2581/ecad/spec.py +46 -0
- pyedb/ipc2581/history_record.py +37 -0
- pyedb/ipc2581/ipc2581.py +387 -0
- pyedb/ipc2581/logistic_header.py +25 -0
- pyedb/misc/__init__.py +0 -0
- pyedb/misc/aedtlib_personalib_install.py +14 -0
- pyedb/misc/downloads.py +322 -0
- pyedb/misc/misc.py +67 -0
- pyedb/misc/pyedb.runtimeconfig.json +13 -0
- pyedb/misc/siw_feature_config/__init__.py +0 -0
- pyedb/misc/siw_feature_config/emc/__init__.py +0 -0
- pyedb/misc/siw_feature_config/emc/component_tags.py +46 -0
- pyedb/misc/siw_feature_config/emc/net_tags.py +37 -0
- pyedb/misc/siw_feature_config/emc/tag_library.py +62 -0
- pyedb/misc/siw_feature_config/emc/xml_generic.py +78 -0
- pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +179 -0
- pyedb/misc/utilities.py +27 -0
- pyedb/modeler/geometry_operators.py +2082 -0
- pyedb-0.2.0.dist-info/LICENSE +21 -0
- pyedb-0.2.0.dist-info/METADATA +208 -0
- pyedb-0.2.0.dist-info/RECORD +128 -0
- pyedb-0.2.0.dist-info/WHEEL +4 -0
pyedb/generic/plot.py
ADDED
|
@@ -0,0 +1,1840 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
import csv
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import math
|
|
6
|
+
import os
|
|
7
|
+
import tempfile
|
|
8
|
+
import time
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
11
|
+
from pyedb.generic.constants import AEDT_UNITS, CSS4_COLORS
|
|
12
|
+
from pyedb.generic.general_methods import (
|
|
13
|
+
is_ironpython,
|
|
14
|
+
open_file,
|
|
15
|
+
pyedb_function_handler,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if not is_ironpython: # pragma: no cover
|
|
19
|
+
try:
|
|
20
|
+
import numpy as np
|
|
21
|
+
except ImportError:
|
|
22
|
+
warnings.warn(
|
|
23
|
+
"The NumPy module is required to run some functionalities of PostProcess.\n"
|
|
24
|
+
"Install with \n\npip install numpy\n\nRequires CPython."
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import pyvista as pv
|
|
29
|
+
|
|
30
|
+
pyvista_available = True
|
|
31
|
+
except ImportError:
|
|
32
|
+
warnings.warn(
|
|
33
|
+
"The PyVista module is required to run some functionalities of PostProcess.\n"
|
|
34
|
+
"Install with \n\npip install pyvista\n\nRequires CPython."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
from matplotlib.patches import PathPatch
|
|
39
|
+
from matplotlib.path import Path
|
|
40
|
+
|
|
41
|
+
# Use matplotlib agg backend (non-interactive) when the CI is running.
|
|
42
|
+
if bool(int(os.getenv("PYEDB_CI_NO_DISPLAY", "0"))): # pragma: no cover
|
|
43
|
+
import matplotlib
|
|
44
|
+
|
|
45
|
+
matplotlib.use("Agg")
|
|
46
|
+
import matplotlib.pyplot as plt
|
|
47
|
+
|
|
48
|
+
except ImportError:
|
|
49
|
+
warnings.warn(
|
|
50
|
+
"The Matplotlib module is required to run some functionalities of PostProcess.\n"
|
|
51
|
+
"Install with \n\npip install matplotlib\n\nRequires CPython."
|
|
52
|
+
)
|
|
53
|
+
except:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pyedb_function_handler()
|
|
58
|
+
def get_structured_mesh(theta, phi, ff_data): # pragma: no cover
|
|
59
|
+
if ff_data.min() < 0:
|
|
60
|
+
ff_data_renorm = ff_data + np.abs(ff_data.min())
|
|
61
|
+
else:
|
|
62
|
+
ff_data_renorm = ff_data
|
|
63
|
+
phi_grid, theta_grid = np.meshgrid(phi, theta)
|
|
64
|
+
r_no_renorm = np.reshape(ff_data, (len(theta), len(phi)))
|
|
65
|
+
r = np.reshape(ff_data_renorm, (len(theta), len(phi)))
|
|
66
|
+
|
|
67
|
+
x = r * np.sin(theta_grid) * np.cos(phi_grid)
|
|
68
|
+
y = r * np.sin(theta_grid) * np.sin(phi_grid)
|
|
69
|
+
z = r * np.cos(theta_grid)
|
|
70
|
+
|
|
71
|
+
mag = np.ndarray.flatten(r_no_renorm, order="F")
|
|
72
|
+
ff_mesh = pv.StructuredGrid(x, y, z)
|
|
73
|
+
ff_mesh["FarFieldData"] = mag
|
|
74
|
+
return ff_mesh
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def is_notebook(): # pragma: no cover
|
|
78
|
+
"""Check if pyedb is running in Jupyter or not.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
bool
|
|
83
|
+
"""
|
|
84
|
+
try:
|
|
85
|
+
shell = get_ipython().__class__.__name__
|
|
86
|
+
if shell == "ZMQInteractiveShell":
|
|
87
|
+
return True # Jupyter notebook or qtconsole
|
|
88
|
+
else:
|
|
89
|
+
return False
|
|
90
|
+
except NameError:
|
|
91
|
+
return False # Probably standard Python interpreter
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def is_float(istring): # pragma: no cover
|
|
95
|
+
"""Convert a string to a float.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
istring : str
|
|
100
|
+
String to convert to a float.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
float
|
|
105
|
+
Converted float when successful, ``0`` when when failed.
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
return float(istring.strip())
|
|
109
|
+
except Exception:
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _triangle_vertex(elements_nodes, num_nodes_per_element, take_all_nodes=True): # pragma: no cover
|
|
114
|
+
trg_vertex = []
|
|
115
|
+
if num_nodes_per_element == 10 and take_all_nodes:
|
|
116
|
+
for e in elements_nodes:
|
|
117
|
+
trg_vertex.append([e[0], e[1], e[3]])
|
|
118
|
+
trg_vertex.append([e[1], e[2], e[4]])
|
|
119
|
+
trg_vertex.append([e[1], e[4], e[3]])
|
|
120
|
+
trg_vertex.append([e[3], e[4], e[5]])
|
|
121
|
+
|
|
122
|
+
trg_vertex.append([e[9], e[6], e[8]])
|
|
123
|
+
trg_vertex.append([e[6], e[0], e[3]])
|
|
124
|
+
trg_vertex.append([e[6], e[3], e[8]])
|
|
125
|
+
trg_vertex.append([e[8], e[3], e[5]])
|
|
126
|
+
|
|
127
|
+
trg_vertex.append([e[9], e[7], e[8]])
|
|
128
|
+
trg_vertex.append([e[7], e[2], e[4]])
|
|
129
|
+
trg_vertex.append([e[7], e[4], e[8]])
|
|
130
|
+
trg_vertex.append([e[8], e[4], e[5]])
|
|
131
|
+
|
|
132
|
+
trg_vertex.append([e[9], e[7], e[6]])
|
|
133
|
+
trg_vertex.append([e[7], e[2], e[1]])
|
|
134
|
+
trg_vertex.append([e[7], e[1], e[6]])
|
|
135
|
+
trg_vertex.append([e[6], e[1], e[0]])
|
|
136
|
+
|
|
137
|
+
elif num_nodes_per_element == 10 and not take_all_nodes:
|
|
138
|
+
for e in elements_nodes:
|
|
139
|
+
trg_vertex.append([e[0], e[2], e[5]])
|
|
140
|
+
trg_vertex.append([e[9], e[0], e[5]])
|
|
141
|
+
trg_vertex.append([e[9], e[2], e[0]])
|
|
142
|
+
trg_vertex.append([e[9], e[2], e[5]])
|
|
143
|
+
|
|
144
|
+
elif num_nodes_per_element == 6 and not take_all_nodes:
|
|
145
|
+
for e in elements_nodes:
|
|
146
|
+
trg_vertex.append([e[0], e[2], e[5]])
|
|
147
|
+
|
|
148
|
+
elif num_nodes_per_element == 6 and take_all_nodes:
|
|
149
|
+
for e in elements_nodes:
|
|
150
|
+
trg_vertex.append([e[0], e[1], e[3]])
|
|
151
|
+
trg_vertex.append([e[1], e[2], e[4]])
|
|
152
|
+
trg_vertex.append([e[1], e[4], e[3]])
|
|
153
|
+
trg_vertex.append([e[3], e[4], e[5]])
|
|
154
|
+
|
|
155
|
+
elif num_nodes_per_element == 4 and take_all_nodes:
|
|
156
|
+
for e in elements_nodes:
|
|
157
|
+
trg_vertex.append([e[0], e[1], e[3]])
|
|
158
|
+
trg_vertex.append([e[1], e[2], e[3]])
|
|
159
|
+
trg_vertex.append([e[0], e[1], e[2]])
|
|
160
|
+
trg_vertex.append([e[0], e[2], e[3]])
|
|
161
|
+
|
|
162
|
+
elif num_nodes_per_element == 3:
|
|
163
|
+
trg_vertex = elements_nodes
|
|
164
|
+
|
|
165
|
+
return trg_vertex
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _parse_aedtplt(filepath): # pragma: no cover
|
|
169
|
+
lines = []
|
|
170
|
+
vertices = []
|
|
171
|
+
faces = []
|
|
172
|
+
scalars = []
|
|
173
|
+
with open_file(filepath, "r") as f:
|
|
174
|
+
drawing_found = False
|
|
175
|
+
for line in f:
|
|
176
|
+
if "$begin Drawing" in line:
|
|
177
|
+
drawing_found = True
|
|
178
|
+
l_tmp = []
|
|
179
|
+
continue
|
|
180
|
+
if "$end Drawing" in line:
|
|
181
|
+
lines.append(l_tmp)
|
|
182
|
+
drawing_found = False
|
|
183
|
+
continue
|
|
184
|
+
if drawing_found:
|
|
185
|
+
l_tmp.append(line)
|
|
186
|
+
continue
|
|
187
|
+
for drawing_lines in lines:
|
|
188
|
+
bounding = []
|
|
189
|
+
elements = []
|
|
190
|
+
nodes_list = []
|
|
191
|
+
solution = []
|
|
192
|
+
for l in drawing_lines:
|
|
193
|
+
if "BoundingBox(" in l:
|
|
194
|
+
bounding = l[l.find("(") + 1 : -2].split(",")
|
|
195
|
+
bounding = [i.strip() for i in bounding]
|
|
196
|
+
if "Elements(" in l:
|
|
197
|
+
elements = l[l.find("(") + 1 : -2].split(",")
|
|
198
|
+
elements = [int(i.strip()) for i in elements]
|
|
199
|
+
if "Nodes(" in l:
|
|
200
|
+
nodes_list = l[l.find("(") + 1 : -2].split(",")
|
|
201
|
+
nodes_list = [float(i.strip()) for i in nodes_list]
|
|
202
|
+
if "ElemSolution(" in l:
|
|
203
|
+
# convert list of strings to list of floats
|
|
204
|
+
sols = l[l.find("(") + 1 : -2].split(",")
|
|
205
|
+
sols = [is_float(value) for value in sols]
|
|
206
|
+
# sols = [float(i.strip()) for i in sols]
|
|
207
|
+
num_solution_per_element = int(sols[2])
|
|
208
|
+
num_elements = elements[1]
|
|
209
|
+
num_nodes = elements[6]
|
|
210
|
+
sols = sols[3:]
|
|
211
|
+
if num_nodes == num_solution_per_element or num_solution_per_element // num_nodes < 3:
|
|
212
|
+
sols = [
|
|
213
|
+
sols[i : i + num_solution_per_element] for i in range(0, len(sols), num_solution_per_element)
|
|
214
|
+
]
|
|
215
|
+
solution = [sum(i) / num_solution_per_element for i in sols]
|
|
216
|
+
else:
|
|
217
|
+
sols = [
|
|
218
|
+
sols[i : i + num_solution_per_element] for i in range(0, len(sols), num_solution_per_element)
|
|
219
|
+
]
|
|
220
|
+
solution = [
|
|
221
|
+
[sum(i[::3]) / num_solution_per_element * 3 for i in sols],
|
|
222
|
+
[sum(i[1::3]) / num_solution_per_element * 3 for i in sols],
|
|
223
|
+
[sum(i[2::3]) / num_solution_per_element * 3 for i in sols],
|
|
224
|
+
]
|
|
225
|
+
|
|
226
|
+
nodes = [[nodes_list[i], nodes_list[i + 1], nodes_list[i + 2]] for i in range(0, len(nodes_list), 3)]
|
|
227
|
+
num_nodes = elements[0]
|
|
228
|
+
num_elements = elements[1]
|
|
229
|
+
elements = elements[2:]
|
|
230
|
+
element_type = elements[0]
|
|
231
|
+
num_nodes_per_element = elements[4]
|
|
232
|
+
header_length = 5
|
|
233
|
+
elements_nodes = []
|
|
234
|
+
# Todo Aedt 23R2 supports mixed elements size. To be implemented.
|
|
235
|
+
for i in range(0, len(elements), num_nodes_per_element + header_length):
|
|
236
|
+
elements_nodes.append([elements[i + header_length + n] for n in range(num_nodes_per_element)])
|
|
237
|
+
if solution:
|
|
238
|
+
take_all_nodes = True # solution case
|
|
239
|
+
else:
|
|
240
|
+
take_all_nodes = False # mesh case
|
|
241
|
+
trg_vertex = _triangle_vertex(elements_nodes, num_nodes_per_element, take_all_nodes)
|
|
242
|
+
# remove duplicates
|
|
243
|
+
nodup_list = [list(i) for i in list(set([frozenset(t) for t in trg_vertex]))]
|
|
244
|
+
log = True
|
|
245
|
+
if solution:
|
|
246
|
+
if isinstance(solution[0], list):
|
|
247
|
+
temps = []
|
|
248
|
+
for sol in solution:
|
|
249
|
+
sv = {}
|
|
250
|
+
sv_i = {}
|
|
251
|
+
sv = defaultdict(lambda: 0, sv)
|
|
252
|
+
sv_i = defaultdict(lambda: 1, sv_i)
|
|
253
|
+
for els, s in zip(elements_nodes, sol):
|
|
254
|
+
for el in els:
|
|
255
|
+
sv[el] = (sv[el] + s) / sv_i[el]
|
|
256
|
+
sv_i[el] = 2
|
|
257
|
+
temps.append(np.array([sv[v] for v in sorted(sv.keys())]))
|
|
258
|
+
else:
|
|
259
|
+
sv = {}
|
|
260
|
+
sv_i = {}
|
|
261
|
+
sv = defaultdict(lambda: 0, sv)
|
|
262
|
+
sv_i = defaultdict(lambda: 1, sv_i)
|
|
263
|
+
|
|
264
|
+
for els, s in zip(elements_nodes, solution):
|
|
265
|
+
for el in els:
|
|
266
|
+
sv[el] = (sv[el] + s) / sv_i[el]
|
|
267
|
+
sv_i[el] = 2
|
|
268
|
+
temps = np.array([sv[v] for v in sorted(sv.keys())])
|
|
269
|
+
scalars.append(temps)
|
|
270
|
+
if np.min(temps) <= 0:
|
|
271
|
+
log = False
|
|
272
|
+
array = [[3] + [j - 1 for j in i] for i in nodup_list]
|
|
273
|
+
|
|
274
|
+
faces.append(np.hstack(array))
|
|
275
|
+
vertices.append(np.array(nodes))
|
|
276
|
+
# surf = pv.PolyData(vertices, faces)
|
|
277
|
+
|
|
278
|
+
# surf.point_data[field.label] = temps
|
|
279
|
+
# field.log = log
|
|
280
|
+
# field._cached_polydata = surf
|
|
281
|
+
return vertices, faces, scalars, log
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@pyedb_function_handler()
|
|
285
|
+
def plot_polar_chart(
|
|
286
|
+
plot_data, size=(2000, 1000), show_legend=True, xlabel="", ylabel="", title="", snapshot_path=None
|
|
287
|
+
): # pragma: no cover
|
|
288
|
+
"""Create a matplotlib polar plot based on a list of data.
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
plot_data : list of list
|
|
293
|
+
List of plot data. Every item has to be in the following format
|
|
294
|
+
`[x points, y points, label]`.
|
|
295
|
+
size : tuple, optional
|
|
296
|
+
Image size in pixel (width, height).
|
|
297
|
+
show_legend : bool
|
|
298
|
+
Either to show legend or not.
|
|
299
|
+
xlabel : str
|
|
300
|
+
Plot X label.
|
|
301
|
+
ylabel : str
|
|
302
|
+
Plot Y label.
|
|
303
|
+
title : str
|
|
304
|
+
Plot Title label.
|
|
305
|
+
snapshot_path : str
|
|
306
|
+
Full path to image file if a snapshot is needed.
|
|
307
|
+
"""
|
|
308
|
+
dpi = 100.0
|
|
309
|
+
|
|
310
|
+
ax = plt.subplot(111, projection="polar")
|
|
311
|
+
|
|
312
|
+
label_id = 1
|
|
313
|
+
legend = []
|
|
314
|
+
for object in plot_data:
|
|
315
|
+
if len(object) == 3:
|
|
316
|
+
label = object[2]
|
|
317
|
+
else:
|
|
318
|
+
label = "Trace " + str(label_id)
|
|
319
|
+
theta = np.array(object[0])
|
|
320
|
+
r = np.array(object[1])
|
|
321
|
+
ax.plot(theta, r)
|
|
322
|
+
ax.grid(True)
|
|
323
|
+
ax.set_theta_zero_location("N")
|
|
324
|
+
ax.set_theta_direction(-1)
|
|
325
|
+
legend.append(label)
|
|
326
|
+
label_id += 1
|
|
327
|
+
|
|
328
|
+
ax.set(xlabel=xlabel, ylabel=ylabel, title=title)
|
|
329
|
+
if show_legend:
|
|
330
|
+
ax.legend(legend)
|
|
331
|
+
|
|
332
|
+
fig = plt.gcf()
|
|
333
|
+
fig.set_size_inches(size[0] / dpi, size[1] / dpi)
|
|
334
|
+
if snapshot_path:
|
|
335
|
+
fig.savefig(snapshot_path)
|
|
336
|
+
else:
|
|
337
|
+
fig.show()
|
|
338
|
+
return fig
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@pyedb_function_handler()
|
|
342
|
+
def plot_3d_chart(plot_data, size=(2000, 1000), xlabel="", ylabel="", title="", snapshot_path=None): # pragma: no cover
|
|
343
|
+
"""Create a matplotlib 3D plot based on a list of data.
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
plot_data : list of list
|
|
348
|
+
List of plot data. Every item has to be in the following format
|
|
349
|
+
`[x points, y points, z points, label]`.
|
|
350
|
+
size : tuple, optional
|
|
351
|
+
Image size in pixel (width, height).
|
|
352
|
+
xlabel : str
|
|
353
|
+
Plot X label.
|
|
354
|
+
ylabel : str
|
|
355
|
+
Plot Y label.
|
|
356
|
+
title : str
|
|
357
|
+
Plot Title label.
|
|
358
|
+
snapshot_path : str
|
|
359
|
+
Full path to image file if a snapshot is needed.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
:class:`matplotlib.plt`
|
|
364
|
+
Matplotlib fig object.
|
|
365
|
+
"""
|
|
366
|
+
dpi = 100.0
|
|
367
|
+
|
|
368
|
+
ax = plt.subplot(111, projection="3d")
|
|
369
|
+
|
|
370
|
+
if isinstance(plot_data[0], np.ndarray):
|
|
371
|
+
x = plot_data[0]
|
|
372
|
+
y = plot_data[1]
|
|
373
|
+
z = plot_data[2]
|
|
374
|
+
else:
|
|
375
|
+
theta_grid, phi_grid = np.meshgrid(plot_data[0], plot_data[1])
|
|
376
|
+
if isinstance(plot_data[2], list):
|
|
377
|
+
r = np.array(plot_data[2])
|
|
378
|
+
else:
|
|
379
|
+
r = plot_data[2]
|
|
380
|
+
x = r * np.sin(theta_grid) * np.cos(phi_grid)
|
|
381
|
+
y = r * np.sin(theta_grid) * np.sin(phi_grid)
|
|
382
|
+
z = r * np.cos(theta_grid)
|
|
383
|
+
ax.set(xlabel=xlabel, ylabel=ylabel, title=title)
|
|
384
|
+
ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=plt.get_cmap("jet"), linewidth=0, antialiased=True, alpha=0.8)
|
|
385
|
+
fig = plt.gcf()
|
|
386
|
+
fig.set_size_inches(size[0] / dpi, size[1] / dpi)
|
|
387
|
+
if snapshot_path:
|
|
388
|
+
fig.savefig(snapshot_path)
|
|
389
|
+
else:
|
|
390
|
+
fig.show()
|
|
391
|
+
return fig
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@pyedb_function_handler()
|
|
395
|
+
def plot_2d_chart(
|
|
396
|
+
plot_data, size=(2000, 1000), show_legend=True, xlabel="", ylabel="", title="", snapshot_path=None
|
|
397
|
+
): # pragma: no cover
|
|
398
|
+
"""Create a matplotlib plot based on a list of data.
|
|
399
|
+
|
|
400
|
+
Parameters
|
|
401
|
+
----------
|
|
402
|
+
plot_data : list of list
|
|
403
|
+
List of plot data. Every item has to be in the following format
|
|
404
|
+
`[x points, y points, label]`.
|
|
405
|
+
size : tuple, optional
|
|
406
|
+
Image size in pixel (width, height).
|
|
407
|
+
show_legend : bool, optional
|
|
408
|
+
Either to show legend or not. The default value is ``True``.
|
|
409
|
+
xlabel : str, optional
|
|
410
|
+
Plot X label. The default value is ``""``.
|
|
411
|
+
ylabel : str, optional
|
|
412
|
+
Plot Y label. The default value is ``""``.
|
|
413
|
+
title : str, optional
|
|
414
|
+
Plot Title label. The default value is ``""``.
|
|
415
|
+
snapshot_path : str, optional
|
|
416
|
+
Full path to image file if a snapshot is needed.
|
|
417
|
+
The default value is ``None``.
|
|
418
|
+
|
|
419
|
+
Returns
|
|
420
|
+
-------
|
|
421
|
+
:class:`matplotlib.plt`
|
|
422
|
+
Matplotlib fig object.
|
|
423
|
+
"""
|
|
424
|
+
dpi = 100.0
|
|
425
|
+
figsize = (size[0] / dpi, size[1] / dpi)
|
|
426
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
427
|
+
label_id = 1
|
|
428
|
+
for plo_obj in plot_data:
|
|
429
|
+
if len(plo_obj) == 3:
|
|
430
|
+
label = plo_obj[2]
|
|
431
|
+
else:
|
|
432
|
+
label = "Trace " + str(label_id)
|
|
433
|
+
if isinstance(plo_obj[0], np.ndarray):
|
|
434
|
+
x = plo_obj[0]
|
|
435
|
+
y = plo_obj[1]
|
|
436
|
+
else:
|
|
437
|
+
x = np.array([i for i, j in zip(plo_obj[0], plo_obj[1]) if j])
|
|
438
|
+
y = np.array([i for i in plo_obj[1] if i])
|
|
439
|
+
ax.plot(x, y, label=label)
|
|
440
|
+
label_id += 1
|
|
441
|
+
|
|
442
|
+
ax.set(xlabel=xlabel, ylabel=ylabel, title=title)
|
|
443
|
+
if show_legend:
|
|
444
|
+
ax.legend()
|
|
445
|
+
|
|
446
|
+
if snapshot_path:
|
|
447
|
+
fig.savefig(snapshot_path)
|
|
448
|
+
elif not is_notebook():
|
|
449
|
+
fig.show()
|
|
450
|
+
return fig
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
@pyedb_function_handler()
|
|
454
|
+
def plot_matplotlib(
|
|
455
|
+
plot_data,
|
|
456
|
+
size=(2000, 1000),
|
|
457
|
+
show_legend=True,
|
|
458
|
+
xlabel="",
|
|
459
|
+
ylabel="",
|
|
460
|
+
title="",
|
|
461
|
+
snapshot_path=None,
|
|
462
|
+
x_limits=None,
|
|
463
|
+
y_limits=None,
|
|
464
|
+
axis_equal=False,
|
|
465
|
+
annotations=None,
|
|
466
|
+
show=True,
|
|
467
|
+
):
|
|
468
|
+
"""Create a matplotlib plot based on a list of data.
|
|
469
|
+
|
|
470
|
+
Parameters
|
|
471
|
+
----------
|
|
472
|
+
plot_data : list of list
|
|
473
|
+
List of plot data. Every item has to be in the following format
|
|
474
|
+
For type ``fill``: `[x points, y points, color, label, alpha, type=="fill"]`.
|
|
475
|
+
For type ``path``: `[vertices, codes, color, label, alpha, type=="path"]`.
|
|
476
|
+
For type ``contour``: `[vertices, codes, color, label, alpha, line_width, type=="contour"]`.
|
|
477
|
+
size : tuple, optional
|
|
478
|
+
Image size in pixel (width, height). Default is `(2000, 1000)`.
|
|
479
|
+
show_legend : bool, optional
|
|
480
|
+
Either to show legend or not. Default is `True`.
|
|
481
|
+
xlabel : str, optional
|
|
482
|
+
Plot X label. Default is `""`.
|
|
483
|
+
ylabel : str, optional
|
|
484
|
+
Plot Y label. Default is `""`.
|
|
485
|
+
title : str, optional
|
|
486
|
+
Plot Title label. Default is `""`.
|
|
487
|
+
snapshot_path : str, optional
|
|
488
|
+
Full path to image file if a snapshot is needed. Default is `None`.
|
|
489
|
+
x_limits : list, optional
|
|
490
|
+
List of x limits (left and right). Default is `None`.
|
|
491
|
+
y_limits : list, optional
|
|
492
|
+
List of y limits (bottom and top). Default is `None`.
|
|
493
|
+
axis_equal : bool, optional
|
|
494
|
+
Whether to show the same scale on both axis or have a different scale based on plot size.
|
|
495
|
+
Default is `False`.
|
|
496
|
+
annotations : list, optional
|
|
497
|
+
List of annotations to add to the plot. The format is [x, y, string, dictionary of font options].
|
|
498
|
+
Default is `None`.
|
|
499
|
+
show : bool, optional
|
|
500
|
+
Whether to show the plot or return the matplotlib object. Default is `True`.
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
Returns
|
|
504
|
+
-------
|
|
505
|
+
:class:`matplotlib.plt`
|
|
506
|
+
Matplotlib fig object.
|
|
507
|
+
"""
|
|
508
|
+
dpi = 100.0
|
|
509
|
+
figsize = (size[0] / dpi, size[1] / dpi)
|
|
510
|
+
fig = plt.figure(figsize=figsize)
|
|
511
|
+
ax = fig.add_subplot(1, 1, 1)
|
|
512
|
+
if isinstance(plot_data, str):
|
|
513
|
+
plot_data = ast.literal_eval(plot_data)
|
|
514
|
+
for points in plot_data:
|
|
515
|
+
if points[-1] == "fill":
|
|
516
|
+
plt.fill(points[0], points[1], c=points[2], label=points[3], alpha=points[4])
|
|
517
|
+
elif points[-1] == "path":
|
|
518
|
+
path = Path(points[0], points[1])
|
|
519
|
+
patch = PathPatch(path, color=points[2], alpha=points[4], label=points[3])
|
|
520
|
+
ax.add_patch(patch)
|
|
521
|
+
elif points[-1] == "contour":
|
|
522
|
+
path = Path(points[0], points[1])
|
|
523
|
+
patch = PathPatch(path, color=points[2], alpha=points[4], label=points[3], fill=False, linewidth=points[5])
|
|
524
|
+
ax.add_patch(patch)
|
|
525
|
+
|
|
526
|
+
ax.set(xlabel=xlabel, ylabel=ylabel, title=title)
|
|
527
|
+
if show_legend:
|
|
528
|
+
ax.legend(loc="upper right")
|
|
529
|
+
|
|
530
|
+
# evaluating the limits
|
|
531
|
+
xmin = ymin = 1e30
|
|
532
|
+
xmax = ymax = -1e30
|
|
533
|
+
for points in plot_data:
|
|
534
|
+
if points[-1] == "fill":
|
|
535
|
+
xmin = min(xmin, min(points[0]))
|
|
536
|
+
xmax = max(xmax, max(points[0]))
|
|
537
|
+
ymin = min(ymin, min(points[1]))
|
|
538
|
+
ymax = max(ymax, max(points[1]))
|
|
539
|
+
else:
|
|
540
|
+
for p in points[0]:
|
|
541
|
+
xmin = min(xmin, p[0])
|
|
542
|
+
xmax = max(xmax, p[0])
|
|
543
|
+
ymin = min(ymin, p[1])
|
|
544
|
+
ymax = max(ymax, p[1])
|
|
545
|
+
if x_limits:
|
|
546
|
+
ax.set_xlim(x_limits)
|
|
547
|
+
else:
|
|
548
|
+
ax.set_xlim([xmin, xmax])
|
|
549
|
+
if y_limits:
|
|
550
|
+
ax.set_ylim(y_limits)
|
|
551
|
+
else:
|
|
552
|
+
ax.set_ylim([ymin, ymax])
|
|
553
|
+
|
|
554
|
+
if axis_equal:
|
|
555
|
+
ax.axis("equal")
|
|
556
|
+
|
|
557
|
+
if annotations:
|
|
558
|
+
for annotation in annotations:
|
|
559
|
+
plt.text(annotation[0], annotation[1], annotation[2], **annotation[3])
|
|
560
|
+
|
|
561
|
+
if snapshot_path:
|
|
562
|
+
plt.savefig(snapshot_path)
|
|
563
|
+
elif show:
|
|
564
|
+
plt.show()
|
|
565
|
+
return plt
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
@pyedb_function_handler()
|
|
569
|
+
def plot_contour(
|
|
570
|
+
qty_to_plot, x, y, size=(2000, 1600), xlabel="", ylabel="", title="", levels=64, snapshot_path=None
|
|
571
|
+
): # pragma: no cover
|
|
572
|
+
"""Create a matplotlib contour plot.
|
|
573
|
+
|
|
574
|
+
Parameters
|
|
575
|
+
----------
|
|
576
|
+
qty_to_plot : :class:`numpy.ndarray`
|
|
577
|
+
Quantity to plot.
|
|
578
|
+
x : :class:`numpy.ndarray`
|
|
579
|
+
X axis quantity.
|
|
580
|
+
y : :class:`numpy.ndarray`
|
|
581
|
+
Y axis quantity.
|
|
582
|
+
size : tuple, list, optional
|
|
583
|
+
Window Size. Default is `(2000,1600)`.
|
|
584
|
+
xlabel : str, optional
|
|
585
|
+
X Label. Default is `""`.
|
|
586
|
+
ylabel : str, optional
|
|
587
|
+
Y Label. Default is `""`.
|
|
588
|
+
title : str, optional
|
|
589
|
+
Plot Title Label. Default is `""`.
|
|
590
|
+
levels : int, optional
|
|
591
|
+
Colormap levels. Default is `64`.
|
|
592
|
+
snapshot_path : str, optional
|
|
593
|
+
Full path to image to save. Default is None.
|
|
594
|
+
|
|
595
|
+
Returns
|
|
596
|
+
-------
|
|
597
|
+
:class:`matplotlib.plt`
|
|
598
|
+
Matplotlib fig object.
|
|
599
|
+
"""
|
|
600
|
+
dpi = 100.0
|
|
601
|
+
figsize = (size[0] / dpi, size[1] / dpi)
|
|
602
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
603
|
+
if title:
|
|
604
|
+
plt.title(title)
|
|
605
|
+
if xlabel:
|
|
606
|
+
plt.xlabel(xlabel)
|
|
607
|
+
if ylabel:
|
|
608
|
+
plt.ylabel(ylabel)
|
|
609
|
+
|
|
610
|
+
plt.contourf(
|
|
611
|
+
x,
|
|
612
|
+
y,
|
|
613
|
+
qty_to_plot.T,
|
|
614
|
+
levels=levels,
|
|
615
|
+
cmap="jet",
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
plt.colorbar()
|
|
619
|
+
if snapshot_path:
|
|
620
|
+
plt.savefig(snapshot_path)
|
|
621
|
+
else:
|
|
622
|
+
plt.show()
|
|
623
|
+
return plt
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
class ObjClass(object):
|
|
627
|
+
"""Manages mesh files to be plotted in pyvista.
|
|
628
|
+
|
|
629
|
+
Parameters
|
|
630
|
+
----------
|
|
631
|
+
path : str
|
|
632
|
+
Full path to the file.
|
|
633
|
+
color : str or tuple
|
|
634
|
+
Can be a string with color name or a tuple with (r,g,b) values.
|
|
635
|
+
opacity : float
|
|
636
|
+
Value between 0 to 1 of opacity.
|
|
637
|
+
units : str
|
|
638
|
+
Model units.
|
|
639
|
+
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
def __init__(self, path, color, opacity, units): # pragma: no cover
|
|
643
|
+
self.path = path
|
|
644
|
+
self._color = (0, 0, 0)
|
|
645
|
+
self.color = color
|
|
646
|
+
self.opacity = opacity
|
|
647
|
+
self.units = units
|
|
648
|
+
self._cached_mesh = None
|
|
649
|
+
self._cached_polydata = None
|
|
650
|
+
self.name = os.path.splitext(os.path.basename(self.path))[0]
|
|
651
|
+
|
|
652
|
+
@property
|
|
653
|
+
def color(self):
|
|
654
|
+
return self._color
|
|
655
|
+
|
|
656
|
+
@color.setter
|
|
657
|
+
def color(self, value):
|
|
658
|
+
if isinstance(value, (tuple, list)):
|
|
659
|
+
self._color = value
|
|
660
|
+
elif value in CSS4_COLORS:
|
|
661
|
+
h = CSS4_COLORS[value].lstrip("#")
|
|
662
|
+
self._color = tuple(int(h[i : i + 2], 16) for i in (0, 2, 4))
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
class FieldClass(object):
|
|
666
|
+
"""Class to manage Field data to be plotted in pyvista.
|
|
667
|
+
|
|
668
|
+
Parameters
|
|
669
|
+
----------
|
|
670
|
+
path : str
|
|
671
|
+
Full path to the file.
|
|
672
|
+
log_scale : bool, optional
|
|
673
|
+
Either if the field has to be plotted log or not. The default value is ``True``.
|
|
674
|
+
coordinate_units : str, optional
|
|
675
|
+
Fields coordinates units. The default value is ``"meter"``.
|
|
676
|
+
opacity : float, optional
|
|
677
|
+
Value between 0 to 1 of opacity. The default value is ``1``.
|
|
678
|
+
color_map : str, optional
|
|
679
|
+
Color map of field plot. The default value is ``"rainbow"``.
|
|
680
|
+
label : str, optional
|
|
681
|
+
Name of the field. The default value is ``"Field"``.
|
|
682
|
+
tolerance : float, optional
|
|
683
|
+
Delauny tolerance value used for interpolating points. The default value is ``1e-3``.
|
|
684
|
+
headers : int, optional
|
|
685
|
+
Number of lines to of the file containing header info that has to be removed.
|
|
686
|
+
The default value is ``2``.
|
|
687
|
+
"""
|
|
688
|
+
|
|
689
|
+
def __init__(
|
|
690
|
+
self,
|
|
691
|
+
path,
|
|
692
|
+
log_scale=True,
|
|
693
|
+
coordinate_units="meter",
|
|
694
|
+
opacity=1,
|
|
695
|
+
color_map="jet",
|
|
696
|
+
label="Field",
|
|
697
|
+
tolerance=1e-3,
|
|
698
|
+
headers=2,
|
|
699
|
+
show_edge=True,
|
|
700
|
+
): # pragma: no cover
|
|
701
|
+
self.path = path
|
|
702
|
+
self.log_scale = log_scale
|
|
703
|
+
self.units = coordinate_units
|
|
704
|
+
self.opacity = opacity
|
|
705
|
+
self.color_map = color_map
|
|
706
|
+
self._cached_mesh = None
|
|
707
|
+
self._cached_polydata = None
|
|
708
|
+
self.label = label
|
|
709
|
+
self.name = os.path.splitext(os.path.basename(self.path))[0]
|
|
710
|
+
self.color = (255, 0, 0)
|
|
711
|
+
self.surface_mapping_tolerance = tolerance
|
|
712
|
+
self.header_lines = headers
|
|
713
|
+
self.show_edge = show_edge
|
|
714
|
+
self._is_frame = False
|
|
715
|
+
self.is_vector = False
|
|
716
|
+
self.vector_scale = 1.0
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
class CommonPlotter(object):
|
|
720
|
+
def __init__(self): # pragma: no cover
|
|
721
|
+
self._objects = []
|
|
722
|
+
self._fields = []
|
|
723
|
+
self._frames = []
|
|
724
|
+
self.show_axes = True
|
|
725
|
+
self.show_legend = True
|
|
726
|
+
self.show_grid = True
|
|
727
|
+
self.is_notebook = is_notebook()
|
|
728
|
+
self.gif_file = None
|
|
729
|
+
self._background_color = (255, 255, 255)
|
|
730
|
+
self._background_image = None
|
|
731
|
+
self.off_screen = False
|
|
732
|
+
if self.is_notebook:
|
|
733
|
+
self.windows_size = [600, 600]
|
|
734
|
+
else:
|
|
735
|
+
self.windows_size = [1024, 768]
|
|
736
|
+
self.pv = None
|
|
737
|
+
self._orientation = ["xy", 0, 0, 0]
|
|
738
|
+
self.units = "meter"
|
|
739
|
+
self.frame_per_seconds = 3
|
|
740
|
+
self._plot_meshes = []
|
|
741
|
+
self.range_min = None
|
|
742
|
+
self.range_max = None
|
|
743
|
+
self.image_file = None
|
|
744
|
+
self._camera_position = "yz"
|
|
745
|
+
self._view_up = (0.0, 1.0, 0.0)
|
|
746
|
+
self._focal_point = (0.0, 0.0, 0.0)
|
|
747
|
+
self._roll_angle = 0
|
|
748
|
+
self._azimuth_angle = 0
|
|
749
|
+
self._elevation_angle = 0
|
|
750
|
+
self._zoom = 1
|
|
751
|
+
self._isometric_view = True
|
|
752
|
+
self.bounding_box = True
|
|
753
|
+
self.color_bar = True
|
|
754
|
+
self.array_coordinates = []
|
|
755
|
+
self.meshes = None
|
|
756
|
+
self._x_scale = 1.0
|
|
757
|
+
self._y_scale = 1.0
|
|
758
|
+
self._z_scale = 1.0
|
|
759
|
+
self._convert_fields_in_db = False
|
|
760
|
+
self._log_multiplier = 10.0
|
|
761
|
+
|
|
762
|
+
@property
|
|
763
|
+
def convert_fields_in_db(self):
|
|
764
|
+
"""Either if convert the fields before plotting in dB. Log scale will be disabled.
|
|
765
|
+
|
|
766
|
+
Returns
|
|
767
|
+
-------
|
|
768
|
+
bool
|
|
769
|
+
"""
|
|
770
|
+
return self._convert_fields_in_db
|
|
771
|
+
|
|
772
|
+
@convert_fields_in_db.setter
|
|
773
|
+
def convert_fields_in_db(self, value):
|
|
774
|
+
self._convert_fields_in_db = value
|
|
775
|
+
for f in self.fields:
|
|
776
|
+
f._cached_polydata = None
|
|
777
|
+
for f in self.frames:
|
|
778
|
+
f._cached_polydata = None
|
|
779
|
+
|
|
780
|
+
@property
|
|
781
|
+
def log_multiplier(self):
|
|
782
|
+
"""Multiply the log value.
|
|
783
|
+
|
|
784
|
+
Returns
|
|
785
|
+
-------
|
|
786
|
+
float
|
|
787
|
+
"""
|
|
788
|
+
return self._log_multiplier
|
|
789
|
+
|
|
790
|
+
@log_multiplier.setter
|
|
791
|
+
def log_multiplier(self, value):
|
|
792
|
+
self._log_multiplier = value
|
|
793
|
+
|
|
794
|
+
@property
|
|
795
|
+
def x_scale(self):
|
|
796
|
+
"""Scale plot on X.
|
|
797
|
+
|
|
798
|
+
Returns
|
|
799
|
+
-------
|
|
800
|
+
float
|
|
801
|
+
"""
|
|
802
|
+
return self._x_scale
|
|
803
|
+
|
|
804
|
+
@x_scale.setter
|
|
805
|
+
def x_scale(self, value):
|
|
806
|
+
self._x_scale = value
|
|
807
|
+
|
|
808
|
+
@property
|
|
809
|
+
def y_scale(self):
|
|
810
|
+
"""Scale plot on Y.
|
|
811
|
+
|
|
812
|
+
Returns
|
|
813
|
+
-------
|
|
814
|
+
float
|
|
815
|
+
"""
|
|
816
|
+
return self._y_scale
|
|
817
|
+
|
|
818
|
+
@y_scale.setter
|
|
819
|
+
def y_scale(self, value):
|
|
820
|
+
self._y_scale = value
|
|
821
|
+
|
|
822
|
+
@property
|
|
823
|
+
def z_scale(self):
|
|
824
|
+
"""Scale plot on Z.
|
|
825
|
+
|
|
826
|
+
Returns
|
|
827
|
+
-------
|
|
828
|
+
float
|
|
829
|
+
"""
|
|
830
|
+
return self._z_scale
|
|
831
|
+
|
|
832
|
+
@z_scale.setter
|
|
833
|
+
def z_scale(self, value):
|
|
834
|
+
self._z_scale = value
|
|
835
|
+
|
|
836
|
+
@property
|
|
837
|
+
def isometric_view(self):
|
|
838
|
+
"""Enable or disable the default iso view.
|
|
839
|
+
|
|
840
|
+
Parameters
|
|
841
|
+
----------
|
|
842
|
+
value : bool
|
|
843
|
+
Either if iso view is enabled or disabled.
|
|
844
|
+
|
|
845
|
+
Returns
|
|
846
|
+
-------
|
|
847
|
+
bool
|
|
848
|
+
"""
|
|
849
|
+
return self._isometric_view
|
|
850
|
+
|
|
851
|
+
@isometric_view.setter
|
|
852
|
+
def isometric_view(self, value=True):
|
|
853
|
+
self._isometric_view = value
|
|
854
|
+
|
|
855
|
+
@property
|
|
856
|
+
def view_up(self):
|
|
857
|
+
"""Get/Set the camera view axis. It disables the default iso view.
|
|
858
|
+
|
|
859
|
+
Parameters
|
|
860
|
+
----------
|
|
861
|
+
value : tuple
|
|
862
|
+
Value of camera view position.
|
|
863
|
+
|
|
864
|
+
Returns
|
|
865
|
+
-------
|
|
866
|
+
tuple
|
|
867
|
+
"""
|
|
868
|
+
return self._view_up
|
|
869
|
+
|
|
870
|
+
@view_up.setter
|
|
871
|
+
def view_up(self, value):
|
|
872
|
+
if isinstance(value, list):
|
|
873
|
+
self._view_up = tuple(value)
|
|
874
|
+
else:
|
|
875
|
+
self._view_up = value
|
|
876
|
+
self.isometric_view = False
|
|
877
|
+
|
|
878
|
+
@property
|
|
879
|
+
def focal_point(self):
|
|
880
|
+
"""Get/Set the camera focal point value. It disables the default iso view.
|
|
881
|
+
|
|
882
|
+
Parameters
|
|
883
|
+
----------
|
|
884
|
+
value : tuple
|
|
885
|
+
Value of focal point position.
|
|
886
|
+
|
|
887
|
+
Returns
|
|
888
|
+
-------
|
|
889
|
+
tuple
|
|
890
|
+
"""
|
|
891
|
+
return self._focal_point
|
|
892
|
+
|
|
893
|
+
@focal_point.setter
|
|
894
|
+
def focal_point(self, value):
|
|
895
|
+
if isinstance(value, list):
|
|
896
|
+
self._focal_point = tuple(value)
|
|
897
|
+
else:
|
|
898
|
+
self._focal_point = value
|
|
899
|
+
self.isometric_view = False
|
|
900
|
+
|
|
901
|
+
@property
|
|
902
|
+
def camera_position(self):
|
|
903
|
+
"""Get or set the camera position value. This parameter disables the default iso view.
|
|
904
|
+
Value for the camera position. The value is for ``"xy"``, ``"xz"`` or ``"yz"``.
|
|
905
|
+
|
|
906
|
+
Returns
|
|
907
|
+
-------
|
|
908
|
+
str
|
|
909
|
+
"""
|
|
910
|
+
return self._camera_position
|
|
911
|
+
|
|
912
|
+
@camera_position.setter
|
|
913
|
+
def camera_position(self, value):
|
|
914
|
+
if isinstance(value, list):
|
|
915
|
+
self._camera_position = tuple(value)
|
|
916
|
+
else:
|
|
917
|
+
self._camera_position = value
|
|
918
|
+
self.isometric_view = False
|
|
919
|
+
|
|
920
|
+
@property
|
|
921
|
+
def roll_angle(self):
|
|
922
|
+
"""Get/Set the roll angle value. It disables the default iso view.
|
|
923
|
+
|
|
924
|
+
Parameters
|
|
925
|
+
----------
|
|
926
|
+
value : float
|
|
927
|
+
Value of roll angle in degrees.
|
|
928
|
+
|
|
929
|
+
Returns
|
|
930
|
+
-------
|
|
931
|
+
float
|
|
932
|
+
"""
|
|
933
|
+
return self._roll_angle
|
|
934
|
+
|
|
935
|
+
@roll_angle.setter
|
|
936
|
+
def roll_angle(self, value=20):
|
|
937
|
+
self._roll_angle = value
|
|
938
|
+
self.isometric_view = False
|
|
939
|
+
|
|
940
|
+
@property
|
|
941
|
+
def azimuth_angle(self):
|
|
942
|
+
"""Get/Set the azimuth angle value. It disables the default iso view.
|
|
943
|
+
|
|
944
|
+
Parameters
|
|
945
|
+
----------
|
|
946
|
+
value : float
|
|
947
|
+
Value of azimuth angle in degrees.
|
|
948
|
+
|
|
949
|
+
Returns
|
|
950
|
+
-------
|
|
951
|
+
float
|
|
952
|
+
"""
|
|
953
|
+
return self._azimuth_angle
|
|
954
|
+
|
|
955
|
+
@azimuth_angle.setter
|
|
956
|
+
def azimuth_angle(self, value=45):
|
|
957
|
+
self._azimuth_angle = value
|
|
958
|
+
self.use_default_iso_view = False
|
|
959
|
+
|
|
960
|
+
@property
|
|
961
|
+
def elevation_angle(self):
|
|
962
|
+
"""Get/Set the elevation angle value. It disables the default iso view.
|
|
963
|
+
|
|
964
|
+
Parameters
|
|
965
|
+
----------
|
|
966
|
+
value : float
|
|
967
|
+
Value of elevation angle in degrees.
|
|
968
|
+
|
|
969
|
+
Returns
|
|
970
|
+
-------
|
|
971
|
+
float
|
|
972
|
+
"""
|
|
973
|
+
return self._elevation_angle
|
|
974
|
+
|
|
975
|
+
@elevation_angle.setter
|
|
976
|
+
def elevation_angle(self, value=45):
|
|
977
|
+
self._elevation_angle = value
|
|
978
|
+
self.use_default_iso_view = False
|
|
979
|
+
|
|
980
|
+
@property
|
|
981
|
+
def zoom(self):
|
|
982
|
+
"""Get/Set the zoom value.
|
|
983
|
+
|
|
984
|
+
Parameters
|
|
985
|
+
----------
|
|
986
|
+
value : float
|
|
987
|
+
Value of zoom in degrees.
|
|
988
|
+
|
|
989
|
+
Returns
|
|
990
|
+
-------
|
|
991
|
+
float
|
|
992
|
+
"""
|
|
993
|
+
return self._zoom
|
|
994
|
+
|
|
995
|
+
@zoom.setter
|
|
996
|
+
def zoom(self, value=1):
|
|
997
|
+
self._zoom = value
|
|
998
|
+
|
|
999
|
+
@pyedb_function_handler()
|
|
1000
|
+
def set_orientation(self, camera_position="xy", roll_angle=0, azimuth_angle=45, elevation_angle=20):
|
|
1001
|
+
"""Change the plot default orientation.
|
|
1002
|
+
|
|
1003
|
+
Parameters
|
|
1004
|
+
----------
|
|
1005
|
+
camera_position : str
|
|
1006
|
+
Camera view. Default is `"xy"`. Options are `"xz"` and `"yz"`.
|
|
1007
|
+
roll_angle : int, float
|
|
1008
|
+
Roll camera angle on the specified the camera_position.
|
|
1009
|
+
azimuth_angle : int, float
|
|
1010
|
+
Azimuth angle of camera on the specified the camera_position.
|
|
1011
|
+
elevation_angle : int, float
|
|
1012
|
+
Elevation camera angle on the specified the camera_position.
|
|
1013
|
+
|
|
1014
|
+
Returns
|
|
1015
|
+
-------
|
|
1016
|
+
bool
|
|
1017
|
+
"""
|
|
1018
|
+
if camera_position in ["xy", "yz", "xz"]:
|
|
1019
|
+
self.camera_position = camera_position
|
|
1020
|
+
else:
|
|
1021
|
+
warnings.warn("Plane has to be one of xy, xz, yz.")
|
|
1022
|
+
self.roll_angle = roll_angle
|
|
1023
|
+
self.azimuth_angle = azimuth_angle
|
|
1024
|
+
self.elevation_angle = elevation_angle
|
|
1025
|
+
self.use_default_iso_view = False
|
|
1026
|
+
return True
|
|
1027
|
+
|
|
1028
|
+
@property
|
|
1029
|
+
def background_color(self):
|
|
1030
|
+
"""Background color.
|
|
1031
|
+
It can be a tuple of (r,g,b) or color name."""
|
|
1032
|
+
return self._background_color
|
|
1033
|
+
|
|
1034
|
+
@background_color.setter
|
|
1035
|
+
def background_color(self, value):
|
|
1036
|
+
if isinstance(value, (tuple, list)):
|
|
1037
|
+
self._background_color = value
|
|
1038
|
+
elif value in CSS4_COLORS:
|
|
1039
|
+
h = CSS4_COLORS[value].lstrip("#")
|
|
1040
|
+
self._background_color = tuple(int(h[i : i + 2], 16) for i in (0, 2, 4))
|
|
1041
|
+
|
|
1042
|
+
@property
|
|
1043
|
+
def background_image(self):
|
|
1044
|
+
"""Background image.
|
|
1045
|
+
|
|
1046
|
+
Returns
|
|
1047
|
+
-------
|
|
1048
|
+
str
|
|
1049
|
+
"""
|
|
1050
|
+
return self._background_image
|
|
1051
|
+
|
|
1052
|
+
@background_image.setter
|
|
1053
|
+
def background_image(self, value):
|
|
1054
|
+
if os.path.exists(value):
|
|
1055
|
+
self._background_image = value
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
class ModelPlotter(CommonPlotter):
|
|
1059
|
+
"""Manages the data to be plotted with ``pyvista``.
|
|
1060
|
+
|
|
1061
|
+
Examples
|
|
1062
|
+
--------
|
|
1063
|
+
This Class can be instantiated within PyEDB (with plot_model_object or different field plots
|
|
1064
|
+
and standalone).
|
|
1065
|
+
Here an example of standalone project
|
|
1066
|
+
|
|
1067
|
+
>>> model = ModelPlotter()
|
|
1068
|
+
>>> model.add_object(r'D:\Simulation\antenna.obj', (200,20,255), 0.6, "in")
|
|
1069
|
+
>>> model.add_object(r'D:\Simulation\helix.obj', (0,255,0), 0.5, "in")
|
|
1070
|
+
>>> model.add_field_from_file(r'D:\Simulation\helic_antenna.csv', True, "meter", 1)
|
|
1071
|
+
>>> model.background_color = (0,0,0)
|
|
1072
|
+
>>> model.plot()
|
|
1073
|
+
|
|
1074
|
+
And here an example of animation:
|
|
1075
|
+
|
|
1076
|
+
>>> model = ModelPlotter()
|
|
1077
|
+
>>> model.add_object(r'D:\Simulation\antenna.obj', (200,20,255), 0.6, "in")
|
|
1078
|
+
>>> model.add_object(r'D:\Simulation\helix.obj', (0,255,0), 0.5, "in")
|
|
1079
|
+
>>> frames = [r'D:\Simulation\helic_antenna.csv', r'D:\Simulation\helic_antenna_10.fld',
|
|
1080
|
+
... r'D:\Simulation\helic_antenna_20.fld', r'D:\Simulation\helic_antenna_30.fld',
|
|
1081
|
+
... r'D:\Simulation\helic_antenna_40.fld']
|
|
1082
|
+
>>> model.gif_file = r"D:\Simulation\animation.gif"
|
|
1083
|
+
>>> model.animate()
|
|
1084
|
+
"""
|
|
1085
|
+
|
|
1086
|
+
def __init__(self):
|
|
1087
|
+
CommonPlotter.__init__(self)
|
|
1088
|
+
|
|
1089
|
+
@property
|
|
1090
|
+
def fields(self):
|
|
1091
|
+
"""List of fields object.
|
|
1092
|
+
|
|
1093
|
+
Returns
|
|
1094
|
+
-------
|
|
1095
|
+
list of :class:`pyaedt.modules.AdvancedPostProcessing.FieldClass`
|
|
1096
|
+
"""
|
|
1097
|
+
return self._fields
|
|
1098
|
+
|
|
1099
|
+
@property
|
|
1100
|
+
def frames(self):
|
|
1101
|
+
"""Frames list for animation.
|
|
1102
|
+
|
|
1103
|
+
Returns
|
|
1104
|
+
-------
|
|
1105
|
+
list of :class:`pyaedt.modules.AdvancedPostProcessing.FieldClass`
|
|
1106
|
+
"""
|
|
1107
|
+
return self._frames
|
|
1108
|
+
|
|
1109
|
+
@property
|
|
1110
|
+
def objects(self):
|
|
1111
|
+
"""List of class objects.
|
|
1112
|
+
|
|
1113
|
+
Returns
|
|
1114
|
+
-------
|
|
1115
|
+
list of :class:`pyaedt.modules.AdvancedPostProcessing.ObjClass`
|
|
1116
|
+
"""
|
|
1117
|
+
return self._objects
|
|
1118
|
+
|
|
1119
|
+
@pyedb_function_handler()
|
|
1120
|
+
def add_object(self, cad_path, cad_color="dodgerblue", opacity=1, units="mm"):
|
|
1121
|
+
"""Add an mesh file to the scenario. It can be obj or any of pyvista supported files.
|
|
1122
|
+
|
|
1123
|
+
Parameters
|
|
1124
|
+
----------
|
|
1125
|
+
cad_path : str
|
|
1126
|
+
Full path to the file.
|
|
1127
|
+
cad_color : str or tuple
|
|
1128
|
+
Can be a string with color name or a tuple with (r,g,b) values.
|
|
1129
|
+
The default value is ``"dodgerblue"``.
|
|
1130
|
+
opacity : float
|
|
1131
|
+
Value between 0 to 1 of opacity. The default value is ``1``.
|
|
1132
|
+
units : str
|
|
1133
|
+
Model units. The default value is ``"mm"``.
|
|
1134
|
+
|
|
1135
|
+
Returns
|
|
1136
|
+
-------
|
|
1137
|
+
bool
|
|
1138
|
+
"""
|
|
1139
|
+
self._objects.append(ObjClass(cad_path, cad_color, opacity, units))
|
|
1140
|
+
self.units = units
|
|
1141
|
+
return True
|
|
1142
|
+
|
|
1143
|
+
@pyedb_function_handler()
|
|
1144
|
+
def add_field_from_file(
|
|
1145
|
+
self,
|
|
1146
|
+
field_path,
|
|
1147
|
+
log_scale=True,
|
|
1148
|
+
coordinate_units="meter",
|
|
1149
|
+
opacity=1,
|
|
1150
|
+
color_map="jet",
|
|
1151
|
+
label_name="Field",
|
|
1152
|
+
surface_mapping_tolerance=1e-3,
|
|
1153
|
+
header_lines=2,
|
|
1154
|
+
show_edges=True,
|
|
1155
|
+
):
|
|
1156
|
+
"""Add a field file to the scenario.
|
|
1157
|
+
It can be aedtplt, fld or csv file or any txt file with 4 column [x,y,z,field].
|
|
1158
|
+
If text file they have to be space separated column.
|
|
1159
|
+
|
|
1160
|
+
Parameters
|
|
1161
|
+
----------
|
|
1162
|
+
field_path : str
|
|
1163
|
+
Full path to the file.
|
|
1164
|
+
log_scale : bool
|
|
1165
|
+
Either if the field has to be plotted log or not.
|
|
1166
|
+
coordinate_units : str
|
|
1167
|
+
Fields coordinates units.
|
|
1168
|
+
opacity : float
|
|
1169
|
+
Value between 0 to 1 of opacity.
|
|
1170
|
+
color_map : str
|
|
1171
|
+
Color map of field plot. Default rainbow.
|
|
1172
|
+
label_name : str, optional
|
|
1173
|
+
Name of the field.
|
|
1174
|
+
surface_mapping_tolerance : float, optional
|
|
1175
|
+
Delauny tolerance value used for interpolating points.
|
|
1176
|
+
header_lines : int
|
|
1177
|
+
Number of lines to of the file containing header info that has to be removed.
|
|
1178
|
+
|
|
1179
|
+
Returns
|
|
1180
|
+
-------
|
|
1181
|
+
bool
|
|
1182
|
+
"""
|
|
1183
|
+
self._fields.append(
|
|
1184
|
+
FieldClass(
|
|
1185
|
+
field_path,
|
|
1186
|
+
log_scale,
|
|
1187
|
+
coordinate_units,
|
|
1188
|
+
opacity,
|
|
1189
|
+
color_map,
|
|
1190
|
+
label_name,
|
|
1191
|
+
surface_mapping_tolerance,
|
|
1192
|
+
header_lines,
|
|
1193
|
+
show_edges,
|
|
1194
|
+
)
|
|
1195
|
+
)
|
|
1196
|
+
|
|
1197
|
+
@pyedb_function_handler()
|
|
1198
|
+
def add_frames_from_file(
|
|
1199
|
+
self,
|
|
1200
|
+
field_files,
|
|
1201
|
+
log_scale=True,
|
|
1202
|
+
coordinate_units="meter",
|
|
1203
|
+
opacity=1,
|
|
1204
|
+
color_map="jet",
|
|
1205
|
+
label_name="Field",
|
|
1206
|
+
surface_mapping_tolerance=1e-3,
|
|
1207
|
+
header_lines=2,
|
|
1208
|
+
): # pragma: no cover
|
|
1209
|
+
"""Add a field file to the scenario. It can be aedtplt, fld or csv file.
|
|
1210
|
+
|
|
1211
|
+
Parameters
|
|
1212
|
+
----------
|
|
1213
|
+
field_files : list
|
|
1214
|
+
List of full path to frame file.
|
|
1215
|
+
log_scale : bool
|
|
1216
|
+
Either if the field has to be plotted log or not.
|
|
1217
|
+
coordinate_units : str
|
|
1218
|
+
Fields coordinates units.
|
|
1219
|
+
opacity : float
|
|
1220
|
+
Value between 0 to 1 of opacity.
|
|
1221
|
+
color_map : str
|
|
1222
|
+
Color map of field plot. Default rainbow.
|
|
1223
|
+
label_name : str, optional
|
|
1224
|
+
Name of the field.
|
|
1225
|
+
surface_mapping_tolerance : float, optional
|
|
1226
|
+
Delauny tolerance value used for interpolating points.
|
|
1227
|
+
header_lines : int
|
|
1228
|
+
Number of lines to of the file containing header info that has to be removed.
|
|
1229
|
+
|
|
1230
|
+
Returns
|
|
1231
|
+
-------
|
|
1232
|
+
bool
|
|
1233
|
+
"""
|
|
1234
|
+
for field in field_files:
|
|
1235
|
+
self._frames.append(
|
|
1236
|
+
FieldClass(
|
|
1237
|
+
field,
|
|
1238
|
+
log_scale,
|
|
1239
|
+
coordinate_units,
|
|
1240
|
+
opacity,
|
|
1241
|
+
color_map,
|
|
1242
|
+
label_name,
|
|
1243
|
+
surface_mapping_tolerance,
|
|
1244
|
+
header_lines,
|
|
1245
|
+
False,
|
|
1246
|
+
)
|
|
1247
|
+
)
|
|
1248
|
+
self._frames[-1]._is_frame = True
|
|
1249
|
+
|
|
1250
|
+
@pyedb_function_handler()
|
|
1251
|
+
def add_field_from_data(
|
|
1252
|
+
self,
|
|
1253
|
+
coordinates,
|
|
1254
|
+
fields_data,
|
|
1255
|
+
log_scale=True,
|
|
1256
|
+
coordinate_units="meter",
|
|
1257
|
+
opacity=1,
|
|
1258
|
+
color_map="jet",
|
|
1259
|
+
label_name="Field",
|
|
1260
|
+
surface_mapping_tolerance=1e-3,
|
|
1261
|
+
show_edges=True,
|
|
1262
|
+
): # pragma: no cover
|
|
1263
|
+
"""Add field data to the scenario.
|
|
1264
|
+
|
|
1265
|
+
Parameters
|
|
1266
|
+
----------
|
|
1267
|
+
coordinates : list of list
|
|
1268
|
+
List of list [x,y,z] coordinates.
|
|
1269
|
+
fields_data : list
|
|
1270
|
+
List of list Fields Value.
|
|
1271
|
+
log_scale : bool
|
|
1272
|
+
Either if the field has to be plotted log or not.
|
|
1273
|
+
coordinate_units : str
|
|
1274
|
+
Fields coordinates units.
|
|
1275
|
+
opacity : float
|
|
1276
|
+
Value between 0 to 1 of opacity.
|
|
1277
|
+
color_map : str
|
|
1278
|
+
Color map of field plot. Default rainbow.
|
|
1279
|
+
label_name : str, optional
|
|
1280
|
+
Name of the field.
|
|
1281
|
+
surface_mapping_tolerance : float, optional
|
|
1282
|
+
Delauny tolerance value used for interpolating points.
|
|
1283
|
+
|
|
1284
|
+
Returns
|
|
1285
|
+
-------
|
|
1286
|
+
bool
|
|
1287
|
+
"""
|
|
1288
|
+
self._fields.append(
|
|
1289
|
+
FieldClass(
|
|
1290
|
+
None, log_scale, coordinate_units, opacity, color_map, label_name, surface_mapping_tolerance, show_edges
|
|
1291
|
+
)
|
|
1292
|
+
)
|
|
1293
|
+
vertices = np.array(coordinates)
|
|
1294
|
+
filedata = pv.PolyData(vertices)
|
|
1295
|
+
filedata = filedata.delaunay_2d(tol=surface_mapping_tolerance)
|
|
1296
|
+
filedata.point_data[self.fields[-1].label] = np.array(fields_data)
|
|
1297
|
+
self.fields[-1]._cached_polydata = filedata
|
|
1298
|
+
|
|
1299
|
+
@pyedb_function_handler()
|
|
1300
|
+
def _read_mesh_files(self, read_frames=False): # pragma: no cover
|
|
1301
|
+
for cad in self.objects:
|
|
1302
|
+
if not cad._cached_polydata:
|
|
1303
|
+
filedata = pv.read(cad.path)
|
|
1304
|
+
cad._cached_polydata = filedata
|
|
1305
|
+
color_cad = [i / 255 for i in cad.color]
|
|
1306
|
+
cad._cached_mesh = self.pv.add_mesh(cad._cached_polydata, color=color_cad, opacity=cad.opacity)
|
|
1307
|
+
if self.meshes:
|
|
1308
|
+
self.meshes += cad._cached_polydata
|
|
1309
|
+
else:
|
|
1310
|
+
self.meshes = cad._cached_polydata
|
|
1311
|
+
obj_to_iterate = [i for i in self._fields]
|
|
1312
|
+
if read_frames:
|
|
1313
|
+
for i in self.frames:
|
|
1314
|
+
obj_to_iterate.append(i)
|
|
1315
|
+
for field in obj_to_iterate:
|
|
1316
|
+
if field.path and not field._cached_polydata:
|
|
1317
|
+
if ".aedtplt" in field.path:
|
|
1318
|
+
vertices, faces, scalars, log1 = _parse_aedtplt(field.path)
|
|
1319
|
+
if self.convert_fields_in_db:
|
|
1320
|
+
scalars = [np.multiply(np.log10(i), self.log_multiplier) for i in scalars]
|
|
1321
|
+
fields_vals = pv.PolyData(vertices[0], faces[0])
|
|
1322
|
+
field._cached_polydata = fields_vals
|
|
1323
|
+
if isinstance(scalars[0], list):
|
|
1324
|
+
vector_scale = (max(fields_vals.bounds) - min(fields_vals.bounds)) / (
|
|
1325
|
+
50 * (np.vstack(scalars[0]).max() - np.vstack(scalars[0]).min())
|
|
1326
|
+
)
|
|
1327
|
+
|
|
1328
|
+
field._cached_polydata["vectors"] = np.vstack(scalars[0]).T * vector_scale
|
|
1329
|
+
field.label = "Vector " + field.label
|
|
1330
|
+
field._cached_polydata.point_data[field.label] = np.array(
|
|
1331
|
+
[np.linalg.norm(x) for x in np.vstack(scalars[0]).T]
|
|
1332
|
+
)
|
|
1333
|
+
|
|
1334
|
+
field.is_vector = True
|
|
1335
|
+
else:
|
|
1336
|
+
field._cached_polydata.point_data[field.label] = scalars[0]
|
|
1337
|
+
field.is_vector = False
|
|
1338
|
+
field.log = log1
|
|
1339
|
+
else:
|
|
1340
|
+
nodes = []
|
|
1341
|
+
values = []
|
|
1342
|
+
is_vector = False
|
|
1343
|
+
with open_file(field.path, "r") as f:
|
|
1344
|
+
try:
|
|
1345
|
+
lines = f.read().splitlines()[field.header_lines :]
|
|
1346
|
+
if ".csv" in field.path:
|
|
1347
|
+
sniffer = csv.Sniffer()
|
|
1348
|
+
delimiter = sniffer.sniff(lines[0]).delimiter
|
|
1349
|
+
else:
|
|
1350
|
+
delimiter = " "
|
|
1351
|
+
if len(lines) > 2000 and not field._is_frame:
|
|
1352
|
+
lines = list(dict.fromkeys(lines))
|
|
1353
|
+
# decimate = 2
|
|
1354
|
+
# del lines[decimate - 1 :: decimate]
|
|
1355
|
+
except:
|
|
1356
|
+
lines = []
|
|
1357
|
+
for line in lines:
|
|
1358
|
+
tmp = line.strip().split(delimiter)
|
|
1359
|
+
if len(tmp) < 4:
|
|
1360
|
+
continue
|
|
1361
|
+
nodes.append([float(tmp[0]), float(tmp[1]), float(tmp[2])])
|
|
1362
|
+
if len(tmp) == 6:
|
|
1363
|
+
values.append([float(tmp[3]), float(tmp[4]), float(tmp[5])])
|
|
1364
|
+
is_vector = True
|
|
1365
|
+
elif len(tmp) == 9:
|
|
1366
|
+
values.append([float(tmp[3]), float(tmp[5]), float(tmp[7])])
|
|
1367
|
+
is_vector = True
|
|
1368
|
+
else:
|
|
1369
|
+
values.append(float(tmp[3]))
|
|
1370
|
+
if self.convert_fields_in_db:
|
|
1371
|
+
if not isinstance(values[0], list):
|
|
1372
|
+
values = [self.log_multiplier * math.log10(abs(i)) for i in values]
|
|
1373
|
+
else:
|
|
1374
|
+
values = [[self.log_multiplier * math.log10(abs(i)) for i in value] for value in values]
|
|
1375
|
+
if nodes:
|
|
1376
|
+
try:
|
|
1377
|
+
conv = 1 / AEDT_UNITS["Length"][self.units]
|
|
1378
|
+
except:
|
|
1379
|
+
conv = 1
|
|
1380
|
+
vertices = np.array(nodes) * conv
|
|
1381
|
+
filedata = pv.PolyData(vertices)
|
|
1382
|
+
if is_vector:
|
|
1383
|
+
vector_scale = (max(filedata.bounds) - min(filedata.bounds)) / (
|
|
1384
|
+
20 * (np.vstack(values).max() - np.vstack(values).min())
|
|
1385
|
+
)
|
|
1386
|
+
filedata["vectors"] = np.vstack(values) * vector_scale
|
|
1387
|
+
field.label = "Vector " + field.label
|
|
1388
|
+
filedata.point_data[field.label] = np.array([np.linalg.norm(x) for x in np.vstack(values)])
|
|
1389
|
+
field.is_vector = True
|
|
1390
|
+
else:
|
|
1391
|
+
filedata = filedata.delaunay_2d(tol=field.surface_mapping_tolerance)
|
|
1392
|
+
filedata.point_data[field.label] = np.array(values)
|
|
1393
|
+
field._cached_polydata = filedata
|
|
1394
|
+
|
|
1395
|
+
@pyedb_function_handler()
|
|
1396
|
+
def _add_buttons(self): # pragma: no cover
|
|
1397
|
+
size = int(self.pv.window_size[1] / 40)
|
|
1398
|
+
startpos = self.pv.window_size[1] - 2 * size
|
|
1399
|
+
endpos = 100
|
|
1400
|
+
color = self.pv.background_color
|
|
1401
|
+
axes_color = [0 if i >= 0.5 else 255 for i in color]
|
|
1402
|
+
buttons = []
|
|
1403
|
+
texts = []
|
|
1404
|
+
max_elements = (startpos - endpos) // (size + (size // 10))
|
|
1405
|
+
|
|
1406
|
+
class SetVisibilityCallback:
|
|
1407
|
+
"""Helper callback to keep a reference to the actor being modified."""
|
|
1408
|
+
|
|
1409
|
+
def __init__(self, actor):
|
|
1410
|
+
self.actor = actor
|
|
1411
|
+
|
|
1412
|
+
def __call__(self, state):
|
|
1413
|
+
try:
|
|
1414
|
+
self.actor._cached_mesh.SetVisibility(state)
|
|
1415
|
+
except AttributeError:
|
|
1416
|
+
self.actor.SetVisibility(state)
|
|
1417
|
+
|
|
1418
|
+
class ChangePageCallback:
|
|
1419
|
+
"""Helper callback to keep a reference to the actor being modified."""
|
|
1420
|
+
|
|
1421
|
+
def __init__(self, plot, actor, axes_color):
|
|
1422
|
+
self.plot = plot
|
|
1423
|
+
self.actors = actor
|
|
1424
|
+
self.id = 0
|
|
1425
|
+
self.endpos = 100
|
|
1426
|
+
self.size = int(plot.window_size[1] / 40)
|
|
1427
|
+
self.startpos = plot.window_size[1] - 2 * self.size
|
|
1428
|
+
self.max_elements = (self.startpos - self.endpos) // (self.size + (self.size // 10))
|
|
1429
|
+
self.i = self.max_elements
|
|
1430
|
+
self.axes_color = axes_color
|
|
1431
|
+
self.text = []
|
|
1432
|
+
|
|
1433
|
+
def __call__(self, state):
|
|
1434
|
+
try:
|
|
1435
|
+
self.plot.button_widgets = [self.plot.button_widgets[0]]
|
|
1436
|
+
except:
|
|
1437
|
+
self.plot.button_widgets = []
|
|
1438
|
+
self.id += 1
|
|
1439
|
+
k = 0
|
|
1440
|
+
startpos = self.startpos
|
|
1441
|
+
while k < self.max_elements:
|
|
1442
|
+
if len(self.text) > k:
|
|
1443
|
+
self.plot.remove_actor(self.text[k])
|
|
1444
|
+
k += 1
|
|
1445
|
+
self.text = []
|
|
1446
|
+
k = 0
|
|
1447
|
+
|
|
1448
|
+
while k < min(self.max_elements, len(self.actors)):
|
|
1449
|
+
if self.i >= len(self.actors):
|
|
1450
|
+
self.i = 0
|
|
1451
|
+
self.id = 0
|
|
1452
|
+
callback = SetVisibilityCallback(self.actors[self.i])
|
|
1453
|
+
self.plot.add_checkbox_button_widget(
|
|
1454
|
+
callback,
|
|
1455
|
+
value=self.actors[self.i]._cached_mesh.GetVisibility() == 1,
|
|
1456
|
+
position=(5.0, startpos),
|
|
1457
|
+
size=self.size,
|
|
1458
|
+
border_size=1,
|
|
1459
|
+
color_on=[i / 255 for i in self.actors[self.i].color],
|
|
1460
|
+
color_off="grey",
|
|
1461
|
+
background_color=None,
|
|
1462
|
+
)
|
|
1463
|
+
self.text.append(
|
|
1464
|
+
self.plot.add_text(
|
|
1465
|
+
self.actors[self.i].name,
|
|
1466
|
+
position=(25.0, startpos),
|
|
1467
|
+
font_size=self.size // 3,
|
|
1468
|
+
color=self.axes_color,
|
|
1469
|
+
)
|
|
1470
|
+
)
|
|
1471
|
+
startpos = startpos - self.size - (self.size // 10)
|
|
1472
|
+
k += 1
|
|
1473
|
+
self.i += 1
|
|
1474
|
+
|
|
1475
|
+
if len(self.objects) > 100:
|
|
1476
|
+
actors = [i for i in self._fields if i._cached_mesh] + self._objects
|
|
1477
|
+
else:
|
|
1478
|
+
actors = [i for i in self._fields if i._cached_mesh] + self._objects
|
|
1479
|
+
# if texts and len(texts) < len(actors):
|
|
1480
|
+
callback = ChangePageCallback(self.pv, actors, axes_color)
|
|
1481
|
+
|
|
1482
|
+
callback.__call__(False)
|
|
1483
|
+
if callback.max_elements < len(actors):
|
|
1484
|
+
self.pv.add_checkbox_button_widget(
|
|
1485
|
+
callback,
|
|
1486
|
+
value=True,
|
|
1487
|
+
position=(5.0, self.pv.window_size[1]),
|
|
1488
|
+
size=int(1.5 * size),
|
|
1489
|
+
border_size=2,
|
|
1490
|
+
color_on=axes_color,
|
|
1491
|
+
color_off=axes_color,
|
|
1492
|
+
)
|
|
1493
|
+
self.pv.add_text("Next", position=(50.0, self.pv.window_size[1]), font_size=size // 3, color="grey")
|
|
1494
|
+
self.pv.button_widgets.insert(
|
|
1495
|
+
0, self.pv.button_widgets.pop(self.pv.button_widgets.index(self.pv.button_widgets[-1]))
|
|
1496
|
+
)
|
|
1497
|
+
|
|
1498
|
+
@pyedb_function_handler()
|
|
1499
|
+
def plot(self, export_image_path=None): # pragma: no cover
|
|
1500
|
+
"""Plot the current available Data. With `s` key a screenshot is saved in export_image_path or in tempdir.
|
|
1501
|
+
|
|
1502
|
+
Parameters
|
|
1503
|
+
----------
|
|
1504
|
+
|
|
1505
|
+
export_image_path : str
|
|
1506
|
+
Path to image to save.
|
|
1507
|
+
|
|
1508
|
+
Returns
|
|
1509
|
+
-------
|
|
1510
|
+
bool
|
|
1511
|
+
"""
|
|
1512
|
+
self.pv = pv.Plotter(notebook=self.is_notebook, off_screen=self.off_screen, window_size=self.windows_size)
|
|
1513
|
+
self.pv.enable_ssao()
|
|
1514
|
+
self.pv.enable_parallel_projection()
|
|
1515
|
+
self.meshes = None
|
|
1516
|
+
if self.background_image:
|
|
1517
|
+
self.pv.add_background_image(self.background_image)
|
|
1518
|
+
else:
|
|
1519
|
+
self.pv.background_color = [i / 255 for i in self.background_color]
|
|
1520
|
+
self._read_mesh_files()
|
|
1521
|
+
axes_color = [0 if i >= 128 else 255 for i in self.background_color]
|
|
1522
|
+
if self.color_bar:
|
|
1523
|
+
sargs = dict(
|
|
1524
|
+
title_font_size=10,
|
|
1525
|
+
label_font_size=10,
|
|
1526
|
+
shadow=True,
|
|
1527
|
+
n_labels=9,
|
|
1528
|
+
italic=True,
|
|
1529
|
+
fmt="%.1f",
|
|
1530
|
+
font_family="arial",
|
|
1531
|
+
interactive=True,
|
|
1532
|
+
color=axes_color,
|
|
1533
|
+
vertical=False,
|
|
1534
|
+
)
|
|
1535
|
+
else:
|
|
1536
|
+
sargs = dict(
|
|
1537
|
+
position_x=2,
|
|
1538
|
+
position_y=2,
|
|
1539
|
+
)
|
|
1540
|
+
for field in self._fields:
|
|
1541
|
+
if field.is_vector:
|
|
1542
|
+
field._cached_polydata.set_active_vectors("vectors")
|
|
1543
|
+
field._cached_polydata["vectors"] = field._cached_polydata["vectors"] * field.vector_scale
|
|
1544
|
+
self.pv.add_mesh(
|
|
1545
|
+
field._cached_polydata.arrows,
|
|
1546
|
+
scalars=field.label,
|
|
1547
|
+
log_scale=False if self.convert_fields_in_db else field.log_scale,
|
|
1548
|
+
scalar_bar_args=sargs,
|
|
1549
|
+
cmap=field.color_map,
|
|
1550
|
+
)
|
|
1551
|
+
field._cached_polydata["vectors"] = field._cached_polydata["vectors"] / field.vector_scale
|
|
1552
|
+
elif self.range_max is not None and self.range_min is not None:
|
|
1553
|
+
field._cached_mesh = self.pv.add_mesh(
|
|
1554
|
+
field._cached_polydata,
|
|
1555
|
+
scalars=field.label,
|
|
1556
|
+
log_scale=False if self.convert_fields_in_db else field.log_scale,
|
|
1557
|
+
scalar_bar_args=sargs,
|
|
1558
|
+
cmap=field.color_map,
|
|
1559
|
+
clim=[self.range_min, self.range_max],
|
|
1560
|
+
opacity=field.opacity,
|
|
1561
|
+
show_edges=field.show_edge,
|
|
1562
|
+
)
|
|
1563
|
+
else:
|
|
1564
|
+
field._cached_mesh = self.pv.add_mesh(
|
|
1565
|
+
field._cached_polydata,
|
|
1566
|
+
scalars=field.label,
|
|
1567
|
+
log_scale=False if self.convert_fields_in_db else field.log_scale,
|
|
1568
|
+
scalar_bar_args=sargs,
|
|
1569
|
+
cmap=field.color_map,
|
|
1570
|
+
opacity=field.opacity,
|
|
1571
|
+
show_edges=field.show_edge,
|
|
1572
|
+
)
|
|
1573
|
+
|
|
1574
|
+
self.pv.set_scale(self.x_scale, self.y_scale, self.z_scale)
|
|
1575
|
+
|
|
1576
|
+
if self.show_legend:
|
|
1577
|
+
self._add_buttons()
|
|
1578
|
+
|
|
1579
|
+
if self.show_axes:
|
|
1580
|
+
self.pv.show_axes()
|
|
1581
|
+
if not self.is_notebook and self.show_grid:
|
|
1582
|
+
self.pv.show_grid(color=tuple(axes_color), grid=self.show_grid, fmt="%.3e")
|
|
1583
|
+
if self.bounding_box:
|
|
1584
|
+
self.pv.add_bounding_box(color=tuple(axes_color))
|
|
1585
|
+
self.pv.set_focus(self.pv.mesh.center)
|
|
1586
|
+
|
|
1587
|
+
if not self.isometric_view:
|
|
1588
|
+
if isinstance(self.camera_position, (tuple, list)):
|
|
1589
|
+
self.pv.camera.position = self.camera_position
|
|
1590
|
+
self.pv.camera.focal_point = self.focal_point
|
|
1591
|
+
self.pv.camera.viewup = self.view_up
|
|
1592
|
+
else:
|
|
1593
|
+
self.pv.camera_position = self.camera_position
|
|
1594
|
+
self.pv.camera.focal_point = self.focal_point
|
|
1595
|
+
self.pv.camera.azimuth += self.azimuth_angle
|
|
1596
|
+
self.pv.camera.roll += self.roll_angle
|
|
1597
|
+
self.pv.camera.elevation += self.elevation_angle
|
|
1598
|
+
else:
|
|
1599
|
+
self.pv.isometric_view()
|
|
1600
|
+
self.pv.camera.zoom(self.zoom)
|
|
1601
|
+
if export_image_path:
|
|
1602
|
+
path_image = os.path.dirname(export_image_path)
|
|
1603
|
+
root_name, format = os.path.splitext(os.path.basename(export_image_path))
|
|
1604
|
+
else:
|
|
1605
|
+
path_image = tempfile.gettempdir() # pragma: no cover
|
|
1606
|
+
format = ".png" # pragma: no cover
|
|
1607
|
+
root_name = "Image" # pragma: no cover
|
|
1608
|
+
|
|
1609
|
+
def s_callback(): # pragma: no cover
|
|
1610
|
+
"""save screenshots"""
|
|
1611
|
+
exp = os.path.join(
|
|
1612
|
+
path_image, "{}{}{}".format(root_name, datetime.now().strftime("%Y_%M_%d_%H-%M-%S"), format)
|
|
1613
|
+
)
|
|
1614
|
+
self.pv.screenshot(exp, return_img=False)
|
|
1615
|
+
|
|
1616
|
+
self.pv.add_key_event("s", s_callback)
|
|
1617
|
+
if export_image_path:
|
|
1618
|
+
self.pv.show(screenshot=export_image_path, full_screen=True)
|
|
1619
|
+
elif self.is_notebook: # pragma: no cover
|
|
1620
|
+
self.pv.show() # pragma: no cover
|
|
1621
|
+
else:
|
|
1622
|
+
self.pv.show(full_screen=True) # pragma: no cover
|
|
1623
|
+
|
|
1624
|
+
self.image_file = export_image_path
|
|
1625
|
+
return True
|
|
1626
|
+
|
|
1627
|
+
@pyedb_function_handler()
|
|
1628
|
+
def clean_cache_and_files(self, remove_objs=True, remove_fields=True, clean_cache=False): # pragma: no cover
|
|
1629
|
+
"""Clean downloaded files, and, on demand, also the cached meshes.
|
|
1630
|
+
|
|
1631
|
+
Parameters
|
|
1632
|
+
----------
|
|
1633
|
+
remove_objs : bool
|
|
1634
|
+
remove_fields : bool
|
|
1635
|
+
clean_cache : bool
|
|
1636
|
+
|
|
1637
|
+
Returns
|
|
1638
|
+
-------
|
|
1639
|
+
bool
|
|
1640
|
+
"""
|
|
1641
|
+
if remove_objs:
|
|
1642
|
+
for el in self.objects:
|
|
1643
|
+
if os.path.exists(el.path):
|
|
1644
|
+
os.remove(el.path)
|
|
1645
|
+
if clean_cache:
|
|
1646
|
+
el._cached_mesh = None
|
|
1647
|
+
el._cached_polydata = None
|
|
1648
|
+
if remove_fields:
|
|
1649
|
+
for el in self.fields:
|
|
1650
|
+
if os.path.exists(el.path):
|
|
1651
|
+
os.remove(el.path)
|
|
1652
|
+
if clean_cache:
|
|
1653
|
+
el._cached_mesh = None
|
|
1654
|
+
el._cached_polydata = None
|
|
1655
|
+
return True
|
|
1656
|
+
|
|
1657
|
+
@pyedb_function_handler()
|
|
1658
|
+
def animate(self): # pragma: no cover
|
|
1659
|
+
"""Animate the current field plot.
|
|
1660
|
+
|
|
1661
|
+
Returns
|
|
1662
|
+
-------
|
|
1663
|
+
bool
|
|
1664
|
+
"""
|
|
1665
|
+
|
|
1666
|
+
assert len(self.frames) > 0, "Number of Fields have to be greater than 1 to do an animation."
|
|
1667
|
+
if self.is_notebook:
|
|
1668
|
+
self.pv = pv.Plotter(notebook=self.is_notebook, off_screen=True, window_size=self.windows_size)
|
|
1669
|
+
else:
|
|
1670
|
+
self.pv = pv.Plotter(notebook=self.is_notebook, off_screen=self.off_screen, window_size=self.windows_size)
|
|
1671
|
+
if self.background_image:
|
|
1672
|
+
self.pv.add_background_image(self.background_image)
|
|
1673
|
+
else:
|
|
1674
|
+
self.pv.background_color = [i / 255 for i in self.background_color]
|
|
1675
|
+
self._read_mesh_files(read_frames=True)
|
|
1676
|
+
|
|
1677
|
+
axes_color = [0 if i >= 128 else 1 for i in self.background_color]
|
|
1678
|
+
|
|
1679
|
+
self.pv.set_scale(self.x_scale, self.y_scale, self.z_scale)
|
|
1680
|
+
if self.show_axes:
|
|
1681
|
+
self.pv.show_axes()
|
|
1682
|
+
if self.show_grid and not self.is_notebook:
|
|
1683
|
+
self.pv.show_grid(color=tuple(axes_color))
|
|
1684
|
+
if self.bounding_box:
|
|
1685
|
+
self.pv.add_bounding_box(color=tuple(axes_color))
|
|
1686
|
+
if self.show_legend:
|
|
1687
|
+
labels = []
|
|
1688
|
+
for m in self.objects:
|
|
1689
|
+
labels.append([m.name, [i / 255 for i in m.color]])
|
|
1690
|
+
for m in self.fields:
|
|
1691
|
+
labels.append([m.name, "red"])
|
|
1692
|
+
self.pv.add_legend(labels=labels, bcolor=None, face="circle", size=[0.15, 0.15])
|
|
1693
|
+
if not self.isometric_view:
|
|
1694
|
+
if isinstance(self.camera_position, (tuple, list)):
|
|
1695
|
+
self.pv.camera.position = self.camera_position
|
|
1696
|
+
self.pv.camera.focal_point = self.focal_point
|
|
1697
|
+
self.pv.camera.up = self.view_up
|
|
1698
|
+
else:
|
|
1699
|
+
self.pv.camera_position = self.camera_position
|
|
1700
|
+
self.pv.camera.azimuth += self.azimuth_angle
|
|
1701
|
+
self.pv.camera.roll += self.roll_angle
|
|
1702
|
+
self.pv.camera.elevation += self.elevation_angle
|
|
1703
|
+
else:
|
|
1704
|
+
self.pv.isometric_view()
|
|
1705
|
+
self.pv.zoom = self.zoom
|
|
1706
|
+
self._animating = True
|
|
1707
|
+
|
|
1708
|
+
if self.gif_file:
|
|
1709
|
+
self.pv.open_gif(self.gif_file)
|
|
1710
|
+
|
|
1711
|
+
def q_callback():
|
|
1712
|
+
"""exit when user wants to leave"""
|
|
1713
|
+
self._animating = False
|
|
1714
|
+
|
|
1715
|
+
self._pause = False
|
|
1716
|
+
|
|
1717
|
+
def p_callback():
|
|
1718
|
+
"""exit when user wants to leave"""
|
|
1719
|
+
self._pause = not self._pause
|
|
1720
|
+
|
|
1721
|
+
self.pv.add_text(
|
|
1722
|
+
"Press p for Play/Pause, Press q to exit ", font_size=8, position="upper_left", color=tuple(axes_color)
|
|
1723
|
+
)
|
|
1724
|
+
self.pv.add_text(" ", font_size=10, position=[0, 0], color=tuple(axes_color))
|
|
1725
|
+
self.pv.add_key_event("q", q_callback)
|
|
1726
|
+
self.pv.add_key_event("p", p_callback)
|
|
1727
|
+
if self.color_bar:
|
|
1728
|
+
sargs = dict(
|
|
1729
|
+
title_font_size=10,
|
|
1730
|
+
label_font_size=10,
|
|
1731
|
+
shadow=True,
|
|
1732
|
+
n_labels=9,
|
|
1733
|
+
italic=True,
|
|
1734
|
+
fmt="%.1f",
|
|
1735
|
+
font_family="arial",
|
|
1736
|
+
)
|
|
1737
|
+
else:
|
|
1738
|
+
sargs = dict(
|
|
1739
|
+
position_x=2,
|
|
1740
|
+
position_y=2,
|
|
1741
|
+
)
|
|
1742
|
+
|
|
1743
|
+
for field in self._fields:
|
|
1744
|
+
field._cached_mesh = self.pv.add_mesh(
|
|
1745
|
+
field._cached_polydata,
|
|
1746
|
+
scalars=field.label,
|
|
1747
|
+
log_scale=False if self.convert_fields_in_db else field.log_scale,
|
|
1748
|
+
scalar_bar_args=sargs,
|
|
1749
|
+
cmap=field.color_map,
|
|
1750
|
+
opacity=field.opacity,
|
|
1751
|
+
)
|
|
1752
|
+
# run until q is pressed
|
|
1753
|
+
if self.pv.mesh:
|
|
1754
|
+
self.pv.set_focus(self.pv.mesh.center)
|
|
1755
|
+
|
|
1756
|
+
cpos = self.pv.show(interactive=False, auto_close=False, interactive_update=not self.off_screen)
|
|
1757
|
+
|
|
1758
|
+
if self.range_min is not None and self.range_max is not None:
|
|
1759
|
+
mins = self.range_min
|
|
1760
|
+
maxs = self.range_max
|
|
1761
|
+
else:
|
|
1762
|
+
mins = 1e20
|
|
1763
|
+
maxs = -1e20
|
|
1764
|
+
for el in self.frames:
|
|
1765
|
+
if np.min(el._cached_polydata.point_data[el.label]) < mins:
|
|
1766
|
+
mins = np.min(el._cached_polydata.point_data[el.label])
|
|
1767
|
+
if np.max(el._cached_polydata.point_data[el.label]) > maxs:
|
|
1768
|
+
maxs = np.max(el._cached_polydata.point_data[el.label])
|
|
1769
|
+
|
|
1770
|
+
self.frames[0]._cached_mesh = self.pv.add_mesh(
|
|
1771
|
+
self.frames[0]._cached_polydata,
|
|
1772
|
+
scalars=self.frames[0].label,
|
|
1773
|
+
log_scale=False if self.convert_fields_in_db else self.frames[0].log_scale,
|
|
1774
|
+
scalar_bar_args=sargs,
|
|
1775
|
+
cmap=self.frames[0].color_map,
|
|
1776
|
+
clim=[mins, maxs],
|
|
1777
|
+
show_edges=False,
|
|
1778
|
+
pickable=True,
|
|
1779
|
+
smooth_shading=True,
|
|
1780
|
+
name="FieldPlot",
|
|
1781
|
+
opacity=self.frames[0].opacity,
|
|
1782
|
+
)
|
|
1783
|
+
start = time.time()
|
|
1784
|
+
try:
|
|
1785
|
+
self.pv.update(1, force_redraw=True)
|
|
1786
|
+
except:
|
|
1787
|
+
pass
|
|
1788
|
+
if self.gif_file:
|
|
1789
|
+
first_loop = True
|
|
1790
|
+
self.pv.write_frame()
|
|
1791
|
+
else:
|
|
1792
|
+
first_loop = False
|
|
1793
|
+
i = 1
|
|
1794
|
+
while self._animating:
|
|
1795
|
+
if self._pause:
|
|
1796
|
+
time.sleep(1)
|
|
1797
|
+
self.pv.update(1, force_redraw=True)
|
|
1798
|
+
continue
|
|
1799
|
+
# p.remove_actor("FieldPlot")
|
|
1800
|
+
if i >= len(self.frames):
|
|
1801
|
+
if self.off_screen or self.is_notebook:
|
|
1802
|
+
break
|
|
1803
|
+
i = 0
|
|
1804
|
+
first_loop = False
|
|
1805
|
+
scalars = self.frames[i]._cached_polydata.point_data[self.frames[i].label]
|
|
1806
|
+
self.pv.update_scalars(scalars, render=False)
|
|
1807
|
+
if not hasattr(self.pv, "ren_win"):
|
|
1808
|
+
break
|
|
1809
|
+
time.sleep(max(0, (1 / self.frame_per_seconds) - (time.time() - start)))
|
|
1810
|
+
start = time.time()
|
|
1811
|
+
if self.off_screen:
|
|
1812
|
+
self.pv.render()
|
|
1813
|
+
else:
|
|
1814
|
+
self.pv.update(1, force_redraw=True)
|
|
1815
|
+
if first_loop:
|
|
1816
|
+
self.pv.write_frame()
|
|
1817
|
+
i += 1
|
|
1818
|
+
self.pv.close()
|
|
1819
|
+
if self.gif_file:
|
|
1820
|
+
return self.gif_file
|
|
1821
|
+
else:
|
|
1822
|
+
return True
|
|
1823
|
+
|
|
1824
|
+
@pyedb_function_handler()
|
|
1825
|
+
def generate_geometry_mesh(self): # pragma: no cover
|
|
1826
|
+
"""Generate mesh for objects only.
|
|
1827
|
+
|
|
1828
|
+
Returns
|
|
1829
|
+
-------
|
|
1830
|
+
Mesh
|
|
1831
|
+
"""
|
|
1832
|
+
self.pv = pv.Plotter(notebook=self.is_notebook, off_screen=self.off_screen, window_size=self.windows_size)
|
|
1833
|
+
self._read_mesh_files()
|
|
1834
|
+
if self.array_coordinates:
|
|
1835
|
+
duplicate_mesh = self.meshes.copy()
|
|
1836
|
+
for offset_xyz in self.array_coordinates:
|
|
1837
|
+
translated_mesh = duplicate_mesh.copy()
|
|
1838
|
+
translated_mesh.translate(offset_xyz, inplace=True)
|
|
1839
|
+
self.meshes += translated_mesh
|
|
1840
|
+
return self.meshes
|