Coverage for trimesh/exchange/misc.py: 76%
67 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-24 04:40 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-24 04:40 +0000
1import json
2from tempfile import NamedTemporaryFile
4from .. import util
5from ..exceptions import ExceptionWrapper
8def load_dict(file_obj, **kwargs):
9 """
10 Load multiple input types into kwargs for a Trimesh constructor.
11 Tries to extract keys:
12 'faces'
13 'vertices'
14 'face_normals'
15 'vertex_normals'
17 Parameters
18 ----------
19 file_obj : dict
20 accepts multiple forms
21 -dict: has keys for vertices and faces as (n,3) numpy arrays
22 -dict: has keys for vertices/faces (n,3) arrays encoded as dicts/base64
23 with trimesh.util.array_to_encoded/trimesh.util.encoded_to_array
24 -str: json blob as dict with either straight array or base64 values
25 -file object: json blob of dict
26 file_type: not used
28 Returns
29 -----------
30 loaded: dict with keys
31 -vertices: (n,3) float
32 -faces: (n,3) int
33 -face_normals: (n,3) float (optional)
34 """
35 if file_obj is None:
36 raise ValueError("file_obj passed to load_dict was None!")
37 if util.is_instance_named(file_obj, "Trimesh"):
38 return file_obj
39 if isinstance(file_obj, str):
40 if "{" not in file_obj:
41 raise ValueError("Object is not a JSON encoded dictionary!")
42 file_obj = json.loads(file_obj.decode("utf-8"))
43 elif util.is_file(file_obj):
44 file_obj = json.load(file_obj)
46 # what shape should the file_obj be to be usable
47 mesh_file_obj = {
48 "vertices": (-1, 3),
49 "faces": (-1, (3, 4)),
50 "face_normals": (-1, 3),
51 "face_colors": (-1, (3, 4)),
52 "vertex_normals": (-1, 3),
53 "vertex_colors": (-1, (3, 4)),
54 }
56 # now go through file_obj structure and if anything is encoded as base64
57 # pull it back into numpy arrays
58 if not isinstance(file_obj, dict):
59 raise ValueError(f"`{type(file_obj)}` object passed to dict loader!")
61 loaded = {}
62 file_obj = util.decode_keys(file_obj, "utf-8")
63 for key, shape in mesh_file_obj.items():
64 if key in file_obj:
65 loaded[key] = util.encoded_to_array(file_obj[key])
66 if not util.is_shape(loaded[key], shape):
67 raise ValueError(
68 "Shape of %s is %s, not %s!",
69 key,
70 str(loaded[key].shape),
71 str(shape),
72 )
73 if len(loaded) == 0:
74 raise ValueError("Unable to extract a mesh from the dict!")
76 return loaded
79def load_meshio(file_obj, file_type: str, **kwargs):
80 """
81 Load a meshio-supported file into the kwargs for a Trimesh
82 constructor.
85 Parameters
86 ----------
87 file_obj : file object
88 Contains a meshio file
89 file_type : str
90 File extension, aka 'vtk'
92 Returns
93 ----------
94 loaded : dict
95 kwargs for Trimesh constructor
96 """
97 import warnings
99 warnings.warn(
100 "`meshio` loading is deprecated and will be removed after June 2027; "
101 + "use `meshio` directly if you need these formats.",
102 category=DeprecationWarning,
103 stacklevel=2,
104 )
106 # trimesh "file types" are really filename extensions
107 # meshio may return multiple answers for each file extension
108 file_formats = meshio.extension_to_filetypes["." + file_type]
110 mesh = None
111 exceptions = []
113 # meshio appears to only support loading by file name so use a tempfile
114 with NamedTemporaryFile(suffix=f".{file_type}") as temp:
115 temp.write(file_obj.read())
116 temp.flush()
117 # try the loaders in order
118 for file_format in file_formats:
119 try:
120 mesh = meshio.read(temp.name, file_format=file_format)
121 break
122 except BaseException as E:
123 exceptions.append(str(E))
125 if mesh is None:
126 raise ValueError("Failed to load file:" + "\n".join(exceptions))
128 # save file_obj as kwargs for a trimesh.Trimesh
129 result = {}
130 # pass kwargs to mesh constructor
131 result.update(kwargs)
132 # add vertices
133 result["vertices"] = mesh.points
134 try:
135 # add faces
136 result["faces"] = mesh.get_cells_type("triangle")
137 except BaseException:
138 util.log.warning("unable to get faces", exc_info=True)
139 result["faces"] = []
141 return result
144_misc_loaders = {"dict": load_dict, "dict64": load_dict}
145_misc_loaders = {}
148try:
149 import meshio
151 # add meshio loaders here
152 _meshio_loaders = {k[1:]: load_meshio for k in meshio.extension_to_filetypes.keys()}
153 _misc_loaders.update(_meshio_loaders)
154except BaseException:
155 _meshio_loaders = {}
157try:
158 import openctm
160 _misc_loaders["ctm"] = openctm.load_ctm
161except BaseException as E:
162 _misc_loaders["ctm"] = ExceptionWrapper(E)