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 class Progressor: def __init__(self, wm, precision = 0.0001): self.wm = wm self.report = 0 self.precision = precision self.next_update = 0 @property def total(self): return self.report @total.setter def total(self, t): if self.report >= t: return if self.report: self.wm.progress_end() self.next_update = 0 self.wm.progress_begin(0, t) self.report = t print ("begin new report , total {}".format(t)) @property def current(self): return 0 @current.setter def current(self, c): prog = float(c)/self.total if prog >= self.next_update: self.next_update = prog + self.precision self.wm.progress_update(c) def end(self): if self.report: self.report = 0 self.next_update = 0 self.wm.progress_end() def __call__(self, update): self.total = update.total self.current = update.current class SceneInserter(object): class Layer(object): def __init__(self, context, number): self.name = "layer{}".format(number) if self.name not in bpy.data.groups: # create new group self.group = bpy.data.groups.new(self.name) self.name = self.group.name else: self.group = bpy.data.groups[self.name] def link(self, obj): self.group.objects.link(obj) def __init__(self, context, lib, ignore_structures={}, top_cell=None): self.lib = lib self.context = context self.layers = {} self.ignore_structures = {} self.top_cell = top_cell if top_cell == "": self.top_cell = None if self.top_cell: self.top_cell = self.top_cell.strip() return for sname in ignore_structures: if sname in lib.structures: self.ignore_structures[sname] = True def new_empty(self, name): obj = bpy.data.objects.new(name, None) self.link(obj) return obj def link(self, obj): self.context.scene.objects.link(obj) def build_object(self, structure): # is the object already existent => return new instance if structure.name in bpy.data.objects: return self.new_instance(bpy.data.objects[structure.name]) verts = {} faces = {} # create control empty root = self.new_empty(structure.name) # reset name in case blender # changes the object name # (e.g. due to duplicates) structure.name = root.name #print("structure name: {}".format(structure.name)) # add all geometry for element in structure.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]: pos = self.lib.normalize_coord(point) verts[element.layer].append((pos[0], pos[1], 0)) indices.append(len(verts[element.layer])-1) faces[element.layer].append(tuple(indices)) # build structure mesh objects # create mesh object for each layer for layer in verts.keys(): if layer not in self.layers: self.layers[layer] = SceneInserter.Layer(self.context, layer) meshname = "{}[{}]".format(structure.name, layer) mesh = bpy.data.meshes.new(meshname) mesh.from_pydata(verts[layer], [], faces[layer]) mesh.update() obj = bpy.data.objects.new(meshname, mesh) self.link(obj) self.layers[layer].link(obj) obj.parent = root # handle all references # build tree depth first for reference in structure.references: obj = self.build_object(reference.structure) if hasattr(reference, "size"): def add(a,b): return (a[0] + b[0], a[1] + b[1]) def inv(a): return (-a[0], -a[1]) def sub(a, b): return add(inv(a), b) def scale(a, f): return (a[0] * f, a[1] * f) vx = sub(reference.bounds[0], reference.position) vy = sub(reference.bounds[1], reference.position) xspace = 1.0/reference.size[0] yspace = 1.0/reference.size[1] master = obj obj = self.new_empty(reference.structure.name) master.parent = obj master.location = (0,0,0) # this is an aref, create object array for i in range(0, reference.size[0]): for j in range(0, reference.size[1]): if i == 0 and j == 0: continue # calc new pos iobj = self.new_instance(master) pos = add(scale(vy, j*yspace), scale(vx, i*xspace)) loc = self.lib.normalize_coord(pos) iobj.location = (loc[0], loc[1], 0) iobj.hide = True else: # this is a single reference # just copy the object pass # use single arrow for references obj.empty_draw_type = "SINGLE_ARROW" # reparent new instance obj.parent = root pos = self.lib.normalize_coord(reference.position) obj.location = (pos[0], pos[1], 0) # apply transformations trans = reference.transformation obj.rotation_euler.z = math.radians(trans.rotation) obj.scale = (trans.zoom, trans.zoom, 1) return root def new_instance(self, obj): root = obj.copy() self.link(root) for child in obj.children: childObj = child.copy() self.link(childObj) childObj.parent = root # relink copy to groups for group in child.users_group: group.objects.link(childObj) return root def __call__(self, progress=None): class ProgressTracker: def __init__(self, total, callback): self.total = total self.current = 0 self.callback = callback def inc(self): if self.callback: self.current += 1 self.callback(self) # build one cell only if self.top_cell: if self.top_cell not in self.lib.structures: return None self.build_object(self.lib.structures[self.top_cell]) return True # build all structures in object progressTracker = ProgressTracker(2*(len(self.lib.structures)-len(self.ignore_structures)), progress) # build all structures first # since references could reference unknown objects otherwise for name, value in self.lib.structures.items(): progressTracker.inc() if name in self.ignore_structures: continue self.build_object(value) progressTracker.total += len(value.references) return True import math def load(context, filepath, *, top_cell_name="" ): path = pathlib.Path(filepath) lib = None print(lib) with ProgressReport(context.window_manager) as progress: progress.enter_substeps(2, "Importing GDS %r..." % filepath) prog = Progressor(context.window_manager) with open(filepath, "rb") as f: lib = gds.parse_file(f, progress_func=prog) progress.step("linking references") prog.end() lib.link_refs(prog) progress.step("inserting structures into scene") prog.end() if not SceneInserter(context=context, lib=lib, top_cell=top_cell_name)(prog): return {"CANCELLED"} progress.leave_substeps("Done") prog.end() context.scene.update() return {"FINISHED"}