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:
parent
cb2f133712
commit
f4878ea31b
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue