35 if field_name
in mesh.point_data:
36 data = mesh.point_data[field_name]
37 target = mesh.point_data
38 elif field_name
in mesh.cell_data:
39 data = mesh.cell_data[field_name]
40 target = mesh.cell_data
42 raise KeyError(f
"Field '{field_name}' was not found in point or cell data.")
44 values = np.asarray(data)
46 values = values.reshape(-1, 1)
48 values = values.reshape(values.shape[0], -1)
50 component_index = component
52 if component_index < 0
or component_index >= values.shape[1]:
54 f
"Field '{field_name}' has {values.shape[1]} component(s), "
55 f
"so component {component} is out of range. "
56 f
"Use 0..{values.shape[1] - 1}."
59 component_name = f
"{field_name}[{component_index}]"
60 target[component_name] = values[:, component_index]
78 plotter = pv.Plotter(notebook=
False, off_screen=off_screen)
85 show_edges=args.show_edges,
92 show_edges=args.show_edges,
98 if glyph_source
is not None:
102 color=args.glyph_color,
107 plotter.camera_position = args.d2
108 plotter.camera.zoom(args.zoom)
109 plotter.camera.roll += args.roll
110 plotter.camera.azimuth += args.azimuth
134 values = np.asarray(data)
136 values = values.reshape(-1, 1)
138 values = values.reshape(values.shape[0], -1)
140 if values.shape[1] < 2:
142 f
"Glyph field '{field_name}' has {values.shape[1]} component(s). "
143 "Use a vector field with at least 2 components."
146 vectors = np.zeros((values.shape[0], 3), dtype=float)
147 vectors[:, : min(values.shape[1], 3)] = values[:, :3]
152 if not args.glyph_field:
155 if args.glyph_field
in mesh.point_data:
156 points = np.asarray(mesh.points, dtype=float)
157 vectors =
vector_values(mesh.point_data[args.glyph_field], args.glyph_field)
158 elif args.glyph_field
in mesh.cell_data:
159 centers = mesh.cell_centers()
160 points = np.asarray(centers.points, dtype=float)
161 vectors =
vector_values(mesh.cell_data[args.glyph_field], args.glyph_field)
164 f
"Glyph field '{args.glyph_field}' was not found in point or cell data."
167 vector_norms = np.linalg.norm(vectors, axis=1)
169 np.isfinite(points).all(axis=1)
170 & np.isfinite(vectors).all(axis=1)
173 points = points[valid]
174 vectors = vectors[valid]
179 stride = max(1, int(args.glyph_stride))
180 points = points[::stride]
181 vectors = vectors[::stride]
183 source = pv.PolyData(points)
184 source.point_data[GLYPH_VECTOR_NAME] = vectors
198 values = np.asarray(values, dtype=float)
199 finite_values = values[np.isfinite(values)]
200 if finite_values.size:
201 value_min = float(np.min(finite_values))
202 value_max = float(np.max(finite_values))
207 if value_max > value_min:
208 normalized = (values - value_min) / (value_max - value_min)
210 normalized = np.full(values.shape, 0.5)
212 normalized = np.nan_to_num(normalized, nan=0.5, posinf=1.0, neginf=0.0)
213 colors =
get_cmap()(np.clip(normalized, 0.0, 1.0))[:, :3]
214 return colors, value_min, value_max
232 if glyph_surface.n_points == 0
or glyph_surface.n_cells == 0:
239 faces = np.asarray(glyph_surface.faces, dtype=np.int64).reshape(-1, 4)[:, 1:4]
240 points = np.asarray(glyph_surface.points, dtype=float)
241 triangle_points = points[faces]
242 face_normals = np.cross(
243 triangle_points[:, 1] - triangle_points[:, 0],
244 triangle_points[:, 2] - triangle_points[:, 0],
246 normal_lengths = np.linalg.norm(face_normals, axis=1)
247 valid_normals = normal_lengths > 0
248 face_normals[valid_normals] /= normal_lengths[valid_normals,
None]
249 face_normals[~valid_normals] = (0.0, 0.0, 1.0)
251 positions = ((triangle_points.reshape(-1, 3) - center) / radius).astype(float)
252 normals = np.repeat(face_normals, 3, axis=0)
253 glyph_color = np.array(mcolors.to_rgb(args.glyph_color), dtype=float)
254 colors = np.tile(glyph_color, (positions.shape[0], 1))
257 "positions": positions.ravel().round(8).tolist(),
258 "normals": normals.ravel().round(8).tolist(),
259 "colors": colors.ravel().round(8).tolist(),
266 faces = np.asarray(surface.faces, dtype=np.int64).reshape(-1, 4)[:, 1:4]
267 points = np.asarray(surface.points, dtype=float)
269 if points.size == 0
or faces.size == 0:
270 raise RuntimeError(f
"File '{file}' does not contain surface triangles.")
272 triangle_points = points[faces]
273 face_normals = np.cross(
274 triangle_points[:, 1] - triangle_points[:, 0],
275 triangle_points[:, 2] - triangle_points[:, 0],
277 normal_lengths = np.linalg.norm(face_normals, axis=1)
278 valid_normals = normal_lengths > 0
279 face_normals[valid_normals] /= normal_lengths[valid_normals,
None]
280 face_normals[~valid_normals] = (0.0, 0.0, 1.0)
283 if scalar_name
in surface.point_data:
285 colors, scalar_min, scalar_max =
values_to_rgb(scalar_data[faces].ravel())
286 elif scalar_name
in surface.cell_data:
288 colors, scalar_min, scalar_max =
values_to_rgb(np.repeat(scalar_data, 3))
291 f
"Field '{scalar_name}' was not found on the extracted surface."
294 colors = np.tile(np.array([[0.92, 0.92, 0.88]]), (faces.size, 1))
298 center = np.mean(points, axis=0)
299 radius = np.max(np.linalg.norm(points - center, axis=1))
300 if not np.isfinite(radius)
or radius <= 0:
303 positions = ((triangle_points.reshape(-1, 3) - center) / radius).astype(float)
304 normals = np.repeat(face_normals, 3, axis=0)
311 "file": Path(file).name,
312 "scalar": scalar_name,
313 "glyph": args.glyph_field
or None,
314 "scalarMin": scalar_min,
315 "scalarMax": scalar_max,
316 "showEdges":
bool(args.show_edges),
317 "zoom": float(args.zoom),
319 "roll": float(args.roll),
320 "azimuth": float(args.azimuth),
321 "positions": positions.ravel().round(8).tolist(),
322 "normals": normals.ravel().round(8).tolist(),
323 "colors": colors.ravel().round(8).tolist(),
324 "edges": edge_positions.ravel().round(8).tolist(),
325 "glyphPositions": glyph_payload[
"positions"],
326 "glyphNormals": glyph_payload[
"normals"],
327 "glyphColors": glyph_payload[
"colors"],