blender: various improvments and fixes in object generator
This commit is contained in:
parent
b82475a4ab
commit
4661b9e5c8
279
import_gds.py
279
import_gds.py
@ -1,4 +1,5 @@
|
||||
from . import gds
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import importlib
|
||||
if "gds" in locals():
|
||||
@ -31,8 +32,6 @@ class Progressor:
|
||||
self.wm.progress_begin(0, t)
|
||||
self.report = t
|
||||
|
||||
print ("begin new report , total {}".format(t))
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return 0
|
||||
@ -57,18 +56,84 @@ class Progressor:
|
||||
|
||||
class SceneInserter(object):
|
||||
class Layer(object):
|
||||
def __init__(self, context, number):
|
||||
self.name = "layer{}".format(number)
|
||||
def __init__(self, context, number, thickness=1, datatypes=[], ):
|
||||
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
|
||||
self.group = bpy.data.groups.new(self.name)
|
||||
self.name = self.group.name
|
||||
self.groups[datatype] = bpy.data.groups.new(name)
|
||||
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):
|
||||
self.lib = lib
|
||||
@ -77,6 +142,8 @@ class SceneInserter(object):
|
||||
self.layers = {}
|
||||
self.ignore_structures = {}
|
||||
|
||||
self.add_stackup()
|
||||
|
||||
self.top_cell = top_cell
|
||||
if top_cell == "":
|
||||
self.top_cell = None
|
||||
@ -89,6 +156,26 @@ class SceneInserter(object):
|
||||
if sname in lib.structures:
|
||||
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):
|
||||
obj = bpy.data.objects.new(name, None)
|
||||
@ -98,6 +185,55 @@ class SceneInserter(object):
|
||||
def link(self, 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):
|
||||
# is the object already existent => return new instance
|
||||
if structure.name in bpy.data.objects:
|
||||
@ -106,8 +242,13 @@ class SceneInserter(object):
|
||||
if progress:
|
||||
progress.total += 1 + len(structure.references)
|
||||
|
||||
verts = {}
|
||||
faces = {}
|
||||
# for boundary elements
|
||||
class Mesh:
|
||||
def __init__(self):
|
||||
self.verts = []
|
||||
self.faces = []
|
||||
|
||||
boundaries = {}
|
||||
|
||||
# create control empty
|
||||
root = self.new_empty(structure.name)
|
||||
@ -120,37 +261,52 @@ class SceneInserter(object):
|
||||
#print("structure name: {}".format(structure.name))
|
||||
|
||||
# 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 element.layer not in faces:
|
||||
verts[element.layer] = []
|
||||
faces[element.layer] = []
|
||||
# every element should be of derived of gds.Drawable
|
||||
if index not in boundaries:
|
||||
boundaries[index] = Mesh()
|
||||
|
||||
indices = []
|
||||
for point in element.points[:-1]:
|
||||
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
|
||||
# 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)
|
||||
else:
|
||||
print("import of {} not implemented".format(type(element)))
|
||||
|
||||
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.from_pydata(verts[layer], [], faces[layer])
|
||||
mesh.update()
|
||||
mesh = bpy.data.meshes.new(meshname)
|
||||
mesh.from_pydata(meshtemplate.verts, [], meshtemplate.faces)
|
||||
mesh.update()
|
||||
|
||||
obj = bpy.data.objects.new(meshname, mesh)
|
||||
self.link(obj)
|
||||
self.layers[layer].link(obj)
|
||||
obj.parent = root
|
||||
obj = bpy.data.objects.new(meshname, mesh)
|
||||
self.link(obj)
|
||||
layer.link(layerident[1], obj)
|
||||
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:
|
||||
progress.inc()
|
||||
@ -169,6 +325,7 @@ class SceneInserter(object):
|
||||
obj = self.build_object(reference.structure, progress=progress)
|
||||
|
||||
if hasattr(reference, "size"):
|
||||
# this is an Array reference
|
||||
def add(a,b):
|
||||
return (a[0] + b[0], a[1] + b[1])
|
||||
def inv(a):
|
||||
@ -182,28 +339,26 @@ class SceneInserter(object):
|
||||
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]
|
||||
xspace = scale(vx, 1.0/reference.size[0] * self.lib.units_per_dbunit)
|
||||
yspace = scale(vy, 1.0/reference.size[1] * self.lib.units_per_dbunit)
|
||||
|
||||
master = obj
|
||||
obj = self.new_empty(reference.structure.name)
|
||||
master.parent = obj
|
||||
master.location = (0,0,0)
|
||||
# use array modifiers to create the aref array
|
||||
for child in obj.children:
|
||||
mod_x = child.modifiers.new("ax", "ARRAY")
|
||||
|
||||
# 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
|
||||
mod_x.use_constant_offset = True
|
||||
mod_x.use_relative_offset = False
|
||||
mod_x.constant_offset_displace[0] = xspace[0]
|
||||
mod_x.constant_offset_displace[1] = xspace[1]
|
||||
mod_x.count = reference.size[0]
|
||||
|
||||
# calc new pos
|
||||
iobj = self.new_instance(master)
|
||||
mod_y = child.modifiers.new("ay", "ARRAY")
|
||||
|
||||
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
|
||||
mod_y.use_constant_offset = True
|
||||
mod_y.use_relative_offset = False
|
||||
mod_y.constant_offset_displace[0] = yspace[0]
|
||||
mod_y.constant_offset_displace[1] = yspace[1]
|
||||
mod_y.count = reference.size[1]
|
||||
else:
|
||||
# this is a single reference
|
||||
# just copy the object
|
||||
@ -233,15 +388,33 @@ class SceneInserter(object):
|
||||
self.link(root)
|
||||
|
||||
for child in obj.children:
|
||||
childObj = child.copy()
|
||||
self.link(childObj)
|
||||
if child.name.find("profile") != -1:
|
||||
# only copy path profiles
|
||||
continue
|
||||
|
||||
childObj = child.copy()
|
||||
childObj.parent = root
|
||||
self.link(childObj)
|
||||
|
||||
# relink copy to groups
|
||||
for group in child.users_group:
|
||||
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
|
||||
|
||||
|
||||
@ -294,9 +467,7 @@ def load(context,
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user