Coverage for lasso/plotting/plot_shell_mesh.py: 15%
80 statements
« prev ^ index » next coverage.py v7.2.4, created at 2023-04-28 18:42 +0100
« prev ^ index » next coverage.py v7.2.4, created at 2023-04-28 18:42 +0100
1import os
2import io
3import uuid
4import json
5from base64 import b64encode
6from zipfile import ZipFile, ZIP_DEFLATED
7from typing import Union, Tuple
8import numpy as np
11def _read_file(filepath: str):
12 """This function reads file as str
14 Parameters
15 ----------
16 filepath : str
17 filepath of the file to read as string
19 Returns
20 -------
21 file_content : str
22 """
24 with open(filepath, "r", encoding="utf-8") as fp_filepath:
25 return fp_filepath.read()
28def plot_shell_mesh(
29 node_coordinates: np.ndarray,
30 shell_node_indexes: np.ndarray,
31 field: Union[np.ndarray, None] = None,
32 is_element_field: bool = True,
33 fringe_limits: Union[Tuple[float, float], None] = None,
34):
35 """Plot a mesh
37 Parameters
38 ----------
39 node_coordinates : np.ndarray
40 array of node coordinates for elements
41 shell_node_indexes : np.ndarray
42 node indexes of shells
43 field : Union[np.ndarray, None]
44 Array containing a field value for every element or node
45 is_element_field : bool
46 if the specified field is for elements or nodes
47 fringe_limits : Union[Tuple[float, float], None]
48 limits for the fringe bar. Set by default to min and max.
50 Returns
51 -------
52 html : str
53 html code for plotting as string
54 """
56 # pylint: disable = too-many-locals, too-many-statements
58 assert node_coordinates.ndim == 2
59 assert node_coordinates.shape[1] == 3
60 assert shell_node_indexes.ndim == 2
61 assert shell_node_indexes.shape[1] in [3, 4]
62 if isinstance(field, np.ndarray):
63 assert field.ndim == 1
64 if is_element_field:
65 assert field.shape[0] == shell_node_indexes.shape[0]
66 else:
67 assert field.shape[0] == node_coordinates.shape[0]
69 # cast types correctly
70 # the types MUST be float32
71 node_coordinates = node_coordinates.astype(np.float32)
72 if isinstance(field, np.ndarray):
73 field = field.astype(np.float32)
75 # distinguish tria and quads
76 is_quad = shell_node_indexes[:, 2] != shell_node_indexes[:, 3]
77 is_tria = np.logical_not(is_quad)
79 # separate tria and quads ... I know its sad :(
80 tria_node_indexes = shell_node_indexes[is_tria][:, :3]
81 quad_node_indexes = shell_node_indexes[is_quad]
83 # we can only plot tria, therefore we need to split quads
84 # into two trias
85 quad_node_indexes_tria1 = quad_node_indexes[:, :3]
86 # quad_node_indexes_tria2 = quad_node_indexes[:, [True, False, True, True]]
87 quad_node_indexes_tria2 = quad_node_indexes[:, [0, 2, 3]]
89 # assemble elements for plotting
90 # This seems to take a lot of memory, and you are right, thinking this,
91 # the issue is just in order to plot fringe values, we need to output
92 # the element values at the 3 corner nodes. Since elements share nodes
93 # we can not use the same nodes, thus we need to create multiple nodes
94 # at the same position but with different fringe.
95 nodes_xyz = np.concatenate(
96 [
97 node_coordinates[tria_node_indexes].reshape((-1, 3)),
98 node_coordinates[quad_node_indexes_tria1].reshape((-1, 3)),
99 node_coordinates[quad_node_indexes_tria2].reshape((-1, 3)),
100 ]
101 )
103 # fringe value and hover title
104 if isinstance(field, np.ndarray):
106 if is_element_field:
107 n_shells = len(shell_node_indexes)
108 n_tria = np.sum(is_tria)
109 n_quads = n_shells - n_tria
111 # split field according to elements
112 field_tria = field[is_tria]
113 field_quad = field[is_quad]
115 # allocate fringe array
116 node_fringe = np.zeros((len(field_tria) + 2 * len(field_quad), 3), dtype=np.float32)
118 # set fringe values
119 node_fringe[:n_tria, 0] = field_tria
120 node_fringe[:n_tria, 1] = field_tria
121 node_fringe[:n_tria, 2] = field_tria
123 node_fringe[n_tria : n_tria + n_quads, 0] = field_quad
124 node_fringe[n_tria : n_tria + n_quads, 1] = field_quad
125 node_fringe[n_tria : n_tria + n_quads, 2] = field_quad
127 node_fringe[n_tria + n_quads : n_tria + 2 * n_quads, 0] = field_quad
128 node_fringe[n_tria + n_quads : n_tria + 2 * n_quads, 1] = field_quad
129 node_fringe[n_tria + n_quads : n_tria + 2 * n_quads, 2] = field_quad
131 # flatty paddy
132 node_fringe = node_fringe.flatten()
133 else:
134 # copy & paste ftw
135 node_fringe = np.concatenate(
136 [
137 field[tria_node_indexes].reshape((-1, 3)),
138 field[quad_node_indexes_tria1].reshape((-1, 3)),
139 field[quad_node_indexes_tria2].reshape((-1, 3)),
140 ]
141 )
142 node_fringe = node_fringe.flatten()
144 # element text
145 node_txt = [str(entry) for entry in node_fringe.flatten()]
146 else:
147 node_fringe = np.zeros(len(nodes_xyz), dtype=np.float32)
148 node_txt = [""] * len(nodes_xyz)
150 # zip compression of data for HTML (reduces size)
151 zip_data = io.BytesIO()
152 with ZipFile(zip_data, "w", compression=ZIP_DEFLATED) as zipfile:
153 zipfile.writestr("/intensities", node_fringe.tostring())
154 zipfile.writestr("/positions", nodes_xyz.tostring())
155 zipfile.writestr("/text", json.dumps(node_txt))
156 zip_data = b64encode(zip_data.getvalue()).decode("utf-8")
158 # read html template
159 _html_template = _read_file(
160 os.path.join(os.path.dirname(__file__), "resources", "template.html")
161 )
163 # format html template file
164 min_value = 0
165 max_value = 0
166 if fringe_limits:
167 min_value = fringe_limits[0]
168 max_value = fringe_limits[1]
169 elif isinstance(field, np.ndarray):
170 min_value = field.min()
171 max_value = field.max()
173 _html_div = _html_template.format(
174 div_id=uuid.uuid4(), lowIntensity=min_value, highIntensity=max_value, zdata=zip_data
175 )
177 # wrap it up with all needed js libraries
179 script_string_js = '<script type="text/javascript">{0}</script>'
180 jszip_js_format = _read_file(
181 os.path.join(os.path.dirname(__file__), "resources", "jszip.min.js")
182 )
183 jszip_three_format = _read_file(
184 os.path.join(os.path.dirname(__file__), "resources", "three.min.js")
185 )
186 jszip_chroma_format = _read_file(
187 os.path.join(os.path.dirname(__file__), "resources", "chroma.min.js")
188 )
189 jszip_jquery_format = _read_file(
190 os.path.join(os.path.dirname(__file__), "resources", "jquery.min.js")
191 )
192 _html_jszip_js = script_string_js.format(jszip_js_format)
193 _html_three_js = script_string_js.format(jszip_three_format)
194 _html_chroma_js = script_string_js.format(jszip_chroma_format)
195 _html_jquery_js = script_string_js.format(jszip_jquery_format)
197 # pylint: disable = consider-using-f-string
198 return """
199<!DOCTYPE html>
200<html lang="en">
201 <head>
202 <meta charset="utf-8" />
203 {_jquery_js}
204 {_jszip_js}
205 {_three_js}
206 {_chroma_js}
207 </head>
208 <body>
209 {_html_div}
210 </body>
211</html>""".format(
212 _html_div=_html_div,
213 _jszip_js=_html_jszip_js,
214 _three_js=_html_three_js,
215 _chroma_js=_html_chroma_js,
216 _jquery_js=_html_jquery_js,
217 )