blender: make definition of top cell possible

This commit is contained in:
Julian Daube 2019-07-03 13:53:25 +02:00
parent 3aaabc6326
commit 07eec6f648
2 changed files with 299 additions and 153 deletions

View File

@ -31,7 +31,6 @@ from bpy_extras.io_utils import (
IOOBJOrientationHelper = orientation_helper_factory("IOOBJOrientationHelper", axis_forward='-Z', axis_up='Y') IOOBJOrientationHelper = orientation_helper_factory("IOOBJOrientationHelper", axis_forward='-Z', axis_up='Y')
class ImportGDS(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper): class ImportGDS(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
"""Load a GDSII File""" """Load a GDSII File"""
bl_idname = "import_scene.gds" bl_idname = "import_scene.gds"
@ -44,110 +43,112 @@ class ImportGDS(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
options={'HIDDEN'}, options={'HIDDEN'},
) )
use_edges = BoolProperty( # use_edges = BoolProperty(
name="Lines", # name="Lines",
description="Import lines and faces with 2 verts as edge", # description="Import lines and faces with 2 verts as edge",
default=True, # default=True,
) # )
use_smooth_groups = BoolProperty( # use_smooth_groups = BoolProperty(
name="Smooth Groups", # name="Smooth Groups",
description="Surround smooth groups by sharp edges", # description="Surround smooth groups by sharp edges",
default=True, # default=True,
) # )
use_split_objects = BoolProperty( # use_split_objects = BoolProperty(
name="Object", # name="Object",
description="Import OBJ Objects into Blender Objects", # description="Import OBJ Objects into Blender Objects",
default=True, # default=True,
) # )
use_split_groups = BoolProperty( # use_split_groups = BoolProperty(
name="Group", # name="Group",
description="Import OBJ Groups into Blender Objects", # description="Import OBJ Groups into Blender Objects",
default=True, # default=True,
) # )
use_groups_as_vgroups = BoolProperty( # use_groups_as_vgroups = BoolProperty(
name="Poly Groups", # name="Poly Groups",
description="Import OBJ groups as vertex groups", # description="Import OBJ groups as vertex groups",
default=False, # default=False,
) # )
use_image_search = BoolProperty( # use_image_search = BoolProperty(
name="Image Search", # name="Image Search",
description="Search subdirs for any associated images " # description="Search subdirs for any associated images "
"(Warning, may be slow)", # "(Warning, may be slow)",
default=True, # default=True,
) # )
split_mode = EnumProperty( # split_mode = EnumProperty(
name="Split", # name="Split",
items=(('ON', "Split", "Split geometry, omits unused verts"), # items=(('ON', "Split", "Split geometry, omits unused verts"),
('OFF', "Keep Vert Order", "Keep vertex order from file"), # ('OFF', "Keep Vert Order", "Keep vertex order from file"),
), # ),
) # )
global_clamp_size = FloatProperty( # global_clamp_size = FloatProperty(
name="Clamp Size", # name="Clamp Size",
description="Clamp bounds under this value (zero to disable)", # description="Clamp bounds under this value (zero to disable)",
min=0.0, max=1000.0, # min=0.0, max=1000.0,
soft_min=0.0, soft_max=1000.0, # soft_min=0.0, soft_max=1000.0,
default=0.0, # default=0.0,
) # )
top_cell_name = StringProperty(
name="Top Cell Name",
#descriptionn="When set, only import the structure with that name",
default="",
)
def execute(self, context): def execute(self, context):
# print("Selected: " + context.active_object.name) # print("Selected: " + context.active_object.name)
from . import import_gds from . import import_gds
if self.split_mode == 'OFF': # ignore axis helper arguments
self.use_split_objects = False
self.use_split_groups = False
else:
self.use_groups_as_vgroups = False
keywords = self.as_keywords(ignore=("axis_forward", keywords = self.as_keywords(ignore=("axis_forward",
"axis_up", "axis_up",
"filter_glob", "filter_glob",
"split_mode", "split_mode",
)) ))
global_matrix = axis_conversion(from_forward=self.axis_forward,
from_up=self.axis_up,
).to_4x4()
keywords["global_matrix"] = global_matrix
keywords["use_cycles"] = (context.scene.render.engine == 'CYCLES')
if bpy.data.is_saved and context.user_preferences.filepaths.use_relative_paths: if bpy.data.is_saved and context.user_preferences.filepaths.use_relative_paths:
import os import os
keywords["relpath"] = os.path.dirname(bpy.data.filepath) keywords["relpath"] = os.path.dirname(bpy.data.filepath)
return import_gds.load(context, **keywords) result= import_gds.load(context, **keywords)
# properly report errors to user in ui
if len(result) != 0 and "CANCELLED" in result:
self.report({"ERROR_INVALID_INPUT"}, "Could not find structure with name {}".format(keywords["top_cell_name"]))
return result
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
row = layout.row(align=True) row = layout.row()
row.prop(self, "use_smooth_groups") row.prop(self, "top_cell_name")
row.prop(self, "use_edges")
box = layout.box() # row = layout.row(align=True)
row = box.row() # row.prop(self, "use_smooth_groups")
row.prop(self, "split_mode", expand=True) # row.prop(self, "use_edges")
row = box.row() # box = layout.box()
if self.split_mode == 'ON': # row = box.row()
row.label(text="Split by:") # row.prop(self, "split_mode", expand=True)
row.prop(self, "use_split_objects")
row.prop(self, "use_split_groups")
else:
row.prop(self, "use_groups_as_vgroups")
row = layout.split(percentage=0.67) # row = box.row()
row.prop(self, "global_clamp_size") # if self.split_mode == 'ON':
layout.prop(self, "axis_forward") # row.label(text="Split by:")
layout.prop(self, "axis_up") # row.prop(self, "use_split_objects")
# row.prop(self, "use_split_groups")
# else:
# row.prop(self, "use_groups_as_vgroups")
layout.prop(self, "use_image_search") # row = layout.split(percentage=0.67)
# row.prop(self, "global_clamp_size")
# layout.prop(self, "axis_forward")
# layout.prop(self, "axis_up")
# layout.prop(self, "use_image_search")

View File

@ -9,7 +9,7 @@ import bpy
from bpy_extras.wm_utils.progress_report import ProgressReport from bpy_extras.wm_utils.progress_report import ProgressReport
class Progressor: class Progressor:
def __init__(self, wm, precision = 0.001): def __init__(self, wm, precision = 0.0001):
self.wm = wm self.wm = wm
self.report = 0 self.report = 0
self.precision = precision self.precision = precision
@ -26,10 +26,13 @@ class Progressor:
if self.report: if self.report:
self.wm.progress_end() self.wm.progress_end()
self.next_update = 0
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
@ -44,71 +47,229 @@ class Progressor:
def end(self): def end(self):
if self.report: if self.report:
self.report = 0 self.report = 0
self.next_update = 0
self.wm.progress_end() self.wm.progress_end()
def __call__(self, update): def __call__(self, update):
self.total = update.total self.total = update.total
self.current = update.current self.current = update.current
def build_object(context, layergroups, lib, struct): class SceneInserter(object):
name = struct.name 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]
verts = {} def link(self, obj):
faces = {} self.group.objects.link(obj)
print("structure name: {}".format(name)) def __init__(self, context, lib, ignore_structures={}, top_cell=None):
self.lib = lib
self.context = context
root = bpy.data.objects.new(name, None) self.layers = {}
context.scene.objects.link(root) 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 element in struct.elements: for sname in ignore_structures:
# handle all elements if sname in lib.structures:
if isinstance(element, gds.Boundary): self.ignore_structures[sname] = True
if element.layer not in faces:
verts[element.layer] = []
faces[element.layer] = []
indices = []
for point in element.points[:-1]:
pos = 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 object
for layer in verts.keys():
if layer not in layergroups:
layergroups[layer] = bpy.data.groups.new("layer{}".format(layer))
meshname = "{}[{}]".format(name, layer)
mesh = bpy.data.meshes.new(meshname)
mesh.from_pydata(verts[layer], [], faces[layer])
mesh.update()
obj = bpy.data.objects.new(meshname, mesh)
context.scene.objects.link(obj) def new_empty(self, name):
layergroups[layer].objects.link(obj) obj = bpy.data.objects.new(name, None)
obj.parent = root self.link(obj)
return obj
return root 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, def load(context,
filepath, filepath,
*, *,
global_clamp_size=0.0, top_cell_name=""
use_smooth_groups=True,
use_edges=True,
use_split_objects=True,
use_split_groups=True,
use_image_search=True,
use_groups_as_vgroups=False,
use_cycles=True,
relpath=None,
global_matrix=None
): ):
path = pathlib.Path(filepath) path = pathlib.Path(filepath)
@ -121,38 +282,22 @@ def load(context,
with open(filepath, "rb") as f: with open(filepath, "rb") as f:
lib = gds.parse_file(f, progress_func=prog) lib = gds.parse_file(f, progress_func=prog)
progress.step("Done, linking references") progress.step("linking references")
lib.link_refs(prog)
progress.step("Done, building meshes")
prog.end() prog.end()
layergroups = {} lib.link_refs(prog)
for name, value in lib.structures.items(): progress.step("inserting structures into scene")
build_object(context, layergroups, lib, value) prog.end()
# process all references if not SceneInserter(context=context, lib=lib, top_cell=top_cell_name)(prog):
for name, value in lib.structures.items(): return {"CANCELLED"}
root = bpy.data.objects[name]
for reference in value.references:
sname = reference.structure.name
if not sname in bpy.data.objects:
print("object not build for referenced structure \"{}\"".format(sname))
obj = build_object(context, layergroups, lib, reference.structure)
else:
obj = bpy.data.objects[sname]
#create copy of object
obj = obj.copy()
# reparent new instance
obj.parent = root
pos = lib.normalize_coord(reference.position)
obj.position = (pos[0], pos[1], 0)
progress.leave_substeps("Done") progress.leave_substeps("Done")
prog.end()
context.scene.update()
return {"FINISHED"} return {"FINISHED"}