304 lines
8.7 KiB
Python
304 lines
8.7 KiB
Python
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"}
|
|
|