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.

This commit is contained in:
Lukas Treyer 2016-03-22 10:18:32 +01:00
parent cb2f133712
commit f4878ea31b
5 changed files with 209 additions and 26 deletions

View File

@ -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:

View File

@ -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):

View File

@ -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:

View File

@ -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)

View File

@ -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)