From 167c40c9e5781a1f5f1adfa3b6b9b6a96aac2246 Mon Sep 17 00:00:00 2001 From: Julian Daube Date: Tue, 2 Jul 2019 14:44:42 +0200 Subject: [PATCH] blender: first plugin version --- __init__.py | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++ import_gds.py | 85 ++++++++++++++++++++++++ 2 files changed, 263 insertions(+) create mode 100644 __init__.py create mode 100644 import_gds.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..9c98d53 --- /dev/null +++ b/__init__.py @@ -0,0 +1,178 @@ +bl_info = { + "name": "gdsii OBJ format", + "author": "Julian Daube", + "version": (0, 9, 0), + "blender": (2, 78, 0), + "location": "File > Import-Export", + "description": "Import GdsII files", + "warning": "", + "category": "Import-Export"} + +if "bpy" in locals(): + import importlib + if "import_gds" in locals(): + importlib.reload(import_gds) + +import bpy + +from bpy.props import ( + BoolProperty, + FloatProperty, + StringProperty, + EnumProperty, + ) +from bpy_extras.io_utils import ( + ImportHelper, + ExportHelper, + orientation_helper_factory, + path_reference_mode, + axis_conversion, + ) + + +IOOBJOrientationHelper = orientation_helper_factory("IOOBJOrientationHelper", axis_forward='-Z', axis_up='Y') + + +class ImportGDS(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper): + """Load a GDSII File""" + bl_idname = "import_scene.gds" + bl_label = "Import GDSII" + bl_options = {'PRESET', 'UNDO'} + + filename_ext = ".obj" + filter_glob = StringProperty( + default="*.gds", + options={'HIDDEN'}, + ) + + use_edges = BoolProperty( + name="Lines", + description="Import lines and faces with 2 verts as edge", + default=True, + ) + use_smooth_groups = BoolProperty( + name="Smooth Groups", + description="Surround smooth groups by sharp edges", + default=True, + ) + + use_split_objects = BoolProperty( + name="Object", + description="Import OBJ Objects into Blender Objects", + default=True, + ) + use_split_groups = BoolProperty( + name="Group", + description="Import OBJ Groups into Blender Objects", + default=True, + ) + + use_groups_as_vgroups = BoolProperty( + name="Poly Groups", + description="Import OBJ groups as vertex groups", + default=False, + ) + + use_image_search = BoolProperty( + name="Image Search", + description="Search subdirs for any associated images " + "(Warning, may be slow)", + default=True, + ) + + split_mode = EnumProperty( + name="Split", + items=(('ON', "Split", "Split geometry, omits unused verts"), + ('OFF', "Keep Vert Order", "Keep vertex order from file"), + ), + ) + + global_clamp_size = FloatProperty( + name="Clamp Size", + description="Clamp bounds under this value (zero to disable)", + min=0.0, max=1000.0, + soft_min=0.0, soft_max=1000.0, + default=0.0, + ) + + def execute(self, context): + # print("Selected: " + context.active_object.name) + from . import import_gds + + if self.split_mode == 'OFF': + self.use_split_objects = False + self.use_split_groups = False + else: + self.use_groups_as_vgroups = False + + keywords = self.as_keywords(ignore=("axis_forward", + "axis_up", + "filter_glob", + "split_mode", + )) + + global_matrix = axis_conversion(from_forward=self.axis_forward, + from_up=self.axis_up, + ).to_4x4() + + keywords["global_matrix"] = global_matrix + keywords["use_cycles"] = (context.scene.render.engine == 'CYCLES') + + if bpy.data.is_saved and context.user_preferences.filepaths.use_relative_paths: + import os + keywords["relpath"] = os.path.dirname(bpy.data.filepath) + + return import_gds.load(context, **keywords) + + def draw(self, context): + layout = self.layout + + row = layout.row(align=True) + row.prop(self, "use_smooth_groups") + row.prop(self, "use_edges") + + box = layout.box() + row = box.row() + row.prop(self, "split_mode", expand=True) + + row = box.row() + if self.split_mode == 'ON': + row.label(text="Split by:") + row.prop(self, "use_split_objects") + row.prop(self, "use_split_groups") + else: + row.prop(self, "use_groups_as_vgroups") + + row = layout.split(percentage=0.67) + row.prop(self, "global_clamp_size") + layout.prop(self, "axis_forward") + layout.prop(self, "axis_up") + + layout.prop(self, "use_image_search") + + + +def menu_func_import(self, context): + self.layout.operator(ImportGDS.bl_idname, text="GDSII Stream (.gds)") + +classes = ( + ImportGDS, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.INFO_MT_file_import.append(menu_func_import) + + +def unregister(): + bpy.types.INFO_MT_file_import.remove(menu_func_import) + + for cls in classes: + bpy.utils.unregister_class(cls) + + +if __name__ == "__main__": + register() diff --git a/import_gds.py b/import_gds.py new file mode 100644 index 0000000..1b825d8 --- /dev/null +++ b/import_gds.py @@ -0,0 +1,85 @@ +from . import gds + +import importlib +if "gds" in locals(): + gds = importlib.reload(gds) + +import pathlib +import bpy +from bpy_extras.wm_utils.progress_report import ProgressReport + +def load(context, + filepath, + *, + global_clamp_size=0.0, + use_smooth_groups=True, + use_edges=True, + use_split_objects=True, + use_split_groups=True, + use_image_search=True, + use_groups_as_vgroups=False, + use_cycles=True, + relpath=None, + global_matrix=None + ): + + path = pathlib.Path(filepath) + + + with ProgressReport(context.window_manager) as progress: + progress.enter_substeps(1, "Importing GDS %r..." % filepath) + with open(filepath, "rb") as f: + lib = gds.parse_file(f) + + progress.step("Done, linking references") + lib.link_refs() + progress.step("Done, building meshes") + + layergroups = {} + + for name, value in lib.structures.items(): + if not len(value.elements): + continue + + verts = {} + faces = {} + + root = bpy.data.objects.new(name, None) + context.scene.objects.link(root) + + for element in value.elements: + # handle all elements + if isinstance(element, gds.Boundary): + if element.layer not in faces: + verts[element.layer] = [] + faces[element.layer] = [] + + indices = [] + for point in element.points[:-1]: + verts[element.layer].append((point[0] * lib.units_per_dbunit, point[1]*lib.units_per_dbunit, 0)) + indices.append(len(verts[element.layer])-1) + + faces[element.layer].append(tuple(indices)) + + for layer in verts.keys(): + if layer not in layergroups: + layergroups[layer] = bpy.data.groups.new("layer{}".format(layer)) + + meshname = "{}[{}]".format(name.replace("\x00", ""), layer) + + print(layer, meshname) + mesh = bpy.data.meshes.new(meshname) + mesh.from_pydata(verts[layer], [], faces[layer]) + mesh.update() + + obj = bpy.data.objects.new(meshname, mesh) + + context.scene.objects.link(obj) + layergroups[layer].objects.link(obj) + obj.parent = root + + + progress.leave_substeps("Done") + + return {"FINISHED"} +