blender: various improvments and fixes in object generator

This commit is contained in:
Julian Daube 2019-07-04 21:19:12 +02:00
parent b82475a4ab
commit 4661b9e5c8

View File

@ -1,4 +1,5 @@
from . import gds from . import gds
from concurrent.futures import ThreadPoolExecutor
import importlib import importlib
if "gds" in locals(): if "gds" in locals():
@ -31,8 +32,6 @@ class Progressor:
self.wm.progress_begin(0, t) self.wm.progress_begin(0, t)
self.report = t self.report = t
print ("begin new report , total {}".format(t))
@property @property
def current(self): def current(self):
return 0 return 0
@ -57,18 +56,84 @@ class Progressor:
class SceneInserter(object): class SceneInserter(object):
class Layer(object): class Layer(object):
def __init__(self, context, number): def __init__(self, context, number, thickness=1, datatypes=[], ):
self.name = "layer{}".format(number) self.number = number
self.context = context
if self.name not in bpy.data.groups: # stackup results
self.thickness = thickness
self.absolute_height = 0
# stackup
self.Prev = None
self.Next = None
self.groups = {}
self.materials = {}
for datatype in datatypes:
self.new_datatype(type)
def name_for_layer(self, datatype):
return "layer{}/{}".format(self.number, datatype)
def new_datatype(self, datatype):
name = self.name_for_layer(datatype)
if name not in bpy.data.groups:
# create new group # create new group
self.group = bpy.data.groups.new(self.name) self.groups[datatype] = bpy.data.groups.new(name)
self.name = self.group.name
else: else:
self.group = bpy.data.groups[self.name] self.groups[datatype] = bpy.data.groups[name]
if name not in bpy.data.materials:
self.materials[name] = bpy.data.materials.new(name)
else:
self.materials[name] = bpy.data.materials[name]
def group_for_datatype(self, dtype):
if dtype not in self.groups:
self.new_datatype(dtype)
return self.groups[dtype]
def insert_after(self, other):
end = self
while end.Next:
end = end.Next
if self.Prev:
self.Prev.Next = None
self.Prev = None
# link own list to new list
if other.Next:
if other.Next.Prev:
other.Next.Prev = end
end.Next = other.Next
other.Next = self
self.Prev = other
# recalc height
end = self
while end:
end.absolute_height = end.Prev.absolute_height + end.Prev.thickness
end = end.Next
return self
def link(self, datatype, obj):
self.group_for_datatype(datatype).objects.link(obj)
mat = self.materials[self.name_for_layer(datatype)]
obj.data.materials.append(mat)
def link(self, obj):
self.group.objects.link(obj)
def __init__(self, context, lib, ignore_structures={}, top_cell=None): def __init__(self, context, lib, ignore_structures={}, top_cell=None):
self.lib = lib self.lib = lib
@ -77,6 +142,8 @@ class SceneInserter(object):
self.layers = {} self.layers = {}
self.ignore_structures = {} self.ignore_structures = {}
self.add_stackup()
self.top_cell = top_cell self.top_cell = top_cell
if top_cell == "": if top_cell == "":
self.top_cell = None self.top_cell = None
@ -89,6 +156,26 @@ class SceneInserter(object):
if sname in lib.structures: if sname in lib.structures:
self.ignore_structures[sname] = True self.ignore_structures[sname] = True
def add_layer(self, number, thickness=1):
self.layers[number] = SceneInserter.Layer(self.context, number, thickness)
return self.layers[number]
def get_layer(self, number):
if number not in self.layers:
return self.add_layer(number)
return self.layers[number]
def add_stackup(self):
prev = None
for i in range(0,200):
l = self.add_layer(i)
if prev:
l.insert_after(prev)
prev = l
# example stackup for now
#self.add_layer(8).insert_after(self.add_layer(6).insert_after(self.add_layer(5).insert_after(self.add_layer(4))))
def new_empty(self, name): def new_empty(self, name):
obj = bpy.data.objects.new(name, None) obj = bpy.data.objects.new(name, None)
@ -98,6 +185,55 @@ class SceneInserter(object):
def link(self, obj): def link(self, obj):
self.context.scene.objects.link(obj) self.context.scene.objects.link(obj)
def build_path_element(self, name, root, element):
# build path profile
widthcurve = bpy.data.curves.new("profile-" + name , "CURVE")
bevelobj = bpy.data.objects.new("profile-" + name , widthcurve)
bevelobj.parent = root
# hide the bevel object
self.link(bevelobj)
bevelobj.hide = True
bevelobj.hide_render = True
widthspline = widthcurve.splines.new("POLY")
widthspline.use_smooth = False
widthspline.use_cyclic_u = True
widthspline.points.add(3)
# create bevel profile
width = element.width * self.lib.units_per_dbunit
thickness = self.get_layer(element.layer).thickness
widthspline.points[0].co = (-1*width/2.0, thickness/2.0, 0,0)
widthspline.points[1].co = ( width/2.0, thickness/2.0, 0,0)
widthspline.points[2].co = ( width/2.0, -1*thickness/2.0, 0,0)
widthspline.points[3].co = (-1*width/2.0, -1*thickness/2.0, 0,0)
# create actual path
pathcurve = bpy.data.curves.new(name, "CURVE")
pathcurve.use_fill_caps = True
pathcurve.bevel_object = bevelobj
pathspline = pathcurve.splines.new("POLY")
pathspline.points.add(len(element.points)-1)
pathspline.use_smooth = False
# import path
for i, p in enumerate(element.points):
pos = self.lib.normalize_coord(p)
pathspline.points[i].co = (pos[0], pos[1], 0,0)
# setup pathobject
pathobj = bpy.data.objects.new(name, pathcurve)
self.link(pathobj)
self.get_layer(element.layer).link(element.datatype, pathobj)
pathobj.location.z = self.get_layer(element.layer).absolute_height
pathobj.parent = root
def build_object(self, structure, progress=None): def build_object(self, structure, progress=None):
# is the object already existent => return new instance # is the object already existent => return new instance
if structure.name in bpy.data.objects: if structure.name in bpy.data.objects:
@ -106,8 +242,13 @@ class SceneInserter(object):
if progress: if progress:
progress.total += 1 + len(structure.references) progress.total += 1 + len(structure.references)
verts = {} # for boundary elements
faces = {} class Mesh:
def __init__(self):
self.verts = []
self.faces = []
boundaries = {}
# create control empty # create control empty
root = self.new_empty(structure.name) root = self.new_empty(structure.name)
@ -120,38 +261,53 @@ class SceneInserter(object):
#print("structure name: {}".format(structure.name)) #print("structure name: {}".format(structure.name))
# add all geometry # add all geometry
for element in structure.elements: for number, element in enumerate(structure.elements):
index = (element.layer, element.datatype)
if isinstance(element, gds.Boundary): if isinstance(element, gds.Boundary):
if element.layer not in faces: # every element should be of derived of gds.Drawable
verts[element.layer] = [] if index not in boundaries:
faces[element.layer] = [] boundaries[index] = Mesh()
indices = [] indices = []
for point in element.points[:-1]: for point in element.points[:-1]:
pos = self.lib.normalize_coord(point) pos = self.lib.normalize_coord(point)
verts[element.layer].append((pos[0], pos[1], 0)) boundaries[index].verts.append((pos[0], pos[1], 0))
indices.append(len(boundaries[index].verts)-1)
indices.append(len(verts[element.layer])-1) boundaries[index].faces.append(tuple(indices))
faces[element.layer].append(tuple(indices)) elif isinstance(element, gds.Path):
cname = "{}-{}[{}]".format(structure.name, number, element.layer)
self.build_path_element(cname, root, element)
# build structure mesh objects else:
# create mesh object for each layer print("import of {} not implemented".format(type(element)))
for layer in verts.keys():
if layer not in self.layers:
self.layers[layer] = SceneInserter.Layer(self.context, layer)
meshname = "{}[{}]".format(structure.name, layer) with ThreadPoolExecutor(max_workers=5) as executor:
# build structure mesh for boundary elements
for ident, template in boundaries.items():
def do_boundary(layerident, meshtemplate):
layer = self.get_layer(layerident[0])
meshname = "{}[{}]".format(structure.name, layerident)
mesh = bpy.data.meshes.new(meshname) mesh = bpy.data.meshes.new(meshname)
mesh.from_pydata(verts[layer], [], faces[layer]) mesh.from_pydata(meshtemplate.verts, [], meshtemplate.faces)
mesh.update() mesh.update()
obj = bpy.data.objects.new(meshname, mesh) obj = bpy.data.objects.new(meshname, mesh)
self.link(obj) self.link(obj)
self.layers[layer].link(obj) layer.link(layerident[1], obj)
obj.parent = root obj.parent = root
# create solifiy modifier
mod = obj.modifiers.new("Solidify", 'SOLIDIFY')
mod.thickness = 1 # probaly meters ?
mod.use_even_offset = True
mod.offset = 0
obj.location.z = layer.absolute_height
executor.submit(do_boundary, ident, template)
if progress: if progress:
progress.inc() progress.inc()
@ -169,6 +325,7 @@ class SceneInserter(object):
obj = self.build_object(reference.structure, progress=progress) obj = self.build_object(reference.structure, progress=progress)
if hasattr(reference, "size"): if hasattr(reference, "size"):
# this is an Array reference
def add(a,b): def add(a,b):
return (a[0] + b[0], a[1] + b[1]) return (a[0] + b[0], a[1] + b[1])
def inv(a): def inv(a):
@ -182,28 +339,26 @@ class SceneInserter(object):
vx = sub(reference.bounds[0], reference.position) vx = sub(reference.bounds[0], reference.position)
vy = sub(reference.bounds[1], reference.position) vy = sub(reference.bounds[1], reference.position)
xspace = 1.0/reference.size[0] xspace = scale(vx, 1.0/reference.size[0] * self.lib.units_per_dbunit)
yspace = 1.0/reference.size[1] yspace = scale(vy, 1.0/reference.size[1] * self.lib.units_per_dbunit)
master = obj # use array modifiers to create the aref array
obj = self.new_empty(reference.structure.name) for child in obj.children:
master.parent = obj mod_x = child.modifiers.new("ax", "ARRAY")
master.location = (0,0,0)
# this is an aref, create object array mod_x.use_constant_offset = True
for i in range(0, reference.size[0]): mod_x.use_relative_offset = False
for j in range(0, reference.size[1]): mod_x.constant_offset_displace[0] = xspace[0]
if i == 0 and j == 0: mod_x.constant_offset_displace[1] = xspace[1]
continue mod_x.count = reference.size[0]
# calc new pos mod_y = child.modifiers.new("ay", "ARRAY")
iobj = self.new_instance(master)
pos = add(scale(vy, j*yspace), scale(vx, i*xspace)) mod_y.use_constant_offset = True
loc = self.lib.normalize_coord(pos) mod_y.use_relative_offset = False
mod_y.constant_offset_displace[0] = yspace[0]
iobj.location = (loc[0], loc[1], 0) mod_y.constant_offset_displace[1] = yspace[1]
iobj.hide = True mod_y.count = reference.size[1]
else: else:
# this is a single reference # this is a single reference
# just copy the object # just copy the object
@ -233,15 +388,33 @@ class SceneInserter(object):
self.link(root) self.link(root)
for child in obj.children: for child in obj.children:
childObj = child.copy() if child.name.find("profile") != -1:
self.link(childObj) # only copy path profiles
continue
childObj = child.copy()
childObj.parent = root childObj.parent = root
self.link(childObj)
# relink copy to groups # relink copy to groups
for group in child.users_group: for group in child.users_group:
group.objects.link(childObj) group.objects.link(childObj)
# resetup curves
if childObj.type == "CURVE":
for spline in childObj.data.splines:
spline.use_smooth = False
# copy profile
profile = bpy.data.objects["profile-" + child.name].copy()
profile.parent = root
self.link(profile)
# resetup path
childObj.data.use_fill_caps = True
childObj.data.bevel_object = profile
return root return root
@ -294,9 +467,7 @@ def load(context,
top_cell_name="" top_cell_name=""
): ):
path = pathlib.Path(filepath)
lib = None lib = None
print(lib)
with ProgressReport(context.window_manager) as progress: with ProgressReport(context.window_manager) as progress:
progress.enter_substeps(2, "Importing GDS %r..." % filepath) progress.enter_substeps(2, "Importing GDS %r..." % filepath)