blender: make definition of top cell possible
This commit is contained in:
parent
3aaabc6326
commit
07eec6f648
153
__init__.py
153
__init__.py
@ -31,7 +31,6 @@ from bpy_extras.io_utils import (
|
||||
|
||||
IOOBJOrientationHelper = orientation_helper_factory("IOOBJOrientationHelper", axis_forward='-Z', axis_up='Y')
|
||||
|
||||
|
||||
class ImportGDS(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
|
||||
"""Load a GDSII File"""
|
||||
bl_idname = "import_scene.gds"
|
||||
@ -44,110 +43,112 @@ class ImportGDS(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
use_edges = BoolProperty(
|
||||
name="Lines",
|
||||
description="Import lines and faces with 2 verts as edge",
|
||||
default=True,
|
||||
)
|
||||
use_smooth_groups = BoolProperty(
|
||||
name="Smooth Groups",
|
||||
description="Surround smooth groups by sharp edges",
|
||||
default=True,
|
||||
)
|
||||
# use_edges = BoolProperty(
|
||||
# name="Lines",
|
||||
# description="Import lines and faces with 2 verts as edge",
|
||||
# default=True,
|
||||
# )
|
||||
# use_smooth_groups = BoolProperty(
|
||||
# name="Smooth Groups",
|
||||
# description="Surround smooth groups by sharp edges",
|
||||
# default=True,
|
||||
# )
|
||||
|
||||
use_split_objects = BoolProperty(
|
||||
name="Object",
|
||||
description="Import OBJ Objects into Blender Objects",
|
||||
default=True,
|
||||
)
|
||||
use_split_groups = BoolProperty(
|
||||
name="Group",
|
||||
description="Import OBJ Groups into Blender Objects",
|
||||
default=True,
|
||||
)
|
||||
# use_split_objects = BoolProperty(
|
||||
# name="Object",
|
||||
# description="Import OBJ Objects into Blender Objects",
|
||||
# default=True,
|
||||
# )
|
||||
# use_split_groups = BoolProperty(
|
||||
# name="Group",
|
||||
# description="Import OBJ Groups into Blender Objects",
|
||||
# default=True,
|
||||
# )
|
||||
|
||||
use_groups_as_vgroups = BoolProperty(
|
||||
name="Poly Groups",
|
||||
description="Import OBJ groups as vertex groups",
|
||||
default=False,
|
||||
)
|
||||
# use_groups_as_vgroups = BoolProperty(
|
||||
# name="Poly Groups",
|
||||
# description="Import OBJ groups as vertex groups",
|
||||
# default=False,
|
||||
# )
|
||||
|
||||
use_image_search = BoolProperty(
|
||||
name="Image Search",
|
||||
description="Search subdirs for any associated images "
|
||||
"(Warning, may be slow)",
|
||||
default=True,
|
||||
)
|
||||
# use_image_search = BoolProperty(
|
||||
# name="Image Search",
|
||||
# description="Search subdirs for any associated images "
|
||||
# "(Warning, may be slow)",
|
||||
# default=True,
|
||||
# )
|
||||
|
||||
split_mode = EnumProperty(
|
||||
name="Split",
|
||||
items=(('ON', "Split", "Split geometry, omits unused verts"),
|
||||
('OFF', "Keep Vert Order", "Keep vertex order from file"),
|
||||
),
|
||||
)
|
||||
# split_mode = EnumProperty(
|
||||
# name="Split",
|
||||
# items=(('ON', "Split", "Split geometry, omits unused verts"),
|
||||
# ('OFF', "Keep Vert Order", "Keep vertex order from file"),
|
||||
# ),
|
||||
# )
|
||||
|
||||
global_clamp_size = FloatProperty(
|
||||
name="Clamp Size",
|
||||
description="Clamp bounds under this value (zero to disable)",
|
||||
min=0.0, max=1000.0,
|
||||
soft_min=0.0, soft_max=1000.0,
|
||||
default=0.0,
|
||||
# global_clamp_size = FloatProperty(
|
||||
# name="Clamp Size",
|
||||
# description="Clamp bounds under this value (zero to disable)",
|
||||
# min=0.0, max=1000.0,
|
||||
# soft_min=0.0, soft_max=1000.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):
|
||||
# print("Selected: " + context.active_object.name)
|
||||
from . import import_gds
|
||||
|
||||
if self.split_mode == 'OFF':
|
||||
self.use_split_objects = False
|
||||
self.use_split_groups = False
|
||||
else:
|
||||
self.use_groups_as_vgroups = False
|
||||
|
||||
# ignore axis helper arguments
|
||||
keywords = self.as_keywords(ignore=("axis_forward",
|
||||
"axis_up",
|
||||
"filter_glob",
|
||||
"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:
|
||||
import os
|
||||
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):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(self, "use_smooth_groups")
|
||||
row.prop(self, "use_edges")
|
||||
row = layout.row()
|
||||
row.prop(self, "top_cell_name")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.prop(self, "split_mode", expand=True)
|
||||
# row = layout.row(align=True)
|
||||
# row.prop(self, "use_smooth_groups")
|
||||
# row.prop(self, "use_edges")
|
||||
|
||||
row = box.row()
|
||||
if self.split_mode == 'ON':
|
||||
row.label(text="Split by:")
|
||||
row.prop(self, "use_split_objects")
|
||||
row.prop(self, "use_split_groups")
|
||||
else:
|
||||
row.prop(self, "use_groups_as_vgroups")
|
||||
# box = layout.box()
|
||||
# row = box.row()
|
||||
# row.prop(self, "split_mode", expand=True)
|
||||
|
||||
row = layout.split(percentage=0.67)
|
||||
row.prop(self, "global_clamp_size")
|
||||
layout.prop(self, "axis_forward")
|
||||
layout.prop(self, "axis_up")
|
||||
# row = box.row()
|
||||
# if self.split_mode == 'ON':
|
||||
# row.label(text="Split by:")
|
||||
# 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")
|
||||
|
||||
|
||||
|
||||
|
249
import_gds.py
249
import_gds.py
@ -9,7 +9,7 @@ import bpy
|
||||
from bpy_extras.wm_utils.progress_report import ProgressReport
|
||||
|
||||
class Progressor:
|
||||
def __init__(self, wm, precision = 0.001):
|
||||
def __init__(self, wm, precision = 0.0001):
|
||||
self.wm = wm
|
||||
self.report = 0
|
||||
self.precision = precision
|
||||
@ -26,10 +26,13 @@ class Progressor:
|
||||
|
||||
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
|
||||
@ -44,25 +47,77 @@ class Progressor:
|
||||
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
|
||||
|
||||
def build_object(context, layergroups, lib, struct):
|
||||
name = struct.name
|
||||
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 = {}
|
||||
|
||||
print("structure name: {}".format(name))
|
||||
# create control empty
|
||||
root = self.new_empty(structure.name)
|
||||
|
||||
root = bpy.data.objects.new(name, None)
|
||||
context.scene.objects.link(root)
|
||||
# reset name in case blender
|
||||
# changes the object name
|
||||
# (e.g. due to duplicates)
|
||||
structure.name = root.name
|
||||
|
||||
for element in struct.elements:
|
||||
# handle all elements
|
||||
#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] = []
|
||||
@ -70,45 +125,151 @@ def build_object(context, layergroups, lib, struct):
|
||||
|
||||
indices = []
|
||||
for point in element.points[:-1]:
|
||||
pos = lib.normalize_coord(point)
|
||||
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 object
|
||||
# build structure mesh objects
|
||||
# create mesh object for each layer
|
||||
for layer in verts.keys():
|
||||
if layer not in layergroups:
|
||||
layergroups[layer] = bpy.data.groups.new("layer{}".format(layer))
|
||||
if layer not in self.layers:
|
||||
self.layers[layer] = SceneInserter.Layer(self.context, layer)
|
||||
|
||||
meshname = "{}[{}]".format(name, 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)
|
||||
|
||||
context.scene.objects.link(obj)
|
||||
layergroups[layer].objects.link(obj)
|
||||
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,
|
||||
*,
|
||||
global_clamp_size=0.0,
|
||||
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
|
||||
top_cell_name=""
|
||||
):
|
||||
|
||||
path = pathlib.Path(filepath)
|
||||
@ -121,38 +282,22 @@ def load(context,
|
||||
with open(filepath, "rb") as f:
|
||||
lib = gds.parse_file(f, progress_func=prog)
|
||||
|
||||
progress.step("Done, linking references")
|
||||
lib.link_refs(prog)
|
||||
progress.step("Done, building meshes")
|
||||
progress.step("linking references")
|
||||
prog.end()
|
||||
|
||||
layergroups = {}
|
||||
lib.link_refs(prog)
|
||||
|
||||
for name, value in lib.structures.items():
|
||||
build_object(context, layergroups, lib, value)
|
||||
progress.step("inserting structures into scene")
|
||||
prog.end()
|
||||
|
||||
# process all references
|
||||
for name, value in lib.structures.items():
|
||||
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)
|
||||
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"}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user