blender: various improvments and fixes in object generator
This commit is contained in:
parent
b82475a4ab
commit
4661b9e5c8
301
import_gds.py
301
import_gds.py
@ -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
|
||||||
@ -55,20 +54,86 @@ class Progressor:
|
|||||||
self.total = update.total
|
self.total = update.total
|
||||||
self.current = update.current
|
self.current = update.current
|
||||||
|
|
||||||
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:
|
|
||||||
# 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):
|
# stackup results
|
||||||
self.group.objects.link(obj)
|
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.groups[datatype] = bpy.data.groups.new(name)
|
||||||
|
else:
|
||||||
|
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 __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,9 +242,14 @@ 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,37 +261,52 @@ 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)
|
||||||
# build structure mesh objects
|
self.build_path_element(cname, root, element)
|
||||||
# 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)
|
else:
|
||||||
|
print("import of {} not implemented".format(type(element)))
|
||||||
|
|
||||||
mesh = bpy.data.meshes.new(meshname)
|
with ThreadPoolExecutor(max_workers=5) as executor:
|
||||||
mesh.from_pydata(verts[layer], [], faces[layer])
|
# build structure mesh for boundary elements
|
||||||
mesh.update()
|
for ident, template in boundaries.items():
|
||||||
|
def do_boundary(layerident, meshtemplate):
|
||||||
|
layer = self.get_layer(layerident[0])
|
||||||
|
meshname = "{}[{}]".format(structure.name, layerident)
|
||||||
|
|
||||||
obj = bpy.data.objects.new(meshname, mesh)
|
mesh = bpy.data.meshes.new(meshname)
|
||||||
self.link(obj)
|
mesh.from_pydata(meshtemplate.verts, [], meshtemplate.faces)
|
||||||
self.layers[layer].link(obj)
|
mesh.update()
|
||||||
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:
|
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)
|
|
||||||
|
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]
|
||||||
|
|
||||||
# this is an aref, create object array
|
mod_y = child.modifiers.new("ay", "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))
|
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:
|
||||||
|
if child.name.find("profile") != -1:
|
||||||
|
# only copy path profiles
|
||||||
|
continue
|
||||||
|
|
||||||
childObj = child.copy()
|
childObj = child.copy()
|
||||||
self.link(childObj)
|
|
||||||
|
|
||||||
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,10 +467,8 @@ 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)
|
||||||
prog = Progressor(context.window_manager)
|
prog = Progressor(context.window_manager)
|
||||||
@ -311,7 +482,7 @@ def load(context,
|
|||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
progress.leave_substeps("Done")
|
progress.leave_substeps("Done")
|
||||||
prog.end()
|
prog.end()
|
||||||
|
|
||||||
context.scene.update()
|
context.scene.update()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user