Update for 2.8
[blender-addons.git] / io_mesh_stl / __init__.py
blob934cdbb4c0b3ff034bfcf7c5a7c29ce3cbff5834
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8-80 compliant>
21 bl_info = {
22 "name": "STL format",
23 "author": "Guillaume Bouchard (Guillaum)",
24 "version": (1, 1, 2),
25 "blender": (2, 80, 0),
26 "location": "File > Import-Export > Stl",
27 "description": "Import-Export STL files",
28 "warning": "",
29 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
30 "Scripts/Import-Export/STL",
31 "support": 'OFFICIAL',
32 "category": "Import-Export",
36 # @todo write the wiki page
38 """
39 Import-Export STL files (binary or ascii)
41 - Import automatically remove the doubles.
42 - Export can export with/without modifiers applied
44 Issues:
46 Import:
47 - Does not handle endien
48 """
50 if "bpy" in locals():
51 import importlib
52 if "stl_utils" in locals():
53 importlib.reload(stl_utils)
54 if "blender_utils" in locals():
55 importlib.reload(blender_utils)
57 import os
59 import bpy
60 from bpy.props import (
61 StringProperty,
62 BoolProperty,
63 CollectionProperty,
64 EnumProperty,
65 FloatProperty,
67 from bpy_extras.io_utils import (
68 ImportHelper,
69 ExportHelper,
70 orientation_helper,
71 axis_conversion,
73 from bpy.types import (
74 Operator,
75 OperatorFileListElement,
79 @orientation_helper(axis_forward='Y', axis_up='Z')
80 class ImportSTL(Operator, ImportHelper):
81 """Load STL triangle mesh data"""
82 bl_idname = "import_mesh.stl"
83 bl_label = "Import STL"
84 bl_options = {'UNDO'}
86 filename_ext = ".stl"
88 filter_glob: StringProperty(
89 default="*.stl",
90 options={'HIDDEN'},
92 files: CollectionProperty(
93 name="File Path",
94 type=OperatorFileListElement,
96 directory: StringProperty(
97 subtype='DIR_PATH',
100 global_scale: FloatProperty(
101 name="Scale",
102 soft_min=0.001, soft_max=1000.0,
103 min=1e-6, max=1e6,
104 default=1.0,
107 use_scene_unit: BoolProperty(
108 name="Scene Unit",
109 description="Apply current scene's unit (as defined by unit scale) to imported data",
110 default=False,
113 use_facet_normal: BoolProperty(
114 name="Facet Normals",
115 description="Use (import) facet normals (note that this will still give flat shading)",
116 default=False,
119 def execute(self, context):
120 from . import stl_utils
121 from . import blender_utils
122 from mathutils import Matrix
124 paths = [os.path.join(self.directory, name.name)
125 for name in self.files]
127 scene = context.scene
129 # Take into account scene's unit scale, so that 1 inch in Blender gives 1 inch elsewhere! See T42000.
130 global_scale = self.global_scale
131 if scene.unit_settings.system != 'NONE' and self.use_scene_unit:
132 global_scale /= scene.unit_settings.scale_length
134 global_matrix = axis_conversion(from_forward=self.axis_forward,
135 from_up=self.axis_up,
136 ).to_4x4() @ Matrix.Scale(global_scale, 4)
138 if not paths:
139 paths.append(self.filepath)
141 if bpy.ops.object.mode_set.poll():
142 bpy.ops.object.mode_set(mode='OBJECT')
144 if bpy.ops.object.select_all.poll():
145 bpy.ops.object.select_all(action='DESELECT')
147 for path in paths:
148 objName = bpy.path.display_name(os.path.basename(path))
149 tris, tri_nors, pts = stl_utils.read_stl(path)
150 tri_nors = tri_nors if self.use_facet_normal else None
151 blender_utils.create_and_link_mesh(objName, tris, tri_nors, pts, global_matrix)
153 return {'FINISHED'}
156 @orientation_helper(axis_forward='Y', axis_up='Z')
157 class ExportSTL(Operator, ExportHelper):
158 """Save STL triangle mesh data from the active object"""
159 bl_idname = "export_mesh.stl"
160 bl_label = "Export STL"
162 filename_ext = ".stl"
163 filter_glob: StringProperty(default="*.stl", options={'HIDDEN'})
165 use_selection: BoolProperty(
166 name="Selection Only",
167 description="Export selected objects only",
168 default=False,
170 global_scale: FloatProperty(
171 name="Scale",
172 min=0.01, max=1000.0,
173 default=1.0,
176 use_scene_unit: BoolProperty(
177 name="Scene Unit",
178 description="Apply current scene's unit (as defined by unit scale) to exported data",
179 default=False,
181 ascii: BoolProperty(
182 name="Ascii",
183 description="Save the file in ASCII file format",
184 default=False,
186 use_mesh_modifiers: BoolProperty(
187 name="Apply Modifiers",
188 description="Apply the modifiers before saving",
189 default=True,
191 batch_mode: EnumProperty(
192 name="Batch Mode",
193 items=(('OFF', "Off", "All data in one file"),
194 ('OBJECT', "Object", "Each object as a file"),
197 @property
198 def check_extension(self):
199 return self.batch_mode == 'OFF'
201 def execute(self, context):
202 from . import stl_utils
203 from . import blender_utils
204 import itertools
205 from mathutils import Matrix
206 keywords = self.as_keywords(ignore=("axis_forward",
207 "axis_up",
208 "use_selection",
209 "global_scale",
210 "check_existing",
211 "filter_glob",
212 "use_scene_unit",
213 "use_mesh_modifiers",
214 "batch_mode"
217 scene = context.scene
218 if self.use_selection:
219 data_seq = context.selected_objects
220 else:
221 data_seq = scene.objects
223 # Take into account scene's unit scale, so that 1 inch in Blender gives 1 inch elsewhere! See T42000.
224 global_scale = self.global_scale
225 if scene.unit_settings.system != 'NONE' and self.use_scene_unit:
226 global_scale *= scene.unit_settings.scale_length
228 global_matrix = axis_conversion(to_forward=self.axis_forward,
229 to_up=self.axis_up,
230 ).to_4x4() @ Matrix.Scale(global_scale, 4)
232 if self.batch_mode == 'OFF':
233 faces = itertools.chain.from_iterable(
234 blender_utils.faces_from_mesh(ob, global_matrix, self.use_mesh_modifiers)
235 for ob in data_seq)
237 stl_utils.write_stl(faces=faces, **keywords)
238 elif self.batch_mode == 'OBJECT':
239 prefix = os.path.splitext(self.filepath)[0]
240 keywords_temp = keywords.copy()
241 for ob in data_seq:
242 faces = blender_utils.faces_from_mesh(ob, global_matrix, self.use_mesh_modifiers)
243 keywords_temp["filepath"] = prefix + bpy.path.clean_name(ob.name) + ".stl"
244 stl_utils.write_stl(faces=faces, **keywords_temp)
246 return {'FINISHED'}
249 def menu_import(self, context):
250 self.layout.operator(ImportSTL.bl_idname, text="Stl (.stl)")
253 def menu_export(self, context):
254 self.layout.operator(ExportSTL.bl_idname, text="Stl (.stl)")
257 classes = [
258 ImportSTL,
259 ExportSTL
262 def register():
263 for cls in classes:
264 bpy.utils.register_class(cls)
266 bpy.types.TOPBAR_MT_file_import.append(menu_import)
267 bpy.types.TOPBAR_MT_file_export.append(menu_export)
270 def unregister():
271 for cls in classes:
272 bpy.utils.unregister_class(cls)
274 bpy.types.TOPBAR_MT_file_import.remove(menu_import)
275 bpy.types.TOPBAR_MT_file_export.remove(menu_export)
278 if __name__ == "__main__":
279 register()