Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'io_import_dxf/dxfimport/do.py')
-rw-r--r--io_import_dxf/dxfimport/do.py1498
1 files changed, 1498 insertions, 0 deletions
diff --git a/io_import_dxf/dxfimport/do.py b/io_import_dxf/dxfimport/do.py
new file mode 100644
index 00000000..0e39c5a9
--- /dev/null
+++ b/io_import_dxf/dxfimport/do.py
@@ -0,0 +1,1498 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+import bpy
+import os
+import re
+from mathutils import Vector, Matrix, Euler, Color, geometry
+from math import pi, radians
+
+import bmesh
+from .. import dxfgrabber
+from . import convert, is_, groupsort
+from .line_merger import line_merger
+from ..transverse_mercator import TransverseMercator
+
+
+try:
+ from pyproj import Proj, transform as proj_transform
+ PYPROJ = True
+except:
+ PYPROJ = False
+
+BY_LAYER = 0
+BY_DXFTYPE = 1
+SEPARATED = 2
+LINKED_OBJECTS = 3
+GROUP_INSTANCES = 4
+
+
+def transform(p1, p2, c1, c2, c3):
+ if PYPROJ:
+ if type(p1) is Proj and type(p2) is Proj:
+ if p1.srs != p2.srs:
+ return proj_transform(p1, p2, c1, c2, c3)
+ else:
+ return (c1, c2, c3)
+ elif type(p2) is TransverseMercator:
+ wgs84 = Proj(init="EPSG:4326")
+ if p1.srs != wgs84.srs:
+ t2, t1, t3 = proj_transform(p1, wgs84, c1, c2, c3)
+ else:
+ t1, t2, t3 = c2, c1, c3 # mind c2, c1 inversion
+ tm1, tm2 = p2.fromGeographic(t1, t2)
+ return (tm1, tm2, t3)
+ else:
+ if p1.spherical:
+ t1, t2 = p2.fromGeographic(c2, c1) # mind c2, c1 inversion
+ return (t1, t2, c3)
+ else:
+ return (c1, c2, c3)
+
+
+def float_len(f):
+ s = str(f)
+ if 'e' in s:
+ return int(s[3:5])
+ else:
+ return len(s[2:])
+
+
+class Indicator:
+ spherical = False
+ euclidean = False
+
+ def __init__(self, i):
+ if i == "spherical":
+ self.spherical = True
+ elif i == "euclidean":
+ self.euclidean = True
+ else:
+ raise AttributeError("Indication must be 'spherical' or 'planar'.")
+
+
+class Do:
+ __slots__ = (
+ "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",
+ )
+
+ def __init__(self, dxf_filename, c=BY_LAYER, import_text=True, import_light=True, export_acis=True,
+ merge_lines=True, do_bbox=True, block_rep=LINKED_OBJECTS, recenter=False, pDXF=None, pScene=None,
+ thicknessWidth=True, but_group_by_att=True, dxf_unit_scale=1.0):
+ self.dwg = dxfgrabber.readfile(dxf_filename, {"assure_3d_coords": True})
+ self.combination = c
+ self.known_blocks = {}
+ self.import_text = import_text
+ self.import_light = import_light
+ self.export_acis = export_acis
+ self.merge_lines = merge_lines
+ self.do_bounding_boxes = do_bbox
+ self.acis_files = []
+ self.errors = set([])
+ self.block_representation = block_rep
+ self.recenter = recenter
+ self.did_group_instance = False
+ self.objects_before = []
+ self.pDXF = pDXF
+ self.pScene = pScene
+ self.thickness_and_width = thicknessWidth
+ self.but_group_by_att = but_group_by_att
+ self.current_scene = None
+ self.dxf_unit_scale = dxf_unit_scale
+
+ def proj(self, co):
+ """
+ :param co: coordinate
+ :return: transformed coordinate if self.pScene is defined
+ """
+ if self.pScene is not None and self.pDXF is not None:
+ u = self.dxf_unit_scale
+ if len(co) == 3:
+ c1, c2, c3 = co
+ else:
+ c1, c2 = co
+ c3 = 0
+ if u != 1.0:
+ c1 *= u
+ c2 *= u
+ c3 *= u
+
+ # add
+ add = Vector((0, 0, 0))
+ if "latitude" in self.current_scene and "longitude" in self.current_scene:
+ if PYPROJ and type(self.pScene) not in (TransverseMercator, Indicator):
+ wgs84 = Proj(init="EPSG:4326")
+ cscn_lat = self.current_scene.get('latitude', 0)
+ cscn_lon = self.current_scene.get('longitude', 0)
+ cscn_alt = self.current_scene.get('altitude', 0)
+ add = Vector(transform(wgs84, self.pScene, cscn_lat, cscn_lon, cscn_alt))
+
+ # projection
+ newco = Vector(transform(self.pDXF, self.pScene, c1, c2, c3))
+ newco = newco - add
+ if any((c == float("inf") or c == float("-inf") for c in newco)):
+ self.errors.add("Projection results in +/- infinity coordinates.")
+ return newco
+ else:
+ u = self.dxf_unit_scale
+ if u != 1:
+ if len(co) == 3:
+ c1, c2, c3 = co
+ else:
+ c1, c2 = co
+ c3 = 0
+ c1 *= u
+ c2 *= u
+ c3 *= u
+ return Vector((c1, c2, c3))
+ else:
+ return Vector((co[0], co[1], co[2] if len(co) == 3 else 0))
+
+ def georeference(self, scene, center):
+ if "latitude" not in scene and "longitude" not in scene:
+ if type(self.pScene) is TransverseMercator:
+ scene['latitude'] = self.pScene.lat
+ scene['longitude'] = self.pScene.lon
+ scene['altitude'] = 0
+ elif type(self.pScene) is not None:
+ wgs84 = Proj(init="EPSG:4326")
+ latlon = transform(self.pScene, wgs84, center[0], center[1], center[2])
+ scene['latitude'] = latlon[0]
+ scene['longitude'] = latlon[1]
+ scene['altitude'] = latlon[2]
+
+ """ GEOMETRY DXF TYPES TO BLENDER CURVES FILTERS"""
+ # type(self, dxf entity, blender curve data)
+
+ def _cubic_bezier_closed(self, ptuple, curve):
+ count = (len(ptuple)-1)/3
+ points = [ptuple[-2]]
+ ptuples = ptuple[:-2]
+ points += [p for p in ptuples]
+
+ spl = curve.splines.new('BEZIER')
+ spl.use_cyclic_u = True
+ b = spl.bezier_points
+ b.add(count - 1)
+ for i, j in enumerate(range(1, len(points), 3)):
+ b[i].handle_left = self.proj(points[j - 1])
+ b[i].co = self.proj(points[j])
+ b[i].handle_right = self.proj(points[j + 1])
+
+ def _cubic_bezier_open(self, points, curve):
+ count = (len(points) - 1) / 3 + 1
+ spl = curve.splines.new('BEZIER')
+ b = spl.bezier_points
+ b.add(count - 1)
+
+ b[0].co = self.proj(points[0])
+ b[0].handle_left = self.proj(points[0])
+ b[0].handle_right = self.proj(points[1])
+
+ b[-1].co = self.proj(points[-1])
+ b[-1].handle_right = self.proj(points[-1])
+ b[-1].handle_left = self.proj(points[-2])
+
+ for i, j in enumerate(range(3, len(points) - 2, 3), 1):
+ b[i].handle_left = self.proj(points[j - 1])
+ b[i].co = self.proj(points[j])
+ b[i].handle_right = self.proj(points[j + 1])
+
+ def _cubic_bezier(self, points, curve, is_closed):
+ """
+ points: control points; list of (x,y,z) tuples
+ curve: Blender curve data of type "CURVE" (object.data) where the bezier should be added to
+ is_closed: True / False to indicate if the curve is open or closed
+ """
+ if is_closed:
+ self._cubic_bezier_closed(points, curve)
+ else:
+ self._cubic_bezier_open(points, curve)
+
+ def _poly(self, points, curve, is_closed=False):
+ """
+ points: list of (x,y,z)
+ curve: Blender curve data of type "CURVE" (object.data) to which the poly should be added to
+ is_closed: True / False to indicate if the polygon is open or closed
+ """
+ p = curve.splines.new("POLY")
+ p.use_smooth = False
+ p.use_cyclic_u = is_closed
+ p.points.add(len(points) - 1)
+
+ for i, pt in enumerate(points):
+ p.points[i].co = self.proj(pt).to_4d()
+
+ def _gen_poly(self, en, curve):
+ if any([b != 0 for b in en.bulge]):
+ self._cubic_bezier(convert.bulgepoly_to_cubic(self, en), curve, en.is_closed)
+ else:
+ self._poly(en.points, curve, en.is_closed)
+
+ def polyline(self, en, curve):
+ """
+ en: DXF entity of type `POLYLINE`
+ curve: Blender data structure of type `CURVE`
+ """
+ self._gen_poly(en, curve)
+
+ def polygon(self, en, curve):
+ """
+ en: DXF entity of type `POLYGON`
+ curve: Blender data structure of type `CURVE`
+ """
+ self._gen_poly(en, curve)
+
+ def lwpolyline(self, en, curve):
+ """
+ en: DXF entity of type `LWPOLYLINE`
+ curve: Blender data structure of type `CURVE`
+ """
+ self._gen_poly(en, curve)
+
+ def line(self, en, curve):
+ """
+ en: DXF entity of type `LINE`
+ curve: Blender data structure of type `CURVE`
+ """
+ self._poly([en.start, en.end], curve, False)
+
+ def arc(self, en, curve=None, aunits=None, angdir=None, angbase=None):
+ """
+ en: dxf entity (en.startangle, en.endangle, en.center, en.radius)
+ curve: optional; Blender curve data of type "CURVE" (object.data) to which the arc should be added to
+ return control points of a cubic spline (do be used in a spline with bulges / series of arcs)
+ note: en.startangle + en.endangle: angles measured from the angle base (angbase) in the direction of
+ angdir (1 = clockwise, 0 = counterclockwise)
+ """
+ treshold = 0.005
+
+ if aunits is None:
+ aunits = self.dwg.header.get('$AUNITS', 0)
+ if angbase is None:
+ angbase = self.dwg.header.get('$ANGBASE', 0)
+ if angdir is None:
+ angdir = self.dwg.header.get('$ANGDIR', 0)
+ kappa = 0.5522848
+
+ if aunits == 0:
+ s = radians(en.startangle+angbase)
+ e = radians(en.endangle+angbase)
+ else:
+ s = en.startangle+angbase
+ e = en.endangle+angbase
+
+ if s > e:
+ e += 2 * pi
+ angle = e - s
+
+ # curve == None means arc is called from bulge conversion
+ # nothing should be projected at this stage, since the
+ # lwpolyline (the only entity with bulges) will be projected
+ # as a whole afterwars (small little error; took ages to debug)
+ if curve is not None:
+ vc = self.proj(en.center)
+ else:
+ vc = en.center
+ x_vec = Vector((1, 0, 0))
+ radius = en.radius
+
+ # turn clockwise
+ if angdir == 0:
+ rot = Matrix.Rotation(radians(-90), 3, "Z")
+ start = x_vec * radius * Matrix.Rotation(-s, 3, "Z")
+ end = x_vec * radius * Matrix.Rotation(-e, 3, "Z")
+
+ # turn counter-clockwise
+ else:
+ rot = Matrix.Rotation(radians(90), 3, "Z")
+ start = x_vec * radius * Matrix.Rotation(s, 3, "Z")
+ end = x_vec * radius * Matrix.Rotation(e, 3, "Z")
+
+ # start
+ spline = list()
+ spline.append(vc + start)
+ if abs(angle) - pi / 2 > treshold: # if angle is more than pi/2 incl. treshold
+ spline.append(vc + start + start * kappa * rot)
+ else:
+ spline.append(vc + start + start * kappa * angle / (pi / 2) * rot)
+
+ # fill if angle is larger than 90 degrees
+ a = pi / 2
+ if abs(angle) - treshold > a:
+ fill = start
+
+ while abs(angle) - a > treshold:
+ fillnext = fill * rot
+ spline.append(vc + fillnext + fill * kappa)
+ spline.append(vc + fillnext)
+ # if this was the last fill control point
+ if abs(angle) - a - pi / 2 < treshold:
+ end_angle = (abs(angle) - a) * abs(angle) / angle
+ spline.append(vc + fillnext + fillnext * kappa * end_angle / (pi / 2) * rot)
+ else:
+ spline.append(vc + fillnext + fillnext * kappa * rot)
+ fill = fillnext
+ a += pi / 2
+
+ else:
+ end_angle = angle
+
+ # end
+ spline.append(vc + end + end * -kappa * end_angle / (pi / 2) * rot)
+ spline.append(vc + end)
+
+ if len(spline) % 3 != 1:
+ print("DXF-IMPORT: DO ARC: CHECK PLEASE: ", len(spline), spline)
+
+ if curve is not None:
+ self._cubic_bezier_open(spline, curve)
+ return spline
+ else:
+ return spline
+
+ def circle(self, en, curve, major=Vector((1, 0, 0))):
+ """
+ en: dxf entity
+ curve: Blender curve data of type "CURVE" (object.data) to which the circle should be added to
+ major: optional; if the circle is used as a base for an ellipse, major denotes the ellipse's major direction
+ """
+ c = curve.splines.new("BEZIER")
+ c.use_cyclic_u = True
+ b = c.bezier_points
+ b.add(3)
+
+ for i in range(4):
+ b[i].handle_left_type = 'AUTO'
+ b[i].handle_right_type = 'AUTO'
+
+ vc = self.proj(en.center)
+ clockwise = Matrix(((0, -1, 0), (1, 0, 0), (0, 0, 1)))
+
+ r = major
+ if len(r) == 2:
+ r = r.to_3d()
+ r = r * en.radius
+
+ try:
+ b[0].co = vc + r
+ b[1].co = vc + r * clockwise
+ b[2].co = vc + r * clockwise * clockwise
+ b[3].co = vc + r * clockwise * clockwise * clockwise
+ except:
+ print("Circle center: ", vc, "radius: ", r)
+ raise
+
+ return c
+
+ def scale_controlpoint(self, p, factor):
+ """
+ p: Blender control-point
+ factor: (Float)
+ Repositions left and right handle of a bezier control point.
+ """
+ p.handle_left = p.co + (p.handle_left - p.co) * factor
+ p.handle_right = p.co + (p.handle_right - p.co) * factor
+
+ def ellipse(self, en, curve):
+ """
+ center: (x,y,z) of circle center
+ major: the ellipse's major direction
+ ratio: ratio between major and minor axis lengths (always < 1)
+ curve: Blender curve data of type "CURVE" (object.data) to which the ellipse should be added to
+ """
+ major = Vector(en.majoraxis)
+ en.__dict__["radius"] = major.length
+ c = self.circle(en, curve, major.normalized())
+ b = c.bezier_points
+
+ if en.ratio < 1:
+ for i in range(4):
+ b[i].handle_left_type = 'ALIGNED'
+ b[i].handle_right_type = 'ALIGNED'
+
+ vc = self.proj(en.center)
+ clockwise = Matrix(((0, -1, 0), (1, 0, 0), (0, 0, 1)))
+ if len(major) == 2:
+ major = major.to_3d()
+ minor = major * en.ratio * clockwise
+
+ lh = b[1].handle_left - b[1].co
+ rh = b[1].handle_right - b[1].co
+ b[1].co = vc + minor
+ b[1].handle_left = b[1].co + lh
+ b[1].handle_right = b[1].co + rh
+ b[3].co = vc + minor * clockwise * clockwise
+ b[3].handle_left = b[3].co + rh
+ b[3].handle_right = b[3].co + lh
+
+ self.scale_controlpoint(b[0], en.ratio)
+ self.scale_controlpoint(b[2], en.ratio)
+
+ def spline(self, en, curve, _3D=True):
+ """
+ en: DXF entity of type `SPLINE`
+ curve: Blender data structure of type `CURVE`
+ """
+ if _3D:
+ curve.dimensions = "3D"
+ spline = convert.bspline_to_cubic(self, en, curve, self.errors)
+ if spline is None:
+ self.errors.add("Not able to import bspline with degree > 3")
+ else:
+ self._cubic_bezier(spline, curve, en.is_closed)
+
+ def helix(self, en, curve):
+ """
+ en: DXF entity of type `HELIX`
+ curve: Blender data structure of type `CURVE`
+ """
+ self.spline(en, curve, not en.is_planar)
+
+ """ GEOMETRY DXF TYPES TO BLENDER MESHES FILTERS"""
+ # type(self, dxf entity, blender bmesh data)
+
+ def _gen_meshface(self, points, bm):
+ """
+ points: list of (x,y,z) tuples
+ bm: bmesh to add the (face-) points
+ Used by the3dface() and solid()
+ """
+
+ def _is_on_edge(point):
+ return abs(sum((e - point).length for e in (edge1, edge2)) - (edge1 - edge2).length) < 0.01
+
+ points = list(points)
+
+ i = 0
+ while i < len(points):
+ if points.count(points[i]) > 1:
+ points.pop(i)
+ else:
+ i += 1
+
+ verts = []
+ for p in points:
+ verts.append(bm.verts.new(self.proj(p)))
+ face = bm.faces.new(verts)
+
+ if len(points) == 4:
+ for i in range(2):
+ edge1 = verts[i].co
+ edge2 = verts[i + 1].co
+ opposite1 = verts[i + 2].co
+ opposite2 = verts[(i + 3) % 4].co
+ ii = geometry.intersect_line_line(edge1, edge2, opposite1, opposite2)
+ if ii is not None:
+ if _is_on_edge(ii[0]):
+ bm.faces.remove(face)
+ iv = bm.verts.new(ii[0])
+ bm.faces.new((verts[i], iv, verts[(i + 3) % 4]))
+ bm.faces.new((verts[i + 1], iv, verts[i + 2]))
+
+ def the3dface(self, en, bm):
+ """ f: dxf entity
+ bm: Blender bmesh data to which the 3DFACE should be added to.
+ <-? bm.from_mesh(object.data) ?->
+ """
+ if en.points[-1] == en.points[-2]:
+ points = en.points[:3]
+ else:
+ points = en.points
+ self._gen_meshface(points, bm)
+
+ def solid(self, en, bm):
+ """ f: dxf entity
+ bm: Blender bmesh data to which the SOLID should be added to.
+ <-? bm.from_mesh(object.data) ?->
+ """
+ p = en.points
+ points = (p[0], p[1], p[3], p[2])
+ self._gen_meshface(points, bm)
+
+ def trace(self, en, bm):
+ self.solid(en, bm)
+
+ def point(self, en, bm):
+ """
+ en: DXF entity of type `POINT`
+ bm: Blender bmesh instance
+ """
+ bm.verts.new(en.point)
+
+ def polyface(self, en, bm):
+ """
+ pf: polyface
+ bm: Blender bmesh data to which the POLYFACE should be added to.
+ <-? bm.from_mesh(object.data) ?->
+ """
+ for v in en.vertices:
+ bm.verts.new(v.location)
+ for subface in en:
+ idx = subface.indices()
+ points = []
+ for p in idx:
+ if p not in points:
+ points.append(p)
+ if len(points) in (3, 4):
+ bm.faces.new([bm.verts[i] for i in points])
+
+ def polymesh(self, en, bm):
+ """
+ en: POLYMESH entitiy
+ bm: Blender bmesh instance
+ """
+ mc = en.mcount if not en.is_mclosed else en.mcount + 1
+ nc = en.ncount if not en.is_nclosed else en.ncount + 1
+ for i in range(1, mc):
+ i = i % en.mcount
+ i_ = (i - 1) % en.mcount
+ for j in range(1, nc):
+ j = j % en.ncount
+ j_ = (j - 1) % en.ncount
+ face = []
+ face.append(bm.verts.new(en.get_location((i_, j_))))
+ face.append(bm.verts.new(en.get_location((i, j_))))
+ face.append(bm.verts.new(en.get_location((i, j))))
+ face.append(bm.verts.new(en.get_location((i_, j))))
+
+ bm.faces.new(face)
+
+ def mesh(self, en, bm):
+ """
+ mesh: dxf entity
+ m: Blender MESH data (object.data) to which the dxf-mesh should be added
+ """
+ # verts:
+ for v in en.vertices:
+ bm.verts.new(v)
+
+ # edges:
+ if any((c < 0 for c in en.edge_crease_list)):
+ layerkey = bm.edges.layers.crease.new("SubsurfCrease")
+ for i, edge in enumerate(en.edges):
+ bme = bm.edges.new([bm.verts[edge[0]], bm.verts[edge[1]]])
+ bme[layerkey] = -en.edge_crease_list[i]
+ else:
+ for i, edge in enumerate(en.edges):
+ bm.edges.new([bm.verts[edge[0]], bm.verts[edge[1]]])
+
+ # faces:
+ for face in en.faces:
+ bm.faces.new([bm.verts[i] for i in face])
+
+ """ SEPARATE BLENDER OBJECTS FROM (CON)TEXT / STRUCTURE DXF TYPES """
+ # type(self, dxf entity, name string)
+ # returns blender object
+
+ def _extrusion(self, obj, entity):
+ """
+ extrusion describes the normal vector of the entity
+ """
+ if entity.dxftype not in {"LINE", "POINT"}:
+ if Vector(entity.extrusion) != Vector((0, 0, 1)):
+ transformation = convert.extrusion_to_matrix(entity)
+ obj.location = transformation * obj.location
+ obj.rotation_euler.rotate(transformation)
+
+ def _bbox(self, objects, scene):
+ xmin = ymin = zmin = float('+inf')
+ xmax = ymax = zmax = float('-inf')
+ scene.update()
+
+ for obj in objects:
+ om = obj.matrix_basis
+ for v in obj.bound_box:
+ p = om * Vector(v)
+ if p.x < xmin:
+ xmin = p.x
+ if p.x > xmax:
+ xmax = p.x
+ if p.y < ymin:
+ ymin = p.y
+ if p.y > ymax:
+ ymax = p.y
+ if p.z < zmin:
+ zmin = p.z
+ if p.z > zmax:
+ zmax = p.z
+
+ if xmin == float('+inf'):
+ xmin = 0
+ if ymin == float('+inf'):
+ ymin = 0
+ if zmin == float('+inf'):
+ zmin = 0
+ if xmax == float('-inf'):
+ xmax = 0
+ if ymax == float('-inf'):
+ ymax = 0
+ if zmax == float('-inf'):
+ zmax = 0
+
+ return xmin, ymin, zmin, xmax, ymax, zmax
+
+ def _object_bbox(self, objects, scene, name, do_widgets=True):
+ xmin, ymin, zmin, xmax, ymax, zmax = self._bbox(objects, scene)
+
+ # creating bbox geometry
+ bm = bmesh.new()
+ verts = []
+ # left side vertices
+ verts.append(bm.verts.new((xmin, ymin, zmin)))
+ verts.append(bm.verts.new((xmin, ymin, zmax)))
+ verts.append(bm.verts.new((xmin, ymax, zmax)))
+ verts.append(bm.verts.new((xmin, ymax, zmin)))
+ # right side vertices
+ verts.append(bm.verts.new((xmax, ymin, zmin)))
+ verts.append(bm.verts.new((xmax, ymin, zmax)))
+ verts.append(bm.verts.new((xmax, ymax, zmax)))
+ verts.append(bm.verts.new((xmax, ymax, zmin)))
+
+ # creating the widgets
+ if do_widgets:
+ for i in range(2):
+ for j in range(4):
+ point = verts[j + (i * 4)]
+ prevp = verts[((j - 1) % 4) + (i * 4)]
+ nextp = verts[((j + 1) % 4) + (i * 4)]
+ neigh = verts[(j + (i * 4) + 4) % 8]
+
+ for con in (prevp, nextp, neigh):
+ vec = Vector(con.co - point.co) / 10
+ bm.edges.new((point, bm.verts.new(point.co + vec)))
+
+ d = bpy.data.meshes.new(name + "BBOX")
+ bm.to_mesh(d)
+ o = bpy.data.objects.new(name, d)
+ return o
+
+ def _vertex_duplication(self, x, y, x_count, y_count):
+ bm = bmesh.new()
+ for i in range(x_count):
+ for j in range(y_count):
+ bm.verts.new(Vector((x * i, y * j, 0.)))
+
+ d = bpy.data.meshes.new("vertex_duplicator")
+ bm.to_mesh(d)
+ return d
+
+ def point_object(self, en, scene, name=None):
+ if name is None:
+ name = en.dxftype
+ o = bpy.data.objects.new("Point", None)
+ o.location = self.proj(en.point)
+ self._extrusion(o, en)
+ scene.objects.link(o)
+
+ group = self._get_group(en.layer)
+ group.objects.link(o)
+
+ def light(self, en, scene, name):
+ """
+ en: dxf entity
+ name: ignored; exists to make separate and merged objects methods universally callable from _call_types()
+ Creates, links and returns a new light object depending on the type and color of the dxf entity.
+ """
+ # light_type : distant = 1; point = 2; spot = 3
+ if self.import_light:
+ type_map = ["NONE", "SUN", "POINT", "SPOT"]
+ layer = self.dwg.layers[en.layer]
+ lamp = bpy.data.lamps.new(en.name, type_map[en.light_type])
+ if en.color != 256:
+ aci = en.color
+ else:
+ aci = layer.color
+ c = dxfgrabber.aci_to_true_color(aci)
+ lamp.color = Color(c.rgb())
+ if en.light_type == 3:
+ lamp.spot_size = en.hotspot_angle
+ o = bpy.data.objects.new(en.name, lamp)
+ o.location = self.proj(en.position)
+ dir = self.proj(en.target) - self.proj(en.position)
+ o.rotation_quaternion = dir.rotation_difference(Vector((0, 0, -1)))
+ scene.objects.link(o)
+ return o
+
+ def mtext(self, en, scene, name):
+ """
+ en: dxf entity
+ name: ignored; exists to make separate and merged objects methods universally callable from _call_types()
+ Returns a new multi-line text object.
+ """
+ if self.import_text:
+ text = en.plain_text()
+ name = text[:8]
+ d = bpy.data.curves.new(name, "FONT")
+ o = bpy.data.objects.new(name, d)
+ d.body = text
+ d.size = en.height
+ if en.rect_width is not None:
+ if en.rect_width > 50:
+ width = 50
+ ratio = 50 / en.rect_width
+ d.size = en.height * ratio * 1.4 # XXX HACK
+ scale = (1 / ratio, 1 / ratio, 1 / ratio)
+ d.space_line = en.linespacing
+ else:
+ width = en.rect_width
+ scale = (1, 1, 1)
+ d.text_boxes[0].width = width
+ o.scale = scale
+
+ # HACK
+ d.space_line *= 1.5
+
+ o.rotation_euler = Vector((1, 0, 0)).rotation_difference(en.xdirection).to_euler()
+ o.location = en.insert
+ return o
+
+ def text(self, en, scene, name):
+ """
+ en: dxf entity
+ name: ignored; exists to make separate and merged objects methods universally callable from _call_types()
+ Returns a new single line text object.
+ """
+ if self.import_text:
+ name = en.text[:8]
+ d = bpy.data.curves.new(name, "FONT")
+ d.body = en.plain_text()
+ d.size = en.height
+ o = bpy.data.objects.new(name, d)
+ o.rotation_euler = Euler((0, 0, radians(en.rotation)), 'XYZ')
+ basepoint = self.proj(en.basepoint) if hasattr(en, "basepoint") else self.proj((0, 0, 0))
+ o.location = self.proj((en.insert)) + basepoint
+ if hasattr(en, "thickness"):
+ et = en.thickness / 2
+ d.extrude = abs(et)
+ if et > 0:
+ o.location.z += et
+ elif et < 0:
+ o.location.z -= et
+ return o
+
+ def block_linked_object(self, entity, scene, name=None, override_group=None, invisible=None, recursion_level=0):
+ """
+ entity: DXF entity of type `BLOCK`
+ name: to be consistent with all functions that get mapped to a DXF type; is set when called from insert()
+ override_group: is set when called from insert() (bpy_types.Group)
+ invisible: boolean to control the visibility of the returned object
+ Returns an object. All blocks with the same name share the same geometry (Linked Blender Objects). If the
+ entities in a block have to mapped to different Blender-types (like curve, mesh, surface, text, light) a list of
+ sub-objects is being created. `INSERT`s inside a block are being added to the same list. If the list has more
+ than one element they are being parented to a newly created Blender-Empty which is return instead of the
+ single object in the sub-objects-list that is being returned only if it is the only object in the list.
+ """
+ def _recursive_copy_inserts(parent, known_inserts, inserts, group, invisible):
+ for ki in known_inserts:
+ new_insert = ki.copy()
+ _recursive_copy_inserts(new_insert, ki.children, None, group, invisible)
+ if new_insert.name not in group.objects:
+ group.objects.link(new_insert)
+ if invisible is not None:
+ new_insert.hide = bool(invisible)
+ if inserts is not None:
+ inserts.append(new_insert)
+ new_insert.parent = parent
+ scene.objects.link(new_insert)
+
+ if name is None:
+ name = entity.name
+ # get group
+ if override_group is not None:
+ group = override_group
+ else:
+ group = self._get_group(entity.layer)
+
+ # get object(s)
+ objects = []
+ inserts = []
+ if name not in self.known_blocks.keys():
+ block_inserts = [en for en in entity if is_.insert(en.dxftype)]
+ bc = (en for en in entity if is_.combined_entity(en))
+ bs = (en for en in entity if is_.separated_entity(en) and not is_.insert(en.dxftype))
+
+ if self.combination != SEPARATED:
+ objects += self.combined_objects(bc, scene, "BL|" + name, group)
+ else:
+ bs = (en for en in entity if (is_.combined_entity(en) or is_.separated_entity(en)) and
+ not is_.insert(en.dxftype))
+ objects += self.separated_entities(bs, scene, "BL|" + name, group)
+
+ # create inserts - RECURSION
+ insert_bounding_boxes = []
+ for INSERT in block_inserts:
+ insert = self.insert(INSERT, scene, None, group, invisible, recursion_level + 1)
+ if len(insert.children) > 0:
+ i_copy = bpy.data.objects.new(insert.name, None)
+ i_copy.matrix_basis = insert.matrix_basis
+ scene.objects.link(i_copy)
+ group.objects.link(i_copy)
+ kids = insert.children[:]
+ for child in kids:
+ child.parent = i_copy
+ inserts.append(i_copy)
+ insert_bounding_boxes.append(insert)
+ else:
+ inserts.append(insert)
+
+ # determining the main object o
+ if len(objects) > 1 or len(insert_bounding_boxes) > 0:
+ if self.do_bounding_boxes:
+ o = self._object_bbox(objects + insert_bounding_boxes, scene, name, recursion_level == 0)
+ scene.objects.link(o)
+ else:
+ o = bpy.data.objects.new(name, None)
+ scene.objects.link(o)
+ if len(objects) > 0:
+ for obj in objects:
+ obj.parent = o
+
+ elif len(objects) == 1:
+ o = objects.pop(0)
+ else:
+ # strange case but possible according to the testfiles
+ o = bpy.data.objects.new(name, None)
+ scene.objects.link(o)
+
+ # unlink bounding boxes of inserts
+ for ib in insert_bounding_boxes:
+ if ib.name in group.objects:
+ group.objects.unlink(ib)
+ scene.objects.unlink(ib)
+
+ # parent inserts to this block before any transformation on the block is being applied
+ for obj in inserts:
+ obj.parent = o
+
+ # put a copy of the retreived objects into the known_blocks dict, so that the attributes being added to
+ # the object from this point onwards (from INSERT attributes) are not being copied to new/other INSERTs
+ self.known_blocks[name] = [[o.copy() for o in objects], inserts]
+
+ # so that it gets assigned to the group and inherits visibility too
+ objects.append(o)
+
+ self.known_blocks[name].append(o.copy())
+ else:
+ known_objects, known_inserts, known_o = self.known_blocks[name]
+
+ for known_object in known_objects:
+ oc = known_object.copy()
+ scene.objects.link(oc)
+ objects.append(oc)
+
+ o = known_o.copy()
+ scene.objects.link(o)
+
+ _recursive_copy_inserts(o, known_inserts, inserts, group, invisible)
+
+ # parent objects to o
+ for obj in objects:
+ obj.parent = o
+
+ objects.append(o)
+
+ # link group
+ for obj in objects:
+ if obj.name not in group.objects:
+ group.objects.link(obj)
+
+ # visibility
+ if invisible is not None:
+ for obj in objects:
+ obj.hide = bool(invisible)
+
+ # block transformations
+ o.location = self.proj(entity.basepoint)
+
+ return o
+
+ def block_group_instances(self, entity, scene, name=None, override_group=None, invisible=None, recursion_level=0):
+ self.did_group_instance = True
+
+ if name is None:
+ name = entity.name
+ # get group
+ if override_group is not None:
+ group = override_group
+ else:
+ group = self._get_group(entity.layer)
+
+ block_group = self._get_group(entity.name+"_BLOCK")
+
+ if "Blocks" not in bpy.data.scenes:
+ block_scene = bpy.data.scenes.new("Blocks")
+ else:
+ block_scene = bpy.data.scenes["Blocks"]
+
+ # create the block
+ if len(block_group.objects) == 0 or name not in self.known_blocks.keys():
+ bpy.context.screen.scene = block_scene
+ block_inserts = [en for en in entity if is_.insert(en.dxftype)]
+ bc = (en for en in entity if is_.combined_entity(en))
+ bs = (en for en in entity if is_.separated_entity(en) and not is_.insert(en.dxftype))
+
+ objects = []
+ if self.combination != SEPARATED:
+ objects += self.combined_objects(bc, block_scene, "BL|" + name, block_group)
+ else:
+ bs = (en for en in entity if (is_.combined_entity(en) or is_.separated_entity(en)) and
+ not is_.insert(en.dxftype))
+ objects += self.separated_entities(bs, block_scene, "BL|" + name, block_group)
+
+ # create inserts - RECURSION
+ inserts = []
+ for INSERT in block_inserts:
+ i = self.insert(INSERT, block_scene, None, block_group, invisible, recursion_level + 1, True)
+ inserts.append(i)
+
+ bbox = self._object_bbox(objects + inserts, block_scene, name, True)
+
+ for i in inserts:
+ sub_group = i.dupli_group
+ block_scene.objects.unlink(i)
+ block_group.objects.unlink(i)
+ i_empty = bpy.data.objects.new(i.name, None)
+ i_empty.matrix_basis = i.matrix_basis
+ i_empty.dupli_type = "GROUP"
+ i_empty.dupli_group = sub_group
+ block_group.objects.link(i_empty)
+ block_scene.objects.link(i_empty)
+
+ self.known_blocks[name] = [objects, inserts, bbox]
+ else:
+ bbox = self.known_blocks[name][2]
+
+ bpy.context.screen.scene = scene
+ o = bbox.copy()
+ # o.empty_draw_size = 0.3
+ o.dupli_type = "GROUP"
+ o.dupli_group = block_group
+ group.objects.link(o)
+ if invisible is not None:
+ o.hide = invisible
+ o.location = self.proj(entity.basepoint)
+ scene.objects.link(o)
+ # block_scene.update()
+
+ return o
+
+ def insert(self, entity, scene, name, group=None, invisible=None, recursion_level=0, need_group_inst=None):
+ """
+ entity: DXF entity
+ name: String; not used but required to be consistent with the methods being called from _call_type()
+ group: Blender group of type (bpy_types.group) being set if called from block()
+ invisible: boolean to control visibility; being set if called from block()
+ """
+ aunits = self.dwg.header.get('$AUNITS', 0)
+
+ # check if group instances are needed
+ kids = sum(1 for i in self.dwg.blocks[entity.name] if i.dxftype == "INSERT")
+ sep = sum(1 for sep in self.dwg.blocks[entity.name] if is_.separated_entity(sep))
+ objtypes = sum(1 for ot, ens in groupsort.by_blender_type(en for en in self.dwg.blocks[entity.name]
+ if is_.combined_entity(en))
+ if ot in {"object_mesh", "object_curve"})
+ if need_group_inst is None:
+ need_group_inst = (entity.row_count or entity.col_count) > 1 and \
+ (kids > 0 or objtypes > 1 or sep > 1 or (objtypes > 0 and sep > 0))
+
+ if group is None:
+ group = self._get_group(entity.layer)
+
+ if self.block_representation == GROUP_INSTANCES or need_group_inst:
+ o = self.block_group_instances(self.dwg.blocks[entity.name], scene, entity.name, group,
+ entity.invisible, recursion_level)
+ else:
+ o = self.block_linked_object(self.dwg.blocks[entity.name], scene, entity.name, group,
+ entity.invisible, recursion_level)
+
+ # column & row
+ if (entity.row_count or entity.col_count) > 1:
+ if len(o.children) == 0 and self.block_representation == LINKED_OBJECTS and not need_group_inst:
+ arr_row = o.modifiers.new("ROW", "ARRAY")
+ arr_col = o.modifiers.new("COL", "ARRAY")
+ arr_row.show_expanded = False
+ arr_row.count = entity.row_count
+ arr_row.relative_offset_displace = (0, entity.row_spacing, 0)
+ arr_col.show_expanded = False
+ arr_col.count = entity.col_count
+ arr_col.relative_offset_displace = (entity.col_spacing / 2, 0, 0)
+
+ else:
+ instance = o
+ x = (Vector(o.bound_box[4]) - Vector(o.bound_box[0])).length
+ y = (Vector(o.bound_box[3]) - Vector(o.bound_box[0])).length
+ dm = self._vertex_duplication(x * entity.col_spacing / 2, y * entity.row_spacing,
+ entity.col_count, entity.row_count)
+ o = bpy.data.objects.new(entity.name, dm)
+ instance.parent = o
+ o.dupli_type = "VERTS"
+
+ # insert transformations
+ rot = radians(entity.rotation) if aunits == 0 else entity.rotation
+ o.location += self.proj(entity.insert)
+ o.rotation_euler = Euler((0, 0, rot))
+ o.scale = entity.scale
+
+ # mirror (extrusion value of an INSERT ENTITY)
+ self._extrusion(o, entity)
+
+ # visibility
+ if invisible is None:
+ o.hide = bool(entity.invisible)
+ else:
+ o.hide = bool(invisible)
+
+ # attributes
+ if self.import_text:
+ if entity.attribsfollow:
+ for a in entity.attribs:
+ # Blender custom property
+ o[a.tag] = a.text
+ attname = entity.name + "_" + a.tag
+ scene.objects.link(self.text(a, scene, attname))
+
+ return o
+
+ """ COMBINED BLENDER OBJECT FROM GEOMETRY DXF TYPES """
+ # type(self, dxf entities, object name string)
+ # returns blender object
+
+ def _check3D_object(self, curve):
+ """
+ Checks if a curve object has coordinates that are elevated from the z-plane.
+ If so the curve.dimensions are set to 3D.
+ """
+ if any((p.co.z != 0 for spline in curve.splines for p in spline.points)) or \
+ any((p.co.z != 0 for spline in curve.splines for p in spline.bezier_points)):
+ curve.dimensions = '3D'
+
+ def _merge_lines(self, lines, curve):
+ """
+ lines: list of LINE entities
+ curve: Blender curve data
+ merges a list of LINE entities to a polygon-point-list and adds it to the Blender curve
+ """
+ polylines = line_merger(lines)
+ for polyline in polylines:
+ self._poly(polyline, curve, polyline[0] == polyline[-1])
+
+ def _thickness(self, bm, thickness):
+ """
+ Used for mesh types
+ """
+ if not self.thickness_and_width:
+ return
+ original_faces = [face for face in bm.faces]
+ bm.normal_update()
+ for face in original_faces:
+ normal = face.normal.copy()
+ if normal.z < 0:
+ normal *= -1
+ ret = bmesh.ops.extrude_face_region(bm, geom=[face])
+ new_geom = ret["geom"]
+ verts = (g for g in new_geom if type(g) == bmesh.types.BMVert)
+ for v in verts:
+ v.co += normal * thickness
+ del ret
+ del original_faces
+
+ def _thickness_and_width(self, obj, entity, scene):
+ """
+ Used for curve types
+ """
+ if not self.thickness_and_width:
+ return
+ has_varying_width = is_.varying_width(entity)
+ th = entity.thickness
+ w = entity.width[0][0] if hasattr(entity, "width") else 0
+
+ if w == 0 and not has_varying_width:
+ if th != 0:
+ obj.data.extrude = abs(th / 2)
+ if th > 0:
+ obj.location.z += th / 2
+ else:
+ obj.location.z -= th / 2
+ obj.data.dimensions = "3D"
+ obj.data.twist_mode = "Z_UP"
+
+ else:
+ # CURVE BEVEL
+ ew = entity.width
+ max_w = max((w for w_pair in ew for w in w_pair))
+
+ bevd = bpy.data.curves.new("BEVEL", "CURVE")
+ bevdp = bevd.splines.new("POLY")
+ bevdp.points.add(1)
+ bevdp.points[0].co = Vector((-max_w / 2, 0, 0, 0))
+ bevdp.points[1].co = Vector((max_w / 2, 0, 0, 0))
+
+ bevel = bpy.data.objects.new("BEVEL", bevd)
+ obj.data.bevel_object = bevel
+ scene.objects.link(bevel)
+
+ # CURVE TAPER
+ if has_varying_width and len(ew) == 1:
+ tapd = bpy.data.curves.new("TAPER", "CURVE")
+ tapdp = tapd.splines.new("POLY")
+ # lenlist = convert.bulgepoly_to_lenlist(entity)
+ # amount = len(ew) if entity.is_closed else len(ew) - 1
+
+ tapdp.points[0].co = Vector((0, ew[0][0] / max_w, 0, 0))
+ tapdp.points.add(1)
+ tapdp.points[1].co = Vector((1, ew[0][1] / max_w, 0, 0))
+
+ # for i in range(1, amount):
+ # start_w = ew[i][0]
+ # end_w = ew[i][1]
+ # tapdp.points.add(2)
+ # tapdp.points[-2].co = Vector((sum(lenlist[:i]), start_w / max_w, 0, 0))
+ # tapdp.points[-1].co = Vector((sum(lenlist[:i + 1]), end_w / max_w, 0, 0))
+
+ taper = bpy.data.objects.new("TAPER", tapd)
+ obj.data.taper_object = taper
+ scene.objects.link(taper)
+
+ # THICKNESS FOR CURVES HAVING A WIDTH
+ if th != 0:
+ solidify = obj.modifiers.new("THICKNESS", "SOLIDIFY")
+ solidify.thickness = th
+ solidify.use_even_offset = True
+ solidify.offset = 1
+ solidify.show_expanded = False
+
+ # make the shading look good
+ esp = obj.modifiers.new("EdgeSplit", "EDGE_SPLIT")
+ esp.show_expanded = False
+
+ def _subdivision(self, obj, entity):
+ if entity.subdivision_levels > 0:
+ subd = obj.modifiers.new("SubD", "SUBSURF")
+ subd.levels = entity.subdivision_levels
+ subd.show_expanded = False
+
+ def object_mesh(self, entities, scene, name):
+ """
+ entities: list of DXF entities
+ name: name of the returned Blender object (String)
+ Accumulates all entities into a Blender bmesh and returns a Blender object containing it.
+ """
+ d = bpy.data.meshes.new(name)
+ bm = bmesh.new()
+
+ i = 0
+ for en in entities:
+ i += 1
+ if en.dxftype == "3DFACE":
+ self.the3dface(en, bm)
+ else:
+ dxftype = getattr(self, en.dxftype.lower(), None)
+ if dxftype is not None:
+ dxftype(en, bm)
+ else:
+ self.errors.add(en.dxftype.lower() + " - unknown dxftype")
+ if i > 0:
+ if hasattr(en, "thickness"):
+ if en.thickness != 0:
+ self._thickness(bm, en.thickness)
+ bm.to_mesh(d)
+ o = bpy.data.objects.new(name, d)
+ # for POLYFACE
+ if hasattr(en, "extrusion"):
+ self._extrusion(o, en)
+ if hasattr(en, "subdivision_levels"):
+ self._subdivision(o, en)
+ return o
+ return None
+
+ def object_curve(self, entities, scene, name):
+ """
+ entities: list of DXF entities
+ name: name of the returned Blender object (String)
+ Accumulates all entities in the list into a Blender curve and returns a Blender object containing it.
+ """
+ d = bpy.data.curves.new(name, "CURVE")
+
+ i = 0
+ lines = []
+ for en in entities:
+ i += 1
+ TYPE = en.dxftype
+ if TYPE == "LINE" and self.merge_lines:
+ lines.append(en)
+ continue
+ typefunc = getattr(self, TYPE.lower(), None)
+ if typefunc is not None:
+ typefunc(en, d)
+ else:
+ self.errors.add(en.dxftype.lower() + " - unknown dxftype")
+
+ if len(lines) > 0:
+ self._merge_lines(lines, d)
+
+ if i > 0:
+ self._check3D_object(d)
+ o = bpy.data.objects.new(name, d)
+ self._thickness_and_width(o, en, scene)
+ self._extrusion(o, en)
+ return o
+
+ return None
+
+ def object_surface(self, entities, scene, name):
+ """
+ entities: list of DXF entities
+ name: name of the returned Blender object (String) (for future use and also to make it callable from
+ _call_types()
+ Returns None. Exports all NURB entities to ACIS files if the GUI option for it is set.
+ """
+ def _get_acis_filename(name, ending):
+ df = self.dwg.filename
+ dir = os.path.dirname(df)
+ filename = bpy.path.display_name(df)
+ return os.path.join(dir, "{}_{}.{}".format(filename, name, ending))
+
+ if self.export_acis:
+ for en in entities:
+ if name in self.acis_files:
+ name = name + "." + str(len([n for n in self.acis_files if name in n])).zfill(3)
+ # store SAB files
+ if self.dwg.header.get("$ACADVER", "AC1024") > "AC1024":
+ filename = _get_acis_filename(name, "sab")
+ self.acis_files.append(name)
+ with open(filename, 'wb') as f:
+ f.write(en.acis)
+ # store SAT files
+ else:
+ filename = _get_acis_filename(name, "sat")
+ self.acis_files.append(name)
+ with open(filename, 'w') as f:
+ f.write('\n'.join(en.acis))
+ return None
+
+ """ ITERATE OVER DXF ENTITIES AND CREATE BLENDER OBJECTS """
+
+ def _get_group(self, name):
+ """
+ name: name of group (String)
+ Finds group by name or creates it if it does not exist.
+ """
+ groups = bpy.data.groups
+ if name in groups.keys():
+ group = groups[name]
+ else:
+ group = bpy.data.groups.new(name)
+ return group
+
+ def _call_object_types(self, TYPE, entities, group, name, scene, separated=False):
+ """
+ TYPE: DXF type
+ entities: list of DXF entities
+ group: Blender group (type: bpy_types.Group)
+ name: name of the object that is being created and returned (String)
+ separated: flag to make _call_types uniformly available for combined_objects() and separated_objects()
+ """
+ if separated:
+ entity = entities[0]
+ else:
+ entity = entities
+
+ # call merged geometry methods
+ if is_.mesh(TYPE):
+ o = self.object_mesh(entities, scene, name)
+ elif is_.curve(TYPE):
+ o = self.object_curve(entities, scene, name)
+ elif is_.nurbs(TYPE):
+ o = self.object_surface(entities, scene, name)
+
+ # call separate object methods (or merged geometry if TYPE depending on type)
+ else:
+ try:
+ type_func = getattr(self, TYPE.lower(), None)
+ o = type_func(entity, scene, name)
+ except (AttributeError, TypeError):
+ # don't call self.light(en), self.mtext(o, en), self.text(o, en) with a list of entities
+ if is_.separated(TYPE) and not separated:
+ self.errors.add("DXF-Import: multiple %ss cannot be merged into a Blender object." % TYPE)
+ elif TYPE == "not_mergeable":
+ self.errors.add("DXF-Import: Not mergeable dxf type '%s' should not be called in merge-mode" % TYPE)
+ else:
+ self.errors.add("DXF-import: Unsupported dxftype: %s" % TYPE)
+ raise
+
+ if type(o) == bpy.types.Object:
+ if o.name not in scene.objects:
+ scene.objects.link(o)
+
+ if o.name not in group.objects:
+ group.objects.link(o)
+ return o
+
+ def _recenter(self, scene, name):
+ bpy.context.screen.scene = scene
+ scene.update()
+ bpy.ops.object.select_all(action='DESELECT')
+
+ recenter_objects = (o for o in scene.objects if "BEVEL" not in o.name and "TAPER" not in o.name
+ and o not in self.objects_before)
+ xmin, ymin, zmin, xmax, ymax, zmax = self._bbox(recenter_objects, scene)
+ vmin = Vector((xmin, ymin, zmin))
+ vmax = Vector((xmax, ymax, zmax))
+ center = vmin + (vmax - vmin) / 2
+ for o in (o for o in scene.objects if "BEVEL" not in o.name and "TAPER" not in o.name
+ and o not in self.objects_before and o.parent is None):
+ o.location = o.location - center
+ o.select = True
+
+ if not self.did_group_instance:
+ bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
+
+ if self.pDXF is not None:
+ self.georeference(scene, center)
+ else:
+ scene[name + "_recenter"] = center
+
+ def combined_objects(self, entities, scene, override_name=None, override_group=None):
+ """
+ entities: list of dxf entities
+ override_group & override_name: for use within insert() and block()
+ Adds multiple dxf entities to one Blender object (per blender or dxf type).
+ """
+ objects = []
+ for layer_name, layer_ents in groupsort.by_layer(entities):
+ # group and name
+ if override_group is None:
+ group = self._get_group(layer_name)
+ else:
+ group = override_group
+ if override_name is not None:
+ layer_name = override_name
+
+ # sort
+ if self.combination == BY_LAYER:
+ group_sorted = groupsort.by_blender_type(layer_ents)
+ elif self.combination == BY_DXFTYPE:
+ group_sorted = groupsort.by_dxftype(layer_ents)
+ else:
+ break
+
+ for TYPE, grouped_entities in group_sorted:
+ if self.but_group_by_att:
+ for atts, by_att in groupsort.by_attributes(grouped_entities):
+ thickness, subd, width, extrusion = atts
+ att = ""
+ if thickness != 0:
+ att += "thickness" + str(thickness) + ", "
+ if subd > 0:
+ att += "subd" + str(subd) + ", "
+ if width != [(0, 0)]:
+ att += "width" + str(width) + ", "
+ if extrusion != (0, 0, 1):
+ att += "extrusion" + str([str(round(c, 1)) + ".." + str(c)[-1:] for c in extrusion]) + ", "
+ name = layer_name + "_" + TYPE.replace("object_", "") + "_" + att
+
+ o = self._call_object_types(TYPE, by_att, group, name, scene, False)
+ 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)
+ return objects
+
+ def separated_entities(self, entities, scene, override_name=None, override_group=None):
+ """
+ entities: list of dxf entities
+ override_group & override_name: for use within insert() and block()
+ Adds multiple DXF entities to multiple Blender objects.
+ """
+ def _do_it(en):
+ # group and name
+ if override_group is None:
+ group = self._get_group(en.layer)
+ else:
+ group = override_group
+
+ if override_name is None:
+ name = en.dxftype
+ else:
+ name = override_name
+
+ if en.dxftype == "POINT":
+ o = self.point_object(en)
+ else:
+ o = self._call_object_types(en.dxftype, [en], group, name, scene, separated=True)
+
+ if o is not None:
+ objects.append(o)
+
+ objects = []
+ split_entities = []
+
+ for en in entities:
+ split = convert.split_by_width(en)
+ if len(split) > 1:
+ split_entities.extend(split)
+ continue
+ _do_it(en)
+
+ for en in split_entities:
+ _do_it(en)
+
+ return objects
+
+ def entities(self, name, scene=None):
+ """
+ Iterates over all DXF entities according to the options set by user.
+ """
+ if scene is None:
+ scene = bpy.context.scene
+
+ self.current_scene = scene
+
+ if self.recenter:
+ self.objects_before += scene.objects[:]
+
+ if 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:
+ self.separated_entities((en for en in self.dwg.modelspace() if en.dxftype != "ATTDEF"), scene)
+
+ if self.recenter:
+ self._recenter(scene, name)
+ elif self.pDXF is not None:
+ self.georeference(scene, Vector((0, 0, 0)))
+
+ if type(self.pScene) is TransverseMercator:
+ scene['SRID'] = "tmerc"
+ elif self.pScene is not None: # assume Proj
+ scene['SRID'] = re.findall("\+init=(.+)\s", self.pScene.srs)[0]
+
+ bpy.context.screen.scene = scene
+
+ return self.errors
+ # trying to import dimensions:
+ # self.separated_objects((block for block in self.dwg.blocks if block.name.startswith("*")))