1import os
2import logging
3import platform
4import subprocess
5
6from string import Template
7from tempfile import NamedTemporaryFile
8from subprocess import check_call
9
10from .. import exchange
11
12# create a default logger
13log = logging.getLogger('trimesh.interfaces')
14
15
16class MeshScript:
17
18    def __init__(self,
19                 meshes,
20                 script,
21                 tmpfile_ext='stl',
22                 debug=False,
23                 **kwargs):
24
25        self.debug = debug
26        self.kwargs = kwargs
27        self.meshes = meshes
28        self.script = script
29        self.tmpfile_ext = tmpfile_ext
30
31    def __enter__(self):
32        # windows has problems with multiple programs using open files so we close
33        # them at the end of the enter call, and delete them ourselves at the
34        # exit
35        self.mesh_pre = [
36            NamedTemporaryFile(
37                suffix='.{}'.format(
38                    self.tmpfile_ext),
39                mode='wb',
40                delete=False) for i in self.meshes]
41        self.mesh_post = NamedTemporaryFile(
42            suffix='.{}'.format(
43                self.tmpfile_ext),
44            mode='rb',
45            delete=False)
46        self.script_out = NamedTemporaryFile(mode='wb',
47                                             delete=False)
48
49        # export the meshes to a temporary STL container
50        for mesh, file_obj in zip(self.meshes, self.mesh_pre):
51            mesh.export(file_obj.name)
52
53        self.replacement = {'MESH_' + str(i): m.name
54                            for i, m in enumerate(self.mesh_pre)}
55        self.replacement['MESH_PRE'] = str(
56            [i.name for i in self.mesh_pre])
57        self.replacement['MESH_POST'] = self.mesh_post.name
58        self.replacement['SCRIPT'] = self.script_out.name
59
60        script_text = Template(self.script).substitute(self.replacement)
61        if platform.system() == 'Windows':
62            script_text = script_text.replace('\\', '\\\\')
63
64        self.script_out.write(script_text.encode('utf-8'))
65
66        # close all temporary files
67        self.script_out.close()
68        self.mesh_post.close()
69        for file_obj in self.mesh_pre:
70            file_obj.close()
71        return self
72
73    def run(self, command):
74        command_run = Template(command).substitute(
75            self.replacement).split()
76        # run the binary
77        # avoid resourcewarnings with null
78        with open(os.devnull, 'w') as devnull:
79            startupinfo = None
80            if platform.system() == 'Windows':
81                startupinfo = subprocess.STARTUPINFO()
82                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
83            if self.debug:
84                # in debug mode print the output
85                stdout = None
86            else:
87                stdout = devnull
88
89            if self.debug:
90                log.info('executing: {}'.format(' '.join(command_run)))
91            check_call(command_run,
92                       stdout=stdout,
93                       stderr=subprocess.STDOUT,
94                       startupinfo=startupinfo)
95
96        # bring the binaries result back as a set of Trimesh kwargs
97        mesh_results = exchange.load.load_mesh(self.mesh_post.name,
98                                               **self.kwargs)
99
100        return mesh_results
101
102    def __exit__(self, *args, **kwargs):
103        if self.debug:
104            print('MeshScript.debug: not deleting {}'.format(
105                self.script_out.name))
106            return
107        # delete all the temporary files by name
108        # they are closed but their names are still available
109        os.remove(self.script_out.name)
110        for file_obj in self.mesh_pre:
111            os.remove(file_obj.name)
112        os.remove(self.mesh_post.name)
113