diff options
author | Lukas Treyer <treyer@arch.ethz.ch> | 2016-03-22 12:18:32 +0300 |
---|---|---|
committer | Lukas Treyer <treyer@arch.ethz.ch> | 2016-03-22 12:18:32 +0300 |
commit | f4878ea31b08fecfab2068f8bbc82de1ee7396da (patch) | |
tree | bc1a6afb192497f6890533a0306a95cf4bae778b | |
parent | cb2f133712599040b2f5ffaa1f9ff2e3a1e55e73 (diff) |
two new merge methods: 1. merge by layers and closed no-bulge polylines: merges closed no-bulge polys to a mesh. These kinds of polys actually fit quite well to bmesh's faces. 2 merge by layers (and dxf-type) and blocks: this option inserts blocks using duplifaces. this means if a user has a dxf with a block being inserted MANY times he can use this option to increase import speed. Instead of inserting a new object for each block-insert, only a new face in a mesh is being inserted.
-rw-r--r-- | io_import_dxf/__init__.py | 40 | ||||
-rw-r--r-- | io_import_dxf/dxfimport/convert.py | 14 | ||||
-rw-r--r-- | io_import_dxf/dxfimport/do.py | 153 | ||||
-rw-r--r-- | io_import_dxf/dxfimport/groupsort.py | 14 | ||||
-rw-r--r-- | io_import_dxf/dxfimport/is_.py | 14 |
5 files changed, 209 insertions, 26 deletions
diff --git a/io_import_dxf/__init__.py b/io_import_dxf/__init__.py index d8385f89..50051173 100644 --- a/io_import_dxf/__init__.py +++ b/io_import_dxf/__init__.py @@ -35,7 +35,7 @@ except: bl_info = { "name": "Import AutoCAD DXF Format (.dxf)", "author": "Lukas Treyer, Manfred Moitzi (support + dxfgrabber library), Vladimir Elistratov, Bastien Montagne", - "version": (0, 8, 5), + "version": (0, 8, 6), "blender": (2, 7, 1), "location": "File > Import > AutoCAD DXF", "description": "Import files in the Autocad DXF format (.dxf)", @@ -81,9 +81,14 @@ __version__ = '.'.join([str(s) for s in bl_info['version']]) BY_LAYER = 0 BY_DXFTYPE = 1 -SEPARATED = 2 -LINKED_OBJECTS = 3 -GROUP_INSTANCES = 4 +BY_CLOSED_NO_BULGE_POLY = 2 +SEPARATED = 3 +LINKED_OBJECTS = 4 +GROUP_INSTANCES = 5 +BY_BLOCKS = 6 + +merge_map = {"BY_LAYER": BY_LAYER, "BY_TYPE": BY_DXFTYPE, + "BY_CLOSED_NO_BULGE_POLY": BY_CLOSED_NO_BULGE_POLY, "BY_BLOCKS": BY_BLOCKS} T_Merge = True T_ImportText = True @@ -94,7 +99,7 @@ T_OutlinerGroups = True T_Bbox = True T_CreateNewScene = False T_Recenter = False -T_ThicknessBevel = False +T_ThicknessBevel = True T_import_atts = True RELEASE_TEST = False @@ -180,7 +185,11 @@ def _update_proj_scene_do(self, context): def _update_import_atts_do(self, context): - if self.represent_thickness_and_width and self.merge: + mo = merge_map[self.merge_options] + if mo == BY_CLOSED_NO_BULGE_POLY or mo == BY_BLOCKS: + self.import_atts = False + self.represent_thickness_and_width = False + elif self.represent_thickness_and_width and self.merge: self.import_atts = True elif not self.merge: self.import_atts = False @@ -216,12 +225,18 @@ class IMPORT_OT_dxf(bpy.types.Operator): update=_update_merge ) + def _update_merge_options(self, context): + _update_import_atts_do(self, context) + merge_options = EnumProperty( name="Merge", description="Merge multiple DXF entities into one Blender object", - items=[('BY_TYPE', "By Layer AND Dxf-Type", "Merge DXF entities by type AND layer"), - ('BY_LAYER', "By Layer", "Merge DXF entities of a layer to an object")], + items=[('BY_LAYER', "By Layer", "Merge DXF entities of a layer to an object"), + ('BY_TYPE', "By Layer AND DXF-Type", "Merge DXF entities by type AND layer"), + ('BY_CLOSED_NO_BULGE_POLY', "By Layer AND closed no-bulge polys", "Polys can have a transformation attribute that makes DXF polys resemble Blender mesh faces quite a bit. Merging them results in one MESH object."), + ('BY_BLOCKS', "By Layer AND DXF-Type AND Blocks", "Merging blocks results in all uniformly scaled blocks being referenced by a dupliface mesh instead of object containers. Non-uniformly scaled blocks will be imported as indicated by 'Blocks As'.")], default='BY_LAYER', + update=_update_merge_options ) merge_lines = BoolProperty( @@ -260,12 +275,15 @@ class IMPORT_OT_dxf(bpy.types.Operator): default=T_Bbox ) + + block_options = EnumProperty( name="Blocks As", description="Select the representation of DXF blocks: linked objects or group instances", items=[('LINKED_OBJECTS', "Linked Objects", "Block objects get imported as linked objects"), ('GROUP_INSTANCES', "Group Instances", "Block objects get imported as group instances")], default='LINKED_OBJECTS', + ) def _update_create_new_scene(self, context): @@ -372,7 +390,9 @@ class IMPORT_OT_dxf(bpy.types.Operator): # merge options layout.label("Merge Options:") box = layout.box() - box.prop(self, "block_options") + sub = box.row() + #sub.enabled = merge_map[self.merge_options] != BY_BLOCKS + sub.prop(self, "block_options") box.prop(self, "do_bbox") box.prop(self, "merge") sub = box.row() @@ -383,6 +403,7 @@ class IMPORT_OT_dxf(bpy.types.Operator): # general options layout.label("Line thickness and width:") box = layout.box() + box.enabled = not merge_map[self.merge_options] == BY_CLOSED_NO_BULGE_POLY box.prop(self, "represent_thickness_and_width") sub = box.row() sub.enabled = (not self.represent_thickness_and_width and self.merge) @@ -483,7 +504,6 @@ class IMPORT_OT_dxf(bpy.types.Operator): box.label('Scene SRID %r is ignored!' % code) def execute(self, context): - merge_map = {"BY_LAYER": BY_LAYER, "BY_TYPE": BY_DXFTYPE} block_map = {"LINKED_OBJECTS": LINKED_OBJECTS, "GROUP_INSTANCES": GROUP_INSTANCES} merge_options = SEPARATED if self.merge: diff --git a/io_import_dxf/dxfimport/convert.py b/io_import_dxf/dxfimport/convert.py index 64df5c35..8260b519 100644 --- a/io_import_dxf/dxfimport/convert.py +++ b/io_import_dxf/dxfimport/convert.py @@ -244,7 +244,19 @@ def extrusion_to_matrix(entity): az = Vector(entity.extrusion) ax, ay = arbitrary_x_axis(az) - return Matrix((ax, ay, az)).inverted() + ax4 = ax.to_4d() + ay4 = ay.to_4d() + az4 = az.to_4d() + ax4[3] = 0 + ay4[3] = 0 + az4[3] = 0 + translation = Vector((0, 0, 0, 1)) + if hasattr(entity, "elevation"): + if type(entity.elevation) is tuple: + translation = Vector(entity.elevation).to_4d() + else: + translation = (az * entity.elevation).to_4d() + return Matrix((ax4, ay4, az4, translation)).transposed() def split_by_width(entity): diff --git a/io_import_dxf/dxfimport/do.py b/io_import_dxf/dxfimport/do.py index 85efdf82..632a558e 100644 --- a/io_import_dxf/dxfimport/do.py +++ b/io_import_dxf/dxfimport/do.py @@ -22,7 +22,7 @@ import bpy import os import re from mathutils import Vector, Matrix, Euler, Color, geometry -from math import pi, radians +from math import pi, radians, sqrt import bmesh from .. import dxfgrabber @@ -39,9 +39,11 @@ except: BY_LAYER = 0 BY_DXFTYPE = 1 -SEPARATED = 2 -LINKED_OBJECTS = 3 -GROUP_INSTANCES = 4 +BY_CLOSED_NO_BULGE_POLY = 2 +SEPARATED = 3 +LINKED_OBJECTS = 4 +GROUP_INSTANCES = 5 +BY_BLOCK = 6 def transform(p1, p2, c1, c2, c3): @@ -93,7 +95,7 @@ class Do: "dwg", "combination", "known_blocks", "import_text", "import_light", "export_acis", "merge_lines", "do_bounding_boxes", "acis_files", "errors", "block_representation", "recenter", "did_group_instance", "objects_before", "pDXF", "pScene", "thickness_and_width", "but_group_by_att", "current_scene", - "dxf_unit_scale", + "dxf_unit_scale" ) def __init__(self, dxf_filename, c=BY_LAYER, import_text=True, import_light=True, export_acis=True, @@ -610,7 +612,7 @@ class Do: extrusion describes the normal vector of the entity """ if entity.dxftype not in {"LINE", "POINT"}: - if Vector(entity.extrusion) != Vector((0, 0, 1)): + if is_.extrusion(entity): transformation = convert.extrusion_to_matrix(entity) obj.location = transformation * obj.location obj.rotation_euler.rotate(transformation) @@ -1192,6 +1194,27 @@ class Do: subd.levels = entity.subdivision_levels subd.show_expanded = False + def polys_to_mesh(self, entities, scene, name): + d = bpy.data.meshes.new(name) + bm = bmesh.new() + m = Matrix(((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))) + for en in entities: + t = m + verts = [] + if is_.extrusion(en): + t = convert.extrusion_to_matrix(en) + for p in en.points: + verts.append(bm.verts.new(self.proj((t*Vector(p)).to_3d()))) + if len(verts) > 2: + bm.faces.new(verts) + elif len(verts) == 2: + bm.edges.new(verts) + + bm.to_mesh(d) + o = bpy.data.objects.new(name, d) + scene.objects.link(o) + return o + def object_mesh(self, entities, scene, name): """ entities: list of DXF entities @@ -1319,7 +1342,10 @@ class Do: entity = entities # call merged geometry methods - if is_.mesh(TYPE): + + if TYPE is True: # TYPE == True == is_.closed_poly_no_bulge for all entities + o = self.polys_to_mesh(entities, scene, name) + elif is_.mesh(TYPE): o = self.object_mesh(entities, scene, name) elif is_.curve(TYPE): o = self.object_curve(entities, scene, name) @@ -1373,6 +1399,71 @@ class Do: else: scene[name + "_recenter"] = center + def _dupliface(self, blockname, inserts, blgroup, scene): + """ + go through all inserts and check if there is one having no rotation or extrusion (if there is none + then there is none... any other measure to hide the original is also not water proof; just try the first + and most obvious way and if it doesn't work, we will not try to cover uncoverable cases) + - Place the duplicator object with a first face according to the chosen start insert. + - Add the block as a child object to the duplicator object. If there are any recursive inserts in the + insert, keep appending children to the children. + - Any subsequent inserts in the list are represented just by a face in the duplicator object. + """ + aunits = self.dwg.header.get('$AUNITS', 0) + f = 20 + base = [ + Vector(( sqrt(((1/f)**2))/2, sqrt(((1/f)**2))/2,0)), + Vector(( sqrt(((1/f)**2))/2,-sqrt(((1/f)**2))/2,0)), + Vector((-sqrt(((1/f)**2))/2,-sqrt(((1/f)**2))/2,0)), + Vector((-sqrt(((1/f)**2))/2, sqrt(((1/f)**2))/2,0)), + ] + + bm = bmesh.new() + location = None + for entity in inserts: + extrusion = convert.extrusion_to_matrix(entity) + scale = Matrix(((entity.scale[0],0,0,0),(0,entity.scale[1],0,0),(0,0,entity.scale[2],0),(0,0,0,1))) + rotation = radians(entity.rotation) if aunits == 0 else entity.rotation + rotm = scale * extrusion * extrusion.Rotation(rotation, 4, "Z") + if location is None: + location = rotm * Vector(entity.insert) + entity.insert = (0, 0, 0) + transformation = rotm + else: + transformation = rotm.Translation((extrusion * Vector(entity.insert))-location) * rotm + verts = [] + for v in base: + verts.append(bm.verts.new(transformation * v)) + bm.faces.new(verts) + + + + m = bpy.data.meshes.new(blockname+"_geometry") + bm.to_mesh(m) + o = bpy.data.objects.new(blockname, m) + o.location = location + scene.objects.link(o) + + self._nest_block(o, blockname, blgroup, scene) + o.dupli_type = "FACES" + o.use_dupli_faces_scale = True + o.dupli_faces_scale = f + + def _nest_block(self, parent, name, blgroup, scene): + b = self.dwg.blocks[name] + e = bpy.data.objects.new(name, None) + scene.objects.link(e) + #e.location = parent.location + e.parent = parent + for TYPE, grouped in groupsort.by_dxftype(b): + if TYPE == "INSERT": + for en in grouped: + self._nest_block(e, en.name, blgroup, scene) + else: + o = self._call_object_types(TYPE, grouped, blgroup, name+"_"+TYPE, scene) + #o.location = e.location + o.parent = e + def combined_objects(self, entities, scene, override_name=None, override_group=None): """ entities: list of dxf entities @@ -1392,13 +1483,15 @@ class Do: # sort if self.combination == BY_LAYER: group_sorted = groupsort.by_blender_type(layer_ents) - elif self.combination == BY_DXFTYPE: + elif self.combination == BY_DXFTYPE or self.combination == BY_BLOCK: group_sorted = groupsort.by_dxftype(layer_ents) + elif self.combination == BY_CLOSED_NO_BULGE_POLY: + group_sorted = groupsort.by_closed_poly_no_bulge(layer_ents) else: break for TYPE, grouped_entities in group_sorted: - if self.but_group_by_att: + if self.but_group_by_att and self.combination != BY_CLOSED_NO_BULGE_POLY and self.combination != BY_BLOCK: for atts, by_att in groupsort.by_attributes(grouped_entities): thickness, subd, width, extrusion = atts att = "" @@ -1416,10 +1509,40 @@ class Do: if o is not None: objects.append(o) else: - name = layer_name + "_" + TYPE.replace("object_", "") - o = self._call_object_types(TYPE, grouped_entities, group, name, scene, False) - if o is not None: - objects.append(o) + if type(TYPE) is bool and not TYPE: + for ttype, sub_entities in groupsort.by_blender_type(grouped_entities): + name = layer_name + "_" + ttype.replace("object_", "") + o = self._call_object_types(ttype, sub_entities, group, name, scene, False) + if o is not None: + objects.append(o) + else: + if TYPE == "INSERT" and self.combination == BY_BLOCK: + for NAME, grouped_inserts in groupsort.by_insert_block_name(grouped_entities): + sorted_inserts = [] + separates = [] + for i in grouped_inserts: + sames = 1 + for c in range(2): + if i.scale[c+1] - i.scale[0] < 0.00001: + sames += 1 + if not (sames == 3 or (sames == 2 and i.scale[2] == 1)): + print(i.scale) + separates.append(i) + else: + if i.extrusion == (0, 0, 1) and i.rotation == 0.0 and i.scale == (1, 1, 1): + sorted_inserts.insert(0, i) + else: + sorted_inserts.append(i) + + if len(sorted_inserts) > 0: + self._dupliface(NAME, sorted_inserts, group, scene) + for s in separates: + self.insert(s, scene, NAME, group) + else: + name = layer_name + "_" + TYPE.replace("object_", "") if type(TYPE) is str else "MERGED_POLYS" + o = self._call_object_types(TYPE, grouped_entities, group, name, scene, False) + if o is not None: + objects.append(o) return objects def separated_entities(self, entities, scene, override_name=None, override_group=None): @@ -1475,7 +1598,9 @@ class Do: if self.recenter: self.objects_before += scene.objects[:] - if self.combination != SEPARATED: + if self.combination == BY_BLOCK: + self.combined_objects((en for en in self.dwg.modelspace()), scene) + elif self.combination != SEPARATED: self.combined_objects((en for en in self.dwg.modelspace() if is_.combined_entity(en)), scene) self.separated_entities((en for en in self.dwg.modelspace() if is_.separated_entity(en)), scene) else: diff --git a/io_import_dxf/dxfimport/groupsort.py b/io_import_dxf/dxfimport/groupsort.py index 9722d05e..e4af8c84 100644 --- a/io_import_dxf/dxfimport/groupsort.py +++ b/io_import_dxf/dxfimport/groupsort.py @@ -53,6 +53,13 @@ def by_layer(entities): keyf = lambda e: e.layer return itertools.groupby(sorted(entities, key=keyf), key=keyf) +def by_closed_poly_no_bulge(entities): + """ + entities: list of DXF entities + """ + keyf = lambda e: is_.closed_poly_no_bulge(e) + return itertools.groupby(sorted(entities, key=keyf), key=keyf) + def by_dxftype(entities): """ @@ -81,3 +88,10 @@ def by_attributes(entities): return entity.thickness, subd, width, extrusion return itertools.groupby(sorted(entities, key=attributes), key=attributes) + +def by_insert_block_name(inserts): + """ + entities: list of DXF inserts + """ + keyf = lambda e: e.name + return itertools.groupby(sorted(inserts, key=keyf), key=keyf) diff --git a/io_import_dxf/dxfimport/is_.py b/io_import_dxf/dxfimport/is_.py index 38928aa0..20d6fe43 100644 --- a/io_import_dxf/dxfimport/is_.py +++ b/io_import_dxf/dxfimport/is_.py @@ -18,6 +18,9 @@ # <pep8 compliant> +from mathutils import Vector + + _MESH_ENTITIES = frozenset(["POLYFACE", "POLYMESH", "MESH", "POINT", "3DFACE", "SOLID", "TRACE"]) @@ -28,11 +31,15 @@ def mesh_entity(entity): def mesh(typestr): return typestr in _MESH_ENTITIES +_POLYS = frozenset(["LWPOLYLINE", "POLYLINE"]) + +def closed_poly_no_bulge(entity): + return entity.dxftype in _POLYS and not any([b != 0 for b in entity.bulge]) and entity.is_closed + _CURVE_ENTITIES = frozenset(("POLYLINE", "POLYGON", "LWPOLYLINE", "SPLINE", "CIRCLE", "ARC", "ELLIPSE", "LINE", "HELIX")) - def curve_entity(entity): return entity.dxftype in _CURVE_ENTITIES @@ -127,3 +134,8 @@ def combined_entity(entity): def combined(typestr): return typestr not in _NOT_COMBINED_ENTITIES + + +def extrusion(entity): + return Vector(entity.extrusion) != Vector((0, 0, 1)) \ + or (hasattr(entity, "elevation") and entity.elevation != 0)
\ No newline at end of file |